diff options
203 files changed, 13344 insertions, 1921 deletions
diff --git a/.travis.yml b/.travis.yml index 79377c8de0..c1e99237b2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -49,9 +49,10 @@ env: - TEST_CMD="make check" - MAKEFLAGS="-j3" matrix: - - CONFIG="" - - CONFIG="--enable-debug --enable-debug-tcg --enable-trace-backends=log" - - CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-uuid --disable-libusb" + - CONFIG="--disable-system" + - CONFIG="--disable-user" + - CONFIG="--enable-debug --enable-debug-tcg" + - CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-uuid --disable-libusb --disable-user" - CONFIG="--enable-modules --disable-linux-user" - CONFIG="--with-coroutine=ucontext --disable-linux-user" - CONFIG="--with-coroutine=sigaltstack --disable-linux-user" diff --git a/MAINTAINERS b/MAINTAINERS index f409f3b158..354a18ce49 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1100,6 +1100,14 @@ M: Peter Crosthwaite <crosthwaite.peter@gmail.com> S: Maintained F: hw/ssi/xilinx_* +SD (Secure Card) +M: Philippe Mathieu-Daudé <f4bug@amsat.org> +S: Odd Fixes +F: include/hw/sd/sd* +F: hw/sd/core.c +F: hw/sd/sd* +F: tests/sd* + USB M: Gerd Hoffmann <kraxel@redhat.com> S: Maintained @@ -425,6 +425,10 @@ dummy := $(call unnest-vars,, \ io-obj-y \ common-obj-y \ common-obj-m \ + ui-obj-y \ + ui-obj-m \ + audio-obj-y \ + audio-obj-m \ trace-obj-y) include $(SRC_PATH)/tests/Makefile.include @@ -851,7 +855,7 @@ ifneq ($(BLOBS),) $(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(qemu_datadir)"; \ done endif -ifeq ($(CONFIG_GTK),y) +ifeq ($(CONFIG_GTK),m) $(MAKE) -C po $@ endif $(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/keymaps" diff --git a/Makefile.objs b/Makefile.objs index d8b44a2d3c..c6c9b8fc21 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -104,6 +104,7 @@ common-obj-$(CONFIG_LINUX) += fsdev/ common-obj-y += migration/ common-obj-y += audio/ +common-obj-m += audio/ common-obj-y += hw/ common-obj-y += replay/ @@ -73,7 +73,7 @@ The QEMU website is also maintained under source control. git clone git://git.qemu.org/qemu-web.git https://www.qemu.org/2017/02/04/the-new-qemu-website-is-up/ -A 'git-profile' utility was created to make above process less +A 'git-publish' utility was created to make above process less cumbersome, and is highly recommended for making regular contributions, or even just for sending consecutive patch series revisions. It also requires a working 'git send-email' setup, and by default doesn't diff --git a/accel/Makefile.objs b/accel/Makefile.objs index 10666eda71..c3718a10c5 100644 --- a/accel/Makefile.objs +++ b/accel/Makefile.objs @@ -1,4 +1,4 @@ obj-$(CONFIG_SOFTMMU) += accel.o -obj-y += kvm/ +obj-$(CONFIG_KVM) += kvm/ obj-$(CONFIG_TCG) += tcg/ obj-y += stubs/ diff --git a/accel/kvm/Makefile.objs b/accel/kvm/Makefile.objs index 85351e7de7..fdfa481578 100644 --- a/accel/kvm/Makefile.objs +++ b/accel/kvm/Makefile.objs @@ -1 +1,2 @@ -obj-$(CONFIG_KVM) += kvm-all.o +obj-y += kvm-all.o +obj-$(call lnot,$(CONFIG_SEV)) += sev-stub.o diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index b91fcb7160..ffee68e603 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -38,6 +38,7 @@ #include "qemu/event_notifier.h" #include "trace.h" #include "hw/irq.h" +#include "sysemu/sev.h" #include "hw/boards.h" @@ -103,6 +104,10 @@ struct KVMState #endif KVMMemoryListener memory_listener; QLIST_HEAD(, KVMParkedVcpu) kvm_parked_vcpus; + + /* memory encryption */ + void *memcrypt_handle; + int (*memcrypt_encrypt_data)(void *handle, uint8_t *ptr, uint64_t len); }; KVMState *kvm_state; @@ -138,6 +143,26 @@ int kvm_get_max_memslots(void) return s->nr_slots; } +bool kvm_memcrypt_enabled(void) +{ + if (kvm_state && kvm_state->memcrypt_handle) { + return true; + } + + return false; +} + +int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len) +{ + if (kvm_state->memcrypt_handle && + kvm_state->memcrypt_encrypt_data) { + return kvm_state->memcrypt_encrypt_data(kvm_state->memcrypt_handle, + ptr, len); + } + + return 1; +} + static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml) { KVMState *s = kvm_state; @@ -1636,6 +1661,20 @@ static int kvm_init(MachineState *ms) kvm_state = s; + /* + * if memory encryption object is specified then initialize the memory + * encryption context. + */ + if (ms->memory_encryption) { + kvm_state->memcrypt_handle = sev_guest_init(ms->memory_encryption); + if (!kvm_state->memcrypt_handle) { + ret = -1; + goto err; + } + + kvm_state->memcrypt_encrypt_data = sev_encrypt_data; + } + ret = kvm_arch_init(ms, s); if (ret < 0) { goto err; diff --git a/accel/kvm/sev-stub.c b/accel/kvm/sev-stub.c new file mode 100644 index 0000000000..4f97452585 --- /dev/null +++ b/accel/kvm/sev-stub.c @@ -0,0 +1,26 @@ +/* + * QEMU SEV stub + * + * Copyright Advanced Micro Devices 2018 + * + * Authors: + * Brijesh Singh <brijesh.singh@amd.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "sysemu/sev.h" + +int sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len) +{ + abort(); +} + +void *sev_guest_init(const char *id) +{ + return NULL; +} diff --git a/accel/stubs/kvm-stub.c b/accel/stubs/kvm-stub.c index c964af3e1c..02d5170031 100644 --- a/accel/stubs/kvm-stub.c +++ b/accel/stubs/kvm-stub.c @@ -105,6 +105,16 @@ int kvm_on_sigbus(int code, void *addr) return 1; } +bool kvm_memcrypt_enabled(void) +{ + return false; +} + +int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len) +{ + return 1; +} + #ifndef CONFIG_USER_ONLY int kvm_irqchip_add_msi_route(KVMState *s, int vector, PCIDevice *dev) { diff --git a/audio/Makefile.objs b/audio/Makefile.objs index f6ce5c6744..db4fa7f18f 100644 --- a/audio/Makefile.objs +++ b/audio/Makefile.objs @@ -1,19 +1,31 @@ common-obj-y = audio.o noaudio.o wavaudio.o mixeng.o -common-obj-$(CONFIG_AUDIO_SDL) += sdlaudio.o -common-obj-$(CONFIG_AUDIO_OSS) += ossaudio.o common-obj-$(CONFIG_SPICE) += spiceaudio.o common-obj-$(CONFIG_AUDIO_COREAUDIO) += coreaudio.o -common-obj-$(CONFIG_AUDIO_ALSA) += alsaaudio.o common-obj-$(CONFIG_AUDIO_DSOUND) += dsoundaudio.o -common-obj-$(CONFIG_AUDIO_PA) += paaudio.o common-obj-$(CONFIG_AUDIO_PT_INT) += audio_pt_int.o common-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o common-obj-y += wavcapture.o -sdlaudio.o-cflags := $(SDL_CFLAGS) -sdlaudio.o-libs := $(SDL_LIBS) -alsaaudio.o-libs := $(ALSA_LIBS) -paaudio.o-libs := $(PULSE_LIBS) coreaudio.o-libs := $(COREAUDIO_LIBS) dsoundaudio.o-libs := $(DSOUND_LIBS) -ossaudio.o-libs := $(OSS_LIBS) + +# alsa module +common-obj-$(CONFIG_AUDIO_ALSA) += alsa.mo +alsa.mo-objs = alsaaudio.o +alsa.mo-libs := $(ALSA_LIBS) + +# oss module +common-obj-$(CONFIG_AUDIO_OSS) += oss.mo +oss.mo-objs = ossaudio.o +oss.mo-libs := $(OSS_LIBS) + +# pulseaudio module +common-obj-$(CONFIG_AUDIO_PA) += pa.mo +pa.mo-objs = paaudio.o +pa.mo-libs := $(PULSE_LIBS) + +# sdl module +common-obj-$(CONFIG_AUDIO_SDL) += sdl.mo +sdl.mo-objs = sdlaudio.o +sdl.mo-cflags := $(SDL_CFLAGS) +sdl.mo-libs := $(SDL_LIBS) diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c index 92a96f8b2b..362a2276fd 100644 --- a/audio/alsaaudio.c +++ b/audio/alsaaudio.c @@ -1213,7 +1213,7 @@ static struct audio_pcm_ops alsa_pcm_ops = { .ctl_in = alsa_ctl_in, }; -struct audio_driver alsa_audio_driver = { +static struct audio_driver alsa_audio_driver = { .name = "alsa", .descr = "ALSA http://www.alsa-project.org", .options = alsa_options, @@ -1226,3 +1226,9 @@ struct audio_driver alsa_audio_driver = { .voice_size_out = sizeof (ALSAVoiceOut), .voice_size_in = sizeof (ALSAVoiceIn) }; + +static void register_audio_alsa(void) +{ + audio_driver_register(&alsa_audio_driver); +} +type_init(register_audio_alsa); diff --git a/audio/audio.c b/audio/audio.c index 7658d2af66..6eccdb17ee 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -45,15 +45,49 @@ The 1st one is the one used by default, that is the reason that we generate the list. */ -static struct audio_driver *drvtab[] = { -#ifdef CONFIG_SPICE - &spice_audio_driver, -#endif +static const char *audio_prio_list[] = { + "spice", CONFIG_AUDIO_DRIVERS - &no_audio_driver, - &wav_audio_driver + "none", + "wav", }; +static QLIST_HEAD(, audio_driver) audio_drivers; + +void audio_driver_register(audio_driver *drv) +{ + QLIST_INSERT_HEAD(&audio_drivers, drv, next); +} + +audio_driver *audio_driver_lookup(const char *name) +{ + struct audio_driver *d; + + QLIST_FOREACH(d, &audio_drivers, next) { + if (strcmp(name, d->name) == 0) { + return d; + } + } + + audio_module_load_one(name); + QLIST_FOREACH(d, &audio_drivers, next) { + if (strcmp(name, d->name) == 0) { + return d; + } + } + + return NULL; +} + +static void audio_module_load_all(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(audio_prio_list); i++) { + audio_driver_lookup(audio_prio_list[i]); + } +} + struct fixed_settings { int enabled; int nb_voices; @@ -1656,11 +1690,13 @@ static void audio_pp_nb_voices (const char *typ, int nb) void AUD_help (void) { - size_t i; + struct audio_driver *d; + + /* make sure we print the help text for modular drivers too */ + audio_module_load_all(); audio_process_options ("AUDIO", audio_options); - for (i = 0; i < ARRAY_SIZE (drvtab); i++) { - struct audio_driver *d = drvtab[i]; + QLIST_FOREACH(d, &audio_drivers, next) { if (d->options) { audio_process_options (d->name, d->options); } @@ -1672,8 +1708,7 @@ void AUD_help (void) printf ("Available drivers:\n"); - for (i = 0; i < ARRAY_SIZE (drvtab); i++) { - struct audio_driver *d = drvtab[i]; + QLIST_FOREACH(d, &audio_drivers, next) { printf ("Name: %s\n", d->name); printf ("Description: %s\n", d->descr); @@ -1807,6 +1842,7 @@ static void audio_init (void) const char *drvname; VMChangeStateEntry *e; AudioState *s = &glob_audio_state; + struct audio_driver *driver; if (s->drv) { return; @@ -1842,32 +1878,27 @@ static void audio_init (void) } if (drvname) { - int found = 0; - - for (i = 0; i < ARRAY_SIZE (drvtab); i++) { - if (!strcmp (drvname, drvtab[i]->name)) { - done = !audio_driver_init (s, drvtab[i]); - found = 1; - break; - } - } - - if (!found) { + driver = audio_driver_lookup(drvname); + if (driver) { + done = !audio_driver_init(s, driver); + } else { dolog ("Unknown audio driver `%s'\n", drvname); dolog ("Run with -audio-help to list available drivers\n"); } } if (!done) { - for (i = 0; !done && i < ARRAY_SIZE (drvtab); i++) { - if (drvtab[i]->can_be_default) { - done = !audio_driver_init (s, drvtab[i]); + for (i = 0; !done && i < ARRAY_SIZE(audio_prio_list); i++) { + driver = audio_driver_lookup(audio_prio_list[i]); + if (driver && driver->can_be_default) { + done = !audio_driver_init(s, driver); } } } if (!done) { - done = !audio_driver_init (s, &no_audio_driver); + driver = audio_driver_lookup("none"); + done = !audio_driver_init(s, driver); assert(done); dolog("warning: Using timer based audio emulation\n"); } diff --git a/audio/audio_int.h b/audio/audio_int.h index 700bd43143..244b454012 100644 --- a/audio/audio_int.h +++ b/audio/audio_int.h @@ -141,6 +141,7 @@ struct SWVoiceIn { QLIST_ENTRY (SWVoiceIn) entries; }; +typedef struct audio_driver audio_driver; struct audio_driver { const char *name; const char *descr; @@ -154,6 +155,7 @@ struct audio_driver { int voice_size_out; int voice_size_in; int ctl_caps; + QLIST_ENTRY(audio_driver) next; }; struct audio_pcm_ops { @@ -203,17 +205,11 @@ struct AudioState { int vm_running; }; -extern struct audio_driver no_audio_driver; -extern struct audio_driver oss_audio_driver; -extern struct audio_driver sdl_audio_driver; -extern struct audio_driver wav_audio_driver; -extern struct audio_driver alsa_audio_driver; -extern struct audio_driver coreaudio_audio_driver; -extern struct audio_driver dsound_audio_driver; -extern struct audio_driver pa_audio_driver; -extern struct audio_driver spice_audio_driver; extern const struct mixeng_volume nominal_volume; +void audio_driver_register(audio_driver *drv); +audio_driver *audio_driver_lookup(const char *name); + void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as); void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len); diff --git a/audio/coreaudio.c b/audio/coreaudio.c index c75142084f..638c60b300 100644 --- a/audio/coreaudio.c +++ b/audio/coreaudio.c @@ -722,7 +722,7 @@ static struct audio_pcm_ops coreaudio_pcm_ops = { .ctl_out = coreaudio_ctl_out }; -struct audio_driver coreaudio_audio_driver = { +static struct audio_driver coreaudio_audio_driver = { .name = "coreaudio", .descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html", .options = coreaudio_options, @@ -735,3 +735,9 @@ struct audio_driver coreaudio_audio_driver = { .voice_size_out = sizeof (coreaudioVoiceOut), .voice_size_in = 0 }; + +static void register_audio_coreaudio(void) +{ + audio_driver_register(&coreaudio_audio_driver); +} +type_init(register_audio_coreaudio); diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c index bc39cb9b4d..3ed73a30d1 100644 --- a/audio/dsoundaudio.c +++ b/audio/dsoundaudio.c @@ -890,7 +890,7 @@ static struct audio_pcm_ops dsound_pcm_ops = { .ctl_in = dsound_ctl_in }; -struct audio_driver dsound_audio_driver = { +static struct audio_driver dsound_audio_driver = { .name = "dsound", .descr = "DirectSound http://wikipedia.org/wiki/DirectSound", .options = dsound_options, @@ -903,3 +903,9 @@ struct audio_driver dsound_audio_driver = { .voice_size_out = sizeof (DSoundVoiceOut), .voice_size_in = sizeof (DSoundVoiceIn) }; + +static void register_audio_dsound(void) +{ + audio_driver_register(&dsound_audio_driver); +} +type_init(register_audio_dsound); diff --git a/audio/noaudio.c b/audio/noaudio.c index 9ca9eaf01f..1bfebeca7d 100644 --- a/audio/noaudio.c +++ b/audio/noaudio.c @@ -160,7 +160,7 @@ static struct audio_pcm_ops no_pcm_ops = { .ctl_in = no_ctl_in }; -struct audio_driver no_audio_driver = { +static struct audio_driver no_audio_driver = { .name = "none", .descr = "Timer based audio emulation", .options = NULL, @@ -173,3 +173,9 @@ struct audio_driver no_audio_driver = { .voice_size_out = sizeof (NoVoiceOut), .voice_size_in = sizeof (NoVoiceIn) }; + +static void register_audio_none(void) +{ + audio_driver_register(&no_audio_driver); +} +type_init(register_audio_none); diff --git a/audio/ossaudio.c b/audio/ossaudio.c index a0428881c2..6c69622b4c 100644 --- a/audio/ossaudio.c +++ b/audio/ossaudio.c @@ -922,7 +922,7 @@ static struct audio_pcm_ops oss_pcm_ops = { .ctl_in = oss_ctl_in }; -struct audio_driver oss_audio_driver = { +static struct audio_driver oss_audio_driver = { .name = "oss", .descr = "OSS http://www.opensound.com", .options = oss_options, @@ -935,3 +935,9 @@ struct audio_driver oss_audio_driver = { .voice_size_out = sizeof (OSSVoiceOut), .voice_size_in = sizeof (OSSVoiceIn) }; + +static void register_audio_oss(void) +{ + audio_driver_register(&oss_audio_driver); +} +type_init(register_audio_oss); diff --git a/audio/paaudio.c b/audio/paaudio.c index aa0a7477d3..949769774d 100644 --- a/audio/paaudio.c +++ b/audio/paaudio.c @@ -937,7 +937,7 @@ static struct audio_pcm_ops qpa_pcm_ops = { .ctl_in = qpa_ctl_in }; -struct audio_driver pa_audio_driver = { +static struct audio_driver pa_audio_driver = { .name = "pa", .descr = "http://www.pulseaudio.org/", .options = qpa_options, @@ -951,3 +951,9 @@ struct audio_driver pa_audio_driver = { .voice_size_in = sizeof (PAVoiceIn), .ctl_caps = VOICE_VOLUME_CAP }; + +static void register_audio_pa(void) +{ + audio_driver_register(&pa_audio_driver); +} +type_init(register_audio_pa); diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c index e92135bd2f..9db5ac92bc 100644 --- a/audio/sdlaudio.c +++ b/audio/sdlaudio.c @@ -500,7 +500,7 @@ static struct audio_pcm_ops sdl_pcm_ops = { .ctl_out = sdl_ctl_out, }; -struct audio_driver sdl_audio_driver = { +static struct audio_driver sdl_audio_driver = { .name = "sdl", .descr = "SDL http://www.libsdl.org", .options = sdl_options, @@ -513,3 +513,9 @@ struct audio_driver sdl_audio_driver = { .voice_size_out = sizeof (SDLVoiceOut), .voice_size_in = 0 }; + +static void register_audio_sdl(void) +{ + audio_driver_register(&sdl_audio_driver); +} +type_init(register_audio_sdl); diff --git a/audio/spiceaudio.c b/audio/spiceaudio.c index 5580e76307..6ad0eafbc6 100644 --- a/audio/spiceaudio.c +++ b/audio/spiceaudio.c @@ -391,7 +391,7 @@ static struct audio_pcm_ops audio_callbacks = { .ctl_in = line_in_ctl, }; -struct audio_driver spice_audio_driver = { +static struct audio_driver spice_audio_driver = { .name = "spice", .descr = "spice audio driver", .options = audio_options, @@ -411,3 +411,9 @@ void qemu_spice_audio_init (void) { spice_audio_driver.can_be_default = 1; } + +static void register_audio_spice(void) +{ + audio_driver_register(&spice_audio_driver); +} +type_init(register_audio_spice); diff --git a/audio/wavaudio.c b/audio/wavaudio.c index 068a595732..40adfa30c3 100644 --- a/audio/wavaudio.c +++ b/audio/wavaudio.c @@ -278,7 +278,7 @@ static struct audio_pcm_ops wav_pcm_ops = { .ctl_out = wav_ctl_out, }; -struct audio_driver wav_audio_driver = { +static struct audio_driver wav_audio_driver = { .name = "wav", .descr = "WAV renderer http://wikipedia.org/wiki/WAV", .options = wav_options, @@ -291,3 +291,9 @@ struct audio_driver wav_audio_driver = { .voice_size_out = sizeof (WAVVoiceOut), .voice_size_in = 0 }; + +static void register_audio_wav(void) +{ + audio_driver_register(&wav_audio_driver); +} +type_init(register_audio_wav); @@ -34,6 +34,8 @@ #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qstring.h" +#include "qapi/qobject-output-visitor.h" +#include "qapi/qapi-visit-block-core.h" #include "sysemu/block-backend.h" #include "sysemu/sysemu.h" #include "qemu/notify.h" @@ -368,7 +370,7 @@ BlockDriver *bdrv_find_format(const char *format_name) return bdrv_do_find_format(format_name); } -static int bdrv_is_whitelisted(BlockDriver *drv, bool read_only) +int bdrv_is_whitelisted(BlockDriver *drv, bool read_only) { static const char *whitelist_rw[] = { CONFIG_BDRV_RW_WHITELIST @@ -2406,6 +2408,51 @@ BdrvChild *bdrv_open_child(const char *filename, return c; } +/* TODO Future callers may need to specify parent/child_role in order for + * option inheritance to work. Existing callers use it for the root node. */ +BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp) +{ + BlockDriverState *bs = NULL; + Error *local_err = NULL; + QObject *obj = NULL; + QDict *qdict = NULL; + const char *reference = NULL; + Visitor *v = NULL; + + if (ref->type == QTYPE_QSTRING) { + reference = ref->u.reference; + } else { + BlockdevOptions *options = &ref->u.definition; + assert(ref->type == QTYPE_QDICT); + + v = qobject_output_visitor_new(&obj); + visit_type_BlockdevOptions(v, NULL, &options, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto fail; + } + visit_complete(v, &obj); + + qdict = qobject_to_qdict(obj); + qdict_flatten(qdict); + + /* bdrv_open_inherit() defaults to the values in bdrv_flags (for + * compatibility with other callers) rather than what we want as the + * real defaults. Apply the defaults here instead. */ + qdict_set_default_str(qdict, BDRV_OPT_CACHE_DIRECT, "off"); + qdict_set_default_str(qdict, BDRV_OPT_CACHE_NO_FLUSH, "off"); + qdict_set_default_str(qdict, BDRV_OPT_READ_ONLY, "off"); + } + + bs = bdrv_open_inherit(NULL, reference, qdict, 0, NULL, NULL, errp); + obj = NULL; + +fail: + qobject_decref(obj); + visit_free(v); + return bs; +} + static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, QDict *snapshot_options, @@ -3455,17 +3502,54 @@ static void bdrv_delete(BlockDriverState *bs) * free of errors) or -errno when an internal error occurred. The results of the * check are stored in res. */ -int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix) +static int coroutine_fn bdrv_co_check(BlockDriverState *bs, + BdrvCheckResult *res, BdrvCheckMode fix) { if (bs->drv == NULL) { return -ENOMEDIUM; } - if (bs->drv->bdrv_check == NULL) { + if (bs->drv->bdrv_co_check == NULL) { return -ENOTSUP; } memset(res, 0, sizeof(*res)); - return bs->drv->bdrv_check(bs, res, fix); + return bs->drv->bdrv_co_check(bs, res, fix); +} + +typedef struct CheckCo { + BlockDriverState *bs; + BdrvCheckResult *res; + BdrvCheckMode fix; + int ret; +} CheckCo; + +static void bdrv_check_co_entry(void *opaque) +{ + CheckCo *cco = opaque; + cco->ret = bdrv_co_check(cco->bs, cco->res, cco->fix); +} + +int bdrv_check(BlockDriverState *bs, + BdrvCheckResult *res, BdrvCheckMode fix) +{ + Coroutine *co; + CheckCo cco = { + .bs = bs, + .res = res, + .ret = -EINPROGRESS, + .fix = fix, + }; + + if (qemu_in_coroutine()) { + /* Fast-path if already in coroutine context */ + bdrv_check_co_entry(&cco); + } else { + co = qemu_coroutine_create(bdrv_check_co_entry, &cco); + qemu_coroutine_enter(co); + BDRV_POLL_WHILE(bs, cco.ret == -EINPROGRESS); + } + + return cco.ret; } /* @@ -3635,6 +3719,11 @@ int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc, error_setg(errp, "No medium inserted"); return -ENOMEDIUM; } + if (offset < 0) { + error_setg(errp, "Image size cannot be negative"); + return -EINVAL; + } + if (!drv->bdrv_truncate) { if (bs->file && drv->is_filter) { return bdrv_truncate(bs->file, offset, prealloc, errp); @@ -4209,7 +4298,8 @@ void bdrv_init_with_whitelist(void) bdrv_init(); } -void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp) +static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, + Error **errp) { BdrvChild *child, *parent; uint64_t perm, shared_perm; @@ -4225,7 +4315,7 @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp) } QLIST_FOREACH(child, &bs->children, next) { - bdrv_invalidate_cache(child->bs, &local_err); + bdrv_co_invalidate_cache(child->bs, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -4255,8 +4345,8 @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp) } bdrv_set_perm(bs, perm, shared_perm); - if (bs->drv->bdrv_invalidate_cache) { - bs->drv->bdrv_invalidate_cache(bs, &local_err); + if (bs->drv->bdrv_co_invalidate_cache) { + bs->drv->bdrv_co_invalidate_cache(bs, &local_err); if (local_err) { bs->open_flags |= BDRV_O_INACTIVE; error_propagate(errp, local_err); @@ -4282,6 +4372,38 @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp) } } +typedef struct InvalidateCacheCo { + BlockDriverState *bs; + Error **errp; + bool done; +} InvalidateCacheCo; + +static void coroutine_fn bdrv_invalidate_cache_co_entry(void *opaque) +{ + InvalidateCacheCo *ico = opaque; + bdrv_co_invalidate_cache(ico->bs, ico->errp); + ico->done = true; +} + +void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp) +{ + Coroutine *co; + InvalidateCacheCo ico = { + .bs = bs, + .done = false, + .errp = errp + }; + + if (qemu_in_coroutine()) { + /* Fast-path if already in coroutine context */ + bdrv_invalidate_cache_co_entry(&ico); + } else { + co = qemu_coroutine_create(bdrv_invalidate_cache_co_entry, &ico); + qemu_coroutine_enter(co); + BDRV_POLL_WHILE(bs, !ico.done); + } +} + void bdrv_invalidate_cache_all(Error **errp) { BlockDriverState *bs; diff --git a/block/Makefile.objs b/block/Makefile.objs index aede94f105..d644bac60a 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs @@ -9,7 +9,7 @@ block-obj-y += block-backend.o snapshot.o qapi.o block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o block-obj-$(CONFIG_POSIX) += file-posix.o block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o -block-obj-y += null.o mirror.o commit.o io.o +block-obj-y += null.o mirror.o commit.o io.o create.o block-obj-y += throttle-groups.o block-obj-$(CONFIG_LINUX) += nvme.o diff --git a/block/block-backend.c b/block/block-backend.c index b3c790e2bd..f2e0a855ff 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1150,7 +1150,7 @@ int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset, typedef struct BlkRwCo { BlockBackend *blk; int64_t offset; - QEMUIOVector *qiov; + void *iobuf; int ret; BdrvRequestFlags flags; } BlkRwCo; @@ -1158,17 +1158,19 @@ typedef struct BlkRwCo { static void blk_read_entry(void *opaque) { BlkRwCo *rwco = opaque; + QEMUIOVector *qiov = rwco->iobuf; - rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, rwco->qiov->size, - rwco->qiov, rwco->flags); + rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, qiov->size, + qiov, rwco->flags); } static void blk_write_entry(void *opaque) { BlkRwCo *rwco = opaque; + QEMUIOVector *qiov = rwco->iobuf; - rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, rwco->qiov->size, - rwco->qiov, rwco->flags); + rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, qiov->size, + qiov, rwco->flags); } static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf, @@ -1188,7 +1190,7 @@ static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf, rwco = (BlkRwCo) { .blk = blk, .offset = offset, - .qiov = &qiov, + .iobuf = &qiov, .flags = flags, .ret = NOT_DONE, }; @@ -1296,7 +1298,7 @@ static void blk_aio_complete_bh(void *opaque) } static BlockAIOCB *blk_aio_prwv(BlockBackend *blk, int64_t offset, int bytes, - QEMUIOVector *qiov, CoroutineEntry co_entry, + void *iobuf, CoroutineEntry co_entry, BdrvRequestFlags flags, BlockCompletionFunc *cb, void *opaque) { @@ -1308,7 +1310,7 @@ static BlockAIOCB *blk_aio_prwv(BlockBackend *blk, int64_t offset, int bytes, acb->rwco = (BlkRwCo) { .blk = blk, .offset = offset, - .qiov = qiov, + .iobuf = iobuf, .flags = flags, .ret = NOT_DONE, }; @@ -1331,10 +1333,11 @@ static void blk_aio_read_entry(void *opaque) { BlkAioEmAIOCB *acb = opaque; BlkRwCo *rwco = &acb->rwco; + QEMUIOVector *qiov = rwco->iobuf; - assert(rwco->qiov->size == acb->bytes); + assert(qiov->size == acb->bytes); rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, acb->bytes, - rwco->qiov, rwco->flags); + qiov, rwco->flags); blk_aio_complete(acb); } @@ -1342,10 +1345,11 @@ static void blk_aio_write_entry(void *opaque) { BlkAioEmAIOCB *acb = opaque; BlkRwCo *rwco = &acb->rwco; + QEMUIOVector *qiov = rwco->iobuf; - assert(!rwco->qiov || rwco->qiov->size == acb->bytes); + assert(!qiov || qiov->size == acb->bytes); rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, acb->bytes, - rwco->qiov, rwco->flags); + qiov, rwco->flags); blk_aio_complete(acb); } @@ -1474,8 +1478,10 @@ int blk_co_ioctl(BlockBackend *blk, unsigned long int req, void *buf) static void blk_ioctl_entry(void *opaque) { BlkRwCo *rwco = opaque; + QEMUIOVector *qiov = rwco->iobuf; + rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset, - rwco->qiov->iov[0].iov_base); + qiov->iov[0].iov_base); } int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf) @@ -1488,24 +1494,15 @@ static void blk_aio_ioctl_entry(void *opaque) BlkAioEmAIOCB *acb = opaque; BlkRwCo *rwco = &acb->rwco; - rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset, - rwco->qiov->iov[0].iov_base); + rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset, rwco->iobuf); + blk_aio_complete(acb); } BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, BlockCompletionFunc *cb, void *opaque) { - QEMUIOVector qiov; - struct iovec iov; - - iov = (struct iovec) { - .iov_base = buf, - .iov_len = 0, - }; - qemu_iovec_init_external(&qiov, &iov, 1); - - return blk_aio_prwv(blk, req, 0, &qiov, blk_aio_ioctl_entry, 0, cb, opaque); + return blk_aio_prwv(blk, req, 0, buf, blk_aio_ioctl_entry, 0, cb, opaque); } int blk_co_pdiscard(BlockBackend *blk, int64_t offset, int bytes) @@ -1949,7 +1946,9 @@ int blk_truncate(BlockBackend *blk, int64_t offset, PreallocMode prealloc, static void blk_pdiscard_entry(void *opaque) { BlkRwCo *rwco = opaque; - rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, rwco->qiov->size); + QEMUIOVector *qiov = rwco->iobuf; + + rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, qiov->size); } int blk_pdiscard(BlockBackend *blk, int64_t offset, int bytes) diff --git a/block/create.c b/block/create.c new file mode 100644 index 0000000000..8bd8a03719 --- /dev/null +++ b/block/create.c @@ -0,0 +1,76 @@ +/* + * Block layer code related to image creation + * + * Copyright (c) 2018 Kevin Wolf <kwolf@redhat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "block/block_int.h" +#include "qapi/qapi-commands-block-core.h" +#include "qapi/error.h" + +typedef struct BlockdevCreateCo { + BlockDriver *drv; + BlockdevCreateOptions *opts; + int ret; + Error **errp; +} BlockdevCreateCo; + +static void coroutine_fn bdrv_co_create_co_entry(void *opaque) +{ + BlockdevCreateCo *cco = opaque; + cco->ret = cco->drv->bdrv_co_create(cco->opts, cco->errp); +} + +void qmp_x_blockdev_create(BlockdevCreateOptions *options, Error **errp) +{ + const char *fmt = BlockdevDriver_str(options->driver); + BlockDriver *drv = bdrv_find_format(fmt); + Coroutine *co; + BlockdevCreateCo cco; + + /* If the driver is in the schema, we know that it exists. But it may not + * be whitelisted. */ + assert(drv); + if (bdrv_uses_whitelist() && !bdrv_is_whitelisted(drv, false)) { + error_setg(errp, "Driver is not whitelisted"); + return; + } + + /* Call callback if it exists */ + if (!drv->bdrv_co_create) { + error_setg(errp, "Driver does not support blockdev-create"); + return; + } + + cco = (BlockdevCreateCo) { + .drv = drv, + .opts = options, + .ret = -EINPROGRESS, + .errp = errp, + }; + + co = qemu_coroutine_create(bdrv_co_create_co_entry, &cco); + qemu_coroutine_enter(co); + while (cco.ret == -EINPROGRESS) { + aio_poll(qemu_get_aio_context(), true); + } +} diff --git a/block/crypto.c b/block/crypto.c index 17b5c0abad..e6095e7807 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -384,6 +384,12 @@ static void block_crypto_close(BlockDriverState *bs) qcrypto_block_free(crypto->block); } +static int block_crypto_reopen_prepare(BDRVReopenState *state, + BlockReopenQueue *queue, Error **errp) +{ + /* nothing needs checking */ + return 0; +} /* * 1 MB bounce buffer gives good performance / memory tradeoff @@ -621,6 +627,7 @@ BlockDriver bdrv_crypto_luks = { .bdrv_truncate = block_crypto_truncate, .create_opts = &block_crypto_create_opts_luks, + .bdrv_reopen_prepare = block_crypto_reopen_prepare, .bdrv_refresh_limits = block_crypto_refresh_limits, .bdrv_co_preadv = block_crypto_co_preadv, .bdrv_co_pwritev = block_crypto_co_pwritev, diff --git a/block/file-posix.c b/block/file-posix.c index 7f2cc63c60..d7fb772c14 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -1686,11 +1686,15 @@ static int raw_regular_truncate(int fd, int64_t offset, PreallocMode prealloc, * file systems that do not support fallocate(), trying to check if a * block is allocated before allocating it, so don't do that here. */ - result = -posix_fallocate(fd, current_length, offset - current_length); - if (result != 0) { - /* posix_fallocate() doesn't set errno. */ - error_setg_errno(errp, -result, - "Could not preallocate new data"); + if (offset != current_length) { + result = -posix_fallocate(fd, current_length, offset - current_length); + if (result != 0) { + /* posix_fallocate() doesn't set errno. */ + error_setg_errno(errp, -result, + "Could not preallocate new data"); + } + } else { + result = 0; } goto out; #endif @@ -1982,34 +1986,25 @@ static int64_t raw_get_allocated_file_size(BlockDriverState *bs) return (int64_t)st.st_blocks * 512; } -static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts, - Error **errp) +static int raw_co_create(BlockdevCreateOptions *options, Error **errp) { + BlockdevCreateOptionsFile *file_opts; int fd; int result = 0; - int64_t total_size = 0; - bool nocow = false; - PreallocMode prealloc; - char *buf = NULL; - Error *local_err = NULL; - strstart(filename, "file:", &filename); + /* Validate options and set default values */ + assert(options->driver == BLOCKDEV_DRIVER_FILE); + file_opts = &options->u.file; - /* Read out options */ - total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), - BDRV_SECTOR_SIZE); - nocow = qemu_opt_get_bool(opts, BLOCK_OPT_NOCOW, false); - buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC); - prealloc = qapi_enum_parse(&PreallocMode_lookup, buf, - PREALLOC_MODE_OFF, &local_err); - g_free(buf); - if (local_err) { - error_propagate(errp, local_err); - result = -EINVAL; - goto out; + if (!file_opts->has_nocow) { + file_opts->nocow = false; + } + if (!file_opts->has_preallocation) { + file_opts->preallocation = PREALLOC_MODE_OFF; } - fd = qemu_open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, + /* Create file */ + fd = qemu_open(file_opts->filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0644); if (fd < 0) { result = -errno; @@ -2017,7 +2012,7 @@ static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts, goto out; } - if (nocow) { + if (file_opts->nocow) { #ifdef __linux__ /* Set NOCOW flag to solve performance issue on fs like btrfs. * This is an optimisation. The FS_IOC_SETFLAGS ioctl return value @@ -2032,7 +2027,8 @@ static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts, #endif } - result = raw_regular_truncate(fd, total_size, prealloc, errp); + result = raw_regular_truncate(fd, file_opts->size, file_opts->preallocation, + errp); if (result < 0) { goto out_close; } @@ -2046,6 +2042,46 @@ out: return result; } +static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts, + Error **errp) +{ + BlockdevCreateOptions options; + int64_t total_size = 0; + bool nocow = false; + PreallocMode prealloc; + char *buf = NULL; + Error *local_err = NULL; + + /* Skip file: protocol prefix */ + strstart(filename, "file:", &filename); + + /* Read out options */ + total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); + nocow = qemu_opt_get_bool(opts, BLOCK_OPT_NOCOW, false); + buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC); + prealloc = qapi_enum_parse(&PreallocMode_lookup, buf, + PREALLOC_MODE_OFF, &local_err); + g_free(buf); + if (local_err) { + error_propagate(errp, local_err); + return -EINVAL; + } + + options = (BlockdevCreateOptions) { + .driver = BLOCKDEV_DRIVER_FILE, + .u.file = { + .filename = (char *) filename, + .size = total_size, + .has_preallocation = true, + .preallocation = prealloc, + .has_nocow = true, + .nocow = nocow, + }, + }; + return raw_co_create(&options, errp); +} + /* * Find allocation range in @bs around offset @start. * May change underlying file descriptor's file offset. @@ -2277,6 +2313,7 @@ BlockDriver bdrv_file = { .bdrv_reopen_commit = raw_reopen_commit, .bdrv_reopen_abort = raw_reopen_abort, .bdrv_close = raw_close, + .bdrv_co_create = raw_co_create, .bdrv_co_create_opts = raw_co_create_opts, .bdrv_has_zero_init = bdrv_has_zero_init_1, .bdrv_co_block_status = raw_co_block_status, diff --git a/block/file-win32.c b/block/file-win32.c index 4a430d45f1..2e2f746bb1 100644 --- a/block/file-win32.c +++ b/block/file-win32.c @@ -553,30 +553,59 @@ static int64_t raw_get_allocated_file_size(BlockDriverState *bs) return st.st_size; } -static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts, - Error **errp) +static int raw_co_create(BlockdevCreateOptions *options, Error **errp) { + BlockdevCreateOptionsFile *file_opts; int fd; - int64_t total_size = 0; - strstart(filename, "file:", &filename); + assert(options->driver == BLOCKDEV_DRIVER_FILE); + file_opts = &options->u.file; - /* Read out options */ - total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), - BDRV_SECTOR_SIZE); + if (file_opts->has_preallocation) { + error_setg(errp, "Preallocation is not supported on Windows"); + return -EINVAL; + } + if (file_opts->has_nocow) { + error_setg(errp, "nocow is not supported on Windows"); + return -EINVAL; + } - fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, + fd = qemu_open(file_opts->filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); if (fd < 0) { error_setg_errno(errp, errno, "Could not create file"); return -EIO; } set_sparse(fd); - ftruncate(fd, total_size); + ftruncate(fd, file_opts->size); qemu_close(fd); + return 0; } +static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts, + Error **errp) +{ + BlockdevCreateOptions options; + int64_t total_size = 0; + + strstart(filename, "file:", &filename); + + /* Read out options */ + total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); + + options = (BlockdevCreateOptions) { + .driver = BLOCKDEV_DRIVER_FILE, + .u.file = { + .filename = (char *) filename, + .size = total_size, + .has_preallocation = false, + .has_nocow = false, + }, + }; + return raw_co_create(&options, errp); +} static QemuOptsList raw_create_opts = { .name = "raw-create-opts", diff --git a/block/gluster.c b/block/gluster.c index 79b4cfdf74..296e036b3d 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -655,21 +655,22 @@ out: return -errno; } -static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf, - const char *filename, - QDict *options, Error **errp) +/* Converts options given in @filename and the @options QDict into the QAPI + * object @gconf. */ +static int qemu_gluster_parse(BlockdevOptionsGluster *gconf, + const char *filename, + QDict *options, Error **errp) { int ret; if (filename) { ret = qemu_gluster_parse_uri(gconf, filename); if (ret < 0) { - error_setg(errp, "invalid URI"); + error_setg(errp, "invalid URI %s", filename); error_append_hint(errp, "Usage: file=gluster[+transport]://" "[host[:port]]volume/path[?socket=...]" "[,file.debug=N]" "[,file.logfile=/path/filename.log]\n"); - errno = -ret; - return NULL; + return ret; } } else { ret = qemu_gluster_parse_json(gconf, options, errp); @@ -685,10 +686,23 @@ static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf, "file.server.1.transport=unix," "file.server.1.socket=/var/run/glusterd.socket ..." "\n"); - errno = -ret; - return NULL; + return ret; } + } + return 0; +} + +static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf, + const char *filename, + QDict *options, Error **errp) +{ + int ret; + + ret = qemu_gluster_parse(gconf, filename, options, errp); + if (ret < 0) { + errno = -ret; + return NULL; } return qemu_gluster_glfs_init(gconf, errp); @@ -1021,20 +1035,72 @@ static int qemu_gluster_do_truncate(struct glfs_fd *fd, int64_t offset, return 0; } +static int qemu_gluster_co_create(BlockdevCreateOptions *options, + Error **errp) +{ + BlockdevCreateOptionsGluster *opts = &options->u.gluster; + struct glfs *glfs; + struct glfs_fd *fd = NULL; + int ret = 0; + + assert(options->driver == BLOCKDEV_DRIVER_GLUSTER); + + glfs = qemu_gluster_glfs_init(opts->location, errp); + if (!glfs) { + ret = -errno; + goto out; + } + + fd = glfs_creat(glfs, opts->location->path, + O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR); + if (!fd) { + ret = -errno; + goto out; + } + + ret = qemu_gluster_do_truncate(fd, opts->size, opts->preallocation, errp); + +out: + if (fd) { + if (glfs_close(fd) != 0 && ret == 0) { + ret = -errno; + } + } + glfs_clear_preopened(glfs); + return ret; +} + static int coroutine_fn qemu_gluster_co_create_opts(const char *filename, QemuOpts *opts, Error **errp) { + BlockdevCreateOptions *options; + BlockdevCreateOptionsGluster *gopts; BlockdevOptionsGluster *gconf; - struct glfs *glfs; - struct glfs_fd *fd = NULL; - int ret = 0; - PreallocMode prealloc; - int64_t total_size = 0; char *tmp = NULL; Error *local_err = NULL; + int ret; + + options = g_new0(BlockdevCreateOptions, 1); + options->driver = BLOCKDEV_DRIVER_GLUSTER; + gopts = &options->u.gluster; gconf = g_new0(BlockdevOptionsGluster, 1); + gopts->location = gconf; + + gopts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); + + tmp = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC); + gopts->preallocation = qapi_enum_parse(&PreallocMode_lookup, tmp, + PREALLOC_MODE_OFF, &local_err); + g_free(tmp); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto fail; + } + gconf->debug = qemu_opt_get_number_del(opts, GLUSTER_OPT_DEBUG, GLUSTER_DEBUG_DEFAULT); if (gconf->debug < 0) { @@ -1050,42 +1116,19 @@ static int coroutine_fn qemu_gluster_co_create_opts(const char *filename, } gconf->has_logfile = true; - glfs = qemu_gluster_init(gconf, filename, NULL, errp); - if (!glfs) { - ret = -errno; - goto out; - } - - total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), - BDRV_SECTOR_SIZE); - - tmp = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC); - prealloc = qapi_enum_parse(&PreallocMode_lookup, tmp, PREALLOC_MODE_OFF, - &local_err); - g_free(tmp); - if (local_err) { - error_propagate(errp, local_err); - ret = -EINVAL; - goto out; + ret = qemu_gluster_parse(gconf, filename, NULL, errp); + if (ret < 0) { + goto fail; } - fd = glfs_creat(glfs, gconf->path, - O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR); - if (!fd) { - ret = -errno; - goto out; + ret = qemu_gluster_co_create(options, errp); + if (ret < 0) { + goto fail; } - ret = qemu_gluster_do_truncate(fd, total_size, prealloc, errp); - -out: - if (fd) { - if (glfs_close(fd) != 0 && ret == 0) { - ret = -errno; - } - } - qapi_free_BlockdevOptionsGluster(gconf); - glfs_clear_preopened(glfs); + ret = 0; +fail: + qapi_free_BlockdevCreateOptions(options); return ret; } @@ -1436,6 +1479,7 @@ static BlockDriver bdrv_gluster = { .bdrv_reopen_commit = qemu_gluster_reopen_commit, .bdrv_reopen_abort = qemu_gluster_reopen_abort, .bdrv_close = qemu_gluster_close, + .bdrv_co_create = qemu_gluster_co_create, .bdrv_co_create_opts = qemu_gluster_co_create_opts, .bdrv_getlength = qemu_gluster_getlength, .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, @@ -1464,6 +1508,7 @@ static BlockDriver bdrv_gluster_tcp = { .bdrv_reopen_commit = qemu_gluster_reopen_commit, .bdrv_reopen_abort = qemu_gluster_reopen_abort, .bdrv_close = qemu_gluster_close, + .bdrv_co_create = qemu_gluster_co_create, .bdrv_co_create_opts = qemu_gluster_co_create_opts, .bdrv_getlength = qemu_gluster_getlength, .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, @@ -1492,6 +1537,7 @@ static BlockDriver bdrv_gluster_unix = { .bdrv_reopen_commit = qemu_gluster_reopen_commit, .bdrv_reopen_abort = qemu_gluster_reopen_abort, .bdrv_close = qemu_gluster_close, + .bdrv_co_create = qemu_gluster_co_create, .bdrv_co_create_opts = qemu_gluster_co_create_opts, .bdrv_getlength = qemu_gluster_getlength, .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, @@ -1526,6 +1572,7 @@ static BlockDriver bdrv_gluster_rdma = { .bdrv_reopen_commit = qemu_gluster_reopen_commit, .bdrv_reopen_abort = qemu_gluster_reopen_abort, .bdrv_close = qemu_gluster_close, + .bdrv_co_create = qemu_gluster_co_create, .bdrv_co_create_opts = qemu_gluster_co_create_opts, .bdrv_getlength = qemu_gluster_getlength, .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, diff --git a/block/iscsi.c b/block/iscsi.c index 8bf0e87244..a82170f16e 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -2177,8 +2177,8 @@ static int iscsi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) return 0; } -static void iscsi_invalidate_cache(BlockDriverState *bs, - Error **errp) +static void coroutine_fn iscsi_co_invalidate_cache(BlockDriverState *bs, + Error **errp) { IscsiLun *iscsilun = bs->opaque; iscsi_allocmap_invalidate(iscsilun); @@ -2209,7 +2209,7 @@ static BlockDriver bdrv_iscsi = { .create_opts = &iscsi_create_opts, .bdrv_reopen_prepare = iscsi_reopen_prepare, .bdrv_reopen_commit = iscsi_reopen_commit, - .bdrv_invalidate_cache = iscsi_invalidate_cache, + .bdrv_co_invalidate_cache = iscsi_co_invalidate_cache, .bdrv_getlength = iscsi_getlength, .bdrv_get_info = iscsi_get_info, diff --git a/block/nfs.c b/block/nfs.c index 1d82ff5042..2577df4b26 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -367,49 +367,6 @@ static int coroutine_fn nfs_co_flush(BlockDriverState *bs) return task.ret; } -static QemuOptsList runtime_opts = { - .name = "nfs", - .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), - .desc = { - { - .name = "path", - .type = QEMU_OPT_STRING, - .help = "Path of the image on the host", - }, - { - .name = "user", - .type = QEMU_OPT_NUMBER, - .help = "UID value to use when talking to the server", - }, - { - .name = "group", - .type = QEMU_OPT_NUMBER, - .help = "GID value to use when talking to the server", - }, - { - .name = "tcp-syn-count", - .type = QEMU_OPT_NUMBER, - .help = "Number of SYNs to send during the session establish", - }, - { - .name = "readahead-size", - .type = QEMU_OPT_NUMBER, - .help = "Set the readahead size in bytes", - }, - { - .name = "page-cache-size", - .type = QEMU_OPT_NUMBER, - .help = "Set the pagecache size in bytes", - }, - { - .name = "debug", - .type = QEMU_OPT_NUMBER, - .help = "Set the NFS debug level (max 2)", - }, - { /* end of list */ } - }, -}; - static void nfs_detach_aio_context(BlockDriverState *bs) { NFSClient *client = bs->opaque; @@ -452,71 +409,16 @@ static void nfs_file_close(BlockDriverState *bs) nfs_client_close(client); } -static NFSServer *nfs_config(QDict *options, Error **errp) -{ - NFSServer *server = NULL; - QDict *addr = NULL; - QObject *crumpled_addr = NULL; - Visitor *iv = NULL; - Error *local_error = NULL; - - qdict_extract_subqdict(options, &addr, "server."); - if (!qdict_size(addr)) { - error_setg(errp, "NFS server address missing"); - goto out; - } - - crumpled_addr = qdict_crumple(addr, errp); - if (!crumpled_addr) { - goto out; - } - - /* - * Caution: this works only because all scalar members of - * NFSServer are QString in @crumpled_addr. The visitor expects - * @crumpled_addr to be typed according to the QAPI schema. It - * is when @options come from -blockdev or blockdev_add. But when - * they come from -drive, they're all QString. - */ - iv = qobject_input_visitor_new(crumpled_addr); - visit_type_NFSServer(iv, NULL, &server, &local_error); - if (local_error) { - error_propagate(errp, local_error); - goto out; - } - -out: - QDECREF(addr); - qobject_decref(crumpled_addr); - visit_free(iv); - return server; -} - - -static int64_t nfs_client_open(NFSClient *client, QDict *options, +static int64_t nfs_client_open(NFSClient *client, BlockdevOptionsNfs *opts, int flags, int open_flags, Error **errp) { int64_t ret = -EINVAL; - QemuOpts *opts = NULL; - Error *local_err = NULL; struct stat st; char *file = NULL, *strp = NULL; qemu_mutex_init(&client->mutex); - opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); - qemu_opts_absorb_qdict(opts, options, &local_err); - if (local_err) { - error_propagate(errp, local_err); - ret = -EINVAL; - goto fail; - } - client->path = g_strdup(qemu_opt_get(opts, "path")); - if (!client->path) { - ret = -EINVAL; - error_setg(errp, "No path was specified"); - goto fail; - } + client->path = g_strdup(opts->path); strp = strrchr(client->path, '/'); if (strp == NULL) { @@ -526,12 +428,10 @@ static int64_t nfs_client_open(NFSClient *client, QDict *options, file = g_strdup(strp); *strp = 0; - /* Pop the config into our state object, Exit if invalid */ - client->server = nfs_config(options, errp); - if (!client->server) { - ret = -EINVAL; - goto fail; - } + /* Steal the NFSServer object from opts; set the original pointer to NULL + * to avoid use after free and double free. */ + client->server = opts->server; + opts->server = NULL; client->context = nfs_init_context(); if (client->context == NULL) { @@ -539,29 +439,29 @@ static int64_t nfs_client_open(NFSClient *client, QDict *options, goto fail; } - if (qemu_opt_get(opts, "user")) { - client->uid = qemu_opt_get_number(opts, "user", 0); + if (opts->has_user) { + client->uid = opts->user; nfs_set_uid(client->context, client->uid); } - if (qemu_opt_get(opts, "group")) { - client->gid = qemu_opt_get_number(opts, "group", 0); + if (opts->has_group) { + client->gid = opts->group; nfs_set_gid(client->context, client->gid); } - if (qemu_opt_get(opts, "tcp-syn-count")) { - client->tcp_syncnt = qemu_opt_get_number(opts, "tcp-syn-count", 0); + if (opts->has_tcp_syn_count) { + client->tcp_syncnt = opts->tcp_syn_count; nfs_set_tcp_syncnt(client->context, client->tcp_syncnt); } #ifdef LIBNFS_FEATURE_READAHEAD - if (qemu_opt_get(opts, "readahead-size")) { + if (opts->has_readahead_size) { if (open_flags & BDRV_O_NOCACHE) { error_setg(errp, "Cannot enable NFS readahead " "if cache.direct = on"); goto fail; } - client->readahead = qemu_opt_get_number(opts, "readahead-size", 0); + client->readahead = opts->readahead_size; if (client->readahead > QEMU_NFS_MAX_READAHEAD_SIZE) { warn_report("Truncating NFS readahead size to %d", QEMU_NFS_MAX_READAHEAD_SIZE); @@ -576,13 +476,13 @@ static int64_t nfs_client_open(NFSClient *client, QDict *options, #endif #ifdef LIBNFS_FEATURE_PAGECACHE - if (qemu_opt_get(opts, "page-cache-size")) { + if (opts->has_page_cache_size) { if (open_flags & BDRV_O_NOCACHE) { error_setg(errp, "Cannot enable NFS pagecache " "if cache.direct = on"); goto fail; } - client->pagecache = qemu_opt_get_number(opts, "page-cache-size", 0); + client->pagecache = opts->page_cache_size; if (client->pagecache > QEMU_NFS_MAX_PAGECACHE_SIZE) { warn_report("Truncating NFS pagecache size to %d pages", QEMU_NFS_MAX_PAGECACHE_SIZE); @@ -595,8 +495,8 @@ static int64_t nfs_client_open(NFSClient *client, QDict *options, #endif #ifdef LIBNFS_FEATURE_DEBUG - if (qemu_opt_get(opts, "debug")) { - client->debug = qemu_opt_get_number(opts, "debug", 0); + if (opts->has_debug) { + client->debug = opts->debug; /* limit the maximum debug level to avoid potential flooding * of our log files. */ if (client->debug > QEMU_NFS_MAX_DEBUG_LEVEL) { @@ -647,11 +547,53 @@ static int64_t nfs_client_open(NFSClient *client, QDict *options, fail: nfs_client_close(client); out: - qemu_opts_del(opts); g_free(file); return ret; } +static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options, + Error **errp) +{ + BlockdevOptionsNfs *opts = NULL; + QObject *crumpled = NULL; + Visitor *v; + Error *local_err = NULL; + + crumpled = qdict_crumple(options, errp); + if (crumpled == NULL) { + return NULL; + } + + v = qobject_input_visitor_new_keyval(crumpled); + visit_type_BlockdevOptionsNfs(v, NULL, &opts, &local_err); + visit_free(v); + qobject_decref(crumpled); + + if (local_err) { + return NULL; + } + + return opts; +} + +static int64_t nfs_client_open_qdict(NFSClient *client, QDict *options, + int flags, int open_flags, Error **errp) +{ + BlockdevOptionsNfs *opts; + int ret; + + opts = nfs_options_qdict_to_qapi(options, errp); + if (opts == NULL) { + ret = -EINVAL; + goto fail; + } + + ret = nfs_client_open(client, opts, flags, open_flags, errp); +fail: + qapi_free_BlockdevOptionsNfs(opts); + return ret; +} + static int nfs_file_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { NFSClient *client = bs->opaque; @@ -659,9 +601,9 @@ static int nfs_file_open(BlockDriverState *bs, QDict *options, int flags, client->aio_context = bdrv_get_aio_context(bs); - ret = nfs_client_open(client, options, - (flags & BDRV_O_RDWR) ? O_RDWR : O_RDONLY, - bs->open_flags, errp); + ret = nfs_client_open_qdict(client, options, + (flags & BDRV_O_RDWR) ? O_RDWR : O_RDONLY, + bs->open_flags, errp); if (ret < 0) { return ret; } @@ -684,18 +626,43 @@ static QemuOptsList nfs_create_opts = { } }; -static int coroutine_fn nfs_file_co_create_opts(const char *url, QemuOpts *opts, - Error **errp) +static int nfs_file_co_create(BlockdevCreateOptions *options, Error **errp) { - int64_t ret, total_size; + BlockdevCreateOptionsNfs *opts = &options->u.nfs; NFSClient *client = g_new0(NFSClient, 1); - QDict *options = NULL; + int ret; + + assert(options->driver == BLOCKDEV_DRIVER_NFS); client->aio_context = qemu_get_aio_context(); + ret = nfs_client_open(client, opts->location, O_CREAT, 0, errp); + if (ret < 0) { + goto out; + } + ret = nfs_ftruncate(client->context, client->fh, opts->size); + nfs_client_close(client); + +out: + g_free(client); + return ret; +} + +static int coroutine_fn nfs_file_co_create_opts(const char *url, QemuOpts *opts, + Error **errp) +{ + BlockdevCreateOptions *create_options; + BlockdevCreateOptionsNfs *nfs_opts; + QDict *options; + int ret; + + create_options = g_new0(BlockdevCreateOptions, 1); + create_options->driver = BLOCKDEV_DRIVER_NFS; + nfs_opts = &create_options->u.nfs; + /* Read out options */ - total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), - BDRV_SECTOR_SIZE); + nfs_opts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); options = qdict_new(); ret = nfs_parse_uri(url, options, errp); @@ -703,15 +670,21 @@ static int coroutine_fn nfs_file_co_create_opts(const char *url, QemuOpts *opts, goto out; } - ret = nfs_client_open(client, options, O_CREAT, 0, errp); + nfs_opts->location = nfs_options_qdict_to_qapi(options, errp); + if (nfs_opts->location == NULL) { + ret = -EINVAL; + goto out; + } + + ret = nfs_file_co_create(create_options, errp); if (ret < 0) { goto out; } - ret = nfs_ftruncate(client->context, client->fh, total_size); - nfs_client_close(client); + + ret = 0; out: QDECREF(options); - g_free(client); + qapi_free_BlockdevCreateOptions(create_options); return ret; } @@ -876,8 +849,8 @@ static void nfs_refresh_filename(BlockDriverState *bs, QDict *options) } #ifdef LIBNFS_FEATURE_PAGECACHE -static void nfs_invalidate_cache(BlockDriverState *bs, - Error **errp) +static void coroutine_fn nfs_co_invalidate_cache(BlockDriverState *bs, + Error **errp) { NFSClient *client = bs->opaque; nfs_pagecache_invalidate(client->context, client->fh); @@ -898,6 +871,7 @@ static BlockDriver bdrv_nfs = { .bdrv_file_open = nfs_file_open, .bdrv_close = nfs_file_close, + .bdrv_co_create = nfs_file_co_create, .bdrv_co_create_opts = nfs_file_co_create_opts, .bdrv_reopen_prepare = nfs_reopen_prepare, @@ -910,7 +884,7 @@ static BlockDriver bdrv_nfs = { .bdrv_refresh_filename = nfs_refresh_filename, #ifdef LIBNFS_FEATURE_PAGECACHE - .bdrv_invalidate_cache = nfs_invalidate_cache, + .bdrv_co_invalidate_cache = nfs_co_invalidate_cache, #endif }; diff --git a/block/parallels.c b/block/parallels.c index 81085795c2..c13cb619e6 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -378,8 +378,9 @@ static coroutine_fn int parallels_co_readv(BlockDriverState *bs, } -static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res, - BdrvCheckMode fix) +static int coroutine_fn parallels_co_check(BlockDriverState *bs, + BdrvCheckResult *res, + BdrvCheckMode fix) { BDRVParallelsState *s = bs->opaque; int64_t size, prev_off, high_off; @@ -394,6 +395,7 @@ static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res, return size; } + qemu_co_mutex_lock(&s->lock); if (s->header_unclean) { fprintf(stderr, "%s image was not closed correctly\n", fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR"); @@ -442,11 +444,12 @@ static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res, prev_off = off; } + ret = 0; if (flush_bat) { ret = bdrv_pwrite_sync(bs->file, 0, s->header, s->header_size); if (ret < 0) { res->check_errors++; - return ret; + goto out; } } @@ -465,13 +468,15 @@ static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res, if (ret < 0) { error_report_err(local_err); res->check_errors++; - return ret; + goto out; } res->leaks_fixed += count; } } - return 0; +out: + qemu_co_mutex_unlock(&s->lock); + return ret; } @@ -799,7 +804,7 @@ static BlockDriver bdrv_parallels = { .bdrv_co_writev = parallels_co_writev, .supports_backing = true, .bdrv_co_create_opts = parallels_co_create_opts, - .bdrv_check = parallels_check, + .bdrv_co_check = parallels_co_check, .create_opts = ¶llels_create_opts, }; diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c index 5127276f90..3010adb909 100644 --- a/block/qcow2-bitmap.c +++ b/block/qcow2-bitmap.c @@ -110,7 +110,7 @@ static int update_header_sync(BlockDriverState *bs) return ret; } - return bdrv_flush(bs); + return bdrv_flush(bs->file->bs); } static inline void bitmap_table_to_be(uint64_t *bitmap_table, size_t size) @@ -882,7 +882,7 @@ static int update_ext_header_and_dir(BlockDriverState *bs, return ret; } - ret = bdrv_flush(bs->file->bs); + ret = qcow2_flush_caches(bs); if (ret < 0) { goto fail; } diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 98908c4264..1aee726c6a 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -25,6 +25,7 @@ #include "qemu/osdep.h" #include <zlib.h> +#include "qapi/error.h" #include "qemu-common.h" #include "block/block_int.h" #include "block/qcow2.h" @@ -2092,11 +2093,21 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs, } for (i = 0; i < s->nb_snapshots; i++) { - int l1_sectors = DIV_ROUND_UP(s->snapshots[i].l1_size * - sizeof(uint64_t), BDRV_SECTOR_SIZE); + int l1_size2; + uint64_t *new_l1_table; + Error *local_err = NULL; + + ret = qcow2_validate_table(bs, s->snapshots[i].l1_table_offset, + s->snapshots[i].l1_size, sizeof(uint64_t), + QCOW_MAX_L1_SIZE, "Snapshot L1 table", + &local_err); + if (ret < 0) { + error_report_err(local_err); + goto fail; + } - uint64_t *new_l1_table = - g_try_realloc(l1_table, l1_sectors * BDRV_SECTOR_SIZE); + l1_size2 = s->snapshots[i].l1_size * sizeof(uint64_t); + new_l1_table = g_try_realloc(l1_table, l1_size2); if (!new_l1_table) { ret = -ENOMEM; @@ -2105,9 +2116,8 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs, l1_table = new_l1_table; - ret = bdrv_read(bs->file, - s->snapshots[i].l1_table_offset / BDRV_SECTOR_SIZE, - (void *)l1_table, l1_sectors); + ret = bdrv_pread(bs->file, s->snapshots[i].l1_table_offset, + l1_table, l1_size2); if (ret < 0) { goto fail; } diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 126cca3276..362deaf303 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -1171,7 +1171,35 @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry, } } +int coroutine_fn qcow2_write_caches(BlockDriverState *bs) +{ + BDRVQcow2State *s = bs->opaque; + int ret; + + ret = qcow2_cache_write(bs, s->l2_table_cache); + if (ret < 0) { + return ret; + } + + if (qcow2_need_accurate_refcounts(s)) { + ret = qcow2_cache_write(bs, s->refcount_block_cache); + if (ret < 0) { + return ret; + } + } + + return 0; +} + +int coroutine_fn qcow2_flush_caches(BlockDriverState *bs) +{ + int ret = qcow2_write_caches(bs); + if (ret < 0) { + return ret; + } + return bdrv_flush(bs->file->bs); +} /*********************************************************/ /* snapshots and image creation */ @@ -2019,6 +2047,20 @@ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res, /* snapshots */ for (i = 0; i < s->nb_snapshots; i++) { sn = s->snapshots + i; + if (offset_into_cluster(s, sn->l1_table_offset)) { + fprintf(stderr, "ERROR snapshot %s (%s) l1_offset=%#" PRIx64 ": " + "L1 table is not cluster aligned; snapshot table entry " + "corrupted\n", sn->id_str, sn->name, sn->l1_table_offset); + res->corruptions++; + continue; + } + if (sn->l1_size > QCOW_MAX_L1_SIZE / sizeof(uint64_t)) { + fprintf(stderr, "ERROR snapshot %s (%s) l1_size=%#" PRIx32 ": " + "L1 table is too large; snapshot table entry corrupted\n", + sn->id_str, sn->name, sn->l1_size); + res->corruptions++; + continue; + } ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters, sn->l1_table_offset, sn->l1_size, 0, fix); if (ret < 0) { @@ -2614,9 +2656,17 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, uint64_t l1_ofs = s->snapshots[i].l1_table_offset; uint32_t l1_sz = s->snapshots[i].l1_size; uint64_t l1_sz2 = l1_sz * sizeof(uint64_t); - uint64_t *l1 = g_try_malloc(l1_sz2); + uint64_t *l1; int ret; + ret = qcow2_validate_table(bs, l1_ofs, l1_sz, sizeof(uint64_t), + QCOW_MAX_L1_SIZE, "", NULL); + if (ret < 0) { + return ret; + } + + l1 = g_try_malloc(l1_sz2); + if (l1_sz2 && l1 == NULL) { return -ENOMEM; } diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c index cee25f582b..74293be470 100644 --- a/block/qcow2-snapshot.c +++ b/block/qcow2-snapshot.c @@ -465,6 +465,7 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) { BDRVQcow2State *s = bs->opaque; QCowSnapshot *sn; + Error *local_err = NULL; int i, snapshot_index; int cur_l1_bytes, sn_l1_bytes; int ret; @@ -477,6 +478,14 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) } sn = &s->snapshots[snapshot_index]; + ret = qcow2_validate_table(bs, sn->l1_table_offset, sn->l1_size, + sizeof(uint64_t), QCOW_MAX_L1_SIZE, + "Snapshot L1 table", &local_err); + if (ret < 0) { + error_report_err(local_err); + goto fail; + } + if (sn->disk_size != bs->total_sectors * BDRV_SECTOR_SIZE) { error_report("qcow2: Loading snapshots with different disk " "size is not implemented"); @@ -602,6 +611,13 @@ int qcow2_snapshot_delete(BlockDriverState *bs, } sn = s->snapshots[snapshot_index]; + ret = qcow2_validate_table(bs, sn.l1_table_offset, sn.l1_size, + sizeof(uint64_t), QCOW_MAX_L1_SIZE, + "Snapshot L1 table", errp); + if (ret < 0) { + return ret; + } + /* Remove it from the snapshot list */ memmove(s->snapshots + snapshot_index, s->snapshots + snapshot_index + 1, @@ -704,9 +720,11 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs, sn = &s->snapshots[snapshot_index]; /* Allocate and read in the snapshot's L1 table */ - if (sn->l1_size > QCOW_MAX_L1_SIZE / sizeof(uint64_t)) { - error_setg(errp, "Snapshot L1 table too large"); - return -EFBIG; + ret = qcow2_validate_table(bs, sn->l1_table_offset, sn->l1_size, + sizeof(uint64_t), QCOW_MAX_L1_SIZE, + "Snapshot L1 table", errp); + if (ret < 0) { + return ret; } new_l1_bytes = sn->l1_size * sizeof(uint64_t); new_l1_table = qemu_try_blockalign(bs->file->bs, diff --git a/block/qcow2.c b/block/qcow2.c index 071dc4d608..7472af6931 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -37,7 +37,8 @@ #include "qemu/option_int.h" #include "qemu/cutils.h" #include "qemu/bswap.h" -#include "qapi/opts-visitor.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qapi-visit-block-core.h" #include "block/crypto.h" /* @@ -500,7 +501,7 @@ static int qcow2_mark_clean(BlockDriverState *bs) s->incompatible_features &= ~QCOW2_INCOMPAT_DIRTY; - ret = bdrv_flush(bs); + ret = qcow2_flush_caches(bs); if (ret < 0) { return ret; } @@ -530,7 +531,7 @@ int qcow2_mark_consistent(BlockDriverState *bs) BDRVQcow2State *s = bs->opaque; if (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT) { - int ret = bdrv_flush(bs); + int ret = qcow2_flush_caches(bs); if (ret < 0) { return ret; } @@ -541,8 +542,9 @@ int qcow2_mark_consistent(BlockDriverState *bs) return 0; } -static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result, - BdrvCheckMode fix) +static int coroutine_fn qcow2_co_check_locked(BlockDriverState *bs, + BdrvCheckResult *result, + BdrvCheckMode fix) { int ret = qcow2_check_refcounts(bs, result, fix); if (ret < 0) { @@ -559,26 +561,36 @@ static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result, return ret; } -static int validate_table_offset(BlockDriverState *bs, uint64_t offset, - uint64_t entries, size_t entry_len) +static int coroutine_fn qcow2_co_check(BlockDriverState *bs, + BdrvCheckResult *result, + BdrvCheckMode fix) { BDRVQcow2State *s = bs->opaque; - uint64_t size; + int ret; - /* Use signed INT64_MAX as the maximum even for uint64_t header fields, - * because values will be passed to qemu functions taking int64_t. */ - if (entries > INT64_MAX / entry_len) { - return -EINVAL; - } + qemu_co_mutex_lock(&s->lock); + ret = qcow2_co_check_locked(bs, result, fix); + qemu_co_mutex_unlock(&s->lock); + return ret; +} - size = entries * entry_len; +int qcow2_validate_table(BlockDriverState *bs, uint64_t offset, + uint64_t entries, size_t entry_len, + int64_t max_size_bytes, const char *table_name, + Error **errp) +{ + BDRVQcow2State *s = bs->opaque; - if (INT64_MAX - size < offset) { - return -EINVAL; + if (entries > max_size_bytes / entry_len) { + error_setg(errp, "%s too large", table_name); + return -EFBIG; } - /* Tables must be cluster aligned */ - if (offset_into_cluster(s, offset) != 0) { + /* Use signed INT64_MAX as the maximum even for uint64_t header fields, + * because values will be passed to qemu functions taking int64_t. */ + if ((INT64_MAX - entries * entry_len < offset) || + (offset_into_cluster(s, offset) != 0)) { + error_setg(errp, "%s offset invalid", table_name); return -EINVAL; } @@ -1118,8 +1130,9 @@ static int qcow2_update_options(BlockDriverState *bs, QDict *options, return ret; } -static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, - Error **errp) +/* Called with s->lock held. */ +static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, + int flags, Error **errp) { BDRVQcow2State *s = bs->opaque; unsigned int len, i; @@ -1308,47 +1321,42 @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, s->refcount_table_size = header.refcount_table_clusters << (s->cluster_bits - 3); - if (header.refcount_table_clusters > qcow2_max_refcount_clusters(s)) { - error_setg(errp, "Reference count table too large"); - ret = -EINVAL; - goto fail; - } - if (header.refcount_table_clusters == 0 && !(flags & BDRV_O_CHECK)) { error_setg(errp, "Image does not contain a reference count table"); ret = -EINVAL; goto fail; } - ret = validate_table_offset(bs, s->refcount_table_offset, - s->refcount_table_size, sizeof(uint64_t)); + ret = qcow2_validate_table(bs, s->refcount_table_offset, + header.refcount_table_clusters, + s->cluster_size, QCOW_MAX_REFTABLE_SIZE, + "Reference count table", errp); if (ret < 0) { - error_setg(errp, "Invalid reference count table offset"); goto fail; } - /* Snapshot table offset/length */ - if (header.nb_snapshots > QCOW_MAX_SNAPSHOTS) { - error_setg(errp, "Too many snapshots"); - ret = -EINVAL; - goto fail; - } - - ret = validate_table_offset(bs, header.snapshots_offset, - header.nb_snapshots, - sizeof(QCowSnapshotHeader)); + /* The total size in bytes of the snapshot table is checked in + * qcow2_read_snapshots() because the size of each snapshot is + * variable and we don't know it yet. + * Here we only check the offset and number of snapshots. */ + ret = qcow2_validate_table(bs, header.snapshots_offset, + header.nb_snapshots, + sizeof(QCowSnapshotHeader), + sizeof(QCowSnapshotHeader) * QCOW_MAX_SNAPSHOTS, + "Snapshot table", errp); if (ret < 0) { - error_setg(errp, "Invalid snapshot table offset"); goto fail; } /* read the level 1 table */ - if (header.l1_size > QCOW_MAX_L1_SIZE / sizeof(uint64_t)) { - error_setg(errp, "Active L1 table too large"); - ret = -EFBIG; + ret = qcow2_validate_table(bs, header.l1_table_offset, + header.l1_size, sizeof(uint64_t), + QCOW_MAX_L1_SIZE, "Active L1 table", errp); + if (ret < 0) { goto fail; } s->l1_size = header.l1_size; + s->l1_table_offset = header.l1_table_offset; l1_vm_state_index = size_to_l1(s, header.size); if (l1_vm_state_index > INT_MAX) { @@ -1366,15 +1374,6 @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - ret = validate_table_offset(bs, header.l1_table_offset, - header.l1_size, sizeof(uint64_t)); - if (ret < 0) { - error_setg(errp, "Invalid L1 table offset"); - goto fail; - } - s->l1_table_offset = header.l1_table_offset; - - if (s->l1_size > 0) { s->l1_table = qemu_try_blockalign(bs->file->bs, ROUND_UP(s->l1_size * sizeof(uint64_t), 512)); @@ -1498,8 +1497,6 @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, } } - /* Initialise locks */ - qemu_co_mutex_init(&s->lock); bs->supported_zero_flags = header.version >= 3 ? BDRV_REQ_MAY_UNMAP : 0; /* Repair image if dirty */ @@ -1507,7 +1504,8 @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, (s->incompatible_features & QCOW2_INCOMPAT_DIRTY)) { BdrvCheckResult result = {0}; - ret = qcow2_check(bs, &result, BDRV_FIX_ERRORS | BDRV_FIX_LEAKS); + ret = qcow2_co_check_locked(bs, &result, + BDRV_FIX_ERRORS | BDRV_FIX_LEAKS); if (ret < 0 || result.check_errors) { if (ret >= 0) { ret = -EIO; @@ -1545,16 +1543,53 @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, return ret; } +typedef struct QCow2OpenCo { + BlockDriverState *bs; + QDict *options; + int flags; + Error **errp; + int ret; +} QCow2OpenCo; + +static void coroutine_fn qcow2_open_entry(void *opaque) +{ + QCow2OpenCo *qoc = opaque; + BDRVQcow2State *s = qoc->bs->opaque; + + qemu_co_mutex_lock(&s->lock); + qoc->ret = qcow2_do_open(qoc->bs, qoc->options, qoc->flags, qoc->errp); + qemu_co_mutex_unlock(&s->lock); +} + static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { + BDRVQcow2State *s = bs->opaque; + QCow2OpenCo qoc = { + .bs = bs, + .options = options, + .flags = flags, + .errp = errp, + .ret = -EINPROGRESS + }; + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false, errp); if (!bs->file) { return -EINVAL; } - return qcow2_do_open(bs, options, flags, errp); + /* Initialise locks */ + qemu_co_mutex_init(&s->lock); + + if (qemu_in_coroutine()) { + /* From bdrv_co_create. */ + qcow2_open_entry(&qoc); + } else { + qemu_coroutine_enter(qemu_coroutine_create(qcow2_open_entry, &qoc)); + BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS); + } + return qoc.ret; } static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp) @@ -2106,7 +2141,8 @@ static void qcow2_close(BlockDriverState *bs) qcow2_free_snapshots(bs); } -static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp) +static void coroutine_fn qcow2_co_invalidate_cache(BlockDriverState *bs, + Error **errp) { BDRVQcow2State *s = bs->opaque; int flags = s->flags; @@ -2129,7 +2165,9 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp) options = qdict_clone_shallow(bs->options); flags &= ~BDRV_O_INACTIVE; + qemu_co_mutex_lock(&s->lock); ret = qcow2_do_open(bs, options, flags, &local_err); + qemu_co_mutex_unlock(&s->lock); QDECREF(options); if (local_err) { error_propagate(errp, local_err); @@ -2412,39 +2450,26 @@ static int qcow2_crypt_method_from_format(const char *encryptfmt) } } -static int qcow2_set_up_encryption(BlockDriverState *bs, const char *encryptfmt, - QemuOpts *opts, Error **errp) +static int qcow2_set_up_encryption(BlockDriverState *bs, + QCryptoBlockCreateOptions *cryptoopts, + Error **errp) { BDRVQcow2State *s = bs->opaque; - QCryptoBlockCreateOptions *cryptoopts = NULL; QCryptoBlock *crypto = NULL; - int ret = -EINVAL; - QDict *options, *encryptopts; - int fmt; - - options = qemu_opts_to_qdict(opts, NULL); - qdict_extract_subqdict(options, &encryptopts, "encrypt."); - QDECREF(options); + int fmt, ret; - fmt = qcow2_crypt_method_from_format(encryptfmt); - - switch (fmt) { - case QCOW_CRYPT_LUKS: - cryptoopts = block_crypto_create_opts_init( - Q_CRYPTO_BLOCK_FORMAT_LUKS, encryptopts, errp); + switch (cryptoopts->format) { + case Q_CRYPTO_BLOCK_FORMAT_LUKS: + fmt = QCOW_CRYPT_LUKS; break; - case QCOW_CRYPT_AES: - cryptoopts = block_crypto_create_opts_init( - Q_CRYPTO_BLOCK_FORMAT_QCOW, encryptopts, errp); + case Q_CRYPTO_BLOCK_FORMAT_QCOW: + fmt = QCOW_CRYPT_AES; break; default: - error_setg(errp, "Unknown encryption format '%s'", encryptfmt); - break; - } - if (!cryptoopts) { - ret = -EINVAL; - goto out; + error_setg(errp, "Crypto format not supported in qcow2"); + return -EINVAL; } + s->crypt_method_header = fmt; crypto = qcrypto_block_create(cryptoopts, "encrypt.", @@ -2452,8 +2477,7 @@ static int qcow2_set_up_encryption(BlockDriverState *bs, const char *encryptfmt, qcow2_crypto_hdr_write_func, bs, errp); if (!crypto) { - ret = -EINVAL; - goto out; + return -EINVAL; } ret = qcow2_update_header(bs); @@ -2462,10 +2486,9 @@ static int qcow2_set_up_encryption(BlockDriverState *bs, const char *encryptfmt, goto out; } + ret = 0; out: - QDECREF(encryptopts); qcrypto_block_free(crypto); - qapi_free_QCryptoBlockCreateOptions(cryptoopts); return ret; } @@ -2663,19 +2686,26 @@ static int64_t qcow2_calc_prealloc_size(int64_t total_size, return meta_size + aligned_total_size; } -static size_t qcow2_opt_get_cluster_size_del(QemuOpts *opts, Error **errp) +static bool validate_cluster_size(size_t cluster_size, Error **errp) { - size_t cluster_size; - int cluster_bits; - - cluster_size = qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE, - DEFAULT_CLUSTER_SIZE); - cluster_bits = ctz32(cluster_size); + int cluster_bits = ctz32(cluster_size); if (cluster_bits < MIN_CLUSTER_BITS || cluster_bits > MAX_CLUSTER_BITS || (1 << cluster_bits) != cluster_size) { error_setg(errp, "Cluster size must be a power of two between %d and " "%dk", 1 << MIN_CLUSTER_BITS, 1 << (MAX_CLUSTER_BITS - 10)); + return false; + } + return true; +} + +static size_t qcow2_opt_get_cluster_size_del(QemuOpts *opts, Error **errp) +{ + size_t cluster_size; + + cluster_size = qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE, + DEFAULT_CLUSTER_SIZE); + if (!validate_cluster_size(cluster_size, errp)) { return 0; } return cluster_size; @@ -2724,12 +2754,9 @@ static uint64_t qcow2_opt_get_refcount_bits_del(QemuOpts *opts, int version, } static int coroutine_fn -qcow2_co_create2(const char *filename, int64_t total_size, - const char *backing_file, const char *backing_format, - int flags, size_t cluster_size, PreallocMode prealloc, - QemuOpts *opts, int version, int refcount_order, - const char *encryptfmt, Error **errp) +qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) { + BlockdevCreateOptionsQcow2 *qcow2_opts; QDict *options; /* @@ -2744,36 +2771,132 @@ qcow2_co_create2(const char *filename, int64_t total_size, * 2 GB for 64k clusters, and we don't want to have a 2 GB initial file * size for any qcow2 image. */ - BlockBackend *blk; + BlockBackend *blk = NULL; + BlockDriverState *bs = NULL; QCowHeader *header; + size_t cluster_size; + int version; + int refcount_order; uint64_t* refcount_table; Error *local_err = NULL; int ret; - if (prealloc == PREALLOC_MODE_FULL || prealloc == PREALLOC_MODE_FALLOC) { - int64_t prealloc_size = - qcow2_calc_prealloc_size(total_size, cluster_size, refcount_order); - qemu_opt_set_number(opts, BLOCK_OPT_SIZE, prealloc_size, &error_abort); - qemu_opt_set(opts, BLOCK_OPT_PREALLOC, PreallocMode_str(prealloc), - &error_abort); + assert(create_options->driver == BLOCKDEV_DRIVER_QCOW2); + qcow2_opts = &create_options->u.qcow2; + + bs = bdrv_open_blockdev_ref(qcow2_opts->file, errp); + if (bs == NULL) { + return -EIO; } - ret = bdrv_create_file(filename, opts, &local_err); - if (ret < 0) { - error_propagate(errp, local_err); - return ret; + /* Validate options and set default values */ + if (!QEMU_IS_ALIGNED(qcow2_opts->size, BDRV_SECTOR_SIZE)) { + error_setg(errp, "Image size must be a multiple of 512 bytes"); + ret = -EINVAL; + goto out; } - blk = blk_new_open(filename, NULL, NULL, - BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, - &local_err); - if (blk == NULL) { - error_propagate(errp, local_err); - return -EIO; + if (qcow2_opts->has_version) { + switch (qcow2_opts->version) { + case BLOCKDEV_QCOW2_VERSION_V2: + version = 2; + break; + case BLOCKDEV_QCOW2_VERSION_V3: + version = 3; + break; + default: + g_assert_not_reached(); + } + } else { + version = 3; + } + + if (qcow2_opts->has_cluster_size) { + cluster_size = qcow2_opts->cluster_size; + } else { + cluster_size = DEFAULT_CLUSTER_SIZE; + } + + if (!validate_cluster_size(cluster_size, errp)) { + ret = -EINVAL; + goto out; + } + + if (!qcow2_opts->has_preallocation) { + qcow2_opts->preallocation = PREALLOC_MODE_OFF; + } + if (qcow2_opts->has_backing_file && + qcow2_opts->preallocation != PREALLOC_MODE_OFF) + { + error_setg(errp, "Backing file and preallocation cannot be used at " + "the same time"); + ret = -EINVAL; + goto out; + } + if (qcow2_opts->has_backing_fmt && !qcow2_opts->has_backing_file) { + error_setg(errp, "Backing format cannot be used without backing file"); + ret = -EINVAL; + goto out; + } + + if (!qcow2_opts->has_lazy_refcounts) { + qcow2_opts->lazy_refcounts = false; + } + if (version < 3 && qcow2_opts->lazy_refcounts) { + error_setg(errp, "Lazy refcounts only supported with compatibility " + "level 1.1 and above (use version=v3 or greater)"); + ret = -EINVAL; + goto out; } + if (!qcow2_opts->has_refcount_bits) { + qcow2_opts->refcount_bits = 16; + } + if (qcow2_opts->refcount_bits > 64 || + !is_power_of_2(qcow2_opts->refcount_bits)) + { + error_setg(errp, "Refcount width must be a power of two and may not " + "exceed 64 bits"); + ret = -EINVAL; + goto out; + } + if (version < 3 && qcow2_opts->refcount_bits != 16) { + error_setg(errp, "Different refcount widths than 16 bits require " + "compatibility level 1.1 or above (use version=v3 or " + "greater)"); + ret = -EINVAL; + goto out; + } + refcount_order = ctz32(qcow2_opts->refcount_bits); + + + /* Create BlockBackend to write to the image */ + blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL); + ret = blk_insert_bs(blk, bs, errp); + if (ret < 0) { + goto out; + } blk_set_allow_write_beyond_eof(blk, true); + /* Clear the protocol layer and preallocate it if necessary */ + ret = blk_truncate(blk, 0, PREALLOC_MODE_OFF, errp); + if (ret < 0) { + goto out; + } + + if (qcow2_opts->preallocation == PREALLOC_MODE_FULL || + qcow2_opts->preallocation == PREALLOC_MODE_FALLOC) + { + int64_t prealloc_size = + qcow2_calc_prealloc_size(qcow2_opts->size, cluster_size, + refcount_order); + + ret = blk_truncate(blk, prealloc_size, qcow2_opts->preallocation, errp); + if (ret < 0) { + goto out; + } + } + /* Write the header */ QEMU_BUILD_BUG_ON((1 << MIN_CLUSTER_BITS) < sizeof(*header)); header = g_malloc0(cluster_size); @@ -2793,7 +2916,7 @@ qcow2_co_create2(const char *filename, int64_t total_size, /* We'll update this to correct value later */ header->crypt_method = cpu_to_be32(QCOW_CRYPT_NONE); - if (flags & BLOCK_FLAG_LAZY_REFCOUNTS) { + if (qcow2_opts->lazy_refcounts) { header->compatible_features |= cpu_to_be64(QCOW2_COMPAT_LAZY_REFCOUNTS); } @@ -2826,7 +2949,8 @@ qcow2_co_create2(const char *filename, int64_t total_size, */ options = qdict_new(); qdict_put_str(options, "driver", "qcow2"); - blk = blk_new_open(filename, NULL, options, + qdict_put_str(options, "file", bs->node_name); + blk = blk_new_open(NULL, NULL, options, BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_NO_FLUSH, &local_err); if (blk == NULL) { @@ -2854,33 +2978,41 @@ qcow2_co_create2(const char *filename, int64_t total_size, } /* Okay, now that we have a valid image, let's give it the right size */ - ret = blk_truncate(blk, total_size, PREALLOC_MODE_OFF, errp); + ret = blk_truncate(blk, qcow2_opts->size, PREALLOC_MODE_OFF, errp); if (ret < 0) { error_prepend(errp, "Could not resize image: "); goto out; } /* Want a backing file? There you go.*/ - if (backing_file) { - ret = bdrv_change_backing_file(blk_bs(blk), backing_file, backing_format); + if (qcow2_opts->has_backing_file) { + const char *backing_format = NULL; + + if (qcow2_opts->has_backing_fmt) { + backing_format = BlockdevDriver_str(qcow2_opts->backing_fmt); + } + + ret = bdrv_change_backing_file(blk_bs(blk), qcow2_opts->backing_file, + backing_format); if (ret < 0) { error_setg_errno(errp, -ret, "Could not assign backing file '%s' " - "with format '%s'", backing_file, backing_format); + "with format '%s'", qcow2_opts->backing_file, + backing_format); goto out; } } /* Want encryption? There you go. */ - if (encryptfmt) { - ret = qcow2_set_up_encryption(blk_bs(blk), encryptfmt, opts, errp); + if (qcow2_opts->has_encrypt) { + ret = qcow2_set_up_encryption(blk_bs(blk), qcow2_opts->encrypt, errp); if (ret < 0) { goto out; } } /* And if we're supposed to preallocate metadata, do that now */ - if (prealloc != PREALLOC_MODE_OFF) { - ret = preallocate(blk_bs(blk), 0, total_size); + if (qcow2_opts->preallocation != PREALLOC_MODE_OFF) { + ret = preallocate(blk_bs(blk), 0, qcow2_opts->size); if (ret < 0) { error_setg_errno(errp, -ret, "Could not preallocate metadata"); goto out; @@ -2898,7 +3030,8 @@ qcow2_co_create2(const char *filename, int64_t total_size, */ options = qdict_new(); qdict_put_str(options, "driver", "qcow2"); - blk = blk_new_open(filename, NULL, options, + qdict_put_str(options, "file", bs->node_name); + blk = blk_new_open(NULL, NULL, options, BDRV_O_RDWR | BDRV_O_NO_BACKING | BDRV_O_NO_IO, &local_err); if (blk == NULL) { @@ -2909,104 +3042,120 @@ qcow2_co_create2(const char *filename, int64_t total_size, ret = 0; out: - if (blk) { - blk_unref(blk); - } + blk_unref(blk); + bdrv_unref(bs); return ret; } static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opts, Error **errp) { - char *backing_file = NULL; - char *backing_fmt = NULL; - char *buf = NULL; - uint64_t size = 0; - int flags = 0; - size_t cluster_size = DEFAULT_CLUSTER_SIZE; - PreallocMode prealloc; - int version; - uint64_t refcount_bits; - int refcount_order; - char *encryptfmt = NULL; + BlockdevCreateOptions *create_options = NULL; + QDict *qdict = NULL; + QObject *qobj; + Visitor *v; + BlockDriverState *bs = NULL; Error *local_err = NULL; + const char *val; int ret; - /* Read out options */ - size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), - BDRV_SECTOR_SIZE); - backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE); - backing_fmt = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FMT); - encryptfmt = qemu_opt_get_del(opts, BLOCK_OPT_ENCRYPT_FORMAT); - if (encryptfmt) { - if (qemu_opt_get(opts, BLOCK_OPT_ENCRYPT)) { - error_setg(errp, "Options " BLOCK_OPT_ENCRYPT " and " - BLOCK_OPT_ENCRYPT_FORMAT " are mutually exclusive"); - ret = -EINVAL; - goto finish; - } - } else if (qemu_opt_get_bool_del(opts, BLOCK_OPT_ENCRYPT, false)) { - encryptfmt = g_strdup("aes"); - } - cluster_size = qcow2_opt_get_cluster_size_del(opts, &local_err); - if (local_err) { - error_propagate(errp, local_err); + /* Only the keyval visitor supports the dotted syntax needed for + * encryption, so go through a QDict before getting a QAPI type. Ignore + * options meant for the protocol layer so that the visitor doesn't + * complain. */ + qdict = qemu_opts_to_qdict_filtered(opts, NULL, bdrv_qcow2.create_opts, + true); + + /* Handle encryption options */ + val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT); + if (val && !strcmp(val, "on")) { + qdict_put_str(qdict, BLOCK_OPT_ENCRYPT, "qcow"); + } else if (val && !strcmp(val, "off")) { + qdict_del(qdict, BLOCK_OPT_ENCRYPT); + } + + val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT_FORMAT); + if (val && !strcmp(val, "aes")) { + qdict_put_str(qdict, BLOCK_OPT_ENCRYPT_FORMAT, "qcow"); + } + + /* Convert compat=0.10/1.1 into compat=v2/v3, to be renamed into + * version=v2/v3 below. */ + val = qdict_get_try_str(qdict, BLOCK_OPT_COMPAT_LEVEL); + if (val && !strcmp(val, "0.10")) { + qdict_put_str(qdict, BLOCK_OPT_COMPAT_LEVEL, "v2"); + } else if (val && !strcmp(val, "1.1")) { + qdict_put_str(qdict, BLOCK_OPT_COMPAT_LEVEL, "v3"); + } + + /* Change legacy command line options into QMP ones */ + static const QDictRenames opt_renames[] = { + { BLOCK_OPT_BACKING_FILE, "backing-file" }, + { BLOCK_OPT_BACKING_FMT, "backing-fmt" }, + { BLOCK_OPT_CLUSTER_SIZE, "cluster-size" }, + { BLOCK_OPT_LAZY_REFCOUNTS, "lazy-refcounts" }, + { BLOCK_OPT_REFCOUNT_BITS, "refcount-bits" }, + { BLOCK_OPT_ENCRYPT, BLOCK_OPT_ENCRYPT_FORMAT }, + { BLOCK_OPT_COMPAT_LEVEL, "version" }, + { NULL, NULL }, + }; + + if (!qdict_rename_keys(qdict, opt_renames, errp)) { ret = -EINVAL; goto finish; } - buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC); - prealloc = qapi_enum_parse(&PreallocMode_lookup, buf, - PREALLOC_MODE_OFF, &local_err); - if (local_err) { - error_propagate(errp, local_err); - ret = -EINVAL; + + /* Create and open the file (protocol layer) */ + ret = bdrv_create_file(filename, opts, errp); + if (ret < 0) { goto finish; } - version = qcow2_opt_get_version_del(opts, &local_err); - if (local_err) { - error_propagate(errp, local_err); - ret = -EINVAL; + bs = bdrv_open(filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); + if (bs == NULL) { + ret = -EIO; goto finish; } - if (qemu_opt_get_bool_del(opts, BLOCK_OPT_LAZY_REFCOUNTS, false)) { - flags |= BLOCK_FLAG_LAZY_REFCOUNTS; - } + /* Set 'driver' and 'node' options */ + qdict_put_str(qdict, "driver", "qcow2"); + qdict_put_str(qdict, "file", bs->node_name); - if (backing_file && prealloc != PREALLOC_MODE_OFF) { - error_setg(errp, "Backing file and preallocation cannot be used at " - "the same time"); + /* Now get the QAPI type BlockdevCreateOptions */ + qobj = qdict_crumple(qdict, errp); + QDECREF(qdict); + qdict = qobject_to_qdict(qobj); + if (qdict == NULL) { ret = -EINVAL; goto finish; } - if (version < 3 && (flags & BLOCK_FLAG_LAZY_REFCOUNTS)) { - error_setg(errp, "Lazy refcounts only supported with compatibility " - "level 1.1 and above (use compat=1.1 or greater)"); - ret = -EINVAL; - goto finish; - } + v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); + visit_free(v); - refcount_bits = qcow2_opt_get_refcount_bits_del(opts, version, &local_err); if (local_err) { error_propagate(errp, local_err); ret = -EINVAL; goto finish; } - refcount_order = ctz32(refcount_bits); + /* Silently round up size */ + create_options->u.qcow2.size = ROUND_UP(create_options->u.qcow2.size, + BDRV_SECTOR_SIZE); - ret = qcow2_co_create2(filename, size, backing_file, backing_fmt, flags, - cluster_size, prealloc, opts, version, refcount_order, - encryptfmt, &local_err); - error_propagate(errp, local_err); + /* Create the qcow2 image (format layer) */ + ret = qcow2_co_create(create_options, errp); + if (ret < 0) { + goto finish; + } + ret = 0; finish: - g_free(backing_file); - g_free(backing_fmt); - g_free(encryptfmt); - g_free(buf); + QDECREF(qdict); + bdrv_unref(bs); + qapi_free_BlockdevCreateOptions(create_options); return ret; } @@ -3647,22 +3796,10 @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs) int ret; qemu_co_mutex_lock(&s->lock); - ret = qcow2_cache_write(bs, s->l2_table_cache); - if (ret < 0) { - qemu_co_mutex_unlock(&s->lock); - return ret; - } - - if (qcow2_need_accurate_refcounts(s)) { - ret = qcow2_cache_write(bs, s->refcount_block_cache); - if (ret < 0) { - qemu_co_mutex_unlock(&s->lock); - return ret; - } - } + ret = qcow2_write_caches(bs); qemu_co_mutex_unlock(&s->lock); - return 0; + return ret; } static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, @@ -4353,6 +4490,7 @@ BlockDriver bdrv_qcow2 = { .bdrv_join_options = qcow2_join_options, .bdrv_child_perm = bdrv_format_default_perms, .bdrv_co_create_opts = qcow2_co_create_opts, + .bdrv_co_create = qcow2_co_create, .bdrv_has_zero_init = bdrv_has_zero_init_1, .bdrv_co_block_status = qcow2_co_block_status, @@ -4382,11 +4520,11 @@ BlockDriver bdrv_qcow2 = { .bdrv_change_backing_file = qcow2_change_backing_file, .bdrv_refresh_limits = qcow2_refresh_limits, - .bdrv_invalidate_cache = qcow2_invalidate_cache, + .bdrv_co_invalidate_cache = qcow2_co_invalidate_cache, .bdrv_inactivate = qcow2_inactivate, .create_opts = &qcow2_create_opts, - .bdrv_check = qcow2_check, + .bdrv_co_check = qcow2_co_check, .bdrv_amend_options = qcow2_amend_options, .bdrv_detach_aio_context = qcow2_detach_aio_context, diff --git a/block/qcow2.h b/block/qcow2.h index 1a84cc77b0..ccb92a9696 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -485,11 +485,6 @@ static inline int64_t qcow2_vm_state_offset(BDRVQcow2State *s) return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits); } -static inline uint64_t qcow2_max_refcount_clusters(BDRVQcow2State *s) -{ - return QCOW_MAX_REFTABLE_SIZE >> s->cluster_bits; -} - static inline QCow2ClusterType qcow2_get_cluster_type(uint64_t l2_entry) { if (l2_entry & QCOW_OFLAG_COMPRESSED) { @@ -547,6 +542,11 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset, int64_t size, const char *message_format, ...) GCC_FMT_ATTR(5, 6); +int qcow2_validate_table(BlockDriverState *bs, uint64_t offset, + uint64_t entries, size_t entry_len, + int64_t max_size_bytes, const char *table_name, + Error **errp); + /* qcow2-refcount.c functions */ int qcow2_refcount_init(BlockDriverState *bs); void qcow2_refcount_close(BlockDriverState *bs); @@ -576,6 +576,8 @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry, int qcow2_update_snapshot_refcount(BlockDriverState *bs, int64_t l1_table_offset, int l1_size, int addend); +int coroutine_fn qcow2_flush_caches(BlockDriverState *bs); +int coroutine_fn qcow2_write_caches(BlockDriverState *bs); int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix); diff --git a/block/qed-check.c b/block/qed-check.c index dcd4f036b8..0edac03159 100644 --- a/block/qed-check.c +++ b/block/qed-check.c @@ -217,6 +217,7 @@ static void qed_check_mark_clean(BDRVQEDState *s, BdrvCheckResult *result) qed_write_header_sync(s); } +/* Called with table_lock held. */ int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix) { QEDCheck check = { diff --git a/block/qed-table.c b/block/qed-table.c index eead8b0fc7..7df5680adb 100644 --- a/block/qed-table.c +++ b/block/qed-table.c @@ -18,7 +18,7 @@ #include "qed.h" #include "qemu/bswap.h" -/* Called either from qed_check or with table_lock held. */ +/* Called with table_lock held. */ static int qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table) { QEMUIOVector qiov; @@ -33,13 +33,9 @@ static int qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table) trace_qed_read_table(s, offset, table); - if (qemu_in_coroutine()) { - qemu_co_mutex_unlock(&s->table_lock); - } + qemu_co_mutex_unlock(&s->table_lock); ret = bdrv_preadv(s->bs->file, offset, &qiov); - if (qemu_in_coroutine()) { - qemu_co_mutex_lock(&s->table_lock); - } + qemu_co_mutex_lock(&s->table_lock); if (ret < 0) { goto out; } @@ -67,7 +63,7 @@ out: * @n: Number of elements * @flush: Whether or not to sync to disk * - * Called either from qed_check or with table_lock held. + * Called with table_lock held. */ static int qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table, unsigned int index, unsigned int n, bool flush) @@ -104,13 +100,9 @@ static int qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table, /* Adjust for offset into table */ offset += start * sizeof(uint64_t); - if (qemu_in_coroutine()) { - qemu_co_mutex_unlock(&s->table_lock); - } + qemu_co_mutex_unlock(&s->table_lock); ret = bdrv_pwritev(s->bs->file, offset, &qiov); - if (qemu_in_coroutine()) { - qemu_co_mutex_lock(&s->table_lock); - } + qemu_co_mutex_lock(&s->table_lock); trace_qed_write_table_cb(s, table, flush, ret); if (ret < 0) { goto out; @@ -134,7 +126,7 @@ int qed_read_l1_table_sync(BDRVQEDState *s) return qed_read_table(s, s->header.l1_table_offset, s->l1_table); } -/* Called either from qed_check or with table_lock held. */ +/* Called with table_lock held. */ int qed_write_l1_table(BDRVQEDState *s, unsigned int index, unsigned int n) { BLKDBG_EVENT(s->bs->file, BLKDBG_L1_UPDATE); @@ -148,7 +140,7 @@ int qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index, return qed_write_l1_table(s, index, n); } -/* Called either from qed_check or with table_lock held. */ +/* Called with table_lock held. */ int qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset) { int ret; @@ -191,7 +183,7 @@ int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request, uint64_t offset return qed_read_l2_table(s, request, offset); } -/* Called either from qed_check or with table_lock held. */ +/* Called with table_lock held. */ int qed_write_l2_table(BDRVQEDState *s, QEDRequest *request, unsigned int index, unsigned int n, bool flush) { diff --git a/block/qed.c b/block/qed.c index 72cf2f58ab..5e6a6bfaa0 100644 --- a/block/qed.c +++ b/block/qed.c @@ -381,8 +381,9 @@ static void bdrv_qed_init_state(BlockDriverState *bs) qemu_co_queue_init(&s->allocating_write_reqs); } -static int bdrv_qed_do_open(BlockDriverState *bs, QDict *options, int flags, - Error **errp) +/* Called with table_lock held. */ +static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, + int flags, Error **errp) { BDRVQEDState *s = bs->opaque; QEDHeader le_header; @@ -513,9 +514,35 @@ out: return ret; } +typedef struct QEDOpenCo { + BlockDriverState *bs; + QDict *options; + int flags; + Error **errp; + int ret; +} QEDOpenCo; + +static void coroutine_fn bdrv_qed_open_entry(void *opaque) +{ + QEDOpenCo *qoc = opaque; + BDRVQEDState *s = qoc->bs->opaque; + + qemu_co_mutex_lock(&s->table_lock); + qoc->ret = bdrv_qed_do_open(qoc->bs, qoc->options, qoc->flags, qoc->errp); + qemu_co_mutex_unlock(&s->table_lock); +} + static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { + QEDOpenCo qoc = { + .bs = bs, + .options = options, + .flags = flags, + .errp = errp, + .ret = -EINPROGRESS + }; + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false, errp); if (!bs->file) { @@ -523,7 +550,14 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, } bdrv_qed_init_state(bs); - return bdrv_qed_do_open(bs, options, flags, errp); + if (qemu_in_coroutine()) { + bdrv_qed_open_entry(&qoc); + } else { + qemu_coroutine_enter(qemu_coroutine_create(bdrv_qed_open_entry, &qoc)); + BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS); + } + BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS); + return qoc.ret; } static void bdrv_qed_refresh_limits(BlockDriverState *bs, Error **errp) @@ -1487,7 +1521,8 @@ static int bdrv_qed_change_backing_file(BlockDriverState *bs, return ret; } -static void bdrv_qed_invalidate_cache(BlockDriverState *bs, Error **errp) +static void coroutine_fn bdrv_qed_co_invalidate_cache(BlockDriverState *bs, + Error **errp) { BDRVQEDState *s = bs->opaque; Error *local_err = NULL; @@ -1496,13 +1531,9 @@ static void bdrv_qed_invalidate_cache(BlockDriverState *bs, Error **errp) bdrv_qed_close(bs); bdrv_qed_init_state(bs); - if (qemu_in_coroutine()) { - qemu_co_mutex_lock(&s->table_lock); - } + qemu_co_mutex_lock(&s->table_lock); ret = bdrv_qed_do_open(bs, NULL, bs->open_flags, &local_err); - if (qemu_in_coroutine()) { - qemu_co_mutex_unlock(&s->table_lock); - } + qemu_co_mutex_unlock(&s->table_lock); if (local_err) { error_propagate(errp, local_err); error_prepend(errp, "Could not reopen qed layer: "); @@ -1513,12 +1544,17 @@ static void bdrv_qed_invalidate_cache(BlockDriverState *bs, Error **errp) } } -static int bdrv_qed_check(BlockDriverState *bs, BdrvCheckResult *result, - BdrvCheckMode fix) +static int bdrv_qed_co_check(BlockDriverState *bs, BdrvCheckResult *result, + BdrvCheckMode fix) { BDRVQEDState *s = bs->opaque; + int ret; + + qemu_co_mutex_lock(&s->table_lock); + ret = qed_check(s, result, !!fix); + qemu_co_mutex_unlock(&s->table_lock); - return qed_check(s, result, !!fix); + return ret; } static QemuOptsList qed_create_opts = { @@ -1577,8 +1613,8 @@ static BlockDriver bdrv_qed = { .bdrv_get_info = bdrv_qed_get_info, .bdrv_refresh_limits = bdrv_qed_refresh_limits, .bdrv_change_backing_file = bdrv_qed_change_backing_file, - .bdrv_invalidate_cache = bdrv_qed_invalidate_cache, - .bdrv_check = bdrv_qed_check, + .bdrv_co_invalidate_cache = bdrv_qed_co_invalidate_cache, + .bdrv_co_check = bdrv_qed_co_check, .bdrv_detach_aio_context = bdrv_qed_detach_aio_context, .bdrv_attach_aio_context = bdrv_qed_attach_aio_context, .bdrv_co_drain_begin = bdrv_qed_co_drain_begin, diff --git a/block/rbd.c b/block/rbd.c index c7dd32e213..294ed07ac4 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -24,6 +24,8 @@ #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qlist.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qapi-visit-block-core.h" /* * When specifying the image filename use: @@ -101,6 +103,11 @@ typedef struct BDRVRBDState { char *snap; } BDRVRBDState; +static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx, + BlockdevOptionsRbd *opts, bool cache, + const char *keypairs, const char *secretid, + Error **errp); + static char *qemu_rbd_next_tok(char *src, char delim, char **p) { char *end; @@ -268,13 +275,14 @@ static int qemu_rbd_set_keypairs(rados_t cluster, const char *keypairs_json, key = qstring_get_str(name); ret = rados_conf_set(cluster, key, qstring_get_str(value)); - QDECREF(name); QDECREF(value); if (ret < 0) { error_setg_errno(errp, -ret, "invalid conf option %s", key); + QDECREF(name); ret = -EINVAL; break; } + QDECREF(name); } QDECREF(keypairs); @@ -325,67 +333,93 @@ static QemuOptsList runtime_opts = { /* * server.* extracted manually, see qemu_rbd_mon_host() */ - { - .name = "password-secret", - .type = QEMU_OPT_STRING, - .help = "ID of secret providing the password", - }, - - /* - * Keys for qemu_rbd_parse_filename(), not in the QAPI schema - */ - { - /* - * HACK: name starts with '=' so that qemu_opts_parse() - * can't set it - */ - .name = "=keyvalue-pairs", - .type = QEMU_OPT_STRING, - .help = "Legacy rados key/value option parameters", - }, - { - .name = "filename", - .type = QEMU_OPT_STRING, - }, { /* end of list */ } }, }; -static int coroutine_fn qemu_rbd_co_create_opts(const char *filename, - QemuOpts *opts, - Error **errp) +/* FIXME Deprecate and remove keypairs or make it available in QMP. + * password_secret should eventually be configurable in opts->location. Support + * for it in .bdrv_open will make it work here as well. */ +static int qemu_rbd_do_create(BlockdevCreateOptions *options, + const char *keypairs, const char *password_secret, + Error **errp) { - Error *local_err = NULL; - int64_t bytes = 0; - int64_t objsize; - int obj_order = 0; - const char *pool, *image_name, *conf, *user, *keypairs; - const char *secretid; + BlockdevCreateOptionsRbd *opts = &options->u.rbd; rados_t cluster; rados_ioctx_t io_ctx; - QDict *options = NULL; - int ret = 0; + int obj_order = 0; + int ret; - secretid = qemu_opt_get(opts, "password-secret"); + assert(options->driver == BLOCKDEV_DRIVER_RBD); + if (opts->location->has_snapshot) { + error_setg(errp, "Can't use snapshot name for image creation"); + return -EINVAL; + } - /* Read out options */ - bytes = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), - BDRV_SECTOR_SIZE); - objsize = qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE, 0); - if (objsize) { + if (opts->has_cluster_size) { + int64_t objsize = opts->cluster_size; if ((objsize - 1) & objsize) { /* not a power of 2? */ error_setg(errp, "obj size needs to be power of 2"); - ret = -EINVAL; - goto exit; + return -EINVAL; } if (objsize < 4096) { error_setg(errp, "obj size too small"); - ret = -EINVAL; - goto exit; + return -EINVAL; } obj_order = ctz32(objsize); } + ret = qemu_rbd_connect(&cluster, &io_ctx, opts->location, false, keypairs, + password_secret, errp); + if (ret < 0) { + return ret; + } + + ret = rbd_create(io_ctx, opts->location->image, opts->size, &obj_order); + if (ret < 0) { + error_setg_errno(errp, -ret, "error rbd create"); + goto out; + } + + ret = 0; +out: + rados_ioctx_destroy(io_ctx); + rados_shutdown(cluster); + return ret; +} + +static int qemu_rbd_co_create(BlockdevCreateOptions *options, Error **errp) +{ + return qemu_rbd_do_create(options, NULL, NULL, errp); +} + +static int coroutine_fn qemu_rbd_co_create_opts(const char *filename, + QemuOpts *opts, + Error **errp) +{ + BlockdevCreateOptions *create_options; + BlockdevCreateOptionsRbd *rbd_opts; + BlockdevOptionsRbd *loc; + Error *local_err = NULL; + const char *keypairs, *password_secret; + QDict *options = NULL; + int ret = 0; + + create_options = g_new0(BlockdevCreateOptions, 1); + create_options->driver = BLOCKDEV_DRIVER_RBD; + rbd_opts = &create_options->u.rbd; + + rbd_opts->location = g_new0(BlockdevOptionsRbd, 1); + + password_secret = qemu_opt_get(opts, "password-secret"); + + /* Read out options */ + rbd_opts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); + rbd_opts->cluster_size = qemu_opt_get_size_del(opts, + BLOCK_OPT_CLUSTER_SIZE, 0); + rbd_opts->has_cluster_size = (rbd_opts->cluster_size != 0); + options = qdict_new(); qemu_rbd_parse_filename(filename, options, &local_err); if (local_err) { @@ -400,61 +434,23 @@ static int coroutine_fn qemu_rbd_co_create_opts(const char *filename, * or blockdev_add, its members are typed according to the QAPI * schema, but when they come from -drive, they're all QString. */ - pool = qdict_get_try_str(options, "pool"); - conf = qdict_get_try_str(options, "conf"); - user = qdict_get_try_str(options, "user"); - image_name = qdict_get_try_str(options, "image"); - keypairs = qdict_get_try_str(options, "=keyvalue-pairs"); - - ret = rados_create(&cluster, user); + loc = rbd_opts->location; + loc->pool = g_strdup(qdict_get_try_str(options, "pool")); + loc->conf = g_strdup(qdict_get_try_str(options, "conf")); + loc->has_conf = !!loc->conf; + loc->user = g_strdup(qdict_get_try_str(options, "user")); + loc->has_user = !!loc->user; + loc->image = g_strdup(qdict_get_try_str(options, "image")); + keypairs = qdict_get_try_str(options, "=keyvalue-pairs"); + + ret = qemu_rbd_do_create(create_options, keypairs, password_secret, errp); if (ret < 0) { - error_setg_errno(errp, -ret, "error initializing"); goto exit; } - /* try default location when conf=NULL, but ignore failure */ - ret = rados_conf_read_file(cluster, conf); - if (conf && ret < 0) { - error_setg_errno(errp, -ret, "error reading conf file %s", conf); - ret = -EIO; - goto shutdown; - } - - ret = qemu_rbd_set_keypairs(cluster, keypairs, errp); - if (ret < 0) { - ret = -EIO; - goto shutdown; - } - - if (qemu_rbd_set_auth(cluster, secretid, errp) < 0) { - ret = -EIO; - goto shutdown; - } - - ret = rados_connect(cluster); - if (ret < 0) { - error_setg_errno(errp, -ret, "error connecting"); - goto shutdown; - } - - ret = rados_ioctx_create(cluster, pool, &io_ctx); - if (ret < 0) { - error_setg_errno(errp, -ret, "error opening pool %s", pool); - goto shutdown; - } - - ret = rbd_create(io_ctx, image_name, bytes, &obj_order); - if (ret < 0) { - error_setg_errno(errp, -ret, "error rbd create"); - } - - rados_ioctx_destroy(io_ctx); - -shutdown: - rados_shutdown(cluster); - exit: QDECREF(options); + qapi_free_BlockdevCreateOptions(create_options); return ret; } @@ -505,131 +501,83 @@ static void qemu_rbd_complete_aio(RADOSCB *rcb) qemu_aio_unref(acb); } -static char *qemu_rbd_mon_host(QDict *options, Error **errp) +static char *qemu_rbd_mon_host(BlockdevOptionsRbd *opts, Error **errp) { - const char **vals = g_new(const char *, qdict_size(options) + 1); - char keybuf[32]; + const char **vals; const char *host, *port; char *rados_str; - int i; - - for (i = 0;; i++) { - sprintf(keybuf, "server.%d.host", i); - host = qdict_get_try_str(options, keybuf); - qdict_del(options, keybuf); - sprintf(keybuf, "server.%d.port", i); - port = qdict_get_try_str(options, keybuf); - qdict_del(options, keybuf); - if (!host && !port) { - break; - } - if (!host) { - error_setg(errp, "Parameter server.%d.host is missing", i); - rados_str = NULL; - goto out; - } + InetSocketAddressBaseList *p; + int i, cnt; + + if (!opts->has_server) { + return NULL; + } + + for (cnt = 0, p = opts->server; p; p = p->next) { + cnt++; + } + + vals = g_new(const char *, cnt + 1); + + for (i = 0, p = opts->server; p; p = p->next, i++) { + host = p->value->host; + port = p->value->port; if (strchr(host, ':')) { - vals[i] = port ? g_strdup_printf("[%s]:%s", host, port) - : g_strdup_printf("[%s]", host); + vals[i] = g_strdup_printf("[%s]:%s", host, port); } else { - vals[i] = port ? g_strdup_printf("%s:%s", host, port) - : g_strdup(host); + vals[i] = g_strdup_printf("%s:%s", host, port); } } vals[i] = NULL; rados_str = i ? g_strjoinv(";", (char **)vals) : NULL; -out: g_strfreev((char **)vals); return rados_str; } -static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, - Error **errp) +static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx, + BlockdevOptionsRbd *opts, bool cache, + const char *keypairs, const char *secretid, + Error **errp) { - BDRVRBDState *s = bs->opaque; - const char *pool, *snap, *conf, *user, *image_name, *keypairs; - const char *secretid, *filename; - QemuOpts *opts; - Error *local_err = NULL; char *mon_host = NULL; + Error *local_err = NULL; int r; - /* If we are given a filename, parse the filename, with precedence given to - * filename encoded options */ - filename = qdict_get_try_str(options, "filename"); - if (filename) { - warn_report("'filename' option specified. " - "This is an unsupported option, and may be deprecated " - "in the future"); - qemu_rbd_parse_filename(filename, options, &local_err); - if (local_err) { - r = -EINVAL; - error_propagate(errp, local_err); - goto exit; - } - } - - opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); - qemu_opts_absorb_qdict(opts, options, &local_err); + mon_host = qemu_rbd_mon_host(opts, &local_err); if (local_err) { error_propagate(errp, local_err); r = -EINVAL; goto failed_opts; } - mon_host = qemu_rbd_mon_host(options, &local_err); - if (local_err) { - error_propagate(errp, local_err); - r = -EINVAL; - goto failed_opts; - } - - secretid = qemu_opt_get(opts, "password-secret"); - - pool = qemu_opt_get(opts, "pool"); - conf = qemu_opt_get(opts, "conf"); - snap = qemu_opt_get(opts, "snapshot"); - user = qemu_opt_get(opts, "user"); - image_name = qemu_opt_get(opts, "image"); - keypairs = qemu_opt_get(opts, "=keyvalue-pairs"); - - if (!pool || !image_name) { - error_setg(errp, "Parameters 'pool' and 'image' are required"); - r = -EINVAL; - goto failed_opts; - } - - r = rados_create(&s->cluster, user); + r = rados_create(cluster, opts->user); if (r < 0) { error_setg_errno(errp, -r, "error initializing"); goto failed_opts; } - s->snap = g_strdup(snap); - s->image_name = g_strdup(image_name); - /* try default location when conf=NULL, but ignore failure */ - r = rados_conf_read_file(s->cluster, conf); - if (conf && r < 0) { - error_setg_errno(errp, -r, "error reading conf file %s", conf); + r = rados_conf_read_file(*cluster, opts->conf); + if (opts->has_conf && r < 0) { + error_setg_errno(errp, -r, "error reading conf file %s", opts->conf); goto failed_shutdown; } - r = qemu_rbd_set_keypairs(s->cluster, keypairs, errp); + r = qemu_rbd_set_keypairs(*cluster, keypairs, errp); if (r < 0) { goto failed_shutdown; } if (mon_host) { - r = rados_conf_set(s->cluster, "mon_host", mon_host); + r = rados_conf_set(*cluster, "mon_host", mon_host); if (r < 0) { goto failed_shutdown; } } - if (qemu_rbd_set_auth(s->cluster, secretid, errp) < 0) { + if (qemu_rbd_set_auth(*cluster, secretid, errp) < 0) { r = -EIO; goto failed_shutdown; } @@ -641,24 +589,97 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, * librbd defaults to no caching. If write through caching cannot * be set up, fall back to no caching. */ - if (flags & BDRV_O_NOCACHE) { - rados_conf_set(s->cluster, "rbd_cache", "false"); + if (cache) { + rados_conf_set(*cluster, "rbd_cache", "true"); } else { - rados_conf_set(s->cluster, "rbd_cache", "true"); + rados_conf_set(*cluster, "rbd_cache", "false"); } - r = rados_connect(s->cluster); + r = rados_connect(*cluster); if (r < 0) { error_setg_errno(errp, -r, "error connecting"); goto failed_shutdown; } - r = rados_ioctx_create(s->cluster, pool, &s->io_ctx); + r = rados_ioctx_create(*cluster, opts->pool, io_ctx); if (r < 0) { - error_setg_errno(errp, -r, "error opening pool %s", pool); + error_setg_errno(errp, -r, "error opening pool %s", opts->pool); goto failed_shutdown; } + return 0; + +failed_shutdown: + rados_shutdown(*cluster); +failed_opts: + g_free(mon_host); + return r; +} + +static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + BDRVRBDState *s = bs->opaque; + BlockdevOptionsRbd *opts = NULL; + Visitor *v; + QObject *crumpled = NULL; + Error *local_err = NULL; + const char *filename; + char *keypairs, *secretid; + int r; + + /* If we are given a filename, parse the filename, with precedence given to + * filename encoded options */ + filename = qdict_get_try_str(options, "filename"); + if (filename) { + warn_report("'filename' option specified. " + "This is an unsupported option, and may be deprecated " + "in the future"); + qemu_rbd_parse_filename(filename, options, &local_err); + qdict_del(options, "filename"); + if (local_err) { + error_propagate(errp, local_err); + return -EINVAL; + } + } + + keypairs = g_strdup(qdict_get_try_str(options, "=keyvalue-pairs")); + if (keypairs) { + qdict_del(options, "=keyvalue-pairs"); + } + + secretid = g_strdup(qdict_get_try_str(options, "password-secret")); + if (secretid) { + qdict_del(options, "password-secret"); + } + + /* Convert the remaining options into a QAPI object */ + crumpled = qdict_crumple(options, errp); + if (crumpled == NULL) { + r = -EINVAL; + goto out; + } + + v = qobject_input_visitor_new_keyval(crumpled); + visit_type_BlockdevOptionsRbd(v, NULL, &opts, &local_err); + visit_free(v); + qobject_decref(crumpled); + + if (local_err) { + error_propagate(errp, local_err); + r = -EINVAL; + goto out; + } + + r = qemu_rbd_connect(&s->cluster, &s->io_ctx, opts, + !(flags & BDRV_O_NOCACHE), keypairs, secretid, errp); + if (r < 0) { + goto out; + } + + s->snap = g_strdup(opts->snapshot); + s->image_name = g_strdup(opts->image); + /* rbd_open is always r/w */ r = rbd_open(s->io_ctx, s->image_name, &s->image, s->snap); if (r < 0) { @@ -683,19 +704,18 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, } } - qemu_opts_del(opts); - return 0; + r = 0; + goto out; failed_open: rados_ioctx_destroy(s->io_ctx); -failed_shutdown: - rados_shutdown(s->cluster); g_free(s->snap); g_free(s->image_name); -failed_opts: - qemu_opts_del(opts); - g_free(mon_host); -exit: + rados_shutdown(s->cluster); +out: + qapi_free_BlockdevOptionsRbd(opts); + g_free(keypairs); + g_free(secretid); return r; } @@ -1093,8 +1113,8 @@ static BlockAIOCB *qemu_rbd_aio_pdiscard(BlockDriverState *bs, #endif #ifdef LIBRBD_SUPPORTS_INVALIDATE -static void qemu_rbd_invalidate_cache(BlockDriverState *bs, - Error **errp) +static void coroutine_fn qemu_rbd_co_invalidate_cache(BlockDriverState *bs, + Error **errp) { BDRVRBDState *s = bs->opaque; int r = rbd_invalidate_cache(s->image); @@ -1134,6 +1154,7 @@ static BlockDriver bdrv_rbd = { .bdrv_file_open = qemu_rbd_open, .bdrv_close = qemu_rbd_close, .bdrv_reopen_prepare = qemu_rbd_reopen_prepare, + .bdrv_co_create = qemu_rbd_co_create, .bdrv_co_create_opts = qemu_rbd_co_create_opts, .bdrv_has_zero_init = bdrv_has_zero_init_1, .bdrv_get_info = qemu_rbd_getinfo, @@ -1160,7 +1181,7 @@ static BlockDriver bdrv_rbd = { .bdrv_snapshot_list = qemu_rbd_snap_list, .bdrv_snapshot_goto = qemu_rbd_snap_rollback, #ifdef LIBRBD_SUPPORTS_INVALIDATE - .bdrv_invalidate_cache = qemu_rbd_invalidate_cache, + .bdrv_co_invalidate_cache = qemu_rbd_co_invalidate_cache, #endif }; diff --git a/block/sheepdog.c b/block/sheepdog.c index d8c10b7cac..797ea5953b 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -15,8 +15,10 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qapi/qapi-visit-sockets.h" +#include "qapi/qapi-visit-block-core.h" #include "qapi/qmp/qdict.h" #include "qapi/qobject-input-visitor.h" +#include "qapi/qobject-output-visitor.h" #include "qemu/uri.h" #include "qemu/error-report.h" #include "qemu/option.h" @@ -533,23 +535,6 @@ static void sd_aio_setup(SheepdogAIOCB *acb, BDRVSheepdogState *s, qemu_co_mutex_unlock(&s->queue_lock); } -static SocketAddress *sd_socket_address(const char *path, - const char *host, const char *port) -{ - SocketAddress *addr = g_new0(SocketAddress, 1); - - if (path) { - addr->type = SOCKET_ADDRESS_TYPE_UNIX; - addr->u.q_unix.path = g_strdup(path); - } else { - addr->type = SOCKET_ADDRESS_TYPE_INET; - addr->u.inet.host = g_strdup(host ?: SD_DEFAULT_ADDR); - addr->u.inet.port = g_strdup(port ?: stringify(SD_DEFAULT_PORT)); - } - - return addr; -} - static SocketAddress *sd_server_config(QDict *options, Error **errp) { QDict *server = NULL; @@ -1051,7 +1036,7 @@ static void sd_parse_uri(SheepdogConfig *cfg, const char *filename, cfg->uri = uri = uri_parse(filename); if (!uri) { - error_setg(&err, "invalid URI"); + error_setg(&err, "invalid URI '%s'", filename); goto out; } @@ -1882,6 +1867,86 @@ out_with_err_set: return ret; } +static int sd_create_prealloc(BlockdevOptionsSheepdog *location, int64_t size, + Error **errp) +{ + BlockDriverState *bs; + Visitor *v; + QObject *obj = NULL; + QDict *qdict; + Error *local_err = NULL; + int ret; + + v = qobject_output_visitor_new(&obj); + visit_type_BlockdevOptionsSheepdog(v, NULL, &location, &local_err); + visit_free(v); + + if (local_err) { + error_propagate(errp, local_err); + qobject_decref(obj); + return -EINVAL; + } + + qdict = qobject_to_qdict(obj); + qdict_flatten(qdict); + + qdict_put_str(qdict, "driver", "sheepdog"); + + bs = bdrv_open(NULL, NULL, qdict, BDRV_O_PROTOCOL | BDRV_O_RDWR, errp); + if (bs == NULL) { + ret = -EIO; + goto fail; + } + + ret = sd_prealloc(bs, 0, size, errp); +fail: + bdrv_unref(bs); + QDECREF(qdict); + return ret; +} + +static int parse_redundancy(BDRVSheepdogState *s, SheepdogRedundancy *opt) +{ + struct SheepdogInode *inode = &s->inode; + + switch (opt->type) { + case SHEEPDOG_REDUNDANCY_TYPE_FULL: + if (opt->u.full.copies > SD_MAX_COPIES || opt->u.full.copies < 1) { + return -EINVAL; + } + inode->copy_policy = 0; + inode->nr_copies = opt->u.full.copies; + return 0; + + case SHEEPDOG_REDUNDANCY_TYPE_ERASURE_CODED: + { + int64_t copy = opt->u.erasure_coded.data_strips; + int64_t parity = opt->u.erasure_coded.parity_strips; + + if (copy != 2 && copy != 4 && copy != 8 && copy != 16) { + return -EINVAL; + } + + if (parity >= SD_EC_MAX_STRIP || parity < 1) { + return -EINVAL; + } + + /* + * 4 bits for parity and 4 bits for data. + * We have to compress upper data bits because it can't represent 16 + */ + inode->copy_policy = ((copy / 2) << 4) + parity; + inode->nr_copies = copy + parity; + return 0; + } + + default: + g_assert_not_reached(); + } + + return -EINVAL; +} + /* * Sheepdog support two kinds of redundancy, full replication and erasure * coding. @@ -1892,60 +1957,61 @@ out_with_err_set: * # create a erasure coded vdi with x data strips and y parity strips * -o redundancy=x:y (x must be one of {2,4,8,16} and 1 <= y < SD_EC_MAX_STRIP) */ -static int parse_redundancy(BDRVSheepdogState *s, const char *opt) +static SheepdogRedundancy *parse_redundancy_str(const char *opt) { - struct SheepdogInode *inode = &s->inode; + SheepdogRedundancy *redundancy; const char *n1, *n2; long copy, parity; char p[10]; + int ret; pstrcpy(p, sizeof(p), opt); n1 = strtok(p, ":"); n2 = strtok(NULL, ":"); if (!n1) { - return -EINVAL; + return NULL; } - copy = strtol(n1, NULL, 10); - /* FIXME fix error checking by switching to qemu_strtol() */ - if (copy > SD_MAX_COPIES || copy < 1) { - return -EINVAL; - } - if (!n2) { - inode->copy_policy = 0; - inode->nr_copies = copy; - return 0; + ret = qemu_strtol(n1, NULL, 10, ©); + if (ret < 0) { + return NULL; } - if (copy != 2 && copy != 4 && copy != 8 && copy != 16) { - return -EINVAL; - } + redundancy = g_new0(SheepdogRedundancy, 1); + if (!n2) { + *redundancy = (SheepdogRedundancy) { + .type = SHEEPDOG_REDUNDANCY_TYPE_FULL, + .u.full.copies = copy, + }; + } else { + ret = qemu_strtol(n2, NULL, 10, &parity); + if (ret < 0) { + return NULL; + } - parity = strtol(n2, NULL, 10); - /* FIXME fix error checking by switching to qemu_strtol() */ - if (parity >= SD_EC_MAX_STRIP || parity < 1) { - return -EINVAL; + *redundancy = (SheepdogRedundancy) { + .type = SHEEPDOG_REDUNDANCY_TYPE_ERASURE_CODED, + .u.erasure_coded = { + .data_strips = copy, + .parity_strips = parity, + }, + }; } - /* - * 4 bits for parity and 4 bits for data. - * We have to compress upper data bits because it can't represent 16 - */ - inode->copy_policy = ((copy / 2) << 4) + parity; - inode->nr_copies = copy + parity; - - return 0; + return redundancy; } -static int parse_block_size_shift(BDRVSheepdogState *s, QemuOpts *opt) +static int parse_block_size_shift(BDRVSheepdogState *s, + BlockdevCreateOptionsSheepdog *opts) { struct SheepdogInode *inode = &s->inode; uint64_t object_size; int obj_order; - object_size = qemu_opt_get_size_del(opt, BLOCK_OPT_OBJECT_SIZE, 0); - if (object_size) { + if (opts->has_object_size) { + object_size = opts->object_size; + if ((object_size - 1) & object_size) { /* not a power of 2? */ return -EINVAL; } @@ -1959,57 +2025,55 @@ static int parse_block_size_shift(BDRVSheepdogState *s, QemuOpts *opt) return 0; } -static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts, - Error **errp) +static int sd_co_create(BlockdevCreateOptions *options, Error **errp) { - Error *err = NULL; + BlockdevCreateOptionsSheepdog *opts = &options->u.sheepdog; int ret = 0; uint32_t vid = 0; char *backing_file = NULL; char *buf = NULL; BDRVSheepdogState *s; - SheepdogConfig cfg; uint64_t max_vdi_size; bool prealloc = false; + assert(options->driver == BLOCKDEV_DRIVER_SHEEPDOG); + s = g_new0(BDRVSheepdogState, 1); - if (strstr(filename, "://")) { - sd_parse_uri(&cfg, filename, &err); - } else { - parse_vdiname(&cfg, filename, &err); - } - if (err) { - error_propagate(errp, err); + /* Steal SocketAddress from QAPI, set NULL to prevent double free */ + s->addr = opts->location->server; + opts->location->server = NULL; + + if (strlen(opts->location->vdi) >= sizeof(s->name)) { + error_setg(errp, "'vdi' string too long"); + ret = -EINVAL; goto out; } + pstrcpy(s->name, sizeof(s->name), opts->location->vdi); - buf = cfg.port ? g_strdup_printf("%d", cfg.port) : NULL; - s->addr = sd_socket_address(cfg.path, cfg.host, buf); - g_free(buf); - strcpy(s->name, cfg.vdi); - sd_config_done(&cfg); + s->inode.vdi_size = opts->size; + backing_file = opts->backing_file; - s->inode.vdi_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), - BDRV_SECTOR_SIZE); - backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE); - buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC); - if (!buf || !strcmp(buf, "off")) { + if (!opts->has_preallocation) { + opts->preallocation = PREALLOC_MODE_OFF; + } + switch (opts->preallocation) { + case PREALLOC_MODE_OFF: prealloc = false; - } else if (!strcmp(buf, "full")) { + break; + case PREALLOC_MODE_FULL: prealloc = true; - } else { - error_setg(errp, "Invalid preallocation mode: '%s'", buf); + break; + default: + error_setg(errp, "Preallocation mode not supported for Sheepdog"); ret = -EINVAL; goto out; } - g_free(buf); - buf = qemu_opt_get_del(opts, BLOCK_OPT_REDUNDANCY); - if (buf) { - ret = parse_redundancy(s, buf); + if (opts->has_redundancy) { + ret = parse_redundancy(s, opts->redundancy); if (ret < 0) { - error_setg(errp, "Invalid redundancy mode: '%s'", buf); + error_setg(errp, "Invalid redundancy mode"); goto out; } } @@ -2021,20 +2085,20 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts, goto out; } - if (backing_file) { + if (opts->has_backing_file) { BlockBackend *blk; BDRVSheepdogState *base; BlockDriver *drv; /* Currently, only Sheepdog backing image is supported. */ - drv = bdrv_find_protocol(backing_file, true, NULL); + drv = bdrv_find_protocol(opts->backing_file, true, NULL); if (!drv || strcmp(drv->protocol_name, "sheepdog") != 0) { error_setg(errp, "backing_file must be a sheepdog image"); ret = -EINVAL; goto out; } - blk = blk_new_open(backing_file, NULL, NULL, + blk = blk_new_open(opts->backing_file, NULL, NULL, BDRV_O_PROTOCOL, errp); if (blk == NULL) { ret = -EIO; @@ -2102,28 +2166,96 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts, } if (prealloc) { - BlockDriverState *bs; - QDict *opts; - - opts = qdict_new(); - qdict_put_str(opts, "driver", "sheepdog"); - bs = bdrv_open(filename, NULL, opts, BDRV_O_PROTOCOL | BDRV_O_RDWR, - errp); - if (!bs) { - goto out; - } - - ret = sd_prealloc(bs, 0, s->inode.vdi_size, errp); - - bdrv_unref(bs); + ret = sd_create_prealloc(opts->location, opts->size, errp); } out: g_free(backing_file); g_free(buf); + g_free(s->addr); g_free(s); return ret; } +static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts, + Error **errp) +{ + BlockdevCreateOptions *create_options = NULL; + QDict *qdict, *location_qdict; + QObject *crumpled; + Visitor *v; + const char *redundancy; + Error *local_err = NULL; + int ret; + + redundancy = qemu_opt_get_del(opts, BLOCK_OPT_REDUNDANCY); + + qdict = qemu_opts_to_qdict(opts, NULL); + qdict_put_str(qdict, "driver", "sheepdog"); + + location_qdict = qdict_new(); + qdict_put(qdict, "location", location_qdict); + + sd_parse_filename(filename, location_qdict, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto fail; + } + + qdict_flatten(qdict); + + /* Change legacy command line options into QMP ones */ + static const QDictRenames opt_renames[] = { + { BLOCK_OPT_BACKING_FILE, "backing-file" }, + { BLOCK_OPT_OBJECT_SIZE, "object-size" }, + { NULL, NULL }, + }; + + if (!qdict_rename_keys(qdict, opt_renames, errp)) { + ret = -EINVAL; + goto fail; + } + + /* Get the QAPI object */ + crumpled = qdict_crumple(qdict, errp); + if (crumpled == NULL) { + ret = -EINVAL; + goto fail; + } + + v = qobject_input_visitor_new_keyval(crumpled); + visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); + visit_free(v); + qobject_decref(crumpled); + + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto fail; + } + + assert(create_options->driver == BLOCKDEV_DRIVER_SHEEPDOG); + create_options->u.sheepdog.size = + ROUND_UP(create_options->u.sheepdog.size, BDRV_SECTOR_SIZE); + + if (redundancy) { + create_options->u.sheepdog.has_redundancy = true; + create_options->u.sheepdog.redundancy = + parse_redundancy_str(redundancy); + if (create_options->u.sheepdog.redundancy == NULL) { + error_setg(errp, "Invalid redundancy mode"); + ret = -EINVAL; + goto fail; + } + } + + ret = sd_co_create(create_options, errp); +fail: + qapi_free_BlockdevCreateOptions(create_options); + QDECREF(qdict); + return ret; +} + static void sd_close(BlockDriverState *bs) { Error *local_err = NULL; @@ -3103,6 +3235,7 @@ static BlockDriver bdrv_sheepdog = { .bdrv_reopen_commit = sd_reopen_commit, .bdrv_reopen_abort = sd_reopen_abort, .bdrv_close = sd_close, + .bdrv_co_create = sd_co_create, .bdrv_co_create_opts = sd_co_create_opts, .bdrv_has_zero_init = bdrv_has_zero_init_1, .bdrv_getlength = sd_getlength, @@ -3139,6 +3272,7 @@ static BlockDriver bdrv_sheepdog_tcp = { .bdrv_reopen_commit = sd_reopen_commit, .bdrv_reopen_abort = sd_reopen_abort, .bdrv_close = sd_close, + .bdrv_co_create = sd_co_create, .bdrv_co_create_opts = sd_co_create_opts, .bdrv_has_zero_init = bdrv_has_zero_init_1, .bdrv_getlength = sd_getlength, @@ -3175,6 +3309,7 @@ static BlockDriver bdrv_sheepdog_unix = { .bdrv_reopen_commit = sd_reopen_commit, .bdrv_reopen_abort = sd_reopen_abort, .bdrv_close = sd_close, + .bdrv_co_create = sd_co_create, .bdrv_co_create_opts = sd_co_create_opts, .bdrv_has_zero_init = bdrv_has_zero_init_1, .bdrv_getlength = sd_getlength, diff --git a/block/ssh.c b/block/ssh.c index ff9929497d..ab3acf0c22 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -35,6 +35,7 @@ #include "qemu/sockets.h" #include "qemu/uri.h" #include "qapi/qapi-visit-sockets.h" +#include "qapi/qapi-visit-block-core.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qstring.h" #include "qapi/qobject-input-visitor.h" @@ -430,31 +431,35 @@ check_host_key_hash(BDRVSSHState *s, const char *hash, } static int check_host_key(BDRVSSHState *s, const char *host, int port, - const char *host_key_check, Error **errp) + SshHostKeyCheck *hkc, Error **errp) { - /* host_key_check=no */ - if (strcmp(host_key_check, "no") == 0) { - return 0; - } - - /* host_key_check=md5:xx:yy:zz:... */ - if (strncmp(host_key_check, "md5:", 4) == 0) { - return check_host_key_hash(s, &host_key_check[4], - LIBSSH2_HOSTKEY_HASH_MD5, 16, errp); - } + SshHostKeyCheckMode mode; - /* host_key_check=sha1:xx:yy:zz:... */ - if (strncmp(host_key_check, "sha1:", 5) == 0) { - return check_host_key_hash(s, &host_key_check[5], - LIBSSH2_HOSTKEY_HASH_SHA1, 20, errp); + if (hkc) { + mode = hkc->mode; + } else { + mode = SSH_HOST_KEY_CHECK_MODE_KNOWN_HOSTS; } - /* host_key_check=yes */ - if (strcmp(host_key_check, "yes") == 0) { + switch (mode) { + case SSH_HOST_KEY_CHECK_MODE_NONE: + return 0; + case SSH_HOST_KEY_CHECK_MODE_HASH: + if (hkc->u.hash.type == SSH_HOST_KEY_CHECK_HASH_TYPE_MD5) { + return check_host_key_hash(s, hkc->u.hash.hash, + LIBSSH2_HOSTKEY_HASH_MD5, 16, errp); + } else if (hkc->u.hash.type == SSH_HOST_KEY_CHECK_HASH_TYPE_SHA1) { + return check_host_key_hash(s, hkc->u.hash.hash, + LIBSSH2_HOSTKEY_HASH_SHA1, 20, errp); + } + g_assert_not_reached(); + break; + case SSH_HOST_KEY_CHECK_MODE_KNOWN_HOSTS: return check_host_key_knownhosts(s, host, port, errp); + default: + g_assert_not_reached(); } - error_setg(errp, "unknown host_key_check setting (%s)", host_key_check); return -EINVAL; } @@ -544,16 +549,6 @@ static QemuOptsList ssh_runtime_opts = { .help = "Port to connect to", }, { - .name = "path", - .type = QEMU_OPT_STRING, - .help = "Path of the image on the host", - }, - { - .name = "user", - .type = QEMU_OPT_STRING, - .help = "User as which to connect", - }, - { .name = "host_key_check", .type = QEMU_OPT_STRING, .help = "Defines how and what to check the host key against", @@ -562,12 +557,13 @@ static QemuOptsList ssh_runtime_opts = { }, }; -static bool ssh_process_legacy_socket_options(QDict *output_opts, - QemuOpts *legacy_opts, - Error **errp) +static bool ssh_process_legacy_options(QDict *output_opts, + QemuOpts *legacy_opts, + Error **errp) { const char *host = qemu_opt_get(legacy_opts, "host"); const char *port = qemu_opt_get(legacy_opts, "port"); + const char *host_key_check = qemu_opt_get(legacy_opts, "host_key_check"); if (!host && port) { error_setg(errp, "port may not be used without host"); @@ -579,26 +575,56 @@ static bool ssh_process_legacy_socket_options(QDict *output_opts, qdict_put_str(output_opts, "server.port", port ?: stringify(22)); } + if (host_key_check) { + if (strcmp(host_key_check, "no") == 0) { + qdict_put_str(output_opts, "host-key-check.mode", "none"); + } else if (strncmp(host_key_check, "md5:", 4) == 0) { + qdict_put_str(output_opts, "host-key-check.mode", "hash"); + qdict_put_str(output_opts, "host-key-check.type", "md5"); + qdict_put_str(output_opts, "host-key-check.hash", + &host_key_check[4]); + } else if (strncmp(host_key_check, "sha1:", 5) == 0) { + qdict_put_str(output_opts, "host-key-check.mode", "hash"); + qdict_put_str(output_opts, "host-key-check.type", "sha1"); + qdict_put_str(output_opts, "host-key-check.hash", + &host_key_check[5]); + } else if (strcmp(host_key_check, "yes") == 0) { + qdict_put_str(output_opts, "host-key-check.mode", "known_hosts"); + } else { + error_setg(errp, "unknown host_key_check setting (%s)", + host_key_check); + return false; + } + } + return true; } -static InetSocketAddress *ssh_config(QDict *options, Error **errp) +static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp) { - InetSocketAddress *inet = NULL; - QDict *addr = NULL; - QObject *crumpled_addr = NULL; - Visitor *iv = NULL; - Error *local_error = NULL; - - qdict_extract_subqdict(options, &addr, "server."); - if (!qdict_size(addr)) { - error_setg(errp, "SSH server address missing"); - goto out; + BlockdevOptionsSsh *result = NULL; + QemuOpts *opts = NULL; + Error *local_err = NULL; + QObject *crumpled; + const QDictEntry *e; + Visitor *v; + + /* Translate legacy options */ + opts = qemu_opts_create(&ssh_runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, options, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto fail; } - crumpled_addr = qdict_crumple(addr, errp); - if (!crumpled_addr) { - goto out; + if (!ssh_process_legacy_options(options, opts, errp)) { + goto fail; + } + + /* Create the QAPI object */ + crumpled = qdict_crumple(options, errp); + if (crumpled == NULL) { + goto fail; } /* @@ -609,51 +635,37 @@ static InetSocketAddress *ssh_config(QDict *options, Error **errp) * but when they come from -drive, they're all QString. The * visitor expects the former. */ - iv = qobject_input_visitor_new(crumpled_addr); - visit_type_InetSocketAddress(iv, NULL, &inet, &local_error); - if (local_error) { - error_propagate(errp, local_error); - goto out; + v = qobject_input_visitor_new(crumpled); + visit_type_BlockdevOptionsSsh(v, NULL, &result, &local_err); + visit_free(v); + qobject_decref(crumpled); + + if (local_err) { + error_propagate(errp, local_err); + goto fail; } -out: - QDECREF(addr); - qobject_decref(crumpled_addr); - visit_free(iv); - return inet; + /* Remove the processed options from the QDict (the visitor processes + * _all_ options in the QDict) */ + while ((e = qdict_first(options))) { + qdict_del(options, e->key); + } + +fail: + qemu_opts_del(opts); + return result; } -static int connect_to_ssh(BDRVSSHState *s, QDict *options, +static int connect_to_ssh(BDRVSSHState *s, BlockdevOptionsSsh *opts, int ssh_flags, int creat_mode, Error **errp) { int r, ret; - QemuOpts *opts = NULL; - Error *local_err = NULL; - const char *user, *path, *host_key_check; + const char *user; long port = 0; - opts = qemu_opts_create(&ssh_runtime_opts, NULL, 0, &error_abort); - qemu_opts_absorb_qdict(opts, options, &local_err); - if (local_err) { - ret = -EINVAL; - error_propagate(errp, local_err); - goto err; - } - - if (!ssh_process_legacy_socket_options(options, opts, errp)) { - ret = -EINVAL; - goto err; - } - - path = qemu_opt_get(opts, "path"); - if (!path) { - ret = -EINVAL; - error_setg(errp, "No path was specified"); - goto err; - } - - user = qemu_opt_get(opts, "user"); - if (!user) { + if (opts->has_user) { + user = opts->user; + } else { user = g_get_user_name(); if (!user) { error_setg_errno(errp, errno, "Can't get user name"); @@ -662,17 +674,9 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options, } } - host_key_check = qemu_opt_get(opts, "host_key_check"); - if (!host_key_check) { - host_key_check = "yes"; - } - /* Pop the config into our state object, Exit if invalid */ - s->inet = ssh_config(options, errp); - if (!s->inet) { - ret = -EINVAL; - goto err; - } + s->inet = opts->server; + opts->server = NULL; if (qemu_strtol(s->inet->port, NULL, 10, &port) < 0) { error_setg(errp, "Use only numeric port value"); @@ -707,8 +711,7 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options, } /* Check the remote host's key against known_hosts. */ - ret = check_host_key(s, s->inet->host, port, host_key_check, - errp); + ret = check_host_key(s, s->inet->host, port, opts->host_key_check, errp); if (ret < 0) { goto err; } @@ -729,16 +732,16 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options, /* Open the remote file. */ DPRINTF("opening file %s flags=0x%x creat_mode=0%o", - path, ssh_flags, creat_mode); - s->sftp_handle = libssh2_sftp_open(s->sftp, path, ssh_flags, creat_mode); + opts->path, ssh_flags, creat_mode); + s->sftp_handle = libssh2_sftp_open(s->sftp, opts->path, ssh_flags, + creat_mode); if (!s->sftp_handle) { - session_error_setg(errp, s, "failed to open remote file '%s'", path); + session_error_setg(errp, s, "failed to open remote file '%s'", + opts->path); ret = -EINVAL; goto err; } - qemu_opts_del(opts); - r = libssh2_sftp_fstat(s->sftp_handle, &s->attrs); if (r < 0) { sftp_error_setg(errp, s, "failed to read file attributes"); @@ -764,8 +767,6 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options, } s->session = NULL; - qemu_opts_del(opts); - return ret; } @@ -773,6 +774,7 @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags, Error **errp) { BDRVSSHState *s = bs->opaque; + BlockdevOptionsSsh *opts; int ret; int ssh_flags; @@ -783,8 +785,13 @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags, ssh_flags |= LIBSSH2_FXF_WRITE; } + opts = ssh_parse_options(options, errp); + if (opts == NULL) { + return -EINVAL; + } + /* Start up SSH. */ - ret = connect_to_ssh(s, options, ssh_flags, 0, errp); + ret = connect_to_ssh(s, opts, ssh_flags, 0, errp); if (ret < 0) { goto err; } @@ -792,6 +799,8 @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags, /* Go non-blocking. */ libssh2_session_set_blocking(s->session, 0); + qapi_free_BlockdevOptionsSsh(opts); + return 0; err: @@ -800,6 +809,8 @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags, } s->sock = -1; + qapi_free_BlockdevOptionsSsh(opts); + return ret; } @@ -843,51 +854,71 @@ static QemuOptsList ssh_create_opts = { } }; +static int ssh_co_create(BlockdevCreateOptions *options, Error **errp) +{ + BlockdevCreateOptionsSsh *opts = &options->u.ssh; + BDRVSSHState s; + int ret; + + assert(options->driver == BLOCKDEV_DRIVER_SSH); + + ssh_state_init(&s); + + ret = connect_to_ssh(&s, opts->location, + LIBSSH2_FXF_READ|LIBSSH2_FXF_WRITE| + LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC, + 0644, errp); + if (ret < 0) { + goto fail; + } + + if (opts->size > 0) { + ret = ssh_grow_file(&s, opts->size, errp); + if (ret < 0) { + goto fail; + } + } + + ret = 0; +fail: + ssh_state_free(&s); + return ret; +} + static int coroutine_fn ssh_co_create_opts(const char *filename, QemuOpts *opts, Error **errp) { - int r, ret; - int64_t total_size = 0; + BlockdevCreateOptions *create_options; + BlockdevCreateOptionsSsh *ssh_opts; + int ret; QDict *uri_options = NULL; - BDRVSSHState s; - ssh_state_init(&s); + create_options = g_new0(BlockdevCreateOptions, 1); + create_options->driver = BLOCKDEV_DRIVER_SSH; + ssh_opts = &create_options->u.ssh; /* Get desired file size. */ - total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), - BDRV_SECTOR_SIZE); - DPRINTF("total_size=%" PRIi64, total_size); + ssh_opts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); + DPRINTF("total_size=%" PRIi64, ssh_opts->size); uri_options = qdict_new(); - r = parse_uri(filename, uri_options, errp); - if (r < 0) { - ret = r; + ret = parse_uri(filename, uri_options, errp); + if (ret < 0) { goto out; } - r = connect_to_ssh(&s, uri_options, - LIBSSH2_FXF_READ|LIBSSH2_FXF_WRITE| - LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC, - 0644, errp); - if (r < 0) { - ret = r; + ssh_opts->location = ssh_parse_options(uri_options, errp); + if (ssh_opts->location == NULL) { + ret = -EINVAL; goto out; } - if (total_size > 0) { - ret = ssh_grow_file(&s, total_size, errp); - if (ret < 0) { - goto out; - } - } - - ret = 0; + ret = ssh_co_create(create_options, errp); out: - ssh_state_free(&s); - if (uri_options != NULL) { - QDECREF(uri_options); - } + QDECREF(uri_options); + qapi_free_BlockdevCreateOptions(create_options); return ret; } @@ -1249,6 +1280,7 @@ static BlockDriver bdrv_ssh = { .instance_size = sizeof(BDRVSSHState), .bdrv_parse_filename = ssh_parse_filename, .bdrv_file_open = ssh_file_open, + .bdrv_co_create = ssh_co_create, .bdrv_co_create_opts = ssh_co_create_opts, .bdrv_close = ssh_close, .bdrv_has_zero_init = ssh_has_zero_init, diff --git a/block/vdi.c b/block/vdi.c index 68592cc58d..2b5ddd0666 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -263,8 +263,8 @@ static void vdi_header_print(VdiHeader *header) } #endif -static int vdi_check(BlockDriverState *bs, BdrvCheckResult *res, - BdrvCheckMode fix) +static int coroutine_fn vdi_co_check(BlockDriverState *bs, BdrvCheckResult *res, + BdrvCheckMode fix) { /* TODO: additional checks possible. */ BDRVVdiState *s = (BDRVVdiState *)bs->opaque; @@ -908,7 +908,7 @@ static BlockDriver bdrv_vdi = { .bdrv_get_info = vdi_get_info, .create_opts = &vdi_create_opts, - .bdrv_check = vdi_check, + .bdrv_co_check = vdi_co_check, }; static void bdrv_vdi_init(void) diff --git a/block/vhdx.c b/block/vhdx.c index 3fbff5048b..d82350d07c 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -1944,8 +1944,9 @@ exit: * r/w and any log has already been replayed, so there is nothing (currently) * for us to do here */ -static int vhdx_check(BlockDriverState *bs, BdrvCheckResult *result, - BdrvCheckMode fix) +static int coroutine_fn vhdx_co_check(BlockDriverState *bs, + BdrvCheckResult *result, + BdrvCheckMode fix) { BDRVVHDXState *s = bs->opaque; @@ -2006,7 +2007,7 @@ static BlockDriver bdrv_vhdx = { .bdrv_co_writev = vhdx_co_writev, .bdrv_co_create_opts = vhdx_co_create_opts, .bdrv_get_info = vhdx_get_info, - .bdrv_check = vhdx_check, + .bdrv_co_check = vhdx_co_check, .bdrv_has_zero_init = bdrv_has_zero_init_1, .create_opts = &vhdx_create_opts, diff --git a/block/vmdk.c b/block/vmdk.c index 67342ed69b..f94c49a9c0 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -2221,8 +2221,9 @@ static ImageInfo *vmdk_get_extent_info(VmdkExtent *extent) return info; } -static int vmdk_check(BlockDriverState *bs, BdrvCheckResult *result, - BdrvCheckMode fix) +static int coroutine_fn vmdk_co_check(BlockDriverState *bs, + BdrvCheckResult *result, + BdrvCheckMode fix) { BDRVVmdkState *s = bs->opaque; VmdkExtent *extent = NULL; @@ -2391,7 +2392,7 @@ static BlockDriver bdrv_vmdk = { .instance_size = sizeof(BDRVVmdkState), .bdrv_probe = vmdk_probe, .bdrv_open = vmdk_open, - .bdrv_check = vmdk_check, + .bdrv_co_check = vmdk_co_check, .bdrv_reopen_prepare = vmdk_reopen_prepare, .bdrv_child_perm = bdrv_format_default_perms, .bdrv_co_preadv = vmdk_co_preadv, @@ -1692,6 +1692,7 @@ gcc_flags="-Wno-missing-include-dirs -Wempty-body -Wnested-externs $gcc_flags" gcc_flags="-Wendif-labels -Wno-shift-negative-value $gcc_flags" gcc_flags="-Wno-initializer-overrides -Wexpansion-to-defined $gcc_flags" gcc_flags="-Wno-string-plus-int $gcc_flags" +gcc_flags="-Wno-error=address-of-packed-member $gcc_flags" # Note that we do not add -Werror to gcc_flags here, because that would # enable it for all configure tests. If a configure test failed due # to -Werror this would just silently disable some features, @@ -2874,6 +2875,7 @@ if test "$sdl" != "no" ; then int main( void ) { return SDL_Init (SDL_INIT_VIDEO); } EOF sdl_cflags=$($sdlconfig --cflags 2>/dev/null) + sdl_cflags="$sdl_cflags -Wno-undef" # workaround 2.0.8 bug if test "$static" = "yes" ; then if $pkg_config $sdlname --exists; then sdl_libs=$($pkg_config $sdlname --static --libs 2>/dev/null) @@ -3352,7 +3354,7 @@ else fi glib_modules=gthread-2.0 if test "$modules" = yes; then - glib_modules="$glib_modules gmodule-2.0" + glib_modules="$glib_modules gmodule-export-2.0" fi # This workaround is required due to a bug in pkg-config file for glib as it @@ -4860,7 +4862,6 @@ fi pragma_disable_unused_but_set=no cat > $TMPC << EOF #pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wstrict-prototypes" #pragma GCC diagnostic pop @@ -5973,7 +5974,12 @@ fi echo "CONFIG_AUDIO_DRIVERS=$audio_drv_list" >> $config_host_mak for drv in $audio_drv_list; do def=CONFIG_AUDIO_$(echo $drv | LC_ALL=C tr '[a-z]' '[A-Z]') - echo "$def=y" >> $config_host_mak + case "$drv" in + alsa | oss | pa | sdl) + echo "$def=m" >> $config_host_mak ;; + *) + echo "$def=y" >> $config_host_mak ;; + esac done echo "ALSA_LIBS=$alsa_libs" >> $config_host_mak echo "PULSE_LIBS=$pulse_libs" >> $config_host_mak @@ -993,7 +993,7 @@ void cpu_synchronize_all_pre_loadvm(void) } } -static int do_vm_stop(RunState state) +static int do_vm_stop(RunState state, bool send_stop) { int ret = 0; @@ -1002,7 +1002,9 @@ static int do_vm_stop(RunState state) pause_all_vcpus(); runstate_set(state); vm_state_notify(0, state); - qapi_event_send_stop(&error_abort); + if (send_stop) { + qapi_event_send_stop(&error_abort); + } } bdrv_drain_all(); @@ -1012,6 +1014,14 @@ static int do_vm_stop(RunState state) return ret; } +/* Special vm_stop() variant for terminating the process. Historically clients + * did not expect a QMP STOP event and so we need to retain compatibility. + */ +int vm_shutdown(void) +{ + return do_vm_stop(RUN_STATE_SHUTDOWN, false); +} + static bool cpu_can_run(CPUState *cpu) { if (cpu->stop) { @@ -1994,7 +2004,7 @@ int vm_stop(RunState state) return 0; } - return do_vm_stop(state); + return do_vm_stop(state, true); } /** diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index d724106bd3..dd29e741c2 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -47,6 +47,7 @@ CONFIG_A9MPCORE=y CONFIG_A15MPCORE=y CONFIG_ARM_V7M=y +CONFIG_NETDUINO2=y CONFIG_ARM_GIC=y CONFIG_ARM_GIC_KVM=$(CONFIG_KVM) @@ -109,6 +110,7 @@ CONFIG_TZ_PPC=y CONFIG_IOTKIT=y CONFIG_IOTKIT_SECCTL=y +CONFIG_VERSATILE=y CONFIG_VERSATILE_PCI=y CONFIG_VERSATILE_I2C=y @@ -117,6 +119,7 @@ CONFIG_VFIO_XGMAC=y CONFIG_VFIO_AMD_XGBE=y CONFIG_SDHCI=y +CONFIG_INTEGRATOR=y CONFIG_INTEGRATOR_DEBUG=y CONFIG_ALLWINNER_A10_PIT=y @@ -126,6 +129,7 @@ CONFIG_ALLWINNER_A10=y CONFIG_FSL_IMX6=y CONFIG_FSL_IMX31=y CONFIG_FSL_IMX25=y +CONFIG_FSL_IMX7=y CONFIG_IMX_I2C=y @@ -140,3 +144,8 @@ CONFIG_GPIO_KEY=y CONFIG_MSF2=y CONFIG_FW_CFG_DMA=y CONFIG_XILINX_AXI=y +CONFIG_PCI_DESIGNWARE=y + +CONFIG_STRONGARM=y +CONFIG_HIGHBANK=y +CONFIG_MUSICPAL=y diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak index 3326e3e0bb..8973579bd0 100644 --- a/default-configs/i386-softmmu.mak +++ b/default-configs/i386-softmmu.mak @@ -63,3 +63,4 @@ CONFIG_PXB=y CONFIG_ACPI_VMGENID=y CONFIG_FW_CFG_DMA=y CONFIG_I2C=y +CONFIG_SEV=$(CONFIG_KVM) diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak index 1c6cda1d9a..5e27a7a4f4 100644 --- a/default-configs/x86_64-softmmu.mak +++ b/default-configs/x86_64-softmmu.mak @@ -63,3 +63,4 @@ CONFIG_PXB=y CONFIG_ACPI_VMGENID=y CONFIG_FW_CFG_DMA=y CONFIG_I2C=y +CONFIG_SEV=$(CONFIG_KVM) diff --git a/docs/amd-memory-encryption.txt b/docs/amd-memory-encryption.txt new file mode 100644 index 0000000000..f483795eaa --- /dev/null +++ b/docs/amd-memory-encryption.txt @@ -0,0 +1,109 @@ +Secure Encrypted Virtualization (SEV) is a feature found on AMD processors. + +SEV is an extension to the AMD-V architecture which supports running encrypted +virtual machine (VMs) under the control of KVM. Encrypted VMs have their pages +(code and data) secured such that only the guest itself has access to the +unencrypted version. Each encrypted VM is associated with a unique encryption +key; if its data is accessed to a different entity using a different key the +encrypted guests data will be incorrectly decrypted, leading to unintelligible +data. + +The key management of this feature is handled by separate processor known as +AMD secure processor (AMD-SP) which is present in AMD SOCs. Firmware running +inside the AMD-SP provide commands to support common VM lifecycle. This +includes commands for launching, snapshotting, migrating and debugging the +encrypted guest. Those SEV command can be issued via KVM_MEMORY_ENCRYPT_OP +ioctls. + +Launching +--------- +Boot images (such as bios) must be encrypted before guest can be booted. +MEMORY_ENCRYPT_OP ioctl provides commands to encrypt the images :LAUNCH_START, +LAUNCH_UPDATE_DATA, LAUNCH_MEASURE and LAUNCH_FINISH. These four commands +together generate a fresh memory encryption key for the VM, encrypt the boot +images and provide a measurement than can be used as an attestation of the +successful launch. + +LAUNCH_START is called first to create a cryptographic launch context within +the firmware. To create this context, guest owner must provides guest policy, +its public Diffie-Hellman key (PDH) and session parameters. These inputs +should be treated as binary blob and must be passed as-is to the SEV firmware. + +The guest policy is passed as plaintext and hypervisor may able to read it +but should not modify it (any modification of the policy bits will result +in bad measurement). The guest policy is a 4-byte data structure containing +several flags that restricts what can be done on running SEV guest. +See KM Spec section 3 and 6.2 for more details. + +The guest policy can be provided via the 'policy' property (see below) + +# ${QEMU} \ + sev-guest,id=sev0,policy=0x1...\ + +Guest owners provided DH certificate and session parameters will be used to +establish a cryptographic session with the guest owner to negotiate keys used +for the attestation. + +The DH certificate and session blob can be provided via 'dh-cert-file' and +'session-file' property (see below + +# ${QEMU} \ + sev-guest,id=sev0,dh-cert-file=<file1>,session-file=<file2> + +LAUNCH_UPDATE_DATA encrypts the memory region using the cryptographic context +created via LAUNCH_START command. If required, this command can be called +multiple times to encrypt different memory regions. The command also calculates +the measurement of the memory contents as it encrypts. + +LAUNCH_MEASURE command can be used to retrieve the measurement of encrypted +memory. This measurement is a signature of the memory contents that can be +sent to the guest owner as an attestation that the memory was encrypted +correctly by the firmware. The guest owner may wait to provide the guest +confidential information until it can verify the attestation measurement. +Since the guest owner knows the initial contents of the guest at boot, the +attestation measurement can be verified by comparing it to what the guest owner +expects. + +LAUNCH_FINISH command finalizes the guest launch and destroy's the cryptographic +context. + +See SEV KM API Spec [1] 'Launching a guest' usage flow (Appendix A) for the +complete flow chart. + +To launch a SEV guest + +# ${QEMU} \ + -machine ...,memory-encryption=sev0 \ + -object sev-guest,id=sev0,cbitpos=47,reduced-phys-bits=1 + +Debugging +----------- +Since memory contents of SEV guest is encrypted hence hypervisor access to the +guest memory will get a cipher text. If guest policy allows debugging, then +hypervisor can use DEBUG_DECRYPT and DEBUG_ENCRYPT commands access the guest +memory region for debug purposes. This is not supported in QEMU yet. + +Snapshot/Restore +----------------- +TODO + +Live Migration +---------------- +TODO + +References +----------------- + +AMD Memory Encryption whitepaper: +http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2013/12/AMD_Memory_Encryption_Whitepaper_v7-Public.pdf + +Secure Encrypted Virutualization Key Management: +[1] http://support.amd.com/TechDocs/55766_SEV-KM API_Specification.pdf + +KVM Forum slides: +http://www.linux-kvm.org/images/7/74/02x08A-Thomas_Lendacky-AMDs_Virtualizatoin_Memory_Encryption_Technology.pdf + +AMD64 Architecture Programmer's Manual: + http://support.amd.com/TechDocs/24593.pdf + SME is section 7.10 + SEV is section 15.34 diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index ad590a4ffb..ddfcd5adcc 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -867,6 +867,22 @@ Display the amount of initially allocated and present hotpluggable (if enabled) memory in bytes. ETEXI +#if defined(TARGET_I386) + { + .name = "sev", + .args_type = "", + .params = "", + .help = "show SEV information", + .cmd = hmp_info_sev, + }, +#endif + +STEXI +@item info sev +@findex info sev +Show SEV information. +ETEXI + STEXI @end table ETEXI diff --git a/hmp-commands.hx b/hmp-commands.hx index 964eb515cf..1723cbe1df 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -253,9 +253,10 @@ ETEXI { .name = "screendump", - .args_type = "filename:F", - .params = "filename", - .help = "save screen into PPM image 'filename'", + .args_type = "filename:F,device:s?,head:i?", + .params = "filename [device [head]]", + .help = "save screen from head 'head' of display device 'device' " + "into PPM image 'filename'", .cmd = hmp_screendump, }, @@ -2140,9 +2140,11 @@ err_out: void hmp_screendump(Monitor *mon, const QDict *qdict) { const char *filename = qdict_get_str(qdict, "filename"); + const char *id = qdict_get_try_str(qdict, "device"); + int64_t head = qdict_get_try_int(qdict, "head", 0); Error *err = NULL; - qmp_screendump(filename, &err); + qmp_screendump(filename, id != NULL, id, id != NULL, head, &err); hmp_handle_error(mon, &err); } @@ -143,5 +143,6 @@ void hmp_info_ramblock(Monitor *mon, const QDict *qdict); void hmp_hotpluggable_cpus(Monitor *mon, const QDict *qdict); void hmp_info_vm_generation_id(Monitor *mon, const QDict *qdict); void hmp_info_memory_size_summary(Monitor *mon, const QDict *qdict); +void hmp_info_sev(Monitor *mon, const QDict *qdict); #endif diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 232258160a..2885e3e234 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -1,15 +1,27 @@ -obj-y += boot.o collie.o exynos4_boards.o gumstix.o highbank.o -obj-$(CONFIG_DIGIC) += digic_boards.o -obj-y += integratorcp.o mainstone.o musicpal.o nseries.o -obj-y += omap_sx1.o palm.o realview.o spitz.o stellaris.o -obj-y += tosa.o versatilepb.o vexpress.o virt.o xilinx_zynq.o z2.o +obj-y += boot.o virt.o sysbus-fdt.o obj-$(CONFIG_ACPI) += virt-acpi-build.o -obj-y += netduino2.o -obj-y += sysbus-fdt.o +obj-$(CONFIG_DIGIC) += digic_boards.o +obj-$(CONFIG_EXYNOS4) += exynos4_boards.o +obj-$(CONFIG_HIGHBANK) += highbank.o +obj-$(CONFIG_INTEGRATOR) += integratorcp.o +obj-$(CONFIG_MAINSTONE) += mainstone.o +obj-$(CONFIG_MUSICPAL) += musicpal.o +obj-$(CONFIG_NETDUINO2) += netduino2.o +obj-$(CONFIG_NSERIES) += nseries.o +obj-$(CONFIG_OMAP) += omap_sx1.o palm.o +obj-$(CONFIG_PXA2XX) += gumstix.o spitz.o tosa.o z2.o +obj-$(CONFIG_REALVIEW) += realview.o +obj-$(CONFIG_STELLARIS) += stellaris.o +obj-$(CONFIG_STRONGARM) += collie.o +obj-$(CONFIG_VERSATILE) += vexpress.o versatilepb.o +obj-$(CONFIG_ZYNQ) += xilinx_zynq.o -obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o +obj-$(CONFIG_ARM_V7M) += armv7m.o +obj-$(CONFIG_EXYNOS4) += exynos4210.o +obj-$(CONFIG_PXA2XX) += pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o obj-$(CONFIG_DIGIC) += digic.o -obj-y += omap1.o omap2.o strongarm.o +obj-$(CONFIG_OMAP) += omap1.o omap2.o +obj-$(CONFIG_STRONGARM) += strongarm.o obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o cubieboard.o obj-$(CONFIG_RASPI) += bcm2835_peripherals.o bcm2836.o raspi.o obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o @@ -22,3 +34,4 @@ obj-$(CONFIG_MPS2) += mps2.o obj-$(CONFIG_MPS2) += mps2-tz.o obj-$(CONFIG_MSF2) += msf2-soc.o msf2-som.o obj-$(CONFIG_IOTKIT) += iotkit.o +obj-$(CONFIG_FSL_IMX7) += fsl-imx7.o mcimx7d-sabre.o diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 6d0c92ab88..196c7fb242 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -829,6 +829,7 @@ static uint64_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry, load_elf_hdr(info->kernel_filename, &elf_header, &elf_is64, &err); if (err) { + error_free(err); return ret; } @@ -890,7 +891,8 @@ static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base, } /* check the arm64 magic header value -- very old kernels may not have it */ - if (memcmp(buffer + ARM64_MAGIC_OFFSET, "ARM\x64", 4) == 0) { + if (size > ARM64_MAGIC_OFFSET + 4 && + memcmp(buffer + ARM64_MAGIC_OFFSET, "ARM\x64", 4) == 0) { uint64_t hdrvals[2]; /* The arm64 Image header has text_offset and image_size fields at 8 and diff --git a/hw/arm/fsl-imx7.c b/hw/arm/fsl-imx7.c new file mode 100644 index 0000000000..26ef36c79a --- /dev/null +++ b/hw/arm/fsl-imx7.c @@ -0,0 +1,582 @@ +/* + * Copyright (c) 2018, Impinj, Inc. + * + * i.MX7 SoC definitions + * + * Author: Andrey Smirnov <andrew.smirnov@gmail.com> + * + * Based on hw/arm/fsl-imx6.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "hw/arm/fsl-imx7.h" +#include "hw/misc/unimp.h" +#include "sysemu/sysemu.h" +#include "qemu/error-report.h" + +#define NAME_SIZE 20 + +static void fsl_imx7_init(Object *obj) +{ + BusState *sysbus = sysbus_get_default(); + FslIMX7State *s = FSL_IMX7(obj); + char name[NAME_SIZE]; + int i; + + if (smp_cpus > FSL_IMX7_NUM_CPUS) { + error_report("%s: Only %d CPUs are supported (%d requested)", + TYPE_FSL_IMX7, FSL_IMX7_NUM_CPUS, smp_cpus); + exit(1); + } + + for (i = 0; i < smp_cpus; i++) { + object_initialize(&s->cpu[i], sizeof(s->cpu[i]), + ARM_CPU_TYPE_NAME("cortex-a7")); + snprintf(name, NAME_SIZE, "cpu%d", i); + object_property_add_child(obj, name, OBJECT(&s->cpu[i]), + &error_fatal); + } + + /* + * A7MPCORE + */ + object_initialize(&s->a7mpcore, sizeof(s->a7mpcore), TYPE_A15MPCORE_PRIV); + qdev_set_parent_bus(DEVICE(&s->a7mpcore), sysbus); + object_property_add_child(obj, "a7mpcore", + OBJECT(&s->a7mpcore), &error_fatal); + + /* + * GPIOs 1 to 7 + */ + for (i = 0; i < FSL_IMX7_NUM_GPIOS; i++) { + object_initialize(&s->gpio[i], sizeof(s->gpio[i]), + TYPE_IMX_GPIO); + qdev_set_parent_bus(DEVICE(&s->gpio[i]), sysbus); + snprintf(name, NAME_SIZE, "gpio%d", i); + object_property_add_child(obj, name, + OBJECT(&s->gpio[i]), &error_fatal); + } + + /* + * GPT1, 2, 3, 4 + */ + for (i = 0; i < FSL_IMX7_NUM_GPTS; i++) { + object_initialize(&s->gpt[i], sizeof(s->gpt[i]), TYPE_IMX7_GPT); + qdev_set_parent_bus(DEVICE(&s->gpt[i]), sysbus); + snprintf(name, NAME_SIZE, "gpt%d", i); + object_property_add_child(obj, name, OBJECT(&s->gpt[i]), + &error_fatal); + } + + /* + * CCM + */ + object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX7_CCM); + qdev_set_parent_bus(DEVICE(&s->ccm), sysbus); + object_property_add_child(obj, "ccm", OBJECT(&s->ccm), &error_fatal); + + /* + * Analog + */ + object_initialize(&s->analog, sizeof(s->analog), TYPE_IMX7_ANALOG); + qdev_set_parent_bus(DEVICE(&s->analog), sysbus); + object_property_add_child(obj, "analog", OBJECT(&s->analog), &error_fatal); + + /* + * GPCv2 + */ + object_initialize(&s->gpcv2, sizeof(s->gpcv2), TYPE_IMX_GPCV2); + qdev_set_parent_bus(DEVICE(&s->gpcv2), sysbus); + object_property_add_child(obj, "gpcv2", OBJECT(&s->gpcv2), &error_fatal); + + for (i = 0; i < FSL_IMX7_NUM_ECSPIS; i++) { + object_initialize(&s->spi[i], sizeof(s->spi[i]), TYPE_IMX_SPI); + qdev_set_parent_bus(DEVICE(&s->spi[i]), sysbus_get_default()); + snprintf(name, NAME_SIZE, "spi%d", i + 1); + object_property_add_child(obj, name, OBJECT(&s->spi[i]), NULL); + } + + + for (i = 0; i < FSL_IMX7_NUM_I2CS; i++) { + object_initialize(&s->i2c[i], sizeof(s->i2c[i]), TYPE_IMX_I2C); + qdev_set_parent_bus(DEVICE(&s->i2c[i]), sysbus_get_default()); + snprintf(name, NAME_SIZE, "i2c%d", i + 1); + object_property_add_child(obj, name, OBJECT(&s->i2c[i]), NULL); + } + + /* + * UART + */ + for (i = 0; i < FSL_IMX7_NUM_UARTS; i++) { + object_initialize(&s->uart[i], sizeof(s->uart[i]), TYPE_IMX_SERIAL); + qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus); + snprintf(name, NAME_SIZE, "uart%d", i); + object_property_add_child(obj, name, OBJECT(&s->uart[i]), + &error_fatal); + } + + /* + * Ethernet + */ + for (i = 0; i < FSL_IMX7_NUM_ETHS; i++) { + object_initialize(&s->eth[i], sizeof(s->eth[i]), TYPE_IMX_ENET); + qdev_set_parent_bus(DEVICE(&s->eth[i]), sysbus); + snprintf(name, NAME_SIZE, "eth%d", i); + object_property_add_child(obj, name, OBJECT(&s->eth[i]), + &error_fatal); + } + + /* + * SDHCI + */ + for (i = 0; i < FSL_IMX7_NUM_USDHCS; i++) { + object_initialize(&s->usdhc[i], sizeof(s->usdhc[i]), + TYPE_IMX_USDHC); + qdev_set_parent_bus(DEVICE(&s->usdhc[i]), sysbus); + snprintf(name, NAME_SIZE, "usdhc%d", i); + object_property_add_child(obj, name, OBJECT(&s->usdhc[i]), + &error_fatal); + } + + /* + * SNVS + */ + object_initialize(&s->snvs, sizeof(s->snvs), TYPE_IMX7_SNVS); + qdev_set_parent_bus(DEVICE(&s->snvs), sysbus); + object_property_add_child(obj, "snvs", OBJECT(&s->snvs), &error_fatal); + + /* + * Watchdog + */ + for (i = 0; i < FSL_IMX7_NUM_WDTS; i++) { + object_initialize(&s->wdt[i], sizeof(s->wdt[i]), TYPE_IMX2_WDT); + qdev_set_parent_bus(DEVICE(&s->wdt[i]), sysbus); + snprintf(name, NAME_SIZE, "wdt%d", i); + object_property_add_child(obj, name, OBJECT(&s->wdt[i]), + &error_fatal); + } + + /* + * GPR + */ + object_initialize(&s->gpr, sizeof(s->gpr), TYPE_IMX7_GPR); + qdev_set_parent_bus(DEVICE(&s->gpr), sysbus); + object_property_add_child(obj, "gpr", OBJECT(&s->gpr), &error_fatal); + + object_initialize(&s->pcie, sizeof(s->pcie), TYPE_DESIGNWARE_PCIE_HOST); + qdev_set_parent_bus(DEVICE(&s->pcie), sysbus); + object_property_add_child(obj, "pcie", OBJECT(&s->pcie), &error_fatal); + + for (i = 0; i < FSL_IMX7_NUM_USBS; i++) { + object_initialize(&s->usb[i], + sizeof(s->usb[i]), TYPE_CHIPIDEA); + qdev_set_parent_bus(DEVICE(&s->usb[i]), sysbus); + snprintf(name, NAME_SIZE, "usb%d", i); + object_property_add_child(obj, name, + OBJECT(&s->usb[i]), &error_fatal); + } +} + +static void fsl_imx7_realize(DeviceState *dev, Error **errp) +{ + FslIMX7State *s = FSL_IMX7(dev); + Object *o; + int i; + qemu_irq irq; + char name[NAME_SIZE]; + + for (i = 0; i < smp_cpus; i++) { + o = OBJECT(&s->cpu[i]); + + object_property_set_int(o, QEMU_PSCI_CONDUIT_SMC, + "psci-conduit", &error_abort); + + /* On uniprocessor, the CBAR is set to 0 */ + if (smp_cpus > 1) { + object_property_set_int(o, FSL_IMX7_A7MPCORE_ADDR, + "reset-cbar", &error_abort); + } + + if (i) { + /* Secondary CPUs start in PSCI powered-down state */ + object_property_set_bool(o, true, + "start-powered-off", &error_abort); + } + + object_property_set_bool(o, true, "realized", &error_abort); + } + + /* + * A7MPCORE + */ + object_property_set_int(OBJECT(&s->a7mpcore), smp_cpus, "num-cpu", + &error_abort); + object_property_set_int(OBJECT(&s->a7mpcore), + FSL_IMX7_MAX_IRQ + GIC_INTERNAL, + "num-irq", &error_abort); + + object_property_set_bool(OBJECT(&s->a7mpcore), true, "realized", + &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->a7mpcore), 0, FSL_IMX7_A7MPCORE_ADDR); + + for (i = 0; i < smp_cpus; i++) { + SysBusDevice *sbd = SYS_BUS_DEVICE(&s->a7mpcore); + DeviceState *d = DEVICE(qemu_get_cpu(i)); + + irq = qdev_get_gpio_in(d, ARM_CPU_IRQ); + sysbus_connect_irq(sbd, i, irq); + irq = qdev_get_gpio_in(d, ARM_CPU_FIQ); + sysbus_connect_irq(sbd, i + smp_cpus, irq); + } + + /* + * A7MPCORE DAP + */ + create_unimplemented_device("a7mpcore-dap", FSL_IMX7_A7MPCORE_DAP_ADDR, + 0x100000); + + /* + * GPT1, 2, 3, 4 + */ + for (i = 0; i < FSL_IMX7_NUM_GPTS; i++) { + static const hwaddr FSL_IMX7_GPTn_ADDR[FSL_IMX7_NUM_GPTS] = { + FSL_IMX7_GPT1_ADDR, + FSL_IMX7_GPT2_ADDR, + FSL_IMX7_GPT3_ADDR, + FSL_IMX7_GPT4_ADDR, + }; + + s->gpt[i].ccm = IMX_CCM(&s->ccm); + object_property_set_bool(OBJECT(&s->gpt[i]), true, "realized", + &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpt[i]), 0, FSL_IMX7_GPTn_ADDR[i]); + } + + for (i = 0; i < FSL_IMX7_NUM_GPIOS; i++) { + static const hwaddr FSL_IMX7_GPIOn_ADDR[FSL_IMX7_NUM_GPIOS] = { + FSL_IMX7_GPIO1_ADDR, + FSL_IMX7_GPIO2_ADDR, + FSL_IMX7_GPIO3_ADDR, + FSL_IMX7_GPIO4_ADDR, + FSL_IMX7_GPIO5_ADDR, + FSL_IMX7_GPIO6_ADDR, + FSL_IMX7_GPIO7_ADDR, + }; + + object_property_set_bool(OBJECT(&s->gpio[i]), true, "realized", + &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, FSL_IMX7_GPIOn_ADDR[i]); + } + + /* + * IOMUXC and IOMUXC_LPSR + */ + for (i = 0; i < FSL_IMX7_NUM_IOMUXCS; i++) { + static const hwaddr FSL_IMX7_IOMUXCn_ADDR[FSL_IMX7_NUM_IOMUXCS] = { + FSL_IMX7_IOMUXC_ADDR, + FSL_IMX7_IOMUXC_LPSR_ADDR, + }; + + snprintf(name, NAME_SIZE, "iomuxc%d", i); + create_unimplemented_device(name, FSL_IMX7_IOMUXCn_ADDR[i], + FSL_IMX7_IOMUXCn_SIZE); + } + + /* + * CCM + */ + object_property_set_bool(OBJECT(&s->ccm), true, "realized", &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccm), 0, FSL_IMX7_CCM_ADDR); + + /* + * Analog + */ + object_property_set_bool(OBJECT(&s->analog), true, "realized", + &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->analog), 0, FSL_IMX7_ANALOG_ADDR); + + /* + * GPCv2 + */ + object_property_set_bool(OBJECT(&s->gpcv2), true, + "realized", &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpcv2), 0, FSL_IMX7_GPC_ADDR); + + /* Initialize all ECSPI */ + for (i = 0; i < FSL_IMX7_NUM_ECSPIS; i++) { + static const hwaddr FSL_IMX7_SPIn_ADDR[FSL_IMX7_NUM_ECSPIS] = { + FSL_IMX7_ECSPI1_ADDR, + FSL_IMX7_ECSPI2_ADDR, + FSL_IMX7_ECSPI3_ADDR, + FSL_IMX7_ECSPI4_ADDR, + }; + + static const hwaddr FSL_IMX7_SPIn_IRQ[FSL_IMX7_NUM_ECSPIS] = { + FSL_IMX7_ECSPI1_IRQ, + FSL_IMX7_ECSPI2_IRQ, + FSL_IMX7_ECSPI3_IRQ, + FSL_IMX7_ECSPI4_IRQ, + }; + + /* Initialize the SPI */ + object_property_set_bool(OBJECT(&s->spi[i]), true, "realized", + &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, + FSL_IMX7_SPIn_ADDR[i]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi[i]), 0, + qdev_get_gpio_in(DEVICE(&s->a7mpcore), + FSL_IMX7_SPIn_IRQ[i])); + } + + for (i = 0; i < FSL_IMX7_NUM_I2CS; i++) { + static const hwaddr FSL_IMX7_I2Cn_ADDR[FSL_IMX7_NUM_I2CS] = { + FSL_IMX7_I2C1_ADDR, + FSL_IMX7_I2C2_ADDR, + FSL_IMX7_I2C3_ADDR, + FSL_IMX7_I2C4_ADDR, + }; + + static const hwaddr FSL_IMX7_I2Cn_IRQ[FSL_IMX7_NUM_I2CS] = { + FSL_IMX7_I2C1_IRQ, + FSL_IMX7_I2C2_IRQ, + FSL_IMX7_I2C3_IRQ, + FSL_IMX7_I2C4_IRQ, + }; + + object_property_set_bool(OBJECT(&s->i2c[i]), true, "realized", + &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c[i]), 0, FSL_IMX7_I2Cn_ADDR[i]); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[i]), 0, + qdev_get_gpio_in(DEVICE(&s->a7mpcore), + FSL_IMX7_I2Cn_IRQ[i])); + } + + /* + * UART + */ + for (i = 0; i < FSL_IMX7_NUM_UARTS; i++) { + static const hwaddr FSL_IMX7_UARTn_ADDR[FSL_IMX7_NUM_UARTS] = { + FSL_IMX7_UART1_ADDR, + FSL_IMX7_UART2_ADDR, + FSL_IMX7_UART3_ADDR, + FSL_IMX7_UART4_ADDR, + FSL_IMX7_UART5_ADDR, + FSL_IMX7_UART6_ADDR, + FSL_IMX7_UART7_ADDR, + }; + + static const int FSL_IMX7_UARTn_IRQ[FSL_IMX7_NUM_UARTS] = { + FSL_IMX7_UART1_IRQ, + FSL_IMX7_UART2_IRQ, + FSL_IMX7_UART3_IRQ, + FSL_IMX7_UART4_IRQ, + FSL_IMX7_UART5_IRQ, + FSL_IMX7_UART6_IRQ, + FSL_IMX7_UART7_IRQ, + }; + + + if (i < MAX_SERIAL_PORTS) { + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hds[i]); + } + + object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", + &error_abort); + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, FSL_IMX7_UARTn_ADDR[i]); + + irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_UARTn_IRQ[i]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, irq); + } + + /* + * Ethernet + */ + for (i = 0; i < FSL_IMX7_NUM_ETHS; i++) { + static const hwaddr FSL_IMX7_ENETn_ADDR[FSL_IMX7_NUM_ETHS] = { + FSL_IMX7_ENET1_ADDR, + FSL_IMX7_ENET2_ADDR, + }; + + object_property_set_uint(OBJECT(&s->eth[i]), FSL_IMX7_ETH_NUM_TX_RINGS, + "tx-ring-num", &error_abort); + qdev_set_nic_properties(DEVICE(&s->eth[i]), &nd_table[i]); + object_property_set_bool(OBJECT(&s->eth[i]), true, "realized", + &error_abort); + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->eth[i]), 0, FSL_IMX7_ENETn_ADDR[i]); + + irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_ENET_IRQ(i, 0)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->eth[i]), 0, irq); + irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_ENET_IRQ(i, 3)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->eth[i]), 1, irq); + } + + /* + * USDHC + */ + for (i = 0; i < FSL_IMX7_NUM_USDHCS; i++) { + static const hwaddr FSL_IMX7_USDHCn_ADDR[FSL_IMX7_NUM_USDHCS] = { + FSL_IMX7_USDHC1_ADDR, + FSL_IMX7_USDHC2_ADDR, + FSL_IMX7_USDHC3_ADDR, + }; + + static const int FSL_IMX7_USDHCn_IRQ[FSL_IMX7_NUM_USDHCS] = { + FSL_IMX7_USDHC1_IRQ, + FSL_IMX7_USDHC2_IRQ, + FSL_IMX7_USDHC3_IRQ, + }; + + object_property_set_bool(OBJECT(&s->usdhc[i]), true, "realized", + &error_abort); + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->usdhc[i]), 0, + FSL_IMX7_USDHCn_ADDR[i]); + + irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_USDHCn_IRQ[i]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->usdhc[i]), 0, irq); + } + + /* + * SNVS + */ + object_property_set_bool(OBJECT(&s->snvs), true, "realized", &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->snvs), 0, FSL_IMX7_SNVS_ADDR); + + /* + * SRC + */ + create_unimplemented_device("sdma", FSL_IMX7_SRC_ADDR, FSL_IMX7_SRC_SIZE); + + /* + * Watchdog + */ + for (i = 0; i < FSL_IMX7_NUM_WDTS; i++) { + static const hwaddr FSL_IMX7_WDOGn_ADDR[FSL_IMX7_NUM_WDTS] = { + FSL_IMX7_WDOG1_ADDR, + FSL_IMX7_WDOG2_ADDR, + FSL_IMX7_WDOG3_ADDR, + FSL_IMX7_WDOG4_ADDR, + }; + + object_property_set_bool(OBJECT(&s->wdt[i]), true, "realized", + &error_abort); + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0, FSL_IMX7_WDOGn_ADDR[i]); + } + + /* + * SDMA + */ + create_unimplemented_device("sdma", FSL_IMX7_SDMA_ADDR, FSL_IMX7_SDMA_SIZE); + + + object_property_set_bool(OBJECT(&s->gpr), true, "realized", + &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpr), 0, FSL_IMX7_GPR_ADDR); + + object_property_set_bool(OBJECT(&s->pcie), true, + "realized", &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->pcie), 0, FSL_IMX7_PCIE_REG_ADDR); + + irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTA_IRQ); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 0, irq); + irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTB_IRQ); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 1, irq); + irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTC_IRQ); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 2, irq); + irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_PCI_INTD_IRQ); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 3, irq); + + + for (i = 0; i < FSL_IMX7_NUM_USBS; i++) { + static const hwaddr FSL_IMX7_USBMISCn_ADDR[FSL_IMX7_NUM_USBS] = { + FSL_IMX7_USBMISC1_ADDR, + FSL_IMX7_USBMISC2_ADDR, + FSL_IMX7_USBMISC3_ADDR, + }; + + static const hwaddr FSL_IMX7_USBn_ADDR[FSL_IMX7_NUM_USBS] = { + FSL_IMX7_USB1_ADDR, + FSL_IMX7_USB2_ADDR, + FSL_IMX7_USB3_ADDR, + }; + + static const hwaddr FSL_IMX7_USBn_IRQ[FSL_IMX7_NUM_USBS] = { + FSL_IMX7_USB1_IRQ, + FSL_IMX7_USB2_IRQ, + FSL_IMX7_USB3_IRQ, + }; + + object_property_set_bool(OBJECT(&s->usb[i]), true, "realized", + &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->usb[i]), 0, + FSL_IMX7_USBn_ADDR[i]); + + irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore), FSL_IMX7_USBn_IRQ[i]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->usb[i]), 0, irq); + + snprintf(name, NAME_SIZE, "usbmisc%d", i); + create_unimplemented_device(name, FSL_IMX7_USBMISCn_ADDR[i], + FSL_IMX7_USBMISCn_SIZE); + } + + /* + * ADCs + */ + for (i = 0; i < FSL_IMX7_NUM_ADCS; i++) { + static const hwaddr FSL_IMX7_ADCn_ADDR[FSL_IMX7_NUM_ADCS] = { + FSL_IMX7_ADC1_ADDR, + FSL_IMX7_ADC2_ADDR, + }; + + snprintf(name, NAME_SIZE, "adc%d", i); + create_unimplemented_device(name, FSL_IMX7_ADCn_ADDR[i], + FSL_IMX7_ADCn_SIZE); + } + + /* + * LCD + */ + create_unimplemented_device("lcdif", FSL_IMX7_LCDIF_ADDR, + FSL_IMX7_LCDIF_SIZE); +} + +static void fsl_imx7_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = fsl_imx7_realize; + + /* Reason: Uses serial_hds and nd_table in realize() directly */ + dc->user_creatable = false; + dc->desc = "i.MX7 SOC"; +} + +static const TypeInfo fsl_imx7_type_info = { + .name = TYPE_FSL_IMX7, + .parent = TYPE_DEVICE, + .instance_size = sizeof(FslIMX7State), + .instance_init = fsl_imx7_init, + .class_init = fsl_imx7_class_init, +}; + +static void fsl_imx7_register_types(void) +{ + type_register_static(&fsl_imx7_type_info); +} +type_init(fsl_imx7_register_types) diff --git a/hw/arm/mcimx7d-sabre.c b/hw/arm/mcimx7d-sabre.c new file mode 100644 index 0000000000..95fb409d9c --- /dev/null +++ b/hw/arm/mcimx7d-sabre.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2018, Impinj, Inc. + * + * MCIMX7D_SABRE Board System emulation. + * + * Author: Andrey Smirnov <andrew.smirnov@gmail.com> + * + * This code is licensed under the GPL, version 2 or later. + * See the file `COPYING' in the top level directory. + * + * It (partially) emulates a mcimx7d_sabre board, with a Freescale + * i.MX7 SoC + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "hw/arm/fsl-imx7.h" +#include "hw/boards.h" +#include "sysemu/sysemu.h" +#include "sysemu/device_tree.h" +#include "qemu/error-report.h" +#include "sysemu/qtest.h" +#include "net/net.h" + +typedef struct { + FslIMX7State soc; + MemoryRegion ram; +} MCIMX7Sabre; + +static void mcimx7d_sabre_init(MachineState *machine) +{ + static struct arm_boot_info boot_info; + MCIMX7Sabre *s = g_new0(MCIMX7Sabre, 1); + Object *soc; + int i; + + if (machine->ram_size > FSL_IMX7_MMDC_SIZE) { + error_report("RAM size " RAM_ADDR_FMT " above max supported (%08x)", + machine->ram_size, FSL_IMX7_MMDC_SIZE); + exit(1); + } + + boot_info = (struct arm_boot_info) { + .loader_start = FSL_IMX7_MMDC_ADDR, + .board_id = -1, + .ram_size = machine->ram_size, + .kernel_filename = machine->kernel_filename, + .kernel_cmdline = machine->kernel_cmdline, + .initrd_filename = machine->initrd_filename, + .nb_cpus = smp_cpus, + }; + + object_initialize(&s->soc, sizeof(s->soc), TYPE_FSL_IMX7); + soc = OBJECT(&s->soc); + object_property_add_child(OBJECT(machine), "soc", soc, &error_fatal); + object_property_set_bool(soc, true, "realized", &error_fatal); + + memory_region_allocate_system_memory(&s->ram, NULL, "mcimx7d-sabre.ram", + machine->ram_size); + memory_region_add_subregion(get_system_memory(), + FSL_IMX7_MMDC_ADDR, &s->ram); + + for (i = 0; i < FSL_IMX7_NUM_USDHCS; i++) { + BusState *bus; + DeviceState *carddev; + DriveInfo *di; + BlockBackend *blk; + + di = drive_get_next(IF_SD); + blk = di ? blk_by_legacy_dinfo(di) : NULL; + bus = qdev_get_child_bus(DEVICE(&s->soc.usdhc[i]), "sd-bus"); + carddev = qdev_create(bus, TYPE_SD_CARD); + qdev_prop_set_drive(carddev, "drive", blk, &error_fatal); + object_property_set_bool(OBJECT(carddev), true, + "realized", &error_fatal); + } + + if (!qtest_enabled()) { + arm_load_kernel(&s->soc.cpu[0], &boot_info); + } +} + +static void mcimx7d_sabre_machine_init(MachineClass *mc) +{ + mc->desc = "Freescale i.MX7 DUAL SABRE (Cortex A7)"; + mc->init = mcimx7d_sabre_init; + mc->max_cpus = FSL_IMX7_NUM_CPUS; +} +DEFINE_MACHINE("mcimx7d-sabre", mcimx7d_sabre_machine_init) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index dbb3c8036a..2c07245047 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -169,6 +169,7 @@ static const char *valid_cpus[] = { ARM_CPU_TYPE_NAME("cortex-a53"), ARM_CPU_TYPE_NAME("cortex-a57"), ARM_CPU_TYPE_NAME("host"), + ARM_CPU_TYPE_NAME("max"), }; static bool cpu_type_valid(const char *cpu) @@ -1206,16 +1207,23 @@ static void machvirt_init(MachineState *machine) /* We can probe only here because during property set * KVM is not available yet */ - if (!vms->gic_version) { + if (vms->gic_version <= 0) { + /* "host" or "max" */ if (!kvm_enabled()) { - error_report("gic-version=host requires KVM"); - exit(1); - } - - vms->gic_version = kvm_arm_vgic_probe(); - if (!vms->gic_version) { - error_report("Unable to determine GIC version supported by host"); - exit(1); + if (vms->gic_version == 0) { + error_report("gic-version=host requires KVM"); + exit(1); + } else { + /* "max": currently means 3 for TCG */ + vms->gic_version = 3; + } + } else { + vms->gic_version = kvm_arm_vgic_probe(); + if (!vms->gic_version) { + error_report( + "Unable to determine GIC version supported by host"); + exit(1); + } } } @@ -1479,9 +1487,11 @@ static void virt_set_gic_version(Object *obj, const char *value, Error **errp) vms->gic_version = 2; } else if (!strcmp(value, "host")) { vms->gic_version = 0; /* Will probe later */ + } else if (!strcmp(value, "max")) { + vms->gic_version = -1; /* Will probe later */ } else { error_setg(errp, "Invalid gic-version value"); - error_append_hint(errp, "Valid values are 3, 2, host.\n"); + error_append_hint(errp, "Valid values are 3, 2, host, max.\n"); } } diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index 69227fd4c9..465796e97c 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -282,6 +282,8 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) s->virt, "has_el2", NULL); object_property_set_int(OBJECT(&s->apu_cpu[i]), GIC_BASE_ADDR, "reset-cbar", &error_abort); + object_property_set_int(OBJECT(&s->apu_cpu[i]), num_apus, + "core-count", &error_abort); object_property_set_bool(OBJECT(&s->apu_cpu[i]), true, "realized", &err); if (err) { diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c index 2cb990997e..101f32cf66 100644 --- a/hw/block/dataplane/virtio-blk.c +++ b/hw/block/dataplane/virtio-blk.c @@ -34,6 +34,7 @@ struct VirtIOBlockDataPlane { VirtIODevice *vdev; QEMUBH *bh; /* bh for guest notification */ unsigned long *batch_notify_vqs; + bool batch_notifications; /* Note that these EventNotifiers are assigned by value. This is * fine as long as you do not call event_notifier_cleanup on them @@ -47,8 +48,12 @@ struct VirtIOBlockDataPlane { /* Raise an interrupt to signal guest, if necessary */ void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s, VirtQueue *vq) { - set_bit(virtio_get_queue_index(vq), s->batch_notify_vqs); - qemu_bh_schedule(s->bh); + if (s->batch_notifications) { + set_bit(virtio_get_queue_index(vq), s->batch_notify_vqs); + qemu_bh_schedule(s->bh); + } else { + virtio_notify_irqfd(s->vdev, vq); + } } static void notify_guest_bh(void *opaque) @@ -177,6 +182,12 @@ int virtio_blk_data_plane_start(VirtIODevice *vdev) s->starting = true; + if (!virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { + s->batch_notifications = true; + } else { + s->batch_notifications = false; + } + /* Set up guest notifier (irq) */ r = k->set_guest_notifiers(qbus->parent, nvqs, true); if (r != 0) { @@ -229,6 +240,22 @@ int virtio_blk_data_plane_start(VirtIODevice *vdev) return -ENOSYS; } +/* Stop notifications for new requests from guest. + * + * Context: BH in IOThread + */ +static void virtio_blk_data_plane_stop_bh(void *opaque) +{ + VirtIOBlockDataPlane *s = opaque; + unsigned i; + + for (i = 0; i < s->conf->num_queues; i++) { + VirtQueue *vq = virtio_get_queue(s->vdev, i); + + virtio_queue_aio_set_host_notifier_handler(vq, s->ctx, NULL); + } +} + /* Context: QEMU global mutex held */ void virtio_blk_data_plane_stop(VirtIODevice *vdev) { @@ -253,13 +280,7 @@ void virtio_blk_data_plane_stop(VirtIODevice *vdev) trace_virtio_blk_data_plane_stop(s); aio_context_acquire(s->ctx); - - /* Stop notifications for new requests from guest */ - for (i = 0; i < nvqs; i++) { - VirtQueue *vq = virtio_get_queue(s->vdev, i); - - virtio_queue_aio_set_host_notifier_handler(vq, s->ctx, NULL); - } + aio_wait_bh_oneshot(s->ctx, virtio_blk_data_plane_stop_bh, s); /* Drain and switch bs back to the QEMU main loop */ blk_set_aio_context(s->conf->conf.blk, qemu_get_aio_context()); diff --git a/hw/core/machine.c b/hw/core/machine.c index 5e2bbcdace..2040177664 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -334,6 +334,22 @@ static bool machine_get_enforce_config_section(Object *obj, Error **errp) return ms->enforce_config_section; } +static char *machine_get_memory_encryption(Object *obj, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + return g_strdup(ms->memory_encryption); +} + +static void machine_set_memory_encryption(Object *obj, const char *value, + Error **errp) +{ + MachineState *ms = MACHINE(obj); + + g_free(ms->memory_encryption); + ms->memory_encryption = g_strdup(value); +} + void machine_class_allow_dynamic_sysbus_dev(MachineClass *mc, const char *type) { strList *item = g_new0(strList, 1); @@ -612,6 +628,12 @@ static void machine_class_init(ObjectClass *oc, void *data) &error_abort); object_class_property_set_description(oc, "enforce-config-section", "Set on to enforce configuration section migration", &error_abort); + + object_class_property_add_str(oc, "memory-encryption", + machine_get_memory_encryption, machine_set_memory_encryption, + &error_abort); + object_class_property_set_description(oc, "memory-encryption", + "Set memory encyption object to use", &error_abort); } static void machine_class_base_init(ObjectClass *oc, void *data) diff --git a/hw/display/vga.c b/hw/display/vga.c index 28f298b342..72181330b8 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -1483,6 +1483,8 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) region_start = (s->start_addr * 4); region_end = region_start + (ram_addr_t)s->line_offset * height; + region_end += width * s->get_bpp(s) / 8; /* scanline length */ + region_end -= s->line_offset; if (region_end > s->vbe_size) { /* wraps around (can happen with cirrus vbe modes) */ region_start = 0; diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index 4325575e7d..73ac783f20 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -113,6 +113,8 @@ static void pc_system_flash_init(MemoryRegion *rom_memory) pflash_t *system_flash; MemoryRegion *flash_mem; char name[64]; + void *flash_ptr; + int ret, flash_size; sector_bits = 12; sector_size = 1 << sector_bits; @@ -169,6 +171,17 @@ static void pc_system_flash_init(MemoryRegion *rom_memory) if (unit == 0) { flash_mem = pflash_cfi01_get_memory(system_flash); pc_isa_bios_init(rom_memory, flash_mem, size); + + /* Encrypt the pflash boot ROM */ + if (kvm_memcrypt_enabled()) { + flash_ptr = memory_region_get_ram_ptr(flash_mem); + flash_size = memory_region_size(flash_mem); + ret = kvm_memcrypt_encrypt_data(flash_ptr, flash_size); + if (ret) { + error_report("failed to encrypt pflash rom"); + exit(1); + } + } } } } diff --git a/hw/pci-host/Makefile.objs b/hw/pci-host/Makefile.objs index 4b69f737b5..6d6597c065 100644 --- a/hw/pci-host/Makefile.objs +++ b/hw/pci-host/Makefile.objs @@ -17,3 +17,5 @@ common-obj-$(CONFIG_PCI_PIIX) += piix.o common-obj-$(CONFIG_PCI_Q35) += q35.o common-obj-$(CONFIG_PCI_GENERIC) += gpex.o common-obj-$(CONFIG_PCI_XILINX) += xilinx-pcie.o + +common-obj-$(CONFIG_PCI_DESIGNWARE) += designware.o diff --git a/hw/pci-host/designware.c b/hw/pci-host/designware.c new file mode 100644 index 0000000000..29ea313798 --- /dev/null +++ b/hw/pci-host/designware.c @@ -0,0 +1,754 @@ +/* + * Copyright (c) 2018, Impinj, Inc. + * + * Designware PCIe IP block emulation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/pci/msi.h" +#include "hw/pci/pci_bridge.h" +#include "hw/pci/pci_host.h" +#include "hw/pci/pcie_port.h" +#include "hw/pci-host/designware.h" + +#define DESIGNWARE_PCIE_PORT_LINK_CONTROL 0x710 +#define DESIGNWARE_PCIE_PHY_DEBUG_R1 0x72C +#define DESIGNWARE_PCIE_PHY_DEBUG_R1_XMLH_LINK_UP BIT(4) +#define DESIGNWARE_PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C +#define DESIGNWARE_PCIE_PORT_LOGIC_SPEED_CHANGE BIT(17) +#define DESIGNWARE_PCIE_MSI_ADDR_LO 0x820 +#define DESIGNWARE_PCIE_MSI_ADDR_HI 0x824 +#define DESIGNWARE_PCIE_MSI_INTR0_ENABLE 0x828 +#define DESIGNWARE_PCIE_MSI_INTR0_MASK 0x82C +#define DESIGNWARE_PCIE_MSI_INTR0_STATUS 0x830 +#define DESIGNWARE_PCIE_ATU_VIEWPORT 0x900 +#define DESIGNWARE_PCIE_ATU_REGION_INBOUND BIT(31) +#define DESIGNWARE_PCIE_ATU_CR1 0x904 +#define DESIGNWARE_PCIE_ATU_TYPE_MEM (0x0 << 0) +#define DESIGNWARE_PCIE_ATU_CR2 0x908 +#define DESIGNWARE_PCIE_ATU_ENABLE BIT(31) +#define DESIGNWARE_PCIE_ATU_LOWER_BASE 0x90C +#define DESIGNWARE_PCIE_ATU_UPPER_BASE 0x910 +#define DESIGNWARE_PCIE_ATU_LIMIT 0x914 +#define DESIGNWARE_PCIE_ATU_LOWER_TARGET 0x918 +#define DESIGNWARE_PCIE_ATU_BUS(x) (((x) >> 24) & 0xff) +#define DESIGNWARE_PCIE_ATU_DEVFN(x) (((x) >> 16) & 0xff) +#define DESIGNWARE_PCIE_ATU_UPPER_TARGET 0x91C + +static DesignwarePCIEHost * +designware_pcie_root_to_host(DesignwarePCIERoot *root) +{ + BusState *bus = qdev_get_parent_bus(DEVICE(root)); + return DESIGNWARE_PCIE_HOST(bus->parent); +} + +static void designware_pcie_root_msi_write(void *opaque, hwaddr addr, + uint64_t val, unsigned len) +{ + DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(opaque); + DesignwarePCIEHost *host = designware_pcie_root_to_host(root); + + root->msi.intr[0].status |= BIT(val) & root->msi.intr[0].enable; + + if (root->msi.intr[0].status & ~root->msi.intr[0].mask) { + qemu_set_irq(host->pci.irqs[0], 1); + } +} + +static const MemoryRegionOps designware_pci_host_msi_ops = { + .write = designware_pcie_root_msi_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void designware_pcie_root_update_msi_mapping(DesignwarePCIERoot *root) + +{ + MemoryRegion *mem = &root->msi.iomem; + const uint64_t base = root->msi.base; + const bool enable = root->msi.intr[0].enable; + + memory_region_set_address(mem, base); + memory_region_set_enabled(mem, enable); +} + +static DesignwarePCIEViewport * +designware_pcie_root_get_current_viewport(DesignwarePCIERoot *root) +{ + const unsigned int idx = root->atu_viewport & 0xF; + const unsigned int dir = + !!(root->atu_viewport & DESIGNWARE_PCIE_ATU_REGION_INBOUND); + return &root->viewports[dir][idx]; +} + +static uint32_t +designware_pcie_root_config_read(PCIDevice *d, uint32_t address, int len) +{ + DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(d); + DesignwarePCIEViewport *viewport = + designware_pcie_root_get_current_viewport(root); + + uint32_t val; + + switch (address) { + case DESIGNWARE_PCIE_PORT_LINK_CONTROL: + /* + * Linux guest uses this register only to configure number of + * PCIE lane (which in our case is irrelevant) and doesn't + * really care about the value it reads from this register + */ + val = 0xDEADBEEF; + break; + + case DESIGNWARE_PCIE_LINK_WIDTH_SPEED_CONTROL: + /* + * To make sure that any code in guest waiting for speed + * change does not time out we always report + * PORT_LOGIC_SPEED_CHANGE as set + */ + val = DESIGNWARE_PCIE_PORT_LOGIC_SPEED_CHANGE; + break; + + case DESIGNWARE_PCIE_MSI_ADDR_LO: + val = root->msi.base; + break; + + case DESIGNWARE_PCIE_MSI_ADDR_HI: + val = root->msi.base >> 32; + break; + + case DESIGNWARE_PCIE_MSI_INTR0_ENABLE: + val = root->msi.intr[0].enable; + break; + + case DESIGNWARE_PCIE_MSI_INTR0_MASK: + val = root->msi.intr[0].mask; + break; + + case DESIGNWARE_PCIE_MSI_INTR0_STATUS: + val = root->msi.intr[0].status; + break; + + case DESIGNWARE_PCIE_PHY_DEBUG_R1: + val = DESIGNWARE_PCIE_PHY_DEBUG_R1_XMLH_LINK_UP; + break; + + case DESIGNWARE_PCIE_ATU_VIEWPORT: + val = root->atu_viewport; + break; + + case DESIGNWARE_PCIE_ATU_LOWER_BASE: + val = viewport->base; + break; + + case DESIGNWARE_PCIE_ATU_UPPER_BASE: + val = viewport->base >> 32; + break; + + case DESIGNWARE_PCIE_ATU_LOWER_TARGET: + val = viewport->target; + break; + + case DESIGNWARE_PCIE_ATU_UPPER_TARGET: + val = viewport->target >> 32; + break; + + case DESIGNWARE_PCIE_ATU_LIMIT: + val = viewport->limit; + break; + + case DESIGNWARE_PCIE_ATU_CR1: + case DESIGNWARE_PCIE_ATU_CR2: /* FALLTHROUGH */ + val = viewport->cr[(address - DESIGNWARE_PCIE_ATU_CR1) / + sizeof(uint32_t)]; + break; + + default: + val = pci_default_read_config(d, address, len); + break; + } + + return val; +} + +static uint64_t designware_pcie_root_data_access(void *opaque, hwaddr addr, + uint64_t *val, unsigned len) +{ + DesignwarePCIEViewport *viewport = opaque; + DesignwarePCIERoot *root = viewport->root; + + const uint8_t busnum = DESIGNWARE_PCIE_ATU_BUS(viewport->target); + const uint8_t devfn = DESIGNWARE_PCIE_ATU_DEVFN(viewport->target); + PCIBus *pcibus = pci_get_bus(PCI_DEVICE(root)); + PCIDevice *pcidev = pci_find_device(pcibus, busnum, devfn); + + if (pcidev) { + addr &= pci_config_size(pcidev) - 1; + + if (val) { + pci_host_config_write_common(pcidev, addr, + pci_config_size(pcidev), + *val, len); + } else { + return pci_host_config_read_common(pcidev, addr, + pci_config_size(pcidev), + len); + } + } + + return UINT64_MAX; +} + +static uint64_t designware_pcie_root_data_read(void *opaque, hwaddr addr, + unsigned len) +{ + return designware_pcie_root_data_access(opaque, addr, NULL, len); +} + +static void designware_pcie_root_data_write(void *opaque, hwaddr addr, + uint64_t val, unsigned len) +{ + designware_pcie_root_data_access(opaque, addr, &val, len); +} + +static const MemoryRegionOps designware_pci_host_conf_ops = { + .read = designware_pcie_root_data_read, + .write = designware_pcie_root_data_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static void designware_pcie_update_viewport(DesignwarePCIERoot *root, + DesignwarePCIEViewport *viewport) +{ + const uint64_t target = viewport->target; + const uint64_t base = viewport->base; + const uint64_t size = (uint64_t)viewport->limit - base + 1; + const bool enabled = viewport->cr[1] & DESIGNWARE_PCIE_ATU_ENABLE; + + MemoryRegion *current, *other; + + if (viewport->cr[0] == DESIGNWARE_PCIE_ATU_TYPE_MEM) { + current = &viewport->mem; + other = &viewport->cfg; + memory_region_set_alias_offset(current, target); + } else { + current = &viewport->cfg; + other = &viewport->mem; + } + + /* + * An outbound viewport can be reconfigure from being MEM to CFG, + * to account for that we disable the "other" memory region that + * becomes unused due to that fact. + */ + memory_region_set_enabled(other, false); + if (enabled) { + memory_region_set_size(current, size); + memory_region_set_address(current, base); + } + memory_region_set_enabled(current, enabled); +} + +static void designware_pcie_root_config_write(PCIDevice *d, uint32_t address, + uint32_t val, int len) +{ + DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(d); + DesignwarePCIEHost *host = designware_pcie_root_to_host(root); + DesignwarePCIEViewport *viewport = + designware_pcie_root_get_current_viewport(root); + + switch (address) { + case DESIGNWARE_PCIE_PORT_LINK_CONTROL: + case DESIGNWARE_PCIE_LINK_WIDTH_SPEED_CONTROL: + case DESIGNWARE_PCIE_PHY_DEBUG_R1: + /* No-op */ + break; + + case DESIGNWARE_PCIE_MSI_ADDR_LO: + root->msi.base &= 0xFFFFFFFF00000000ULL; + root->msi.base |= val; + break; + + case DESIGNWARE_PCIE_MSI_ADDR_HI: + root->msi.base &= 0x00000000FFFFFFFFULL; + root->msi.base |= (uint64_t)val << 32; + break; + + case DESIGNWARE_PCIE_MSI_INTR0_ENABLE: { + const bool update_msi_mapping = !root->msi.intr[0].enable ^ !!val; + + root->msi.intr[0].enable = val; + + if (update_msi_mapping) { + designware_pcie_root_update_msi_mapping(root); + } + break; + } + + case DESIGNWARE_PCIE_MSI_INTR0_MASK: + root->msi.intr[0].mask = val; + break; + + case DESIGNWARE_PCIE_MSI_INTR0_STATUS: + root->msi.intr[0].status ^= val; + if (!root->msi.intr[0].status) { + qemu_set_irq(host->pci.irqs[0], 0); + } + break; + + case DESIGNWARE_PCIE_ATU_VIEWPORT: + root->atu_viewport = val; + break; + + case DESIGNWARE_PCIE_ATU_LOWER_BASE: + viewport->base &= 0xFFFFFFFF00000000ULL; + viewport->base |= val; + break; + + case DESIGNWARE_PCIE_ATU_UPPER_BASE: + viewport->base &= 0x00000000FFFFFFFFULL; + viewport->base |= (uint64_t)val << 32; + break; + + case DESIGNWARE_PCIE_ATU_LOWER_TARGET: + viewport->target &= 0xFFFFFFFF00000000ULL; + viewport->target |= val; + break; + + case DESIGNWARE_PCIE_ATU_UPPER_TARGET: + viewport->target &= 0x00000000FFFFFFFFULL; + viewport->target |= val; + break; + + case DESIGNWARE_PCIE_ATU_LIMIT: + viewport->limit = val; + break; + + case DESIGNWARE_PCIE_ATU_CR1: + viewport->cr[0] = val; + break; + case DESIGNWARE_PCIE_ATU_CR2: + viewport->cr[1] = val; + designware_pcie_update_viewport(root, viewport); + break; + + default: + pci_bridge_write_config(d, address, val, len); + break; + } +} + +static char *designware_pcie_viewport_name(const char *direction, + unsigned int i, + const char *type) +{ + return g_strdup_printf("PCI %s Viewport %u [%s]", + direction, i, type); +} + +static void designware_pcie_root_realize(PCIDevice *dev, Error **errp) +{ + DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(dev); + DesignwarePCIEHost *host = designware_pcie_root_to_host(root); + MemoryRegion *address_space = &host->pci.memory; + PCIBridge *br = PCI_BRIDGE(dev); + DesignwarePCIEViewport *viewport; + /* + * Dummy values used for initial configuration of MemoryRegions + * that belong to a given viewport + */ + const hwaddr dummy_offset = 0; + const uint64_t dummy_size = 4; + size_t i; + + br->bus_name = "dw-pcie"; + + pci_set_word(dev->config + PCI_COMMAND, + PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + + pci_config_set_interrupt_pin(dev->config, 1); + pci_bridge_initfn(dev, TYPE_PCIE_BUS); + + pcie_port_init_reg(dev); + + pcie_cap_init(dev, 0x70, PCI_EXP_TYPE_ROOT_PORT, + 0, &error_fatal); + + msi_nonbroken = true; + msi_init(dev, 0x50, 32, true, true, &error_fatal); + + for (i = 0; i < DESIGNWARE_PCIE_NUM_VIEWPORTS; i++) { + MemoryRegion *source, *destination, *mem; + const char *direction; + char *name; + + viewport = &root->viewports[DESIGNWARE_PCIE_VIEWPORT_INBOUND][i]; + viewport->inbound = true; + viewport->base = 0x0000000000000000ULL; + viewport->target = 0x0000000000000000ULL; + viewport->limit = UINT32_MAX; + viewport->cr[0] = DESIGNWARE_PCIE_ATU_TYPE_MEM; + + source = &host->pci.address_space_root; + destination = get_system_memory(); + direction = "Inbound"; + + /* + * Configure MemoryRegion implementing PCI -> CPU memory + * access + */ + mem = &viewport->mem; + name = designware_pcie_viewport_name(direction, i, "MEM"); + memory_region_init_alias(mem, OBJECT(root), name, destination, + dummy_offset, dummy_size); + memory_region_add_subregion_overlap(source, dummy_offset, mem, -1); + memory_region_set_enabled(mem, false); + g_free(name); + + viewport = &root->viewports[DESIGNWARE_PCIE_VIEWPORT_OUTBOUND][i]; + viewport->root = root; + viewport->inbound = false; + viewport->base = 0x0000000000000000ULL; + viewport->target = 0x0000000000000000ULL; + viewport->limit = UINT32_MAX; + viewport->cr[0] = DESIGNWARE_PCIE_ATU_TYPE_MEM; + + destination = &host->pci.memory; + direction = "Outbound"; + source = get_system_memory(); + + /* + * Configure MemoryRegion implementing CPU -> PCI memory + * access + */ + mem = &viewport->mem; + name = designware_pcie_viewport_name(direction, i, "MEM"); + memory_region_init_alias(mem, OBJECT(root), name, destination, + dummy_offset, dummy_size); + memory_region_add_subregion(source, dummy_offset, mem); + memory_region_set_enabled(mem, false); + g_free(name); + + /* + * Configure MemoryRegion implementing access to configuration + * space + */ + mem = &viewport->cfg; + name = designware_pcie_viewport_name(direction, i, "CFG"); + memory_region_init_io(&viewport->cfg, OBJECT(root), + &designware_pci_host_conf_ops, + viewport, name, dummy_size); + memory_region_add_subregion(source, dummy_offset, mem); + memory_region_set_enabled(mem, false); + g_free(name); + } + + /* + * If no inbound iATU windows are configured, HW defaults to + * letting inbound TLPs to pass in. We emulate that by exlicitly + * configuring first inbound window to cover all of target's + * address space. + * + * NOTE: This will not work correctly for the case when first + * configured inbound window is window 0 + */ + viewport = &root->viewports[DESIGNWARE_PCIE_VIEWPORT_INBOUND][0]; + viewport->cr[1] = DESIGNWARE_PCIE_ATU_ENABLE; + designware_pcie_update_viewport(root, viewport); + + memory_region_init_io(&root->msi.iomem, OBJECT(root), + &designware_pci_host_msi_ops, + root, "pcie-msi", 0x4); + /* + * We initially place MSI interrupt I/O region a adress 0 and + * disable it. It'll be later moved to correct offset and enabled + * in designware_pcie_root_update_msi_mapping() as a part of + * initialization done by guest OS + */ + memory_region_add_subregion(address_space, dummy_offset, &root->msi.iomem); + memory_region_set_enabled(&root->msi.iomem, false); +} + +static void designware_pcie_set_irq(void *opaque, int irq_num, int level) +{ + DesignwarePCIEHost *host = DESIGNWARE_PCIE_HOST(opaque); + + qemu_set_irq(host->pci.irqs[irq_num], level); +} + +static const char * +designware_pcie_host_root_bus_path(PCIHostState *host_bridge, PCIBus *rootbus) +{ + return "0000:00"; +} + +static const VMStateDescription vmstate_designware_pcie_msi_bank = { + .name = "designware-pcie-msi-bank", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(enable, DesignwarePCIEMSIBank), + VMSTATE_UINT32(mask, DesignwarePCIEMSIBank), + VMSTATE_UINT32(status, DesignwarePCIEMSIBank), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_designware_pcie_msi = { + .name = "designware-pcie-msi", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT64(base, DesignwarePCIEMSI), + VMSTATE_STRUCT_ARRAY(intr, + DesignwarePCIEMSI, + DESIGNWARE_PCIE_NUM_MSI_BANKS, + 1, + vmstate_designware_pcie_msi_bank, + DesignwarePCIEMSIBank), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_designware_pcie_viewport = { + .name = "designware-pcie-viewport", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT64(base, DesignwarePCIEViewport), + VMSTATE_UINT64(target, DesignwarePCIEViewport), + VMSTATE_UINT32(limit, DesignwarePCIEViewport), + VMSTATE_UINT32_ARRAY(cr, DesignwarePCIEViewport, 2), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_designware_pcie_root = { + .name = "designware-pcie-root", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(parent_obj, PCIBridge), + VMSTATE_UINT32(atu_viewport, DesignwarePCIERoot), + VMSTATE_STRUCT_2DARRAY(viewports, + DesignwarePCIERoot, + 2, + DESIGNWARE_PCIE_NUM_VIEWPORTS, + 1, + vmstate_designware_pcie_viewport, + DesignwarePCIEViewport), + VMSTATE_STRUCT(msi, + DesignwarePCIERoot, + 1, + vmstate_designware_pcie_msi, + DesignwarePCIEMSI), + VMSTATE_END_OF_LIST() + } +}; + +static void designware_pcie_root_class_init(ObjectClass *klass, void *data) +{ + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); + + k->vendor_id = PCI_VENDOR_ID_SYNOPSYS; + k->device_id = 0xABCD; + k->revision = 0; + k->class_id = PCI_CLASS_BRIDGE_PCI; + k->is_bridge = true; + k->exit = pci_bridge_exitfn; + k->realize = designware_pcie_root_realize; + k->config_read = designware_pcie_root_config_read; + k->config_write = designware_pcie_root_config_write; + + dc->reset = pci_bridge_reset; + /* + * PCI-facing part of the host bridge, not usable without the + * host-facing part, which can't be device_add'ed, yet. + */ + dc->user_creatable = false; + dc->vmsd = &vmstate_designware_pcie_root; +} + +static uint64_t designware_pcie_host_mmio_read(void *opaque, hwaddr addr, + unsigned int size) +{ + PCIHostState *pci = PCI_HOST_BRIDGE(opaque); + PCIDevice *device = pci_find_device(pci->bus, 0, 0); + + return pci_host_config_read_common(device, + addr, + pci_config_size(device), + size); +} + +static void designware_pcie_host_mmio_write(void *opaque, hwaddr addr, + uint64_t val, unsigned int size) +{ + PCIHostState *pci = PCI_HOST_BRIDGE(opaque); + PCIDevice *device = pci_find_device(pci->bus, 0, 0); + + return pci_host_config_write_common(device, + addr, + pci_config_size(device), + val, size); +} + +static const MemoryRegionOps designware_pci_mmio_ops = { + .read = designware_pcie_host_mmio_read, + .write = designware_pcie_host_mmio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static AddressSpace *designware_pcie_host_set_iommu(PCIBus *bus, void *opaque, + int devfn) +{ + DesignwarePCIEHost *s = DESIGNWARE_PCIE_HOST(opaque); + + return &s->pci.address_space; +} + +static void designware_pcie_host_realize(DeviceState *dev, Error **errp) +{ + PCIHostState *pci = PCI_HOST_BRIDGE(dev); + DesignwarePCIEHost *s = DESIGNWARE_PCIE_HOST(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + size_t i; + + for (i = 0; i < ARRAY_SIZE(s->pci.irqs); i++) { + sysbus_init_irq(sbd, &s->pci.irqs[i]); + } + + memory_region_init_io(&s->mmio, + OBJECT(s), + &designware_pci_mmio_ops, + s, + "pcie.reg", 4 * 1024); + sysbus_init_mmio(sbd, &s->mmio); + + memory_region_init(&s->pci.io, OBJECT(s), "pcie-pio", 16); + memory_region_init(&s->pci.memory, OBJECT(s), + "pcie-bus-memory", + UINT64_MAX); + + pci->bus = pci_register_root_bus(dev, "pcie", + designware_pcie_set_irq, + pci_swizzle_map_irq_fn, + s, + &s->pci.memory, + &s->pci.io, + 0, 4, + TYPE_PCIE_BUS); + + memory_region_init(&s->pci.address_space_root, + OBJECT(s), + "pcie-bus-address-space-root", + UINT64_MAX); + memory_region_add_subregion(&s->pci.address_space_root, + 0x0, &s->pci.memory); + address_space_init(&s->pci.address_space, + &s->pci.address_space_root, + "pcie-bus-address-space"); + pci_setup_iommu(pci->bus, designware_pcie_host_set_iommu, s); + + qdev_set_parent_bus(DEVICE(&s->root), BUS(pci->bus)); + qdev_init_nofail(DEVICE(&s->root)); +} + +static const VMStateDescription vmstate_designware_pcie_host = { + .name = "designware-pcie-host", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(root, + DesignwarePCIEHost, + 1, + vmstate_designware_pcie_root, + DesignwarePCIERoot), + VMSTATE_END_OF_LIST() + } +}; + +static void designware_pcie_host_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); + + hc->root_bus_path = designware_pcie_host_root_bus_path; + dc->realize = designware_pcie_host_realize; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); + dc->fw_name = "pci"; + dc->vmsd = &vmstate_designware_pcie_host; +} + +static void designware_pcie_host_init(Object *obj) +{ + DesignwarePCIEHost *s = DESIGNWARE_PCIE_HOST(obj); + DesignwarePCIERoot *root = &s->root; + + object_initialize(root, sizeof(*root), TYPE_DESIGNWARE_PCIE_ROOT); + object_property_add_child(obj, "root", OBJECT(root), NULL); + qdev_prop_set_int32(DEVICE(root), "addr", PCI_DEVFN(0, 0)); + qdev_prop_set_bit(DEVICE(root), "multifunction", false); +} + +static const TypeInfo designware_pcie_root_info = { + .name = TYPE_DESIGNWARE_PCIE_ROOT, + .parent = TYPE_PCI_BRIDGE, + .instance_size = sizeof(DesignwarePCIERoot), + .class_init = designware_pcie_root_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, + { } + }, +}; + +static const TypeInfo designware_pcie_host_info = { + .name = TYPE_DESIGNWARE_PCIE_HOST, + .parent = TYPE_PCI_HOST_BRIDGE, + .instance_size = sizeof(DesignwarePCIEHost), + .instance_init = designware_pcie_host_init, + .class_init = designware_pcie_host_class_init, +}; + +static void designware_pcie_register(void) +{ + type_register_static(&designware_pcie_root_info); + type_register_static(&designware_pcie_host_info); +} +type_init(designware_pcie_register) diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c index 1c33322ba6..912e5005d8 100644 --- a/hw/scsi/virtio-scsi-dataplane.c +++ b/hw/scsi/virtio-scsi-dataplane.c @@ -107,9 +107,10 @@ static int virtio_scsi_vring_init(VirtIOSCSI *s, VirtQueue *vq, int n, return 0; } -/* assumes s->ctx held */ -static void virtio_scsi_clear_aio(VirtIOSCSI *s) +/* Context: BH in IOThread */ +static void virtio_scsi_dataplane_stop_bh(void *opaque) { + VirtIOSCSI *s = opaque; VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); int i; @@ -171,7 +172,7 @@ int virtio_scsi_dataplane_start(VirtIODevice *vdev) return 0; fail_vrings: - virtio_scsi_clear_aio(s); + aio_wait_bh_oneshot(s->ctx, virtio_scsi_dataplane_stop_bh, s); aio_context_release(s->ctx); for (i = 0; i < vs->conf.num_queues + 2; i++) { virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); @@ -207,7 +208,7 @@ void virtio_scsi_dataplane_stop(VirtIODevice *vdev) s->dataplane_stopping = true; aio_context_acquire(s->ctx); - virtio_scsi_clear_aio(s); + aio_wait_bh_oneshot(s->ctx, virtio_scsi_dataplane_stop_bh, s); aio_context_release(s->ctx); blk_drain_all(); /* ensure there are no in-flight requests */ diff --git a/hw/sd/Makefile.objs b/hw/sd/Makefile.objs index c2b7664264..a99d9fbb04 100644 --- a/hw/sd/Makefile.objs +++ b/hw/sd/Makefile.objs @@ -1,6 +1,6 @@ common-obj-$(CONFIG_PL181) += pl181.o common-obj-$(CONFIG_SSI_SD) += ssi-sd.o -common-obj-$(CONFIG_SD) += sd.o core.o +common-obj-$(CONFIG_SD) += sd.o core.o sdmmc-internal.o common-obj-$(CONFIG_SDHCI) += sdhci.o obj-$(CONFIG_MILKYMIST) += milkymist-memcard.o diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 933890e86f..235e0518d6 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -120,6 +120,7 @@ struct SDState { qemu_irq readonly_cb; qemu_irq inserted_cb; QEMUTimer *ocr_power_timer; + const char *proto_name; bool enable; uint8_t dat_lines; bool cmd_line; @@ -866,13 +867,19 @@ static void sd_lock_command(SDState *sd) sd->card_status &= ~CARD_IS_LOCKED; } -static sd_rsp_type_t sd_normal_command(SDState *sd, - SDRequest req) +static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) { uint32_t rca = 0x0000; uint64_t addr = (sd->ocr & (1 << 30)) ? (uint64_t) req.arg << 9 : req.arg; - trace_sdcard_normal_command(req.cmd, req.arg, sd_state_name(sd->state)); + /* CMD55 precedes an ACMD, so we are not interested in tracing it. + * However there is no ACMD55, so we want to trace this particular case. + */ + if (req.cmd != 55 || sd->expecting_acmd) { + trace_sdcard_normal_command(sd->proto_name, + sd_cmd_name(req.cmd), req.cmd, + req.arg, sd_state_name(sd->state)); + } /* Not interpreting this as an app command */ sd->card_status &= ~APP_CMD; @@ -1162,6 +1169,14 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, } break; + case 19: /* CMD19: SEND_TUNING_BLOCK (SD) */ + if (sd->state == sd_transfer_state) { + sd->state = sd_sendingdata_state; + sd->data_offset = 0; + return sd_r1; + } + break; + case 23: /* CMD23: SET_BLOCK_COUNT */ switch (sd->state) { case sd_transfer_state: @@ -1450,7 +1465,8 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, static sd_rsp_type_t sd_app_command(SDState *sd, SDRequest req) { - trace_sdcard_app_command(req.cmd, req.arg); + trace_sdcard_app_command(sd->proto_name, sd_acmd_name(req.cmd), + req.cmd, req.arg, sd_state_name(sd->state)); sd->card_status |= APP_CMD; switch (req.cmd) { case 6: /* ACMD6: SET_BUS_WIDTH */ @@ -1765,7 +1781,9 @@ void sd_write_data(SDState *sd, uint8_t value) if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION)) return; - trace_sdcard_write_data(sd->current_cmd, value); + trace_sdcard_write_data(sd->proto_name, + sd_acmd_name(sd->current_cmd), + sd->current_cmd, value); switch (sd->current_cmd) { case 24: /* CMD24: WRITE_SINGLE_BLOCK */ sd->data[sd->data_offset ++] = value; @@ -1883,6 +1901,20 @@ void sd_write_data(SDState *sd, uint8_t value) } } +#define SD_TUNING_BLOCK_SIZE 64 + +static const uint8_t sd_tuning_block_pattern[SD_TUNING_BLOCK_SIZE] = { + /* See: Physical Layer Simplified Specification Version 3.01, Table 4-2 */ + 0xff, 0x0f, 0xff, 0x00, 0x0f, 0xfc, 0xc3, 0xcc, + 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef, + 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb, + 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef, + 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c, + 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee, + 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff, + 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde, +}; + uint8_t sd_read_data(SDState *sd) { /* TODO: Append CRCs */ @@ -1903,7 +1935,9 @@ uint8_t sd_read_data(SDState *sd) io_len = (sd->ocr & (1 << 30)) ? 512 : sd->blk_len; - trace_sdcard_read_data(sd->current_cmd, io_len); + trace_sdcard_read_data(sd->proto_name, + sd_acmd_name(sd->current_cmd), + sd->current_cmd, io_len); switch (sd->current_cmd) { case 6: /* CMD6: SWITCH_FUNCTION */ ret = sd->data[sd->data_offset ++]; @@ -1960,6 +1994,13 @@ uint8_t sd_read_data(SDState *sd) } break; + case 19: /* CMD19: SEND_TUNING_BLOCK (SD) */ + if (sd->data_offset >= SD_TUNING_BLOCK_SIZE - 1) { + sd->state = sd_transfer_state; + } + ret = sd_tuning_block_pattern[sd->data_offset++]; + break; + case 22: /* ACMD22: SEND_NUM_WR_BLOCKS */ ret = sd->data[sd->data_offset ++]; @@ -2029,6 +2070,8 @@ static void sd_realize(DeviceState *dev, Error **errp) SDState *sd = SD_CARD(dev); int ret; + sd->proto_name = sd->spi ? "SPI" : "SD"; + if (sd->blk && blk_is_read_only(sd->blk)) { error_setg(errp, "Cannot use read-only drive as SD card"); return; diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 97b4a473c8..1b828b104d 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -433,13 +433,13 @@ static void sdhci_read_block_from_card(SDHCIState *s) for (index = 0; index < blk_size; index++) { data = sdbus_read_data(&s->sdbus); if (!FIELD_EX32(s->hostctl2, SDHC_HOSTCTL2, EXECUTE_TUNING)) { - /* Device is not in tunning */ + /* Device is not in tuning */ s->fifo_buffer[index] = data; } } if (FIELD_EX32(s->hostctl2, SDHC_HOSTCTL2, EXECUTE_TUNING)) { - /* Device is in tunning */ + /* Device is in tuning */ s->hostctl2 &= ~R_SDHC_HOSTCTL2_EXECUTE_TUNING_MASK; s->hostctl2 |= R_SDHC_HOSTCTL2_SAMPLING_CLKSEL_MASK; s->prnsts &= ~(SDHC_DAT_LINE_ACTIVE | SDHC_DOING_READ | diff --git a/hw/sd/sdmmc-internal.c b/hw/sd/sdmmc-internal.c new file mode 100644 index 0000000000..2053def3f1 --- /dev/null +++ b/hw/sd/sdmmc-internal.c @@ -0,0 +1,72 @@ +/* + * SD/MMC cards common helpers + * + * Copyright (c) 2018 Philippe Mathieu-Daudé <f4bug@amsat.org> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "sdmmc-internal.h" + +const char *sd_cmd_name(uint8_t cmd) +{ + static const char *cmd_abbrev[SDMMC_CMD_MAX] = { + [0] = "GO_IDLE_STATE", + [2] = "ALL_SEND_CID", [3] = "SEND_RELATIVE_ADDR", + [4] = "SET_DSR", [5] = "IO_SEND_OP_COND", + [6] = "SWITCH_FUNC", [7] = "SELECT/DESELECT_CARD", + [8] = "SEND_IF_COND", [9] = "SEND_CSD", + [10] = "SEND_CID", [11] = "VOLTAGE_SWITCH", + [12] = "STOP_TRANSMISSION", [13] = "SEND_STATUS", + [15] = "GO_INACTIVE_STATE", + [16] = "SET_BLOCKLEN", [17] = "READ_SINGLE_BLOCK", + [18] = "READ_MULTIPLE_BLOCK", [19] = "SEND_TUNING_BLOCK", + [20] = "SPEED_CLASS_CONTROL", [21] = "DPS_spec", + [23] = "SET_BLOCK_COUNT", + [24] = "WRITE_BLOCK", [25] = "WRITE_MULTIPLE_BLOCK", + [26] = "MANUF_RSVD", [27] = "PROGRAM_CSD", + [28] = "SET_WRITE_PROT", [29] = "CLR_WRITE_PROT", + [30] = "SEND_WRITE_PROT", + [32] = "ERASE_WR_BLK_START", [33] = "ERASE_WR_BLK_END", + [34] = "SW_FUNC_RSVD", [35] = "SW_FUNC_RSVD", + [36] = "SW_FUNC_RSVD", [37] = "SW_FUNC_RSVD", + [38] = "ERASE", + [40] = "DPS_spec", + [42] = "LOCK_UNLOCK", [43] = "Q_MANAGEMENT", + [44] = "Q_TASK_INFO_A", [45] = "Q_TASK_INFO_B", + [46] = "Q_RD_TASK", [47] = "Q_WR_TASK", + [48] = "READ_EXTR_SINGLE", [49] = "WRITE_EXTR_SINGLE", + [50] = "SW_FUNC_RSVD", + [52] = "IO_RW_DIRECT", [53] = "IO_RW_EXTENDED", + [54] = "SDIO_RSVD", [55] = "APP_CMD", + [56] = "GEN_CMD", [57] = "SW_FUNC_RSVD", + [58] = "READ_EXTR_MULTI", [59] = "WRITE_EXTR_MULTI", + [60] = "MANUF_RSVD", [61] = "MANUF_RSVD", + [62] = "MANUF_RSVD", [63] = "MANUF_RSVD", + }; + return cmd_abbrev[cmd] ? cmd_abbrev[cmd] : "UNKNOWN_CMD"; +} + +const char *sd_acmd_name(uint8_t cmd) +{ + static const char *acmd_abbrev[SDMMC_CMD_MAX] = { + [6] = "SET_BUS_WIDTH", + [13] = "SD_STATUS", + [14] = "DPS_spec", [15] = "DPS_spec", + [16] = "DPS_spec", + [18] = "SECU_spec", + [22] = "SEND_NUM_WR_BLOCKS", [23] = "SET_WR_BLK_ERASE_COUNT", + [41] = "SD_SEND_OP_COND", + [42] = "SET_CLR_CARD_DETECT", + [51] = "SEND_SCR", + [52] = "SECU_spec", [53] = "SECU_spec", + [54] = "SECU_spec", + [56] = "SECU_spec", [57] = "SECU_spec", + [58] = "SECU_spec", [59] = "SECU_spec", + }; + + return acmd_abbrev[cmd] ? acmd_abbrev[cmd] : "UNKNOWN_ACMD"; +} diff --git a/hw/sd/sdmmc-internal.h b/hw/sd/sdmmc-internal.h index 0e96cb0081..9aa04766fc 100644 --- a/hw/sd/sdmmc-internal.h +++ b/hw/sd/sdmmc-internal.h @@ -12,4 +12,28 @@ #define SDMMC_CMD_MAX 64 +/** + * sd_cmd_name: + * @cmd: A SD "normal" command, up to SDMMC_CMD_MAX. + * + * Returns a human-readable name describing the command. + * The return value is always a static string which does not need + * to be freed after use. + * + * Returns: The command name of @cmd or "UNKNOWN_CMD". + */ +const char *sd_cmd_name(uint8_t cmd); + +/** + * sd_acmd_name: + * @cmd: A SD "Application-Specific" command, up to SDMMC_CMD_MAX. + * + * Returns a human-readable name describing the application command. + * The return value is always a static string which does not need + * to be freed after use. + * + * Returns: The application command name of @cmd or "UNKNOWN_ACMD". + */ +const char *sd_acmd_name(uint8_t cmd); + #endif diff --git a/hw/sd/trace-events b/hw/sd/trace-events index 3040d32560..2059ace61f 100644 --- a/hw/sd/trace-events +++ b/hw/sd/trace-events @@ -24,8 +24,8 @@ sdhci_write_dataport(uint16_t data_count) "write buffer filled with %u bytes of sdhci_capareg(const char *desc, uint16_t val) "%s: %u" # hw/sd/sd.c -sdcard_normal_command(uint8_t cmd, uint32_t arg, const char *state) "CMD%d arg 0x%08x (state %s)" -sdcard_app_command(uint8_t acmd, uint32_t arg) "ACMD%d arg 0x%08x" +sdcard_normal_command(const char *proto, const char *cmd_desc, uint8_t cmd, uint32_t arg, const char *state) "%s %20s/ CMD%02d arg 0x%08x (state %s)" +sdcard_app_command(const char *proto, const char *acmd_desc, uint8_t acmd, uint32_t arg, const char *state) "%s %23s/ACMD%02d arg 0x%08x (state %s)" sdcard_response(const char *rspdesc, int rsplen) "%s (sz:%d)" sdcard_powerup(void) "" sdcard_inquiry_cmd41(void) "" @@ -39,8 +39,8 @@ sdcard_lock(void) "" sdcard_unlock(void) "" sdcard_read_block(uint64_t addr, uint32_t len) "addr 0x%" PRIx64 " size 0x%x" sdcard_write_block(uint64_t addr, uint32_t len) "addr 0x%" PRIx64 " size 0x%x" -sdcard_write_data(uint8_t cmd, uint8_t value) "CMD%02d value 0x%02x" -sdcard_read_data(uint8_t cmd, int length) "CMD%02d len %d" +sdcard_write_data(const char *proto, const char *cmd_desc, uint8_t cmd, uint8_t value) "%s %20s/ CMD%02d value 0x%02x" +sdcard_read_data(const char *proto, const char *cmd_desc, uint8_t cmd, int length) "%s %20s/ CMD%02d len %d" sdcard_set_voltage(uint16_t millivolts) "%u mV" # hw/sd/milkymist-memcard.c diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index ec174309db..65a9196c1a 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -106,10 +106,10 @@ struct USBRedirDevice { USBDevice dev; /* Properties */ CharBackend cs; + bool enable_streams; uint8_t debug; - char *filter_str; int32_t bootindex; - bool enable_streams; + char *filter_str; /* Data passed from chardev the fd_read cb to the usbredirparser read cb */ const uint8_t *read_buf; int read_buf_size; diff --git a/include/block/aio-wait.h b/include/block/aio-wait.h index a48c744fa8..8c90a2e66e 100644 --- a/include/block/aio-wait.h +++ b/include/block/aio-wait.h @@ -50,8 +50,8 @@ * } */ typedef struct { - /* Is the main loop waiting for a kick? Accessed with atomic ops. */ - bool need_kick; + /* Number of waiting AIO_WAIT_WHILE() callers. Accessed with atomic ops. */ + unsigned num_waiters; } AioWait; /** @@ -71,35 +71,34 @@ typedef struct { * wait on conditions between two IOThreads since that could lead to deadlock, * go via the main loop instead. */ -#define AIO_WAIT_WHILE(wait, ctx, cond) ({ \ - bool waited_ = false; \ - bool busy_ = true; \ - AioWait *wait_ = (wait); \ - AioContext *ctx_ = (ctx); \ - if (in_aio_context_home_thread(ctx_)) { \ - while ((cond) || busy_) { \ - busy_ = aio_poll(ctx_, (cond)); \ - waited_ |= !!(cond) | busy_; \ - } \ - } else { \ - assert(qemu_get_current_aio_context() == \ - qemu_get_aio_context()); \ - assert(!wait_->need_kick); \ - /* Set wait_->need_kick before evaluating cond. */ \ - atomic_mb_set(&wait_->need_kick, true); \ - while (busy_) { \ - if ((cond)) { \ - waited_ = busy_ = true; \ - aio_context_release(ctx_); \ - aio_poll(qemu_get_aio_context(), true); \ - aio_context_acquire(ctx_); \ - } else { \ - busy_ = aio_poll(ctx_, false); \ - waited_ |= busy_; \ - } \ - } \ - atomic_set(&wait_->need_kick, false); \ - } \ +#define AIO_WAIT_WHILE(wait, ctx, cond) ({ \ + bool waited_ = false; \ + bool busy_ = true; \ + AioWait *wait_ = (wait); \ + AioContext *ctx_ = (ctx); \ + if (in_aio_context_home_thread(ctx_)) { \ + while ((cond) || busy_) { \ + busy_ = aio_poll(ctx_, (cond)); \ + waited_ |= !!(cond) | busy_; \ + } \ + } else { \ + assert(qemu_get_current_aio_context() == \ + qemu_get_aio_context()); \ + /* Increment wait_->num_waiters before evaluating cond. */ \ + atomic_inc(&wait_->num_waiters); \ + while (busy_) { \ + if ((cond)) { \ + waited_ = busy_ = true; \ + aio_context_release(ctx_); \ + aio_poll(qemu_get_aio_context(), true); \ + aio_context_acquire(ctx_); \ + } else { \ + busy_ = aio_poll(ctx_, false); \ + waited_ |= busy_; \ + } \ + } \ + atomic_dec(&wait_->num_waiters); \ + } \ waited_; }) /** @@ -113,4 +112,17 @@ typedef struct { */ void aio_wait_kick(AioWait *wait); +/** + * aio_wait_bh_oneshot: + * @ctx: the aio context + * @cb: the BH callback function + * @opaque: user data for the BH callback function + * + * Run a BH in @ctx and wait for it to complete. + * + * Must be called from the main loop thread with @ctx acquired exactly once. + * Note that main loop event processing may occur. + */ +void aio_wait_bh_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque); + #endif /* QEMU_AIO_WAIT */ diff --git a/include/block/block.h b/include/block/block.h index 8b6db952a2..cdec3639a3 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -226,6 +226,7 @@ char *bdrv_perm_names(uint64_t perm); void bdrv_init(void); void bdrv_init_with_whitelist(void); bool bdrv_uses_whitelist(void); +int bdrv_is_whitelisted(BlockDriver *drv, bool read_only); BlockDriver *bdrv_find_protocol(const char *filename, bool allow_protocol_prefix, Error **errp); @@ -246,6 +247,7 @@ BdrvChild *bdrv_open_child(const char *filename, BlockDriverState* parent, const BdrvChildRole *child_role, bool allow_none, Error **errp); +BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp); void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, Error **errp); int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, diff --git a/include/block/block_int.h b/include/block/block_int.h index 64a5700f2b..27e17addba 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -129,8 +129,11 @@ struct BlockDriver { int (*bdrv_file_open)(BlockDriverState *bs, QDict *options, int flags, Error **errp); void (*bdrv_close)(BlockDriverState *bs); - int coroutine_fn (*bdrv_co_create_opts)(const char *filename, QemuOpts *opts, + int coroutine_fn (*bdrv_co_create)(BlockdevCreateOptions *opts, Error **errp); + int coroutine_fn (*bdrv_co_create_opts)(const char *filename, + QemuOpts *opts, + Error **errp); int (*bdrv_make_empty)(BlockDriverState *bs); void (*bdrv_refresh_filename)(BlockDriverState *bs, QDict *options); @@ -224,7 +227,8 @@ struct BlockDriver { /* * Invalidate any cached meta-data. */ - void (*bdrv_invalidate_cache)(BlockDriverState *bs, Error **errp); + void coroutine_fn (*bdrv_co_invalidate_cache)(BlockDriverState *bs, + Error **errp); int (*bdrv_inactivate)(BlockDriverState *bs); /* @@ -306,8 +310,9 @@ struct BlockDriver { * Returns 0 for completed check, -errno for internal errors. * The check results are stored in result. */ - int (*bdrv_check)(BlockDriverState *bs, BdrvCheckResult *result, - BdrvCheckMode fix); + int coroutine_fn (*bdrv_co_check)(BlockDriverState *bs, + BdrvCheckResult *result, + BdrvCheckMode fix); int (*bdrv_amend_options)(BlockDriverState *bs, QemuOpts *opts, BlockDriverAmendStatusCB *status_cb, diff --git a/include/hw/arm/fsl-imx7.h b/include/hw/arm/fsl-imx7.h new file mode 100644 index 0000000000..d848262bfd --- /dev/null +++ b/include/hw/arm/fsl-imx7.h @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2018, Impinj, Inc. + * + * i.MX7 SoC definitions + * + * Author: Andrey Smirnov <andrew.smirnov@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef FSL_IMX7_H +#define FSL_IMX7_H + +#include "hw/arm/arm.h" +#include "hw/cpu/a15mpcore.h" +#include "hw/intc/imx_gpcv2.h" +#include "hw/misc/imx7_ccm.h" +#include "hw/misc/imx7_snvs.h" +#include "hw/misc/imx7_gpr.h" +#include "hw/misc/imx6_src.h" +#include "hw/misc/imx2_wdt.h" +#include "hw/gpio/imx_gpio.h" +#include "hw/char/imx_serial.h" +#include "hw/timer/imx_gpt.h" +#include "hw/timer/imx_epit.h" +#include "hw/i2c/imx_i2c.h" +#include "hw/gpio/imx_gpio.h" +#include "hw/sd/sdhci.h" +#include "hw/ssi/imx_spi.h" +#include "hw/net/imx_fec.h" +#include "hw/pci-host/designware.h" +#include "hw/usb/chipidea.h" +#include "exec/memory.h" +#include "cpu.h" + +#define TYPE_FSL_IMX7 "fsl,imx7" +#define FSL_IMX7(obj) OBJECT_CHECK(FslIMX7State, (obj), TYPE_FSL_IMX7) + +enum FslIMX7Configuration { + FSL_IMX7_NUM_CPUS = 2, + FSL_IMX7_NUM_UARTS = 7, + FSL_IMX7_NUM_ETHS = 2, + FSL_IMX7_ETH_NUM_TX_RINGS = 3, + FSL_IMX7_NUM_USDHCS = 3, + FSL_IMX7_NUM_WDTS = 4, + FSL_IMX7_NUM_GPTS = 4, + FSL_IMX7_NUM_IOMUXCS = 2, + FSL_IMX7_NUM_GPIOS = 7, + FSL_IMX7_NUM_I2CS = 4, + FSL_IMX7_NUM_ECSPIS = 4, + FSL_IMX7_NUM_USBS = 3, + FSL_IMX7_NUM_ADCS = 2, +}; + +typedef struct FslIMX7State { + /*< private >*/ + DeviceState parent_obj; + + /*< public >*/ + ARMCPU cpu[FSL_IMX7_NUM_CPUS]; + A15MPPrivState a7mpcore; + IMXGPTState gpt[FSL_IMX7_NUM_GPTS]; + IMXGPIOState gpio[FSL_IMX7_NUM_GPIOS]; + IMX7CCMState ccm; + IMX7AnalogState analog; + IMX7SNVSState snvs; + IMXGPCv2State gpcv2; + IMXSPIState spi[FSL_IMX7_NUM_ECSPIS]; + IMXI2CState i2c[FSL_IMX7_NUM_I2CS]; + IMXSerialState uart[FSL_IMX7_NUM_UARTS]; + IMXFECState eth[FSL_IMX7_NUM_ETHS]; + SDHCIState usdhc[FSL_IMX7_NUM_USDHCS]; + IMX2WdtState wdt[FSL_IMX7_NUM_WDTS]; + IMX7GPRState gpr; + ChipideaState usb[FSL_IMX7_NUM_USBS]; + DesignwarePCIEHost pcie; +} FslIMX7State; + +enum FslIMX7MemoryMap { + FSL_IMX7_MMDC_ADDR = 0x80000000, + FSL_IMX7_MMDC_SIZE = 2 * 1024 * 1024 * 1024UL, + + FSL_IMX7_GPIO1_ADDR = 0x30200000, + FSL_IMX7_GPIO2_ADDR = 0x30210000, + FSL_IMX7_GPIO3_ADDR = 0x30220000, + FSL_IMX7_GPIO4_ADDR = 0x30230000, + FSL_IMX7_GPIO5_ADDR = 0x30240000, + FSL_IMX7_GPIO6_ADDR = 0x30250000, + FSL_IMX7_GPIO7_ADDR = 0x30260000, + + FSL_IMX7_IOMUXC_LPSR_GPR_ADDR = 0x30270000, + + FSL_IMX7_WDOG1_ADDR = 0x30280000, + FSL_IMX7_WDOG2_ADDR = 0x30290000, + FSL_IMX7_WDOG3_ADDR = 0x302A0000, + FSL_IMX7_WDOG4_ADDR = 0x302B0000, + + FSL_IMX7_IOMUXC_LPSR_ADDR = 0x302C0000, + + FSL_IMX7_GPT1_ADDR = 0x302D0000, + FSL_IMX7_GPT2_ADDR = 0x302E0000, + FSL_IMX7_GPT3_ADDR = 0x302F0000, + FSL_IMX7_GPT4_ADDR = 0x30300000, + + FSL_IMX7_IOMUXC_ADDR = 0x30330000, + FSL_IMX7_IOMUXC_GPR_ADDR = 0x30340000, + FSL_IMX7_IOMUXCn_SIZE = 0x1000, + + FSL_IMX7_ANALOG_ADDR = 0x30360000, + FSL_IMX7_SNVS_ADDR = 0x30370000, + FSL_IMX7_CCM_ADDR = 0x30380000, + + FSL_IMX7_SRC_ADDR = 0x30390000, + FSL_IMX7_SRC_SIZE = 0x1000, + + FSL_IMX7_ADC1_ADDR = 0x30610000, + FSL_IMX7_ADC2_ADDR = 0x30620000, + FSL_IMX7_ADCn_SIZE = 0x1000, + + FSL_IMX7_GPC_ADDR = 0x303A0000, + + FSL_IMX7_I2C1_ADDR = 0x30A20000, + FSL_IMX7_I2C2_ADDR = 0x30A30000, + FSL_IMX7_I2C3_ADDR = 0x30A40000, + FSL_IMX7_I2C4_ADDR = 0x30A50000, + + FSL_IMX7_ECSPI1_ADDR = 0x30820000, + FSL_IMX7_ECSPI2_ADDR = 0x30830000, + FSL_IMX7_ECSPI3_ADDR = 0x30840000, + FSL_IMX7_ECSPI4_ADDR = 0x30630000, + + FSL_IMX7_LCDIF_ADDR = 0x30730000, + FSL_IMX7_LCDIF_SIZE = 0x1000, + + FSL_IMX7_UART1_ADDR = 0x30860000, + /* + * Some versions of the reference manual claim that UART2 is @ + * 0x30870000, but experiments with HW + DT files in upstream + * Linux kernel show that not to be true and that block is + * acutally located @ 0x30890000 + */ + FSL_IMX7_UART2_ADDR = 0x30890000, + FSL_IMX7_UART3_ADDR = 0x30880000, + FSL_IMX7_UART4_ADDR = 0x30A60000, + FSL_IMX7_UART5_ADDR = 0x30A70000, + FSL_IMX7_UART6_ADDR = 0x30A80000, + FSL_IMX7_UART7_ADDR = 0x30A90000, + + FSL_IMX7_ENET1_ADDR = 0x30BE0000, + FSL_IMX7_ENET2_ADDR = 0x30BF0000, + + FSL_IMX7_USB1_ADDR = 0x30B10000, + FSL_IMX7_USBMISC1_ADDR = 0x30B10200, + FSL_IMX7_USB2_ADDR = 0x30B20000, + FSL_IMX7_USBMISC2_ADDR = 0x30B20200, + FSL_IMX7_USB3_ADDR = 0x30B30000, + FSL_IMX7_USBMISC3_ADDR = 0x30B30200, + FSL_IMX7_USBMISCn_SIZE = 0x200, + + FSL_IMX7_USDHC1_ADDR = 0x30B40000, + FSL_IMX7_USDHC2_ADDR = 0x30B50000, + FSL_IMX7_USDHC3_ADDR = 0x30B60000, + + FSL_IMX7_SDMA_ADDR = 0x30BD0000, + FSL_IMX7_SDMA_SIZE = 0x1000, + + FSL_IMX7_A7MPCORE_ADDR = 0x31000000, + FSL_IMX7_A7MPCORE_DAP_ADDR = 0x30000000, + + FSL_IMX7_PCIE_REG_ADDR = 0x33800000, + FSL_IMX7_PCIE_REG_SIZE = 16 * 1024, + + FSL_IMX7_GPR_ADDR = 0x30340000, +}; + +enum FslIMX7IRQs { + FSL_IMX7_USDHC1_IRQ = 22, + FSL_IMX7_USDHC2_IRQ = 23, + FSL_IMX7_USDHC3_IRQ = 24, + + FSL_IMX7_UART1_IRQ = 26, + FSL_IMX7_UART2_IRQ = 27, + FSL_IMX7_UART3_IRQ = 28, + FSL_IMX7_UART4_IRQ = 29, + FSL_IMX7_UART5_IRQ = 30, + FSL_IMX7_UART6_IRQ = 16, + + FSL_IMX7_ECSPI1_IRQ = 31, + FSL_IMX7_ECSPI2_IRQ = 32, + FSL_IMX7_ECSPI3_IRQ = 33, + FSL_IMX7_ECSPI4_IRQ = 34, + + FSL_IMX7_I2C1_IRQ = 35, + FSL_IMX7_I2C2_IRQ = 36, + FSL_IMX7_I2C3_IRQ = 37, + FSL_IMX7_I2C4_IRQ = 38, + + FSL_IMX7_USB1_IRQ = 43, + FSL_IMX7_USB2_IRQ = 42, + FSL_IMX7_USB3_IRQ = 40, + + FSL_IMX7_PCI_INTA_IRQ = 122, + FSL_IMX7_PCI_INTB_IRQ = 123, + FSL_IMX7_PCI_INTC_IRQ = 124, + FSL_IMX7_PCI_INTD_IRQ = 125, + + FSL_IMX7_UART7_IRQ = 126, + +#define FSL_IMX7_ENET_IRQ(i, n) ((n) + ((i) ? 100 : 118)) + + FSL_IMX7_MAX_IRQ = 128, +}; + +#endif /* FSL_IMX7_H */ diff --git a/include/hw/boards.h b/include/hw/boards.h index efb0a9edfd..8ce9a7a21d 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -243,6 +243,7 @@ struct MachineState { bool suppress_vmdesc; bool enforce_config_section; bool enable_graphics; + char *memory_encryption; ram_addr_t ram_size; ram_addr_t maxram_size; diff --git a/include/hw/pci-host/designware.h b/include/hw/pci-host/designware.h new file mode 100644 index 0000000000..a4f2c0695b --- /dev/null +++ b/include/hw/pci-host/designware.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2017, Impinj, Inc. + * + * Designware PCIe IP block emulation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/>. + */ + +#ifndef DESIGNWARE_H +#define DESIGNWARE_H + +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_bus.h" +#include "hw/pci/pcie_host.h" +#include "hw/pci/pci_bridge.h" + +#define TYPE_DESIGNWARE_PCIE_HOST "designware-pcie-host" +#define DESIGNWARE_PCIE_HOST(obj) \ + OBJECT_CHECK(DesignwarePCIEHost, (obj), TYPE_DESIGNWARE_PCIE_HOST) + +#define TYPE_DESIGNWARE_PCIE_ROOT "designware-pcie-root" +#define DESIGNWARE_PCIE_ROOT(obj) \ + OBJECT_CHECK(DesignwarePCIERoot, (obj), TYPE_DESIGNWARE_PCIE_ROOT) + +struct DesignwarePCIERoot; +typedef struct DesignwarePCIERoot DesignwarePCIERoot; + +typedef struct DesignwarePCIEViewport { + DesignwarePCIERoot *root; + + MemoryRegion cfg; + MemoryRegion mem; + + uint64_t base; + uint64_t target; + uint32_t limit; + uint32_t cr[2]; + + bool inbound; +} DesignwarePCIEViewport; + +typedef struct DesignwarePCIEMSIBank { + uint32_t enable; + uint32_t mask; + uint32_t status; +} DesignwarePCIEMSIBank; + +typedef struct DesignwarePCIEMSI { + uint64_t base; + MemoryRegion iomem; + +#define DESIGNWARE_PCIE_NUM_MSI_BANKS 1 + + DesignwarePCIEMSIBank intr[DESIGNWARE_PCIE_NUM_MSI_BANKS]; +} DesignwarePCIEMSI; + +struct DesignwarePCIERoot { + PCIBridge parent_obj; + + uint32_t atu_viewport; + +#define DESIGNWARE_PCIE_VIEWPORT_OUTBOUND 0 +#define DESIGNWARE_PCIE_VIEWPORT_INBOUND 1 +#define DESIGNWARE_PCIE_NUM_VIEWPORTS 4 + + DesignwarePCIEViewport viewports[2][DESIGNWARE_PCIE_NUM_VIEWPORTS]; + DesignwarePCIEMSI msi; +}; + +typedef struct DesignwarePCIEHost { + PCIHostState parent_obj; + + DesignwarePCIERoot root; + + struct { + AddressSpace address_space; + MemoryRegion address_space_root; + + MemoryRegion memory; + MemoryRegion io; + + qemu_irq irqs[4]; + } pci; + + MemoryRegion mmio; +} DesignwarePCIEHost; + +#endif /* DESIGNWARE_H */ diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h index 1dbf53627c..63acc722a9 100644 --- a/include/hw/pci/pci_ids.h +++ b/include/hw/pci/pci_ids.h @@ -269,4 +269,6 @@ #define PCI_VENDOR_ID_VMWARE 0x15ad #define PCI_DEVICE_ID_VMWARE_PVRDMA 0x0820 +#define PCI_VENDOR_ID_SYNOPSYS 0x16C3 + #endif diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h index ff6f7842c3..7c6d844549 100644 --- a/include/qapi/qmp/qdict.h +++ b/include/qapi/qmp/qdict.h @@ -81,4 +81,10 @@ QObject *qdict_crumple(const QDict *src, Error **errp); void qdict_join(QDict *dest, QDict *src, bool overwrite); +typedef struct QDictRenames { + const char *from; + const char *to; +} QDictRenames; +bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp); + #endif /* QDICT_H */ diff --git a/include/qemu/log-for-trace.h b/include/qemu/log-for-trace.h new file mode 100644 index 0000000000..2f0a5b080e --- /dev/null +++ b/include/qemu/log-for-trace.h @@ -0,0 +1,35 @@ +/* log-for-trace.h: logging basics required by the trace.h generated + * by the log trace backend. + * + * This should not be included directly by any .c file: if you + * need to use the logging functions include "qemu/log.h". + * + * The purpose of splitting these parts out into their own header + * is to catch the easy mistake where a .c file includes trace.h + * but forgets to include qemu/log.h. Without this split, that + * would result in the .c file compiling fine when the default + * trace backend is in use but failing to compile with any other + * backend. + * + * This code is licensed under the GNU General Public License, + * version 2 or (at your option) any later version. + */ + +#ifndef QEMU_LOG_FOR_TRACE_H +#define QEMU_LOG_FOR_TRACE_H + +/* Private global variable, don't use */ +extern int qemu_loglevel; + +#define LOG_TRACE (1 << 15) + +/* Returns true if a bit is set in the current loglevel mask */ +static inline bool qemu_loglevel_mask(int mask) +{ + return (qemu_loglevel & mask) != 0; +} + +/* main logging function */ +int GCC_FMT_ATTR(1, 2) qemu_log(const char *fmt, ...); + +#endif diff --git a/include/qemu/log.h b/include/qemu/log.h index a50e994c21..ff92a8b86a 100644 --- a/include/qemu/log.h +++ b/include/qemu/log.h @@ -1,10 +1,11 @@ #ifndef QEMU_LOG_H #define QEMU_LOG_H +/* A small part of this API is split into its own header */ +#include "qemu/log-for-trace.h" -/* Private global variables, don't use */ +/* Private global variable, don't use */ extern FILE *qemu_logfile; -extern int qemu_loglevel; /* * The new API: @@ -41,16 +42,9 @@ static inline bool qemu_log_separate(void) #define CPU_LOG_MMU (1 << 12) #define CPU_LOG_TB_NOCHAIN (1 << 13) #define CPU_LOG_PAGE (1 << 14) -#define LOG_TRACE (1 << 15) +/* LOG_TRACE (1 << 15) is defined in log-for-trace.h */ #define CPU_LOG_TB_OP_IND (1 << 16) -/* Returns true if a bit is set in the current loglevel mask - */ -static inline bool qemu_loglevel_mask(int mask) -{ - return (qemu_loglevel & mask) != 0; -} - /* Lock output for a series of related logs. Since this is not needed * for a single qemu_log / qemu_log_mask / qemu_log_mask_and_addr, we * assume that qemu_loglevel_mask has already been tested, and that @@ -69,10 +63,6 @@ static inline void qemu_log_unlock(void) /* Logging functions: */ -/* main logging function - */ -int GCC_FMT_ATTR(1, 2) qemu_log(const char *fmt, ...); - /* vfprintf-like logging function */ static inline void GCC_FMT_ATTR(1, 0) diff --git a/include/qemu/module.h b/include/qemu/module.h index 9fea75aaeb..54300ab6e5 100644 --- a/include/qemu/module.h +++ b/include/qemu/module.h @@ -54,6 +54,7 @@ typedef enum { #define block_module_load_one(lib) module_load_one("block-", lib) #define ui_module_load_one(lib) module_load_one("ui-", lib) +#define audio_module_load_one(lib) module_load_one("audio-", lib) void register_module_init(void (*fn)(void), module_init_type type); void register_dso_module_init(void (*fn)(void), module_init_type type); diff --git a/include/qemu/option.h b/include/qemu/option.h index b127fb6db6..306fdb5f7a 100644 --- a/include/qemu/option.h +++ b/include/qemu/option.h @@ -124,6 +124,8 @@ void qemu_opts_set_defaults(QemuOptsList *list, const char *params, int permit_abbrev); QemuOpts *qemu_opts_from_qdict(QemuOptsList *list, const QDict *qdict, Error **errp); +QDict *qemu_opts_to_qdict_filtered(QemuOpts *opts, QDict *qdict, + QemuOptsList *list, bool del); QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict); void qemu_opts_absorb_qdict(QemuOpts *opts, QDict *qdict, Error **errp); diff --git a/include/standard-headers/linux/input-event-codes.h b/include/standard-headers/linux/input-event-codes.h index 79841b543f..9e6a8ba4ce 100644 --- a/include/standard-headers/linux/input-event-codes.h +++ b/include/standard-headers/linux/input-event-codes.h @@ -594,6 +594,7 @@ #define BTN_DPAD_RIGHT 0x223 #define KEY_ALS_TOGGLE 0x230 /* Ambient light sensor */ +#define KEY_ROTATE_LOCK_TOGGLE 0x231 /* Display rotation lock */ #define KEY_BUTTONCONFIG 0x240 /* AL Button Configuration */ #define KEY_TASKMANAGER 0x241 /* AL Task/Project Manager */ diff --git a/include/standard-headers/linux/input.h b/include/standard-headers/linux/input.h index bc3e6d3d5b..939b62775c 100644 --- a/include/standard-headers/linux/input.h +++ b/include/standard-headers/linux/input.h @@ -18,10 +18,21 @@ /* * The event structure itself + * Note that __USE_TIME_BITS64 is defined by libc based on + * application's request to use 64 bit time_t. */ struct input_event { +#if (HOST_LONG_BITS != 32 || !defined(__USE_TIME_BITS64)) && !defined(__KERNEL) struct timeval time; +#define input_event_sec time.tv_sec +#define input_event_usec time.tv_usec +#else + __kernel_ulong_t __sec; + __kernel_ulong_t __usec; +#define input_event_sec __sec +#define input_event_usec __usec +#endif uint16_t type; uint16_t code; int32_t value; diff --git a/include/standard-headers/linux/pci_regs.h b/include/standard-headers/linux/pci_regs.h index 70c2b2ade0..0c79eac5e9 100644 --- a/include/standard-headers/linux/pci_regs.h +++ b/include/standard-headers/linux/pci_regs.h @@ -622,15 +622,19 @@ * safely. */ #define PCI_EXP_DEVCAP2 36 /* Device Capabilities 2 */ +#define PCI_EXP_DEVCAP2_COMP_TMOUT_DIS 0x00000010 /* Completion Timeout Disable supported */ #define PCI_EXP_DEVCAP2_ARI 0x00000020 /* Alternative Routing-ID */ #define PCI_EXP_DEVCAP2_ATOMIC_ROUTE 0x00000040 /* Atomic Op routing */ -#define PCI_EXP_DEVCAP2_ATOMIC_COMP64 0x00000100 /* Atomic 64-bit compare */ +#define PCI_EXP_DEVCAP2_ATOMIC_COMP32 0x00000080 /* 32b AtomicOp completion */ +#define PCI_EXP_DEVCAP2_ATOMIC_COMP64 0x00000100 /* 64b AtomicOp completion */ +#define PCI_EXP_DEVCAP2_ATOMIC_COMP128 0x00000200 /* 128b AtomicOp completion */ #define PCI_EXP_DEVCAP2_LTR 0x00000800 /* Latency tolerance reporting */ #define PCI_EXP_DEVCAP2_OBFF_MASK 0x000c0000 /* OBFF support mechanism */ #define PCI_EXP_DEVCAP2_OBFF_MSG 0x00040000 /* New message signaling */ #define PCI_EXP_DEVCAP2_OBFF_WAKE 0x00080000 /* Re-use WAKE# for OBFF */ #define PCI_EXP_DEVCTL2 40 /* Device Control 2 */ #define PCI_EXP_DEVCTL2_COMP_TIMEOUT 0x000f /* Completion Timeout Value */ +#define PCI_EXP_DEVCTL2_COMP_TMOUT_DIS 0x0010 /* Completion Timeout Disable */ #define PCI_EXP_DEVCTL2_ARI 0x0020 /* Alternative Routing-ID */ #define PCI_EXP_DEVCTL2_ATOMIC_REQ 0x0040 /* Set Atomic requests */ #define PCI_EXP_DEVCTL2_ATOMIC_EGRESS_BLOCK 0x0080 /* Block atomic egress */ @@ -966,26 +970,28 @@ /* Downstream Port Containment */ #define PCI_EXP_DPC_CAP 4 /* DPC Capability */ -#define PCI_EXP_DPC_IRQ 0x1f /* DPC Interrupt Message Number */ -#define PCI_EXP_DPC_CAP_RP_EXT 0x20 /* Root Port Extensions for DPC */ -#define PCI_EXP_DPC_CAP_POISONED_TLP 0x40 /* Poisoned TLP Egress Blocking Supported */ -#define PCI_EXP_DPC_CAP_SW_TRIGGER 0x80 /* Software Triggering Supported */ -#define PCI_EXP_DPC_RP_PIO_LOG_SIZE 0xF00 /* RP PIO log size */ +#define PCI_EXP_DPC_IRQ 0x001F /* Interrupt Message Number */ +#define PCI_EXP_DPC_CAP_RP_EXT 0x0020 /* Root Port Extensions */ +#define PCI_EXP_DPC_CAP_POISONED_TLP 0x0040 /* Poisoned TLP Egress Blocking Supported */ +#define PCI_EXP_DPC_CAP_SW_TRIGGER 0x0080 /* Software Triggering Supported */ +#define PCI_EXP_DPC_RP_PIO_LOG_SIZE 0x0F00 /* RP PIO Log Size */ #define PCI_EXP_DPC_CAP_DL_ACTIVE 0x1000 /* ERR_COR signal on DL_Active supported */ #define PCI_EXP_DPC_CTL 6 /* DPC control */ -#define PCI_EXP_DPC_CTL_EN_NONFATAL 0x02 /* Enable trigger on ERR_NONFATAL message */ -#define PCI_EXP_DPC_CTL_INT_EN 0x08 /* DPC Interrupt Enable */ +#define PCI_EXP_DPC_CTL_EN_NONFATAL 0x0002 /* Enable trigger on ERR_NONFATAL message */ +#define PCI_EXP_DPC_CTL_INT_EN 0x0008 /* DPC Interrupt Enable */ #define PCI_EXP_DPC_STATUS 8 /* DPC Status */ -#define PCI_EXP_DPC_STATUS_TRIGGER 0x01 /* Trigger Status */ -#define PCI_EXP_DPC_STATUS_INTERRUPT 0x08 /* Interrupt Status */ -#define PCI_EXP_DPC_RP_BUSY 0x10 /* Root Port Busy */ +#define PCI_EXP_DPC_STATUS_TRIGGER 0x0001 /* Trigger Status */ +#define PCI_EXP_DPC_STATUS_TRIGGER_RSN 0x0006 /* Trigger Reason */ +#define PCI_EXP_DPC_STATUS_INTERRUPT 0x0008 /* Interrupt Status */ +#define PCI_EXP_DPC_RP_BUSY 0x0010 /* Root Port Busy */ +#define PCI_EXP_DPC_STATUS_TRIGGER_RSN_EXT 0x0060 /* Trig Reason Extension */ #define PCI_EXP_DPC_SOURCE_ID 10 /* DPC Source Identifier */ #define PCI_EXP_DPC_RP_PIO_STATUS 0x0C /* RP PIO Status */ -#define PCI_EXP_DPC_RP_PIO_MASK 0x10 /* RP PIO MASK */ +#define PCI_EXP_DPC_RP_PIO_MASK 0x10 /* RP PIO Mask */ #define PCI_EXP_DPC_RP_PIO_SEVERITY 0x14 /* RP PIO Severity */ #define PCI_EXP_DPC_RP_PIO_SYSERROR 0x18 /* RP PIO SysError */ #define PCI_EXP_DPC_RP_PIO_EXCEPTION 0x1C /* RP PIO Exception */ diff --git a/include/standard-headers/linux/virtio_net.h b/include/standard-headers/linux/virtio_net.h index 30ff24940d..e9f255ea3f 100644 --- a/include/standard-headers/linux/virtio_net.h +++ b/include/standard-headers/linux/virtio_net.h @@ -57,6 +57,8 @@ * Steering */ #define VIRTIO_NET_F_CTRL_MAC_ADDR 23 /* Set MAC address */ +#define VIRTIO_NET_F_SPEED_DUPLEX 63 /* Device set linkspeed and duplex */ + #ifndef VIRTIO_NET_NO_LEGACY #define VIRTIO_NET_F_GSO 6 /* Host handles pkts w/ any GSO type */ #endif /* VIRTIO_NET_NO_LEGACY */ @@ -76,6 +78,17 @@ struct virtio_net_config { uint16_t max_virtqueue_pairs; /* Default maximum transmit unit advice */ uint16_t mtu; + /* + * speed, in units of 1Mb. All values 0 to INT_MAX are legal. + * Any other value stands for unknown. + */ + uint32_t speed; + /* + * 0x00 - half duplex + * 0x01 - full duplex + * Any other value stands for unknown. + */ + uint8_t duplex; } QEMU_PACKED; /* diff --git a/include/standard-headers/linux/virtio_ring.h b/include/standard-headers/linux/virtio_ring.h index f1dc05df25..d26e72bc6b 100644 --- a/include/standard-headers/linux/virtio_ring.h +++ b/include/standard-headers/linux/virtio_ring.h @@ -78,7 +78,7 @@ struct vring_avail { __virtio16 ring[]; }; -/* u32 is used here for ids for padding reasons. */ +/* uint32_t is used here for ids for padding reasons. */ struct vring_used_elem { /* Index of start of used descriptor chain. */ __virtio32 id; diff --git a/include/standard-headers/rdma/vmw_pvrdma-abi.h b/include/standard-headers/rdma/vmw_pvrdma-abi.h index 0d0f7a8aca..07a820d337 100644 --- a/include/standard-headers/rdma/vmw_pvrdma-abi.h +++ b/include/standard-headers/rdma/vmw_pvrdma-abi.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) */ /* * Copyright (c) 2012-2016 VMware, Inc. All rights reserved. * @@ -51,12 +52,14 @@ #define PVRDMA_UVERBS_ABI_VERSION 3 /* ABI Version. */ #define PVRDMA_UAR_HANDLE_MASK 0x00FFFFFF /* Bottom 24 bits. */ #define PVRDMA_UAR_QP_OFFSET 0 /* QP doorbell. */ -#define PVRDMA_UAR_QP_SEND BIT(30) /* Send bit. */ -#define PVRDMA_UAR_QP_RECV BIT(31) /* Recv bit. */ +#define PVRDMA_UAR_QP_SEND (1 << 30) /* Send bit. */ +#define PVRDMA_UAR_QP_RECV (1 << 31) /* Recv bit. */ #define PVRDMA_UAR_CQ_OFFSET 4 /* CQ doorbell. */ -#define PVRDMA_UAR_CQ_ARM_SOL BIT(29) /* Arm solicited bit. */ -#define PVRDMA_UAR_CQ_ARM BIT(30) /* Arm bit. */ -#define PVRDMA_UAR_CQ_POLL BIT(31) /* Poll bit. */ +#define PVRDMA_UAR_CQ_ARM_SOL (1 << 29) /* Arm solicited bit. */ +#define PVRDMA_UAR_CQ_ARM (1 << 30) /* Arm bit. */ +#define PVRDMA_UAR_CQ_POLL (1 << 31) /* Poll bit. */ +#define PVRDMA_UAR_SRQ_OFFSET 8 /* SRQ doorbell. */ +#define PVRDMA_UAR_SRQ_RECV (1 << 30) /* Recv bit. */ enum pvrdma_wr_opcode { PVRDMA_WR_RDMA_WRITE, diff --git a/include/sysemu/iothread.h b/include/sysemu/iothread.h index 799614ffd2..8a7ac2c528 100644 --- a/include/sysemu/iothread.h +++ b/include/sysemu/iothread.h @@ -45,7 +45,6 @@ typedef struct { char *iothread_get_id(IOThread *iothread); IOThread *iothread_by_id(const char *id); AioContext *iothread_get_aio_context(IOThread *iothread); -void iothread_stop_all(void); GMainContext *iothread_get_g_main_context(IOThread *iothread); /* diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 85002ac49a..23669c4d5a 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -231,6 +231,23 @@ int kvm_destroy_vcpu(CPUState *cpu); */ bool kvm_arm_supports_user_irq(void); +/** + * kvm_memcrypt_enabled - return boolean indicating whether memory encryption + * is enabled + * Returns: 1 memory encryption is enabled + * 0 memory encryption is disabled + */ +bool kvm_memcrypt_enabled(void); + +/** + * kvm_memcrypt_encrypt_data: encrypt the memory range + * + * Return: 1 failed to encrypt the range + * 0 succesfully encrypted memory region + */ +int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len); + + #ifdef NEED_CPU_H #include "cpu.h" diff --git a/include/sysemu/sev.h b/include/sysemu/sev.h new file mode 100644 index 0000000000..98c1ec8d38 --- /dev/null +++ b/include/sysemu/sev.h @@ -0,0 +1,21 @@ +/* + * QEMU Secure Encrypted Virutualization (SEV) support + * + * Copyright: Advanced Micro Devices, 2016-2018 + * + * Authors: + * Brijesh Singh <brijesh.singh@amd.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef QEMU_SEV_H +#define QEMU_SEV_H + +#include "sysemu/kvm.h" + +void *sev_guest_init(const char *id); +int sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len); +#endif diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index d24ad09f37..356bfdc1c1 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -56,6 +56,7 @@ void vm_start(void); int vm_prepare_start(void); int vm_stop(RunState state); int vm_stop_force_state(RunState state); +int vm_shutdown(void); typedef enum WakeupReason { /* Always keep QEMU_WAKEUP_REASON_NONE = 0 */ diff --git a/include/ui/console.h b/include/ui/console.h index aae9e44cb3..5fca9afcbc 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -260,6 +260,8 @@ DisplaySurface *qemu_create_displaysurface_guestmem(int width, int height, pixman_format_code_t format, int linesize, uint64_t addr); +DisplaySurface *qemu_create_message_surface(int w, int h, + const char *msg); PixelFormat qemu_default_pixelformat(int bpp); DisplaySurface *qemu_create_displaysurface(int width, int height); diff --git a/include/ui/gtk.h b/include/ui/gtk.h index 849c896eef..2922fc64b2 100644 --- a/include/ui/gtk.h +++ b/include/ui/gtk.h @@ -54,6 +54,9 @@ typedef struct VirtualGfxConsole { int x, y, w, h; egl_fb guest_fb; egl_fb win_fb; + egl_fb cursor_fb; + int cursor_x; + int cursor_y; bool y0_top; bool scanout_mode; #endif @@ -90,6 +93,8 @@ typedef struct VirtualConsole { }; } VirtualConsole; +extern bool gtk_use_gl_area; + /* ui/gtk.c */ void gd_update_windowsize(VirtualConsole *vc); @@ -111,6 +116,15 @@ void gd_egl_scanout_texture(DisplayChangeListener *dcl, uint32_t backing_height, uint32_t x, uint32_t y, uint32_t w, uint32_t h); +void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl, + QemuDmaBuf *dmabuf); +void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl, + QemuDmaBuf *dmabuf, bool have_hot, + uint32_t hot_x, uint32_t hot_y); +void gd_egl_cursor_position(DisplayChangeListener *dcl, + uint32_t pos_x, uint32_t pos_y); +void gd_egl_release_dmabuf(DisplayChangeListener *dcl, + QemuDmaBuf *dmabuf); void gd_egl_scanout_flush(DisplayChangeListener *dcl, uint32_t x, uint32_t y, uint32_t w, uint32_t h); void gtk_egl_init(void); diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h index 6b5c73b21c..87a84a59d4 100644 --- a/include/ui/spice-display.h +++ b/include/ui/spice-display.h @@ -122,6 +122,15 @@ struct SimpleSpiceDisplay { int gl_updates; bool have_scanout; bool have_surface; + + QemuDmaBuf *guest_dmabuf; + bool guest_dmabuf_refresh; + bool render_cursor; + + egl_fb guest_fb; + egl_fb blit_fb; + egl_fb cursor_fb; + bool have_hot; #endif }; diff --git a/iothread.c b/iothread.c index 2ec5a3bffe..1b3463cb00 100644 --- a/iothread.c +++ b/iothread.c @@ -101,18 +101,6 @@ void iothread_stop(IOThread *iothread) qemu_thread_join(&iothread->thread); } -static int iothread_stop_iter(Object *object, void *opaque) -{ - IOThread *iothread; - - iothread = (IOThread *)object_dynamic_cast(object, TYPE_IOTHREAD); - if (!iothread) { - return 0; - } - iothread_stop(iothread); - return 0; -} - static void iothread_instance_init(Object *obj) { IOThread *iothread = IOTHREAD(obj); @@ -333,25 +321,6 @@ IOThreadInfoList *qmp_query_iothreads(Error **errp) return head; } -void iothread_stop_all(void) -{ - Object *container = object_get_objects_root(); - BlockDriverState *bs; - BdrvNextIterator it; - - for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { - AioContext *ctx = bdrv_get_aio_context(bs); - if (ctx == qemu_get_aio_context()) { - continue; - } - aio_context_acquire(ctx); - bdrv_set_aio_context(bs, qemu_get_aio_context()); - aio_context_release(ctx); - } - - object_child_foreach(container, iothread_stop_iter, NULL); -} - static gpointer iothread_g_main_context_init(gpointer opaque) { AioContext *ctx; diff --git a/linux-headers/asm-powerpc/kvm.h b/linux-headers/asm-powerpc/kvm.h index 637b7263cb..833ed9a16a 100644 --- a/linux-headers/asm-powerpc/kvm.h +++ b/linux-headers/asm-powerpc/kvm.h @@ -632,6 +632,8 @@ struct kvm_ppc_cpu_char { #define KVM_REG_PPC_TIDR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xbc) #define KVM_REG_PPC_PSSCR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xbd) +#define KVM_REG_PPC_DEC_EXPIRY (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xbe) + /* Transactional Memory checkpointed state: * This is all GPRs, all VSX regs and a subset of SPRs */ diff --git a/linux-headers/asm-powerpc/unistd.h b/linux-headers/asm-powerpc/unistd.h index 36abf58582..0c08edcfcd 100644 --- a/linux-headers/asm-powerpc/unistd.h +++ b/linux-headers/asm-powerpc/unistd.h @@ -395,5 +395,8 @@ #define __NR_pwritev2 381 #define __NR_kexec_file_load 382 #define __NR_statx 383 +#define __NR_pkey_alloc 384 +#define __NR_pkey_free 385 +#define __NR_pkey_mprotect 386 #endif /* _ASM_POWERPC_UNISTD_H_ */ diff --git a/linux-headers/asm-s390/unistd.h b/linux-headers/asm-s390/unistd.h index 99223b874a..27b8b211c8 100644 --- a/linux-headers/asm-s390/unistd.h +++ b/linux-headers/asm-s390/unistd.h @@ -8,405 +8,10 @@ #ifndef _ASM_S390_UNISTD_H_ #define _ASM_S390_UNISTD_H_ -/* - * This file contains the system call numbers. - */ - -#define __NR_exit 1 -#define __NR_fork 2 -#define __NR_read 3 -#define __NR_write 4 -#define __NR_open 5 -#define __NR_close 6 -#define __NR_restart_syscall 7 -#define __NR_creat 8 -#define __NR_link 9 -#define __NR_unlink 10 -#define __NR_execve 11 -#define __NR_chdir 12 -#define __NR_mknod 14 -#define __NR_chmod 15 -#define __NR_lseek 19 -#define __NR_getpid 20 -#define __NR_mount 21 -#define __NR_umount 22 -#define __NR_ptrace 26 -#define __NR_alarm 27 -#define __NR_pause 29 -#define __NR_utime 30 -#define __NR_access 33 -#define __NR_nice 34 -#define __NR_sync 36 -#define __NR_kill 37 -#define __NR_rename 38 -#define __NR_mkdir 39 -#define __NR_rmdir 40 -#define __NR_dup 41 -#define __NR_pipe 42 -#define __NR_times 43 -#define __NR_brk 45 -#define __NR_signal 48 -#define __NR_acct 51 -#define __NR_umount2 52 -#define __NR_ioctl 54 -#define __NR_fcntl 55 -#define __NR_setpgid 57 -#define __NR_umask 60 -#define __NR_chroot 61 -#define __NR_ustat 62 -#define __NR_dup2 63 -#define __NR_getppid 64 -#define __NR_getpgrp 65 -#define __NR_setsid 66 -#define __NR_sigaction 67 -#define __NR_sigsuspend 72 -#define __NR_sigpending 73 -#define __NR_sethostname 74 -#define __NR_setrlimit 75 -#define __NR_getrusage 77 -#define __NR_gettimeofday 78 -#define __NR_settimeofday 79 -#define __NR_symlink 83 -#define __NR_readlink 85 -#define __NR_uselib 86 -#define __NR_swapon 87 -#define __NR_reboot 88 -#define __NR_readdir 89 -#define __NR_mmap 90 -#define __NR_munmap 91 -#define __NR_truncate 92 -#define __NR_ftruncate 93 -#define __NR_fchmod 94 -#define __NR_getpriority 96 -#define __NR_setpriority 97 -#define __NR_statfs 99 -#define __NR_fstatfs 100 -#define __NR_socketcall 102 -#define __NR_syslog 103 -#define __NR_setitimer 104 -#define __NR_getitimer 105 -#define __NR_stat 106 -#define __NR_lstat 107 -#define __NR_fstat 108 -#define __NR_lookup_dcookie 110 -#define __NR_vhangup 111 -#define __NR_idle 112 -#define __NR_wait4 114 -#define __NR_swapoff 115 -#define __NR_sysinfo 116 -#define __NR_ipc 117 -#define __NR_fsync 118 -#define __NR_sigreturn 119 -#define __NR_clone 120 -#define __NR_setdomainname 121 -#define __NR_uname 122 -#define __NR_adjtimex 124 -#define __NR_mprotect 125 -#define __NR_sigprocmask 126 -#define __NR_create_module 127 -#define __NR_init_module 128 -#define __NR_delete_module 129 -#define __NR_get_kernel_syms 130 -#define __NR_quotactl 131 -#define __NR_getpgid 132 -#define __NR_fchdir 133 -#define __NR_bdflush 134 -#define __NR_sysfs 135 -#define __NR_personality 136 -#define __NR_afs_syscall 137 /* Syscall for Andrew File System */ -#define __NR_getdents 141 -#define __NR_flock 143 -#define __NR_msync 144 -#define __NR_readv 145 -#define __NR_writev 146 -#define __NR_getsid 147 -#define __NR_fdatasync 148 -#define __NR__sysctl 149 -#define __NR_mlock 150 -#define __NR_munlock 151 -#define __NR_mlockall 152 -#define __NR_munlockall 153 -#define __NR_sched_setparam 154 -#define __NR_sched_getparam 155 -#define __NR_sched_setscheduler 156 -#define __NR_sched_getscheduler 157 -#define __NR_sched_yield 158 -#define __NR_sched_get_priority_max 159 -#define __NR_sched_get_priority_min 160 -#define __NR_sched_rr_get_interval 161 -#define __NR_nanosleep 162 -#define __NR_mremap 163 -#define __NR_query_module 167 -#define __NR_poll 168 -#define __NR_nfsservctl 169 -#define __NR_prctl 172 -#define __NR_rt_sigreturn 173 -#define __NR_rt_sigaction 174 -#define __NR_rt_sigprocmask 175 -#define __NR_rt_sigpending 176 -#define __NR_rt_sigtimedwait 177 -#define __NR_rt_sigqueueinfo 178 -#define __NR_rt_sigsuspend 179 -#define __NR_pread64 180 -#define __NR_pwrite64 181 -#define __NR_getcwd 183 -#define __NR_capget 184 -#define __NR_capset 185 -#define __NR_sigaltstack 186 -#define __NR_sendfile 187 -#define __NR_getpmsg 188 -#define __NR_putpmsg 189 -#define __NR_vfork 190 -#define __NR_pivot_root 217 -#define __NR_mincore 218 -#define __NR_madvise 219 -#define __NR_getdents64 220 -#define __NR_readahead 222 -#define __NR_setxattr 224 -#define __NR_lsetxattr 225 -#define __NR_fsetxattr 226 -#define __NR_getxattr 227 -#define __NR_lgetxattr 228 -#define __NR_fgetxattr 229 -#define __NR_listxattr 230 -#define __NR_llistxattr 231 -#define __NR_flistxattr 232 -#define __NR_removexattr 233 -#define __NR_lremovexattr 234 -#define __NR_fremovexattr 235 -#define __NR_gettid 236 -#define __NR_tkill 237 -#define __NR_futex 238 -#define __NR_sched_setaffinity 239 -#define __NR_sched_getaffinity 240 -#define __NR_tgkill 241 -/* Number 242 is reserved for tux */ -#define __NR_io_setup 243 -#define __NR_io_destroy 244 -#define __NR_io_getevents 245 -#define __NR_io_submit 246 -#define __NR_io_cancel 247 -#define __NR_exit_group 248 -#define __NR_epoll_create 249 -#define __NR_epoll_ctl 250 -#define __NR_epoll_wait 251 -#define __NR_set_tid_address 252 -#define __NR_fadvise64 253 -#define __NR_timer_create 254 -#define __NR_timer_settime 255 -#define __NR_timer_gettime 256 -#define __NR_timer_getoverrun 257 -#define __NR_timer_delete 258 -#define __NR_clock_settime 259 -#define __NR_clock_gettime 260 -#define __NR_clock_getres 261 -#define __NR_clock_nanosleep 262 -/* Number 263 is reserved for vserver */ -#define __NR_statfs64 265 -#define __NR_fstatfs64 266 -#define __NR_remap_file_pages 267 -#define __NR_mbind 268 -#define __NR_get_mempolicy 269 -#define __NR_set_mempolicy 270 -#define __NR_mq_open 271 -#define __NR_mq_unlink 272 -#define __NR_mq_timedsend 273 -#define __NR_mq_timedreceive 274 -#define __NR_mq_notify 275 -#define __NR_mq_getsetattr 276 -#define __NR_kexec_load 277 -#define __NR_add_key 278 -#define __NR_request_key 279 -#define __NR_keyctl 280 -#define __NR_waitid 281 -#define __NR_ioprio_set 282 -#define __NR_ioprio_get 283 -#define __NR_inotify_init 284 -#define __NR_inotify_add_watch 285 -#define __NR_inotify_rm_watch 286 -#define __NR_migrate_pages 287 -#define __NR_openat 288 -#define __NR_mkdirat 289 -#define __NR_mknodat 290 -#define __NR_fchownat 291 -#define __NR_futimesat 292 -#define __NR_unlinkat 294 -#define __NR_renameat 295 -#define __NR_linkat 296 -#define __NR_symlinkat 297 -#define __NR_readlinkat 298 -#define __NR_fchmodat 299 -#define __NR_faccessat 300 -#define __NR_pselect6 301 -#define __NR_ppoll 302 -#define __NR_unshare 303 -#define __NR_set_robust_list 304 -#define __NR_get_robust_list 305 -#define __NR_splice 306 -#define __NR_sync_file_range 307 -#define __NR_tee 308 -#define __NR_vmsplice 309 -#define __NR_move_pages 310 -#define __NR_getcpu 311 -#define __NR_epoll_pwait 312 -#define __NR_utimes 313 -#define __NR_fallocate 314 -#define __NR_utimensat 315 -#define __NR_signalfd 316 -#define __NR_timerfd 317 -#define __NR_eventfd 318 -#define __NR_timerfd_create 319 -#define __NR_timerfd_settime 320 -#define __NR_timerfd_gettime 321 -#define __NR_signalfd4 322 -#define __NR_eventfd2 323 -#define __NR_inotify_init1 324 -#define __NR_pipe2 325 -#define __NR_dup3 326 -#define __NR_epoll_create1 327 -#define __NR_preadv 328 -#define __NR_pwritev 329 -#define __NR_rt_tgsigqueueinfo 330 -#define __NR_perf_event_open 331 -#define __NR_fanotify_init 332 -#define __NR_fanotify_mark 333 -#define __NR_prlimit64 334 -#define __NR_name_to_handle_at 335 -#define __NR_open_by_handle_at 336 -#define __NR_clock_adjtime 337 -#define __NR_syncfs 338 -#define __NR_setns 339 -#define __NR_process_vm_readv 340 -#define __NR_process_vm_writev 341 -#define __NR_s390_runtime_instr 342 -#define __NR_kcmp 343 -#define __NR_finit_module 344 -#define __NR_sched_setattr 345 -#define __NR_sched_getattr 346 -#define __NR_renameat2 347 -#define __NR_seccomp 348 -#define __NR_getrandom 349 -#define __NR_memfd_create 350 -#define __NR_bpf 351 -#define __NR_s390_pci_mmio_write 352 -#define __NR_s390_pci_mmio_read 353 -#define __NR_execveat 354 -#define __NR_userfaultfd 355 -#define __NR_membarrier 356 -#define __NR_recvmmsg 357 -#define __NR_sendmmsg 358 -#define __NR_socket 359 -#define __NR_socketpair 360 -#define __NR_bind 361 -#define __NR_connect 362 -#define __NR_listen 363 -#define __NR_accept4 364 -#define __NR_getsockopt 365 -#define __NR_setsockopt 366 -#define __NR_getsockname 367 -#define __NR_getpeername 368 -#define __NR_sendto 369 -#define __NR_sendmsg 370 -#define __NR_recvfrom 371 -#define __NR_recvmsg 372 -#define __NR_shutdown 373 -#define __NR_mlock2 374 -#define __NR_copy_file_range 375 -#define __NR_preadv2 376 -#define __NR_pwritev2 377 -#define __NR_s390_guarded_storage 378 -#define __NR_statx 379 -#define __NR_s390_sthyi 380 -#define NR_syscalls 381 - -/* - * There are some system calls that are not present on 64 bit, some - * have a different name although they do the same (e.g. __NR_chown32 - * is __NR_chown on 64 bit). - */ -#ifndef __s390x__ - -#define __NR_time 13 -#define __NR_lchown 16 -#define __NR_setuid 23 -#define __NR_getuid 24 -#define __NR_stime 25 -#define __NR_setgid 46 -#define __NR_getgid 47 -#define __NR_geteuid 49 -#define __NR_getegid 50 -#define __NR_setreuid 70 -#define __NR_setregid 71 -#define __NR_getrlimit 76 -#define __NR_getgroups 80 -#define __NR_setgroups 81 -#define __NR_fchown 95 -#define __NR_ioperm 101 -#define __NR_setfsuid 138 -#define __NR_setfsgid 139 -#define __NR__llseek 140 -#define __NR__newselect 142 -#define __NR_setresuid 164 -#define __NR_getresuid 165 -#define __NR_setresgid 170 -#define __NR_getresgid 171 -#define __NR_chown 182 -#define __NR_ugetrlimit 191 /* SuS compliant getrlimit */ -#define __NR_mmap2 192 -#define __NR_truncate64 193 -#define __NR_ftruncate64 194 -#define __NR_stat64 195 -#define __NR_lstat64 196 -#define __NR_fstat64 197 -#define __NR_lchown32 198 -#define __NR_getuid32 199 -#define __NR_getgid32 200 -#define __NR_geteuid32 201 -#define __NR_getegid32 202 -#define __NR_setreuid32 203 -#define __NR_setregid32 204 -#define __NR_getgroups32 205 -#define __NR_setgroups32 206 -#define __NR_fchown32 207 -#define __NR_setresuid32 208 -#define __NR_getresuid32 209 -#define __NR_setresgid32 210 -#define __NR_getresgid32 211 -#define __NR_chown32 212 -#define __NR_setuid32 213 -#define __NR_setgid32 214 -#define __NR_setfsuid32 215 -#define __NR_setfsgid32 216 -#define __NR_fcntl64 221 -#define __NR_sendfile64 223 -#define __NR_fadvise64_64 264 -#define __NR_fstatat64 293 - +#ifdef __s390x__ +#include <asm/unistd_64.h> #else - -#define __NR_select 142 -#define __NR_getrlimit 191 /* SuS compliant getrlimit */ -#define __NR_lchown 198 -#define __NR_getuid 199 -#define __NR_getgid 200 -#define __NR_geteuid 201 -#define __NR_getegid 202 -#define __NR_setreuid 203 -#define __NR_setregid 204 -#define __NR_getgroups 205 -#define __NR_setgroups 206 -#define __NR_fchown 207 -#define __NR_setresuid 208 -#define __NR_getresuid 209 -#define __NR_setresgid 210 -#define __NR_getresgid 211 -#define __NR_chown 212 -#define __NR_setuid 213 -#define __NR_setgid 214 -#define __NR_setfsuid 215 -#define __NR_setfsgid 216 -#define __NR_newfstatat 293 - +#include <asm/unistd_32.h> #endif #endif /* _ASM_S390_UNISTD_H_ */ diff --git a/linux-headers/asm-s390/unistd_32.h b/linux-headers/asm-s390/unistd_32.h new file mode 100644 index 0000000000..1ae66a263b --- /dev/null +++ b/linux-headers/asm-s390/unistd_32.h @@ -0,0 +1,364 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _ASM_S390_UNISTD_32_H +#define _ASM_S390_UNISTD_32_H + +#define __NR_exit 1 +#define __NR_fork 2 +#define __NR_read 3 +#define __NR_write 4 +#define __NR_open 5 +#define __NR_close 6 +#define __NR_restart_syscall 7 +#define __NR_creat 8 +#define __NR_link 9 +#define __NR_unlink 10 +#define __NR_execve 11 +#define __NR_chdir 12 +#define __NR_time 13 +#define __NR_mknod 14 +#define __NR_chmod 15 +#define __NR_lchown 16 +#define __NR_lseek 19 +#define __NR_getpid 20 +#define __NR_mount 21 +#define __NR_umount 22 +#define __NR_setuid 23 +#define __NR_getuid 24 +#define __NR_stime 25 +#define __NR_ptrace 26 +#define __NR_alarm 27 +#define __NR_pause 29 +#define __NR_utime 30 +#define __NR_access 33 +#define __NR_nice 34 +#define __NR_sync 36 +#define __NR_kill 37 +#define __NR_rename 38 +#define __NR_mkdir 39 +#define __NR_rmdir 40 +#define __NR_dup 41 +#define __NR_pipe 42 +#define __NR_times 43 +#define __NR_brk 45 +#define __NR_setgid 46 +#define __NR_getgid 47 +#define __NR_signal 48 +#define __NR_geteuid 49 +#define __NR_getegid 50 +#define __NR_acct 51 +#define __NR_umount2 52 +#define __NR_ioctl 54 +#define __NR_fcntl 55 +#define __NR_setpgid 57 +#define __NR_umask 60 +#define __NR_chroot 61 +#define __NR_ustat 62 +#define __NR_dup2 63 +#define __NR_getppid 64 +#define __NR_getpgrp 65 +#define __NR_setsid 66 +#define __NR_sigaction 67 +#define __NR_setreuid 70 +#define __NR_setregid 71 +#define __NR_sigsuspend 72 +#define __NR_sigpending 73 +#define __NR_sethostname 74 +#define __NR_setrlimit 75 +#define __NR_getrlimit 76 +#define __NR_getrusage 77 +#define __NR_gettimeofday 78 +#define __NR_settimeofday 79 +#define __NR_getgroups 80 +#define __NR_setgroups 81 +#define __NR_symlink 83 +#define __NR_readlink 85 +#define __NR_uselib 86 +#define __NR_swapon 87 +#define __NR_reboot 88 +#define __NR_readdir 89 +#define __NR_mmap 90 +#define __NR_munmap 91 +#define __NR_truncate 92 +#define __NR_ftruncate 93 +#define __NR_fchmod 94 +#define __NR_fchown 95 +#define __NR_getpriority 96 +#define __NR_setpriority 97 +#define __NR_statfs 99 +#define __NR_fstatfs 100 +#define __NR_ioperm 101 +#define __NR_socketcall 102 +#define __NR_syslog 103 +#define __NR_setitimer 104 +#define __NR_getitimer 105 +#define __NR_stat 106 +#define __NR_lstat 107 +#define __NR_fstat 108 +#define __NR_lookup_dcookie 110 +#define __NR_vhangup 111 +#define __NR_idle 112 +#define __NR_wait4 114 +#define __NR_swapoff 115 +#define __NR_sysinfo 116 +#define __NR_ipc 117 +#define __NR_fsync 118 +#define __NR_sigreturn 119 +#define __NR_clone 120 +#define __NR_setdomainname 121 +#define __NR_uname 122 +#define __NR_adjtimex 124 +#define __NR_mprotect 125 +#define __NR_sigprocmask 126 +#define __NR_create_module 127 +#define __NR_init_module 128 +#define __NR_delete_module 129 +#define __NR_get_kernel_syms 130 +#define __NR_quotactl 131 +#define __NR_getpgid 132 +#define __NR_fchdir 133 +#define __NR_bdflush 134 +#define __NR_sysfs 135 +#define __NR_personality 136 +#define __NR_afs_syscall 137 +#define __NR_setfsuid 138 +#define __NR_setfsgid 139 +#define __NR__llseek 140 +#define __NR_getdents 141 +#define __NR__newselect 142 +#define __NR_flock 143 +#define __NR_msync 144 +#define __NR_readv 145 +#define __NR_writev 146 +#define __NR_getsid 147 +#define __NR_fdatasync 148 +#define __NR__sysctl 149 +#define __NR_mlock 150 +#define __NR_munlock 151 +#define __NR_mlockall 152 +#define __NR_munlockall 153 +#define __NR_sched_setparam 154 +#define __NR_sched_getparam 155 +#define __NR_sched_setscheduler 156 +#define __NR_sched_getscheduler 157 +#define __NR_sched_yield 158 +#define __NR_sched_get_priority_max 159 +#define __NR_sched_get_priority_min 160 +#define __NR_sched_rr_get_interval 161 +#define __NR_nanosleep 162 +#define __NR_mremap 163 +#define __NR_setresuid 164 +#define __NR_getresuid 165 +#define __NR_query_module 167 +#define __NR_poll 168 +#define __NR_nfsservctl 169 +#define __NR_setresgid 170 +#define __NR_getresgid 171 +#define __NR_prctl 172 +#define __NR_rt_sigreturn 173 +#define __NR_rt_sigaction 174 +#define __NR_rt_sigprocmask 175 +#define __NR_rt_sigpending 176 +#define __NR_rt_sigtimedwait 177 +#define __NR_rt_sigqueueinfo 178 +#define __NR_rt_sigsuspend 179 +#define __NR_pread64 180 +#define __NR_pwrite64 181 +#define __NR_chown 182 +#define __NR_getcwd 183 +#define __NR_capget 184 +#define __NR_capset 185 +#define __NR_sigaltstack 186 +#define __NR_sendfile 187 +#define __NR_getpmsg 188 +#define __NR_putpmsg 189 +#define __NR_vfork 190 +#define __NR_ugetrlimit 191 +#define __NR_mmap2 192 +#define __NR_truncate64 193 +#define __NR_ftruncate64 194 +#define __NR_stat64 195 +#define __NR_lstat64 196 +#define __NR_fstat64 197 +#define __NR_lchown32 198 +#define __NR_getuid32 199 +#define __NR_getgid32 200 +#define __NR_geteuid32 201 +#define __NR_getegid32 202 +#define __NR_setreuid32 203 +#define __NR_setregid32 204 +#define __NR_getgroups32 205 +#define __NR_setgroups32 206 +#define __NR_fchown32 207 +#define __NR_setresuid32 208 +#define __NR_getresuid32 209 +#define __NR_setresgid32 210 +#define __NR_getresgid32 211 +#define __NR_chown32 212 +#define __NR_setuid32 213 +#define __NR_setgid32 214 +#define __NR_setfsuid32 215 +#define __NR_setfsgid32 216 +#define __NR_pivot_root 217 +#define __NR_mincore 218 +#define __NR_madvise 219 +#define __NR_getdents64 220 +#define __NR_fcntl64 221 +#define __NR_readahead 222 +#define __NR_sendfile64 223 +#define __NR_setxattr 224 +#define __NR_lsetxattr 225 +#define __NR_fsetxattr 226 +#define __NR_getxattr 227 +#define __NR_lgetxattr 228 +#define __NR_fgetxattr 229 +#define __NR_listxattr 230 +#define __NR_llistxattr 231 +#define __NR_flistxattr 232 +#define __NR_removexattr 233 +#define __NR_lremovexattr 234 +#define __NR_fremovexattr 235 +#define __NR_gettid 236 +#define __NR_tkill 237 +#define __NR_futex 238 +#define __NR_sched_setaffinity 239 +#define __NR_sched_getaffinity 240 +#define __NR_tgkill 241 +#define __NR_io_setup 243 +#define __NR_io_destroy 244 +#define __NR_io_getevents 245 +#define __NR_io_submit 246 +#define __NR_io_cancel 247 +#define __NR_exit_group 248 +#define __NR_epoll_create 249 +#define __NR_epoll_ctl 250 +#define __NR_epoll_wait 251 +#define __NR_set_tid_address 252 +#define __NR_fadvise64 253 +#define __NR_timer_create 254 +#define __NR_timer_settime 255 +#define __NR_timer_gettime 256 +#define __NR_timer_getoverrun 257 +#define __NR_timer_delete 258 +#define __NR_clock_settime 259 +#define __NR_clock_gettime 260 +#define __NR_clock_getres 261 +#define __NR_clock_nanosleep 262 +#define __NR_fadvise64_64 264 +#define __NR_statfs64 265 +#define __NR_fstatfs64 266 +#define __NR_remap_file_pages 267 +#define __NR_mbind 268 +#define __NR_get_mempolicy 269 +#define __NR_set_mempolicy 270 +#define __NR_mq_open 271 +#define __NR_mq_unlink 272 +#define __NR_mq_timedsend 273 +#define __NR_mq_timedreceive 274 +#define __NR_mq_notify 275 +#define __NR_mq_getsetattr 276 +#define __NR_kexec_load 277 +#define __NR_add_key 278 +#define __NR_request_key 279 +#define __NR_keyctl 280 +#define __NR_waitid 281 +#define __NR_ioprio_set 282 +#define __NR_ioprio_get 283 +#define __NR_inotify_init 284 +#define __NR_inotify_add_watch 285 +#define __NR_inotify_rm_watch 286 +#define __NR_migrate_pages 287 +#define __NR_openat 288 +#define __NR_mkdirat 289 +#define __NR_mknodat 290 +#define __NR_fchownat 291 +#define __NR_futimesat 292 +#define __NR_fstatat64 293 +#define __NR_unlinkat 294 +#define __NR_renameat 295 +#define __NR_linkat 296 +#define __NR_symlinkat 297 +#define __NR_readlinkat 298 +#define __NR_fchmodat 299 +#define __NR_faccessat 300 +#define __NR_pselect6 301 +#define __NR_ppoll 302 +#define __NR_unshare 303 +#define __NR_set_robust_list 304 +#define __NR_get_robust_list 305 +#define __NR_splice 306 +#define __NR_sync_file_range 307 +#define __NR_tee 308 +#define __NR_vmsplice 309 +#define __NR_move_pages 310 +#define __NR_getcpu 311 +#define __NR_epoll_pwait 312 +#define __NR_utimes 313 +#define __NR_fallocate 314 +#define __NR_utimensat 315 +#define __NR_signalfd 316 +#define __NR_timerfd 317 +#define __NR_eventfd 318 +#define __NR_timerfd_create 319 +#define __NR_timerfd_settime 320 +#define __NR_timerfd_gettime 321 +#define __NR_signalfd4 322 +#define __NR_eventfd2 323 +#define __NR_inotify_init1 324 +#define __NR_pipe2 325 +#define __NR_dup3 326 +#define __NR_epoll_create1 327 +#define __NR_preadv 328 +#define __NR_pwritev 329 +#define __NR_rt_tgsigqueueinfo 330 +#define __NR_perf_event_open 331 +#define __NR_fanotify_init 332 +#define __NR_fanotify_mark 333 +#define __NR_prlimit64 334 +#define __NR_name_to_handle_at 335 +#define __NR_open_by_handle_at 336 +#define __NR_clock_adjtime 337 +#define __NR_syncfs 338 +#define __NR_setns 339 +#define __NR_process_vm_readv 340 +#define __NR_process_vm_writev 341 +#define __NR_s390_runtime_instr 342 +#define __NR_kcmp 343 +#define __NR_finit_module 344 +#define __NR_sched_setattr 345 +#define __NR_sched_getattr 346 +#define __NR_renameat2 347 +#define __NR_seccomp 348 +#define __NR_getrandom 349 +#define __NR_memfd_create 350 +#define __NR_bpf 351 +#define __NR_s390_pci_mmio_write 352 +#define __NR_s390_pci_mmio_read 353 +#define __NR_execveat 354 +#define __NR_userfaultfd 355 +#define __NR_membarrier 356 +#define __NR_recvmmsg 357 +#define __NR_sendmmsg 358 +#define __NR_socket 359 +#define __NR_socketpair 360 +#define __NR_bind 361 +#define __NR_connect 362 +#define __NR_listen 363 +#define __NR_accept4 364 +#define __NR_getsockopt 365 +#define __NR_setsockopt 366 +#define __NR_getsockname 367 +#define __NR_getpeername 368 +#define __NR_sendto 369 +#define __NR_sendmsg 370 +#define __NR_recvfrom 371 +#define __NR_recvmsg 372 +#define __NR_shutdown 373 +#define __NR_mlock2 374 +#define __NR_copy_file_range 375 +#define __NR_preadv2 376 +#define __NR_pwritev2 377 +#define __NR_s390_guarded_storage 378 +#define __NR_statx 379 +#define __NR_s390_sthyi 380 + +#endif /* _ASM_S390_UNISTD_32_H */ diff --git a/linux-headers/asm-s390/unistd_64.h b/linux-headers/asm-s390/unistd_64.h new file mode 100644 index 0000000000..8aa9d046a9 --- /dev/null +++ b/linux-headers/asm-s390/unistd_64.h @@ -0,0 +1,331 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _ASM_S390_UNISTD_64_H +#define _ASM_S390_UNISTD_64_H + +#define __NR_exit 1 +#define __NR_fork 2 +#define __NR_read 3 +#define __NR_write 4 +#define __NR_open 5 +#define __NR_close 6 +#define __NR_restart_syscall 7 +#define __NR_creat 8 +#define __NR_link 9 +#define __NR_unlink 10 +#define __NR_execve 11 +#define __NR_chdir 12 +#define __NR_mknod 14 +#define __NR_chmod 15 +#define __NR_lseek 19 +#define __NR_getpid 20 +#define __NR_mount 21 +#define __NR_umount 22 +#define __NR_ptrace 26 +#define __NR_alarm 27 +#define __NR_pause 29 +#define __NR_utime 30 +#define __NR_access 33 +#define __NR_nice 34 +#define __NR_sync 36 +#define __NR_kill 37 +#define __NR_rename 38 +#define __NR_mkdir 39 +#define __NR_rmdir 40 +#define __NR_dup 41 +#define __NR_pipe 42 +#define __NR_times 43 +#define __NR_brk 45 +#define __NR_signal 48 +#define __NR_acct 51 +#define __NR_umount2 52 +#define __NR_ioctl 54 +#define __NR_fcntl 55 +#define __NR_setpgid 57 +#define __NR_umask 60 +#define __NR_chroot 61 +#define __NR_ustat 62 +#define __NR_dup2 63 +#define __NR_getppid 64 +#define __NR_getpgrp 65 +#define __NR_setsid 66 +#define __NR_sigaction 67 +#define __NR_sigsuspend 72 +#define __NR_sigpending 73 +#define __NR_sethostname 74 +#define __NR_setrlimit 75 +#define __NR_getrusage 77 +#define __NR_gettimeofday 78 +#define __NR_settimeofday 79 +#define __NR_symlink 83 +#define __NR_readlink 85 +#define __NR_uselib 86 +#define __NR_swapon 87 +#define __NR_reboot 88 +#define __NR_readdir 89 +#define __NR_mmap 90 +#define __NR_munmap 91 +#define __NR_truncate 92 +#define __NR_ftruncate 93 +#define __NR_fchmod 94 +#define __NR_getpriority 96 +#define __NR_setpriority 97 +#define __NR_statfs 99 +#define __NR_fstatfs 100 +#define __NR_socketcall 102 +#define __NR_syslog 103 +#define __NR_setitimer 104 +#define __NR_getitimer 105 +#define __NR_stat 106 +#define __NR_lstat 107 +#define __NR_fstat 108 +#define __NR_lookup_dcookie 110 +#define __NR_vhangup 111 +#define __NR_idle 112 +#define __NR_wait4 114 +#define __NR_swapoff 115 +#define __NR_sysinfo 116 +#define __NR_ipc 117 +#define __NR_fsync 118 +#define __NR_sigreturn 119 +#define __NR_clone 120 +#define __NR_setdomainname 121 +#define __NR_uname 122 +#define __NR_adjtimex 124 +#define __NR_mprotect 125 +#define __NR_sigprocmask 126 +#define __NR_create_module 127 +#define __NR_init_module 128 +#define __NR_delete_module 129 +#define __NR_get_kernel_syms 130 +#define __NR_quotactl 131 +#define __NR_getpgid 132 +#define __NR_fchdir 133 +#define __NR_bdflush 134 +#define __NR_sysfs 135 +#define __NR_personality 136 +#define __NR_afs_syscall 137 +#define __NR_getdents 141 +#define __NR_select 142 +#define __NR_flock 143 +#define __NR_msync 144 +#define __NR_readv 145 +#define __NR_writev 146 +#define __NR_getsid 147 +#define __NR_fdatasync 148 +#define __NR__sysctl 149 +#define __NR_mlock 150 +#define __NR_munlock 151 +#define __NR_mlockall 152 +#define __NR_munlockall 153 +#define __NR_sched_setparam 154 +#define __NR_sched_getparam 155 +#define __NR_sched_setscheduler 156 +#define __NR_sched_getscheduler 157 +#define __NR_sched_yield 158 +#define __NR_sched_get_priority_max 159 +#define __NR_sched_get_priority_min 160 +#define __NR_sched_rr_get_interval 161 +#define __NR_nanosleep 162 +#define __NR_mremap 163 +#define __NR_query_module 167 +#define __NR_poll 168 +#define __NR_nfsservctl 169 +#define __NR_prctl 172 +#define __NR_rt_sigreturn 173 +#define __NR_rt_sigaction 174 +#define __NR_rt_sigprocmask 175 +#define __NR_rt_sigpending 176 +#define __NR_rt_sigtimedwait 177 +#define __NR_rt_sigqueueinfo 178 +#define __NR_rt_sigsuspend 179 +#define __NR_pread64 180 +#define __NR_pwrite64 181 +#define __NR_getcwd 183 +#define __NR_capget 184 +#define __NR_capset 185 +#define __NR_sigaltstack 186 +#define __NR_sendfile 187 +#define __NR_getpmsg 188 +#define __NR_putpmsg 189 +#define __NR_vfork 190 +#define __NR_getrlimit 191 +#define __NR_lchown 198 +#define __NR_getuid 199 +#define __NR_getgid 200 +#define __NR_geteuid 201 +#define __NR_getegid 202 +#define __NR_setreuid 203 +#define __NR_setregid 204 +#define __NR_getgroups 205 +#define __NR_setgroups 206 +#define __NR_fchown 207 +#define __NR_setresuid 208 +#define __NR_getresuid 209 +#define __NR_setresgid 210 +#define __NR_getresgid 211 +#define __NR_chown 212 +#define __NR_setuid 213 +#define __NR_setgid 214 +#define __NR_setfsuid 215 +#define __NR_setfsgid 216 +#define __NR_pivot_root 217 +#define __NR_mincore 218 +#define __NR_madvise 219 +#define __NR_getdents64 220 +#define __NR_readahead 222 +#define __NR_setxattr 224 +#define __NR_lsetxattr 225 +#define __NR_fsetxattr 226 +#define __NR_getxattr 227 +#define __NR_lgetxattr 228 +#define __NR_fgetxattr 229 +#define __NR_listxattr 230 +#define __NR_llistxattr 231 +#define __NR_flistxattr 232 +#define __NR_removexattr 233 +#define __NR_lremovexattr 234 +#define __NR_fremovexattr 235 +#define __NR_gettid 236 +#define __NR_tkill 237 +#define __NR_futex 238 +#define __NR_sched_setaffinity 239 +#define __NR_sched_getaffinity 240 +#define __NR_tgkill 241 +#define __NR_io_setup 243 +#define __NR_io_destroy 244 +#define __NR_io_getevents 245 +#define __NR_io_submit 246 +#define __NR_io_cancel 247 +#define __NR_exit_group 248 +#define __NR_epoll_create 249 +#define __NR_epoll_ctl 250 +#define __NR_epoll_wait 251 +#define __NR_set_tid_address 252 +#define __NR_fadvise64 253 +#define __NR_timer_create 254 +#define __NR_timer_settime 255 +#define __NR_timer_gettime 256 +#define __NR_timer_getoverrun 257 +#define __NR_timer_delete 258 +#define __NR_clock_settime 259 +#define __NR_clock_gettime 260 +#define __NR_clock_getres 261 +#define __NR_clock_nanosleep 262 +#define __NR_statfs64 265 +#define __NR_fstatfs64 266 +#define __NR_remap_file_pages 267 +#define __NR_mbind 268 +#define __NR_get_mempolicy 269 +#define __NR_set_mempolicy 270 +#define __NR_mq_open 271 +#define __NR_mq_unlink 272 +#define __NR_mq_timedsend 273 +#define __NR_mq_timedreceive 274 +#define __NR_mq_notify 275 +#define __NR_mq_getsetattr 276 +#define __NR_kexec_load 277 +#define __NR_add_key 278 +#define __NR_request_key 279 +#define __NR_keyctl 280 +#define __NR_waitid 281 +#define __NR_ioprio_set 282 +#define __NR_ioprio_get 283 +#define __NR_inotify_init 284 +#define __NR_inotify_add_watch 285 +#define __NR_inotify_rm_watch 286 +#define __NR_migrate_pages 287 +#define __NR_openat 288 +#define __NR_mkdirat 289 +#define __NR_mknodat 290 +#define __NR_fchownat 291 +#define __NR_futimesat 292 +#define __NR_newfstatat 293 +#define __NR_unlinkat 294 +#define __NR_renameat 295 +#define __NR_linkat 296 +#define __NR_symlinkat 297 +#define __NR_readlinkat 298 +#define __NR_fchmodat 299 +#define __NR_faccessat 300 +#define __NR_pselect6 301 +#define __NR_ppoll 302 +#define __NR_unshare 303 +#define __NR_set_robust_list 304 +#define __NR_get_robust_list 305 +#define __NR_splice 306 +#define __NR_sync_file_range 307 +#define __NR_tee 308 +#define __NR_vmsplice 309 +#define __NR_move_pages 310 +#define __NR_getcpu 311 +#define __NR_epoll_pwait 312 +#define __NR_utimes 313 +#define __NR_fallocate 314 +#define __NR_utimensat 315 +#define __NR_signalfd 316 +#define __NR_timerfd 317 +#define __NR_eventfd 318 +#define __NR_timerfd_create 319 +#define __NR_timerfd_settime 320 +#define __NR_timerfd_gettime 321 +#define __NR_signalfd4 322 +#define __NR_eventfd2 323 +#define __NR_inotify_init1 324 +#define __NR_pipe2 325 +#define __NR_dup3 326 +#define __NR_epoll_create1 327 +#define __NR_preadv 328 +#define __NR_pwritev 329 +#define __NR_rt_tgsigqueueinfo 330 +#define __NR_perf_event_open 331 +#define __NR_fanotify_init 332 +#define __NR_fanotify_mark 333 +#define __NR_prlimit64 334 +#define __NR_name_to_handle_at 335 +#define __NR_open_by_handle_at 336 +#define __NR_clock_adjtime 337 +#define __NR_syncfs 338 +#define __NR_setns 339 +#define __NR_process_vm_readv 340 +#define __NR_process_vm_writev 341 +#define __NR_s390_runtime_instr 342 +#define __NR_kcmp 343 +#define __NR_finit_module 344 +#define __NR_sched_setattr 345 +#define __NR_sched_getattr 346 +#define __NR_renameat2 347 +#define __NR_seccomp 348 +#define __NR_getrandom 349 +#define __NR_memfd_create 350 +#define __NR_bpf 351 +#define __NR_s390_pci_mmio_write 352 +#define __NR_s390_pci_mmio_read 353 +#define __NR_execveat 354 +#define __NR_userfaultfd 355 +#define __NR_membarrier 356 +#define __NR_recvmmsg 357 +#define __NR_sendmmsg 358 +#define __NR_socket 359 +#define __NR_socketpair 360 +#define __NR_bind 361 +#define __NR_connect 362 +#define __NR_listen 363 +#define __NR_accept4 364 +#define __NR_getsockopt 365 +#define __NR_setsockopt 366 +#define __NR_getsockname 367 +#define __NR_getpeername 368 +#define __NR_sendto 369 +#define __NR_sendmsg 370 +#define __NR_recvfrom 371 +#define __NR_recvmsg 372 +#define __NR_shutdown 373 +#define __NR_mlock2 374 +#define __NR_copy_file_range 375 +#define __NR_preadv2 376 +#define __NR_pwritev2 377 +#define __NR_s390_guarded_storage 378 +#define __NR_statx 379 +#define __NR_s390_sthyi 380 + +#endif /* _ASM_S390_UNISTD_64_H */ diff --git a/linux-headers/asm-x86/kvm_para.h b/linux-headers/asm-x86/kvm_para.h index 4c300f6aaa..4c58184395 100644 --- a/linux-headers/asm-x86/kvm_para.h +++ b/linux-headers/asm-x86/kvm_para.h @@ -25,6 +25,8 @@ #define KVM_FEATURE_STEAL_TIME 5 #define KVM_FEATURE_PV_EOI 6 #define KVM_FEATURE_PV_UNHALT 7 +#define KVM_FEATURE_PV_TLB_FLUSH 9 +#define KVM_FEATURE_ASYNC_PF_VMEXIT 10 /* The last 8 bits are used to indicate how to interpret the flags field * in pvclock structure. If no bits are set, all flags are ignored. @@ -51,6 +53,9 @@ struct kvm_steal_time { __u32 pad[11]; }; +#define KVM_VCPU_PREEMPTED (1 << 0) +#define KVM_VCPU_FLUSH_TLB (1 << 1) + #define KVM_CLOCK_PAIRING_WALLCLOCK 0 struct kvm_clock_pairing { __s64 sec; diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index d92c9b2f0e..a167be89d1 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -761,6 +761,7 @@ struct kvm_ppc_resize_hpt { #define KVM_TRACE_PAUSE __KVM_DEPRECATED_MAIN_0x07 #define KVM_TRACE_DISABLE __KVM_DEPRECATED_MAIN_0x08 #define KVM_GET_EMULATED_CPUID _IOWR(KVMIO, 0x09, struct kvm_cpuid2) +#define KVM_GET_MSR_FEATURE_INDEX_LIST _IOWR(KVMIO, 0x0a, struct kvm_msr_list) /* * Extension capability list. @@ -934,6 +935,7 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_S390_AIS_MIGRATION 150 #define KVM_CAP_PPC_GET_CPU_CHAR 151 #define KVM_CAP_S390_BPB 152 +#define KVM_CAP_GET_MSR_FEATURES 153 #ifdef KVM_CAP_IRQ_ROUTING @@ -1362,6 +1364,96 @@ struct kvm_s390_ucas_mapping { /* Available with KVM_CAP_S390_CMMA_MIGRATION */ #define KVM_S390_GET_CMMA_BITS _IOWR(KVMIO, 0xb8, struct kvm_s390_cmma_log) #define KVM_S390_SET_CMMA_BITS _IOW(KVMIO, 0xb9, struct kvm_s390_cmma_log) +/* Memory Encryption Commands */ +#define KVM_MEMORY_ENCRYPT_OP _IOWR(KVMIO, 0xba, unsigned long) + +struct kvm_enc_region { + __u64 addr; + __u64 size; +}; + +#define KVM_MEMORY_ENCRYPT_REG_REGION _IOR(KVMIO, 0xbb, struct kvm_enc_region) +#define KVM_MEMORY_ENCRYPT_UNREG_REGION _IOR(KVMIO, 0xbc, struct kvm_enc_region) + +/* Secure Encrypted Virtualization command */ +enum sev_cmd_id { + /* Guest initialization commands */ + KVM_SEV_INIT = 0, + KVM_SEV_ES_INIT, + /* Guest launch commands */ + KVM_SEV_LAUNCH_START, + KVM_SEV_LAUNCH_UPDATE_DATA, + KVM_SEV_LAUNCH_UPDATE_VMSA, + KVM_SEV_LAUNCH_SECRET, + KVM_SEV_LAUNCH_MEASURE, + KVM_SEV_LAUNCH_FINISH, + /* Guest migration commands (outgoing) */ + KVM_SEV_SEND_START, + KVM_SEV_SEND_UPDATE_DATA, + KVM_SEV_SEND_UPDATE_VMSA, + KVM_SEV_SEND_FINISH, + /* Guest migration commands (incoming) */ + KVM_SEV_RECEIVE_START, + KVM_SEV_RECEIVE_UPDATE_DATA, + KVM_SEV_RECEIVE_UPDATE_VMSA, + KVM_SEV_RECEIVE_FINISH, + /* Guest status and debug commands */ + KVM_SEV_GUEST_STATUS, + KVM_SEV_DBG_DECRYPT, + KVM_SEV_DBG_ENCRYPT, + /* Guest certificates commands */ + KVM_SEV_CERT_EXPORT, + + KVM_SEV_NR_MAX, +}; + +struct kvm_sev_cmd { + __u32 id; + __u64 data; + __u32 error; + __u32 sev_fd; +}; + +struct kvm_sev_launch_start { + __u32 handle; + __u32 policy; + __u64 dh_uaddr; + __u32 dh_len; + __u64 session_uaddr; + __u32 session_len; +}; + +struct kvm_sev_launch_update_data { + __u64 uaddr; + __u32 len; +}; + + +struct kvm_sev_launch_secret { + __u64 hdr_uaddr; + __u32 hdr_len; + __u64 guest_uaddr; + __u32 guest_len; + __u64 trans_uaddr; + __u32 trans_len; +}; + +struct kvm_sev_launch_measure { + __u64 uaddr; + __u32 len; +}; + +struct kvm_sev_guest_status { + __u32 handle; + __u32 policy; + __u32 state; +}; + +struct kvm_sev_dbg { + __u64 src_uaddr; + __u64 dst_uaddr; + __u32 len; +}; #define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0) #define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1) diff --git a/linux-headers/linux/psci.h b/linux-headers/linux/psci.h index ccd17731c6..3905492d18 100644 --- a/linux-headers/linux/psci.h +++ b/linux-headers/linux/psci.h @@ -88,6 +88,9 @@ (((ver) & PSCI_VERSION_MAJOR_MASK) >> PSCI_VERSION_MAJOR_SHIFT) #define PSCI_VERSION_MINOR(ver) \ ((ver) & PSCI_VERSION_MINOR_MASK) +#define PSCI_VERSION(maj, min) \ + ((((maj) << PSCI_VERSION_MAJOR_SHIFT) & PSCI_VERSION_MAJOR_MASK) | \ + ((min) & PSCI_VERSION_MINOR_MASK)) /* PSCI features decoding (>=1.0) */ #define PSCI_1_0_FEATURES_CPU_SUSPEND_PF_SHIFT 1 diff --git a/linux-headers/linux/psp-sev.h b/linux-headers/linux/psp-sev.h new file mode 100644 index 0000000000..33e247471a --- /dev/null +++ b/linux-headers/linux/psp-sev.h @@ -0,0 +1,142 @@ +/* + * Userspace interface for AMD Secure Encrypted Virtualization (SEV) + * platform management commands. + * + * Copyright (C) 2016-2017 Advanced Micro Devices, Inc. + * + * Author: Brijesh Singh <brijesh.singh@amd.com> + * + * SEV spec 0.14 is available at: + * http://support.amd.com/TechDocs/55766_SEV-KM%20API_Specification.pdf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __PSP_SEV_USER_H__ +#define __PSP_SEV_USER_H__ + +#include <linux/types.h> + +/** + * SEV platform commands + */ +enum { + SEV_FACTORY_RESET = 0, + SEV_PLATFORM_STATUS, + SEV_PEK_GEN, + SEV_PEK_CSR, + SEV_PDH_GEN, + SEV_PDH_CERT_EXPORT, + SEV_PEK_CERT_IMPORT, + + SEV_MAX, +}; + +/** + * SEV Firmware status code + */ +typedef enum { + SEV_RET_SUCCESS = 0, + SEV_RET_INVALID_PLATFORM_STATE, + SEV_RET_INVALID_GUEST_STATE, + SEV_RET_INAVLID_CONFIG, + SEV_RET_INVALID_LEN, + SEV_RET_ALREADY_OWNED, + SEV_RET_INVALID_CERTIFICATE, + SEV_RET_POLICY_FAILURE, + SEV_RET_INACTIVE, + SEV_RET_INVALID_ADDRESS, + SEV_RET_BAD_SIGNATURE, + SEV_RET_BAD_MEASUREMENT, + SEV_RET_ASID_OWNED, + SEV_RET_INVALID_ASID, + SEV_RET_WBINVD_REQUIRED, + SEV_RET_DFFLUSH_REQUIRED, + SEV_RET_INVALID_GUEST, + SEV_RET_INVALID_COMMAND, + SEV_RET_ACTIVE, + SEV_RET_HWSEV_RET_PLATFORM, + SEV_RET_HWSEV_RET_UNSAFE, + SEV_RET_UNSUPPORTED, + SEV_RET_MAX, +} sev_ret_code; + +/** + * struct sev_user_data_status - PLATFORM_STATUS command parameters + * + * @major: major API version + * @minor: minor API version + * @state: platform state + * @flags: platform config flags + * @build: firmware build id for API version + * @guest_count: number of active guests + */ +struct sev_user_data_status { + __u8 api_major; /* Out */ + __u8 api_minor; /* Out */ + __u8 state; /* Out */ + __u32 flags; /* Out */ + __u8 build; /* Out */ + __u32 guest_count; /* Out */ +} __attribute__((packed)); + +/** + * struct sev_user_data_pek_csr - PEK_CSR command parameters + * + * @address: PEK certificate chain + * @length: length of certificate + */ +struct sev_user_data_pek_csr { + __u64 address; /* In */ + __u32 length; /* In/Out */ +} __attribute__((packed)); + +/** + * struct sev_user_data_cert_import - PEK_CERT_IMPORT command parameters + * + * @pek_address: PEK certificate chain + * @pek_len: length of PEK certificate + * @oca_address: OCA certificate chain + * @oca_len: length of OCA certificate + */ +struct sev_user_data_pek_cert_import { + __u64 pek_cert_address; /* In */ + __u32 pek_cert_len; /* In */ + __u64 oca_cert_address; /* In */ + __u32 oca_cert_len; /* In */ +} __attribute__((packed)); + +/** + * struct sev_user_data_pdh_cert_export - PDH_CERT_EXPORT command parameters + * + * @pdh_address: PDH certificate address + * @pdh_len: length of PDH certificate + * @cert_chain_address: PDH certificate chain + * @cert_chain_len: length of PDH certificate chain + */ +struct sev_user_data_pdh_cert_export { + __u64 pdh_cert_address; /* In */ + __u32 pdh_cert_len; /* In/Out */ + __u64 cert_chain_address; /* In */ + __u32 cert_chain_len; /* In/Out */ +} __attribute__((packed)); + +/** + * struct sev_issue_cmd - SEV ioctl parameters + * + * @cmd: SEV commands to execute + * @opaque: pointer to the command structure + * @error: SEV FW return code on failure + */ +struct sev_issue_cmd { + __u32 cmd; /* In */ + __u64 data; /* In */ + __u32 error; /* Out */ +} __attribute__((packed)); + +#define SEV_IOC_TYPE 'S' +#define SEV_ISSUE_CMD _IOWR(SEV_IOC_TYPE, 0x0, struct sev_issue_cmd) + +#endif /* __PSP_USER_SEV_H */ diff --git a/linux-headers/linux/vfio.h b/linux-headers/linux/vfio.h index 4312e961ff..3a0a305c8c 100644 --- a/linux-headers/linux/vfio.h +++ b/linux-headers/linux/vfio.h @@ -301,6 +301,16 @@ struct vfio_region_info_cap_type { #define VFIO_REGION_SUBTYPE_INTEL_IGD_HOST_CFG (2) #define VFIO_REGION_SUBTYPE_INTEL_IGD_LPC_CFG (3) +/* + * The MSIX mappable capability informs that MSIX data of a BAR can be mmapped + * which allows direct access to non-MSIX registers which happened to be within + * the same system page. + * + * Even though the userspace gets direct access to the MSIX data, the existing + * VFIO_DEVICE_SET_IRQS interface must still be used for MSIX configuration. + */ +#define VFIO_REGION_INFO_CAP_MSIX_MAPPABLE 3 + /** * VFIO_DEVICE_GET_IRQ_INFO - _IOWR(VFIO_TYPE, VFIO_BASE + 9, * struct vfio_irq_info) @@ -503,6 +513,68 @@ struct vfio_pci_hot_reset { #define VFIO_DEVICE_PCI_HOT_RESET _IO(VFIO_TYPE, VFIO_BASE + 13) +/** + * VFIO_DEVICE_QUERY_GFX_PLANE - _IOW(VFIO_TYPE, VFIO_BASE + 14, + * struct vfio_device_query_gfx_plane) + * + * Set the drm_plane_type and flags, then retrieve the gfx plane info. + * + * flags supported: + * - VFIO_GFX_PLANE_TYPE_PROBE and VFIO_GFX_PLANE_TYPE_DMABUF are set + * to ask if the mdev supports dma-buf. 0 on support, -EINVAL on no + * support for dma-buf. + * - VFIO_GFX_PLANE_TYPE_PROBE and VFIO_GFX_PLANE_TYPE_REGION are set + * to ask if the mdev supports region. 0 on support, -EINVAL on no + * support for region. + * - VFIO_GFX_PLANE_TYPE_DMABUF or VFIO_GFX_PLANE_TYPE_REGION is set + * with each call to query the plane info. + * - Others are invalid and return -EINVAL. + * + * Note: + * 1. Plane could be disabled by guest. In that case, success will be + * returned with zero-initialized drm_format, size, width and height + * fields. + * 2. x_hot/y_hot is set to 0xFFFFFFFF if no hotspot information available + * + * Return: 0 on success, -errno on other failure. + */ +struct vfio_device_gfx_plane_info { + __u32 argsz; + __u32 flags; +#define VFIO_GFX_PLANE_TYPE_PROBE (1 << 0) +#define VFIO_GFX_PLANE_TYPE_DMABUF (1 << 1) +#define VFIO_GFX_PLANE_TYPE_REGION (1 << 2) + /* in */ + __u32 drm_plane_type; /* type of plane: DRM_PLANE_TYPE_* */ + /* out */ + __u32 drm_format; /* drm format of plane */ + __u64 drm_format_mod; /* tiled mode */ + __u32 width; /* width of plane */ + __u32 height; /* height of plane */ + __u32 stride; /* stride of plane */ + __u32 size; /* size of plane in bytes, align on page*/ + __u32 x_pos; /* horizontal position of cursor plane */ + __u32 y_pos; /* vertical position of cursor plane*/ + __u32 x_hot; /* horizontal position of cursor hotspot */ + __u32 y_hot; /* vertical position of cursor hotspot */ + union { + __u32 region_index; /* region index */ + __u32 dmabuf_id; /* dma-buf id */ + }; +}; + +#define VFIO_DEVICE_QUERY_GFX_PLANE _IO(VFIO_TYPE, VFIO_BASE + 14) + +/** + * VFIO_DEVICE_GET_GFX_DMABUF - _IOW(VFIO_TYPE, VFIO_BASE + 15, __u32) + * + * Return a new dma-buf file descriptor for an exposed guest framebuffer + * described by the provided dmabuf_id. The dmabuf_id is returned from VFIO_ + * DEVICE_QUERY_GFX_PLANE as a token of the exposed guest framebuffer. + */ + +#define VFIO_DEVICE_GET_GFX_DMABUF _IO(VFIO_TYPE, VFIO_BASE + 15) + /* -------- API for Type1 VFIO IOMMU -------- */ /** diff --git a/linux-user/aarch64/target_syscall.h b/linux-user/aarch64/target_syscall.h index 604ab99b14..205265e619 100644 --- a/linux-user/aarch64/target_syscall.h +++ b/linux-user/aarch64/target_syscall.h @@ -19,4 +19,7 @@ struct target_pt_regs { #define TARGET_MLOCKALL_MCL_CURRENT 1 #define TARGET_MLOCKALL_MCL_FUTURE 2 +#define TARGET_PR_SVE_SET_VL 50 +#define TARGET_PR_SVE_GET_VL 51 + #endif /* AARCH64_TARGET_SYSCALL_H */ diff --git a/linux-user/signal.c b/linux-user/signal.c index 513bc7e1ed..2461edf463 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -1443,35 +1443,65 @@ struct target_fpsimd_context { uint64_t vregs[32 * 2]; /* really uint128_t vregs[32] */ }; -/* - * Auxiliary context saved in the sigcontext.__reserved array. Not exported to - * user space as it will change with the addition of new context. User space - * should check the magic/size information. - */ -struct target_aux_context { - struct target_fpsimd_context fpsimd; - /* additional context to be added before "end" */ - struct target_aarch64_ctx end; +#define TARGET_EXTRA_MAGIC 0x45585401 + +struct target_extra_context { + struct target_aarch64_ctx head; + uint64_t datap; /* 16-byte aligned pointer to extra space cast to __u64 */ + uint32_t size; /* size in bytes of the extra space */ + uint32_t reserved[3]; +}; + +#define TARGET_SVE_MAGIC 0x53564501 + +struct target_sve_context { + struct target_aarch64_ctx head; + uint16_t vl; + uint16_t reserved[3]; + /* The actual SVE data immediately follows. It is layed out + * according to TARGET_SVE_SIG_{Z,P}REG_OFFSET, based off of + * the original struct pointer. + */ }; +#define TARGET_SVE_VQ_BYTES 16 + +#define TARGET_SVE_SIG_ZREG_SIZE(VQ) ((VQ) * TARGET_SVE_VQ_BYTES) +#define TARGET_SVE_SIG_PREG_SIZE(VQ) ((VQ) * (TARGET_SVE_VQ_BYTES / 8)) + +#define TARGET_SVE_SIG_REGS_OFFSET \ + QEMU_ALIGN_UP(sizeof(struct target_sve_context), TARGET_SVE_VQ_BYTES) +#define TARGET_SVE_SIG_ZREG_OFFSET(VQ, N) \ + (TARGET_SVE_SIG_REGS_OFFSET + TARGET_SVE_SIG_ZREG_SIZE(VQ) * (N)) +#define TARGET_SVE_SIG_PREG_OFFSET(VQ, N) \ + (TARGET_SVE_SIG_ZREG_OFFSET(VQ, 32) + TARGET_SVE_SIG_PREG_SIZE(VQ) * (N)) +#define TARGET_SVE_SIG_FFR_OFFSET(VQ) \ + (TARGET_SVE_SIG_PREG_OFFSET(VQ, 16)) +#define TARGET_SVE_SIG_CONTEXT_SIZE(VQ) \ + (TARGET_SVE_SIG_PREG_OFFSET(VQ, 17)) + struct target_rt_sigframe { struct target_siginfo info; struct target_ucontext uc; +}; + +struct target_rt_frame_record { uint64_t fp; uint64_t lr; uint32_t tramp[2]; }; -static int target_setup_sigframe(struct target_rt_sigframe *sf, - CPUARMState *env, target_sigset_t *set) +static void target_setup_general_frame(struct target_rt_sigframe *sf, + CPUARMState *env, target_sigset_t *set) { int i; - struct target_aux_context *aux = - (struct target_aux_context *)sf->uc.tuc_mcontext.__reserved; - /* set up the stack frame for unwinding */ - __put_user(env->xregs[29], &sf->fp); - __put_user(env->xregs[30], &sf->lr); + __put_user(0, &sf->uc.tuc_flags); + __put_user(0, &sf->uc.tuc_link); + + __put_user(target_sigaltstack_used.ss_sp, &sf->uc.tuc_stack.ss_sp); + __put_user(sas_ss_flags(env->xregs[31]), &sf->uc.tuc_stack.ss_flags); + __put_user(target_sigaltstack_used.ss_size, &sf->uc.tuc_stack.ss_size); for (i = 0; i < 31; i++) { __put_user(env->xregs[i], &sf->uc.tuc_mcontext.regs[i]); @@ -1485,39 +1515,79 @@ static int target_setup_sigframe(struct target_rt_sigframe *sf, for (i = 0; i < TARGET_NSIG_WORDS; i++) { __put_user(set->sig[i], &sf->uc.tuc_sigmask.sig[i]); } +} + +static void target_setup_fpsimd_record(struct target_fpsimd_context *fpsimd, + CPUARMState *env) +{ + int i; + + __put_user(TARGET_FPSIMD_MAGIC, &fpsimd->head.magic); + __put_user(sizeof(struct target_fpsimd_context), &fpsimd->head.size); + __put_user(vfp_get_fpsr(env), &fpsimd->fpsr); + __put_user(vfp_get_fpcr(env), &fpsimd->fpcr); for (i = 0; i < 32; i++) { uint64_t *q = aa64_vfp_qreg(env, i); #ifdef TARGET_WORDS_BIGENDIAN - __put_user(q[0], &aux->fpsimd.vregs[i * 2 + 1]); - __put_user(q[1], &aux->fpsimd.vregs[i * 2]); + __put_user(q[0], &fpsimd->vregs[i * 2 + 1]); + __put_user(q[1], &fpsimd->vregs[i * 2]); #else - __put_user(q[0], &aux->fpsimd.vregs[i * 2]); - __put_user(q[1], &aux->fpsimd.vregs[i * 2 + 1]); + __put_user(q[0], &fpsimd->vregs[i * 2]); + __put_user(q[1], &fpsimd->vregs[i * 2 + 1]); #endif } - __put_user(vfp_get_fpsr(env), &aux->fpsimd.fpsr); - __put_user(vfp_get_fpcr(env), &aux->fpsimd.fpcr); - __put_user(TARGET_FPSIMD_MAGIC, &aux->fpsimd.head.magic); - __put_user(sizeof(struct target_fpsimd_context), - &aux->fpsimd.head.size); +} - /* set the "end" magic */ - __put_user(0, &aux->end.magic); - __put_user(0, &aux->end.size); +static void target_setup_extra_record(struct target_extra_context *extra, + uint64_t datap, uint32_t extra_size) +{ + __put_user(TARGET_EXTRA_MAGIC, &extra->head.magic); + __put_user(sizeof(struct target_extra_context), &extra->head.size); + __put_user(datap, &extra->datap); + __put_user(extra_size, &extra->size); +} - return 0; +static void target_setup_end_record(struct target_aarch64_ctx *end) +{ + __put_user(0, &end->magic); + __put_user(0, &end->size); } -static int target_restore_sigframe(CPUARMState *env, - struct target_rt_sigframe *sf) +static void target_setup_sve_record(struct target_sve_context *sve, + CPUARMState *env, int vq, int size) +{ + int i, j; + + __put_user(TARGET_SVE_MAGIC, &sve->head.magic); + __put_user(size, &sve->head.size); + __put_user(vq * TARGET_SVE_VQ_BYTES, &sve->vl); + + /* Note that SVE regs are stored as a byte stream, with each byte element + * at a subsequent address. This corresponds to a little-endian store + * of our 64-bit hunks. + */ + for (i = 0; i < 32; ++i) { + uint64_t *z = (void *)sve + TARGET_SVE_SIG_ZREG_OFFSET(vq, i); + for (j = 0; j < vq * 2; ++j) { + __put_user_e(env->vfp.zregs[i].d[j], z + j, le); + } + } + for (i = 0; i <= 16; ++i) { + uint16_t *p = (void *)sve + TARGET_SVE_SIG_PREG_OFFSET(vq, i); + for (j = 0; j < vq; ++j) { + uint64_t r = env->vfp.pregs[i].p[j >> 2]; + __put_user_e(r >> ((j & 3) * 16), p + j, le); + } + } +} + +static void target_restore_general_frame(CPUARMState *env, + struct target_rt_sigframe *sf) { sigset_t set; - int i; - struct target_aux_context *aux = - (struct target_aux_context *)sf->uc.tuc_mcontext.__reserved; - uint32_t magic, size, fpsr, fpcr; uint64_t pstate; + int i; target_to_host_sigset(&set, &sf->uc.tuc_sigmask); set_sigmask(&set); @@ -1530,34 +1600,154 @@ static int target_restore_sigframe(CPUARMState *env, __get_user(env->pc, &sf->uc.tuc_mcontext.pc); __get_user(pstate, &sf->uc.tuc_mcontext.pstate); pstate_write(env, pstate); +} - __get_user(magic, &aux->fpsimd.head.magic); - __get_user(size, &aux->fpsimd.head.size); +static void target_restore_fpsimd_record(CPUARMState *env, + struct target_fpsimd_context *fpsimd) +{ + uint32_t fpsr, fpcr; + int i; - if (magic != TARGET_FPSIMD_MAGIC - || size != sizeof(struct target_fpsimd_context)) { - return 1; - } + __get_user(fpsr, &fpsimd->fpsr); + vfp_set_fpsr(env, fpsr); + __get_user(fpcr, &fpsimd->fpcr); + vfp_set_fpcr(env, fpcr); for (i = 0; i < 32; i++) { uint64_t *q = aa64_vfp_qreg(env, i); #ifdef TARGET_WORDS_BIGENDIAN - __get_user(q[0], &aux->fpsimd.vregs[i * 2 + 1]); - __get_user(q[1], &aux->fpsimd.vregs[i * 2]); + __get_user(q[0], &fpsimd->vregs[i * 2 + 1]); + __get_user(q[1], &fpsimd->vregs[i * 2]); #else - __get_user(q[0], &aux->fpsimd.vregs[i * 2]); - __get_user(q[1], &aux->fpsimd.vregs[i * 2 + 1]); + __get_user(q[0], &fpsimd->vregs[i * 2]); + __get_user(q[1], &fpsimd->vregs[i * 2 + 1]); #endif } - __get_user(fpsr, &aux->fpsimd.fpsr); - vfp_set_fpsr(env, fpsr); - __get_user(fpcr, &aux->fpsimd.fpcr); - vfp_set_fpcr(env, fpcr); +} - return 0; +static void target_restore_sve_record(CPUARMState *env, + struct target_sve_context *sve, int vq) +{ + int i, j; + + /* Note that SVE regs are stored as a byte stream, with each byte element + * at a subsequent address. This corresponds to a little-endian load + * of our 64-bit hunks. + */ + for (i = 0; i < 32; ++i) { + uint64_t *z = (void *)sve + TARGET_SVE_SIG_ZREG_OFFSET(vq, i); + for (j = 0; j < vq * 2; ++j) { + __get_user_e(env->vfp.zregs[i].d[j], z + j, le); + } + } + for (i = 0; i <= 16; ++i) { + uint16_t *p = (void *)sve + TARGET_SVE_SIG_PREG_OFFSET(vq, i); + for (j = 0; j < vq; ++j) { + uint16_t r; + __get_user_e(r, p + j, le); + if (j & 3) { + env->vfp.pregs[i].p[j >> 2] |= (uint64_t)r << ((j & 3) * 16); + } else { + env->vfp.pregs[i].p[j >> 2] = r; + } + } + } +} + +static int target_restore_sigframe(CPUARMState *env, + struct target_rt_sigframe *sf) +{ + struct target_aarch64_ctx *ctx, *extra = NULL; + struct target_fpsimd_context *fpsimd = NULL; + struct target_sve_context *sve = NULL; + uint64_t extra_datap = 0; + bool used_extra = false; + bool err = false; + int vq = 0, sve_size = 0; + + target_restore_general_frame(env, sf); + + ctx = (struct target_aarch64_ctx *)sf->uc.tuc_mcontext.__reserved; + while (ctx) { + uint32_t magic, size, extra_size; + + __get_user(magic, &ctx->magic); + __get_user(size, &ctx->size); + switch (magic) { + case 0: + if (size != 0) { + err = true; + goto exit; + } + if (used_extra) { + ctx = NULL; + } else { + ctx = extra; + used_extra = true; + } + continue; + + case TARGET_FPSIMD_MAGIC: + if (fpsimd || size != sizeof(struct target_fpsimd_context)) { + err = true; + goto exit; + } + fpsimd = (struct target_fpsimd_context *)ctx; + break; + + case TARGET_SVE_MAGIC: + if (arm_feature(env, ARM_FEATURE_SVE)) { + vq = (env->vfp.zcr_el[1] & 0xf) + 1; + sve_size = QEMU_ALIGN_UP(TARGET_SVE_SIG_CONTEXT_SIZE(vq), 16); + if (!sve && size == sve_size) { + sve = (struct target_sve_context *)ctx; + break; + } + } + err = true; + goto exit; + + case TARGET_EXTRA_MAGIC: + if (extra || size != sizeof(struct target_extra_context)) { + err = true; + goto exit; + } + __get_user(extra_datap, + &((struct target_extra_context *)ctx)->datap); + __get_user(extra_size, + &((struct target_extra_context *)ctx)->size); + extra = lock_user(VERIFY_READ, extra_datap, extra_size, 0); + break; + + default: + /* Unknown record -- we certainly didn't generate it. + * Did we in fact get out of sync? + */ + err = true; + goto exit; + } + ctx = (void *)ctx + size; + } + + /* Require FPSIMD always. */ + if (fpsimd) { + target_restore_fpsimd_record(env, fpsimd); + } else { + err = true; + } + + /* SVE data, if present, overwrites FPSIMD data. */ + if (sve) { + target_restore_sve_record(env, sve, vq); + } + + exit: + unlock_user(extra, extra_datap, 0); + return err; } -static abi_ulong get_sigframe(struct target_sigaction *ka, CPUARMState *env) +static abi_ulong get_sigframe(struct target_sigaction *ka, + CPUARMState *env, int size) { abi_ulong sp; @@ -1570,34 +1760,120 @@ static abi_ulong get_sigframe(struct target_sigaction *ka, CPUARMState *env) sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; } - sp = (sp - sizeof(struct target_rt_sigframe)) & ~15; + sp = (sp - size) & ~15; return sp; } +typedef struct { + int total_size; + int extra_base; + int extra_size; + int std_end_ofs; + int extra_ofs; + int extra_end_ofs; +} target_sigframe_layout; + +static int alloc_sigframe_space(int this_size, target_sigframe_layout *l) +{ + /* Make sure there will always be space for the end marker. */ + const int std_size = sizeof(struct target_rt_sigframe) + - sizeof(struct target_aarch64_ctx); + int this_loc = l->total_size; + + if (l->extra_base) { + /* Once we have begun an extra space, all allocations go there. */ + l->extra_size += this_size; + } else if (this_size + this_loc > std_size) { + /* This allocation does not fit in the standard space. */ + /* Allocate the extra record. */ + l->extra_ofs = this_loc; + l->total_size += sizeof(struct target_extra_context); + + /* Allocate the standard end record. */ + l->std_end_ofs = l->total_size; + l->total_size += sizeof(struct target_aarch64_ctx); + + /* Allocate the requested record. */ + l->extra_base = this_loc = l->total_size; + l->extra_size = this_size; + } + l->total_size += this_size; + + return this_loc; +} + static void target_setup_frame(int usig, struct target_sigaction *ka, target_siginfo_t *info, target_sigset_t *set, CPUARMState *env) { + target_sigframe_layout layout = { + /* Begin with the size pointing to the reserved space. */ + .total_size = offsetof(struct target_rt_sigframe, + uc.tuc_mcontext.__reserved), + }; + int fpsimd_ofs, fr_ofs, sve_ofs = 0, vq = 0, sve_size = 0; struct target_rt_sigframe *frame; + struct target_rt_frame_record *fr; abi_ulong frame_addr, return_addr; - frame_addr = get_sigframe(ka, env); + /* FPSIMD record is always in the standard space. */ + fpsimd_ofs = alloc_sigframe_space(sizeof(struct target_fpsimd_context), + &layout); + + /* SVE state needs saving only if it exists. */ + if (arm_feature(env, ARM_FEATURE_SVE)) { + vq = (env->vfp.zcr_el[1] & 0xf) + 1; + sve_size = QEMU_ALIGN_UP(TARGET_SVE_SIG_CONTEXT_SIZE(vq), 16); + sve_ofs = alloc_sigframe_space(sve_size, &layout); + } + + if (layout.extra_ofs) { + /* Reserve space for the extra end marker. The standard end marker + * will have been allocated when we allocated the extra record. + */ + layout.extra_end_ofs + = alloc_sigframe_space(sizeof(struct target_aarch64_ctx), &layout); + } else { + /* Reserve space for the standard end marker. + * Do not use alloc_sigframe_space because we cheat + * std_size therein to reserve space for this. + */ + layout.std_end_ofs = layout.total_size; + layout.total_size += sizeof(struct target_aarch64_ctx); + } + + /* Reserve space for the return code. On a real system this would + * be within the VDSO. So, despite the name this is not a "real" + * record within the frame. + */ + fr_ofs = layout.total_size; + layout.total_size += sizeof(struct target_rt_frame_record); + + frame_addr = get_sigframe(ka, env, layout.total_size); trace_user_setup_frame(env, frame_addr); if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { goto give_sigsegv; } - __put_user(0, &frame->uc.tuc_flags); - __put_user(0, &frame->uc.tuc_link); + target_setup_general_frame(frame, env, set); + target_setup_fpsimd_record((void *)frame + fpsimd_ofs, env); + target_setup_end_record((void *)frame + layout.std_end_ofs); + if (layout.extra_ofs) { + target_setup_extra_record((void *)frame + layout.extra_ofs, + frame_addr + layout.extra_base, + layout.extra_size); + target_setup_end_record((void *)frame + layout.extra_end_ofs); + } + if (sve_ofs) { + target_setup_sve_record((void *)frame + sve_ofs, env, vq, sve_size); + } + + /* Set up the stack frame for unwinding. */ + fr = (void *)frame + fr_ofs; + __put_user(env->xregs[29], &fr->fp); + __put_user(env->xregs[30], &fr->lr); - __put_user(target_sigaltstack_used.ss_sp, - &frame->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(env->xregs[31]), - &frame->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, - &frame->uc.tuc_stack.ss_size); - target_setup_sigframe(frame, env, set); if (ka->sa_flags & TARGET_SA_RESTORER) { return_addr = ka->sa_restorer; } else { @@ -1606,13 +1882,14 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, * Since these are instructions they need to be put as little-endian * regardless of target default or current CPU endianness. */ - __put_user_e(0xd2801168, &frame->tramp[0], le); - __put_user_e(0xd4000001, &frame->tramp[1], le); - return_addr = frame_addr + offsetof(struct target_rt_sigframe, tramp); + __put_user_e(0xd2801168, &fr->tramp[0], le); + __put_user_e(0xd4000001, &fr->tramp[1], le); + return_addr = frame_addr + fr_ofs + + offsetof(struct target_rt_frame_record, tramp); } env->xregs[0] = usig; env->xregs[31] = frame_addr; - env->xregs[29] = env->xregs[31] + offsetof(struct target_rt_sigframe, fp); + env->xregs[29] = frame_addr + fr_ofs; env->pc = ka->_sa_handler; env->xregs[30] = return_addr; if (info) { diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 6a38542cb1..f7ebe6233b 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -10686,6 +10686,33 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, break; } #endif +#ifdef TARGET_AARCH64 + case TARGET_PR_SVE_SET_VL: + /* We cannot support either PR_SVE_SET_VL_ONEXEC + or PR_SVE_VL_INHERIT. Therefore, anything above + ARM_MAX_VQ results in EINVAL. */ + ret = -TARGET_EINVAL; + if (arm_feature(cpu_env, ARM_FEATURE_SVE) + && arg2 >= 0 && arg2 <= ARM_MAX_VQ * 16 && !(arg2 & 15)) { + CPUARMState *env = cpu_env; + int old_vq = (env->vfp.zcr_el[1] & 0xf) + 1; + int vq = MAX(arg2 / 16, 1); + + if (vq < old_vq) { + aarch64_sve_narrow_vq(env, vq); + } + env->vfp.zcr_el[1] = vq - 1; + ret = vq * 16; + } + break; + case TARGET_PR_SVE_GET_VL: + ret = -TARGET_EINVAL; + if (arm_feature(cpu_env, ARM_FEATURE_SVE)) { + CPUARMState *env = cpu_env; + ret = ((env->vfp.zcr_el[1] & 0xf) + 1) * 16; + } + break; +#endif /* AARCH64 */ case PR_GET_SECCOMP: case PR_SET_SECCOMP: /* Disable seccomp to prevent the target disabling syscalls we diff --git a/migration/block.c b/migration/block.c index 1f03946797..41b95d1dd8 100644 --- a/migration/block.c +++ b/migration/block.c @@ -36,7 +36,7 @@ #define MAX_IS_ALLOCATED_SEARCH (65536 * BDRV_SECTOR_SIZE) -#define MAX_INFLIGHT_IO 512 +#define MAX_IO_BUFFERS 512 //#define DEBUG_BLK_MIGRATION @@ -331,11 +331,10 @@ static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds) */ qemu_mutex_lock_iothread(); aio_context_acquire(blk_get_aio_context(bmds->blk)); - blk->aiocb = blk_aio_preadv(bb, cur_sector * BDRV_SECTOR_SIZE, &blk->qiov, - 0, blk_mig_read_cb, blk); - bdrv_reset_dirty_bitmap(bmds->dirty_bitmap, cur_sector * BDRV_SECTOR_SIZE, nr_sectors * BDRV_SECTOR_SIZE); + blk->aiocb = blk_aio_preadv(bb, cur_sector * BDRV_SECTOR_SIZE, &blk->qiov, + 0, blk_mig_read_cb, blk); aio_context_release(blk_get_aio_context(bmds->blk)); qemu_mutex_unlock_iothread(); @@ -776,9 +775,8 @@ static int block_save_iterate(QEMUFile *f, void *opaque) while ((block_mig_state.submitted + block_mig_state.read_done) * BLOCK_SIZE < qemu_file_get_rate_limit(f) && - (block_mig_state.submitted + - block_mig_state.read_done) < - MAX_INFLIGHT_IO) { + (block_mig_state.submitted + block_mig_state.read_done) < + MAX_IO_BUFFERS) { blk_mig_unlock(); if (block_mig_state.bulk_completed == 0) { /* first finish the bulk phase */ diff --git a/migration/migration.c b/migration/migration.c index e345d0cc7e..6a4780ef6f 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -747,13 +747,15 @@ void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params, { MigrationState *s = migrate_get_current(); MigrationCapabilityStatusList *cap; + bool cap_list[MIGRATION_CAPABILITY__MAX]; if (migration_is_setup_or_active(s->state)) { error_setg(errp, QERR_MIGRATION_ACTIVE); return; } - if (!migrate_caps_check(s->enabled_capabilities, params, errp)) { + memcpy(cap_list, s->enabled_capabilities, sizeof(cap_list)); + if (!migrate_caps_check(cap_list, params, errp)) { return; } @@ -2541,6 +2543,7 @@ static void migration_instance_finalize(Object *obj) g_free(params->tls_hostname); g_free(params->tls_creds); qemu_sem_destroy(&ms->pause_sem); + error_free(ms->error); } static void migration_instance_init(Object *obj) diff --git a/migration/ram.c b/migration/ram.c index 3b6c077964..7266351fd0 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -2258,6 +2258,13 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) int64_t t0; int done = 0; + if (blk_mig_bulk_active()) { + /* Avoid transferring ram during bulk phase of block migration as + * the bulk phase will usually take a long time and transferring + * ram updates during that time is pointless. */ + goto out; + } + rcu_read_lock(); if (ram_list.version != rs->last_version) { ram_state_reset(rs); @@ -2304,6 +2311,7 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) */ ram_control_after_iterate(f, RAM_CONTROL_ROUND); +out: qemu_put_be64(f, RAM_SAVE_FLAG_EOS); ram_counters.transferred += 8; @@ -983,6 +983,9 @@ static void qmp_unregister_commands_hack(void) #endif #ifndef TARGET_I386 qmp_unregister_command(&qmp_commands, "rtc-reset-reinjection"); + qmp_unregister_command(&qmp_commands, "query-sev"); + qmp_unregister_command(&qmp_commands, "query-sev-launch-measure"); + qmp_unregister_command(&qmp_commands, "query-sev-capabilities"); #endif #ifndef TARGET_S390X qmp_unregister_command(&qmp_commands, "dump-skeys"); @@ -4103,6 +4106,24 @@ void qmp_rtc_reset_reinjection(Error **errp) { error_setg(errp, QERR_FEATURE_DISABLED, "rtc-reset-reinjection"); } + +SevInfo *qmp_query_sev(Error **errp) +{ + error_setg(errp, QERR_FEATURE_DISABLED, "query-sev"); + return NULL; +} + +SevLaunchMeasureInfo *qmp_query_sev_launch_measure(Error **errp) +{ + error_setg(errp, QERR_FEATURE_DISABLED, "query-sev-launch-measure"); + return NULL; +} + +SevCapability *qmp_query_sev_capabilities(Error **errp) +{ + error_setg(errp, QERR_FEATURE_DISABLED, "query-sev-capabilities"); + return NULL; +} #endif #ifndef TARGET_S390X diff --git a/qapi/block-core.json b/qapi/block-core.json index 00475f08d4..524d51567a 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2553,6 +2553,63 @@ '*encrypt': 'BlockdevQcow2Encryption' } } ## +# @SshHostKeyCheckMode: +# +# @none Don't check the host key at all +# @hash Compare the host key with a given hash +# @known_hosts Check the host key against the known_hosts file +# +# Since: 2.12 +## +{ 'enum': 'SshHostKeyCheckMode', + 'data': [ 'none', 'hash', 'known_hosts' ] } + +## +# @SshHostKeyCheckHashType: +# +# @md5 The given hash is an md5 hash +# @sha1 The given hash is an sha1 hash +# +# Since: 2.12 +## +{ 'enum': 'SshHostKeyCheckHashType', + 'data': [ 'md5', 'sha1' ] } + +## +# @SshHostKeyHash: +# +# @type The hash algorithm used for the hash +# @hash The expected hash value +# +# Since: 2.12 +## +{ 'struct': 'SshHostKeyHash', + 'data': { 'type': 'SshHostKeyCheckHashType', + 'hash': 'str' }} + +## +# @SshHostKeyDummy: +# +# For those union branches that don't need additional fields. +# +# Since: 2.12 +## +{ 'struct': 'SshHostKeyDummy', + 'data': {} } + +## +# @SshHostKeyCheck: +# +# Since: 2.12 +## +{ 'union': 'SshHostKeyCheck', + 'base': { 'mode': 'SshHostKeyCheckMode' }, + 'discriminator': 'mode', + 'data': { 'none': 'SshHostKeyDummy', + 'hash': 'SshHostKeyHash', + 'known_hosts': 'SshHostKeyDummy' } } + +## # @BlockdevOptionsSsh: # # @server: host address @@ -2562,14 +2619,16 @@ # @user: user as which to connect, defaults to current # local user name # -# TODO: Expose the host_key_check option in QMP +# @host-key-check: Defines how and what to check the host key against +# (default: known_hosts) # # Since: 2.9 ## { 'struct': 'BlockdevOptionsSsh', 'data': { 'server': 'InetSocketAddress', 'path': 'str', - '*user': 'str' } } + '*user': 'str', + '*host-key-check': 'SshHostKeyCheck' } } ## @@ -3359,6 +3418,269 @@ { 'command': 'blockdev-del', 'data': { 'node-name': 'str' } } ## +# @BlockdevCreateOptionsFile: +# +# Driver specific image creation options for file. +# +# @filename Filename for the new image file +# @size Size of the virtual disk in bytes +# @preallocation Preallocation mode for the new image (default: off) +# @nocow Turn off copy-on-write (valid only on btrfs; default: off) +# +# Since: 2.12 +## +{ 'struct': 'BlockdevCreateOptionsFile', + 'data': { 'filename': 'str', + 'size': 'size', + '*preallocation': 'PreallocMode', + '*nocow': 'bool' } } + +## +# @BlockdevCreateOptionsGluster: +# +# Driver specific image creation options for gluster. +# +# @location Where to store the new image file +# @size Size of the virtual disk in bytes +# @preallocation Preallocation mode for the new image (default: off) +# +# Since: 2.12 +## +{ 'struct': 'BlockdevCreateOptionsGluster', + 'data': { 'location': 'BlockdevOptionsGluster', + 'size': 'size', + '*preallocation': 'PreallocMode' } } + +## +# @BlockdevCreateOptionsNfs: +# +# Driver specific image creation options for NFS. +# +# @location Where to store the new image file +# @size Size of the virtual disk in bytes +# +# Since: 2.12 +## +{ 'struct': 'BlockdevCreateOptionsNfs', + 'data': { 'location': 'BlockdevOptionsNfs', + 'size': 'size' } } + +## +# @BlockdevQcow2Version: +# +# @v2: The original QCOW2 format as introduced in qemu 0.10 (version 2) +# @v3: The extended QCOW2 format as introduced in qemu 1.1 (version 3) +# +# Since: 2.12 +## +{ 'enum': 'BlockdevQcow2Version', + 'data': [ 'v2', 'v3' ] } + + +## +# @BlockdevCreateOptionsQcow2: +# +# Driver specific image creation options for qcow2. +# +# @file Node to create the image format on +# @size Size of the virtual disk in bytes +# @version Compatibility level (default: v3) +# @backing-file File name of the backing file if a backing file +# should be used +# @backing-fmt Name of the block driver to use for the backing file +# @encrypt Encryption options if the image should be encrypted +# @cluster-size qcow2 cluster size in bytes (default: 65536) +# @preallocation Preallocation mode for the new image (default: off) +# @lazy-refcounts True if refcounts may be updated lazily (default: off) +# @refcount-bits Width of reference counts in bits (default: 16) +# +# Since: 2.12 +## +{ 'struct': 'BlockdevCreateOptionsQcow2', + 'data': { 'file': 'BlockdevRef', + 'size': 'size', + '*version': 'BlockdevQcow2Version', + '*backing-file': 'str', + '*backing-fmt': 'BlockdevDriver', + '*encrypt': 'QCryptoBlockCreateOptions', + '*cluster-size': 'size', + '*preallocation': 'PreallocMode', + '*lazy-refcounts': 'bool', + '*refcount-bits': 'int' } } + +## +# @BlockdevCreateOptionsRbd: +# +# Driver specific image creation options for rbd/Ceph. +# +# @location Where to store the new image file. This location cannot +# point to a snapshot. +# @size Size of the virtual disk in bytes +# @cluster-size RBD object size +# +# Since: 2.12 +## +{ 'struct': 'BlockdevCreateOptionsRbd', + 'data': { 'location': 'BlockdevOptionsRbd', + 'size': 'size', + '*cluster-size' : 'size' } } + +## +# @SheepdogRedundancyType: +# +# @full Create a fully replicated vdi with x copies +# @erasure-coded Create an erasure coded vdi with x data strips and +# y parity strips +# +# Since: 2.12 +## +{ 'enum': 'SheepdogRedundancyType', + 'data': [ 'full', 'erasure-coded' ] } + +## +# @SheepdogRedundancyFull: +# +# @copies Number of copies to use (between 1 and 31) +# +# Since: 2.12 +## +{ 'struct': 'SheepdogRedundancyFull', + 'data': { 'copies': 'int' }} + +## +# @SheepdogRedundancyErasureCoded: +# +# @data-strips Number of data strips to use (one of {2,4,8,16}) +# @parity-strips Number of parity strips to use (between 1 and 15) +# +# Since: 2.12 +## +{ 'struct': 'SheepdogRedundancyErasureCoded', + 'data': { 'data-strips': 'int', + 'parity-strips': 'int' }} + +## +# @SheepdogRedundancy: +# +# Since: 2.12 +## +{ 'union': 'SheepdogRedundancy', + 'base': { 'type': 'SheepdogRedundancyType' }, + 'discriminator': 'type', + 'data': { 'full': 'SheepdogRedundancyFull', + 'erasure-coded': 'SheepdogRedundancyErasureCoded' } } + +## +# @BlockdevCreateOptionsSheepdog: +# +# Driver specific image creation options for Sheepdog. +# +# @location Where to store the new image file +# @size Size of the virtual disk in bytes +# @backing-file File name of a base image +# @preallocation Preallocation mode (allowed values: off, full) +# @redundancy Redundancy of the image +# @object-size Object size of the image +# +# Since: 2.12 +## +{ 'struct': 'BlockdevCreateOptionsSheepdog', + 'data': { 'location': 'BlockdevOptionsSheepdog', + 'size': 'size', + '*backing-file': 'str', + '*preallocation': 'PreallocMode', + '*redundancy': 'SheepdogRedundancy', + '*object-size': 'size' } } + +## +# @BlockdevCreateOptionsSsh: +# +# Driver specific image creation options for SSH. +# +# @location Where to store the new image file +# @size Size of the virtual disk in bytes +# +# Since: 2.12 +## +{ 'struct': 'BlockdevCreateOptionsSsh', + 'data': { 'location': 'BlockdevOptionsSsh', + 'size': 'size' } } + +## +# @BlockdevCreateNotSupported: +# +# This is used for all drivers that don't support creating images. +# +# Since: 2.12 +## +{ 'struct': 'BlockdevCreateNotSupported', 'data': {}} + +## +# @BlockdevCreateOptions: +# +# Options for creating an image format on a given node. +# +# @driver block driver to create the image format +# +# Since: 2.12 +## +{ 'union': 'BlockdevCreateOptions', + 'base': { + 'driver': 'BlockdevDriver' }, + 'discriminator': 'driver', + 'data': { + 'blkdebug': 'BlockdevCreateNotSupported', + 'blkverify': 'BlockdevCreateNotSupported', + 'bochs': 'BlockdevCreateNotSupported', + 'cloop': 'BlockdevCreateNotSupported', + 'dmg': 'BlockdevCreateNotSupported', + 'file': 'BlockdevCreateOptionsFile', + 'ftp': 'BlockdevCreateNotSupported', + 'ftps': 'BlockdevCreateNotSupported', + 'gluster': 'BlockdevCreateOptionsGluster', + 'host_cdrom': 'BlockdevCreateNotSupported', + 'host_device': 'BlockdevCreateNotSupported', + 'http': 'BlockdevCreateNotSupported', + 'https': 'BlockdevCreateNotSupported', + 'iscsi': 'BlockdevCreateNotSupported', + 'luks': 'BlockdevCreateNotSupported', + 'nbd': 'BlockdevCreateNotSupported', + 'nfs': 'BlockdevCreateOptionsNfs', + 'null-aio': 'BlockdevCreateNotSupported', + 'null-co': 'BlockdevCreateNotSupported', + 'nvme': 'BlockdevCreateNotSupported', + 'parallels': 'BlockdevCreateNotSupported', + 'qcow2': 'BlockdevCreateOptionsQcow2', + 'qcow': 'BlockdevCreateNotSupported', + 'qed': 'BlockdevCreateNotSupported', + 'quorum': 'BlockdevCreateNotSupported', + 'raw': 'BlockdevCreateNotSupported', + 'rbd': 'BlockdevCreateOptionsRbd', + 'replication': 'BlockdevCreateNotSupported', + 'sheepdog': 'BlockdevCreateOptionsSheepdog', + 'ssh': 'BlockdevCreateOptionsSsh', + 'throttle': 'BlockdevCreateNotSupported', + 'vdi': 'BlockdevCreateNotSupported', + 'vhdx': 'BlockdevCreateNotSupported', + 'vmdk': 'BlockdevCreateNotSupported', + 'vpc': 'BlockdevCreateNotSupported', + 'vvfat': 'BlockdevCreateNotSupported', + 'vxhs': 'BlockdevCreateNotSupported' + } } + +## +# @x-blockdev-create: +# +# Create an image format on a given node. +# TODO Replace with something asynchronous (block job?) +# +# Since: 2.12 +## +{ 'command': 'x-blockdev-create', + 'data': 'BlockdevCreateOptions', + 'boxed': true } + +## # @blockdev-open-tray: # # Opens a block device's tray. If there is a block driver state tree inserted as diff --git a/qapi/misc.json b/qapi/misc.json index bcd5d10778..6150b9a003 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -3216,3 +3216,151 @@ # Since: 2.9 ## { 'command': 'query-vm-generation-id', 'returns': 'GuidInfo' } + + +## +# @SevState: +# +# An enumeration of SEV state information used during @query-sev. +# +# @uninit: The guest is uninitialized. +# +# @launch-update: The guest is currently being launched; plaintext data and +# register state is being imported. +# +# @launch-secret: The guest is currently being launched; ciphertext data +# is being imported. +# +# @running: The guest is fully launched or migrated in. +# +# @send-update: The guest is currently being migrated out to another machine. +# +# @receive-update: The guest is currently being migrated from another machine. +# +# Since: 2.12 +## +{ 'enum': 'SevState', + 'data': ['uninit', 'launch-update', 'launch-secret', 'running', + 'send-update', 'receive-update' ] } + +## +# @SevInfo: +# +# Information about Secure Encrypted Virtualization (SEV) support +# +# @enabled: true if SEV is active +# +# @api-major: SEV API major version +# +# @api-minor: SEV API minor version +# +# @build-id: SEV FW build id +# +# @policy: SEV policy value +# +# @state: SEV guest state +# +# @handle: SEV firmware handle +# +# Since: 2.12 +## +{ 'struct': 'SevInfo', + 'data': { 'enabled': 'bool', + 'api-major': 'uint8', + 'api-minor' : 'uint8', + 'build-id' : 'uint8', + 'policy' : 'uint32', + 'state' : 'SevState', + 'handle' : 'uint32' + } +} + +## +# @query-sev: +# +# Returns information about SEV +# +# Returns: @SevInfo +# +# Since: 2.12 +# +# Example: +# +# -> { "execute": "query-sev" } +# <- { "return": { "enabled": true, "api-major" : 0, "api-minor" : 0, +# "build-id" : 0, "policy" : 0, "state" : "running", +# "handle" : 1 } } +# +## +{ 'command': 'query-sev', 'returns': 'SevInfo' } + +## +# @SevLaunchMeasureInfo: +# +# SEV Guest Launch measurement information +# +# @data: the measurement value encoded in base64 +# +# Since: 2.12 +# +## +{ 'struct': 'SevLaunchMeasureInfo', 'data': {'data': 'str'} } + +## +# @query-sev-launch-measure: +# +# Query the SEV guest launch information. +# +# Returns: The @SevLaunchMeasureInfo for the guest +# +# Since: 2.12 +# +# Example: +# +# -> { "execute": "query-sev-launch-measure" } +# <- { "return": { "data": "4l8LXeNlSPUDlXPJG5966/8%YZ" } } +# +## +{ 'command': 'query-sev-launch-measure', 'returns': 'SevLaunchMeasureInfo' } + +## +# @SevCapability: +# +# The struct describes capability for a Secure Encrypted Virtualization +# feature. +# +# @pdh: Platform Diffie-Hellman key (base64 encoded) +# +# @cert-chain: PDH certificate chain (base64 encoded) +# +# @cbitpos: C-bit location in page table entry +# +# @reduced-phys-bits: Number of physical Address bit reduction when SEV is +# enabled +# +# Since: 2.12 +## +{ 'struct': 'SevCapability', + 'data': { 'pdh': 'str', + 'cert-chain': 'str', + 'cbitpos': 'int', + 'reduced-phys-bits': 'int'} } + +## +# @query-sev-capabilities: +# +# This command is used to get the SEV capabilities, and is supported on AMD +# X86 platforms only. +# +# Returns: SevCapability objects. +# +# Since: 2.12 +# +# Example: +# +# -> { "execute": "query-sev-capabilities" } +# <- { "return": { "pdh": "8CCDD8DDD", "cert-chain": "888CCCDDDEE", +# "cbitpos": 47, "reduced-phys-bits": 5}} +# +## +{ 'command': 'query-sev-capabilities', 'returns': 'SevCapability' } diff --git a/qapi/ui.json b/qapi/ui.json index 3e82f25ac5..5d01ad4304 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -77,6 +77,13 @@ # # @filename: the path of a new PPM file to store the image # +# @device: ID of the display device that should be dumped. If this parameter +# is missing, the primary display will be used. (Since 2.12) +# +# @head: head to use in case the device supports multiple heads. If this +# parameter is missing, head #0 will be used. Also note that the head +# can only be specified in conjunction with the device ID. (Since 2.12) +# # Returns: Nothing on success # # Since: 0.14.0 @@ -88,7 +95,8 @@ # <- { "return": {} } # ## -{ 'command': 'screendump', 'data': {'filename': 'str'} } +{ 'command': 'screendump', + 'data': {'filename': 'str', '*device': 'str', '*head': 'int'} } ## # == Spice diff --git a/qemu-options.hx b/qemu-options.hx index 6585058c6c..6113bce08a 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -43,7 +43,8 @@ DEF("machine", HAS_ARG, QEMU_OPTION_machine, \ " suppress-vmdesc=on|off disables self-describing migration (default=off)\n" " nvdimm=on|off controls NVDIMM support (default=off)\n" " enforce-config-section=on|off enforce configuration section migration (default=off)\n" - " s390-squash-mcss=on|off (deprecated) controls support for squashing into default css (default=off)\n", + " s390-squash-mcss=on|off (deprecated) controls support for squashing into default css (default=off)\n" + " memory-encryption=@var{} memory encryption object to use (default=none)\n", QEMU_ARCH_ALL) STEXI @item -machine [type=]@var{name}[,prop=@var{value}[,...]] @@ -110,6 +111,8 @@ code to send configuration section even if the machine-type sets the @option{migration.send-configuration} property to @var{off}. NOTE: this parameter is deprecated. Please use @option{-global} @option{migration.send-configuration}=@var{on|off} instead. +@item memory-encryption=@var{} +Memory encryption object to use. The default is none. @end table ETEXI @@ -4350,6 +4353,50 @@ contents of @code{iv.b64} to the second secret data=$SECRET,iv=$(<iv.b64) @end example +@item -object sev-guest,id=@var{id},cbitpos=@var{cbitpos},reduced-phys-bits=@var{val},[sev-device=@var{string},policy=@var{policy},handle=@var{handle},dh-cert-file=@var{file},session-file=@var{file}] + +Create a Secure Encrypted Virtualization (SEV) guest object, which can be used +to provide the guest memory encryption support on AMD processors. + +When memory encryption is enabled, one of the physical address bit (aka the +C-bit) is utilized to mark if a memory page is protected. The @option{cbitpos} +is used to provide the C-bit position. The C-bit position is Host family dependent +hence user must provide this value. On EPYC, the value should be 47. + +When memory encryption is enabled, we loose certain bits in physical address space. +The @option{reduced-phys-bits} is used to provide the number of bits we loose in +physical address space. Similar to C-bit, the value is Host family dependent. +On EPYC, the value should be 5. + +The @option{sev-device} provides the device file to use for communicating with +the SEV firmware running inside AMD Secure Processor. The default device is +'/dev/sev'. If hardware supports memory encryption then /dev/sev devices are +created by CCP driver. + +The @option{policy} provides the guest policy to be enforced by the SEV firmware +and restrict what configuration and operational commands can be performed on this +guest by the hypervisor. The policy should be provided by the guest owner and is +bound to the guest and cannot be changed throughout the lifetime of the guest. +The default is 0. + +If guest @option{policy} allows sharing the key with another SEV guest then +@option{handle} can be use to provide handle of the guest from which to share +the key. + +The @option{dh-cert-file} and @option{session-file} provides the guest owner's +Public Diffie-Hillman key defined in SEV spec. The PDH and session parameters +are used for establishing a cryptographic session with the guest owner to +negotiate keys used for attestation. The file must be encoded in base64. + +e.g to launch a SEV guest +@example + # $QEMU \ + ...... + -object sev-guest,id=sev0,cbitpos=47,reduced-phys-bits=5 \ + -machine ...,memory-encryption=sev0 + ..... + +@end example @end table ETEXI diff --git a/qobject/qdict.c b/qobject/qdict.c index 23df84f9cd..229b8c840b 100644 --- a/qobject/qdict.c +++ b/qobject/qdict.c @@ -1072,3 +1072,37 @@ void qdict_join(QDict *dest, QDict *src, bool overwrite) entry = next; } } + +/** + * qdict_rename_keys(): Rename keys in qdict according to the replacements + * specified in the array renames. The array must be terminated by an entry + * with from = NULL. + * + * The renames are performed individually in the order of the array, so entries + * may be renamed multiple times and may or may not conflict depending on the + * order of the renames array. + * + * Returns true for success, false in error cases. + */ +bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp) +{ + QObject *qobj; + + while (renames->from) { + if (qdict_haskey(qdict, renames->from)) { + if (qdict_haskey(qdict, renames->to)) { + error_setg(errp, "'%s' and its alias '%s' can't be used at the " + "same time", renames->to, renames->from); + return false; + } + + qobj = qdict_get(qdict, renames->from); + qobject_incref(qobj); + qdict_put_obj(qdict, renames->to, qobj); + qdict_del(qdict, renames->from); + } + + renames++; + } + return true; +} diff --git a/scripts/create_config b/scripts/create_config index 603b826886..d727e5e36e 100755 --- a/scripts/create_config +++ b/scripts/create_config @@ -36,7 +36,7 @@ case $line in drivers=${line#*=} echo "#define CONFIG_AUDIO_DRIVERS \\" for drv in $drivers; do - echo " &${drv}_audio_driver,\\" + echo " \"${drv}\",\\" done echo "" ;; diff --git a/scripts/device-crash-test b/scripts/device-crash-test index 7417177ebb..f04f34924e 100755 --- a/scripts/device-crash-test +++ b/scripts/device-crash-test @@ -1,4 +1,4 @@ -#!/usr/bin/env python2.7 +#!/usr/bin/env python # # Copyright (c) 2017 Red Hat Inc # @@ -217,11 +217,15 @@ ERROR_WHITELIST = [ {'exitcode':-6, 'log':r"Object .* is not an instance of type generic-pc-machine", 'loglevel':logging.ERROR}, {'exitcode':-6, 'log':r"Object .* is not an instance of type e500-ccsr", 'loglevel':logging.ERROR}, {'exitcode':-6, 'log':r"vmstate_register_with_alias_id: Assertion `!se->compat \|\| se->instance_id == 0' failed", 'loglevel':logging.ERROR}, + {'exitcode':-6, 'device':'isa-fdc', 'loglevel':logging.ERROR, 'expected':True}, {'exitcode':-11, 'device':'gus', 'loglevel':logging.ERROR, 'expected':True}, {'exitcode':-11, 'device':'isa-serial', 'loglevel':logging.ERROR, 'expected':True}, {'exitcode':-11, 'device':'sb16', 'loglevel':logging.ERROR, 'expected':True}, {'exitcode':-11, 'device':'cs4231a', 'loglevel':logging.ERROR, 'expected':True}, {'exitcode':-11, 'machine':'isapc', 'device':'.*-iommu', 'loglevel':logging.ERROR, 'expected':True}, + {'exitcode':-11, 'device':'mioe3680_pci', 'loglevel':logging.ERROR, 'expected':True}, + {'exitcode':-11, 'device':'pcm3680_pci', 'loglevel':logging.ERROR, 'expected':True}, + {'exitcode':-11, 'device':'kvaser_pci', 'loglevel':logging.ERROR, 'expected':True}, # everything else (including SIGABRT and SIGSEGV) will be a fatal error: {'exitcode':None, 'fatal':True, 'loglevel':logging.FATAL}, diff --git a/scripts/qemu.py b/scripts/qemu.py index 305a946562..08a3e9af5a 100644 --- a/scripts/qemu.py +++ b/scripts/qemu.py @@ -277,7 +277,7 @@ class QEMUMachine(object): def qmp(self, cmd, conv_keys=True, **args): '''Invoke a QMP command and return the response dict''' qmp_args = dict() - for key, value in args.iteritems(): + for key, value in args.items(): if conv_keys: qmp_args[key.replace('_', '-')] = value else: diff --git a/scripts/qmp/qmp.py b/scripts/qmp/qmp.py index 07c9632e9e..5c8cf6a056 100644 --- a/scripts/qmp/qmp.py +++ b/scripts/qmp/qmp.py @@ -166,7 +166,7 @@ class QEMUMonitorProtocol(object): """ self.logger.debug(">>> %s", qmp_cmd) try: - self.__sock.sendall(json.dumps(qmp_cmd)) + self.__sock.sendall(json.dumps(qmp_cmd).encode('utf-8')) except socket.error as err: if err[0] == errno.EPIPE: return diff --git a/scripts/simpletrace.py b/scripts/simpletrace.py index a3a6315055..9d45c6ba4e 100755 --- a/scripts/simpletrace.py +++ b/scripts/simpletrace.py @@ -168,7 +168,7 @@ class Analyzer(object): def process(events, log, analyzer, read_header=True): """Invoke an analyzer on each event in a log.""" if isinstance(events, str): - events = read_events(open(events, 'r')) + events = read_events(open(events, 'r'), events) if isinstance(log, str): log = open(log, 'rb') @@ -199,7 +199,7 @@ def process(events, log, analyzer, read_header=True): fn_argcount = len(inspect.getargspec(fn)[0]) - 1 if fn_argcount == event_argcount + 1: # Include timestamp as first argument - return lambda _, rec: fn(*((rec[1:2],) + rec[3:3 + event_argcount])) + return lambda _, rec: fn(*(rec[1:2] + rec[3:3 + event_argcount])) elif fn_argcount == event_argcount + 2: # Include timestamp and pid return lambda _, rec: fn(*rec[1:3 + event_argcount]) @@ -233,7 +233,7 @@ def run(analyzer): '<trace-file>\n' % sys.argv[0]) sys.exit(1) - events = read_events(open(sys.argv[1], 'r')) + events = read_events(open(sys.argv[1], 'r'), sys.argv[1]) process(events, sys.argv[2], analyzer, read_header=read_header) if __name__ == '__main__': diff --git a/scripts/tracetool.py b/scripts/tracetool.py index c55a21518b..fe2b0771f2 100755 --- a/scripts/tracetool.py +++ b/scripts/tracetool.py @@ -142,7 +142,7 @@ def main(args): events = [] for arg in args: with open(arg, "r") as fh: - events.extend(tracetool.read_events(fh)) + events.extend(tracetool.read_events(fh, arg)) try: tracetool.generate(events, arg_group, arg_format, arg_backends, diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py index 3646c2b9fc..b20fac34a3 100644 --- a/scripts/tracetool/__init__.py +++ b/scripts/tracetool/__init__.py @@ -41,6 +41,51 @@ def out(*lines, **kwargs): lines = [ l % kwargs for l in lines ] sys.stdout.writelines("\n".join(lines) + "\n") +# We only want to allow standard C types or fixed sized +# integer types. We don't want QEMU specific types +# as we can't assume trace backends can resolve all the +# typedefs +ALLOWED_TYPES = [ + "int", + "long", + "short", + "char", + "bool", + "unsigned", + "signed", + "float", + "double", + "int8_t", + "uint8_t", + "int16_t", + "uint16_t", + "int32_t", + "uint32_t", + "int64_t", + "uint64_t", + "void", + "size_t", + "ssize_t", + "uintptr_t", + "ptrdiff_t", + # Magic substitution is done by tracetool + "TCGv", +] + +def validate_type(name): + bits = name.split(" ") + for bit in bits: + bit = re.sub("\*", "", bit) + if bit == "": + continue + if bit == "const": + continue + if bit not in ALLOWED_TYPES: + raise ValueError("Argument type '%s' is not in whitelist. " + "Only standard C types and fixed size integer " + "types should be used. struct, union, and " + "other complex pointer types should be " + "declared as 'void *'" % name) class Arguments: """Event arguments description.""" @@ -87,6 +132,7 @@ class Arguments: else: arg_type, identifier = arg.rsplit(None, 1) + validate_type(arg_type) res.append((arg_type, identifier)) return Arguments(res) @@ -291,13 +337,15 @@ class Event(object): self) -def read_events(fobj): +def read_events(fobj, fname): """Generate the output for the given (format, backends) pair. Parameters ---------- fobj : file Event description file. + fname : str + Name of event file Returns a list of Event objects """ @@ -312,7 +360,7 @@ def read_events(fobj): try: event = Event.build(line) except ValueError as e: - arg0 = 'Error on line %d: %s' % (lineno, e.args[0]) + arg0 = 'Error at %s:%d: %s' % (fname, lineno, e.args[0]) e.args = (arg0,) + e.args[1:] raise diff --git a/scripts/tracetool/backend/log.py b/scripts/tracetool/backend/log.py index da86f6b882..78933d03ad 100644 --- a/scripts/tracetool/backend/log.py +++ b/scripts/tracetool/backend/log.py @@ -20,7 +20,7 @@ PUBLIC = True def generate_h_begin(events, group): - out('#include "qemu/log.h"', + out('#include "qemu/log-for-trace.h"', '') @@ -35,14 +35,13 @@ def generate_h(event, group): else: cond = "trace_event_get_state(%s)" % ("TRACE_" + event.name.upper()) - out(' if (%(cond)s) {', + out(' if (%(cond)s && qemu_loglevel_mask(LOG_TRACE)) {', ' struct timeval _now;', ' gettimeofday(&_now, NULL);', - ' qemu_log_mask(LOG_TRACE,', - ' "%%d@%%zd.%%06zd:%(name)s " %(fmt)s "\\n",', - ' getpid(),', - ' (size_t)_now.tv_sec, (size_t)_now.tv_usec', - ' %(argnames)s);', + ' qemu_log("%%d@%%zd.%%06zd:%(name)s " %(fmt)s "\\n",', + ' getpid(),', + ' (size_t)_now.tv_sec, (size_t)_now.tv_usec', + ' %(argnames)s);', ' }', cond=cond, name=event.name, diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index be065704df..e152417936 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -56,6 +56,7 @@ cp_portable() { -e 's/__bitwise//' \ -e 's/__attribute__((packed))/QEMU_PACKED/' \ -e 's/__inline__/inline/' \ + -e 's/__BITS_PER_LONG/HOST_LONG_BITS/' \ -e '/sys\/ioctl.h/d' \ -e 's/SW_MAX/SW_MAX_/' \ -e 's/atomic_t/int/' \ @@ -99,6 +100,8 @@ for arch in $ARCHLIST; do mkdir -p "$output/include/standard-headers/asm-$arch" if [ $arch = s390 ]; then cp_portable "$tmpdir/include/asm/virtio-ccw.h" "$output/include/standard-headers/asm-s390/" + cp "$tmpdir/include/asm/unistd_32.h" "$output/linux-headers/asm-s390/" + cp "$tmpdir/include/asm/unistd_64.h" "$output/linux-headers/asm-s390/" fi if [ $arch = arm ]; then cp "$tmpdir/include/asm/unistd-eabi.h" "$output/linux-headers/asm-arm/" @@ -118,7 +121,7 @@ done rm -rf "$output/linux-headers/linux" mkdir -p "$output/linux-headers/linux" for header in kvm.h kvm_para.h vfio.h vfio_ccw.h vhost.h \ - psci.h userfaultfd.h; do + psci.h psp-sev.h userfaultfd.h; do cp "$tmpdir/include/linux/$header" "$output/linux-headers/linux" done rm -rf "$output/linux-headers/asm-generic" diff --git a/target/arm/cpu-qom.h b/target/arm/cpu-qom.h index a42495bac9..d135ff8e06 100644 --- a/target/arm/cpu-qom.h +++ b/target/arm/cpu-qom.h @@ -33,6 +33,8 @@ struct arm_boot_info; #define ARM_CPU_GET_CLASS(obj) \ OBJECT_GET_CLASS(ARMCPUClass, (obj), TYPE_ARM_CPU) +#define TYPE_ARM_MAX_CPU "max-" TYPE_ARM_CPU + /** * ARMCPUClass: * @parent_realize: The parent class' realize handler. diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 6b77aaa445..022d8c5787 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -725,6 +725,19 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) int pagebits; Error *local_err = NULL; + /* If we needed to query the host kernel for the CPU features + * then it's possible that might have failed in the initfn, but + * this is the first point where we can report it. + */ + if (cpu->host_cpu_probe_failed) { + if (!kvm_enabled()) { + error_setg(errp, "The 'host' CPU type can only be used with KVM"); + } else { + error_setg(errp, "Failed to retrieve host CPU features"); + } + return; + } + cpu_exec_realizefn(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); @@ -939,6 +952,11 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) cs->num_ases = 1; } cpu_address_space_init(cs, ARMASIdx_NS, "cpu-memory", cs->memory); + + /* No core_count specified, default to smp_cpus. */ + if (cpu->core_count == -1) { + cpu->core_count = smp_cpus; + } #endif qemu_init_vcpu(cs); @@ -952,9 +970,19 @@ static ObjectClass *arm_cpu_class_by_name(const char *cpu_model) ObjectClass *oc; char *typename; char **cpuname; + const char *cpunamestr; cpuname = g_strsplit(cpu_model, ",", 1); - typename = g_strdup_printf(ARM_CPU_TYPE_NAME("%s"), cpuname[0]); + cpunamestr = cpuname[0]; +#ifdef CONFIG_USER_ONLY + /* For backwards compatibility usermode emulation allows "-cpu any", + * which has the same semantics as "-cpu max". + */ + if (!strcmp(cpunamestr, "any")) { + cpunamestr = "max"; + } +#endif + typename = g_strdup_printf(ARM_CPU_TYPE_NAME("%s"), cpunamestr); oc = object_class_by_name(typename); g_strfreev(cpuname); g_free(typename); @@ -1684,22 +1712,37 @@ static void pxa270c5_initfn(Object *obj) cpu->reset_sctlr = 0x00000078; } -#ifdef CONFIG_USER_ONLY -static void arm_any_initfn(Object *obj) +#ifndef TARGET_AARCH64 +/* -cpu max: if KVM is enabled, like -cpu host (best possible with this host); + * otherwise, a CPU with as many features enabled as our emulation supports. + * The version of '-cpu max' for qemu-system-aarch64 is defined in cpu64.c; + * this only needs to handle 32 bits. + */ +static void arm_max_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); - set_feature(&cpu->env, ARM_FEATURE_V8); - set_feature(&cpu->env, ARM_FEATURE_VFP4); - set_feature(&cpu->env, ARM_FEATURE_NEON); - set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); - set_feature(&cpu->env, ARM_FEATURE_V8_AES); - set_feature(&cpu->env, ARM_FEATURE_V8_SHA1); - set_feature(&cpu->env, ARM_FEATURE_V8_SHA256); - set_feature(&cpu->env, ARM_FEATURE_V8_PMULL); - set_feature(&cpu->env, ARM_FEATURE_CRC); - set_feature(&cpu->env, ARM_FEATURE_V8_RDM); - set_feature(&cpu->env, ARM_FEATURE_V8_FCMA); - cpu->midr = 0xffffffff; + + if (kvm_enabled()) { + kvm_arm_set_cpu_features_from_host(cpu); + } else { + cortex_a15_initfn(obj); +#ifdef CONFIG_USER_ONLY + /* We don't set these in system emulation mode for the moment, + * since we don't correctly set the ID registers to advertise them, + */ + set_feature(&cpu->env, ARM_FEATURE_V8); + set_feature(&cpu->env, ARM_FEATURE_VFP4); + set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); + set_feature(&cpu->env, ARM_FEATURE_V8_AES); + set_feature(&cpu->env, ARM_FEATURE_V8_SHA1); + set_feature(&cpu->env, ARM_FEATURE_V8_SHA256); + set_feature(&cpu->env, ARM_FEATURE_V8_PMULL); + set_feature(&cpu->env, ARM_FEATURE_CRC); + set_feature(&cpu->env, ARM_FEATURE_V8_RDM); + set_feature(&cpu->env, ARM_FEATURE_V8_FCMA); +#endif + } } #endif @@ -1751,8 +1794,11 @@ static const ARMCPUInfo arm_cpus[] = { { .name = "pxa270-b1", .initfn = pxa270b1_initfn }, { .name = "pxa270-c0", .initfn = pxa270c0_initfn }, { .name = "pxa270-c5", .initfn = pxa270c5_initfn }, +#ifndef TARGET_AARCH64 + { .name = "max", .initfn = arm_max_initfn }, +#endif #ifdef CONFIG_USER_ONLY - { .name = "any", .initfn = arm_any_initfn }, + { .name = "any", .initfn = arm_max_initfn }, #endif #endif { .name = NULL } @@ -1765,6 +1811,7 @@ static Property arm_cpu_properties[] = { DEFINE_PROP_UINT64("mp-affinity", ARMCPU, mp_affinity, ARM64_AFFINITY_INVALID), DEFINE_PROP_INT32("node-id", ARMCPU, node_id, CPU_UNSET_NUMA_NODE_ID), + DEFINE_PROP_INT32("core-count", ARMCPU, core_count, -1), DEFINE_PROP_END_OF_LIST() }; @@ -1845,6 +1892,26 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data) #endif } +#ifdef CONFIG_KVM +static void arm_host_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + kvm_arm_set_cpu_features_from_host(cpu); +} + +static const TypeInfo host_arm_cpu_type_info = { + .name = TYPE_ARM_HOST_CPU, +#ifdef TARGET_AARCH64 + .parent = TYPE_AARCH64_CPU, +#else + .parent = TYPE_ARM_CPU, +#endif + .instance_init = arm_host_initfn, +}; + +#endif + static void cpu_register(const ARMCPUInfo *info) { TypeInfo type_info = { @@ -1889,6 +1956,10 @@ static void arm_cpu_register_types(void) cpu_register(info); info++; } + +#ifdef CONFIG_KVM + type_register_static(&host_arm_cpu_type_info); +#endif } type_init(arm_cpu_register_types) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 8dd6b788df..1e7e1f8a7e 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -745,6 +745,16 @@ struct ARMCPU { /* Uniprocessor system with MP extensions */ bool mp_is_up; + /* True if we tried kvm_arm_host_cpu_features() during CPU instance_init + * and the probe failed (so we need to report the error in realize) + */ + bool host_cpu_probe_failed; + + /* Specify the number of cores in this CPU cluster. Used for the L2CTLR + * register. + */ + int32_t core_count; + /* The instance init functions for implementation-specific subclasses * set these fields to specify the implementation-dependent values of * various constant registers and reset values of non-constant @@ -861,6 +871,7 @@ int arm_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, #ifdef TARGET_AARCH64 int aarch64_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); int aarch64_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); +void aarch64_sve_narrow_vq(CPUARMState *env, unsigned vq); #endif target_ulong do_arm_semihosting(CPUARMState *env); diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 4228713b19..991d764674 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -28,6 +28,7 @@ #include "hw/arm/arm.h" #include "sysemu/sysemu.h" #include "sysemu/kvm.h" +#include "kvm_arm.h" static inline void set_feature(CPUARMState *env, int feature) { @@ -42,8 +43,10 @@ static inline void unset_feature(CPUARMState *env, int feature) #ifndef CONFIG_USER_ONLY static uint64_t a57_a53_l2ctlr_read(CPUARMState *env, const ARMCPRegInfo *ri) { - /* Number of processors is in [25:24]; otherwise we RAZ */ - return (smp_cpus - 1) << 24; + ARMCPU *cpu = arm_env_get_cpu(env); + + /* Number of cores is in [25:24]; otherwise we RAZ */ + return (cpu->core_count - 1) << 24; } #endif @@ -212,31 +215,50 @@ static void aarch64_a53_initfn(Object *obj) define_arm_cp_regs(cpu, cortex_a57_a53_cp_reginfo); } -#ifdef CONFIG_USER_ONLY -static void aarch64_any_initfn(Object *obj) +/* -cpu max: if KVM is enabled, like -cpu host (best possible with this host); + * otherwise, a CPU with as many features enabled as our emulation supports. + * The version of '-cpu max' for qemu-system-arm is defined in cpu.c; + * this only needs to handle 64 bits. + */ +static void aarch64_max_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); - set_feature(&cpu->env, ARM_FEATURE_V8); - set_feature(&cpu->env, ARM_FEATURE_VFP4); - set_feature(&cpu->env, ARM_FEATURE_NEON); - set_feature(&cpu->env, ARM_FEATURE_AARCH64); - set_feature(&cpu->env, ARM_FEATURE_V8_AES); - set_feature(&cpu->env, ARM_FEATURE_V8_SHA1); - set_feature(&cpu->env, ARM_FEATURE_V8_SHA256); - set_feature(&cpu->env, ARM_FEATURE_V8_SHA512); - set_feature(&cpu->env, ARM_FEATURE_V8_SHA3); - set_feature(&cpu->env, ARM_FEATURE_V8_SM3); - set_feature(&cpu->env, ARM_FEATURE_V8_SM4); - set_feature(&cpu->env, ARM_FEATURE_V8_PMULL); - set_feature(&cpu->env, ARM_FEATURE_CRC); - set_feature(&cpu->env, ARM_FEATURE_V8_RDM); - set_feature(&cpu->env, ARM_FEATURE_V8_FP16); - set_feature(&cpu->env, ARM_FEATURE_V8_FCMA); - cpu->ctr = 0x80038003; /* 32 byte I and D cacheline size, VIPT icache */ - cpu->dcz_blocksize = 7; /* 512 bytes */ -} + if (kvm_enabled()) { + kvm_arm_set_cpu_features_from_host(cpu); + } else { + aarch64_a57_initfn(obj); +#ifdef CONFIG_USER_ONLY + /* We don't set these in system emulation mode for the moment, + * since we don't correctly set the ID registers to advertise them, + * and in some cases they're only available in AArch64 and not AArch32, + * whereas the architecture requires them to be present in both if + * present in either. + */ + set_feature(&cpu->env, ARM_FEATURE_V8); + set_feature(&cpu->env, ARM_FEATURE_VFP4); + set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_AARCH64); + set_feature(&cpu->env, ARM_FEATURE_V8_AES); + set_feature(&cpu->env, ARM_FEATURE_V8_SHA1); + set_feature(&cpu->env, ARM_FEATURE_V8_SHA256); + set_feature(&cpu->env, ARM_FEATURE_V8_SHA512); + set_feature(&cpu->env, ARM_FEATURE_V8_SHA3); + set_feature(&cpu->env, ARM_FEATURE_V8_SM3); + set_feature(&cpu->env, ARM_FEATURE_V8_SM4); + set_feature(&cpu->env, ARM_FEATURE_V8_PMULL); + set_feature(&cpu->env, ARM_FEATURE_CRC); + set_feature(&cpu->env, ARM_FEATURE_V8_RDM); + set_feature(&cpu->env, ARM_FEATURE_V8_FP16); + set_feature(&cpu->env, ARM_FEATURE_V8_FCMA); + /* For usermode -cpu max we can use a larger and more efficient DCZ + * blocksize since we don't have to follow what the hardware does. + */ + cpu->ctr = 0x80038003; /* 32 byte I and D cacheline size, VIPT icache */ + cpu->dcz_blocksize = 7; /* 512 bytes */ #endif + } +} typedef struct ARMCPUInfo { const char *name; @@ -247,9 +269,7 @@ typedef struct ARMCPUInfo { static const ARMCPUInfo aarch64_cpus[] = { { .name = "cortex-a57", .initfn = aarch64_a57_initfn }, { .name = "cortex-a53", .initfn = aarch64_a53_initfn }, -#ifdef CONFIG_USER_ONLY - { .name = "any", .initfn = aarch64_any_initfn }, -#endif + { .name = "max", .initfn = aarch64_max_initfn }, { .name = NULL } }; @@ -366,3 +386,44 @@ static void aarch64_cpu_register_types(void) } type_init(aarch64_cpu_register_types) + +/* The manual says that when SVE is enabled and VQ is widened the + * implementation is allowed to zero the previously inaccessible + * portion of the registers. The corollary to that is that when + * SVE is enabled and VQ is narrowed we are also allowed to zero + * the now inaccessible portion of the registers. + * + * The intent of this is that no predicate bit beyond VQ is ever set. + * Which means that some operations on predicate registers themselves + * may operate on full uint64_t or even unrolled across the maximum + * uint64_t[4]. Performing 4 bits of host arithmetic unconditionally + * may well be cheaper than conditionals to restrict the operation + * to the relevant portion of a uint16_t[16]. + * + * TODO: Need to call this for changes to the real system registers + * and EL state changes. + */ +void aarch64_sve_narrow_vq(CPUARMState *env, unsigned vq) +{ + int i, j; + uint64_t pmask; + + assert(vq >= 1 && vq <= ARM_MAX_VQ); + + /* Zap the high bits of the zregs. */ + for (i = 0; i < 32; i++) { + memset(&env->vfp.zregs[i].d[2 * vq], 0, 16 * (ARM_MAX_VQ - vq)); + } + + /* Zap the high bits of the pregs and ffr. */ + pmask = 0; + if (vq & 3) { + pmask = ~(-1ULL << (16 * (vq & 3))); + } + for (j = vq / 4; j < ARM_MAX_VQ / 4; j++) { + for (i = 0; i < 17; ++i) { + env->vfp.pregs[i].p[j] &= pmask; + } + pmask = 0; + } +} diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 1219d0062b..ecc39ac295 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -33,6 +33,8 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = { static bool cap_has_mp_state; +static ARMHostCPUFeatures arm_host_cpu_features; + int kvm_arm_vcpu_init(CPUState *cs) { ARMCPU *cpu = ARM_CPU(cs); @@ -129,44 +131,27 @@ void kvm_arm_destroy_scratch_host_vcpu(int *fdarray) } } -static void kvm_arm_host_cpu_class_init(ObjectClass *oc, void *data) +void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu) { - ARMHostCPUClass *ahcc = ARM_HOST_CPU_CLASS(oc); + CPUARMState *env = &cpu->env; - /* All we really need to set up for the 'host' CPU - * is the feature bits -- we rely on the fact that the - * various ID register values in ARMCPU are only used for - * TCG CPUs. - */ - if (!kvm_arm_get_host_cpu_features(ahcc)) { - fprintf(stderr, "Failed to retrieve host CPU features!\n"); - abort(); + if (!arm_host_cpu_features.dtb_compatible) { + if (!kvm_enabled() || + !kvm_arm_get_host_cpu_features(&arm_host_cpu_features)) { + /* We can't report this error yet, so flag that we need to + * in arm_cpu_realizefn(). + */ + cpu->kvm_target = QEMU_KVM_ARM_TARGET_NONE; + cpu->host_cpu_probe_failed = true; + return; + } } -} -static void kvm_arm_host_cpu_initfn(Object *obj) -{ - ARMHostCPUClass *ahcc = ARM_HOST_CPU_GET_CLASS(obj); - ARMCPU *cpu = ARM_CPU(obj); - CPUARMState *env = &cpu->env; - - cpu->kvm_target = ahcc->target; - cpu->dtb_compatible = ahcc->dtb_compatible; - env->features = ahcc->features; + cpu->kvm_target = arm_host_cpu_features.target; + cpu->dtb_compatible = arm_host_cpu_features.dtb_compatible; + env->features = arm_host_cpu_features.features; } -static const TypeInfo host_arm_cpu_type_info = { - .name = TYPE_ARM_HOST_CPU, -#ifdef TARGET_AARCH64 - .parent = TYPE_AARCH64_CPU, -#else - .parent = TYPE_ARM_CPU, -#endif - .instance_init = kvm_arm_host_cpu_initfn, - .class_init = kvm_arm_host_cpu_class_init, - .class_size = sizeof(ARMHostCPUClass), -}; - int kvm_arch_init(MachineState *ms, KVMState *s) { /* For ARM interrupt delivery is always asynchronous, @@ -182,8 +167,6 @@ int kvm_arch_init(MachineState *ms, KVMState *s) cap_has_mp_state = kvm_check_extension(s, KVM_CAP_MP_STATE); - type_register_static(&host_arm_cpu_type_info); - return 0; } diff --git a/target/arm/kvm32.c b/target/arm/kvm32.c index f77c9c494b..1740cda47d 100644 --- a/target/arm/kvm32.c +++ b/target/arm/kvm32.c @@ -28,7 +28,7 @@ static inline void set_feature(uint64_t *features, int feature) *features |= 1ULL << feature; } -bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc) +bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) { /* Identify the feature bits corresponding to the host CPU, and * fill out the ARMHostCPUClass fields accordingly. To do this @@ -74,13 +74,13 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc) return false; } - ahcc->target = init.target; + ahcf->target = init.target; /* This is not strictly blessed by the device tree binding docs yet, * but in practice the kernel does not care about this string so * there is no point maintaining an KVM_ARM_TARGET_* -> string table. */ - ahcc->dtb_compatible = "arm,arm-v7"; + ahcf->dtb_compatible = "arm,arm-v7"; for (i = 0; i < ARRAY_SIZE(idregs); i++) { ret = ioctl(fdarray[2], KVM_GET_ONE_REG, &idregs[i]); @@ -132,7 +132,7 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc) set_feature(&features, ARM_FEATURE_VFP4); } - ahcc->features = features; + ahcf->features = features; return true; } diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c index ac728494a4..e0b8246283 100644 --- a/target/arm/kvm64.c +++ b/target/arm/kvm64.c @@ -443,7 +443,7 @@ static inline void unset_feature(uint64_t *features, int feature) *features &= ~(1ULL << feature); } -bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc) +bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) { /* Identify the feature bits corresponding to the host CPU, and * fill out the ARMHostCPUClass fields accordingly. To do this @@ -471,8 +471,8 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc) return false; } - ahcc->target = init.target; - ahcc->dtb_compatible = "arm,arm-v8"; + ahcf->target = init.target; + ahcf->dtb_compatible = "arm,arm-v8"; kvm_arm_destroy_scratch_host_vcpu(fdarray); @@ -486,7 +486,7 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc) set_feature(&features, ARM_FEATURE_AARCH64); set_feature(&features, ARM_FEATURE_PMU); - ahcc->features = features; + ahcf->features = features; return true; } diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index cfb7e5af72..1e2364007d 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -152,20 +152,16 @@ bool kvm_arm_create_scratch_host_vcpu(const uint32_t *cpus_to_try, void kvm_arm_destroy_scratch_host_vcpu(int *fdarray); #define TYPE_ARM_HOST_CPU "host-" TYPE_ARM_CPU -#define ARM_HOST_CPU_CLASS(klass) \ - OBJECT_CLASS_CHECK(ARMHostCPUClass, (klass), TYPE_ARM_HOST_CPU) -#define ARM_HOST_CPU_GET_CLASS(obj) \ - OBJECT_GET_CLASS(ARMHostCPUClass, (obj), TYPE_ARM_HOST_CPU) - -typedef struct ARMHostCPUClass { - /*< private >*/ - ARMCPUClass parent_class; - /*< public >*/ +/** + * ARMHostCPUFeatures: information about the host CPU (identified + * by asking the host kernel) + */ +typedef struct ARMHostCPUFeatures { uint64_t features; uint32_t target; const char *dtb_compatible; -} ARMHostCPUClass; +} ARMHostCPUFeatures; /** * kvm_arm_get_host_cpu_features: @@ -174,8 +170,16 @@ typedef struct ARMHostCPUClass { * Probe the capabilities of the host kernel's preferred CPU and fill * in the ARMHostCPUClass struct accordingly. */ -bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc); +bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf); +/** + * kvm_arm_set_cpu_features_from_host: + * @cpu: ARMCPU to set the features for + * + * Set up the ARMCPU struct fields up to match the information probed + * from the host CPU. + */ +void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu); /** * kvm_arm_sync_mpstate_to_kvm @@ -200,6 +204,15 @@ void kvm_arm_pmu_init(CPUState *cs); #else +static inline void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu) +{ + /* This should never actually be called in the "not KVM" case, + * but set up the fields to indicate an error anyway. + */ + cpu->kvm_target = QEMU_KVM_ARM_TARGET_NONE; + cpu->host_cpu_probe_failed = true; +} + static inline int kvm_arm_vgic_probe(void) { return 0; diff --git a/target/i386/Makefile.objs b/target/i386/Makefile.objs index f5c6ef20a7..04678f5503 100644 --- a/target/i386/Makefile.objs +++ b/target/i386/Makefile.objs @@ -5,7 +5,9 @@ obj-$(CONFIG_TCG) += int_helper.o mem_helper.o misc_helper.o mpx_helper.o obj-$(CONFIG_TCG) += seg_helper.o smm_helper.o svm_helper.o obj-$(CONFIG_SOFTMMU) += machine.o arch_memory_mapping.o arch_dump.o monitor.o obj-$(CONFIG_KVM) += kvm.o hyperv.o +obj-$(CONFIG_SEV) += sev.o obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o +obj-$(call lnot,$(CONFIG_SEV)) += sev-stub.o # HAX support ifdef CONFIG_WIN32 obj-$(CONFIG_HAX) += hax-all.o hax-mem.o hax-windows.o diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 2c04645cea..6bb4ce8719 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -26,6 +26,7 @@ #include "sysemu/hvf.h" #include "sysemu/cpus.h" #include "kvm_i386.h" +#include "sev_i386.h" #include "qemu/error-report.h" #include "qemu/option.h" @@ -173,7 +174,32 @@ #define L2_ITLB_4K_ASSOC 4 #define L2_ITLB_4K_ENTRIES 512 - +/* CPUID Leaf 0x14 constants: */ +#define INTEL_PT_MAX_SUBLEAF 0x1 +/* + * bit[00]: IA32_RTIT_CTL.CR3 filter can be set to 1 and IA32_RTIT_CR3_MATCH + * MSR can be accessed; + * bit[01]: Support Configurable PSB and Cycle-Accurate Mode; + * bit[02]: Support IP Filtering, TraceStop filtering, and preservation + * of Intel PT MSRs across warm reset; + * bit[03]: Support MTC timing packet and suppression of COFI-based packets; + */ +#define INTEL_PT_MINIMAL_EBX 0xf +/* + * bit[00]: Tracing can be enabled with IA32_RTIT_CTL.ToPA = 1 and + * IA32_RTIT_OUTPUT_BASE and IA32_RTIT_OUTPUT_MASK_PTRS MSRs can be + * accessed; + * bit[01]: ToPA tables can hold any number of output entries, up to the + * maximum allowed by the MaskOrTableOffset field of + * IA32_RTIT_OUTPUT_MASK_PTRS; + * bit[02]: Support Single-Range Output scheme; + */ +#define INTEL_PT_MINIMAL_ECX 0x7 +#define INTEL_PT_ADDR_RANGES_NUM 0x2 /* Number of configurable address ranges */ +#define INTEL_PT_ADDR_RANGES_NUM_MASK 0x3 +#define INTEL_PT_MTC_BITMAP (0x0249 << 16) /* Support ART(0,3,6,9) */ +#define INTEL_PT_CYCLE_BITMAP 0x1fff /* Support 0,2^(0~11) */ +#define INTEL_PT_PSB_BITMAP (0x003f << 16) /* Support 2K,4K,8K,16K,32K,64K */ static void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, uint32_t vendor2, uint32_t vendor3) @@ -359,6 +385,20 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { .cpuid_eax = KVM_CPUID_FEATURES, .cpuid_reg = R_EAX, .tcg_features = TCG_KVM_FEATURES, }, + [FEAT_KVM_HINTS] = { + .feat_names = { + "kvm-hint-dedicated", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, + .cpuid_eax = KVM_CPUID_FEATURES, .cpuid_reg = R_EDX, + .tcg_features = TCG_KVM_FEATURES, + }, [FEAT_HYPERV_EAX] = { .feat_names = { NULL /* hv_msr_vp_runtime_access */, NULL /* hv_msr_time_refcount_access */, @@ -428,7 +468,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { NULL, NULL, "mpx", NULL, "avx512f", "avx512dq", "rdseed", "adx", "smap", "avx512ifma", "pcommit", "clflushopt", - "clwb", NULL, "avx512pf", "avx512er", + "clwb", "intel-pt", "avx512pf", "avx512er", "avx512cd", "sha-ni", "avx512bw", "avx512vl", }, .cpuid_eax = 7, @@ -3453,6 +3493,27 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } break; } + case 0x14: { + /* Intel Processor Trace Enumeration */ + *eax = 0; + *ebx = 0; + *ecx = 0; + *edx = 0; + if (!(env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_INTEL_PT) || + !kvm_enabled()) { + break; + } + + if (count == 0) { + *eax = INTEL_PT_MAX_SUBLEAF; + *ebx = INTEL_PT_MINIMAL_EBX; + *ecx = INTEL_PT_MINIMAL_ECX; + } else if (count == 1) { + *eax = INTEL_PT_MTC_BITMAP | INTEL_PT_ADDR_RANGES_NUM; + *ebx = INTEL_PT_PSB_BITMAP | INTEL_PT_CYCLE_BITMAP; + } + break; + } case 0x40000000: /* * CPUID code in kvm_arch_init_vcpu() ignores stuff @@ -3612,6 +3673,13 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *ecx = 0; *edx = 0; break; + case 0x8000001F: + *eax = sev_enabled() ? 0x2 : 0; + *ebx = sev_get_cbit_position(); + *ebx |= sev_get_reduced_phys_bits() << 6; + *ecx = 0; + *edx = 0; + break; default: /* reserved values: zero */ *eax = 0; @@ -3645,6 +3713,7 @@ static void x86_cpu_reset(CPUState *s) cpu_x86_update_cr0(env, 0x60000010); env->a20_mask = ~0x0; env->smbase = 0x30000; + env->msr_smi_count = 0; env->idt.limit = 0xffff; env->gdt.limit = 0xffff; @@ -4041,6 +4110,11 @@ static void x86_cpu_expand_features(X86CPU *cpu, Error **errp) if (env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_SVM) { x86_cpu_adjust_level(cpu, &env->cpuid_min_xlevel, 0x8000000A); } + + /* SEV requires CPUID[0x8000001F] */ + if (sev_enabled()) { + x86_cpu_adjust_level(cpu, &env->cpuid_min_xlevel, 0x8000001F); + } } /* Set cpuid_*level* based on cpuid_min_*level, if not explicitly set */ @@ -4083,6 +4157,34 @@ static int x86_cpu_filter_features(X86CPU *cpu) } } + if ((env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_INTEL_PT) && + kvm_enabled()) { + KVMState *s = CPU(cpu)->kvm_state; + uint32_t eax_0 = kvm_arch_get_supported_cpuid(s, 0x14, 0, R_EAX); + uint32_t ebx_0 = kvm_arch_get_supported_cpuid(s, 0x14, 0, R_EBX); + uint32_t ecx_0 = kvm_arch_get_supported_cpuid(s, 0x14, 0, R_ECX); + uint32_t eax_1 = kvm_arch_get_supported_cpuid(s, 0x14, 1, R_EAX); + uint32_t ebx_1 = kvm_arch_get_supported_cpuid(s, 0x14, 1, R_EBX); + + if (!eax_0 || + ((ebx_0 & INTEL_PT_MINIMAL_EBX) != INTEL_PT_MINIMAL_EBX) || + ((ecx_0 & INTEL_PT_MINIMAL_ECX) != INTEL_PT_MINIMAL_ECX) || + ((eax_1 & INTEL_PT_MTC_BITMAP) != INTEL_PT_MTC_BITMAP) || + ((eax_1 & INTEL_PT_ADDR_RANGES_NUM_MASK) < + INTEL_PT_ADDR_RANGES_NUM) || + ((ebx_1 & (INTEL_PT_PSB_BITMAP | INTEL_PT_CYCLE_BITMAP)) != + (INTEL_PT_PSB_BITMAP | INTEL_PT_CYCLE_BITMAP))) { + /* + * Processor Trace capabilities aren't configurable, so if the + * host can't emulate the capabilities we report on + * cpu_x86_cpuid(), intel-pt can't be enabled on the current host. + */ + env->features[FEAT_7_0_EBX] &= ~CPUID_7_0_EBX_INTEL_PT; + cpu->filtered_features[FEAT_7_0_EBX] |= CPUID_7_0_EBX_INTEL_PT; + rv = 1; + } + } + return rv; } diff --git a/target/i386/cpu.h b/target/i386/cpu.h index faf39ec1ce..2e2bab5ff3 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1,3 +1,4 @@ + /* * i386 virtual CPU header * @@ -359,6 +360,7 @@ typedef enum X86Seg { #define MSR_P6_PERFCTR0 0xc1 #define MSR_IA32_SMBASE 0x9e +#define MSR_SMI_COUNT 0x34 #define MSR_MTRRcap 0xfe #define MSR_MTRRcap_VCNT 8 #define MSR_MTRRcap_FIXRANGE_SUPPORT (1 << 8) @@ -415,6 +417,21 @@ typedef enum X86Seg { #define MSR_MC0_ADDR 0x402 #define MSR_MC0_MISC 0x403 +#define MSR_IA32_RTIT_OUTPUT_BASE 0x560 +#define MSR_IA32_RTIT_OUTPUT_MASK 0x561 +#define MSR_IA32_RTIT_CTL 0x570 +#define MSR_IA32_RTIT_STATUS 0x571 +#define MSR_IA32_RTIT_CR3_MATCH 0x572 +#define MSR_IA32_RTIT_ADDR0_A 0x580 +#define MSR_IA32_RTIT_ADDR0_B 0x581 +#define MSR_IA32_RTIT_ADDR1_A 0x582 +#define MSR_IA32_RTIT_ADDR1_B 0x583 +#define MSR_IA32_RTIT_ADDR2_A 0x584 +#define MSR_IA32_RTIT_ADDR2_B 0x585 +#define MSR_IA32_RTIT_ADDR3_A 0x586 +#define MSR_IA32_RTIT_ADDR3_B 0x587 +#define MAX_RTIT_ADDRS 8 + #define MSR_EFER 0xc0000080 #define MSR_EFER_SCE (1 << 0) @@ -471,6 +488,7 @@ typedef enum FeatureWord { FEAT_8000_0008_EBX, /* CPUID[8000_0008].EBX */ FEAT_C000_0001_EDX, /* CPUID[C000_0001].EDX */ FEAT_KVM, /* CPUID[4000_0001].EAX (KVM_CPUID_FEATURES) */ + FEAT_KVM_HINTS, /* CPUID[4000_0001].EDX */ FEAT_HYPERV_EAX, /* CPUID[4000_0003].EAX */ FEAT_HYPERV_EBX, /* CPUID[4000_0003].EBX */ FEAT_HYPERV_EDX, /* CPUID[4000_0003].EDX */ @@ -640,6 +658,7 @@ typedef uint32_t FeatureWordArray[FEATURE_WORDS]; #define CPUID_7_0_EBX_PCOMMIT (1U << 22) /* Persistent Commit */ #define CPUID_7_0_EBX_CLFLUSHOPT (1U << 23) /* Flush a Cache Line Optimized */ #define CPUID_7_0_EBX_CLWB (1U << 24) /* Cache Line Write Back */ +#define CPUID_7_0_EBX_INTEL_PT (1U << 25) /* Intel Processor Trace */ #define CPUID_7_0_EBX_AVX512PF (1U << 26) /* AVX-512 Prefetch */ #define CPUID_7_0_EBX_AVX512ER (1U << 27) /* AVX-512 Exponential and Reciprocal */ #define CPUID_7_0_EBX_AVX512CD (1U << 28) /* AVX-512 Conflict Detection */ @@ -666,6 +685,8 @@ typedef uint32_t FeatureWordArray[FEATURE_WORDS]; #define CPUID_7_0_EDX_AVX512_4FMAPS (1U << 3) /* AVX512 Multiply Accumulation Single Precision */ #define CPUID_7_0_EDX_SPEC_CTRL (1U << 26) /* Speculation Control */ +#define KVM_HINTS_DEDICATED (1U << 0) + #define CPUID_8000_0008_EBX_IBPB (1U << 12) /* Indirect Branch Prediction Barrier */ #define CPUID_XSAVE_XSAVEOPT (1U << 0) @@ -1123,6 +1144,7 @@ typedef struct CPUX86State { uint64_t pat; uint32_t smbase; + uint64_t msr_smi_count; uint32_t pkru; @@ -1153,6 +1175,13 @@ typedef struct CPUX86State { uint64_t msr_hv_stimer_config[HV_STIMER_COUNT]; uint64_t msr_hv_stimer_count[HV_STIMER_COUNT]; + uint64_t msr_rtit_ctrl; + uint64_t msr_rtit_status; + uint64_t msr_rtit_output_base; + uint64_t msr_rtit_output_mask; + uint64_t msr_rtit_cr3_match; + uint64_t msr_rtit_addrs[MAX_RTIT_ADDRS]; + /* exception/interrupt handling */ int error_code; int exception_is_int; diff --git a/target/i386/kvm.c b/target/i386/kvm.c index ad4b159b28..d23fff12f5 100644 --- a/target/i386/kvm.c +++ b/target/i386/kvm.c @@ -92,6 +92,7 @@ static bool has_msr_hv_stimer; static bool has_msr_hv_frequencies; static bool has_msr_xss; static bool has_msr_spec_ctrl; +static bool has_msr_smi_count; static uint32_t has_architectural_pmu_version; static uint32_t num_architectural_pmu_gp_counters; @@ -383,6 +384,9 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, if (!kvm_irqchip_in_kernel()) { ret &= ~(1U << KVM_FEATURE_PV_UNHALT); } + } else if (function == KVM_CPUID_FEATURES && reg == R_EDX) { + ret |= KVM_HINTS_DEDICATED; + found = 1; } /* fallback for older kernels */ @@ -801,6 +805,7 @@ int kvm_arch_init_vcpu(CPUState *cs) c = &cpuid_data.entries[cpuid_i++]; c->function = KVM_CPUID_FEATURES | kvm_base; c->eax = env->features[FEAT_KVM]; + c->edx = env->features[FEAT_KVM_HINTS]; } cpu_x86_cpuid(env, 0, 0, &limit, &unused, &unused, &unused); @@ -865,6 +870,29 @@ int kvm_arch_init_vcpu(CPUState *cs) c = &cpuid_data.entries[cpuid_i++]; } break; + case 0x14: { + uint32_t times; + + c->function = i; + c->index = 0; + c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; + cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); + times = c->eax; + + for (j = 1; j <= times; ++j) { + if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { + fprintf(stderr, "cpuid_data is full, no space for " + "cpuid(eax:0x14,ecx:0x%x)\n", j); + abort(); + } + c = &cpuid_data.entries[cpuid_i++]; + c->function = i; + c->index = j; + c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; + cpu_x86_cpuid(env, i, j, &c->eax, &c->ebx, &c->ecx, &c->edx); + } + break; + } default: c->function = i; c->flags = 0; @@ -1124,6 +1152,9 @@ static int kvm_get_supported_msrs(KVMState *s) case MSR_IA32_SMBASE: has_msr_smbase = true; break; + case MSR_SMI_COUNT: + has_msr_smi_count = true; + break; case MSR_IA32_MISC_ENABLE: has_msr_misc_enable = true; break; @@ -1633,6 +1664,9 @@ static int kvm_put_msrs(X86CPU *cpu, int level) if (has_msr_smbase) { kvm_msr_entry_add(cpu, MSR_IA32_SMBASE, env->smbase); } + if (has_msr_smi_count) { + kvm_msr_entry_add(cpu, MSR_SMI_COUNT, env->msr_smi_count); + } if (has_msr_bndcfgs) { kvm_msr_entry_add(cpu, MSR_IA32_BNDCFGS, env->msr_bndcfgs); } @@ -1788,6 +1822,25 @@ static int kvm_put_msrs(X86CPU *cpu, int level) kvm_msr_entry_add(cpu, MSR_MTRRphysMask(i), mask); } } + if (env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_INTEL_PT) { + int addr_num = kvm_arch_get_supported_cpuid(kvm_state, + 0x14, 1, R_EAX) & 0x7; + + kvm_msr_entry_add(cpu, MSR_IA32_RTIT_CTL, + env->msr_rtit_ctrl); + kvm_msr_entry_add(cpu, MSR_IA32_RTIT_STATUS, + env->msr_rtit_status); + kvm_msr_entry_add(cpu, MSR_IA32_RTIT_OUTPUT_BASE, + env->msr_rtit_output_base); + kvm_msr_entry_add(cpu, MSR_IA32_RTIT_OUTPUT_MASK, + env->msr_rtit_output_mask); + kvm_msr_entry_add(cpu, MSR_IA32_RTIT_CR3_MATCH, + env->msr_rtit_cr3_match); + for (i = 0; i < addr_num; i++) { + kvm_msr_entry_add(cpu, MSR_IA32_RTIT_ADDR0_A + i, + env->msr_rtit_addrs[i]); + } + } /* Note: MSR_IA32_FEATURE_CONTROL is written separately, see * kvm_put_msr_feature_control. */ @@ -1979,6 +2032,9 @@ static int kvm_get_msrs(X86CPU *cpu) if (has_msr_smbase) { kvm_msr_entry_add(cpu, MSR_IA32_SMBASE, 0); } + if (has_msr_smi_count) { + kvm_msr_entry_add(cpu, MSR_SMI_COUNT, 0); + } if (has_msr_feature_control) { kvm_msr_entry_add(cpu, MSR_IA32_FEATURE_CONTROL, 0); } @@ -2101,6 +2157,20 @@ static int kvm_get_msrs(X86CPU *cpu) } } + if (env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_INTEL_PT) { + int addr_num = + kvm_arch_get_supported_cpuid(kvm_state, 0x14, 1, R_EAX) & 0x7; + + kvm_msr_entry_add(cpu, MSR_IA32_RTIT_CTL, 0); + kvm_msr_entry_add(cpu, MSR_IA32_RTIT_STATUS, 0); + kvm_msr_entry_add(cpu, MSR_IA32_RTIT_OUTPUT_BASE, 0); + kvm_msr_entry_add(cpu, MSR_IA32_RTIT_OUTPUT_MASK, 0); + kvm_msr_entry_add(cpu, MSR_IA32_RTIT_CR3_MATCH, 0); + for (i = 0; i < addr_num; i++) { + kvm_msr_entry_add(cpu, MSR_IA32_RTIT_ADDR0_A + i, 0); + } + } + ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_MSRS, cpu->kvm_msr_buf); if (ret < 0) { return ret; @@ -2205,6 +2275,9 @@ static int kvm_get_msrs(X86CPU *cpu) case MSR_IA32_SMBASE: env->smbase = msrs[i].data; break; + case MSR_SMI_COUNT: + env->msr_smi_count = msrs[i].data; + break; case MSR_IA32_FEATURE_CONTROL: env->msr_ia32_feature_control = msrs[i].data; break; @@ -2341,6 +2414,24 @@ static int kvm_get_msrs(X86CPU *cpu) case MSR_IA32_SPEC_CTRL: env->spec_ctrl = msrs[i].data; break; + case MSR_IA32_RTIT_CTL: + env->msr_rtit_ctrl = msrs[i].data; + break; + case MSR_IA32_RTIT_STATUS: + env->msr_rtit_status = msrs[i].data; + break; + case MSR_IA32_RTIT_OUTPUT_BASE: + env->msr_rtit_output_base = msrs[i].data; + break; + case MSR_IA32_RTIT_OUTPUT_MASK: + env->msr_rtit_output_mask = msrs[i].data; + break; + case MSR_IA32_RTIT_CR3_MATCH: + env->msr_rtit_cr3_match = msrs[i].data; + break; + case MSR_IA32_RTIT_ADDR0_A ... MSR_IA32_RTIT_ADDR3_B: + env->msr_rtit_addrs[index - MSR_IA32_RTIT_ADDR0_A] = msrs[i].data; + break; } } diff --git a/target/i386/machine.c b/target/i386/machine.c index 361c05aedf..bd2d82e91b 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -395,6 +395,25 @@ static const VMStateDescription vmstate_msr_tsc_adjust = { } }; +static bool msr_smi_count_needed(void *opaque) +{ + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; + + return env->msr_smi_count != 0; +} + +static const VMStateDescription vmstate_msr_smi_count = { + .name = "cpu/msr_smi_count", + .version_id = 1, + .minimum_version_id = 1, + .needed = msr_smi_count_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT64(env.msr_smi_count, X86CPU), + VMSTATE_END_OF_LIST() + } +}; + static bool tscdeadline_needed(void *opaque) { X86CPU *cpu = opaque; @@ -837,6 +856,43 @@ static const VMStateDescription vmstate_spec_ctrl = { } }; +static bool intel_pt_enable_needed(void *opaque) +{ + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; + int i; + + if (env->msr_rtit_ctrl || env->msr_rtit_status || + env->msr_rtit_output_base || env->msr_rtit_output_mask || + env->msr_rtit_cr3_match) { + return true; + } + + for (i = 0; i < MAX_RTIT_ADDRS; i++) { + if (env->msr_rtit_addrs[i]) { + return true; + } + } + + return false; +} + +static const VMStateDescription vmstate_msr_intel_pt = { + .name = "cpu/intel_pt", + .version_id = 1, + .minimum_version_id = 1, + .needed = intel_pt_enable_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT64(env.msr_rtit_ctrl, X86CPU), + VMSTATE_UINT64(env.msr_rtit_status, X86CPU), + VMSTATE_UINT64(env.msr_rtit_output_base, X86CPU), + VMSTATE_UINT64(env.msr_rtit_output_mask, X86CPU), + VMSTATE_UINT64(env.msr_rtit_cr3_match, X86CPU), + VMSTATE_UINT64_ARRAY(env.msr_rtit_addrs, X86CPU, MAX_RTIT_ADDRS), + VMSTATE_END_OF_LIST() + } +}; + VMStateDescription vmstate_x86_cpu = { .name = "cpu", .version_id = 12, @@ -952,11 +1008,13 @@ VMStateDescription vmstate_x86_cpu = { &vmstate_avx512, &vmstate_xss, &vmstate_tsc_khz, + &vmstate_msr_smi_count, #ifdef TARGET_X86_64 &vmstate_pkru, #endif &vmstate_spec_ctrl, &vmstate_mcg_ext_ctl, + &vmstate_msr_intel_pt, NULL } }; diff --git a/target/i386/monitor.c b/target/i386/monitor.c index 75429129fd..011419eba2 100644 --- a/target/i386/monitor.c +++ b/target/i386/monitor.c @@ -29,7 +29,11 @@ #include "qapi/qmp/qdict.h" #include "hw/i386/pc.h" #include "sysemu/kvm.h" +#include "sysemu/sev.h" #include "hmp.h" +#include "qapi/error.h" +#include "sev_i386.h" +#include "qapi/qapi-commands-misc.h" static void print_pte(Monitor *mon, CPUArchState *env, hwaddr addr, @@ -661,3 +665,65 @@ void hmp_info_io_apic(Monitor *mon, const QDict *qdict) ioapic_dump_state(mon, qdict); } } + +SevInfo *qmp_query_sev(Error **errp) +{ + SevInfo *info; + + info = sev_get_info(); + if (!info) { + error_setg(errp, "SEV feature is not available"); + return NULL; + } + + return info; +} + +void hmp_info_sev(Monitor *mon, const QDict *qdict) +{ + SevInfo *info = sev_get_info(); + + if (info && info->enabled) { + monitor_printf(mon, "handle: %d\n", info->handle); + monitor_printf(mon, "state: %s\n", SevState_str(info->state)); + monitor_printf(mon, "build: %d\n", info->build_id); + monitor_printf(mon, "api version: %d.%d\n", + info->api_major, info->api_minor); + monitor_printf(mon, "debug: %s\n", + info->policy & SEV_POLICY_NODBG ? "off" : "on"); + monitor_printf(mon, "key-sharing: %s\n", + info->policy & SEV_POLICY_NOKS ? "off" : "on"); + } else { + monitor_printf(mon, "SEV is not enabled\n"); + } +} + +SevLaunchMeasureInfo *qmp_query_sev_launch_measure(Error **errp) +{ + char *data; + SevLaunchMeasureInfo *info; + + data = sev_get_launch_measurement(); + if (!data) { + error_setg(errp, "Measurement is not available"); + return NULL; + } + + info = g_malloc0(sizeof(*info)); + info->data = data; + + return info; +} + +SevCapability *qmp_query_sev_capabilities(Error **errp) +{ + SevCapability *data; + + data = sev_get_capabilities(); + if (!data) { + error_setg(errp, "SEV feature is not available"); + return NULL; + } + + return data; +} diff --git a/target/i386/sev-stub.c b/target/i386/sev-stub.c new file mode 100644 index 0000000000..59a003a4eb --- /dev/null +++ b/target/i386/sev-stub.c @@ -0,0 +1,51 @@ +/* + * QEMU SEV stub + * + * Copyright Advanced Micro Devices 2018 + * + * Authors: + * Brijesh Singh <brijesh.singh@amd.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "sev_i386.h" + +SevInfo *sev_get_info(void) +{ + return NULL; +} + +bool sev_enabled(void) +{ + return false; +} + +uint64_t sev_get_me_mask(void) +{ + return ~0; +} + +uint32_t sev_get_cbit_position(void) +{ + return 0; +} + +uint32_t sev_get_reduced_phys_bits(void) +{ + return 0; +} + +char *sev_get_launch_measurement(void) +{ + return NULL; +} + +SevCapability *sev_get_capabilities(void) +{ + return NULL; +} diff --git a/target/i386/sev.c b/target/i386/sev.c new file mode 100644 index 0000000000..019d84cef2 --- /dev/null +++ b/target/i386/sev.c @@ -0,0 +1,811 @@ +/* + * QEMU SEV support + * + * Copyright Advanced Micro Devices 2016-2018 + * + * Author: + * Brijesh Singh <brijesh.singh@amd.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include <linux/kvm.h> +#include <linux/psp-sev.h> + +#include <sys/ioctl.h> + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qom/object_interfaces.h" +#include "qemu/base64.h" +#include "sysemu/kvm.h" +#include "sev_i386.h" +#include "sysemu/sysemu.h" +#include "trace.h" +#include "migration/blocker.h" + +#define DEFAULT_GUEST_POLICY 0x1 /* disable debug */ +#define DEFAULT_SEV_DEVICE "/dev/sev" + +static SEVState *sev_state; +static Error *sev_mig_blocker; + +static const char *const sev_fw_errlist[] = { + "", + "Platform state is invalid", + "Guest state is invalid", + "Platform configuration is invalid", + "Buffer too small", + "Platform is already owned", + "Certificate is invalid", + "Policy is not allowed", + "Guest is not active", + "Invalid address", + "Bad signature", + "Bad measurement", + "Asid is already owned", + "Invalid ASID", + "WBINVD is required", + "DF_FLUSH is required", + "Guest handle is invalid", + "Invalid command", + "Guest is active", + "Hardware error", + "Hardware unsafe", + "Feature not supported", + "Invalid parameter" +}; + +#define SEV_FW_MAX_ERROR ARRAY_SIZE(sev_fw_errlist) + +static int +sev_ioctl(int fd, int cmd, void *data, int *error) +{ + int r; + struct kvm_sev_cmd input; + + memset(&input, 0x0, sizeof(input)); + + input.id = cmd; + input.sev_fd = fd; + input.data = (__u64)(unsigned long)data; + + r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_OP, &input); + + if (error) { + *error = input.error; + } + + return r; +} + +static int +sev_platform_ioctl(int fd, int cmd, void *data, int *error) +{ + int r; + struct sev_issue_cmd arg; + + arg.cmd = cmd; + arg.data = (unsigned long)data; + r = ioctl(fd, SEV_ISSUE_CMD, &arg); + if (error) { + *error = arg.error; + } + + return r; +} + +static const char * +fw_error_to_str(int code) +{ + if (code < 0 || code >= SEV_FW_MAX_ERROR) { + return "unknown error"; + } + + return sev_fw_errlist[code]; +} + +static bool +sev_check_state(SevState state) +{ + assert(sev_state); + return sev_state->state == state ? true : false; +} + +static void +sev_set_guest_state(SevState new_state) +{ + assert(new_state < SEV_STATE__MAX); + assert(sev_state); + + trace_kvm_sev_change_state(SevState_str(sev_state->state), + SevState_str(new_state)); + sev_state->state = new_state; +} + +static void +sev_ram_block_added(RAMBlockNotifier *n, void *host, size_t size) +{ + int r; + struct kvm_enc_region range; + + range.addr = (__u64)(unsigned long)host; + range.size = size; + + trace_kvm_memcrypt_register_region(host, size); + r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_REG_REGION, &range); + if (r) { + error_report("%s: failed to register region (%p+%#zx) error '%s'", + __func__, host, size, strerror(errno)); + exit(1); + } +} + +static void +sev_ram_block_removed(RAMBlockNotifier *n, void *host, size_t size) +{ + int r; + struct kvm_enc_region range; + + range.addr = (__u64)(unsigned long)host; + range.size = size; + + trace_kvm_memcrypt_unregister_region(host, size); + r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_UNREG_REGION, &range); + if (r) { + error_report("%s: failed to unregister region (%p+%#zx)", + __func__, host, size); + } +} + +static struct RAMBlockNotifier sev_ram_notifier = { + .ram_block_added = sev_ram_block_added, + .ram_block_removed = sev_ram_block_removed, +}; + +static void +qsev_guest_finalize(Object *obj) +{ +} + +static char * +qsev_guest_get_session_file(Object *obj, Error **errp) +{ + QSevGuestInfo *s = QSEV_GUEST_INFO(obj); + + return s->session_file ? g_strdup(s->session_file) : NULL; +} + +static void +qsev_guest_set_session_file(Object *obj, const char *value, Error **errp) +{ + QSevGuestInfo *s = QSEV_GUEST_INFO(obj); + + s->session_file = g_strdup(value); +} + +static char * +qsev_guest_get_dh_cert_file(Object *obj, Error **errp) +{ + QSevGuestInfo *s = QSEV_GUEST_INFO(obj); + + return g_strdup(s->dh_cert_file); +} + +static void +qsev_guest_set_dh_cert_file(Object *obj, const char *value, Error **errp) +{ + QSevGuestInfo *s = QSEV_GUEST_INFO(obj); + + s->dh_cert_file = g_strdup(value); +} + +static char * +qsev_guest_get_sev_device(Object *obj, Error **errp) +{ + QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); + + return g_strdup(sev->sev_device); +} + +static void +qsev_guest_set_sev_device(Object *obj, const char *value, Error **errp) +{ + QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); + + sev->sev_device = g_strdup(value); +} + +static void +qsev_guest_class_init(ObjectClass *oc, void *data) +{ + object_class_property_add_str(oc, "sev-device", + qsev_guest_get_sev_device, + qsev_guest_set_sev_device, + NULL); + object_class_property_set_description(oc, "sev-device", + "SEV device to use", NULL); + object_class_property_add_str(oc, "dh-cert-file", + qsev_guest_get_dh_cert_file, + qsev_guest_set_dh_cert_file, + NULL); + object_class_property_set_description(oc, "dh-cert-file", + "guest owners DH certificate (encoded with base64)", NULL); + object_class_property_add_str(oc, "session-file", + qsev_guest_get_session_file, + qsev_guest_set_session_file, + NULL); + object_class_property_set_description(oc, "session-file", + "guest owners session parameters (encoded with base64)", NULL); +} + +static void +qsev_guest_set_handle(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); + uint32_t value; + + visit_type_uint32(v, name, &value, errp); + sev->handle = value; +} + +static void +qsev_guest_set_policy(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); + uint32_t value; + + visit_type_uint32(v, name, &value, errp); + sev->policy = value; +} + +static void +qsev_guest_set_cbitpos(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); + uint32_t value; + + visit_type_uint32(v, name, &value, errp); + sev->cbitpos = value; +} + +static void +qsev_guest_set_reduced_phys_bits(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); + uint32_t value; + + visit_type_uint32(v, name, &value, errp); + sev->reduced_phys_bits = value; +} + +static void +qsev_guest_get_policy(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint32_t value; + QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); + + value = sev->policy; + visit_type_uint32(v, name, &value, errp); +} + +static void +qsev_guest_get_handle(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint32_t value; + QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); + + value = sev->handle; + visit_type_uint32(v, name, &value, errp); +} + +static void +qsev_guest_get_cbitpos(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint32_t value; + QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); + + value = sev->cbitpos; + visit_type_uint32(v, name, &value, errp); +} + +static void +qsev_guest_get_reduced_phys_bits(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint32_t value; + QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); + + value = sev->reduced_phys_bits; + visit_type_uint32(v, name, &value, errp); +} + +static void +qsev_guest_init(Object *obj) +{ + QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); + + sev->sev_device = g_strdup(DEFAULT_SEV_DEVICE); + sev->policy = DEFAULT_GUEST_POLICY; + object_property_add(obj, "policy", "uint32", qsev_guest_get_policy, + qsev_guest_set_policy, NULL, NULL, NULL); + object_property_add(obj, "handle", "uint32", qsev_guest_get_handle, + qsev_guest_set_handle, NULL, NULL, NULL); + object_property_add(obj, "cbitpos", "uint32", qsev_guest_get_cbitpos, + qsev_guest_set_cbitpos, NULL, NULL, NULL); + object_property_add(obj, "reduced-phys-bits", "uint32", + qsev_guest_get_reduced_phys_bits, + qsev_guest_set_reduced_phys_bits, NULL, NULL, NULL); +} + +/* sev guest info */ +static const TypeInfo qsev_guest_info = { + .parent = TYPE_OBJECT, + .name = TYPE_QSEV_GUEST_INFO, + .instance_size = sizeof(QSevGuestInfo), + .instance_finalize = qsev_guest_finalize, + .class_size = sizeof(QSevGuestInfoClass), + .class_init = qsev_guest_class_init, + .instance_init = qsev_guest_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + +static QSevGuestInfo * +lookup_sev_guest_info(const char *id) +{ + Object *obj; + QSevGuestInfo *info; + + obj = object_resolve_path_component(object_get_objects_root(), id); + if (!obj) { + return NULL; + } + + info = (QSevGuestInfo *) + object_dynamic_cast(obj, TYPE_QSEV_GUEST_INFO); + if (!info) { + return NULL; + } + + return info; +} + +bool +sev_enabled(void) +{ + return sev_state ? true : false; +} + +uint64_t +sev_get_me_mask(void) +{ + return sev_state ? sev_state->me_mask : ~0; +} + +uint32_t +sev_get_cbit_position(void) +{ + return sev_state ? sev_state->cbitpos : 0; +} + +uint32_t +sev_get_reduced_phys_bits(void) +{ + return sev_state ? sev_state->reduced_phys_bits : 0; +} + +SevInfo * +sev_get_info(void) +{ + SevInfo *info; + + info = g_new0(SevInfo, 1); + info->enabled = sev_state ? true : false; + + if (info->enabled) { + info->api_major = sev_state->api_major; + info->api_minor = sev_state->api_minor; + info->build_id = sev_state->build_id; + info->policy = sev_state->policy; + info->state = sev_state->state; + info->handle = sev_state->handle; + } + + return info; +} + +static int +sev_get_pdh_info(int fd, guchar **pdh, size_t *pdh_len, guchar **cert_chain, + size_t *cert_chain_len) +{ + guchar *pdh_data, *cert_chain_data; + struct sev_user_data_pdh_cert_export export = {}; + int err, r; + + /* query the certificate length */ + r = sev_platform_ioctl(fd, SEV_PDH_CERT_EXPORT, &export, &err); + if (r < 0) { + if (err != SEV_RET_INVALID_LEN) { + error_report("failed to export PDH cert ret=%d fw_err=%d (%s)", + r, err, fw_error_to_str(err)); + return 1; + } + } + + pdh_data = g_new(guchar, export.pdh_cert_len); + cert_chain_data = g_new(guchar, export.cert_chain_len); + export.pdh_cert_address = (unsigned long)pdh_data; + export.cert_chain_address = (unsigned long)cert_chain_data; + + r = sev_platform_ioctl(fd, SEV_PDH_CERT_EXPORT, &export, &err); + if (r < 0) { + error_report("failed to export PDH cert ret=%d fw_err=%d (%s)", + r, err, fw_error_to_str(err)); + goto e_free; + } + + *pdh = pdh_data; + *pdh_len = export.pdh_cert_len; + *cert_chain = cert_chain_data; + *cert_chain_len = export.cert_chain_len; + return 0; + +e_free: + g_free(pdh_data); + g_free(cert_chain_data); + return 1; +} + +SevCapability * +sev_get_capabilities(void) +{ + SevCapability *cap; + guchar *pdh_data, *cert_chain_data; + size_t pdh_len = 0, cert_chain_len = 0; + uint32_t ebx; + int fd; + + fd = open(DEFAULT_SEV_DEVICE, O_RDWR); + if (fd < 0) { + error_report("%s: Failed to open %s '%s'", __func__, + DEFAULT_SEV_DEVICE, strerror(errno)); + return NULL; + } + + if (sev_get_pdh_info(fd, &pdh_data, &pdh_len, + &cert_chain_data, &cert_chain_len)) { + return NULL; + } + + cap = g_new0(SevCapability, 1); + cap->pdh = g_base64_encode(pdh_data, pdh_len); + cap->cert_chain = g_base64_encode(cert_chain_data, cert_chain_len); + + host_cpuid(0x8000001F, 0, NULL, &ebx, NULL, NULL); + cap->cbitpos = ebx & 0x3f; + + /* + * When SEV feature is enabled, we loose one bit in guest physical + * addressing. + */ + cap->reduced_phys_bits = 1; + + g_free(pdh_data); + g_free(cert_chain_data); + + close(fd); + return cap; +} + +static int +sev_read_file_base64(const char *filename, guchar **data, gsize *len) +{ + gsize sz; + gchar *base64; + GError *error = NULL; + + if (!g_file_get_contents(filename, &base64, &sz, &error)) { + error_report("failed to read '%s' (%s)", filename, error->message); + return -1; + } + + *data = g_base64_decode(base64, len); + return 0; +} + +static int +sev_launch_start(SEVState *s) +{ + gsize sz; + int ret = 1; + int fw_error; + QSevGuestInfo *sev = s->sev_info; + struct kvm_sev_launch_start *start; + guchar *session = NULL, *dh_cert = NULL; + + start = g_new0(struct kvm_sev_launch_start, 1); + + start->handle = object_property_get_int(OBJECT(sev), "handle", + &error_abort); + start->policy = object_property_get_int(OBJECT(sev), "policy", + &error_abort); + if (sev->session_file) { + if (sev_read_file_base64(sev->session_file, &session, &sz) < 0) { + return 1; + } + start->session_uaddr = (unsigned long)session; + start->session_len = sz; + } + + if (sev->dh_cert_file) { + if (sev_read_file_base64(sev->dh_cert_file, &dh_cert, &sz) < 0) { + return 1; + } + start->dh_uaddr = (unsigned long)dh_cert; + start->dh_len = sz; + } + + trace_kvm_sev_launch_start(start->policy, session, dh_cert); + ret = sev_ioctl(s->sev_fd, KVM_SEV_LAUNCH_START, start, &fw_error); + if (ret < 0) { + error_report("%s: LAUNCH_START ret=%d fw_error=%d '%s'", + __func__, ret, fw_error, fw_error_to_str(fw_error)); + return 1; + } + + object_property_set_int(OBJECT(sev), start->handle, "handle", + &error_abort); + sev_set_guest_state(SEV_STATE_LAUNCH_UPDATE); + s->handle = start->handle; + s->policy = start->policy; + + g_free(start); + g_free(session); + g_free(dh_cert); + + return 0; +} + +static int +sev_launch_update_data(uint8_t *addr, uint64_t len) +{ + int ret, fw_error; + struct kvm_sev_launch_update_data update; + + if (!addr || !len) { + return 1; + } + + update.uaddr = (__u64)(unsigned long)addr; + update.len = len; + trace_kvm_sev_launch_update_data(addr, len); + ret = sev_ioctl(sev_state->sev_fd, KVM_SEV_LAUNCH_UPDATE_DATA, + &update, &fw_error); + if (ret) { + error_report("%s: LAUNCH_UPDATE ret=%d fw_error=%d '%s'", + __func__, ret, fw_error, fw_error_to_str(fw_error)); + } + + return ret; +} + +static void +sev_launch_get_measure(Notifier *notifier, void *unused) +{ + int ret, error; + guchar *data; + SEVState *s = sev_state; + struct kvm_sev_launch_measure *measurement; + + if (!sev_check_state(SEV_STATE_LAUNCH_UPDATE)) { + return; + } + + measurement = g_new0(struct kvm_sev_launch_measure, 1); + + /* query the measurement blob length */ + ret = sev_ioctl(sev_state->sev_fd, KVM_SEV_LAUNCH_MEASURE, + measurement, &error); + if (!measurement->len) { + error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'", + __func__, ret, error, fw_error_to_str(errno)); + goto free_measurement; + } + + data = g_new0(guchar, measurement->len); + measurement->uaddr = (unsigned long)data; + + /* get the measurement blob */ + ret = sev_ioctl(sev_state->sev_fd, KVM_SEV_LAUNCH_MEASURE, + measurement, &error); + if (ret) { + error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'", + __func__, ret, error, fw_error_to_str(errno)); + goto free_data; + } + + sev_set_guest_state(SEV_STATE_LAUNCH_SECRET); + + /* encode the measurement value and emit the event */ + s->measurement = g_base64_encode(data, measurement->len); + trace_kvm_sev_launch_measurement(s->measurement); + +free_data: + g_free(data); +free_measurement: + g_free(measurement); +} + +char * +sev_get_launch_measurement(void) +{ + if (sev_state && + sev_state->state >= SEV_STATE_LAUNCH_SECRET) { + return g_strdup(sev_state->measurement); + } + + return NULL; +} + +static Notifier sev_machine_done_notify = { + .notify = sev_launch_get_measure, +}; + +static void +sev_launch_finish(SEVState *s) +{ + int ret, error; + Error *local_err = NULL; + + trace_kvm_sev_launch_finish(); + ret = sev_ioctl(sev_state->sev_fd, KVM_SEV_LAUNCH_FINISH, 0, &error); + if (ret) { + error_report("%s: LAUNCH_FINISH ret=%d fw_error=%d '%s'", + __func__, ret, error, fw_error_to_str(error)); + exit(1); + } + + sev_set_guest_state(SEV_STATE_RUNNING); + + /* add migration blocker */ + error_setg(&sev_mig_blocker, + "SEV: Migration is not implemented"); + ret = migrate_add_blocker(sev_mig_blocker, &local_err); + if (local_err) { + error_report_err(local_err); + error_free(sev_mig_blocker); + exit(1); + } +} + +static void +sev_vm_state_change(void *opaque, int running, RunState state) +{ + SEVState *s = opaque; + + if (running) { + if (!sev_check_state(SEV_STATE_RUNNING)) { + sev_launch_finish(s); + } + } +} + +void * +sev_guest_init(const char *id) +{ + SEVState *s; + char *devname; + int ret, fw_error; + uint32_t ebx; + uint32_t host_cbitpos; + struct sev_user_data_status status = {}; + + s = g_new0(SEVState, 1); + s->sev_info = lookup_sev_guest_info(id); + if (!s->sev_info) { + error_report("%s: '%s' is not a valid '%s' object", + __func__, id, TYPE_QSEV_GUEST_INFO); + goto err; + } + + sev_state = s; + s->state = SEV_STATE_UNINIT; + + host_cpuid(0x8000001F, 0, NULL, &ebx, NULL, NULL); + host_cbitpos = ebx & 0x3f; + + s->cbitpos = object_property_get_int(OBJECT(s->sev_info), "cbitpos", NULL); + if (host_cbitpos != s->cbitpos) { + error_report("%s: cbitpos check failed, host '%d' requested '%d'", + __func__, host_cbitpos, s->cbitpos); + goto err; + } + + s->reduced_phys_bits = object_property_get_int(OBJECT(s->sev_info), + "reduced-phys-bits", NULL); + if (s->reduced_phys_bits < 1) { + error_report("%s: reduced_phys_bits check failed, it should be >=1," + "' requested '%d'", __func__, s->reduced_phys_bits); + goto err; + } + + s->me_mask = ~(1UL << s->cbitpos); + + devname = object_property_get_str(OBJECT(s->sev_info), "sev-device", NULL); + s->sev_fd = open(devname, O_RDWR); + if (s->sev_fd < 0) { + error_report("%s: Failed to open %s '%s'", __func__, + devname, strerror(errno)); + goto err; + } + g_free(devname); + + ret = sev_platform_ioctl(s->sev_fd, SEV_PLATFORM_STATUS, &status, + &fw_error); + if (ret) { + error_report("%s: failed to get platform status ret=%d" + "fw_error='%d: %s'", __func__, ret, fw_error, + fw_error_to_str(fw_error)); + goto err; + } + s->build_id = status.build; + s->api_major = status.api_major; + s->api_minor = status.api_minor; + + trace_kvm_sev_init(); + ret = sev_ioctl(s->sev_fd, KVM_SEV_INIT, NULL, &fw_error); + if (ret) { + error_report("%s: failed to initialize ret=%d fw_error=%d '%s'", + __func__, ret, fw_error, fw_error_to_str(fw_error)); + goto err; + } + + ret = sev_launch_start(s); + if (ret) { + error_report("%s: failed to create encryption context", __func__); + goto err; + } + + ram_block_notifier_add(&sev_ram_notifier); + qemu_add_machine_init_done_notifier(&sev_machine_done_notify); + qemu_add_vm_change_state_handler(sev_vm_state_change, s); + + return s; +err: + g_free(sev_state); + sev_state = NULL; + return NULL; +} + +int +sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len) +{ + assert(handle); + + /* if SEV is in update state then encrypt the data else do nothing */ + if (sev_check_state(SEV_STATE_LAUNCH_UPDATE)) { + return sev_launch_update_data(ptr, len); + } + + return 0; +} + +static void +sev_register_types(void) +{ + type_register_static(&qsev_guest_info); +} + +type_init(sev_register_types); diff --git a/target/i386/sev_i386.h b/target/i386/sev_i386.h new file mode 100644 index 0000000000..b8622dfb1e --- /dev/null +++ b/target/i386/sev_i386.h @@ -0,0 +1,88 @@ +/* + * QEMU Secure Encrypted Virutualization (SEV) support + * + * Copyright: Advanced Micro Devices, 2016-2018 + * + * Authors: + * Brijesh Singh <brijesh.singh@amd.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef QEMU_SEV_I386_H +#define QEMU_SEV_I386_H + +#include "qom/object.h" +#include "qapi/error.h" +#include "sysemu/kvm.h" +#include "sysemu/sev.h" +#include "qemu/error-report.h" +#include "qapi/qapi-commands-misc.h" + +#define SEV_POLICY_NODBG 0x1 +#define SEV_POLICY_NOKS 0x2 +#define SEV_POLICY_ES 0x4 +#define SEV_POLICY_NOSEND 0x8 +#define SEV_POLICY_DOMAIN 0x10 +#define SEV_POLICY_SEV 0x20 + +#define TYPE_QSEV_GUEST_INFO "sev-guest" +#define QSEV_GUEST_INFO(obj) \ + OBJECT_CHECK(QSevGuestInfo, (obj), TYPE_QSEV_GUEST_INFO) + +extern bool sev_enabled(void); +extern uint64_t sev_get_me_mask(void); +extern SevInfo *sev_get_info(void); +extern uint32_t sev_get_cbit_position(void); +extern uint32_t sev_get_reduced_phys_bits(void); +extern char *sev_get_launch_measurement(void); +extern SevCapability *sev_get_capabilities(void); + +typedef struct QSevGuestInfo QSevGuestInfo; +typedef struct QSevGuestInfoClass QSevGuestInfoClass; + +/** + * QSevGuestInfo: + * + * The QSevGuestInfo object is used for creating a SEV guest. + * + * # $QEMU \ + * -object sev-guest,id=sev0 \ + * -machine ...,memory-encryption=sev0 + */ +struct QSevGuestInfo { + Object parent_obj; + + char *sev_device; + uint32_t policy; + uint32_t handle; + char *dh_cert_file; + char *session_file; + uint32_t cbitpos; + uint32_t reduced_phys_bits; +}; + +struct QSevGuestInfoClass { + ObjectClass parent_class; +}; + +struct SEVState { + QSevGuestInfo *sev_info; + uint8_t api_major; + uint8_t api_minor; + uint8_t build_id; + uint32_t policy; + uint64_t me_mask; + uint32_t cbitpos; + uint32_t reduced_phys_bits; + uint32_t handle; + int sev_fd; + SevState state; + gchar *measurement; +}; + +typedef struct SEVState SEVState; + +#endif diff --git a/target/i386/trace-events b/target/i386/trace-events index 3153fd4454..6a19a69af5 100644 --- a/target/i386/trace-events +++ b/target/i386/trace-events @@ -5,3 +5,13 @@ kvm_x86_fixup_msi_error(uint32_t gsi) "VT-d failed to remap interrupt for GSI %" kvm_x86_add_msi_route(int virq) "Adding route entry for virq %d" kvm_x86_remove_msi_route(int virq) "Removing route entry for virq %d" kvm_x86_update_msi_routes(int num) "Updated %d MSI routes" + +# target/i386/sev.c +kvm_sev_init(void) "" +kvm_memcrypt_register_region(void *addr, size_t len) "addr %p len 0x%zu" +kvm_memcrypt_unregister_region(void *addr, size_t len) "addr %p len 0x%zu" +kvm_sev_change_state(const char *old, const char *new) "%s -> %s" +kvm_sev_launch_start(int policy, void *session, void *pdh) "policy 0x%x session %p pdh %p" +kvm_sev_launch_update_data(void *addr, uint64_t len) "addr %p len 0x%" PRIu64 +kvm_sev_launch_measurement(const char *value) "data %s" +kvm_sev_launch_finish(void) "" diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c index cdb9b50462..6eeffdf9bb 100644 --- a/target/m68k/fpu_helper.c +++ b/target/m68k/fpu_helper.c @@ -557,3 +557,99 @@ void HELPER(fscale)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) { res->d = floatx80_scale(val1->d, val0->d, &env->fp_status); } + +void HELPER(flognp1)(CPUM68KState *env, FPReg *res, FPReg *val) +{ + res->d = floatx80_lognp1(val->d, &env->fp_status); +} + +void HELPER(flogn)(CPUM68KState *env, FPReg *res, FPReg *val) +{ + res->d = floatx80_logn(val->d, &env->fp_status); +} + +void HELPER(flog10)(CPUM68KState *env, FPReg *res, FPReg *val) +{ + res->d = floatx80_log10(val->d, &env->fp_status); +} + +void HELPER(flog2)(CPUM68KState *env, FPReg *res, FPReg *val) +{ + res->d = floatx80_log2(val->d, &env->fp_status); +} + +void HELPER(fetox)(CPUM68KState *env, FPReg *res, FPReg *val) +{ + res->d = floatx80_etox(val->d, &env->fp_status); +} + +void HELPER(ftwotox)(CPUM68KState *env, FPReg *res, FPReg *val) +{ + res->d = floatx80_twotox(val->d, &env->fp_status); +} + +void HELPER(ftentox)(CPUM68KState *env, FPReg *res, FPReg *val) +{ + res->d = floatx80_tentox(val->d, &env->fp_status); +} + +void HELPER(ftan)(CPUM68KState *env, FPReg *res, FPReg *val) +{ + res->d = floatx80_tan(val->d, &env->fp_status); +} + +void HELPER(fsin)(CPUM68KState *env, FPReg *res, FPReg *val) +{ + res->d = floatx80_sin(val->d, &env->fp_status); +} + +void HELPER(fcos)(CPUM68KState *env, FPReg *res, FPReg *val) +{ + res->d = floatx80_cos(val->d, &env->fp_status); +} + +void HELPER(fsincos)(CPUM68KState *env, FPReg *res0, FPReg *res1, FPReg *val) +{ + floatx80 a = val->d; + /* If res0 and res1 specify the same floating-point data register, + * the sine result is stored in the register, and the cosine + * result is discarded. + */ + res1->d = floatx80_cos(a, &env->fp_status); + res0->d = floatx80_sin(a, &env->fp_status); +} + +void HELPER(fatan)(CPUM68KState *env, FPReg *res, FPReg *val) +{ + res->d = floatx80_atan(val->d, &env->fp_status); +} + +void HELPER(fasin)(CPUM68KState *env, FPReg *res, FPReg *val) +{ + res->d = floatx80_asin(val->d, &env->fp_status); +} + +void HELPER(facos)(CPUM68KState *env, FPReg *res, FPReg *val) +{ + res->d = floatx80_acos(val->d, &env->fp_status); +} + +void HELPER(fatanh)(CPUM68KState *env, FPReg *res, FPReg *val) +{ + res->d = floatx80_atanh(val->d, &env->fp_status); +} + +void HELPER(ftanh)(CPUM68KState *env, FPReg *res, FPReg *val) +{ + res->d = floatx80_tanh(val->d, &env->fp_status); +} + +void HELPER(fsinh)(CPUM68KState *env, FPReg *res, FPReg *val) +{ + res->d = floatx80_sinh(val->d, &env->fp_status); +} + +void HELPER(fcosh)(CPUM68KState *env, FPReg *res, FPReg *val) +{ + res->d = floatx80_cosh(val->d, &env->fp_status); +} diff --git a/target/m68k/helper.h b/target/m68k/helper.h index c348dced3a..feee7be626 100644 --- a/target/m68k/helper.h +++ b/target/m68k/helper.h @@ -68,6 +68,24 @@ DEF_HELPER_4(frem, void, env, fp, fp, fp) DEF_HELPER_3(fgetexp, void, env, fp, fp) DEF_HELPER_3(fgetman, void, env, fp, fp) DEF_HELPER_4(fscale, void, env, fp, fp, fp) +DEF_HELPER_3(flognp1, void, env, fp, fp) +DEF_HELPER_3(flogn, void, env, fp, fp) +DEF_HELPER_3(flog10, void, env, fp, fp) +DEF_HELPER_3(flog2, void, env, fp, fp) +DEF_HELPER_3(fetox, void, env, fp, fp) +DEF_HELPER_3(ftwotox, void, env, fp, fp) +DEF_HELPER_3(ftentox, void, env, fp, fp) +DEF_HELPER_3(ftan, void, env, fp, fp) +DEF_HELPER_3(fsin, void, env, fp, fp) +DEF_HELPER_3(fcos, void, env, fp, fp) +DEF_HELPER_4(fsincos, void, env, fp, fp, fp) +DEF_HELPER_3(fatan, void, env, fp, fp) +DEF_HELPER_3(fasin, void, env, fp, fp) +DEF_HELPER_3(facos, void, env, fp, fp) +DEF_HELPER_3(fatanh, void, env, fp, fp) +DEF_HELPER_3(ftanh, void, env, fp, fp) +DEF_HELPER_3(fsinh, void, env, fp, fp) +DEF_HELPER_3(fcosh, void, env, fp, fp) DEF_HELPER_3(mac_move, void, env, i32, i32) DEF_HELPER_3(macmulf, i64, env, i32, i32) diff --git a/target/m68k/softfloat.c b/target/m68k/softfloat.c index 9cb141900c..dffb371c71 100644 --- a/target/m68k/softfloat.c +++ b/target/m68k/softfloat.c @@ -21,6 +21,11 @@ #include "qemu/osdep.h" #include "softfloat.h" #include "fpu/softfloat-macros.h" +#include "softfloat_fpsp_tables.h" + +#define pi_exp 0x4000 +#define piby2_exp 0x3FFF +#define pi_sig LIT64(0xc90fdaa22168c235) static floatx80 propagateFloatx80NaNOneArg(floatx80 a, float_status *status) { @@ -247,3 +252,2654 @@ floatx80 floatx80_scale(floatx80 a, floatx80 b, float_status *status) return roundAndPackFloatx80(status->floatx80_rounding_precision, aSign, aExp, aSig, 0, status); } + +floatx80 floatx80_move(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t)(aSig << 1)) { + return propagateFloatx80NaNOneArg(a, status); + } + return a; + } + if (aExp == 0) { + if (aSig == 0) { + return a; + } + normalizeRoundAndPackFloatx80(status->floatx80_rounding_precision, + aSign, aExp, aSig, 0, status); + } + return roundAndPackFloatx80(status->floatx80_rounding_precision, aSign, + aExp, aSig, 0, status); +} + +/*---------------------------------------------------------------------------- +| Algorithms for transcendental functions supported by MC68881 and MC68882 +| mathematical coprocessors. The functions are derived from FPSP library. +*----------------------------------------------------------------------------*/ + +#define one_exp 0x3FFF +#define one_sig LIT64(0x8000000000000000) + +/*---------------------------------------------------------------------------- + | Function for compactifying extended double-precision floating point values. + *----------------------------------------------------------------------------*/ + +static int32_t floatx80_make_compact(int32_t aExp, uint64_t aSig) +{ + return (aExp << 16) | (aSig >> 48); +} + +/*---------------------------------------------------------------------------- + | Log base e of x plus 1 + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_lognp1(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig, fSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact, j, k; + floatx80 fp0, fp1, fp2, fp3, f, logof2, klog2, saveu; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig << 1)) { + propagateFloatx80NaNOneArg(a, status); + } + if (aSign) { + float_raise(float_flag_invalid, status); + return floatx80_default_nan(status); + } + return packFloatx80(0, floatx80_infinity.high, floatx80_infinity.low); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(aSign, 0, 0); + } + + if (aSign && aExp >= one_exp) { + if (aExp == one_exp && aSig == one_sig) { + float_raise(float_flag_divbyzero, status); + packFloatx80(aSign, floatx80_infinity.high, floatx80_infinity.low); + } + float_raise(float_flag_invalid, status); + return floatx80_default_nan(status); + } + + if (aExp < 0x3f99 || (aExp == 0x3f99 && aSig == one_sig)) { + /* <= min threshold */ + float_raise(float_flag_inexact, status); + return floatx80_move(a, status); + } + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + status->float_rounding_mode = float_round_nearest_even; + status->floatx80_rounding_precision = 80; + + compact = floatx80_make_compact(aExp, aSig); + + fp0 = a; /* Z */ + fp1 = a; + + fp0 = floatx80_add(fp0, float32_to_floatx80(make_float32(0x3F800000), + status), status); /* X = (1+Z) */ + + aExp = extractFloatx80Exp(fp0); + aSig = extractFloatx80Frac(fp0); + + compact = floatx80_make_compact(aExp, aSig); + + if (compact < 0x3FFE8000 || compact > 0x3FFFC000) { + /* |X| < 1/2 or |X| > 3/2 */ + k = aExp - 0x3FFF; + fp1 = int32_to_floatx80(k, status); + + fSig = (aSig & LIT64(0xFE00000000000000)) | LIT64(0x0100000000000000); + j = (fSig >> 56) & 0x7E; /* DISPLACEMENT FOR 1/F */ + + f = packFloatx80(0, 0x3FFF, fSig); /* F */ + fp0 = packFloatx80(0, 0x3FFF, aSig); /* Y */ + + fp0 = floatx80_sub(fp0, f, status); /* Y-F */ + + lp1cont1: + /* LP1CONT1 */ + fp0 = floatx80_mul(fp0, log_tbl[j], status); /* FP0 IS U = (Y-F)/F */ + logof2 = packFloatx80(0, 0x3FFE, LIT64(0xB17217F7D1CF79AC)); + klog2 = floatx80_mul(fp1, logof2, status); /* FP1 IS K*LOG2 */ + fp2 = floatx80_mul(fp0, fp0, status); /* FP2 IS V=U*U */ + + fp3 = fp2; + fp1 = fp2; + + fp1 = floatx80_mul(fp1, float64_to_floatx80( + make_float64(0x3FC2499AB5E4040B), status), + status); /* V*A6 */ + fp2 = floatx80_mul(fp2, float64_to_floatx80( + make_float64(0xBFC555B5848CB7DB), status), + status); /* V*A5 */ + fp1 = floatx80_add(fp1, float64_to_floatx80( + make_float64(0x3FC99999987D8730), status), + status); /* A4+V*A6 */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0xBFCFFFFFFF6F7E97), status), + status); /* A3+V*A5 */ + fp1 = floatx80_mul(fp1, fp3, status); /* V*(A4+V*A6) */ + fp2 = floatx80_mul(fp2, fp3, status); /* V*(A3+V*A5) */ + fp1 = floatx80_add(fp1, float64_to_floatx80( + make_float64(0x3FD55555555555A4), status), + status); /* A2+V*(A4+V*A6) */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0xBFE0000000000008), status), + status); /* A1+V*(A3+V*A5) */ + fp1 = floatx80_mul(fp1, fp3, status); /* V*(A2+V*(A4+V*A6)) */ + fp2 = floatx80_mul(fp2, fp3, status); /* V*(A1+V*(A3+V*A5)) */ + fp1 = floatx80_mul(fp1, fp0, status); /* U*V*(A2+V*(A4+V*A6)) */ + fp0 = floatx80_add(fp0, fp2, status); /* U+V*(A1+V*(A3+V*A5)) */ + + fp1 = floatx80_add(fp1, log_tbl[j + 1], + status); /* LOG(F)+U*V*(A2+V*(A4+V*A6)) */ + fp0 = floatx80_add(fp0, fp1, status); /* FP0 IS LOG(F) + LOG(1+U) */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, klog2, status); + + float_raise(float_flag_inexact, status); + + return a; + } else if (compact < 0x3FFEF07D || compact > 0x3FFF8841) { + /* |X| < 1/16 or |X| > -1/16 */ + /* LP1CARE */ + fSig = (aSig & LIT64(0xFE00000000000000)) | LIT64(0x0100000000000000); + f = packFloatx80(0, 0x3FFF, fSig); /* F */ + j = (fSig >> 56) & 0x7E; /* DISPLACEMENT FOR 1/F */ + + if (compact >= 0x3FFF8000) { /* 1+Z >= 1 */ + /* KISZERO */ + fp0 = floatx80_sub(float32_to_floatx80(make_float32(0x3F800000), + status), f, status); /* 1-F */ + fp0 = floatx80_add(fp0, fp1, status); /* FP0 IS Y-F = (1-F)+Z */ + fp1 = packFloatx80(0, 0, 0); /* K = 0 */ + } else { + /* KISNEG */ + fp0 = floatx80_sub(float32_to_floatx80(make_float32(0x40000000), + status), f, status); /* 2-F */ + fp1 = floatx80_add(fp1, fp1, status); /* 2Z */ + fp0 = floatx80_add(fp0, fp1, status); /* FP0 IS Y-F = (2-F)+2Z */ + fp1 = packFloatx80(1, one_exp, one_sig); /* K = -1 */ + } + goto lp1cont1; + } else { + /* LP1ONE16 */ + fp1 = floatx80_add(fp1, fp1, status); /* FP1 IS 2Z */ + fp0 = floatx80_add(fp0, float32_to_floatx80(make_float32(0x3F800000), + status), status); /* FP0 IS 1+X */ + + /* LP1CONT2 */ + fp1 = floatx80_div(fp1, fp0, status); /* U */ + saveu = fp1; + fp0 = floatx80_mul(fp1, fp1, status); /* FP0 IS V = U*U */ + fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS W = V*V */ + + fp3 = float64_to_floatx80(make_float64(0x3F175496ADD7DAD6), + status); /* B5 */ + fp2 = float64_to_floatx80(make_float64(0x3F3C71C2FE80C7E0), + status); /* B4 */ + fp3 = floatx80_mul(fp3, fp1, status); /* W*B5 */ + fp2 = floatx80_mul(fp2, fp1, status); /* W*B4 */ + fp3 = floatx80_add(fp3, float64_to_floatx80( + make_float64(0x3F624924928BCCFF), status), + status); /* B3+W*B5 */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3F899999999995EC), status), + status); /* B2+W*B4 */ + fp1 = floatx80_mul(fp1, fp3, status); /* W*(B3+W*B5) */ + fp2 = floatx80_mul(fp2, fp0, status); /* V*(B2+W*B4) */ + fp1 = floatx80_add(fp1, float64_to_floatx80( + make_float64(0x3FB5555555555555), status), + status); /* B1+W*(B3+W*B5) */ + + fp0 = floatx80_mul(fp0, saveu, status); /* FP0 IS U*V */ + fp1 = floatx80_add(fp1, fp2, + status); /* B1+W*(B3+W*B5) + V*(B2+W*B4) */ + fp0 = floatx80_mul(fp0, fp1, + status); /* U*V*([B1+W*(B3+W*B5)] + [V*(B2+W*B4)]) */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, saveu, status); + + /*if (!floatx80_is_zero(a)) { */ + float_raise(float_flag_inexact, status); + /*} */ + + return a; + } +} + +/*---------------------------------------------------------------------------- + | Log base e + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_logn(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig, fSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact, j, k, adjk; + floatx80 fp0, fp1, fp2, fp3, f, logof2, klog2, saveu; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig << 1)) { + propagateFloatx80NaNOneArg(a, status); + } + if (aSign == 0) { + return packFloatx80(0, floatx80_infinity.high, + floatx80_infinity.low); + } + } + + adjk = 0; + + if (aExp == 0) { + if (aSig == 0) { /* zero */ + float_raise(float_flag_divbyzero, status); + return packFloatx80(1, floatx80_infinity.high, + floatx80_infinity.low); + } + if ((aSig & one_sig) == 0) { /* denormal */ + normalizeFloatx80Subnormal(aSig, &aExp, &aSig); + adjk = -100; + aExp += 100; + a = packFloatx80(aSign, aExp, aSig); + } + } + + if (aSign) { + float_raise(float_flag_invalid, status); + return floatx80_default_nan(status); + } + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + status->float_rounding_mode = float_round_nearest_even; + status->floatx80_rounding_precision = 80; + + compact = floatx80_make_compact(aExp, aSig); + + if (compact < 0x3FFEF07D || compact > 0x3FFF8841) { + /* |X| < 15/16 or |X| > 17/16 */ + k = aExp - 0x3FFF; + k += adjk; + fp1 = int32_to_floatx80(k, status); + + fSig = (aSig & LIT64(0xFE00000000000000)) | LIT64(0x0100000000000000); + j = (fSig >> 56) & 0x7E; /* DISPLACEMENT FOR 1/F */ + + f = packFloatx80(0, 0x3FFF, fSig); /* F */ + fp0 = packFloatx80(0, 0x3FFF, aSig); /* Y */ + + fp0 = floatx80_sub(fp0, f, status); /* Y-F */ + + /* LP1CONT1 */ + fp0 = floatx80_mul(fp0, log_tbl[j], status); /* FP0 IS U = (Y-F)/F */ + logof2 = packFloatx80(0, 0x3FFE, LIT64(0xB17217F7D1CF79AC)); + klog2 = floatx80_mul(fp1, logof2, status); /* FP1 IS K*LOG2 */ + fp2 = floatx80_mul(fp0, fp0, status); /* FP2 IS V=U*U */ + + fp3 = fp2; + fp1 = fp2; + + fp1 = floatx80_mul(fp1, float64_to_floatx80( + make_float64(0x3FC2499AB5E4040B), status), + status); /* V*A6 */ + fp2 = floatx80_mul(fp2, float64_to_floatx80( + make_float64(0xBFC555B5848CB7DB), status), + status); /* V*A5 */ + fp1 = floatx80_add(fp1, float64_to_floatx80( + make_float64(0x3FC99999987D8730), status), + status); /* A4+V*A6 */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0xBFCFFFFFFF6F7E97), status), + status); /* A3+V*A5 */ + fp1 = floatx80_mul(fp1, fp3, status); /* V*(A4+V*A6) */ + fp2 = floatx80_mul(fp2, fp3, status); /* V*(A3+V*A5) */ + fp1 = floatx80_add(fp1, float64_to_floatx80( + make_float64(0x3FD55555555555A4), status), + status); /* A2+V*(A4+V*A6) */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0xBFE0000000000008), status), + status); /* A1+V*(A3+V*A5) */ + fp1 = floatx80_mul(fp1, fp3, status); /* V*(A2+V*(A4+V*A6)) */ + fp2 = floatx80_mul(fp2, fp3, status); /* V*(A1+V*(A3+V*A5)) */ + fp1 = floatx80_mul(fp1, fp0, status); /* U*V*(A2+V*(A4+V*A6)) */ + fp0 = floatx80_add(fp0, fp2, status); /* U+V*(A1+V*(A3+V*A5)) */ + + fp1 = floatx80_add(fp1, log_tbl[j + 1], + status); /* LOG(F)+U*V*(A2+V*(A4+V*A6)) */ + fp0 = floatx80_add(fp0, fp1, status); /* FP0 IS LOG(F) + LOG(1+U) */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, klog2, status); + + float_raise(float_flag_inexact, status); + + return a; + } else { /* |X-1| >= 1/16 */ + fp0 = a; + fp1 = a; + fp1 = floatx80_sub(fp1, float32_to_floatx80(make_float32(0x3F800000), + status), status); /* FP1 IS X-1 */ + fp0 = floatx80_add(fp0, float32_to_floatx80(make_float32(0x3F800000), + status), status); /* FP0 IS X+1 */ + fp1 = floatx80_add(fp1, fp1, status); /* FP1 IS 2(X-1) */ + + /* LP1CONT2 */ + fp1 = floatx80_div(fp1, fp0, status); /* U */ + saveu = fp1; + fp0 = floatx80_mul(fp1, fp1, status); /* FP0 IS V = U*U */ + fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS W = V*V */ + + fp3 = float64_to_floatx80(make_float64(0x3F175496ADD7DAD6), + status); /* B5 */ + fp2 = float64_to_floatx80(make_float64(0x3F3C71C2FE80C7E0), + status); /* B4 */ + fp3 = floatx80_mul(fp3, fp1, status); /* W*B5 */ + fp2 = floatx80_mul(fp2, fp1, status); /* W*B4 */ + fp3 = floatx80_add(fp3, float64_to_floatx80( + make_float64(0x3F624924928BCCFF), status), + status); /* B3+W*B5 */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3F899999999995EC), status), + status); /* B2+W*B4 */ + fp1 = floatx80_mul(fp1, fp3, status); /* W*(B3+W*B5) */ + fp2 = floatx80_mul(fp2, fp0, status); /* V*(B2+W*B4) */ + fp1 = floatx80_add(fp1, float64_to_floatx80( + make_float64(0x3FB5555555555555), status), + status); /* B1+W*(B3+W*B5) */ + + fp0 = floatx80_mul(fp0, saveu, status); /* FP0 IS U*V */ + fp1 = floatx80_add(fp1, fp2, status); /* B1+W*(B3+W*B5) + V*(B2+W*B4) */ + fp0 = floatx80_mul(fp0, fp1, + status); /* U*V*([B1+W*(B3+W*B5)] + [V*(B2+W*B4)]) */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, saveu, status); + + /*if (!floatx80_is_zero(a)) { */ + float_raise(float_flag_inexact, status); + /*} */ + + return a; + } +} + +/*---------------------------------------------------------------------------- + | Log base 10 + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_log10(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig; + + int8_t user_rnd_mode, user_rnd_prec; + + floatx80 fp0, fp1; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig << 1)) { + propagateFloatx80NaNOneArg(a, status); + } + if (aSign == 0) { + return packFloatx80(0, floatx80_infinity.high, + floatx80_infinity.low); + } + } + + if (aExp == 0 && aSig == 0) { + float_raise(float_flag_divbyzero, status); + return packFloatx80(1, floatx80_infinity.high, + floatx80_infinity.low); + } + + if (aSign) { + float_raise(float_flag_invalid, status); + return floatx80_default_nan(status); + } + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + status->float_rounding_mode = float_round_nearest_even; + status->floatx80_rounding_precision = 80; + + fp0 = floatx80_logn(a, status); + fp1 = packFloatx80(0, 0x3FFD, LIT64(0xDE5BD8A937287195)); /* INV_L10 */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_mul(fp0, fp1, status); /* LOGN(X)*INV_L10 */ + + float_raise(float_flag_inexact, status); + + return a; +} + +/*---------------------------------------------------------------------------- + | Log base 2 + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_log2(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig; + + int8_t user_rnd_mode, user_rnd_prec; + + floatx80 fp0, fp1; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig << 1)) { + propagateFloatx80NaNOneArg(a, status); + } + if (aSign == 0) { + return packFloatx80(0, floatx80_infinity.high, + floatx80_infinity.low); + } + } + + if (aExp == 0) { + if (aSig == 0) { + float_raise(float_flag_divbyzero, status); + return packFloatx80(1, floatx80_infinity.high, + floatx80_infinity.low); + } + normalizeFloatx80Subnormal(aSig, &aExp, &aSig); + } + + if (aSign) { + float_raise(float_flag_invalid, status); + return floatx80_default_nan(status); + } + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + status->float_rounding_mode = float_round_nearest_even; + status->floatx80_rounding_precision = 80; + + if (aSig == one_sig) { /* X is 2^k */ + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = int32_to_floatx80(aExp - 0x3FFF, status); + } else { + fp0 = floatx80_logn(a, status); + fp1 = packFloatx80(0, 0x3FFF, LIT64(0xB8AA3B295C17F0BC)); /* INV_L2 */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_mul(fp0, fp1, status); /* LOGN(X)*INV_L2 */ + } + + float_raise(float_flag_inexact, status); + + return a; +} + +/*---------------------------------------------------------------------------- + | e to x + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_etox(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact, n, j, k, m, m1; + floatx80 fp0, fp1, fp2, fp3, l2, scale, adjscale; + flag adjflag; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig << 1)) { + return propagateFloatx80NaNOneArg(a, status); + } + if (aSign) { + return packFloatx80(0, 0, 0); + } + return packFloatx80(0, floatx80_infinity.high, + floatx80_infinity.low); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(0, one_exp, one_sig); + } + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + status->float_rounding_mode = float_round_nearest_even; + status->floatx80_rounding_precision = 80; + + adjflag = 0; + + if (aExp >= 0x3FBE) { /* |X| >= 2^(-65) */ + compact = floatx80_make_compact(aExp, aSig); + + if (compact < 0x400CB167) { /* |X| < 16380 log2 */ + fp0 = a; + fp1 = a; + fp0 = floatx80_mul(fp0, float32_to_floatx80( + make_float32(0x42B8AA3B), status), + status); /* 64/log2 * X */ + adjflag = 0; + n = floatx80_to_int32(fp0, status); /* int(64/log2*X) */ + fp0 = int32_to_floatx80(n, status); + + j = n & 0x3F; /* J = N mod 64 */ + m = n / 64; /* NOTE: this is really arithmetic right shift by 6 */ + if (n < 0 && j) { + /* arithmetic right shift is division and + * round towards minus infinity + */ + m--; + } + m += 0x3FFF; /* biased exponent of 2^(M) */ + + expcont1: + fp2 = fp0; /* N */ + fp0 = floatx80_mul(fp0, float32_to_floatx80( + make_float32(0xBC317218), status), + status); /* N * L1, L1 = lead(-log2/64) */ + l2 = packFloatx80(0, 0x3FDC, LIT64(0x82E308654361C4C6)); + fp2 = floatx80_mul(fp2, l2, status); /* N * L2, L1+L2 = -log2/64 */ + fp0 = floatx80_add(fp0, fp1, status); /* X + N*L1 */ + fp0 = floatx80_add(fp0, fp2, status); /* R */ + + fp1 = floatx80_mul(fp0, fp0, status); /* S = R*R */ + fp2 = float32_to_floatx80(make_float32(0x3AB60B70), + status); /* A5 */ + fp2 = floatx80_mul(fp2, fp1, status); /* fp2 is S*A5 */ + fp3 = floatx80_mul(float32_to_floatx80(make_float32(0x3C088895), + status), fp1, + status); /* fp3 is S*A4 */ + fp2 = floatx80_add(fp2, float64_to_floatx80(make_float64( + 0x3FA5555555554431), status), + status); /* fp2 is A3+S*A5 */ + fp3 = floatx80_add(fp3, float64_to_floatx80(make_float64( + 0x3FC5555555554018), status), + status); /* fp3 is A2+S*A4 */ + fp2 = floatx80_mul(fp2, fp1, status); /* fp2 is S*(A3+S*A5) */ + fp3 = floatx80_mul(fp3, fp1, status); /* fp3 is S*(A2+S*A4) */ + fp2 = floatx80_add(fp2, float32_to_floatx80( + make_float32(0x3F000000), status), + status); /* fp2 is A1+S*(A3+S*A5) */ + fp3 = floatx80_mul(fp3, fp0, status); /* fp3 IS R*S*(A2+S*A4) */ + fp2 = floatx80_mul(fp2, fp1, + status); /* fp2 IS S*(A1+S*(A3+S*A5)) */ + fp0 = floatx80_add(fp0, fp3, status); /* fp0 IS R+R*S*(A2+S*A4) */ + fp0 = floatx80_add(fp0, fp2, status); /* fp0 IS EXP(R) - 1 */ + + fp1 = exp_tbl[j]; + fp0 = floatx80_mul(fp0, fp1, status); /* 2^(J/64)*(Exp(R)-1) */ + fp0 = floatx80_add(fp0, float32_to_floatx80(exp_tbl2[j], status), + status); /* accurate 2^(J/64) */ + fp0 = floatx80_add(fp0, fp1, + status); /* 2^(J/64) + 2^(J/64)*(Exp(R)-1) */ + + scale = packFloatx80(0, m, one_sig); + if (adjflag) { + adjscale = packFloatx80(0, m1, one_sig); + fp0 = floatx80_mul(fp0, adjscale, status); + } + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_mul(fp0, scale, status); + + float_raise(float_flag_inexact, status); + + return a; + } else { /* |X| >= 16380 log2 */ + if (compact > 0x400CB27C) { /* |X| >= 16480 log2 */ + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + if (aSign) { + a = roundAndPackFloatx80( + status->floatx80_rounding_precision, + 0, -0x1000, aSig, 0, status); + } else { + a = roundAndPackFloatx80( + status->floatx80_rounding_precision, + 0, 0x8000, aSig, 0, status); + } + float_raise(float_flag_inexact, status); + + return a; + } else { + fp0 = a; + fp1 = a; + fp0 = floatx80_mul(fp0, float32_to_floatx80( + make_float32(0x42B8AA3B), status), + status); /* 64/log2 * X */ + adjflag = 1; + n = floatx80_to_int32(fp0, status); /* int(64/log2*X) */ + fp0 = int32_to_floatx80(n, status); + + j = n & 0x3F; /* J = N mod 64 */ + /* NOTE: this is really arithmetic right shift by 6 */ + k = n / 64; + if (n < 0 && j) { + /* arithmetic right shift is division and + * round towards minus infinity + */ + k--; + } + /* NOTE: this is really arithmetic right shift by 1 */ + m1 = k / 2; + if (k < 0 && (k & 1)) { + /* arithmetic right shift is division and + * round towards minus infinity + */ + m1--; + } + m = k - m1; + m1 += 0x3FFF; /* biased exponent of 2^(M1) */ + m += 0x3FFF; /* biased exponent of 2^(M) */ + + goto expcont1; + } + } + } else { /* |X| < 2^(-65) */ + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(a, float32_to_floatx80(make_float32(0x3F800000), + status), status); /* 1 + X */ + + float_raise(float_flag_inexact, status); + + return a; + } +} + +/*---------------------------------------------------------------------------- + | 2 to x + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_twotox(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact, n, j, l, m, m1; + floatx80 fp0, fp1, fp2, fp3, adjfact, fact1, fact2; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig << 1)) { + return propagateFloatx80NaNOneArg(a, status); + } + if (aSign) { + return packFloatx80(0, 0, 0); + } + return packFloatx80(0, floatx80_infinity.high, + floatx80_infinity.low); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(0, one_exp, one_sig); + } + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + status->float_rounding_mode = float_round_nearest_even; + status->floatx80_rounding_precision = 80; + + fp0 = a; + + compact = floatx80_make_compact(aExp, aSig); + + if (compact < 0x3FB98000 || compact > 0x400D80C0) { + /* |X| > 16480 or |X| < 2^(-70) */ + if (compact > 0x3FFF8000) { /* |X| > 16480 */ + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + if (aSign) { + return roundAndPackFloatx80(status->floatx80_rounding_precision, + 0, -0x1000, aSig, 0, status); + } else { + return roundAndPackFloatx80(status->floatx80_rounding_precision, + 0, 0x8000, aSig, 0, status); + } + } else { /* |X| < 2^(-70) */ + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, float32_to_floatx80( + make_float32(0x3F800000), status), + status); /* 1 + X */ + + float_raise(float_flag_inexact, status); + + return a; + } + } else { /* 2^(-70) <= |X| <= 16480 */ + fp1 = fp0; /* X */ + fp1 = floatx80_mul(fp1, float32_to_floatx80( + make_float32(0x42800000), status), + status); /* X * 64 */ + n = floatx80_to_int32(fp1, status); + fp1 = int32_to_floatx80(n, status); + j = n & 0x3F; + l = n / 64; /* NOTE: this is really arithmetic right shift by 6 */ + if (n < 0 && j) { + /* arithmetic right shift is division and + * round towards minus infinity + */ + l--; + } + m = l / 2; /* NOTE: this is really arithmetic right shift by 1 */ + if (l < 0 && (l & 1)) { + /* arithmetic right shift is division and + * round towards minus infinity + */ + m--; + } + m1 = l - m; + m1 += 0x3FFF; /* ADJFACT IS 2^(M') */ + + adjfact = packFloatx80(0, m1, one_sig); + fact1 = exp2_tbl[j]; + fact1.high += m; + fact2.high = exp2_tbl2[j] >> 16; + fact2.high += m; + fact2.low = (uint64_t)(exp2_tbl2[j] & 0xFFFF); + fact2.low <<= 48; + + fp1 = floatx80_mul(fp1, float32_to_floatx80( + make_float32(0x3C800000), status), + status); /* (1/64)*N */ + fp0 = floatx80_sub(fp0, fp1, status); /* X - (1/64)*INT(64 X) */ + fp2 = packFloatx80(0, 0x3FFE, LIT64(0xB17217F7D1CF79AC)); /* LOG2 */ + fp0 = floatx80_mul(fp0, fp2, status); /* R */ + + /* EXPR */ + fp1 = floatx80_mul(fp0, fp0, status); /* S = R*R */ + fp2 = float64_to_floatx80(make_float64(0x3F56C16D6F7BD0B2), + status); /* A5 */ + fp3 = float64_to_floatx80(make_float64(0x3F811112302C712C), + status); /* A4 */ + fp2 = floatx80_mul(fp2, fp1, status); /* S*A5 */ + fp3 = floatx80_mul(fp3, fp1, status); /* S*A4 */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3FA5555555554CC1), status), + status); /* A3+S*A5 */ + fp3 = floatx80_add(fp3, float64_to_floatx80( + make_float64(0x3FC5555555554A54), status), + status); /* A2+S*A4 */ + fp2 = floatx80_mul(fp2, fp1, status); /* S*(A3+S*A5) */ + fp3 = floatx80_mul(fp3, fp1, status); /* S*(A2+S*A4) */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3FE0000000000000), status), + status); /* A1+S*(A3+S*A5) */ + fp3 = floatx80_mul(fp3, fp0, status); /* R*S*(A2+S*A4) */ + + fp2 = floatx80_mul(fp2, fp1, status); /* S*(A1+S*(A3+S*A5)) */ + fp0 = floatx80_add(fp0, fp3, status); /* R+R*S*(A2+S*A4) */ + fp0 = floatx80_add(fp0, fp2, status); /* EXP(R) - 1 */ + + fp0 = floatx80_mul(fp0, fact1, status); + fp0 = floatx80_add(fp0, fact2, status); + fp0 = floatx80_add(fp0, fact1, status); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_mul(fp0, adjfact, status); + + float_raise(float_flag_inexact, status); + + return a; + } +} + +/*---------------------------------------------------------------------------- + | 10 to x + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_tentox(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact, n, j, l, m, m1; + floatx80 fp0, fp1, fp2, fp3, adjfact, fact1, fact2; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig << 1)) { + return propagateFloatx80NaNOneArg(a, status); + } + if (aSign) { + return packFloatx80(0, 0, 0); + } + return packFloatx80(0, floatx80_infinity.high, + floatx80_infinity.low); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(0, one_exp, one_sig); + } + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + status->float_rounding_mode = float_round_nearest_even; + status->floatx80_rounding_precision = 80; + + fp0 = a; + + compact = floatx80_make_compact(aExp, aSig); + + if (compact < 0x3FB98000 || compact > 0x400B9B07) { + /* |X| > 16480 LOG2/LOG10 or |X| < 2^(-70) */ + if (compact > 0x3FFF8000) { /* |X| > 16480 */ + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + if (aSign) { + return roundAndPackFloatx80(status->floatx80_rounding_precision, + 0, -0x1000, aSig, 0, status); + } else { + return roundAndPackFloatx80(status->floatx80_rounding_precision, + 0, 0x8000, aSig, 0, status); + } + } else { /* |X| < 2^(-70) */ + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, float32_to_floatx80( + make_float32(0x3F800000), status), + status); /* 1 + X */ + + float_raise(float_flag_inexact, status); + + return a; + } + } else { /* 2^(-70) <= |X| <= 16480 LOG 2 / LOG 10 */ + fp1 = fp0; /* X */ + fp1 = floatx80_mul(fp1, float64_to_floatx80( + make_float64(0x406A934F0979A371), + status), status); /* X*64*LOG10/LOG2 */ + n = floatx80_to_int32(fp1, status); /* N=INT(X*64*LOG10/LOG2) */ + fp1 = int32_to_floatx80(n, status); + + j = n & 0x3F; + l = n / 64; /* NOTE: this is really arithmetic right shift by 6 */ + if (n < 0 && j) { + /* arithmetic right shift is division and + * round towards minus infinity + */ + l--; + } + m = l / 2; /* NOTE: this is really arithmetic right shift by 1 */ + if (l < 0 && (l & 1)) { + /* arithmetic right shift is division and + * round towards minus infinity + */ + m--; + } + m1 = l - m; + m1 += 0x3FFF; /* ADJFACT IS 2^(M') */ + + adjfact = packFloatx80(0, m1, one_sig); + fact1 = exp2_tbl[j]; + fact1.high += m; + fact2.high = exp2_tbl2[j] >> 16; + fact2.high += m; + fact2.low = (uint64_t)(exp2_tbl2[j] & 0xFFFF); + fact2.low <<= 48; + + fp2 = fp1; /* N */ + fp1 = floatx80_mul(fp1, float64_to_floatx80( + make_float64(0x3F734413509F8000), status), + status); /* N*(LOG2/64LOG10)_LEAD */ + fp3 = packFloatx80(1, 0x3FCD, LIT64(0xC0219DC1DA994FD2)); + fp2 = floatx80_mul(fp2, fp3, status); /* N*(LOG2/64LOG10)_TRAIL */ + fp0 = floatx80_sub(fp0, fp1, status); /* X - N L_LEAD */ + fp0 = floatx80_sub(fp0, fp2, status); /* X - N L_TRAIL */ + fp2 = packFloatx80(0, 0x4000, LIT64(0x935D8DDDAAA8AC17)); /* LOG10 */ + fp0 = floatx80_mul(fp0, fp2, status); /* R */ + + /* EXPR */ + fp1 = floatx80_mul(fp0, fp0, status); /* S = R*R */ + fp2 = float64_to_floatx80(make_float64(0x3F56C16D6F7BD0B2), + status); /* A5 */ + fp3 = float64_to_floatx80(make_float64(0x3F811112302C712C), + status); /* A4 */ + fp2 = floatx80_mul(fp2, fp1, status); /* S*A5 */ + fp3 = floatx80_mul(fp3, fp1, status); /* S*A4 */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3FA5555555554CC1), status), + status); /* A3+S*A5 */ + fp3 = floatx80_add(fp3, float64_to_floatx80( + make_float64(0x3FC5555555554A54), status), + status); /* A2+S*A4 */ + fp2 = floatx80_mul(fp2, fp1, status); /* S*(A3+S*A5) */ + fp3 = floatx80_mul(fp3, fp1, status); /* S*(A2+S*A4) */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3FE0000000000000), status), + status); /* A1+S*(A3+S*A5) */ + fp3 = floatx80_mul(fp3, fp0, status); /* R*S*(A2+S*A4) */ + + fp2 = floatx80_mul(fp2, fp1, status); /* S*(A1+S*(A3+S*A5)) */ + fp0 = floatx80_add(fp0, fp3, status); /* R+R*S*(A2+S*A4) */ + fp0 = floatx80_add(fp0, fp2, status); /* EXP(R) - 1 */ + + fp0 = floatx80_mul(fp0, fact1, status); + fp0 = floatx80_add(fp0, fact2, status); + fp0 = floatx80_add(fp0, fact1, status); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_mul(fp0, adjfact, status); + + float_raise(float_flag_inexact, status); + + return a; + } +} + +/*---------------------------------------------------------------------------- + | Tangent + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_tan(floatx80 a, float_status *status) +{ + flag aSign, xSign; + int32_t aExp, xExp; + uint64_t aSig, xSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact, l, n, j; + floatx80 fp0, fp1, fp2, fp3, fp4, fp5, invtwopi, twopi1, twopi2; + float32 twoto63; + flag endflag; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig << 1)) { + return propagateFloatx80NaNOneArg(a, status); + } + float_raise(float_flag_invalid, status); + return floatx80_default_nan(status); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(aSign, 0, 0); + } + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + status->float_rounding_mode = float_round_nearest_even; + status->floatx80_rounding_precision = 80; + + compact = floatx80_make_compact(aExp, aSig); + + fp0 = a; + + if (compact < 0x3FD78000 || compact > 0x4004BC7E) { + /* 2^(-40) > |X| > 15 PI */ + if (compact > 0x3FFF8000) { /* |X| >= 15 PI */ + /* REDUCEX */ + fp1 = packFloatx80(0, 0, 0); + if (compact == 0x7FFEFFFF) { + twopi1 = packFloatx80(aSign ^ 1, 0x7FFE, + LIT64(0xC90FDAA200000000)); + twopi2 = packFloatx80(aSign ^ 1, 0x7FDC, + LIT64(0x85A308D300000000)); + fp0 = floatx80_add(fp0, twopi1, status); + fp1 = fp0; + fp0 = floatx80_add(fp0, twopi2, status); + fp1 = floatx80_sub(fp1, fp0, status); + fp1 = floatx80_add(fp1, twopi2, status); + } + loop: + xSign = extractFloatx80Sign(fp0); + xExp = extractFloatx80Exp(fp0); + xExp -= 0x3FFF; + if (xExp <= 28) { + l = 0; + endflag = 1; + } else { + l = xExp - 27; + endflag = 0; + } + invtwopi = packFloatx80(0, 0x3FFE - l, + LIT64(0xA2F9836E4E44152A)); /* INVTWOPI */ + twopi1 = packFloatx80(0, 0x3FFF + l, LIT64(0xC90FDAA200000000)); + twopi2 = packFloatx80(0, 0x3FDD + l, LIT64(0x85A308D300000000)); + + /* SIGN(INARG)*2^63 IN SGL */ + twoto63 = packFloat32(xSign, 0xBE, 0); + + fp2 = floatx80_mul(fp0, invtwopi, status); + fp2 = floatx80_add(fp2, float32_to_floatx80(twoto63, status), + status); /* THE FRACT PART OF FP2 IS ROUNDED */ + fp2 = floatx80_sub(fp2, float32_to_floatx80(twoto63, status), + status); /* FP2 is N */ + fp4 = floatx80_mul(twopi1, fp2, status); /* W = N*P1 */ + fp5 = floatx80_mul(twopi2, fp2, status); /* w = N*P2 */ + fp3 = floatx80_add(fp4, fp5, status); /* FP3 is P */ + fp4 = floatx80_sub(fp4, fp3, status); /* W-P */ + fp0 = floatx80_sub(fp0, fp3, status); /* FP0 is A := R - P */ + fp4 = floatx80_add(fp4, fp5, status); /* FP4 is p = (W-P)+w */ + fp3 = fp0; /* FP3 is A */ + fp1 = floatx80_sub(fp1, fp4, status); /* FP1 is a := r - p */ + fp0 = floatx80_add(fp0, fp1, status); /* FP0 is R := A+a */ + + if (endflag > 0) { + n = floatx80_to_int32(fp2, status); + goto tancont; + } + fp3 = floatx80_sub(fp3, fp0, status); /* A-R */ + fp1 = floatx80_add(fp1, fp3, status); /* FP1 is r := (A-R)+a */ + goto loop; + } else { + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_move(a, status); + + float_raise(float_flag_inexact, status); + + return a; + } + } else { + fp1 = floatx80_mul(fp0, float64_to_floatx80( + make_float64(0x3FE45F306DC9C883), status), + status); /* X*2/PI */ + + n = floatx80_to_int32(fp1, status); + j = 32 + n; + + fp0 = floatx80_sub(fp0, pi_tbl[j], status); /* X-Y1 */ + fp0 = floatx80_sub(fp0, float32_to_floatx80(pi_tbl2[j], status), + status); /* FP0 IS R = (X-Y1)-Y2 */ + + tancont: + if (n & 1) { + /* NODD */ + fp1 = fp0; /* R */ + fp0 = floatx80_mul(fp0, fp0, status); /* S = R*R */ + fp3 = float64_to_floatx80(make_float64(0x3EA0B759F50F8688), + status); /* Q4 */ + fp2 = float64_to_floatx80(make_float64(0xBEF2BAA5A8924F04), + status); /* P3 */ + fp3 = floatx80_mul(fp3, fp0, status); /* SQ4 */ + fp2 = floatx80_mul(fp2, fp0, status); /* SP3 */ + fp3 = floatx80_add(fp3, float64_to_floatx80( + make_float64(0xBF346F59B39BA65F), status), + status); /* Q3+SQ4 */ + fp4 = packFloatx80(0, 0x3FF6, LIT64(0xE073D3FC199C4A00)); + fp2 = floatx80_add(fp2, fp4, status); /* P2+SP3 */ + fp3 = floatx80_mul(fp3, fp0, status); /* S(Q3+SQ4) */ + fp2 = floatx80_mul(fp2, fp0, status); /* S(P2+SP3) */ + fp4 = packFloatx80(0, 0x3FF9, LIT64(0xD23CD68415D95FA1)); + fp3 = floatx80_add(fp3, fp4, status); /* Q2+S(Q3+SQ4) */ + fp4 = packFloatx80(1, 0x3FFC, LIT64(0x8895A6C5FB423BCA)); + fp2 = floatx80_add(fp2, fp4, status); /* P1+S(P2+SP3) */ + fp3 = floatx80_mul(fp3, fp0, status); /* S(Q2+S(Q3+SQ4)) */ + fp2 = floatx80_mul(fp2, fp0, status); /* S(P1+S(P2+SP3)) */ + fp4 = packFloatx80(1, 0x3FFD, LIT64(0xEEF57E0DA84BC8CE)); + fp3 = floatx80_add(fp3, fp4, status); /* Q1+S(Q2+S(Q3+SQ4)) */ + fp2 = floatx80_mul(fp2, fp1, status); /* RS(P1+S(P2+SP3)) */ + fp0 = floatx80_mul(fp0, fp3, status); /* S(Q1+S(Q2+S(Q3+SQ4))) */ + fp1 = floatx80_add(fp1, fp2, status); /* R+RS(P1+S(P2+SP3)) */ + fp0 = floatx80_add(fp0, float32_to_floatx80( + make_float32(0x3F800000), status), + status); /* 1+S(Q1+S(Q2+S(Q3+SQ4))) */ + + xSign = extractFloatx80Sign(fp1); + xExp = extractFloatx80Exp(fp1); + xSig = extractFloatx80Frac(fp1); + xSign ^= 1; + fp1 = packFloatx80(xSign, xExp, xSig); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_div(fp0, fp1, status); + + float_raise(float_flag_inexact, status); + + return a; + } else { + fp1 = floatx80_mul(fp0, fp0, status); /* S = R*R */ + fp3 = float64_to_floatx80(make_float64(0x3EA0B759F50F8688), + status); /* Q4 */ + fp2 = float64_to_floatx80(make_float64(0xBEF2BAA5A8924F04), + status); /* P3 */ + fp3 = floatx80_mul(fp3, fp1, status); /* SQ4 */ + fp2 = floatx80_mul(fp2, fp1, status); /* SP3 */ + fp3 = floatx80_add(fp3, float64_to_floatx80( + make_float64(0xBF346F59B39BA65F), status), + status); /* Q3+SQ4 */ + fp4 = packFloatx80(0, 0x3FF6, LIT64(0xE073D3FC199C4A00)); + fp2 = floatx80_add(fp2, fp4, status); /* P2+SP3 */ + fp3 = floatx80_mul(fp3, fp1, status); /* S(Q3+SQ4) */ + fp2 = floatx80_mul(fp2, fp1, status); /* S(P2+SP3) */ + fp4 = packFloatx80(0, 0x3FF9, LIT64(0xD23CD68415D95FA1)); + fp3 = floatx80_add(fp3, fp4, status); /* Q2+S(Q3+SQ4) */ + fp4 = packFloatx80(1, 0x3FFC, LIT64(0x8895A6C5FB423BCA)); + fp2 = floatx80_add(fp2, fp4, status); /* P1+S(P2+SP3) */ + fp3 = floatx80_mul(fp3, fp1, status); /* S(Q2+S(Q3+SQ4)) */ + fp2 = floatx80_mul(fp2, fp1, status); /* S(P1+S(P2+SP3)) */ + fp4 = packFloatx80(1, 0x3FFD, LIT64(0xEEF57E0DA84BC8CE)); + fp3 = floatx80_add(fp3, fp4, status); /* Q1+S(Q2+S(Q3+SQ4)) */ + fp2 = floatx80_mul(fp2, fp0, status); /* RS(P1+S(P2+SP3)) */ + fp1 = floatx80_mul(fp1, fp3, status); /* S(Q1+S(Q2+S(Q3+SQ4))) */ + fp0 = floatx80_add(fp0, fp2, status); /* R+RS(P1+S(P2+SP3)) */ + fp1 = floatx80_add(fp1, float32_to_floatx80( + make_float32(0x3F800000), status), + status); /* 1+S(Q1+S(Q2+S(Q3+SQ4))) */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_div(fp0, fp1, status); + + float_raise(float_flag_inexact, status); + + return a; + } + } +} + +/*---------------------------------------------------------------------------- + | Sine + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_sin(floatx80 a, float_status *status) +{ + flag aSign, xSign; + int32_t aExp, xExp; + uint64_t aSig, xSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact, l, n, j; + floatx80 fp0, fp1, fp2, fp3, fp4, fp5, x, invtwopi, twopi1, twopi2; + float32 posneg1, twoto63; + flag adjn, endflag; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig << 1)) { + return propagateFloatx80NaNOneArg(a, status); + } + float_raise(float_flag_invalid, status); + return floatx80_default_nan(status); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(aSign, 0, 0); + } + + adjn = 0; + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + status->float_rounding_mode = float_round_nearest_even; + status->floatx80_rounding_precision = 80; + + compact = floatx80_make_compact(aExp, aSig); + + fp0 = a; + + if (compact < 0x3FD78000 || compact > 0x4004BC7E) { + /* 2^(-40) > |X| > 15 PI */ + if (compact > 0x3FFF8000) { /* |X| >= 15 PI */ + /* REDUCEX */ + fp1 = packFloatx80(0, 0, 0); + if (compact == 0x7FFEFFFF) { + twopi1 = packFloatx80(aSign ^ 1, 0x7FFE, + LIT64(0xC90FDAA200000000)); + twopi2 = packFloatx80(aSign ^ 1, 0x7FDC, + LIT64(0x85A308D300000000)); + fp0 = floatx80_add(fp0, twopi1, status); + fp1 = fp0; + fp0 = floatx80_add(fp0, twopi2, status); + fp1 = floatx80_sub(fp1, fp0, status); + fp1 = floatx80_add(fp1, twopi2, status); + } + loop: + xSign = extractFloatx80Sign(fp0); + xExp = extractFloatx80Exp(fp0); + xExp -= 0x3FFF; + if (xExp <= 28) { + l = 0; + endflag = 1; + } else { + l = xExp - 27; + endflag = 0; + } + invtwopi = packFloatx80(0, 0x3FFE - l, + LIT64(0xA2F9836E4E44152A)); /* INVTWOPI */ + twopi1 = packFloatx80(0, 0x3FFF + l, LIT64(0xC90FDAA200000000)); + twopi2 = packFloatx80(0, 0x3FDD + l, LIT64(0x85A308D300000000)); + + /* SIGN(INARG)*2^63 IN SGL */ + twoto63 = packFloat32(xSign, 0xBE, 0); + + fp2 = floatx80_mul(fp0, invtwopi, status); + fp2 = floatx80_add(fp2, float32_to_floatx80(twoto63, status), + status); /* THE FRACT PART OF FP2 IS ROUNDED */ + fp2 = floatx80_sub(fp2, float32_to_floatx80(twoto63, status), + status); /* FP2 is N */ + fp4 = floatx80_mul(twopi1, fp2, status); /* W = N*P1 */ + fp5 = floatx80_mul(twopi2, fp2, status); /* w = N*P2 */ + fp3 = floatx80_add(fp4, fp5, status); /* FP3 is P */ + fp4 = floatx80_sub(fp4, fp3, status); /* W-P */ + fp0 = floatx80_sub(fp0, fp3, status); /* FP0 is A := R - P */ + fp4 = floatx80_add(fp4, fp5, status); /* FP4 is p = (W-P)+w */ + fp3 = fp0; /* FP3 is A */ + fp1 = floatx80_sub(fp1, fp4, status); /* FP1 is a := r - p */ + fp0 = floatx80_add(fp0, fp1, status); /* FP0 is R := A+a */ + + if (endflag > 0) { + n = floatx80_to_int32(fp2, status); + goto sincont; + } + fp3 = floatx80_sub(fp3, fp0, status); /* A-R */ + fp1 = floatx80_add(fp1, fp3, status); /* FP1 is r := (A-R)+a */ + goto loop; + } else { + /* SINSM */ + fp0 = float32_to_floatx80(make_float32(0x3F800000), + status); /* 1 */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + if (adjn) { + /* COSTINY */ + a = floatx80_sub(fp0, float32_to_floatx80( + make_float32(0x00800000), status), status); + } else { + /* SINTINY */ + a = floatx80_move(a, status); + } + float_raise(float_flag_inexact, status); + + return a; + } + } else { + fp1 = floatx80_mul(fp0, float64_to_floatx80( + make_float64(0x3FE45F306DC9C883), status), + status); /* X*2/PI */ + + n = floatx80_to_int32(fp1, status); + j = 32 + n; + + fp0 = floatx80_sub(fp0, pi_tbl[j], status); /* X-Y1 */ + fp0 = floatx80_sub(fp0, float32_to_floatx80(pi_tbl2[j], status), + status); /* FP0 IS R = (X-Y1)-Y2 */ + + sincont: + if ((n + adjn) & 1) { + /* COSPOLY */ + fp0 = floatx80_mul(fp0, fp0, status); /* FP0 IS S */ + fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS T */ + fp2 = float64_to_floatx80(make_float64(0x3D2AC4D0D6011EE3), + status); /* B8 */ + fp3 = float64_to_floatx80(make_float64(0xBDA9396F9F45AC19), + status); /* B7 */ + + xSign = extractFloatx80Sign(fp0); /* X IS S */ + xExp = extractFloatx80Exp(fp0); + xSig = extractFloatx80Frac(fp0); + + if (((n + adjn) >> 1) & 1) { + xSign ^= 1; + posneg1 = make_float32(0xBF800000); /* -1 */ + } else { + xSign ^= 0; + posneg1 = make_float32(0x3F800000); /* 1 */ + } /* X IS NOW R'= SGN*R */ + + fp2 = floatx80_mul(fp2, fp1, status); /* TB8 */ + fp3 = floatx80_mul(fp3, fp1, status); /* TB7 */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3E21EED90612C972), status), + status); /* B6+TB8 */ + fp3 = floatx80_add(fp3, float64_to_floatx80( + make_float64(0xBE927E4FB79D9FCF), status), + status); /* B5+TB7 */ + fp2 = floatx80_mul(fp2, fp1, status); /* T(B6+TB8) */ + fp3 = floatx80_mul(fp3, fp1, status); /* T(B5+TB7) */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3EFA01A01A01D423), status), + status); /* B4+T(B6+TB8) */ + fp4 = packFloatx80(1, 0x3FF5, LIT64(0xB60B60B60B61D438)); + fp3 = floatx80_add(fp3, fp4, status); /* B3+T(B5+TB7) */ + fp2 = floatx80_mul(fp2, fp1, status); /* T(B4+T(B6+TB8)) */ + fp1 = floatx80_mul(fp1, fp3, status); /* T(B3+T(B5+TB7)) */ + fp4 = packFloatx80(0, 0x3FFA, LIT64(0xAAAAAAAAAAAAAB5E)); + fp2 = floatx80_add(fp2, fp4, status); /* B2+T(B4+T(B6+TB8)) */ + fp1 = floatx80_add(fp1, float32_to_floatx80( + make_float32(0xBF000000), status), + status); /* B1+T(B3+T(B5+TB7)) */ + fp0 = floatx80_mul(fp0, fp2, status); /* S(B2+T(B4+T(B6+TB8))) */ + fp0 = floatx80_add(fp0, fp1, status); /* [B1+T(B3+T(B5+TB7))]+ + * [S(B2+T(B4+T(B6+TB8)))] + */ + + x = packFloatx80(xSign, xExp, xSig); + fp0 = floatx80_mul(fp0, x, status); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, float32_to_floatx80(posneg1, status), status); + + float_raise(float_flag_inexact, status); + + return a; + } else { + /* SINPOLY */ + xSign = extractFloatx80Sign(fp0); /* X IS R */ + xExp = extractFloatx80Exp(fp0); + xSig = extractFloatx80Frac(fp0); + + xSign ^= ((n + adjn) >> 1) & 1; /* X IS NOW R'= SGN*R */ + + fp0 = floatx80_mul(fp0, fp0, status); /* FP0 IS S */ + fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS T */ + fp3 = float64_to_floatx80(make_float64(0xBD6AAA77CCC994F5), + status); /* A7 */ + fp2 = float64_to_floatx80(make_float64(0x3DE612097AAE8DA1), + status); /* A6 */ + fp3 = floatx80_mul(fp3, fp1, status); /* T*A7 */ + fp2 = floatx80_mul(fp2, fp1, status); /* T*A6 */ + fp3 = floatx80_add(fp3, float64_to_floatx80( + make_float64(0xBE5AE6452A118AE4), status), + status); /* A5+T*A7 */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3EC71DE3A5341531), status), + status); /* A4+T*A6 */ + fp3 = floatx80_mul(fp3, fp1, status); /* T(A5+TA7) */ + fp2 = floatx80_mul(fp2, fp1, status); /* T(A4+TA6) */ + fp3 = floatx80_add(fp3, float64_to_floatx80( + make_float64(0xBF2A01A01A018B59), status), + status); /* A3+T(A5+TA7) */ + fp4 = packFloatx80(0, 0x3FF8, LIT64(0x88888888888859AF)); + fp2 = floatx80_add(fp2, fp4, status); /* A2+T(A4+TA6) */ + fp1 = floatx80_mul(fp1, fp3, status); /* T(A3+T(A5+TA7)) */ + fp2 = floatx80_mul(fp2, fp0, status); /* S(A2+T(A4+TA6)) */ + fp4 = packFloatx80(1, 0x3FFC, LIT64(0xAAAAAAAAAAAAAA99)); + fp1 = floatx80_add(fp1, fp4, status); /* A1+T(A3+T(A5+TA7)) */ + fp1 = floatx80_add(fp1, fp2, + status); /* [A1+T(A3+T(A5+TA7))]+ + * [S(A2+T(A4+TA6))] + */ + + x = packFloatx80(xSign, xExp, xSig); + fp0 = floatx80_mul(fp0, x, status); /* R'*S */ + fp0 = floatx80_mul(fp0, fp1, status); /* SIN(R')-R' */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, x, status); + + float_raise(float_flag_inexact, status); + + return a; + } + } +} + +/*---------------------------------------------------------------------------- + | Cosine + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_cos(floatx80 a, float_status *status) +{ + flag aSign, xSign; + int32_t aExp, xExp; + uint64_t aSig, xSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact, l, n, j; + floatx80 fp0, fp1, fp2, fp3, fp4, fp5, x, invtwopi, twopi1, twopi2; + float32 posneg1, twoto63; + flag adjn, endflag; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig << 1)) { + return propagateFloatx80NaNOneArg(a, status); + } + float_raise(float_flag_invalid, status); + return floatx80_default_nan(status); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(0, one_exp, one_sig); + } + + adjn = 1; + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + status->float_rounding_mode = float_round_nearest_even; + status->floatx80_rounding_precision = 80; + + compact = floatx80_make_compact(aExp, aSig); + + fp0 = a; + + if (compact < 0x3FD78000 || compact > 0x4004BC7E) { + /* 2^(-40) > |X| > 15 PI */ + if (compact > 0x3FFF8000) { /* |X| >= 15 PI */ + /* REDUCEX */ + fp1 = packFloatx80(0, 0, 0); + if (compact == 0x7FFEFFFF) { + twopi1 = packFloatx80(aSign ^ 1, 0x7FFE, + LIT64(0xC90FDAA200000000)); + twopi2 = packFloatx80(aSign ^ 1, 0x7FDC, + LIT64(0x85A308D300000000)); + fp0 = floatx80_add(fp0, twopi1, status); + fp1 = fp0; + fp0 = floatx80_add(fp0, twopi2, status); + fp1 = floatx80_sub(fp1, fp0, status); + fp1 = floatx80_add(fp1, twopi2, status); + } + loop: + xSign = extractFloatx80Sign(fp0); + xExp = extractFloatx80Exp(fp0); + xExp -= 0x3FFF; + if (xExp <= 28) { + l = 0; + endflag = 1; + } else { + l = xExp - 27; + endflag = 0; + } + invtwopi = packFloatx80(0, 0x3FFE - l, + LIT64(0xA2F9836E4E44152A)); /* INVTWOPI */ + twopi1 = packFloatx80(0, 0x3FFF + l, LIT64(0xC90FDAA200000000)); + twopi2 = packFloatx80(0, 0x3FDD + l, LIT64(0x85A308D300000000)); + + /* SIGN(INARG)*2^63 IN SGL */ + twoto63 = packFloat32(xSign, 0xBE, 0); + + fp2 = floatx80_mul(fp0, invtwopi, status); + fp2 = floatx80_add(fp2, float32_to_floatx80(twoto63, status), + status); /* THE FRACT PART OF FP2 IS ROUNDED */ + fp2 = floatx80_sub(fp2, float32_to_floatx80(twoto63, status), + status); /* FP2 is N */ + fp4 = floatx80_mul(twopi1, fp2, status); /* W = N*P1 */ + fp5 = floatx80_mul(twopi2, fp2, status); /* w = N*P2 */ + fp3 = floatx80_add(fp4, fp5, status); /* FP3 is P */ + fp4 = floatx80_sub(fp4, fp3, status); /* W-P */ + fp0 = floatx80_sub(fp0, fp3, status); /* FP0 is A := R - P */ + fp4 = floatx80_add(fp4, fp5, status); /* FP4 is p = (W-P)+w */ + fp3 = fp0; /* FP3 is A */ + fp1 = floatx80_sub(fp1, fp4, status); /* FP1 is a := r - p */ + fp0 = floatx80_add(fp0, fp1, status); /* FP0 is R := A+a */ + + if (endflag > 0) { + n = floatx80_to_int32(fp2, status); + goto sincont; + } + fp3 = floatx80_sub(fp3, fp0, status); /* A-R */ + fp1 = floatx80_add(fp1, fp3, status); /* FP1 is r := (A-R)+a */ + goto loop; + } else { + /* SINSM */ + fp0 = float32_to_floatx80(make_float32(0x3F800000), status); /* 1 */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + if (adjn) { + /* COSTINY */ + a = floatx80_sub(fp0, float32_to_floatx80( + make_float32(0x00800000), status), + status); + } else { + /* SINTINY */ + a = floatx80_move(a, status); + } + float_raise(float_flag_inexact, status); + + return a; + } + } else { + fp1 = floatx80_mul(fp0, float64_to_floatx80( + make_float64(0x3FE45F306DC9C883), status), + status); /* X*2/PI */ + + n = floatx80_to_int32(fp1, status); + j = 32 + n; + + fp0 = floatx80_sub(fp0, pi_tbl[j], status); /* X-Y1 */ + fp0 = floatx80_sub(fp0, float32_to_floatx80(pi_tbl2[j], status), + status); /* FP0 IS R = (X-Y1)-Y2 */ + + sincont: + if ((n + adjn) & 1) { + /* COSPOLY */ + fp0 = floatx80_mul(fp0, fp0, status); /* FP0 IS S */ + fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS T */ + fp2 = float64_to_floatx80(make_float64(0x3D2AC4D0D6011EE3), + status); /* B8 */ + fp3 = float64_to_floatx80(make_float64(0xBDA9396F9F45AC19), + status); /* B7 */ + + xSign = extractFloatx80Sign(fp0); /* X IS S */ + xExp = extractFloatx80Exp(fp0); + xSig = extractFloatx80Frac(fp0); + + if (((n + adjn) >> 1) & 1) { + xSign ^= 1; + posneg1 = make_float32(0xBF800000); /* -1 */ + } else { + xSign ^= 0; + posneg1 = make_float32(0x3F800000); /* 1 */ + } /* X IS NOW R'= SGN*R */ + + fp2 = floatx80_mul(fp2, fp1, status); /* TB8 */ + fp3 = floatx80_mul(fp3, fp1, status); /* TB7 */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3E21EED90612C972), status), + status); /* B6+TB8 */ + fp3 = floatx80_add(fp3, float64_to_floatx80( + make_float64(0xBE927E4FB79D9FCF), status), + status); /* B5+TB7 */ + fp2 = floatx80_mul(fp2, fp1, status); /* T(B6+TB8) */ + fp3 = floatx80_mul(fp3, fp1, status); /* T(B5+TB7) */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3EFA01A01A01D423), status), + status); /* B4+T(B6+TB8) */ + fp4 = packFloatx80(1, 0x3FF5, LIT64(0xB60B60B60B61D438)); + fp3 = floatx80_add(fp3, fp4, status); /* B3+T(B5+TB7) */ + fp2 = floatx80_mul(fp2, fp1, status); /* T(B4+T(B6+TB8)) */ + fp1 = floatx80_mul(fp1, fp3, status); /* T(B3+T(B5+TB7)) */ + fp4 = packFloatx80(0, 0x3FFA, LIT64(0xAAAAAAAAAAAAAB5E)); + fp2 = floatx80_add(fp2, fp4, status); /* B2+T(B4+T(B6+TB8)) */ + fp1 = floatx80_add(fp1, float32_to_floatx80( + make_float32(0xBF000000), status), + status); /* B1+T(B3+T(B5+TB7)) */ + fp0 = floatx80_mul(fp0, fp2, status); /* S(B2+T(B4+T(B6+TB8))) */ + fp0 = floatx80_add(fp0, fp1, status); + /* [B1+T(B3+T(B5+TB7))]+[S(B2+T(B4+T(B6+TB8)))] */ + + x = packFloatx80(xSign, xExp, xSig); + fp0 = floatx80_mul(fp0, x, status); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, float32_to_floatx80(posneg1, status), status); + + float_raise(float_flag_inexact, status); + + return a; + } else { + /* SINPOLY */ + xSign = extractFloatx80Sign(fp0); /* X IS R */ + xExp = extractFloatx80Exp(fp0); + xSig = extractFloatx80Frac(fp0); + + xSign ^= ((n + adjn) >> 1) & 1; /* X IS NOW R'= SGN*R */ + + fp0 = floatx80_mul(fp0, fp0, status); /* FP0 IS S */ + fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS T */ + fp3 = float64_to_floatx80(make_float64(0xBD6AAA77CCC994F5), + status); /* A7 */ + fp2 = float64_to_floatx80(make_float64(0x3DE612097AAE8DA1), + status); /* A6 */ + fp3 = floatx80_mul(fp3, fp1, status); /* T*A7 */ + fp2 = floatx80_mul(fp2, fp1, status); /* T*A6 */ + fp3 = floatx80_add(fp3, float64_to_floatx80( + make_float64(0xBE5AE6452A118AE4), status), + status); /* A5+T*A7 */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3EC71DE3A5341531), status), + status); /* A4+T*A6 */ + fp3 = floatx80_mul(fp3, fp1, status); /* T(A5+TA7) */ + fp2 = floatx80_mul(fp2, fp1, status); /* T(A4+TA6) */ + fp3 = floatx80_add(fp3, float64_to_floatx80( + make_float64(0xBF2A01A01A018B59), status), + status); /* A3+T(A5+TA7) */ + fp4 = packFloatx80(0, 0x3FF8, LIT64(0x88888888888859AF)); + fp2 = floatx80_add(fp2, fp4, status); /* A2+T(A4+TA6) */ + fp1 = floatx80_mul(fp1, fp3, status); /* T(A3+T(A5+TA7)) */ + fp2 = floatx80_mul(fp2, fp0, status); /* S(A2+T(A4+TA6)) */ + fp4 = packFloatx80(1, 0x3FFC, LIT64(0xAAAAAAAAAAAAAA99)); + fp1 = floatx80_add(fp1, fp4, status); /* A1+T(A3+T(A5+TA7)) */ + fp1 = floatx80_add(fp1, fp2, status); + /* [A1+T(A3+T(A5+TA7))]+[S(A2+T(A4+TA6))] */ + + x = packFloatx80(xSign, xExp, xSig); + fp0 = floatx80_mul(fp0, x, status); /* R'*S */ + fp0 = floatx80_mul(fp0, fp1, status); /* SIN(R')-R' */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, x, status); + + float_raise(float_flag_inexact, status); + + return a; + } + } +} + +/*---------------------------------------------------------------------------- + | Arc tangent + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_atan(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact, tbl_index; + floatx80 fp0, fp1, fp2, fp3, xsave; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig << 1)) { + return propagateFloatx80NaNOneArg(a, status); + } + a = packFloatx80(aSign, piby2_exp, pi_sig); + float_raise(float_flag_inexact, status); + return floatx80_move(a, status); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(aSign, 0, 0); + } + + compact = floatx80_make_compact(aExp, aSig); + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + status->float_rounding_mode = float_round_nearest_even; + status->floatx80_rounding_precision = 80; + + if (compact < 0x3FFB8000 || compact > 0x4002FFFF) { + /* |X| >= 16 or |X| < 1/16 */ + if (compact > 0x3FFF8000) { /* |X| >= 16 */ + if (compact > 0x40638000) { /* |X| > 2^(100) */ + fp0 = packFloatx80(aSign, piby2_exp, pi_sig); + fp1 = packFloatx80(aSign, 0x0001, one_sig); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_sub(fp0, fp1, status); + + float_raise(float_flag_inexact, status); + + return a; + } else { + fp0 = a; + fp1 = packFloatx80(1, one_exp, one_sig); /* -1 */ + fp1 = floatx80_div(fp1, fp0, status); /* X' = -1/X */ + xsave = fp1; + fp0 = floatx80_mul(fp1, fp1, status); /* Y = X'*X' */ + fp1 = floatx80_mul(fp0, fp0, status); /* Z = Y*Y */ + fp3 = float64_to_floatx80(make_float64(0xBFB70BF398539E6A), + status); /* C5 */ + fp2 = float64_to_floatx80(make_float64(0x3FBC7187962D1D7D), + status); /* C4 */ + fp3 = floatx80_mul(fp3, fp1, status); /* Z*C5 */ + fp2 = floatx80_mul(fp2, fp1, status); /* Z*C4 */ + fp3 = floatx80_add(fp3, float64_to_floatx80( + make_float64(0xBFC24924827107B8), status), + status); /* C3+Z*C5 */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3FC999999996263E), status), + status); /* C2+Z*C4 */ + fp1 = floatx80_mul(fp1, fp3, status); /* Z*(C3+Z*C5) */ + fp2 = floatx80_mul(fp2, fp0, status); /* Y*(C2+Z*C4) */ + fp1 = floatx80_add(fp1, float64_to_floatx80( + make_float64(0xBFD5555555555536), status), + status); /* C1+Z*(C3+Z*C5) */ + fp0 = floatx80_mul(fp0, xsave, status); /* X'*Y */ + /* [Y*(C2+Z*C4)]+[C1+Z*(C3+Z*C5)] */ + fp1 = floatx80_add(fp1, fp2, status); + /* X'*Y*([B1+Z*(B3+Z*B5)]+[Y*(B2+Z*(B4+Z*B6))]) ?? */ + fp0 = floatx80_mul(fp0, fp1, status); + fp0 = floatx80_add(fp0, xsave, status); + fp1 = packFloatx80(aSign, piby2_exp, pi_sig); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, fp1, status); + + float_raise(float_flag_inexact, status); + + return a; + } + } else { /* |X| < 1/16 */ + if (compact < 0x3FD78000) { /* |X| < 2^(-40) */ + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_move(a, status); + + float_raise(float_flag_inexact, status); + + return a; + } else { + fp0 = a; + xsave = a; + fp0 = floatx80_mul(fp0, fp0, status); /* Y = X*X */ + fp1 = floatx80_mul(fp0, fp0, status); /* Z = Y*Y */ + fp2 = float64_to_floatx80(make_float64(0x3FB344447F876989), + status); /* B6 */ + fp3 = float64_to_floatx80(make_float64(0xBFB744EE7FAF45DB), + status); /* B5 */ + fp2 = floatx80_mul(fp2, fp1, status); /* Z*B6 */ + fp3 = floatx80_mul(fp3, fp1, status); /* Z*B5 */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3FBC71C646940220), status), + status); /* B4+Z*B6 */ + fp3 = floatx80_add(fp3, float64_to_floatx80( + make_float64(0xBFC24924921872F9), + status), status); /* B3+Z*B5 */ + fp2 = floatx80_mul(fp2, fp1, status); /* Z*(B4+Z*B6) */ + fp1 = floatx80_mul(fp1, fp3, status); /* Z*(B3+Z*B5) */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3FC9999999998FA9), status), + status); /* B2+Z*(B4+Z*B6) */ + fp1 = floatx80_add(fp1, float64_to_floatx80( + make_float64(0xBFD5555555555555), status), + status); /* B1+Z*(B3+Z*B5) */ + fp2 = floatx80_mul(fp2, fp0, status); /* Y*(B2+Z*(B4+Z*B6)) */ + fp0 = floatx80_mul(fp0, xsave, status); /* X*Y */ + /* [B1+Z*(B3+Z*B5)]+[Y*(B2+Z*(B4+Z*B6))] */ + fp1 = floatx80_add(fp1, fp2, status); + /* X*Y*([B1+Z*(B3+Z*B5)]+[Y*(B2+Z*(B4+Z*B6))]) */ + fp0 = floatx80_mul(fp0, fp1, status); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, xsave, status); + + float_raise(float_flag_inexact, status); + + return a; + } + } + } else { + aSig &= LIT64(0xF800000000000000); + aSig |= LIT64(0x0400000000000000); + xsave = packFloatx80(aSign, aExp, aSig); /* F */ + fp0 = a; + fp1 = a; /* X */ + fp2 = packFloatx80(0, one_exp, one_sig); /* 1 */ + fp1 = floatx80_mul(fp1, xsave, status); /* X*F */ + fp0 = floatx80_sub(fp0, xsave, status); /* X-F */ + fp1 = floatx80_add(fp1, fp2, status); /* 1 + X*F */ + fp0 = floatx80_div(fp0, fp1, status); /* U = (X-F)/(1+X*F) */ + + tbl_index = compact; + + tbl_index &= 0x7FFF0000; + tbl_index -= 0x3FFB0000; + tbl_index >>= 1; + tbl_index += compact & 0x00007800; + tbl_index >>= 11; + + fp3 = atan_tbl[tbl_index]; + + fp3.high |= aSign ? 0x8000 : 0; /* ATAN(F) */ + + fp1 = floatx80_mul(fp0, fp0, status); /* V = U*U */ + fp2 = float64_to_floatx80(make_float64(0xBFF6687E314987D8), + status); /* A3 */ + fp2 = floatx80_add(fp2, fp1, status); /* A3+V */ + fp2 = floatx80_mul(fp2, fp1, status); /* V*(A3+V) */ + fp1 = floatx80_mul(fp1, fp0, status); /* U*V */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x4002AC6934A26DB3), status), + status); /* A2+V*(A3+V) */ + fp1 = floatx80_mul(fp1, float64_to_floatx80( + make_float64(0xBFC2476F4E1DA28E), status), + status); /* A1+U*V */ + fp1 = floatx80_mul(fp1, fp2, status); /* A1*U*V*(A2+V*(A3+V)) */ + fp0 = floatx80_add(fp0, fp1, status); /* ATAN(U) */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, fp3, status); /* ATAN(X) */ + + float_raise(float_flag_inexact, status); + + return a; + } +} + +/*---------------------------------------------------------------------------- + | Arc sine + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_asin(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact; + floatx80 fp0, fp1, fp2, one; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF && (uint64_t) (aSig << 1)) { + return propagateFloatx80NaNOneArg(a, status); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(aSign, 0, 0); + } + + compact = floatx80_make_compact(aExp, aSig); + + if (compact >= 0x3FFF8000) { /* |X| >= 1 */ + if (aExp == one_exp && aSig == one_sig) { /* |X| == 1 */ + float_raise(float_flag_inexact, status); + a = packFloatx80(aSign, piby2_exp, pi_sig); + return floatx80_move(a, status); + } else { /* |X| > 1 */ + float_raise(float_flag_invalid, status); + return floatx80_default_nan(status); + } + + } /* |X| < 1 */ + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + status->float_rounding_mode = float_round_nearest_even; + status->floatx80_rounding_precision = 80; + + one = packFloatx80(0, one_exp, one_sig); + fp0 = a; + + fp1 = floatx80_sub(one, fp0, status); /* 1 - X */ + fp2 = floatx80_add(one, fp0, status); /* 1 + X */ + fp1 = floatx80_mul(fp2, fp1, status); /* (1+X)*(1-X) */ + fp1 = floatx80_sqrt(fp1, status); /* SQRT((1+X)*(1-X)) */ + fp0 = floatx80_div(fp0, fp1, status); /* X/SQRT((1+X)*(1-X)) */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_atan(fp0, status); /* ATAN(X/SQRT((1+X)*(1-X))) */ + + float_raise(float_flag_inexact, status); + + return a; +} + +/*---------------------------------------------------------------------------- + | Arc cosine + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_acos(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact; + floatx80 fp0, fp1, one; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF && (uint64_t) (aSig << 1)) { + return propagateFloatx80NaNOneArg(a, status); + } + if (aExp == 0 && aSig == 0) { + float_raise(float_flag_inexact, status); + return roundAndPackFloatx80(status->floatx80_rounding_precision, 0, + piby2_exp, pi_sig, 0, status); + } + + compact = floatx80_make_compact(aExp, aSig); + + if (compact >= 0x3FFF8000) { /* |X| >= 1 */ + if (aExp == one_exp && aSig == one_sig) { /* |X| == 1 */ + if (aSign) { /* X == -1 */ + a = packFloatx80(0, pi_exp, pi_sig); + float_raise(float_flag_inexact, status); + return floatx80_move(a, status); + } else { /* X == +1 */ + return packFloatx80(0, 0, 0); + } + } else { /* |X| > 1 */ + float_raise(float_flag_invalid, status); + return floatx80_default_nan(status); + } + } /* |X| < 1 */ + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + status->float_rounding_mode = float_round_nearest_even; + status->floatx80_rounding_precision = 80; + + one = packFloatx80(0, one_exp, one_sig); + fp0 = a; + + fp1 = floatx80_add(one, fp0, status); /* 1 + X */ + fp0 = floatx80_sub(one, fp0, status); /* 1 - X */ + fp0 = floatx80_div(fp0, fp1, status); /* (1-X)/(1+X) */ + fp0 = floatx80_sqrt(fp0, status); /* SQRT((1-X)/(1+X)) */ + fp0 = floatx80_atan(fp0, status); /* ATAN(SQRT((1-X)/(1+X))) */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, fp0, status); /* 2 * ATAN(SQRT((1-X)/(1+X))) */ + + float_raise(float_flag_inexact, status); + + return a; +} + +/*---------------------------------------------------------------------------- + | Hyperbolic arc tangent + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_atanh(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact; + floatx80 fp0, fp1, fp2, one; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF && (uint64_t) (aSig << 1)) { + return propagateFloatx80NaNOneArg(a, status); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(aSign, 0, 0); + } + + compact = floatx80_make_compact(aExp, aSig); + + if (compact >= 0x3FFF8000) { /* |X| >= 1 */ + if (aExp == one_exp && aSig == one_sig) { /* |X| == 1 */ + float_raise(float_flag_divbyzero, status); + return packFloatx80(aSign, floatx80_infinity.high, + floatx80_infinity.low); + } else { /* |X| > 1 */ + float_raise(float_flag_invalid, status); + return floatx80_default_nan(status); + } + } /* |X| < 1 */ + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + status->float_rounding_mode = float_round_nearest_even; + status->floatx80_rounding_precision = 80; + + one = packFloatx80(0, one_exp, one_sig); + fp2 = packFloatx80(aSign, 0x3FFE, one_sig); /* SIGN(X) * (1/2) */ + fp0 = packFloatx80(0, aExp, aSig); /* Y = |X| */ + fp1 = packFloatx80(1, aExp, aSig); /* -Y */ + fp0 = floatx80_add(fp0, fp0, status); /* 2Y */ + fp1 = floatx80_add(fp1, one, status); /* 1-Y */ + fp0 = floatx80_div(fp0, fp1, status); /* Z = 2Y/(1-Y) */ + fp0 = floatx80_lognp1(fp0, status); /* LOG1P(Z) */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_mul(fp0, fp2, + status); /* ATANH(X) = SIGN(X) * (1/2) * LOG1P(Z) */ + + float_raise(float_flag_inexact, status); + + return a; +} + +/*---------------------------------------------------------------------------- + | e to x minus 1 + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_etoxm1(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact, n, j, m, m1; + floatx80 fp0, fp1, fp2, fp3, l2, sc, onebysc; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig << 1)) { + return propagateFloatx80NaNOneArg(a, status); + } + if (aSign) { + return packFloatx80(aSign, one_exp, one_sig); + } + return packFloatx80(0, floatx80_infinity.high, + floatx80_infinity.low); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(aSign, 0, 0); + } + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + status->float_rounding_mode = float_round_nearest_even; + status->floatx80_rounding_precision = 80; + + if (aExp >= 0x3FFD) { /* |X| >= 1/4 */ + compact = floatx80_make_compact(aExp, aSig); + + if (compact <= 0x4004C215) { /* |X| <= 70 log2 */ + fp0 = a; + fp1 = a; + fp0 = floatx80_mul(fp0, float32_to_floatx80( + make_float32(0x42B8AA3B), status), + status); /* 64/log2 * X */ + n = floatx80_to_int32(fp0, status); /* int(64/log2*X) */ + fp0 = int32_to_floatx80(n, status); + + j = n & 0x3F; /* J = N mod 64 */ + m = n / 64; /* NOTE: this is really arithmetic right shift by 6 */ + if (n < 0 && j) { + /* arithmetic right shift is division and + * round towards minus infinity + */ + m--; + } + m1 = -m; + /*m += 0x3FFF; // biased exponent of 2^(M) */ + /*m1 += 0x3FFF; // biased exponent of -2^(-M) */ + + fp2 = fp0; /* N */ + fp0 = floatx80_mul(fp0, float32_to_floatx80( + make_float32(0xBC317218), status), + status); /* N * L1, L1 = lead(-log2/64) */ + l2 = packFloatx80(0, 0x3FDC, LIT64(0x82E308654361C4C6)); + fp2 = floatx80_mul(fp2, l2, status); /* N * L2, L1+L2 = -log2/64 */ + fp0 = floatx80_add(fp0, fp1, status); /* X + N*L1 */ + fp0 = floatx80_add(fp0, fp2, status); /* R */ + + fp1 = floatx80_mul(fp0, fp0, status); /* S = R*R */ + fp2 = float32_to_floatx80(make_float32(0x3950097B), + status); /* A6 */ + fp2 = floatx80_mul(fp2, fp1, status); /* fp2 is S*A6 */ + fp3 = floatx80_mul(float32_to_floatx80(make_float32(0x3AB60B6A), + status), fp1, status); /* fp3 is S*A5 */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3F81111111174385), status), + status); /* fp2 IS A4+S*A6 */ + fp3 = floatx80_add(fp3, float64_to_floatx80( + make_float64(0x3FA5555555554F5A), status), + status); /* fp3 is A3+S*A5 */ + fp2 = floatx80_mul(fp2, fp1, status); /* fp2 IS S*(A4+S*A6) */ + fp3 = floatx80_mul(fp3, fp1, status); /* fp3 IS S*(A3+S*A5) */ + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3FC5555555555555), status), + status); /* fp2 IS A2+S*(A4+S*A6) */ + fp3 = floatx80_add(fp3, float32_to_floatx80( + make_float32(0x3F000000), status), + status); /* fp3 IS A1+S*(A3+S*A5) */ + fp2 = floatx80_mul(fp2, fp1, + status); /* fp2 IS S*(A2+S*(A4+S*A6)) */ + fp1 = floatx80_mul(fp1, fp3, + status); /* fp1 IS S*(A1+S*(A3+S*A5)) */ + fp2 = floatx80_mul(fp2, fp0, + status); /* fp2 IS R*S*(A2+S*(A4+S*A6)) */ + fp0 = floatx80_add(fp0, fp1, + status); /* fp0 IS R+S*(A1+S*(A3+S*A5)) */ + fp0 = floatx80_add(fp0, fp2, status); /* fp0 IS EXP(R) - 1 */ + + fp0 = floatx80_mul(fp0, exp_tbl[j], + status); /* 2^(J/64)*(Exp(R)-1) */ + + if (m >= 64) { + fp1 = float32_to_floatx80(exp_tbl2[j], status); + onebysc = packFloatx80(1, m1 + 0x3FFF, one_sig); /* -2^(-M) */ + fp1 = floatx80_add(fp1, onebysc, status); + fp0 = floatx80_add(fp0, fp1, status); + fp0 = floatx80_add(fp0, exp_tbl[j], status); + } else if (m < -3) { + fp0 = floatx80_add(fp0, float32_to_floatx80(exp_tbl2[j], + status), status); + fp0 = floatx80_add(fp0, exp_tbl[j], status); + onebysc = packFloatx80(1, m1 + 0x3FFF, one_sig); /* -2^(-M) */ + fp0 = floatx80_add(fp0, onebysc, status); + } else { /* -3 <= m <= 63 */ + fp1 = exp_tbl[j]; + fp0 = floatx80_add(fp0, float32_to_floatx80(exp_tbl2[j], + status), status); + onebysc = packFloatx80(1, m1 + 0x3FFF, one_sig); /* -2^(-M) */ + fp1 = floatx80_add(fp1, onebysc, status); + fp0 = floatx80_add(fp0, fp1, status); + } + + sc = packFloatx80(0, m + 0x3FFF, one_sig); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_mul(fp0, sc, status); + + float_raise(float_flag_inexact, status); + + return a; + } else { /* |X| > 70 log2 */ + if (aSign) { + fp0 = float32_to_floatx80(make_float32(0xBF800000), + status); /* -1 */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, float32_to_floatx80( + make_float32(0x00800000), status), + status); /* -1 + 2^(-126) */ + + float_raise(float_flag_inexact, status); + + return a; + } else { + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + return floatx80_etox(a, status); + } + } + } else { /* |X| < 1/4 */ + if (aExp >= 0x3FBE) { + fp0 = a; + fp0 = floatx80_mul(fp0, fp0, status); /* S = X*X */ + fp1 = float32_to_floatx80(make_float32(0x2F30CAA8), + status); /* B12 */ + fp1 = floatx80_mul(fp1, fp0, status); /* S * B12 */ + fp2 = float32_to_floatx80(make_float32(0x310F8290), + status); /* B11 */ + fp1 = floatx80_add(fp1, float32_to_floatx80( + make_float32(0x32D73220), status), + status); /* B10 */ + fp2 = floatx80_mul(fp2, fp0, status); + fp1 = floatx80_mul(fp1, fp0, status); + fp2 = floatx80_add(fp2, float32_to_floatx80( + make_float32(0x3493F281), status), + status); /* B9 */ + fp1 = floatx80_add(fp1, float64_to_floatx80( + make_float64(0x3EC71DE3A5774682), status), + status); /* B8 */ + fp2 = floatx80_mul(fp2, fp0, status); + fp1 = floatx80_mul(fp1, fp0, status); + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3EFA01A019D7CB68), status), + status); /* B7 */ + fp1 = floatx80_add(fp1, float64_to_floatx80( + make_float64(0x3F2A01A01A019DF3), status), + status); /* B6 */ + fp2 = floatx80_mul(fp2, fp0, status); + fp1 = floatx80_mul(fp1, fp0, status); + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3F56C16C16C170E2), status), + status); /* B5 */ + fp1 = floatx80_add(fp1, float64_to_floatx80( + make_float64(0x3F81111111111111), status), + status); /* B4 */ + fp2 = floatx80_mul(fp2, fp0, status); + fp1 = floatx80_mul(fp1, fp0, status); + fp2 = floatx80_add(fp2, float64_to_floatx80( + make_float64(0x3FA5555555555555), status), + status); /* B3 */ + fp3 = packFloatx80(0, 0x3FFC, LIT64(0xAAAAAAAAAAAAAAAB)); + fp1 = floatx80_add(fp1, fp3, status); /* B2 */ + fp2 = floatx80_mul(fp2, fp0, status); + fp1 = floatx80_mul(fp1, fp0, status); + + fp2 = floatx80_mul(fp2, fp0, status); + fp1 = floatx80_mul(fp1, a, status); + + fp0 = floatx80_mul(fp0, float32_to_floatx80( + make_float32(0x3F000000), status), + status); /* S*B1 */ + fp1 = floatx80_add(fp1, fp2, status); /* Q */ + fp0 = floatx80_add(fp0, fp1, status); /* S*B1+Q */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, a, status); + + float_raise(float_flag_inexact, status); + + return a; + } else { /* |X| < 2^(-65) */ + sc = packFloatx80(1, 1, one_sig); + fp0 = a; + + if (aExp < 0x0033) { /* |X| < 2^(-16382) */ + fp0 = floatx80_mul(fp0, float64_to_floatx80( + make_float64(0x48B0000000000000), status), + status); + fp0 = floatx80_add(fp0, sc, status); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_mul(fp0, float64_to_floatx80( + make_float64(0x3730000000000000), status), + status); + } else { + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, sc, status); + } + + float_raise(float_flag_inexact, status); + + return a; + } + } +} + +/*---------------------------------------------------------------------------- + | Hyperbolic tangent + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_tanh(floatx80 a, float_status *status) +{ + flag aSign, vSign; + int32_t aExp, vExp; + uint64_t aSig, vSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact; + floatx80 fp0, fp1; + uint32_t sign; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig << 1)) { + return propagateFloatx80NaNOneArg(a, status); + } + return packFloatx80(aSign, one_exp, one_sig); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(aSign, 0, 0); + } + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + status->float_rounding_mode = float_round_nearest_even; + status->floatx80_rounding_precision = 80; + + compact = floatx80_make_compact(aExp, aSig); + + if (compact < 0x3FD78000 || compact > 0x3FFFDDCE) { + /* TANHBORS */ + if (compact < 0x3FFF8000) { + /* TANHSM */ + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_move(a, status); + + float_raise(float_flag_inexact, status); + + return a; + } else { + if (compact > 0x40048AA1) { + /* TANHHUGE */ + sign = 0x3F800000; + sign |= aSign ? 0x80000000 : 0x00000000; + fp0 = float32_to_floatx80(make_float32(sign), status); + sign &= 0x80000000; + sign ^= 0x80800000; /* -SIGN(X)*EPS */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, float32_to_floatx80(make_float32(sign), + status), status); + + float_raise(float_flag_inexact, status); + + return a; + } else { + fp0 = packFloatx80(0, aExp + 1, aSig); /* Y = 2|X| */ + fp0 = floatx80_etox(fp0, status); /* FP0 IS EXP(Y) */ + fp0 = floatx80_add(fp0, float32_to_floatx80( + make_float32(0x3F800000), + status), status); /* EXP(Y)+1 */ + sign = aSign ? 0x80000000 : 0x00000000; + fp1 = floatx80_div(float32_to_floatx80(make_float32( + sign ^ 0xC0000000), status), fp0, + status); /* -SIGN(X)*2 / [EXP(Y)+1] */ + fp0 = float32_to_floatx80(make_float32(sign | 0x3F800000), + status); /* SIGN */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp1, fp0, status); + + float_raise(float_flag_inexact, status); + + return a; + } + } + } else { /* 2**(-40) < |X| < (5/2)LOG2 */ + fp0 = packFloatx80(0, aExp + 1, aSig); /* Y = 2|X| */ + fp0 = floatx80_etoxm1(fp0, status); /* FP0 IS Z = EXPM1(Y) */ + fp1 = floatx80_add(fp0, float32_to_floatx80(make_float32(0x40000000), + status), + status); /* Z+2 */ + + vSign = extractFloatx80Sign(fp1); + vExp = extractFloatx80Exp(fp1); + vSig = extractFloatx80Frac(fp1); + + fp1 = packFloatx80(vSign ^ aSign, vExp, vSig); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_div(fp0, fp1, status); + + float_raise(float_flag_inexact, status); + + return a; + } +} + +/*---------------------------------------------------------------------------- + | Hyperbolic sine + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_sinh(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact; + floatx80 fp0, fp1, fp2; + float32 fact; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig << 1)) { + return propagateFloatx80NaNOneArg(a, status); + } + return packFloatx80(aSign, floatx80_infinity.high, + floatx80_infinity.low); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(aSign, 0, 0); + } + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + status->float_rounding_mode = float_round_nearest_even; + status->floatx80_rounding_precision = 80; + + compact = floatx80_make_compact(aExp, aSig); + + if (compact > 0x400CB167) { + /* SINHBIG */ + if (compact > 0x400CB2B3) { + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + return roundAndPackFloatx80(status->floatx80_rounding_precision, + aSign, 0x8000, aSig, 0, status); + } else { + fp0 = floatx80_abs(a); /* Y = |X| */ + fp0 = floatx80_sub(fp0, float64_to_floatx80( + make_float64(0x40C62D38D3D64634), status), + status); /* (|X|-16381LOG2_LEAD) */ + fp0 = floatx80_sub(fp0, float64_to_floatx80( + make_float64(0x3D6F90AEB1E75CC7), status), + status); /* |X| - 16381 LOG2, ACCURATE */ + fp0 = floatx80_etox(fp0, status); + fp2 = packFloatx80(aSign, 0x7FFB, one_sig); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_mul(fp0, fp2, status); + + float_raise(float_flag_inexact, status); + + return a; + } + } else { /* |X| < 16380 LOG2 */ + fp0 = floatx80_abs(a); /* Y = |X| */ + fp0 = floatx80_etoxm1(fp0, status); /* FP0 IS Z = EXPM1(Y) */ + fp1 = floatx80_add(fp0, float32_to_floatx80(make_float32(0x3F800000), + status), status); /* 1+Z */ + fp2 = fp0; + fp0 = floatx80_div(fp0, fp1, status); /* Z/(1+Z) */ + fp0 = floatx80_add(fp0, fp2, status); + + fact = packFloat32(aSign, 0x7E, 0); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_mul(fp0, float32_to_floatx80(fact, status), status); + + float_raise(float_flag_inexact, status); + + return a; + } +} + +/*---------------------------------------------------------------------------- + | Hyperbolic cosine + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_cosh(floatx80 a, float_status *status) +{ + int32_t aExp; + uint64_t aSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact; + floatx80 fp0, fp1; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig << 1)) { + return propagateFloatx80NaNOneArg(a, status); + } + return packFloatx80(0, floatx80_infinity.high, + floatx80_infinity.low); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(0, one_exp, one_sig); + } + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + status->float_rounding_mode = float_round_nearest_even; + status->floatx80_rounding_precision = 80; + + compact = floatx80_make_compact(aExp, aSig); + + if (compact > 0x400CB167) { + if (compact > 0x400CB2B3) { + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + return roundAndPackFloatx80(status->floatx80_rounding_precision, 0, + 0x8000, one_sig, 0, status); + } else { + fp0 = packFloatx80(0, aExp, aSig); + fp0 = floatx80_sub(fp0, float64_to_floatx80( + make_float64(0x40C62D38D3D64634), status), + status); + fp0 = floatx80_sub(fp0, float64_to_floatx80( + make_float64(0x3D6F90AEB1E75CC7), status), + status); + fp0 = floatx80_etox(fp0, status); + fp1 = packFloatx80(0, 0x7FFB, one_sig); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_mul(fp0, fp1, status); + + float_raise(float_flag_inexact, status); + + return a; + } + } + + fp0 = packFloatx80(0, aExp, aSig); /* |X| */ + fp0 = floatx80_etox(fp0, status); /* EXP(|X|) */ + fp0 = floatx80_mul(fp0, float32_to_floatx80(make_float32(0x3F000000), + status), status); /* (1/2)*EXP(|X|) */ + fp1 = float32_to_floatx80(make_float32(0x3E800000), status); /* 1/4 */ + fp1 = floatx80_div(fp1, fp0, status); /* 1/(2*EXP(|X|)) */ + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, fp1, status); + + float_raise(float_flag_inexact, status); + + return a; +} diff --git a/target/m68k/softfloat.h b/target/m68k/softfloat.h index 78fbc0cd0c..602661d5a8 100644 --- a/target/m68k/softfloat.h +++ b/target/m68k/softfloat.h @@ -26,4 +26,23 @@ floatx80 floatx80_mod(floatx80 a, floatx80 b, float_status *status); floatx80 floatx80_getman(floatx80 a, float_status *status); floatx80 floatx80_getexp(floatx80 a, float_status *status); floatx80 floatx80_scale(floatx80 a, floatx80 b, float_status *status); +floatx80 floatx80_move(floatx80 a, float_status *status); +floatx80 floatx80_lognp1(floatx80 a, float_status *status); +floatx80 floatx80_logn(floatx80 a, float_status *status); +floatx80 floatx80_log10(floatx80 a, float_status *status); +floatx80 floatx80_log2(floatx80 a, float_status *status); +floatx80 floatx80_etox(floatx80 a, float_status *status); +floatx80 floatx80_twotox(floatx80 a, float_status *status); +floatx80 floatx80_tentox(floatx80 a, float_status *status); +floatx80 floatx80_tan(floatx80 a, float_status *status); +floatx80 floatx80_sin(floatx80 a, float_status *status); +floatx80 floatx80_cos(floatx80 a, float_status *status); +floatx80 floatx80_atan(floatx80 a, float_status *status); +floatx80 floatx80_asin(floatx80 a, float_status *status); +floatx80 floatx80_acos(floatx80 a, float_status *status); +floatx80 floatx80_atanh(floatx80 a, float_status *status); +floatx80 floatx80_etoxm1(floatx80 a, float_status *status); +floatx80 floatx80_tanh(floatx80 a, float_status *status); +floatx80 floatx80_sinh(floatx80 a, float_status *status); +floatx80 floatx80_cosh(floatx80 a, float_status *status); #endif diff --git a/target/m68k/softfloat_fpsp_tables.h b/target/m68k/softfloat_fpsp_tables.h new file mode 100644 index 0000000000..3f1419ee6e --- /dev/null +++ b/target/m68k/softfloat_fpsp_tables.h @@ -0,0 +1,641 @@ +/* + * Ported from a work by Andreas Grabher for Previous, NeXT Computer Emulator, + * derived from NetBSD M68040 FPSP functions, + * derived from release 2a of the SoftFloat IEC/IEEE Floating-point Arithmetic + * Package. Those parts of the code (and some later contributions) are + * provided under that license, as detailed below. + * It has subsequently been modified by contributors to the QEMU Project, + * so some portions are provided under: + * the SoftFloat-2a license + * the BSD license + * GPL-v2-or-later + * + * Any future contributions to this file will be taken to be licensed under + * the Softfloat-2a license unless specifically indicated otherwise. + */ + +/* Portions of this work are licensed under the terms of the GNU GPL, + * version 2 or later. See the COPYING file in the top-level directory. + */ + +#ifndef TARGET_M68K_SOFTFLOAT_FPSP_TABLES_H +#define TARGET_M68K_SOFTFLOAT_FPSP_TABLES_H + +static const floatx80 log_tbl[128] = { + make_floatx80_init(0x3FFE, 0xFE03F80FE03F80FE), + make_floatx80_init(0x3FF7, 0xFF015358833C47E2), + make_floatx80_init(0x3FFE, 0xFA232CF252138AC0), + make_floatx80_init(0x3FF9, 0xBDC8D83EAD88D549), + make_floatx80_init(0x3FFE, 0xF6603D980F6603DA), + make_floatx80_init(0x3FFA, 0x9CF43DCFF5EAFD48), + make_floatx80_init(0x3FFE, 0xF2B9D6480F2B9D65), + make_floatx80_init(0x3FFA, 0xDA16EB88CB8DF614), + make_floatx80_init(0x3FFE, 0xEF2EB71FC4345238), + make_floatx80_init(0x3FFB, 0x8B29B7751BD70743), + make_floatx80_init(0x3FFE, 0xEBBDB2A5C1619C8C), + make_floatx80_init(0x3FFB, 0xA8D839F830C1FB49), + make_floatx80_init(0x3FFE, 0xE865AC7B7603A197), + make_floatx80_init(0x3FFB, 0xC61A2EB18CD907AD), + make_floatx80_init(0x3FFE, 0xE525982AF70C880E), + make_floatx80_init(0x3FFB, 0xE2F2A47ADE3A18AF), + make_floatx80_init(0x3FFE, 0xE1FC780E1FC780E2), + make_floatx80_init(0x3FFB, 0xFF64898EDF55D551), + make_floatx80_init(0x3FFE, 0xDEE95C4CA037BA57), + make_floatx80_init(0x3FFC, 0x8DB956A97B3D0148), + make_floatx80_init(0x3FFE, 0xDBEB61EED19C5958), + make_floatx80_init(0x3FFC, 0x9B8FE100F47BA1DE), + make_floatx80_init(0x3FFE, 0xD901B2036406C80E), + make_floatx80_init(0x3FFC, 0xA9372F1D0DA1BD17), + make_floatx80_init(0x3FFE, 0xD62B80D62B80D62C), + make_floatx80_init(0x3FFC, 0xB6B07F38CE90E46B), + make_floatx80_init(0x3FFE, 0xD3680D3680D3680D), + make_floatx80_init(0x3FFC, 0xC3FD032906488481), + make_floatx80_init(0x3FFE, 0xD0B69FCBD2580D0B), + make_floatx80_init(0x3FFC, 0xD11DE0FF15AB18CA), + make_floatx80_init(0x3FFE, 0xCE168A7725080CE1), + make_floatx80_init(0x3FFC, 0xDE1433A16C66B150), + make_floatx80_init(0x3FFE, 0xCB8727C065C393E0), + make_floatx80_init(0x3FFC, 0xEAE10B5A7DDC8ADD), + make_floatx80_init(0x3FFE, 0xC907DA4E871146AD), + make_floatx80_init(0x3FFC, 0xF7856E5EE2C9B291), + make_floatx80_init(0x3FFE, 0xC6980C6980C6980C), + make_floatx80_init(0x3FFD, 0x82012CA5A68206D7), + make_floatx80_init(0x3FFE, 0xC4372F855D824CA6), + make_floatx80_init(0x3FFD, 0x882C5FCD7256A8C5), + make_floatx80_init(0x3FFE, 0xC1E4BBD595F6E947), + make_floatx80_init(0x3FFD, 0x8E44C60B4CCFD7DE), + make_floatx80_init(0x3FFE, 0xBFA02FE80BFA02FF), + make_floatx80_init(0x3FFD, 0x944AD09EF4351AF6), + make_floatx80_init(0x3FFE, 0xBD69104707661AA3), + make_floatx80_init(0x3FFD, 0x9A3EECD4C3EAA6B2), + make_floatx80_init(0x3FFE, 0xBB3EE721A54D880C), + make_floatx80_init(0x3FFD, 0xA0218434353F1DE8), + make_floatx80_init(0x3FFE, 0xB92143FA36F5E02E), + make_floatx80_init(0x3FFD, 0xA5F2FCABBBC506DA), + make_floatx80_init(0x3FFE, 0xB70FBB5A19BE3659), + make_floatx80_init(0x3FFD, 0xABB3B8BA2AD362A5), + make_floatx80_init(0x3FFE, 0xB509E68A9B94821F), + make_floatx80_init(0x3FFD, 0xB1641795CE3CA97B), + make_floatx80_init(0x3FFE, 0xB30F63528917C80B), + make_floatx80_init(0x3FFD, 0xB70475515D0F1C61), + make_floatx80_init(0x3FFE, 0xB11FD3B80B11FD3C), + make_floatx80_init(0x3FFD, 0xBC952AFEEA3D13E1), + make_floatx80_init(0x3FFE, 0xAF3ADDC680AF3ADE), + make_floatx80_init(0x3FFD, 0xC2168ED0F458BA4A), + make_floatx80_init(0x3FFE, 0xAD602B580AD602B6), + make_floatx80_init(0x3FFD, 0xC788F439B3163BF1), + make_floatx80_init(0x3FFE, 0xAB8F69E28359CD11), + make_floatx80_init(0x3FFD, 0xCCECAC08BF04565D), + make_floatx80_init(0x3FFE, 0xA9C84A47A07F5638), + make_floatx80_init(0x3FFD, 0xD24204872DD85160), + make_floatx80_init(0x3FFE, 0xA80A80A80A80A80B), + make_floatx80_init(0x3FFD, 0xD78949923BC3588A), + make_floatx80_init(0x3FFE, 0xA655C4392D7B73A8), + make_floatx80_init(0x3FFD, 0xDCC2C4B49887DACC), + make_floatx80_init(0x3FFE, 0xA4A9CF1D96833751), + make_floatx80_init(0x3FFD, 0xE1EEBD3E6D6A6B9E), + make_floatx80_init(0x3FFE, 0xA3065E3FAE7CD0E0), + make_floatx80_init(0x3FFD, 0xE70D785C2F9F5BDC), + make_floatx80_init(0x3FFE, 0xA16B312EA8FC377D), + make_floatx80_init(0x3FFD, 0xEC1F392C5179F283), + make_floatx80_init(0x3FFE, 0x9FD809FD809FD80A), + make_floatx80_init(0x3FFD, 0xF12440D3E36130E6), + make_floatx80_init(0x3FFE, 0x9E4CAD23DD5F3A20), + make_floatx80_init(0x3FFD, 0xF61CCE92346600BB), + make_floatx80_init(0x3FFE, 0x9CC8E160C3FB19B9), + make_floatx80_init(0x3FFD, 0xFB091FD38145630A), + make_floatx80_init(0x3FFE, 0x9B4C6F9EF03A3CAA), + make_floatx80_init(0x3FFD, 0xFFE97042BFA4C2AD), + make_floatx80_init(0x3FFE, 0x99D722DABDE58F06), + make_floatx80_init(0x3FFE, 0x825EFCED49369330), + make_floatx80_init(0x3FFE, 0x9868C809868C8098), + make_floatx80_init(0x3FFE, 0x84C37A7AB9A905C9), + make_floatx80_init(0x3FFE, 0x97012E025C04B809), + make_floatx80_init(0x3FFE, 0x87224C2E8E645FB7), + make_floatx80_init(0x3FFE, 0x95A02568095A0257), + make_floatx80_init(0x3FFE, 0x897B8CAC9F7DE298), + make_floatx80_init(0x3FFE, 0x9445809445809446), + make_floatx80_init(0x3FFE, 0x8BCF55DEC4CD05FE), + make_floatx80_init(0x3FFE, 0x92F113840497889C), + make_floatx80_init(0x3FFE, 0x8E1DC0FB89E125E5), + make_floatx80_init(0x3FFE, 0x91A2B3C4D5E6F809), + make_floatx80_init(0x3FFE, 0x9066E68C955B6C9B), + make_floatx80_init(0x3FFE, 0x905A38633E06C43B), + make_floatx80_init(0x3FFE, 0x92AADE74C7BE59E0), + make_floatx80_init(0x3FFE, 0x8F1779D9FDC3A219), + make_floatx80_init(0x3FFE, 0x94E9BFF615845643), + make_floatx80_init(0x3FFE, 0x8DDA520237694809), + make_floatx80_init(0x3FFE, 0x9723A1B720134203), + make_floatx80_init(0x3FFE, 0x8CA29C046514E023), + make_floatx80_init(0x3FFE, 0x995899C890EB8990), + make_floatx80_init(0x3FFE, 0x8B70344A139BC75A), + make_floatx80_init(0x3FFE, 0x9B88BDAA3A3DAE2F), + make_floatx80_init(0x3FFE, 0x8A42F8705669DB46), + make_floatx80_init(0x3FFE, 0x9DB4224FFFE1157C), + make_floatx80_init(0x3FFE, 0x891AC73AE9819B50), + make_floatx80_init(0x3FFE, 0x9FDADC268B7A12DA), + make_floatx80_init(0x3FFE, 0x87F78087F78087F8), + make_floatx80_init(0x3FFE, 0xA1FCFF17CE733BD4), + make_floatx80_init(0x3FFE, 0x86D905447A34ACC6), + make_floatx80_init(0x3FFE, 0xA41A9E8F5446FB9F), + make_floatx80_init(0x3FFE, 0x85BF37612CEE3C9B), + make_floatx80_init(0x3FFE, 0xA633CD7E6771CD8B), + make_floatx80_init(0x3FFE, 0x84A9F9C8084A9F9D), + make_floatx80_init(0x3FFE, 0xA8489E600B435A5E), + make_floatx80_init(0x3FFE, 0x839930523FBE3368), + make_floatx80_init(0x3FFE, 0xAA59233CCCA4BD49), + make_floatx80_init(0x3FFE, 0x828CBFBEB9A020A3), + make_floatx80_init(0x3FFE, 0xAC656DAE6BCC4985), + make_floatx80_init(0x3FFE, 0x81848DA8FAF0D277), + make_floatx80_init(0x3FFE, 0xAE6D8EE360BB2468), + make_floatx80_init(0x3FFE, 0x8080808080808081), + make_floatx80_init(0x3FFE, 0xB07197A23C46C654) +}; + +static const floatx80 exp_tbl[64] = { + make_floatx80_init(0x3FFF, 0x8000000000000000), + make_floatx80_init(0x3FFF, 0x8164D1F3BC030774), + make_floatx80_init(0x3FFF, 0x82CD8698AC2BA1D8), + make_floatx80_init(0x3FFF, 0x843A28C3ACDE4048), + make_floatx80_init(0x3FFF, 0x85AAC367CC487B14), + make_floatx80_init(0x3FFF, 0x871F61969E8D1010), + make_floatx80_init(0x3FFF, 0x88980E8092DA8528), + make_floatx80_init(0x3FFF, 0x8A14D575496EFD9C), + make_floatx80_init(0x3FFF, 0x8B95C1E3EA8BD6E8), + make_floatx80_init(0x3FFF, 0x8D1ADF5B7E5BA9E4), + make_floatx80_init(0x3FFF, 0x8EA4398B45CD53C0), + make_floatx80_init(0x3FFF, 0x9031DC431466B1DC), + make_floatx80_init(0x3FFF, 0x91C3D373AB11C338), + make_floatx80_init(0x3FFF, 0x935A2B2F13E6E92C), + make_floatx80_init(0x3FFF, 0x94F4EFA8FEF70960), + make_floatx80_init(0x3FFF, 0x96942D3720185A00), + make_floatx80_init(0x3FFF, 0x9837F0518DB8A970), + make_floatx80_init(0x3FFF, 0x99E0459320B7FA64), + make_floatx80_init(0x3FFF, 0x9B8D39B9D54E5538), + make_floatx80_init(0x3FFF, 0x9D3ED9A72CFFB750), + make_floatx80_init(0x3FFF, 0x9EF5326091A111AC), + make_floatx80_init(0x3FFF, 0xA0B0510FB9714FC4), + make_floatx80_init(0x3FFF, 0xA27043030C496818), + make_floatx80_init(0x3FFF, 0xA43515AE09E680A0), + make_floatx80_init(0x3FFF, 0xA5FED6A9B15138EC), + make_floatx80_init(0x3FFF, 0xA7CD93B4E9653568), + make_floatx80_init(0x3FFF, 0xA9A15AB4EA7C0EF8), + make_floatx80_init(0x3FFF, 0xAB7A39B5A93ED338), + make_floatx80_init(0x3FFF, 0xAD583EEA42A14AC8), + make_floatx80_init(0x3FFF, 0xAF3B78AD690A4374), + make_floatx80_init(0x3FFF, 0xB123F581D2AC2590), + make_floatx80_init(0x3FFF, 0xB311C412A9112488), + make_floatx80_init(0x3FFF, 0xB504F333F9DE6484), + make_floatx80_init(0x3FFF, 0xB6FD91E328D17790), + make_floatx80_init(0x3FFF, 0xB8FBAF4762FB9EE8), + make_floatx80_init(0x3FFF, 0xBAFF5AB2133E45FC), + make_floatx80_init(0x3FFF, 0xBD08A39F580C36C0), + make_floatx80_init(0x3FFF, 0xBF1799B67A731084), + make_floatx80_init(0x3FFF, 0xC12C4CCA66709458), + make_floatx80_init(0x3FFF, 0xC346CCDA24976408), + make_floatx80_init(0x3FFF, 0xC5672A115506DADC), + make_floatx80_init(0x3FFF, 0xC78D74C8ABB9B15C), + make_floatx80_init(0x3FFF, 0xC9B9BD866E2F27A4), + make_floatx80_init(0x3FFF, 0xCBEC14FEF2727C5C), + make_floatx80_init(0x3FFF, 0xCE248C151F8480E4), + make_floatx80_init(0x3FFF, 0xD06333DAEF2B2594), + make_floatx80_init(0x3FFF, 0xD2A81D91F12AE45C), + make_floatx80_init(0x3FFF, 0xD4F35AABCFEDFA20), + make_floatx80_init(0x3FFF, 0xD744FCCAD69D6AF4), + make_floatx80_init(0x3FFF, 0xD99D15C278AFD7B4), + make_floatx80_init(0x3FFF, 0xDBFBB797DAF23754), + make_floatx80_init(0x3FFF, 0xDE60F4825E0E9124), + make_floatx80_init(0x3FFF, 0xE0CCDEEC2A94E110), + make_floatx80_init(0x3FFF, 0xE33F8972BE8A5A50), + make_floatx80_init(0x3FFF, 0xE5B906E77C8348A8), + make_floatx80_init(0x3FFF, 0xE8396A503C4BDC68), + make_floatx80_init(0x3FFF, 0xEAC0C6E7DD243930), + make_floatx80_init(0x3FFF, 0xED4F301ED9942B84), + make_floatx80_init(0x3FFF, 0xEFE4B99BDCDAF5CC), + make_floatx80_init(0x3FFF, 0xF281773C59FFB138), + make_floatx80_init(0x3FFF, 0xF5257D152486CC2C), + make_floatx80_init(0x3FFF, 0xF7D0DF730AD13BB8), + make_floatx80_init(0x3FFF, 0xFA83B2DB722A033C), + make_floatx80_init(0x3FFF, 0xFD3E0C0CF486C174) +}; + +static const float32 exp_tbl2[64] = { + const_float32(0x00000000), + const_float32(0x9F841A9B), + const_float32(0x9FC1D5B9), + const_float32(0xA0728369), + const_float32(0x1FC5C95C), + const_float32(0x1EE85C9F), + const_float32(0x9FA20729), + const_float32(0xA07BF9AF), + const_float32(0xA0020DCF), + const_float32(0x205A63DA), + const_float32(0x1EB70051), + const_float32(0x1F6EB029), + const_float32(0xA0781494), + const_float32(0x9EB319B0), + const_float32(0x2017457D), + const_float32(0x1F11D537), + const_float32(0x9FB952DD), + const_float32(0x1FE43087), + const_float32(0x1FA2A818), + const_float32(0x1FDE494D), + const_float32(0x20504890), + const_float32(0xA073691C), + const_float32(0x1F9B7A05), + const_float32(0xA0797126), + const_float32(0xA071A140), + const_float32(0x204F62DA), + const_float32(0x1F283C4A), + const_float32(0x9F9A7FDC), + const_float32(0xA05B3FAC), + const_float32(0x1FDF2610), + const_float32(0x9F705F90), + const_float32(0x201F678A), + const_float32(0x1F32FB13), + const_float32(0x20038B30), + const_float32(0x200DC3CC), + const_float32(0x9F8B2AE6), + const_float32(0xA02BBF70), + const_float32(0xA00BF518), + const_float32(0xA041DD41), + const_float32(0x9FDF137B), + const_float32(0x201F1568), + const_float32(0x1FC13A2E), + const_float32(0xA03F8F03), + const_float32(0x1FF4907D), + const_float32(0x9E6E53E4), + const_float32(0x1FD6D45C), + const_float32(0xA076EDB9), + const_float32(0x9FA6DE21), + const_float32(0x1EE69A2F), + const_float32(0x207F439F), + const_float32(0x201EC207), + const_float32(0x9E8BE175), + const_float32(0x20032C4B), + const_float32(0x2004DFF5), + const_float32(0x1E72F47A), + const_float32(0x1F722F22), + const_float32(0xA017E945), + const_float32(0x1F401A5B), + const_float32(0x9FB9A9E3), + const_float32(0x20744C05), + const_float32(0x1F773A19), + const_float32(0x1FFE90D5), + const_float32(0xA041ED22), + const_float32(0x1F853F3A), +}; + +static const floatx80 exp2_tbl[64] = { + make_floatx80_init(0x3FFF, 0x8000000000000000), + make_floatx80_init(0x3FFF, 0x8164D1F3BC030773), + make_floatx80_init(0x3FFF, 0x82CD8698AC2BA1D7), + make_floatx80_init(0x3FFF, 0x843A28C3ACDE4046), + make_floatx80_init(0x3FFF, 0x85AAC367CC487B15), + make_floatx80_init(0x3FFF, 0x871F61969E8D1010), + make_floatx80_init(0x3FFF, 0x88980E8092DA8527), + make_floatx80_init(0x3FFF, 0x8A14D575496EFD9A), + make_floatx80_init(0x3FFF, 0x8B95C1E3EA8BD6E7), + make_floatx80_init(0x3FFF, 0x8D1ADF5B7E5BA9E6), + make_floatx80_init(0x3FFF, 0x8EA4398B45CD53C0), + make_floatx80_init(0x3FFF, 0x9031DC431466B1DC), + make_floatx80_init(0x3FFF, 0x91C3D373AB11C336), + make_floatx80_init(0x3FFF, 0x935A2B2F13E6E92C), + make_floatx80_init(0x3FFF, 0x94F4EFA8FEF70961), + make_floatx80_init(0x3FFF, 0x96942D3720185A00), + make_floatx80_init(0x3FFF, 0x9837F0518DB8A96F), + make_floatx80_init(0x3FFF, 0x99E0459320B7FA65), + make_floatx80_init(0x3FFF, 0x9B8D39B9D54E5539), + make_floatx80_init(0x3FFF, 0x9D3ED9A72CFFB751), + make_floatx80_init(0x3FFF, 0x9EF5326091A111AE), + make_floatx80_init(0x3FFF, 0xA0B0510FB9714FC2), + make_floatx80_init(0x3FFF, 0xA27043030C496819), + make_floatx80_init(0x3FFF, 0xA43515AE09E6809E), + make_floatx80_init(0x3FFF, 0xA5FED6A9B15138EA), + make_floatx80_init(0x3FFF, 0xA7CD93B4E965356A), + make_floatx80_init(0x3FFF, 0xA9A15AB4EA7C0EF8), + make_floatx80_init(0x3FFF, 0xAB7A39B5A93ED337), + make_floatx80_init(0x3FFF, 0xAD583EEA42A14AC6), + make_floatx80_init(0x3FFF, 0xAF3B78AD690A4375), + make_floatx80_init(0x3FFF, 0xB123F581D2AC2590), + make_floatx80_init(0x3FFF, 0xB311C412A9112489), + make_floatx80_init(0x3FFF, 0xB504F333F9DE6484), + make_floatx80_init(0x3FFF, 0xB6FD91E328D17791), + make_floatx80_init(0x3FFF, 0xB8FBAF4762FB9EE9), + make_floatx80_init(0x3FFF, 0xBAFF5AB2133E45FB), + make_floatx80_init(0x3FFF, 0xBD08A39F580C36BF), + make_floatx80_init(0x3FFF, 0xBF1799B67A731083), + make_floatx80_init(0x3FFF, 0xC12C4CCA66709456), + make_floatx80_init(0x3FFF, 0xC346CCDA24976407), + make_floatx80_init(0x3FFF, 0xC5672A115506DADD), + make_floatx80_init(0x3FFF, 0xC78D74C8ABB9B15D), + make_floatx80_init(0x3FFF, 0xC9B9BD866E2F27A3), + make_floatx80_init(0x3FFF, 0xCBEC14FEF2727C5D), + make_floatx80_init(0x3FFF, 0xCE248C151F8480E4), + make_floatx80_init(0x3FFF, 0xD06333DAEF2B2595), + make_floatx80_init(0x3FFF, 0xD2A81D91F12AE45A), + make_floatx80_init(0x3FFF, 0xD4F35AABCFEDFA1F), + make_floatx80_init(0x3FFF, 0xD744FCCAD69D6AF4), + make_floatx80_init(0x3FFF, 0xD99D15C278AFD7B6), + make_floatx80_init(0x3FFF, 0xDBFBB797DAF23755), + make_floatx80_init(0x3FFF, 0xDE60F4825E0E9124), + make_floatx80_init(0x3FFF, 0xE0CCDEEC2A94E111), + make_floatx80_init(0x3FFF, 0xE33F8972BE8A5A51), + make_floatx80_init(0x3FFF, 0xE5B906E77C8348A8), + make_floatx80_init(0x3FFF, 0xE8396A503C4BDC68), + make_floatx80_init(0x3FFF, 0xEAC0C6E7DD24392F), + make_floatx80_init(0x3FFF, 0xED4F301ED9942B84), + make_floatx80_init(0x3FFF, 0xEFE4B99BDCDAF5CB), + make_floatx80_init(0x3FFF, 0xF281773C59FFB13A), + make_floatx80_init(0x3FFF, 0xF5257D152486CC2C), + make_floatx80_init(0x3FFF, 0xF7D0DF730AD13BB9), + make_floatx80_init(0x3FFF, 0xFA83B2DB722A033A), + make_floatx80_init(0x3FFF, 0xFD3E0C0CF486C175) +}; + +static const uint32_t exp2_tbl2[64] = { + 0x3F738000, 0x3FBEF7CA, 0x3FBDF8A9, 0x3FBCD7C9, + 0xBFBDE8DA, 0x3FBDE85C, 0x3FBEBBF1, 0x3FBB80CA, + 0xBFBA8373, 0xBFBE9670, 0x3FBDB700, 0x3FBEEEB0, + 0x3FBBFD6D, 0xBFBDB319, 0x3FBDBA2B, 0x3FBE91D5, + 0x3FBE8D5A, 0xBFBCDE7B, 0xBFBEBAAF, 0xBFBD86DA, + 0xBFBEBEDD, 0x3FBCC96E, 0xBFBEC90B, 0x3FBBD1DB, + 0x3FBCE5EB, 0xBFBEC274, 0x3FBEA83C, 0x3FBECB00, + 0x3FBE9301, 0xBFBD8367, 0xBFBEF05F, 0x3FBDFB3C, + 0x3FBEB2FB, 0x3FBAE2CB, 0x3FBCDC3C, 0x3FBEE9AA, + 0xBFBEAEFD, 0xBFBCBF51, 0x3FBEF88A, 0x3FBD83B2, + 0x3FBDF8AB, 0xBFBDFB17, 0xBFBEFE3C, 0xBFBBB6F8, + 0xBFBCEE53, 0xBFBDA4AE, 0x3FBC9124, 0x3FBEB243, + 0x3FBDE69A, 0xBFB8BC61, 0x3FBDF610, 0xBFBD8BE1, + 0x3FBACB12, 0x3FBB9BFE, 0x3FBCF2F4, 0x3FBEF22F, + 0xBFBDBF4A, 0x3FBEC01A, 0x3FBE8CAC, 0xBFBCBB3F, + 0x3FBEF73A, 0xBFB8B795, 0x3FBEF84B, 0xBFBEF581 +}; + +static const floatx80 pi_tbl[65] = { + make_floatx80_init(0xC004, 0xC90FDAA22168C235), + make_floatx80_init(0xC004, 0xC2C75BCD105D7C23), + make_floatx80_init(0xC004, 0xBC7EDCF7FF523611), + make_floatx80_init(0xC004, 0xB6365E22EE46F000), + make_floatx80_init(0xC004, 0xAFEDDF4DDD3BA9EE), + make_floatx80_init(0xC004, 0xA9A56078CC3063DD), + make_floatx80_init(0xC004, 0xA35CE1A3BB251DCB), + make_floatx80_init(0xC004, 0x9D1462CEAA19D7B9), + make_floatx80_init(0xC004, 0x96CBE3F9990E91A8), + make_floatx80_init(0xC004, 0x9083652488034B96), + make_floatx80_init(0xC004, 0x8A3AE64F76F80584), + make_floatx80_init(0xC004, 0x83F2677A65ECBF73), + make_floatx80_init(0xC003, 0xFB53D14AA9C2F2C2), + make_floatx80_init(0xC003, 0xEEC2D3A087AC669F), + make_floatx80_init(0xC003, 0xE231D5F66595DA7B), + make_floatx80_init(0xC003, 0xD5A0D84C437F4E58), + make_floatx80_init(0xC003, 0xC90FDAA22168C235), + make_floatx80_init(0xC003, 0xBC7EDCF7FF523611), + make_floatx80_init(0xC003, 0xAFEDDF4DDD3BA9EE), + make_floatx80_init(0xC003, 0xA35CE1A3BB251DCB), + make_floatx80_init(0xC003, 0x96CBE3F9990E91A8), + make_floatx80_init(0xC003, 0x8A3AE64F76F80584), + make_floatx80_init(0xC002, 0xFB53D14AA9C2F2C2), + make_floatx80_init(0xC002, 0xE231D5F66595DA7B), + make_floatx80_init(0xC002, 0xC90FDAA22168C235), + make_floatx80_init(0xC002, 0xAFEDDF4DDD3BA9EE), + make_floatx80_init(0xC002, 0x96CBE3F9990E91A8), + make_floatx80_init(0xC001, 0xFB53D14AA9C2F2C2), + make_floatx80_init(0xC001, 0xC90FDAA22168C235), + make_floatx80_init(0xC001, 0x96CBE3F9990E91A8), + make_floatx80_init(0xC000, 0xC90FDAA22168C235), + make_floatx80_init(0xBFFF, 0xC90FDAA22168C235), + make_floatx80_init(0x0000, 0x0000000000000000), + make_floatx80_init(0x3FFF, 0xC90FDAA22168C235), + make_floatx80_init(0x4000, 0xC90FDAA22168C235), + make_floatx80_init(0x4001, 0x96CBE3F9990E91A8), + make_floatx80_init(0x4001, 0xC90FDAA22168C235), + make_floatx80_init(0x4001, 0xFB53D14AA9C2F2C2), + make_floatx80_init(0x4002, 0x96CBE3F9990E91A8), + make_floatx80_init(0x4002, 0xAFEDDF4DDD3BA9EE), + make_floatx80_init(0x4002, 0xC90FDAA22168C235), + make_floatx80_init(0x4002, 0xE231D5F66595DA7B), + make_floatx80_init(0x4002, 0xFB53D14AA9C2F2C2), + make_floatx80_init(0x4003, 0x8A3AE64F76F80584), + make_floatx80_init(0x4003, 0x96CBE3F9990E91A8), + make_floatx80_init(0x4003, 0xA35CE1A3BB251DCB), + make_floatx80_init(0x4003, 0xAFEDDF4DDD3BA9EE), + make_floatx80_init(0x4003, 0xBC7EDCF7FF523611), + make_floatx80_init(0x4003, 0xC90FDAA22168C235), + make_floatx80_init(0x4003, 0xD5A0D84C437F4E58), + make_floatx80_init(0x4003, 0xE231D5F66595DA7B), + make_floatx80_init(0x4003, 0xEEC2D3A087AC669F), + make_floatx80_init(0x4003, 0xFB53D14AA9C2F2C2), + make_floatx80_init(0x4004, 0x83F2677A65ECBF73), + make_floatx80_init(0x4004, 0x8A3AE64F76F80584), + make_floatx80_init(0x4004, 0x9083652488034B96), + make_floatx80_init(0x4004, 0x96CBE3F9990E91A8), + make_floatx80_init(0x4004, 0x9D1462CEAA19D7B9), + make_floatx80_init(0x4004, 0xA35CE1A3BB251DCB), + make_floatx80_init(0x4004, 0xA9A56078CC3063DD), + make_floatx80_init(0x4004, 0xAFEDDF4DDD3BA9EE), + make_floatx80_init(0x4004, 0xB6365E22EE46F000), + make_floatx80_init(0x4004, 0xBC7EDCF7FF523611), + make_floatx80_init(0x4004, 0xC2C75BCD105D7C23), + make_floatx80_init(0x4004, 0xC90FDAA22168C235) +}; + +static const float32 pi_tbl2[65] = { + const_float32(0x21800000), + const_float32(0xA0D00000), + const_float32(0xA1E80000), + const_float32(0x21480000), + const_float32(0xA1200000), + const_float32(0x21FC0000), + const_float32(0x21100000), + const_float32(0xA1580000), + const_float32(0x21E00000), + const_float32(0x20B00000), + const_float32(0xA1880000), + const_float32(0x21C40000), + const_float32(0x20000000), + const_float32(0x21380000), + const_float32(0xA1300000), + const_float32(0x9FC00000), + const_float32(0x21000000), + const_float32(0xA1680000), + const_float32(0xA0A00000), + const_float32(0x20900000), + const_float32(0x21600000), + const_float32(0xA1080000), + const_float32(0x1F800000), + const_float32(0xA0B00000), + const_float32(0x20800000), + const_float32(0xA0200000), + const_float32(0x20E00000), + const_float32(0x1F000000), + const_float32(0x20000000), + const_float32(0x20600000), + const_float32(0x1F800000), + const_float32(0x1F000000), + const_float32(0x00000000), + const_float32(0x9F000000), + const_float32(0x9F800000), + const_float32(0xA0600000), + const_float32(0xA0000000), + const_float32(0x9F000000), + const_float32(0xA0E00000), + const_float32(0x20200000), + const_float32(0xA0800000), + const_float32(0x20B00000), + const_float32(0x9F800000), + const_float32(0x21080000), + const_float32(0xA1600000), + const_float32(0xA0900000), + const_float32(0x20A00000), + const_float32(0x21680000), + const_float32(0xA1000000), + const_float32(0x1FC00000), + const_float32(0x21300000), + const_float32(0xA1380000), + const_float32(0xA0000000), + const_float32(0xA1C40000), + const_float32(0x21880000), + const_float32(0xA0B00000), + const_float32(0xA1E00000), + const_float32(0x21580000), + const_float32(0xA1100000), + const_float32(0xA1FC0000), + const_float32(0x21200000), + const_float32(0xA1480000), + const_float32(0x21E80000), + const_float32(0x20D00000), + const_float32(0xA1800000), +}; + +static const floatx80 atan_tbl[128] = { + make_floatx80_init(0x3FFB, 0x83D152C5060B7A51), + make_floatx80_init(0x3FFB, 0x8BC8544565498B8B), + make_floatx80_init(0x3FFB, 0x93BE406017626B0D), + make_floatx80_init(0x3FFB, 0x9BB3078D35AEC202), + make_floatx80_init(0x3FFB, 0xA3A69A525DDCE7DE), + make_floatx80_init(0x3FFB, 0xAB98E94362765619), + make_floatx80_init(0x3FFB, 0xB389E502F9C59862), + make_floatx80_init(0x3FFB, 0xBB797E436B09E6FB), + make_floatx80_init(0x3FFB, 0xC367A5C739E5F446), + make_floatx80_init(0x3FFB, 0xCB544C61CFF7D5C6), + make_floatx80_init(0x3FFB, 0xD33F62F82488533E), + make_floatx80_init(0x3FFB, 0xDB28DA8162404C77), + make_floatx80_init(0x3FFB, 0xE310A4078AD34F18), + make_floatx80_init(0x3FFB, 0xEAF6B0A8188EE1EB), + make_floatx80_init(0x3FFB, 0xF2DAF1949DBE79D5), + make_floatx80_init(0x3FFB, 0xFABD581361D47E3E), + make_floatx80_init(0x3FFC, 0x8346AC210959ECC4), + make_floatx80_init(0x3FFC, 0x8B232A08304282D8), + make_floatx80_init(0x3FFC, 0x92FB70B8D29AE2F9), + make_floatx80_init(0x3FFC, 0x9ACF476F5CCD1CB4), + make_floatx80_init(0x3FFC, 0xA29E76304954F23F), + make_floatx80_init(0x3FFC, 0xAA68C5D08AB85230), + make_floatx80_init(0x3FFC, 0xB22DFFFD9D539F83), + make_floatx80_init(0x3FFC, 0xB9EDEF453E900EA5), + make_floatx80_init(0x3FFC, 0xC1A85F1CC75E3EA5), + make_floatx80_init(0x3FFC, 0xC95D1BE828138DE6), + make_floatx80_init(0x3FFC, 0xD10BF300840D2DE4), + make_floatx80_init(0x3FFC, 0xD8B4B2BA6BC05E7A), + make_floatx80_init(0x3FFC, 0xE0572A6BB42335F6), + make_floatx80_init(0x3FFC, 0xE7F32A70EA9CAA8F), + make_floatx80_init(0x3FFC, 0xEF88843264ECEFAA), + make_floatx80_init(0x3FFC, 0xF7170A28ECC06666), + make_floatx80_init(0x3FFD, 0x812FD288332DAD32), + make_floatx80_init(0x3FFD, 0x88A8D1B1218E4D64), + make_floatx80_init(0x3FFD, 0x9012AB3F23E4AEE8), + make_floatx80_init(0x3FFD, 0x976CC3D411E7F1B9), + make_floatx80_init(0x3FFD, 0x9EB689493889A227), + make_floatx80_init(0x3FFD, 0xA5EF72C34487361B), + make_floatx80_init(0x3FFD, 0xAD1700BAF07A7227), + make_floatx80_init(0x3FFD, 0xB42CBCFAFD37EFB7), + make_floatx80_init(0x3FFD, 0xBB303A940BA80F89), + make_floatx80_init(0x3FFD, 0xC22115C6FCAEBBAF), + make_floatx80_init(0x3FFD, 0xC8FEF3E686331221), + make_floatx80_init(0x3FFD, 0xCFC98330B4000C70), + make_floatx80_init(0x3FFD, 0xD6807AA1102C5BF9), + make_floatx80_init(0x3FFD, 0xDD2399BC31252AA3), + make_floatx80_init(0x3FFD, 0xE3B2A8556B8FC517), + make_floatx80_init(0x3FFD, 0xEA2D764F64315989), + make_floatx80_init(0x3FFD, 0xF3BF5BF8BAD1A21D), + make_floatx80_init(0x3FFE, 0x801CE39E0D205C9A), + make_floatx80_init(0x3FFE, 0x8630A2DADA1ED066), + make_floatx80_init(0x3FFE, 0x8C1AD445F3E09B8C), + make_floatx80_init(0x3FFE, 0x91DB8F1664F350E2), + make_floatx80_init(0x3FFE, 0x97731420365E538C), + make_floatx80_init(0x3FFE, 0x9CE1C8E6A0B8CDBA), + make_floatx80_init(0x3FFE, 0xA22832DBCADAAE09), + make_floatx80_init(0x3FFE, 0xA746F2DDB7602294), + make_floatx80_init(0x3FFE, 0xAC3EC0FB997DD6A2), + make_floatx80_init(0x3FFE, 0xB110688AEBDC6F6A), + make_floatx80_init(0x3FFE, 0xB5BCC49059ECC4B0), + make_floatx80_init(0x3FFE, 0xBA44BC7DD470782F), + make_floatx80_init(0x3FFE, 0xBEA94144FD049AAC), + make_floatx80_init(0x3FFE, 0xC2EB4ABB661628B6), + make_floatx80_init(0x3FFE, 0xC70BD54CE602EE14), + make_floatx80_init(0x3FFE, 0xCD000549ADEC7159), + make_floatx80_init(0x3FFE, 0xD48457D2D8EA4EA3), + make_floatx80_init(0x3FFE, 0xDB948DA712DECE3B), + make_floatx80_init(0x3FFE, 0xE23855F969E8096A), + make_floatx80_init(0x3FFE, 0xE8771129C4353259), + make_floatx80_init(0x3FFE, 0xEE57C16E0D379C0D), + make_floatx80_init(0x3FFE, 0xF3E10211A87C3779), + make_floatx80_init(0x3FFE, 0xF919039D758B8D41), + make_floatx80_init(0x3FFE, 0xFE058B8F64935FB3), + make_floatx80_init(0x3FFF, 0x8155FB497B685D04), + make_floatx80_init(0x3FFF, 0x83889E3549D108E1), + make_floatx80_init(0x3FFF, 0x859CFA76511D724B), + make_floatx80_init(0x3FFF, 0x87952ECFFF8131E7), + make_floatx80_init(0x3FFF, 0x89732FD19557641B), + make_floatx80_init(0x3FFF, 0x8B38CAD101932A35), + make_floatx80_init(0x3FFF, 0x8CE7A8D8301EE6B5), + make_floatx80_init(0x3FFF, 0x8F46A39E2EAE5281), + make_floatx80_init(0x3FFF, 0x922DA7D791888487), + make_floatx80_init(0x3FFF, 0x94D19FCBDEDF5241), + make_floatx80_init(0x3FFF, 0x973AB94419D2A08B), + make_floatx80_init(0x3FFF, 0x996FF00E08E10B96), + make_floatx80_init(0x3FFF, 0x9B773F9512321DA7), + make_floatx80_init(0x3FFF, 0x9D55CC320F935624), + make_floatx80_init(0x3FFF, 0x9F100575006CC571), + make_floatx80_init(0x3FFF, 0xA0A9C290D97CC06C), + make_floatx80_init(0x3FFF, 0xA22659EBEBC0630A), + make_floatx80_init(0x3FFF, 0xA388B4AFF6EF0EC9), + make_floatx80_init(0x3FFF, 0xA4D35F1061D292C4), + make_floatx80_init(0x3FFF, 0xA60895DCFBE3187E), + make_floatx80_init(0x3FFF, 0xA72A51DC7367BEAC), + make_floatx80_init(0x3FFF, 0xA83A51530956168F), + make_floatx80_init(0x3FFF, 0xA93A20077539546E), + make_floatx80_init(0x3FFF, 0xAA9E7245023B2605), + make_floatx80_init(0x3FFF, 0xAC4C84BA6FE4D58F), + make_floatx80_init(0x3FFF, 0xADCE4A4A606B9712), + make_floatx80_init(0x3FFF, 0xAF2A2DCD8D263C9C), + make_floatx80_init(0x3FFF, 0xB0656F81F22265C7), + make_floatx80_init(0x3FFF, 0xB18465150F71496A), + make_floatx80_init(0x3FFF, 0xB28AAA156F9ADA35), + make_floatx80_init(0x3FFF, 0xB37B44FF3766B895), + make_floatx80_init(0x3FFF, 0xB458C3DCE9630433), + make_floatx80_init(0x3FFF, 0xB525529D562246BD), + make_floatx80_init(0x3FFF, 0xB5E2CCA95F9D88CC), + make_floatx80_init(0x3FFF, 0xB692CADA7ACA1ADA), + make_floatx80_init(0x3FFF, 0xB736AEA7A6925838), + make_floatx80_init(0x3FFF, 0xB7CFAB287E9F7B36), + make_floatx80_init(0x3FFF, 0xB85ECC66CB219835), + make_floatx80_init(0x3FFF, 0xB8E4FD5A20A593DA), + make_floatx80_init(0x3FFF, 0xB99F41F64AFF9BB5), + make_floatx80_init(0x3FFF, 0xBA7F1E17842BBE7B), + make_floatx80_init(0x3FFF, 0xBB4712857637E17D), + make_floatx80_init(0x3FFF, 0xBBFABE8A4788DF6F), + make_floatx80_init(0x3FFF, 0xBC9D0FAD2B689D79), + make_floatx80_init(0x3FFF, 0xBD306A39471ECD86), + make_floatx80_init(0x3FFF, 0xBDB6C731856AF18A), + make_floatx80_init(0x3FFF, 0xBE31CAC502E80D70), + make_floatx80_init(0x3FFF, 0xBEA2D55CE33194E2), + make_floatx80_init(0x3FFF, 0xBF0B10B7C03128F0), + make_floatx80_init(0x3FFF, 0xBF6B7A18DACB778D), + make_floatx80_init(0x3FFF, 0xBFC4EA4663FA18F6), + make_floatx80_init(0x3FFF, 0xC0181BDE8B89A454), + make_floatx80_init(0x3FFF, 0xC065B066CFBF6439), + make_floatx80_init(0x3FFF, 0xC0AE345F56340AE6), + make_floatx80_init(0x3FFF, 0xC0F222919CB9E6A7) +}; +#endif diff --git a/target/m68k/translate.c b/target/m68k/translate.c index dbb24f8d84..cef6f663ad 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -5042,6 +5042,9 @@ DISAS_INSN(fpu) case 1: /* fint */ gen_helper_firound(cpu_env, cpu_dest, cpu_src); break; + case 2: /* fsinh */ + gen_helper_fsinh(cpu_env, cpu_dest, cpu_src); + break; case 3: /* fintrz */ gen_helper_fitrunc(cpu_env, cpu_dest, cpu_src); break; @@ -5054,6 +5057,45 @@ DISAS_INSN(fpu) case 0x45: /* fdsqrt */ gen_helper_fdsqrt(cpu_env, cpu_dest, cpu_src); break; + case 0x06: /* flognp1 */ + gen_helper_flognp1(cpu_env, cpu_dest, cpu_src); + break; + case 0x09: /* ftanh */ + gen_helper_ftanh(cpu_env, cpu_dest, cpu_src); + break; + case 0x0a: /* fatan */ + gen_helper_fatan(cpu_env, cpu_dest, cpu_src); + break; + case 0x0c: /* fasin */ + gen_helper_fasin(cpu_env, cpu_dest, cpu_src); + break; + case 0x0d: /* fatanh */ + gen_helper_fatanh(cpu_env, cpu_dest, cpu_src); + break; + case 0x0e: /* fsin */ + gen_helper_fsin(cpu_env, cpu_dest, cpu_src); + break; + case 0x0f: /* ftan */ + gen_helper_ftan(cpu_env, cpu_dest, cpu_src); + break; + case 0x10: /* fetox */ + gen_helper_fetox(cpu_env, cpu_dest, cpu_src); + break; + case 0x11: /* ftwotox */ + gen_helper_ftwotox(cpu_env, cpu_dest, cpu_src); + break; + case 0x12: /* ftentox */ + gen_helper_ftentox(cpu_env, cpu_dest, cpu_src); + break; + case 0x14: /* flogn */ + gen_helper_flogn(cpu_env, cpu_dest, cpu_src); + break; + case 0x15: /* flog10 */ + gen_helper_flog10(cpu_env, cpu_dest, cpu_src); + break; + case 0x16: /* flog2 */ + gen_helper_flog2(cpu_env, cpu_dest, cpu_src); + break; case 0x18: /* fabs */ gen_helper_fabs(cpu_env, cpu_dest, cpu_src); break; @@ -5063,6 +5105,9 @@ DISAS_INSN(fpu) case 0x5c: /* fdabs */ gen_helper_fdabs(cpu_env, cpu_dest, cpu_src); break; + case 0x19: /* fcosh */ + gen_helper_fcosh(cpu_env, cpu_dest, cpu_src); + break; case 0x1a: /* fneg */ gen_helper_fneg(cpu_env, cpu_dest, cpu_src); break; @@ -5072,6 +5117,12 @@ DISAS_INSN(fpu) case 0x5e: /* fdneg */ gen_helper_fdneg(cpu_env, cpu_dest, cpu_src); break; + case 0x1c: /* facos */ + gen_helper_facos(cpu_env, cpu_dest, cpu_src); + break; + case 0x1d: /* fcos */ + gen_helper_fcos(cpu_env, cpu_dest, cpu_src); + break; case 0x1e: /* fgetexp */ gen_helper_fgetexp(cpu_env, cpu_dest, cpu_src); break; @@ -5129,6 +5180,14 @@ DISAS_INSN(fpu) case 0x6c: /* fdsub */ gen_helper_fdsub(cpu_env, cpu_dest, cpu_src, cpu_dest); break; + case 0x30: case 0x31: case 0x32: + case 0x33: case 0x34: case 0x35: + case 0x36: case 0x37: { + TCGv_ptr cpu_dest2 = gen_fp_ptr(REG(ext, 0)); + gen_helper_fsincos(cpu_env, cpu_dest, cpu_dest2, cpu_src); + tcg_temp_free_ptr(cpu_dest2); + } + break; case 0x38: /* fcmp */ gen_helper_fcmp(cpu_env, cpu_src, cpu_dest); return; diff --git a/tests/check-qdict.c b/tests/check-qdict.c index ec628f3453..a3faea8bfc 100644 --- a/tests/check-qdict.c +++ b/tests/check-qdict.c @@ -665,6 +665,133 @@ static void qdict_crumple_test_empty(void) QDECREF(dst); } +static int qdict_count_entries(QDict *dict) +{ + const QDictEntry *e; + int count = 0; + + for (e = qdict_first(dict); e; e = qdict_next(dict, e)) { + count++; + } + + return count; +} + +static void qdict_rename_keys_test(void) +{ + QDict *dict = qdict_new(); + QDict *copy; + QDictRenames *renames; + Error *local_err = NULL; + + qdict_put_str(dict, "abc", "foo"); + qdict_put_str(dict, "abcdef", "bar"); + qdict_put_int(dict, "number", 42); + qdict_put_bool(dict, "flag", true); + qdict_put_null(dict, "nothing"); + + /* Empty rename list */ + renames = (QDictRenames[]) { + { NULL, "this can be anything" } + }; + copy = qdict_clone_shallow(dict); + qdict_rename_keys(copy, renames, &error_abort); + + g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo"); + g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar"); + g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42); + g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true); + g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL); + g_assert_cmpint(qdict_count_entries(copy), ==, 5); + + QDECREF(copy); + + /* Simple rename of all entries */ + renames = (QDictRenames[]) { + { "abc", "str1" }, + { "abcdef", "str2" }, + { "number", "int" }, + { "flag", "bool" }, + { "nothing", "null" }, + { NULL , NULL } + }; + copy = qdict_clone_shallow(dict); + qdict_rename_keys(copy, renames, &error_abort); + + g_assert(!qdict_haskey(copy, "abc")); + g_assert(!qdict_haskey(copy, "abcdef")); + g_assert(!qdict_haskey(copy, "number")); + g_assert(!qdict_haskey(copy, "flag")); + g_assert(!qdict_haskey(copy, "nothing")); + + g_assert_cmpstr(qdict_get_str(copy, "str1"), ==, "foo"); + g_assert_cmpstr(qdict_get_str(copy, "str2"), ==, "bar"); + g_assert_cmpint(qdict_get_int(copy, "int"), ==, 42); + g_assert_cmpint(qdict_get_bool(copy, "bool"), ==, true); + g_assert(qobject_type(qdict_get(copy, "null")) == QTYPE_QNULL); + g_assert_cmpint(qdict_count_entries(copy), ==, 5); + + QDECREF(copy); + + /* Renames are processed top to bottom */ + renames = (QDictRenames[]) { + { "abc", "tmp" }, + { "abcdef", "abc" }, + { "number", "abcdef" }, + { "flag", "number" }, + { "nothing", "flag" }, + { "tmp", "nothing" }, + { NULL , NULL } + }; + copy = qdict_clone_shallow(dict); + qdict_rename_keys(copy, renames, &error_abort); + + g_assert_cmpstr(qdict_get_str(copy, "nothing"), ==, "foo"); + g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "bar"); + g_assert_cmpint(qdict_get_int(copy, "abcdef"), ==, 42); + g_assert_cmpint(qdict_get_bool(copy, "number"), ==, true); + g_assert(qobject_type(qdict_get(copy, "flag")) == QTYPE_QNULL); + g_assert(!qdict_haskey(copy, "tmp")); + g_assert_cmpint(qdict_count_entries(copy), ==, 5); + + QDECREF(copy); + + /* Conflicting rename */ + renames = (QDictRenames[]) { + { "abcdef", "abc" }, + { NULL , NULL } + }; + copy = qdict_clone_shallow(dict); + qdict_rename_keys(copy, renames, &local_err); + + g_assert(local_err != NULL); + error_free(local_err); + local_err = NULL; + + g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo"); + g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar"); + g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42); + g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true); + g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL); + g_assert_cmpint(qdict_count_entries(copy), ==, 5); + + QDECREF(copy); + + /* Renames in an empty dict */ + renames = (QDictRenames[]) { + { "abcdef", "abc" }, + { NULL , NULL } + }; + + QDECREF(dict); + dict = qdict_new(); + + qdict_rename_keys(dict, renames, &error_abort); + g_assert(qdict_first(dict) == NULL); + + QDECREF(dict); +} + static void qdict_crumple_test_bad_inputs(void) { QDict *src; @@ -880,6 +1007,8 @@ int main(int argc, char **argv) g_test_add_func("/public/crumple/bad_inputs", qdict_crumple_test_bad_inputs); + g_test_add_func("/public/rename_keys", qdict_rename_keys_test); + /* The Big one */ if (g_test_slow()) { g_test_add_func("/stress/test", qdict_stress_test); diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker index 994a35a332..a00004319e 100644 --- a/tests/docker/dockerfiles/fedora.docker +++ b/tests/docker/dockerfiles/fedora.docker @@ -2,7 +2,7 @@ FROM fedora:27 ENV PACKAGES \ ccache gettext git tar PyYAML sparse flex bison python3 bzip2 hostname \ glib2-devel pixman-devel zlib-devel SDL-devel libfdt-devel \ - gcc gcc-c++ clang make perl which bc findutils libaio-devel \ + gcc gcc-c++ llvm clang make perl which bc findutils libaio-devel \ nettle-devel libasan libubsan \ mingw32-pixman mingw32-glib2 mingw32-gmp mingw32-SDL mingw32-pkg-config \ mingw32-gtk2 mingw32-gtk3 mingw32-gnutls mingw32-nettle mingw32-libtasn1 \ diff --git a/tests/docker/test-debug b/tests/docker/test-debug index d020b06917..d3f9f70d01 100755 --- a/tests/docker/test-debug +++ b/tests/docker/test-debug @@ -1,6 +1,6 @@ #!/bin/bash -e # -# Compile and check with clang & --enable-debug. +# Compile and check with clang & --enable-debug --enable-sanitizers. # # Copyright (c) 2016-2018 Red Hat Inc. # @@ -19,8 +19,8 @@ requires clang asan cd "$BUILD_DIR" OPTS="--cxx=clang++ --cc=clang --host-cc=clang" -OPTS="--enable-debug $OPTS" +OPTS="--enable-debug --enable-sanitizers $OPTS" build_qemu $OPTS -make $MAKEFLAGS check +make $MAKEFLAGS V=1 check install_qemu diff --git a/tests/migration-test.c b/tests/migration-test.c index 74f9361bdd..422bf1afdf 100644 --- a/tests/migration-test.c +++ b/tests/migration-test.c @@ -382,7 +382,7 @@ static void migrate_start_postcopy(QTestState *who) } static void test_migrate_start(QTestState **from, QTestState **to, - const char *uri) + const char *uri, bool hide_stderr) { gchar *cmd_src, *cmd_dst; char *bootpath = g_strdup_printf("%s/bootsect", tmpfs); @@ -427,6 +427,17 @@ static void test_migrate_start(QTestState **from, QTestState **to, g_free(bootpath); + if (hide_stderr) { + gchar *tmp; + tmp = g_strdup_printf("%s 2>/dev/null", cmd_src); + g_free(cmd_src); + cmd_src = tmp; + + tmp = g_strdup_printf("%s 2>/dev/null", cmd_dst); + g_free(cmd_dst); + cmd_dst = tmp; + } + *from = qtest_start(cmd_src); g_free(cmd_src); @@ -518,7 +529,7 @@ static void test_migrate(void) char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); QTestState *from, *to; - test_migrate_start(&from, &to, uri); + test_migrate_start(&from, &to, uri, false); migrate_set_capability(from, "postcopy-ram", "true"); migrate_set_capability(to, "postcopy-ram", "true"); @@ -560,7 +571,7 @@ static void test_baddest(void) const char *status; bool failed; - test_migrate_start(&from, &to, "tcp:0:0"); + test_migrate_start(&from, &to, "tcp:0:0", true); migrate(from, "tcp:0:0"); do { rsp = wait_command(from, "{ 'execute': 'query-migrate' }"); diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030 index 457984b8e9..b5f88959aa 100755 --- a/tests/qemu-iotests/030 +++ b/tests/qemu-iotests/030 @@ -156,7 +156,7 @@ class TestSingleDrive(iotests.QMPTestCase): class TestParallelOps(iotests.QMPTestCase): num_ops = 4 # Number of parallel block-stream operations num_imgs = num_ops * 2 + 1 - image_len = num_ops * 1024 * 1024 + image_len = num_ops * 512 * 1024 imgs = [] def setUp(self): @@ -176,14 +176,14 @@ class TestParallelOps(iotests.QMPTestCase): '-o', 'backing_file=%s' % self.imgs[i-1], self.imgs[i]) # Put data into the images we are copying data from - for i in range(self.num_imgs / 2): - img_index = i * 2 + 1 - # Alternate between 512k and 1M. + odd_img_indexes = [x for x in reversed(range(self.num_imgs)) if x % 2 == 1] + for i in range(len(odd_img_indexes)): + # Alternate between 256KB and 512KB. # This way jobs will not finish in the same order they were created - num_kb = 512 + 512 * (i % 2) + num_kb = 256 + 256 * (i % 2) qemu_io('-f', iotests.imgfmt, - '-c', 'write -P %d %d %d' % (i, i*1024*1024, num_kb * 1024), - self.imgs[img_index]) + '-c', 'write -P 0xFF %dk %dk' % (i * 512, num_kb), + self.imgs[odd_img_indexes[i]]) # Attach the drive to the VM self.vm = iotests.VM() @@ -318,12 +318,14 @@ class TestParallelOps(iotests.QMPTestCase): self.wait_until_completed(drive='commit-drive0') # Test a block-stream and a block-commit job in parallel - def test_stream_commit(self): + # Here the stream job is supposed to finish quickly in order to reproduce + # the scenario that triggers the bug fixed in 3d5d319e1221 and 1a63a907507 + def test_stream_commit_1(self): self.assertLessEqual(8, self.num_imgs) self.assert_no_active_block_jobs() # Stream from node0 into node2 - result = self.vm.qmp('block-stream', device='node2', job_id='node2') + result = self.vm.qmp('block-stream', device='node2', base_node='node0', job_id='node2') self.assert_qmp(result, 'return', {}) # Commit from the active layer into node3 @@ -348,6 +350,38 @@ class TestParallelOps(iotests.QMPTestCase): self.assert_no_active_block_jobs() + # This is similar to test_stream_commit_1 but both jobs are slowed + # down so they can run in parallel for a little while. + def test_stream_commit_2(self): + self.assertLessEqual(8, self.num_imgs) + self.assert_no_active_block_jobs() + + # Stream from node0 into node4 + result = self.vm.qmp('block-stream', device='node4', base_node='node0', job_id='node4', speed=1024*1024) + self.assert_qmp(result, 'return', {}) + + # Commit from the active layer into node5 + result = self.vm.qmp('block-commit', device='drive0', base=self.imgs[5], speed=1024*1024) + self.assert_qmp(result, 'return', {}) + + # Wait for all jobs to be finished. + pending_jobs = ['node4', 'drive0'] + while len(pending_jobs) > 0: + for event in self.vm.get_qmp_events(wait=True): + if event['event'] == 'BLOCK_JOB_COMPLETED': + node_name = self.dictpath(event, 'data/device') + self.assertTrue(node_name in pending_jobs) + self.assert_qmp_absent(event, 'data/error') + pending_jobs.remove(node_name) + if event['event'] == 'BLOCK_JOB_READY': + self.assert_qmp(event, 'data/device', 'drive0') + self.assert_qmp(event, 'data/type', 'commit') + self.assert_qmp_absent(event, 'data/error') + self.assertTrue('drive0' in pending_jobs) + self.vm.qmp('block-job-complete', device='drive0') + + self.assert_no_active_block_jobs() + # Test the base_node parameter def test_stream_base_node_name(self): self.assert_no_active_block_jobs() diff --git a/tests/qemu-iotests/030.out b/tests/qemu-iotests/030.out index 391c8573ca..42314e9c00 100644 --- a/tests/qemu-iotests/030.out +++ b/tests/qemu-iotests/030.out @@ -1,5 +1,5 @@ -....................... +........................ ---------------------------------------------------------------------- -Ran 23 tests +Ran 24 tests OK diff --git a/tests/qemu-iotests/049.out b/tests/qemu-iotests/049.out index 003247023e..0871bff564 100644 --- a/tests/qemu-iotests/049.out +++ b/tests/qemu-iotests/049.out @@ -166,11 +166,11 @@ qemu-img create -f qcow2 -o compat=1.1 TEST_DIR/t.qcow2 64M Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=1.1 cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o compat=0.42 TEST_DIR/t.qcow2 64M -qemu-img: TEST_DIR/t.qcow2: Invalid compatibility level: '0.42' +qemu-img: TEST_DIR/t.qcow2: Invalid parameter '0.42' Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=0.42 cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o compat=foobar TEST_DIR/t.qcow2 64M -qemu-img: TEST_DIR/t.qcow2: Invalid compatibility level: 'foobar' +qemu-img: TEST_DIR/t.qcow2: Invalid parameter 'foobar' Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=foobar cluster_size=65536 lazy_refcounts=off refcount_bits=16 == Check preallocation option == @@ -182,7 +182,7 @@ qemu-img create -f qcow2 -o preallocation=metadata TEST_DIR/t.qcow2 64M Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=65536 preallocation=metadata lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o preallocation=1234 TEST_DIR/t.qcow2 64M -qemu-img: TEST_DIR/t.qcow2: invalid parameter value: 1234 +qemu-img: TEST_DIR/t.qcow2: Invalid parameter '1234' Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=65536 preallocation=1234 lazy_refcounts=off refcount_bits=16 == Check encryption option == @@ -205,7 +205,7 @@ qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=off TEST_DIR/t.qcow2 64M Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=0.10 cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=on TEST_DIR/t.qcow2 64M -qemu-img: TEST_DIR/t.qcow2: Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater) +qemu-img: TEST_DIR/t.qcow2: Lazy refcounts only supported with compatibility level 1.1 and above (use version=v3 or greater) Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat=0.10 cluster_size=65536 lazy_refcounts=on refcount_bits=16 *** done diff --git a/tests/qemu-iotests/059 b/tests/qemu-iotests/059 index 40f89eae18..530bbbe6ce 100755 --- a/tests/qemu-iotests/059 +++ b/tests/qemu-iotests/059 @@ -152,9 +152,8 @@ done echo echo "=== Testing afl image with a very large capacity ===" _use_sample_img afl9.vmdk.bz2 -# The sed makes this test pass on machines with little RAM -# (and also with 32 bit builds) -_img_info | sed -e 's/Cannot allocate memory/Invalid argument/' +_img_info | grep -q 'Cannot allocate memory' && _notrun "Insufficent memory, skipped test" +_img_info _cleanup_test_img # success, all done diff --git a/tests/qemu-iotests/080 b/tests/qemu-iotests/080 index 1c2bd85742..4dbe68e950 100755 --- a/tests/qemu-iotests/080 +++ b/tests/qemu-iotests/080 @@ -171,12 +171,32 @@ poke_file "$TEST_IMG" "$offset_l2_table_0" "\x80\x00\x00\xff\xff\xff\x00\x00" { $QEMU_IMG snapshot -c test $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir echo -echo "== Invalid snapshot L1 table ==" +echo "== Invalid snapshot L1 table offset ==" +_make_test_img 64M +{ $QEMU_IO -c "write 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IMG snapshot -c test $TEST_IMG; } 2>&1 | _filter_testdir +poke_file "$TEST_IMG" "$offset_snap1_l1_offset" "\x00\x00\x00\x00\x00\x40\x02\x00" +{ $QEMU_IMG convert -s test $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_testdir +{ $QEMU_IMG amend -o compat=0.10 $TEST_IMG; } 2>&1 | _filter_testdir +{ $QEMU_IO -c "open -o overlap-check.inactive-l2=on $TEST_IMG" \ + -c 'write 0 4k'; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IMG snapshot -a test $TEST_IMG; } 2>&1 | _filter_testdir +{ $QEMU_IMG snapshot -d test $TEST_IMG; } 2>&1 | _filter_testdir +_check_test_img + +echo +echo "== Invalid snapshot L1 table size ==" _make_test_img 64M { $QEMU_IO -c "write 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir { $QEMU_IMG snapshot -c test $TEST_IMG; } 2>&1 | _filter_testdir poke_file "$TEST_IMG" "$offset_snap1_l1_size" "\x10\x00\x00\x00" { $QEMU_IMG convert -s test $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_testdir +{ $QEMU_IMG amend -o compat=0.10 $TEST_IMG; } 2>&1 | _filter_testdir +{ $QEMU_IO -c "open -o overlap-check.inactive-l2=on $TEST_IMG" \ + -c 'write 0 4k'; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IMG snapshot -a test $TEST_IMG; } 2>&1 | _filter_testdir +{ $QEMU_IMG snapshot -d test $TEST_IMG; } 2>&1 | _filter_testdir +_check_test_img # success, all done echo "*** done" diff --git a/tests/qemu-iotests/080.out b/tests/qemu-iotests/080.out index 6a7fda1356..4e0f7f7b92 100644 --- a/tests/qemu-iotests/080.out +++ b/tests/qemu-iotests/080.out @@ -18,18 +18,18 @@ can't open device TEST_DIR/t.qcow2: Reference count table too large == Misaligned refcount table == Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -can't open device TEST_DIR/t.qcow2: Invalid reference count table offset +can't open device TEST_DIR/t.qcow2: Reference count table offset invalid == Huge refcount offset == Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -can't open device TEST_DIR/t.qcow2: Invalid reference count table offset +can't open device TEST_DIR/t.qcow2: Reference count table offset invalid == Invalid snapshot table == Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -can't open device TEST_DIR/t.qcow2: Too many snapshots -can't open device TEST_DIR/t.qcow2: Too many snapshots -can't open device TEST_DIR/t.qcow2: Invalid snapshot table offset -can't open device TEST_DIR/t.qcow2: Invalid snapshot table offset +can't open device TEST_DIR/t.qcow2: Snapshot table too large +can't open device TEST_DIR/t.qcow2: Snapshot table too large +can't open device TEST_DIR/t.qcow2: Snapshot table offset invalid +can't open device TEST_DIR/t.qcow2: Snapshot table offset invalid == Hitting snapshot table size limit == Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 @@ -41,8 +41,8 @@ read 512/512 bytes at offset 0 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 can't open device TEST_DIR/t.qcow2: Active L1 table too large can't open device TEST_DIR/t.qcow2: Active L1 table too large -can't open device TEST_DIR/t.qcow2: Invalid L1 table offset -can't open device TEST_DIR/t.qcow2: Invalid L1 table offset +can't open device TEST_DIR/t.qcow2: Active L1 table offset invalid +can't open device TEST_DIR/t.qcow2: Active L1 table offset invalid == Invalid L1 table (with internal snapshot in the image) == Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 @@ -59,9 +59,49 @@ wrote 512/512 bytes at offset 0 qemu-img: Could not create snapshot 'test': -27 (File too large) qemu-img: Could not create snapshot 'test': -11 (Resource temporarily unavailable) -== Invalid snapshot L1 table == +== Invalid snapshot L1 table offset == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-img: Failed to load snapshot: Snapshot L1 table offset invalid +qemu-img: Snapshot L1 table offset invalid +qemu-img: Error while amending options: Invalid argument +Failed to flush the refcount block cache: Invalid argument +write failed: Invalid argument +qemu-img: Snapshot L1 table offset invalid +qemu-img: Could not apply snapshot 'test': Failed to load snapshot: Invalid argument +qemu-img: Could not delete snapshot 'test': Snapshot L1 table offset invalid +ERROR snapshot 1 (test) l1_offset=0x400200: L1 table is not cluster aligned; snapshot table entry corrupted +Leaked cluster 4 refcount=2 reference=1 +Leaked cluster 5 refcount=2 reference=1 +Leaked cluster 6 refcount=1 reference=0 + +1 errors were found on the image. +Data may be corrupted, or further writes to the image may corrupt it. + +3 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. + +== Invalid snapshot L1 table size == Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qemu-img: Failed to load snapshot: Snapshot L1 table too large +qemu-img: Snapshot L1 table too large +qemu-img: Error while amending options: File too large +Failed to flush the refcount block cache: File too large +write failed: File too large +qemu-img: Snapshot L1 table too large +qemu-img: Could not apply snapshot 'test': Failed to load snapshot: File too large +qemu-img: Could not delete snapshot 'test': Snapshot L1 table too large +ERROR snapshot 1 (test) l1_size=0x10000000: L1 table is too large; snapshot table entry corrupted +Leaked cluster 4 refcount=2 reference=1 +Leaked cluster 5 refcount=2 reference=1 +Leaked cluster 6 refcount=1 reference=0 + +1 errors were found on the image. +Data may be corrupted, or further writes to the image may corrupt it. + +3 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. *** done diff --git a/tests/qemu-iotests/096 b/tests/qemu-iotests/096 index aeeb3753cf..aeeb3753cf 100644..100755 --- a/tests/qemu-iotests/096 +++ b/tests/qemu-iotests/096 diff --git a/tests/qemu-iotests/112.out b/tests/qemu-iotests/112.out index 81b04d1452..86f041075d 100644 --- a/tests/qemu-iotests/112.out +++ b/tests/qemu-iotests/112.out @@ -21,9 +21,9 @@ refcount bits: 16 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 refcount bits: 16 -qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use or greater) +qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use version=v3 or greater) Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use or greater) +qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use version=v3 or greater) Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 === Snapshot limit on refcount_bits=1 === diff --git a/tests/qemu-iotests/124 b/tests/qemu-iotests/124 index 8e76e62f93..8e76e62f93 100644..100755 --- a/tests/qemu-iotests/124 +++ b/tests/qemu-iotests/124 diff --git a/tests/qemu-iotests/129 b/tests/qemu-iotests/129 index 9e87e1c8d9..9e87e1c8d9 100644..100755 --- a/tests/qemu-iotests/129 +++ b/tests/qemu-iotests/129 diff --git a/tests/qemu-iotests/132 b/tests/qemu-iotests/132 index f53ef6e391..f53ef6e391 100644..100755 --- a/tests/qemu-iotests/132 +++ b/tests/qemu-iotests/132 diff --git a/tests/qemu-iotests/136 b/tests/qemu-iotests/136 index 88b97ea7c6..88b97ea7c6 100644..100755 --- a/tests/qemu-iotests/136 +++ b/tests/qemu-iotests/136 diff --git a/tests/qemu-iotests/139 b/tests/qemu-iotests/139 index cc7fe337f3..cc7fe337f3 100644..100755 --- a/tests/qemu-iotests/139 +++ b/tests/qemu-iotests/139 diff --git a/tests/qemu-iotests/148 b/tests/qemu-iotests/148 index e01b061fe7..e01b061fe7 100644..100755 --- a/tests/qemu-iotests/148 +++ b/tests/qemu-iotests/148 diff --git a/tests/qemu-iotests/152 b/tests/qemu-iotests/152 index fec546d033..fec546d033 100644..100755 --- a/tests/qemu-iotests/152 +++ b/tests/qemu-iotests/152 diff --git a/tests/qemu-iotests/153 b/tests/qemu-iotests/153 index fa25eb24bd..adfd02695b 100755 --- a/tests/qemu-iotests/153 +++ b/tests/qemu-iotests/153 @@ -32,6 +32,7 @@ _cleanup() { _cleanup_test_img rm -f "${TEST_IMG}.base" + rm -f "${TEST_IMG}.overlay" rm -f "${TEST_IMG}.convert" rm -f "${TEST_IMG}.a" rm -f "${TEST_IMG}.b" @@ -177,8 +178,6 @@ rm -f "${TEST_IMG}.lnk" &>/dev/null ln -s ${TEST_IMG} "${TEST_IMG}.lnk" || echo "Failed to create link" _run_qemu_with_images "${TEST_IMG}.lnk" "${TEST_IMG}" -echo -echo "== Closing an image should unlock it ==" _launch_qemu _send_qemu_cmd $QEMU_HANDLE \ @@ -193,7 +192,10 @@ _send_qemu_cmd $QEMU_HANDLE \ _run_cmd $QEMU_IO "${TEST_IMG}" -c 'write 0 512' -echo "Closing drive" +echo "Creating overlay with qemu-img when the guest is running should be allowed" +_run_cmd $QEMU_IMG create -f $IMGFMT -b "${TEST_IMG}" "${TEST_IMG}.overlay" + +echo "== Closing an image should unlock it ==" _send_qemu_cmd $QEMU_HANDLE \ "{ 'execute': 'human-monitor-command', 'arguments': { 'command-line': 'drive_del d0' } }" \ diff --git a/tests/qemu-iotests/153.out b/tests/qemu-iotests/153.out index 5b917b177c..34309cfb20 100644 --- a/tests/qemu-iotests/153.out +++ b/tests/qemu-iotests/153.out @@ -372,15 +372,16 @@ Is another process using the image? == Symbolic link == QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2: Failed to get "write" lock Is another process using the image? - -== Closing an image should unlock it == {"return": {}} Adding drive _qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512 can't open device TEST_DIR/t.qcow2: Failed to get "write" lock Is another process using the image? -Closing drive +Creating overlay with qemu-img when the guest is running should be allowed + +_qemu_img_wrapper create -f qcow2 -b TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.overlay +== Closing an image should unlock it == _qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512 Adding two and closing one diff --git a/tests/qemu-iotests/163 b/tests/qemu-iotests/163 index 403842354e..403842354e 100644..100755 --- a/tests/qemu-iotests/163 +++ b/tests/qemu-iotests/163 diff --git a/tests/qemu-iotests/203 b/tests/qemu-iotests/203 index 2c811917d8..4874a1a0d8 100755 --- a/tests/qemu-iotests/203 +++ b/tests/qemu-iotests/203 @@ -49,11 +49,18 @@ with iotests.FilePath('disk0.img') as disk0_img_path, \ node_name='drive1-node', iothread='iothread0', force=True)) + iotests.log('Enabling migration QMP events...') + iotests.log(vm.qmp('migrate-set-capabilities', capabilities=[ + { + 'capability': 'events', + 'state': True + } + ])) + iotests.log('Starting migration...') iotests.log(vm.qmp('migrate', uri='exec:cat >/dev/null')) while True: - vm.get_qmp_event(wait=60.0) - result = vm.qmp('query-migrate') - status = result.get('return', {}).get('status', None) - if status == 'completed': + event = vm.event_wait('MIGRATION') + iotests.log(event, filters=[iotests.filter_qmp_event]) + if event['data']['status'] == 'completed': break diff --git a/tests/qemu-iotests/203.out b/tests/qemu-iotests/203.out index 3f1ff900e4..1a11f0975c 100644 --- a/tests/qemu-iotests/203.out +++ b/tests/qemu-iotests/203.out @@ -2,5 +2,10 @@ Launching VM... Setting IOThreads... {u'return': {}} {u'return': {}} +Enabling migration QMP events... +{u'return': {}} Starting migration... {u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'setup'}, u'event': u'MIGRATION'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'active'}, u'event': u'MIGRATION'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'completed'}, u'event': u'MIGRATION'} diff --git a/tests/qemu-iotests/205 b/tests/qemu-iotests/205 index e7b2eae51d..e7b2eae51d 100644..100755 --- a/tests/qemu-iotests/205 +++ b/tests/qemu-iotests/205 diff --git a/tests/qemu-iotests/206 b/tests/qemu-iotests/206 new file mode 100755 index 0000000000..0a18b2b19a --- /dev/null +++ b/tests/qemu-iotests/206 @@ -0,0 +1,436 @@ +#!/bin/bash +# +# Test qcow2 and file image creation +# +# Copyright (C) 2018 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# creator +owner=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +status=1 # failure is the default! + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux + +function do_run_qemu() +{ + echo Testing: "$@" + $QEMU -nographic -qmp stdio -serial none "$@" + echo +} + +function run_qemu() +{ + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ + | _filter_qemu | _filter_imgfmt \ + | _filter_actual_image_size +} + +echo +echo "=== Successful image creation (defaults) ===" +echo + +size=$((128 * 1024 * 1024)) + +run_qemu <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "file", + "filename": "$TEST_IMG", + "size": 0 + } +} +{ "execute": "blockdev-add", + "arguments": { + "driver": "file", + "node-name": "imgfile", + "filename": "$TEST_IMG" + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "imgfile", + "size": $size + } +} +{ "execute": "quit" } +EOF + +_img_info --format-specific + +echo +echo "=== Successful image creation (inline blockdev-add, explicit defaults) ===" +echo + +# Choose a different size to show that we got a new image +size=$((64 * 1024 * 1024)) + +run_qemu <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "file", + "filename": "$TEST_IMG", + "size": 0, + "preallocation": "off", + "nocow": false + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": { + "driver": "file", + "filename": "$TEST_IMG" + }, + "size": $size, + "version": "v3", + "cluster-size": 65536, + "preallocation": "off", + "lazy-refcounts": false, + "refcount-bits": 16 + } +} +{ "execute": "quit" } +EOF + +_img_info --format-specific + +echo +echo "=== Successful image creation (v3 non-default options) ===" +echo + +# Choose a different size to show that we got a new image +size=$((32 * 1024 * 1024)) + +run_qemu <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "file", + "filename": "$TEST_IMG", + "size": 0, + "preallocation": "falloc", + "nocow": true + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": { + "driver": "file", + "filename": "$TEST_IMG" + }, + "size": $size, + "version": "v3", + "cluster-size": 2097152, + "preallocation": "metadata", + "lazy-refcounts": true, + "refcount-bits": 1 + } +} +{ "execute": "quit" } +EOF + +_img_info --format-specific + +echo +echo "=== Successful image creation (v2 non-default options) ===" +echo + +mv $TEST_IMG $TEST_IMG.base + +run_qemu <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "file", + "filename": "$TEST_IMG", + "size": 0 + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": { + "driver": "file", + "filename": "$TEST_IMG" + }, + "size": $size, + "backing-file": "$TEST_IMG.base", + "backing-fmt": "qcow2", + "version": "v2", + "cluster-size": 512 + } +} +{ "execute": "quit" } +EOF + +_img_info --format-specific + +echo +echo "=== Successful image creation (encrypted) ===" +echo + +run_qemu -object secret,id=keysec0,data="foo" <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": { + "driver": "file", + "filename": "$TEST_IMG" + }, + "size": $size, + "encrypt": { + "format": "luks", + "key-secret": "keysec0", + "cipher-alg": "twofish-128", + "cipher-mode": "ctr", + "ivgen-alg": "plain64", + "ivgen-hash-alg": "md5", + "hash-alg": "sha1", + "iter-time": 10 + } + } +} +{ "execute": "quit" } +EOF + +_img_info --format-specific | _filter_img_info --format-specific + +echo +echo "=== Invalid BlockdevRef ===" +echo + +run_qemu <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "this doesn't exist", + "size": $size + } +} +{ "execute": "quit" } +EOF + + +echo +echo "=== Invalid sizes ===" +echo + +# TODO Negative image sizes aren't handled correctly, but this is a problem +# with QAPI's implementation of the 'size' type and affects other commands as +# well. Once this is fixed, we may want to add a test case here. + +# 1. Misaligned image size +# 2. 2^64 - 512 +# 3. 2^63 = 8 EB (qemu-img enforces image sizes less than this) +# 4. 2^63 - 512 (generally valid, but qcow2 can't handle images this size) + +run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 1234 + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 18446744073709551104 + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 9223372036854775808 + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 9223372036854775296 + } +} +{ "execute": "quit" } +EOF + +echo +echo "=== Invalid version ===" +echo + +run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 67108864, + "version": "v1" + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 67108864, + "version": "v2", + "lazy-refcounts": true + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 67108864, + "version": "v2", + "refcount-bits": 8 + } +} +{ "execute": "quit" } +EOF + +echo +echo "=== Invalid backing file options ===" +echo + +run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 67108864, + "backing-file": "/dev/null", + "preallocation": "full" + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 67108864, + "backing-fmt": "$IMGFMT" + } +} +{ "execute": "quit" } +EOF + +echo +echo "=== Invalid cluster size ===" +echo + +run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 67108864, + "cluster-size": 1234 + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 67108864, + "cluster-size": 128 + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 67108864, + "cluster-size": 4194304 + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 67108864, + "cluster-size": 0 + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 281474976710656, + "cluster-size": 512 + } +} +{ "execute": "quit" } +EOF + +echo +echo "=== Invalid refcount width ===" +echo + +run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 67108864, + "refcount-bits": 128 + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 67108864, + "refcount-bits": 0 + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "$IMGFMT", + "file": "node0", + "size": 67108864, + "refcount-bits": 7 + } +} +{ "execute": "quit" } +EOF + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/206.out b/tests/qemu-iotests/206.out new file mode 100644 index 0000000000..042342ae9d --- /dev/null +++ b/tests/qemu-iotests/206.out @@ -0,0 +1,209 @@ +QA output created by 206 + +=== Successful image creation (defaults) === + +Testing: +QMP_VERSION +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 128M (134217728 bytes) +cluster_size: 65536 +Format specific information: + compat: 1.1 + lazy refcounts: false + refcount bits: 16 + corrupt: false + +=== Successful image creation (inline blockdev-add, explicit defaults) === + +Testing: +QMP_VERSION +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 64M (67108864 bytes) +cluster_size: 65536 +Format specific information: + compat: 1.1 + lazy refcounts: false + refcount bits: 16 + corrupt: false + +=== Successful image creation (v3 non-default options) === + +Testing: +QMP_VERSION +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 32M (33554432 bytes) +cluster_size: 2097152 +Format specific information: + compat: 1.1 + lazy refcounts: true + refcount bits: 1 + corrupt: false + +=== Successful image creation (v2 non-default options) === + +Testing: +QMP_VERSION +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 32M (33554432 bytes) +cluster_size: 512 +backing file: TEST_DIR/t.IMGFMT.base +backing file format: IMGFMT +Format specific information: + compat: 0.10 + refcount bits: 16 + +=== Successful image creation (encrypted) === + +Testing: -object secret,id=keysec0,data=foo +QMP_VERSION +{"return": {}} +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 32M (33554432 bytes) +Format specific information: + compat: 1.1 + lazy refcounts: false + refcount bits: 16 + encrypt: + ivgen alg: plain64 + hash alg: sha1 + cipher alg: twofish-128 + uuid: 00000000-0000-0000-0000-000000000000 + format: luks + cipher mode: ctr + slots: + [0]: + active: true + iters: 1024 + key offset: 4096 + stripes: 4000 + [1]: + active: false + key offset: 69632 + [2]: + active: false + key offset: 135168 + [3]: + active: false + key offset: 200704 + [4]: + active: false + key offset: 266240 + [5]: + active: false + key offset: 331776 + [6]: + active: false + key offset: 397312 + [7]: + active: false + key offset: 462848 + payload offset: 528384 + master key iters: 1024 + corrupt: false + +=== Invalid BlockdevRef === + +Testing: +QMP_VERSION +{"return": {}} +{"error": {"class": "GenericError", "desc": "Cannot find device=this doesn't exist nor node_name=this doesn't exist"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + + +=== Invalid sizes === + +Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 +QMP_VERSION +{"return": {}} +{"error": {"class": "GenericError", "desc": "Image size must be a multiple of 512 bytes"}} +{"error": {"class": "GenericError", "desc": "Could not resize image: Image size cannot be negative"}} +{"error": {"class": "GenericError", "desc": "Could not resize image: Image size cannot be negative"}} +{"error": {"class": "GenericError", "desc": "Could not resize image: Failed to grow the L1 table: File too large"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + + +=== Invalid version === + +Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 +QMP_VERSION +{"return": {}} +{"error": {"class": "GenericError", "desc": "Invalid parameter 'v1'"}} +{"error": {"class": "GenericError", "desc": "Lazy refcounts only supported with compatibility level 1.1 and above (use version=v3 or greater)"}} +{"error": {"class": "GenericError", "desc": "Different refcount widths than 16 bits require compatibility level 1.1 or above (use version=v3 or greater)"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + + +=== Invalid backing file options === + +Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 +QMP_VERSION +{"return": {}} +{"error": {"class": "GenericError", "desc": "Backing file and preallocation cannot be used at the same time"}} +{"error": {"class": "GenericError", "desc": "Backing format cannot be used without backing file"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + + +=== Invalid cluster size === + +Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 +QMP_VERSION +{"return": {}} +{"error": {"class": "GenericError", "desc": "Cluster size must be a power of two between 512 and 2048k"}} +{"error": {"class": "GenericError", "desc": "Cluster size must be a power of two between 512 and 2048k"}} +{"error": {"class": "GenericError", "desc": "Cluster size must be a power of two between 512 and 2048k"}} +{"error": {"class": "GenericError", "desc": "Cluster size must be a power of two between 512 and 2048k"}} +{"error": {"class": "GenericError", "desc": "Could not resize image: Failed to grow the L1 table: File too large"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + + +=== Invalid refcount width === + +Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 +QMP_VERSION +{"return": {}} +{"error": {"class": "GenericError", "desc": "Refcount width must be a power of two and may not exceed 64 bits"}} +{"error": {"class": "GenericError", "desc": "Refcount width must be a power of two and may not exceed 64 bits"}} +{"error": {"class": "GenericError", "desc": "Refcount width must be a power of two and may not exceed 64 bits"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +*** done diff --git a/tests/qemu-iotests/207 b/tests/qemu-iotests/207 new file mode 100755 index 0000000000..f5c77852d1 --- /dev/null +++ b/tests/qemu-iotests/207 @@ -0,0 +1,261 @@ +#!/bin/bash +# +# Test ssh image creation +# +# Copyright (C) 2018 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# creator +owner=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +status=1 # failure is the default! + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt raw +_supported_proto ssh +_supported_os Linux + +function do_run_qemu() +{ + echo Testing: "$@" + $QEMU -nographic -qmp stdio -serial none "$@" + echo +} + +function run_qemu() +{ + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ + | _filter_qemu | _filter_imgfmt \ + | _filter_actual_image_size +} + +echo +echo "=== Successful image creation (defaults) ===" +echo + +run_qemu <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "ssh", + "location": { + "path": "$TEST_IMG_FILE", + "server": { + "host": "127.0.0.1", + "port": "22" + } + }, + "size": 4194304 + } +} +{ "execute": "quit" } +EOF + +_img_info | _filter_img_info +echo +TEST_IMG=$TEST_IMG_FILE _img_info | _filter_img_info + +echo +echo "=== Test host-key-check options ===" +echo + +run_qemu <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "ssh", + "location": { + "path": "$TEST_IMG_FILE", + "server": { + "host": "127.0.0.1", + "port": "22" + }, + "host-key-check": { + "mode": "none" + } + }, + "size": 8388608 + } +} +{ "execute": "quit" } +EOF + +_img_info | _filter_img_info + +run_qemu <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "ssh", + "location": { + "path": "$TEST_IMG_FILE", + "server": { + "host": "127.0.0.1", + "port": "22" + }, + "host-key-check": { + "mode": "known_hosts" + } + }, + "size": 4194304 + } +} +{ "execute": "quit" } +EOF + +_img_info | _filter_img_info + + +key=$(ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" | + cut -d" " -f3 | base64 -d | md5sum -b | cut -d" " -f1) + +run_qemu <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "ssh", + "location": { + "path": "$TEST_IMG_FILE", + "server": { + "host": "127.0.0.1", + "port": "22" + }, + "host-key-check": { + "mode": "hash", + "type": "md5", + "hash": "wrong" + } + }, + "size": 8388608 + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "ssh", + "location": { + "path": "$TEST_IMG_FILE", + "server": { + "host": "127.0.0.1", + "port": "22" + }, + "host-key-check": { + "mode": "hash", + "type": "md5", + "hash": "$key" + } + }, + "size": 8388608 + } +} +{ "execute": "quit" } +EOF + +_img_info | _filter_img_info + + +key=$(ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" | + cut -d" " -f3 | base64 -d | sha1sum -b | cut -d" " -f1) + +run_qemu <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "ssh", + "location": { + "path": "$TEST_IMG_FILE", + "server": { + "host": "127.0.0.1", + "port": "22" + }, + "host-key-check": { + "mode": "hash", + "type": "sha1", + "hash": "wrong" + } + }, + "size": 4194304 + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "ssh", + "location": { + "path": "$TEST_IMG_FILE", + "server": { + "host": "127.0.0.1", + "port": "22" + }, + "host-key-check": { + "mode": "hash", + "type": "sha1", + "hash": "$key" + } + }, + "size": 4194304 + } +} +{ "execute": "quit" } +EOF + +_img_info | _filter_img_info + +echo +echo "=== Invalid path and user ===" +echo + +run_qemu <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "ssh", + "location": { + "path": "/this/is/not/an/existing/path", + "server": { + "host": "127.0.0.1", + "port": "22" + } + }, + "size": 4194304 + } +} +{ "execute": "x-blockdev-create", + "arguments": { + "driver": "ssh", + "location": { + "path": "$TEST_IMG_FILE", + "user": "invalid user", + "server": { + "host": "127.0.0.1", + "port": "22" + } + }, + "size": 4194304 + } +} +{ "execute": "quit" } +EOF + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/207.out b/tests/qemu-iotests/207.out new file mode 100644 index 0000000000..417deee970 --- /dev/null +++ b/tests/qemu-iotests/207.out @@ -0,0 +1,75 @@ +QA output created by 207 + +=== Successful image creation (defaults) === + +Testing: +QMP_VERSION +{"return": {}} +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}} +file format: IMGFMT +virtual size: 4.0M (4194304 bytes) + +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 4.0M (4194304 bytes) + +=== Test host-key-check options === + +Testing: +QMP_VERSION +{"return": {}} +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}} +file format: IMGFMT +virtual size: 8.0M (8388608 bytes) +Testing: +QMP_VERSION +{"return": {}} +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}} +file format: IMGFMT +virtual size: 4.0M (4194304 bytes) +Testing: +QMP_VERSION +{"return": {}} +{"error": {"class": "GenericError", "desc": "remote host key does not match host_key_check 'wrong'"}} +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}} +file format: IMGFMT +virtual size: 8.0M (8388608 bytes) +Testing: +QMP_VERSION +{"return": {}} +{"error": {"class": "GenericError", "desc": "remote host key does not match host_key_check 'wrong'"}} +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}} +file format: IMGFMT +virtual size: 4.0M (4194304 bytes) + +=== Invalid path and user === + +Testing: +QMP_VERSION +{"return": {}} +{"error": {"class": "GenericError", "desc": "failed to open remote file '/this/is/not/an/existing/path': Failed opening remote file (libssh2 error code: -31)"}} +{"error": {"class": "GenericError", "desc": "failed to authenticate using publickey authentication and the identities held by your ssh-agent"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +*** done diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index a2dfe79d86..c401791fcd 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -202,3 +202,5 @@ 203 rw auto 204 rw auto quick 205 rw auto quick +206 rw auto +207 rw auto diff --git a/tests/qmp-test.c b/tests/qmp-test.c index 22445d9ec2..7470c6b754 100644 --- a/tests/qmp-test.c +++ b/tests/qmp-test.c @@ -204,6 +204,11 @@ static bool query_is_blacklisted(const char *cmd) "query-gic-capabilities", /* arm */ /* Success depends on target-specific build configuration: */ "query-pci", /* CONFIG_PCI */ + /* Success depends on launching SEV guest */ + "query-sev-launch-measure", + /* Success depends on Host or Hypervisor SEV support */ + "query-sev", + "query-sev-capabilities", NULL }; int i; diff --git a/tests/test-qemu-opts.c b/tests/test-qemu-opts.c index 5d5a3daa7b..2c422abcd4 100644 --- a/tests/test-qemu-opts.c +++ b/tests/test-qemu-opts.c @@ -10,6 +10,7 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" #include "qemu/option.h" +#include "qemu/option_int.h" #include "qapi/error.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qstring.h" @@ -23,6 +24,8 @@ static QemuOptsList opts_list_01 = { { .name = "str1", .type = QEMU_OPT_STRING, + .help = "Help texts are preserved in qemu_opts_append", + .def_value_str = "default", },{ .name = "str2", .type = QEMU_OPT_STRING, @@ -32,6 +35,7 @@ static QemuOptsList opts_list_01 = { },{ .name = "number1", .type = QEMU_OPT_NUMBER, + .help = "Having help texts only for some options is okay", },{ .name = "number2", .type = QEMU_OPT_NUMBER, @@ -743,6 +747,250 @@ static void test_opts_parse_size(void) qemu_opts_reset(&opts_list_02); } +static void append_verify_list_01(QemuOptDesc *desc, bool with_overlapping) +{ + int i = 0; + + if (with_overlapping) { + g_assert_cmpstr(desc[i].name, ==, "str1"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING); + g_assert_cmpstr(desc[i].help, ==, + "Help texts are preserved in qemu_opts_append"); + g_assert_cmpstr(desc[i].def_value_str, ==, "default"); + i++; + + g_assert_cmpstr(desc[i].name, ==, "str2"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING); + g_assert_cmpstr(desc[i].help, ==, NULL); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); + i++; + } + + g_assert_cmpstr(desc[i].name, ==, "str3"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING); + g_assert_cmpstr(desc[i].help, ==, NULL); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); + i++; + + g_assert_cmpstr(desc[i].name, ==, "number1"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_NUMBER); + g_assert_cmpstr(desc[i].help, ==, + "Having help texts only for some options is okay"); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); + i++; + + g_assert_cmpstr(desc[i].name, ==, "number2"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_NUMBER); + g_assert_cmpstr(desc[i].help, ==, NULL); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); + i++; + + g_assert_cmpstr(desc[i].name, ==, NULL); +} + +static void append_verify_list_02(QemuOptDesc *desc) +{ + int i = 0; + + g_assert_cmpstr(desc[i].name, ==, "str1"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING); + g_assert_cmpstr(desc[i].help, ==, NULL); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); + i++; + + g_assert_cmpstr(desc[i].name, ==, "str2"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_STRING); + g_assert_cmpstr(desc[i].help, ==, NULL); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); + i++; + + g_assert_cmpstr(desc[i].name, ==, "bool1"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_BOOL); + g_assert_cmpstr(desc[i].help, ==, NULL); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); + i++; + + g_assert_cmpstr(desc[i].name, ==, "bool2"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_BOOL); + g_assert_cmpstr(desc[i].help, ==, NULL); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); + i++; + + g_assert_cmpstr(desc[i].name, ==, "size1"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_SIZE); + g_assert_cmpstr(desc[i].help, ==, NULL); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); + i++; + + g_assert_cmpstr(desc[i].name, ==, "size2"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_SIZE); + g_assert_cmpstr(desc[i].help, ==, NULL); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); + i++; + + g_assert_cmpstr(desc[i].name, ==, "size3"); + g_assert_cmpint(desc[i].type, ==, QEMU_OPT_SIZE); + g_assert_cmpstr(desc[i].help, ==, NULL); + g_assert_cmpstr(desc[i].def_value_str, ==, NULL); +} + +static void test_opts_append_to_null(void) +{ + QemuOptsList *merged; + + merged = qemu_opts_append(NULL, &opts_list_01); + g_assert(merged != &opts_list_01); + + g_assert_cmpstr(merged->name, ==, NULL); + g_assert_cmpstr(merged->implied_opt_name, ==, NULL); + g_assert_false(merged->merge_lists); + + append_verify_list_01(merged->desc, true); + + qemu_opts_free(merged); +} + +static void test_opts_append(void) +{ + QemuOptsList *first, *merged; + + first = qemu_opts_append(NULL, &opts_list_02); + merged = qemu_opts_append(first, &opts_list_01); + g_assert(first != &opts_list_02); + g_assert(merged != &opts_list_01); + + g_assert_cmpstr(merged->name, ==, NULL); + g_assert_cmpstr(merged->implied_opt_name, ==, NULL); + g_assert_false(merged->merge_lists); + + append_verify_list_02(&merged->desc[0]); + append_verify_list_01(&merged->desc[7], false); + + qemu_opts_free(merged); +} + +static void test_opts_to_qdict_basic(void) +{ + QemuOpts *opts; + QDict *dict; + + opts = qemu_opts_parse(&opts_list_01, "str1=foo,str2=,str3=bar,number1=42", + false, &error_abort); + g_assert(opts != NULL); + + dict = qemu_opts_to_qdict(opts, NULL); + g_assert(dict != NULL); + + g_assert_cmpstr(qdict_get_str(dict, "str1"), ==, "foo"); + g_assert_cmpstr(qdict_get_str(dict, "str2"), ==, ""); + g_assert_cmpstr(qdict_get_str(dict, "str3"), ==, "bar"); + g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42"); + g_assert_false(qdict_haskey(dict, "number2")); + + QDECREF(dict); + qemu_opts_del(opts); +} + +static void test_opts_to_qdict_filtered(void) +{ + QemuOptsList *first, *merged; + QemuOpts *opts; + QDict *dict; + + first = qemu_opts_append(NULL, &opts_list_02); + merged = qemu_opts_append(first, &opts_list_01); + + opts = qemu_opts_parse(merged, + "str1=foo,str2=,str3=bar,bool1=off,number1=42", + false, &error_abort); + g_assert(opts != NULL); + + /* Convert to QDict without deleting from opts */ + dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_01, false); + g_assert(dict != NULL); + g_assert_cmpstr(qdict_get_str(dict, "str1"), ==, "foo"); + g_assert_cmpstr(qdict_get_str(dict, "str2"), ==, ""); + g_assert_cmpstr(qdict_get_str(dict, "str3"), ==, "bar"); + g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42"); + g_assert_false(qdict_haskey(dict, "number2")); + g_assert_false(qdict_haskey(dict, "bool1")); + QDECREF(dict); + + dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_02, false); + g_assert(dict != NULL); + g_assert_cmpstr(qdict_get_str(dict, "str1"), ==, "foo"); + g_assert_cmpstr(qdict_get_str(dict, "str2"), ==, ""); + g_assert_cmpstr(qdict_get_str(dict, "bool1"), ==, "off"); + g_assert_false(qdict_haskey(dict, "str3")); + g_assert_false(qdict_haskey(dict, "number1")); + g_assert_false(qdict_haskey(dict, "number2")); + QDECREF(dict); + + /* Now delete converted options from opts */ + dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_01, true); + g_assert(dict != NULL); + g_assert_cmpstr(qdict_get_str(dict, "str1"), ==, "foo"); + g_assert_cmpstr(qdict_get_str(dict, "str2"), ==, ""); + g_assert_cmpstr(qdict_get_str(dict, "str3"), ==, "bar"); + g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42"); + g_assert_false(qdict_haskey(dict, "number2")); + g_assert_false(qdict_haskey(dict, "bool1")); + QDECREF(dict); + + dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_02, true); + g_assert(dict != NULL); + g_assert_cmpstr(qdict_get_str(dict, "bool1"), ==, "off"); + g_assert_false(qdict_haskey(dict, "str1")); + g_assert_false(qdict_haskey(dict, "str2")); + g_assert_false(qdict_haskey(dict, "str3")); + g_assert_false(qdict_haskey(dict, "number1")); + g_assert_false(qdict_haskey(dict, "number2")); + QDECREF(dict); + + g_assert_true(QTAILQ_EMPTY(&opts->head)); + + qemu_opts_del(opts); + qemu_opts_free(merged); +} + +static void test_opts_to_qdict_duplicates(void) +{ + QemuOpts *opts; + QemuOpt *opt; + QDict *dict; + + opts = qemu_opts_parse(&opts_list_03, "foo=a,foo=b", false, &error_abort); + g_assert(opts != NULL); + + /* Verify that opts has two options with the same name */ + opt = QTAILQ_FIRST(&opts->head); + g_assert_cmpstr(opt->name, ==, "foo"); + g_assert_cmpstr(opt->str , ==, "a"); + + opt = QTAILQ_NEXT(opt, next); + g_assert_cmpstr(opt->name, ==, "foo"); + g_assert_cmpstr(opt->str , ==, "b"); + + opt = QTAILQ_NEXT(opt, next); + g_assert(opt == NULL); + + /* In the conversion to QDict, the last one wins */ + dict = qemu_opts_to_qdict(opts, NULL); + g_assert(dict != NULL); + g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "b"); + QDECREF(dict); + + /* The last one still wins if entries are deleted, and both are deleted */ + dict = qemu_opts_to_qdict_filtered(opts, NULL, NULL, true); + g_assert(dict != NULL); + g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "b"); + QDECREF(dict); + + g_assert_true(QTAILQ_EMPTY(&opts->head)); + + qemu_opts_del(opts); +} + int main(int argc, char *argv[]) { register_opts(); @@ -761,6 +1009,11 @@ int main(int argc, char *argv[]) g_test_add_func("/qemu-opts/opts_parse/bool", test_opts_parse_bool); g_test_add_func("/qemu-opts/opts_parse/number", test_opts_parse_number); g_test_add_func("/qemu-opts/opts_parse/size", test_opts_parse_size); + g_test_add_func("/qemu-opts/append_to_null", test_opts_append_to_null); + g_test_add_func("/qemu-opts/append", test_opts_append); + g_test_add_func("/qemu-opts/to_qdict/basic", test_opts_to_qdict_basic); + g_test_add_func("/qemu-opts/to_qdict/filtered", test_opts_to_qdict_filtered); + g_test_add_func("/qemu-opts/to_qdict/duplicates", test_opts_to_qdict_duplicates); g_test_run(); return 0; } diff --git a/trace-events b/trace-events index 89fcad0fd1..855b0ab240 100644 --- a/trace-events +++ b/trace-events @@ -68,9 +68,9 @@ memory_region_tb_read(int cpu_index, uint64_t addr, uint64_t value, unsigned siz memory_region_tb_write(int cpu_index, uint64_t addr, uint64_t value, unsigned size) "cpu %d addr 0x%"PRIx64" value 0x%"PRIx64" size %u" memory_region_ram_device_read(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size) "cpu %d mr %p addr 0x%"PRIx64" value 0x%"PRIx64" size %u" memory_region_ram_device_write(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size) "cpu %d mr %p addr 0x%"PRIx64" value 0x%"PRIx64" size %u" -flatview_new(FlatView *view, MemoryRegion *root) "%p (root %p)" -flatview_destroy(FlatView *view, MemoryRegion *root) "%p (root %p)" -flatview_destroy_rcu(FlatView *view, MemoryRegion *root) "%p (root %p)" +flatview_new(void *view, void *root) "%p (root %p)" +flatview_destroy(void *view, void *root) "%p (root %p)" +flatview_destroy_rcu(void *view, void *root) "%p (root %p)" # gdbstub.c gdbstub_op_start(const char *device) "Starting gdbstub using device %s" diff --git a/ui/Makefile.objs b/ui/Makefile.objs index dcd54a5287..cc784346cb 100644 --- a/ui/Makefile.objs +++ b/ui/Makefile.objs @@ -38,26 +38,27 @@ common-obj-$(CONFIG_GTK) += gtk.mo gtk.mo-objs := gtk.o gtk.mo-cflags := $(GTK_CFLAGS) $(VTE_CFLAGS) gtk.mo-libs := $(GTK_LIBS) $(VTE_LIBS) +ifeq ($(CONFIG_OPENGL),y) +gtk.mo-objs += gtk-egl.o +gtk.mo-libs += $(OPENGL_LIBS) +ifeq ($(CONFIG_GTK_GL),y) +gtk.mo-objs += gtk-gl-area.o +endif +endif common-obj-$(CONFIG_CURSES) += curses.mo curses.mo-objs := curses.o curses.mo-cflags := $(CURSES_CFLAGS) curses.mo-libs := $(CURSES_LIBS) -ifeq ($(CONFIG_OPENGL),y) -common-obj-y += shader.o -common-obj-y += console-gl.o -common-obj-y += egl-helpers.o -common-obj-y += egl-context.o +common-obj-$(CONFIG_OPENGL) += shader.o +common-obj-$(CONFIG_OPENGL) += console-gl.o +common-obj-$(CONFIG_OPENGL) += egl-helpers.o +common-obj-$(CONFIG_OPENGL) += egl-context.o common-obj-$(CONFIG_OPENGL_DMABUF) += egl-headless.o -ifeq ($(CONFIG_GTK_GL),y) -gtk.mo-objs += gtk-gl-area.o -else -gtk.mo-objs += gtk-egl.o -gtk.mo-libs += $(OPENGL_LIBS) -endif -endif shader.o-libs += $(OPENGL_LIBS) console-gl.o-libs += $(OPENGL_LIBS) egl-helpers.o-libs += $(OPENGL_LIBS) +egl-context.o-libs += $(OPENGL_LIBS) +egl-headless.o-libs += $(OPENGL_LIBS) diff --git a/ui/console.c b/ui/console.c index 6ab4ff3baf..a8868fc04f 100644 --- a/ui/console.c +++ b/ui/console.c @@ -344,14 +344,28 @@ write_err: goto out; } -void qmp_screendump(const char *filename, Error **errp) +void qmp_screendump(const char *filename, bool has_device, const char *device, + bool has_head, int64_t head, Error **errp) { - QemuConsole *con = qemu_console_lookup_by_index(0); + QemuConsole *con; DisplaySurface *surface; - if (con == NULL) { - error_setg(errp, "There is no QemuConsole I can screendump from."); - return; + if (has_device) { + con = qemu_console_lookup_by_device_name(device, has_head ? head : 0, + errp); + if (!con) { + return; + } + } else { + if (has_head) { + error_setg(errp, "'head' must be specified together with 'device'"); + return; + } + con = qemu_console_lookup_by_index(0); + if (!con) { + error_setg(errp, "There is no console to take a screendump from"); + return; + } } graphic_hw_update(con); @@ -1039,8 +1053,10 @@ void console_select(unsigned int index) dcl->ops->dpy_gfx_switch(dcl, s->surface); } } - dpy_gfx_update(s, 0, 0, surface_width(s->surface), - surface_height(s->surface)); + if (s->surface) { + dpy_gfx_update(s, 0, 0, surface_width(s->surface), + surface_height(s->surface)); + } } if (ds->have_text) { dpy_text_resize(s, s->width, s->height); @@ -1370,8 +1386,8 @@ DisplaySurface *qemu_create_displaysurface_guestmem(int width, int height, return surface; } -static DisplaySurface *qemu_create_message_surface(int w, int h, - const char *msg) +DisplaySurface *qemu_create_message_surface(int w, int h, + const char *msg) { DisplaySurface *surface = qemu_create_displaysurface(w, h); pixman_color_t bg = color_table_rgb[0][QEMU_COLOR_BLACK]; diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c index eb86c26a1d..9390c6762e 100644 --- a/ui/gtk-egl.c +++ b/ui/gtk-egl.c @@ -19,6 +19,7 @@ #include "ui/console.h" #include "ui/gtk.h" #include "ui/egl-helpers.h" +#include "ui/shader.h" #include "sysemu/sysemu.h" @@ -194,6 +195,58 @@ void gd_egl_scanout_texture(DisplayChangeListener *dcl, backing_id, false); } +void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl, + QemuDmaBuf *dmabuf) +{ +#ifdef CONFIG_OPENGL_DMABUF + egl_dmabuf_import_texture(dmabuf); + if (!dmabuf->texture) { + return; + } + + gd_egl_scanout_texture(dcl, dmabuf->texture, + false, dmabuf->width, dmabuf->height, + 0, 0, dmabuf->width, dmabuf->height); +#endif +} + +void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl, + QemuDmaBuf *dmabuf, bool have_hot, + uint32_t hot_x, uint32_t hot_y) +{ +#ifdef CONFIG_OPENGL_DMABUF + VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); + + if (dmabuf) { + egl_dmabuf_import_texture(dmabuf); + if (!dmabuf->texture) { + return; + } + egl_fb_setup_for_tex(&vc->gfx.cursor_fb, dmabuf->width, dmabuf->height, + dmabuf->texture, false); + } else { + egl_fb_destroy(&vc->gfx.cursor_fb); + } +#endif +} + +void gd_egl_cursor_position(DisplayChangeListener *dcl, + uint32_t pos_x, uint32_t pos_y) +{ + VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); + + vc->gfx.cursor_x = pos_x; + vc->gfx.cursor_y = pos_y; +} + +void gd_egl_release_dmabuf(DisplayChangeListener *dcl, + QemuDmaBuf *dmabuf) +{ +#ifdef CONFIG_OPENGL_DMABUF + egl_dmabuf_release_texture(dmabuf); +#endif +} + void gd_egl_scanout_flush(DisplayChangeListener *dcl, uint32_t x, uint32_t y, uint32_t w, uint32_t h) { @@ -214,7 +267,15 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl, window = gtk_widget_get_window(vc->gfx.drawing_area); gdk_drawable_get_size(window, &ww, &wh); egl_fb_setup_default(&vc->gfx.win_fb, ww, wh); - egl_fb_blit(&vc->gfx.win_fb, &vc->gfx.guest_fb, !vc->gfx.y0_top); + if (vc->gfx.cursor_fb.texture) { + egl_texture_blit(vc->gfx.gls, &vc->gfx.win_fb, &vc->gfx.guest_fb, + vc->gfx.y0_top); + egl_texture_blend(vc->gfx.gls, &vc->gfx.win_fb, &vc->gfx.cursor_fb, + vc->gfx.y0_top, + vc->gfx.cursor_x, vc->gfx.cursor_y); + } else { + egl_fb_blit(&vc->gfx.win_fb, &vc->gfx.guest_fb, !vc->gfx.y0_top); + } eglSwapBuffers(qemu_egl_display, vc->gfx.esurface); } @@ -243,6 +243,8 @@ typedef struct VCChardev { #define TYPE_CHARDEV_VC "chardev-vc" #define VC_CHARDEV(obj) OBJECT_CHECK(VCChardev, (obj), TYPE_CHARDEV_VC) +bool gtk_use_gl_area; + static void gd_grab_pointer(VirtualConsole *vc, const char *reason); static void gd_ungrab_pointer(GtkDisplayState *s); static void gd_grab_keyboard(VirtualConsole *vc, const char *reason); @@ -453,7 +455,7 @@ static void gd_update_full_redraw(VirtualConsole *vc) int ww, wh; gdk_drawable_get_size(gtk_widget_get_window(area), &ww, &wh); #if defined(CONFIG_GTK_GL) - if (vc->gfx.gls) { + if (vc->gfx.gls && gtk_use_gl_area) { gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area)); return; } @@ -725,7 +727,7 @@ static const DisplayChangeListenerOps dcl_gl_area_ops = { .dpy_gl_update = gd_gl_area_scanout_flush, }; -#else +#endif /* CONFIG_GTK_GL */ static const DisplayChangeListenerOps dcl_egl_ops = { .dpy_name = "gtk-egl", @@ -742,10 +744,13 @@ static const DisplayChangeListenerOps dcl_egl_ops = { .dpy_gl_ctx_get_current = qemu_egl_get_current_context, .dpy_gl_scanout_disable = gd_egl_scanout_disable, .dpy_gl_scanout_texture = gd_egl_scanout_texture, + .dpy_gl_scanout_dmabuf = gd_egl_scanout_dmabuf, + .dpy_gl_cursor_dmabuf = gd_egl_cursor_dmabuf, + .dpy_gl_cursor_position = gd_egl_cursor_position, + .dpy_gl_release_dmabuf = gd_egl_release_dmabuf, .dpy_gl_update = gd_egl_scanout_flush, }; -#endif /* CONFIG_GTK_GL */ #endif /* CONFIG_OPENGL */ /** QEMU Events **/ @@ -844,13 +849,13 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque) #if defined(CONFIG_OPENGL) if (vc->gfx.gls) { -#if defined(CONFIG_GTK_GL) - /* invoke render callback please */ - return FALSE; -#else - gd_egl_draw(vc); - return TRUE; -#endif + if (gtk_use_gl_area) { + /* invoke render callback please */ + return FALSE; + } else { + gd_egl_draw(vc); + return TRUE; + } } #endif @@ -1993,7 +1998,7 @@ static void gd_connect_vc_gfx_signals(VirtualConsole *vc) g_signal_connect(vc->gfx.drawing_area, "draw", G_CALLBACK(gd_draw_event), vc); #if defined(CONFIG_GTK_GL) - if (display_opengl) { + if (gtk_use_gl_area) { /* wire up GtkGlArea events */ g_signal_connect(vc->gfx.drawing_area, "render", G_CALLBACK(gd_render_event), vc); @@ -2116,26 +2121,29 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, #if defined(CONFIG_OPENGL) if (display_opengl) { #if defined(CONFIG_GTK_GL) - vc->gfx.drawing_area = gtk_gl_area_new(); - vc->gfx.dcl.ops = &dcl_gl_area_ops; -#else - vc->gfx.drawing_area = gtk_drawing_area_new(); - /* - * gtk_widget_set_double_buffered() was deprecated in 3.14. - * It is required for opengl rendering on X11 though. A - * proper replacement (native opengl support) is only - * available in 3.16+. Silence the warning if possible. - */ + if (gtk_use_gl_area) { + vc->gfx.drawing_area = gtk_gl_area_new(); + vc->gfx.dcl.ops = &dcl_gl_area_ops; + } else +#endif /* CONFIG_GTK_GL */ + { + vc->gfx.drawing_area = gtk_drawing_area_new(); + /* + * gtk_widget_set_double_buffered() was deprecated in 3.14. + * It is required for opengl rendering on X11 though. A + * proper replacement (native opengl support) is only + * available in 3.16+. Silence the warning if possible. + */ #ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif - gtk_widget_set_double_buffered(vc->gfx.drawing_area, FALSE); + gtk_widget_set_double_buffered(vc->gfx.drawing_area, FALSE); #ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE #pragma GCC diagnostic pop #endif - vc->gfx.dcl.ops = &dcl_egl_ops; -#endif /* CONFIG_GTK_GL */ + vc->gfx.dcl.ops = &dcl_egl_ops; + } } else #endif { @@ -2436,11 +2444,15 @@ static void early_gtk_display_init(DisplayOptions *opts) assert(opts->type == DISPLAY_TYPE_GTK); if (opts->has_gl && opts->gl) { #if defined(CONFIG_OPENGL) -#if defined(CONFIG_GTK_GL) - gtk_gl_area_init(); -#else - gtk_egl_init(); +#if defined(CONFIG_GTK_GL) && defined(GDK_WINDOWING_WAYLAND) + if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) { + gtk_use_gl_area = true; + gtk_gl_area_init(); + } #endif + { + gtk_egl_init(); + } #endif } diff --git a/ui/spice-display.c b/ui/spice-display.c index 98ccdfb687..fe734821dd 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -26,20 +26,8 @@ #include "ui/spice-display.h" -static int debug = 0; bool spice_opengl; -static void GCC_FMT_ATTR(2, 3) dprint(int level, const char *fmt, ...) -{ - va_list args; - - if (level <= debug) { - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); - } -} - int qemu_spice_rect_is_empty(const QXLRect* r) { return r->top == r->bottom || r->left == r->right; @@ -322,8 +310,6 @@ void qemu_spice_create_host_memslot(SimpleSpiceDisplay *ssd) { QXLDevMemSlot memslot; - dprint(1, "%s/%d:\n", __func__, ssd->qxl.id); - memset(&memslot, 0, sizeof(memslot)); memslot.slot_group_id = MEMSLOT_GROUP_HOST; memslot.virt_end = ~0; @@ -347,10 +333,6 @@ void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd) ssd->buf = g_malloc(ssd->bufsize); } - dprint(1, "%s/%d: %ux%u (size %" PRIu64 "/%d)\n", __func__, ssd->qxl.id, - surface_width(ssd->ds), surface_height(ssd->ds), - surface_size, ssd->bufsize); - surface.format = SPICE_SURFACE_FMT_32_xRGB; surface.width = surface_width(ssd->ds); surface.height = surface_height(ssd->ds); @@ -366,8 +348,6 @@ void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd) void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd) { - dprint(1, "%s/%d:\n", __func__, ssd->qxl.id); - qemu_spice_destroy_primary_surface(ssd, 0, QXL_SYNC); } @@ -389,8 +369,7 @@ void qemu_spice_display_update(SimpleSpiceDisplay *ssd, { QXLRect update_area; - dprint(2, "%s/%d: x %d y %d w %d h %d\n", __func__, - ssd->qxl.id, x, y, w, h); + trace_qemu_spice_display_update(ssd->qxl.id, x, y, w, h); update_area.left = x, update_area.right = x + w; update_area.top = y; @@ -413,8 +392,10 @@ void qemu_spice_display_switch(SimpleSpiceDisplay *ssd, surface_height(surface) == pixman_image_get_height(ssd->surface) && surface_format(surface) == pixman_image_get_format(ssd->surface)) { /* no-resize fast path: just swap backing store */ - dprint(1, "%s/%d: fast (%dx%d)\n", __func__, ssd->qxl.id, - surface_width(surface), surface_height(surface)); + trace_qemu_spice_display_surface(ssd->qxl.id, + surface_width(surface), + surface_height(surface), + true); qemu_mutex_lock(&ssd->lock); ssd->ds = surface; pixman_image_unref(ssd->surface); @@ -427,11 +408,10 @@ void qemu_spice_display_switch(SimpleSpiceDisplay *ssd, } /* full mode switch */ - dprint(1, "%s/%d: full (%dx%d -> %dx%d)\n", __func__, ssd->qxl.id, - ssd->surface ? pixman_image_get_width(ssd->surface) : 0, - ssd->surface ? pixman_image_get_height(ssd->surface) : 0, - surface ? surface_width(surface) : 0, - surface ? surface_height(surface) : 0); + trace_qemu_spice_display_surface(ssd->qxl.id, + surface ? surface_width(surface) : 0, + surface ? surface_height(surface) : 0, + false); memset(&ssd->dirty, 0, sizeof(ssd->dirty)); if (ssd->surface) { @@ -495,7 +475,6 @@ void qemu_spice_cursor_refresh_bh(void *opaque) void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd) { - dprint(3, "%s/%d:\n", __func__, ssd->qxl.id); graphic_hw_update(ssd->dcl.con); qemu_mutex_lock(&ssd->lock); @@ -505,10 +484,10 @@ void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd) } qemu_mutex_unlock(&ssd->lock); + trace_qemu_spice_display_refresh(ssd->qxl.id, ssd->notify); if (ssd->notify) { ssd->notify = 0; qemu_spice_wakeup(ssd); - dprint(2, "%s/%d: notify\n", __func__, ssd->qxl.id); } } @@ -516,21 +495,17 @@ void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd) static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker) { - SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); - - dprint(1, "%s/%d:\n", __func__, ssd->qxl.id); + /* nothing to do */ } static void interface_set_compression_level(QXLInstance *sin, int level) { - dprint(1, "%s/%d:\n", __func__, sin->id); /* nothing to do */ } #if SPICE_NEEDS_SET_MM_TIME static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time) { - dprint(3, "%s/%d:\n", __func__, sin->id); /* nothing to do */ } #endif @@ -554,8 +529,6 @@ static int interface_get_command(QXLInstance *sin, QXLCommandExt *ext) SimpleSpiceUpdate *update; int ret = false; - dprint(3, "%s/%d:\n", __func__, ssd->qxl.id); - qemu_mutex_lock(&ssd->lock); update = QTAILQ_FIRST(&ssd->updates); if (update != NULL) { @@ -570,7 +543,6 @@ static int interface_get_command(QXLInstance *sin, QXLCommandExt *ext) static int interface_req_cmd_notification(QXLInstance *sin) { - dprint(2, "%s/%d:\n", __func__, sin->id); return 1; } @@ -582,7 +554,6 @@ static void interface_release_resource(QXLInstance *sin, SimpleSpiceCursor *cursor; QXLCommandExt *ext; - dprint(2, "%s/%d:\n", __func__, ssd->qxl.id); ext = (void *)(intptr_t)(rext.info->id); switch (ext->cmd.type) { case QXL_CMD_DRAW: @@ -603,8 +574,6 @@ static int interface_get_cursor_command(QXLInstance *sin, QXLCommandExt *ext) SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); int ret; - dprint(3, "%s/%d:\n", __func__, ssd->qxl.id); - qemu_mutex_lock(&ssd->lock); if (ssd->ptr_define) { *ext = ssd->ptr_define->ext; @@ -623,7 +592,6 @@ static int interface_get_cursor_command(QXLInstance *sin, QXLCommandExt *ext) static int interface_req_cursor_notification(QXLInstance *sin) { - dprint(2, "%s:\n", __func__); return 1; } @@ -680,7 +648,7 @@ static void interface_set_client_capabilities(QXLInstance *sin, uint8_t client_present, uint8_t caps[58]) { - dprint(3, "%s:\n", __func__); + /* nothing to do */ } static int interface_client_monitors_config(QXLInstance *sin, @@ -705,9 +673,9 @@ static int interface_client_monitors_config(QXLInstance *sin, info.width = mc->monitors[head].width; info.height = mc->monitors[head].height; } + + trace_qemu_spice_ui_info(ssd->qxl.id, info.width, info.height); dpy_set_ui_info(ssd->dcl.con, &info); - dprint(1, "%s/%d: size %dx%d\n", __func__, ssd->qxl.id, - info.width, info.height); return 1; } @@ -902,9 +870,10 @@ static void spice_gl_switch(DisplayChangeListener *dcl, return; } - dprint(1, "%s: %dx%d (stride %d/%d, fourcc 0x%x)\n", __func__, - surface_width(ssd->ds), surface_height(ssd->ds), - surface_stride(ssd->ds), stride, fourcc); + trace_qemu_spice_gl_surface(ssd->qxl.id, + surface_width(ssd->ds), + surface_height(ssd->ds), + fourcc); /* note: spice server will close the fd */ spice_qxl_gl_scanout(&ssd->qxl, fd, @@ -932,7 +901,7 @@ static void qemu_spice_gl_scanout_disable(DisplayChangeListener *dcl) { SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); - dprint(1, "%s: no framebuffer\n", __func__); + trace_qemu_spice_gl_scanout_disable(ssd->qxl.id); spice_qxl_gl_scanout(&ssd->qxl, -1, 0, 0, 0, 0, false); qemu_spice_gl_monitor_config(ssd, 0, 0, 0, 0); ssd->have_surface = false; @@ -957,8 +926,7 @@ static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl, fprintf(stderr, "%s: failed to get fd for texture\n", __func__); return; } - dprint(1, "%s: %dx%d (stride %d, fourcc 0x%x)\n", __func__, - w, h, stride, fourcc); + trace_qemu_spice_gl_scanout_texture(ssd->qxl.id, w, h, fourcc); /* note: spice server will close the fd */ spice_qxl_gl_scanout(&ssd->qxl, fd, backing_width, backing_height, @@ -968,17 +936,133 @@ static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl, ssd->have_scanout = true; } +static void qemu_spice_gl_scanout_dmabuf(DisplayChangeListener *dcl, + QemuDmaBuf *dmabuf) +{ + SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); + + ssd->guest_dmabuf = dmabuf; + ssd->guest_dmabuf_refresh = true; + + ssd->have_surface = false; + ssd->have_scanout = true; +} + +static void qemu_spice_gl_cursor_dmabuf(DisplayChangeListener *dcl, + QemuDmaBuf *dmabuf, bool have_hot, + uint32_t hot_x, uint32_t hot_y) +{ + SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); + + ssd->have_hot = have_hot; + ssd->hot_x = hot_x; + ssd->hot_y = hot_y; + + trace_qemu_spice_gl_cursor(ssd->qxl.id, dmabuf != NULL, have_hot); + if (dmabuf) { + egl_dmabuf_import_texture(dmabuf); + if (!dmabuf->texture) { + return; + } + egl_fb_setup_for_tex(&ssd->cursor_fb, dmabuf->width, dmabuf->height, + dmabuf->texture, false); + } else { + egl_fb_destroy(&ssd->cursor_fb); + } +} + +static void qemu_spice_gl_cursor_position(DisplayChangeListener *dcl, + uint32_t pos_x, uint32_t pos_y) +{ + SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); + + ssd->ptr_x = pos_x; + ssd->ptr_y = pos_y; +} + +static void qemu_spice_gl_release_dmabuf(DisplayChangeListener *dcl, + QemuDmaBuf *dmabuf) +{ + SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); + + if (ssd->guest_dmabuf == dmabuf) { + ssd->guest_dmabuf = NULL; + ssd->guest_dmabuf_refresh = false; + } + egl_dmabuf_release_texture(dmabuf); +} + static void qemu_spice_gl_update(DisplayChangeListener *dcl, uint32_t x, uint32_t y, uint32_t w, uint32_t h) { SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); + EGLint stride = 0, fourcc = 0; + bool render_cursor = false; + bool y_0_top = false; /* FIXME */ uint64_t cookie; + int fd; if (!ssd->have_scanout) { return; } - dprint(2, "%s: %dx%d+%d+%d\n", __func__, w, h, x, y); + if (ssd->cursor_fb.texture) { + render_cursor = true; + } + if (ssd->render_cursor != render_cursor) { + ssd->render_cursor = render_cursor; + ssd->guest_dmabuf_refresh = true; + egl_fb_destroy(&ssd->blit_fb); + } + + if (ssd->guest_dmabuf_refresh) { + QemuDmaBuf *dmabuf = ssd->guest_dmabuf; + if (render_cursor) { + egl_dmabuf_import_texture(dmabuf); + if (!dmabuf->texture) { + return; + } + + /* source framebuffer */ + egl_fb_setup_for_tex(&ssd->guest_fb, + dmabuf->width, dmabuf->height, + dmabuf->texture, false); + + /* dest framebuffer */ + if (ssd->blit_fb.width != dmabuf->width || + ssd->blit_fb.height != dmabuf->height) { + trace_qemu_spice_gl_render_dmabuf(ssd->qxl.id, dmabuf->width, + dmabuf->height); + egl_fb_destroy(&ssd->blit_fb); + egl_fb_setup_new_tex(&ssd->blit_fb, + dmabuf->width, dmabuf->height); + fd = egl_get_fd_for_texture(ssd->blit_fb.texture, + &stride, &fourcc); + spice_qxl_gl_scanout(&ssd->qxl, fd, + dmabuf->width, dmabuf->height, + stride, fourcc, false); + } + } else { + trace_qemu_spice_gl_forward_dmabuf(ssd->qxl.id, + dmabuf->width, dmabuf->height); + /* note: spice server will close the fd, so hand over a dup */ + spice_qxl_gl_scanout(&ssd->qxl, dup(dmabuf->fd), + dmabuf->width, dmabuf->height, + dmabuf->stride, dmabuf->fourcc, false); + } + qemu_spice_gl_monitor_config(ssd, 0, 0, dmabuf->width, dmabuf->height); + ssd->guest_dmabuf_refresh = false; + } + + if (render_cursor) { + egl_texture_blit(ssd->gls, &ssd->blit_fb, &ssd->guest_fb, + !y_0_top); + egl_texture_blend(ssd->gls, &ssd->blit_fb, &ssd->cursor_fb, + !y_0_top, ssd->ptr_x, ssd->ptr_y); + glFlush(); + } + + trace_qemu_spice_gl_update(ssd->qxl.id, w, h, x, y); qemu_spice_gl_block(ssd, true); cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0); spice_qxl_gl_draw_async(&ssd->qxl, x, y, w, h, cookie); @@ -1000,6 +1084,10 @@ static const DisplayChangeListenerOps display_listener_gl_ops = { .dpy_gl_scanout_disable = qemu_spice_gl_scanout_disable, .dpy_gl_scanout_texture = qemu_spice_gl_scanout_texture, + .dpy_gl_scanout_dmabuf = qemu_spice_gl_scanout_dmabuf, + .dpy_gl_cursor_dmabuf = qemu_spice_gl_cursor_dmabuf, + .dpy_gl_cursor_position = qemu_spice_gl_cursor_position, + .dpy_gl_release_dmabuf = qemu_spice_gl_release_dmabuf, .dpy_gl_update = qemu_spice_gl_update, }; diff --git a/ui/trace-events b/ui/trace-events index 861b68a305..a957f363f1 100644 --- a/ui/trace-events +++ b/ui/trace-events @@ -75,6 +75,18 @@ qemu_spice_create_primary_surface(int qid, uint32_t sid, void *surface, int asyn qemu_spice_destroy_primary_surface(int qid, uint32_t sid, int async) "%d sid=%u async=%d" qemu_spice_wakeup(uint32_t qid) "%d" qemu_spice_create_update(uint32_t left, uint32_t right, uint32_t top, uint32_t bottom) "lr %d -> %d, tb -> %d -> %d" +qemu_spice_display_update(int qid, uint32_t x, uint32_t y, uint32_t w, uint32_t h) "%d +%d+%d %dx%d" +qemu_spice_display_surface(int qid, uint32_t w, uint32_t h, int fast) "%d %dx%d, fast %d" +qemu_spice_display_refresh(int qid, int notify) "%d notify %d" +qemu_spice_ui_info(int qid, uint32_t width, uint32_t height) "%d %dx%d" + +qemu_spice_gl_surface(int qid, uint32_t w, uint32_t h, uint32_t fourcc) "%d %dx%d, fourcc 0x%x" +qemu_spice_gl_scanout_disable(int qid) "%d" +qemu_spice_gl_scanout_texture(int qid, uint32_t w, uint32_t h, uint32_t fourcc) "%d %dx%d, fourcc 0x%x" +qemu_spice_gl_cursor(int qid, bool enabled, bool hotspot) "%d enabled %d, hotspot %d" +qemu_spice_gl_forward_dmabuf(int qid, uint32_t width, uint32_t height) "%d %dx%d" +qemu_spice_gl_render_dmabuf(int qid, uint32_t width, uint32_t height) "%d %dx%d" +qemu_spice_gl_update(int qid, uint32_t x, uint32_t y, uint32_t w, uint32_t h) "%d +%d+%d %dx%d" # ui/keymaps.c keymap_parse(const char *file) "file %s" @@ -746,9 +746,19 @@ static void vnc_update_server_surface(VncDisplay *vd) static void vnc_dpy_switch(DisplayChangeListener *dcl, DisplaySurface *surface) { + static const char placeholder_msg[] = + "Display output is not active."; + static DisplaySurface *placeholder; VncDisplay *vd = container_of(dcl, VncDisplay, dcl); VncState *vs; + if (surface == NULL) { + if (placeholder == NULL) { + placeholder = qemu_create_message_surface(640, 480, placeholder_msg); + } + surface = placeholder; + } + vnc_abort_display_jobs(vd); vd->ds = surface; diff --git a/util/aio-wait.c b/util/aio-wait.c index a487cdb852..b8a8f86dba 100644 --- a/util/aio-wait.c +++ b/util/aio-wait.c @@ -34,7 +34,38 @@ static void dummy_bh_cb(void *opaque) void aio_wait_kick(AioWait *wait) { /* The barrier (or an atomic op) is in the caller. */ - if (atomic_read(&wait->need_kick)) { + if (atomic_read(&wait->num_waiters)) { aio_bh_schedule_oneshot(qemu_get_aio_context(), dummy_bh_cb, NULL); } } + +typedef struct { + AioWait wait; + bool done; + QEMUBHFunc *cb; + void *opaque; +} AioWaitBHData; + +/* Context: BH in IOThread */ +static void aio_wait_bh(void *opaque) +{ + AioWaitBHData *data = opaque; + + data->cb(data->opaque); + + data->done = true; + aio_wait_kick(&data->wait); +} + +void aio_wait_bh_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque) +{ + AioWaitBHData data = { + .cb = cb, + .opaque = opaque, + }; + + assert(qemu_get_current_aio_context() == qemu_get_aio_context()); + + aio_bh_schedule_oneshot(ctx, aio_wait_bh, &data); + AIO_WAIT_WHILE(&data.wait, ctx, !data.done); +} diff --git a/util/coroutine-ucontext.c b/util/coroutine-ucontext.c index 926d3402e3..090ba21a13 100644 --- a/util/coroutine-ucontext.c +++ b/util/coroutine-ucontext.c @@ -170,7 +170,7 @@ Coroutine *qemu_coroutine_new(void) } #ifdef CONFIG_VALGRIND_H -#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE +#if defined(CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE) && !defined(__clang__) /* Work around an unused variable in the valgrind.h macro... */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-but-set-variable" @@ -179,7 +179,7 @@ static inline void valgrind_stack_deregister(CoroutineUContext *co) { VALGRIND_STACK_DEREGISTER(co->valgrind_stack_id); } -#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE +#if defined(CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE) && !defined(__clang__) #pragma GCC diagnostic pop #endif #endif diff --git a/util/qemu-option.c b/util/qemu-option.c index a401e936da..2b412eff5e 100644 --- a/util/qemu-option.c +++ b/util/qemu-option.c @@ -1007,14 +1007,23 @@ void qemu_opts_absorb_qdict(QemuOpts *opts, QDict *qdict, Error **errp) } /* - * Convert from QemuOpts to QDict. - * The QDict values are of type QString. + * Convert from QemuOpts to QDict. The QDict values are of type QString. + * + * If @list is given, only add those options to the QDict that are contained in + * the list. If @del is true, any options added to the QDict are removed from + * the QemuOpts, otherwise they remain there. + * + * If two options in @opts have the same name, they are processed in order + * so that the last one wins (consistent with the reverse iteration in + * qemu_opt_find()), but all of them are deleted if @del is true. + * * TODO We'll want to use types appropriate for opt->desc->type, but * this is enough for now. */ -QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict) +QDict *qemu_opts_to_qdict_filtered(QemuOpts *opts, QDict *qdict, + QemuOptsList *list, bool del) { - QemuOpt *opt; + QemuOpt *opt, *next; if (!qdict) { qdict = qdict_new(); @@ -1022,12 +1031,35 @@ QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict) if (opts->id) { qdict_put_str(qdict, "id", opts->id); } - QTAILQ_FOREACH(opt, &opts->head, next) { + QTAILQ_FOREACH_SAFE(opt, &opts->head, next, next) { + if (list) { + QemuOptDesc *desc; + bool found = false; + for (desc = list->desc; desc->name; desc++) { + if (!strcmp(desc->name, opt->name)) { + found = true; + break; + } + } + if (!found) { + continue; + } + } qdict_put_str(qdict, opt->name, opt->str); + if (del) { + qemu_opt_del(opt); + } } return qdict; } +/* Copy all options in a QemuOpts to the given QDict. See + * qemu_opts_to_qdict_filtered() for details. */ +QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict) +{ + return qemu_opts_to_qdict_filtered(opts, qdict, NULL, false); +} + /* Validate parsed opts against descriptions where no * descriptions were provided in the QemuOptsList. */ @@ -4722,17 +4722,10 @@ int main(int argc, char **argv, char **envp) os_setup_post(); main_loop(); - replay_disable_events(); - - /* The ordering of the following is delicate. Stop vcpus to prevent new - * I/O requests being queued by the guest. Then stop IOThreads (this - * includes a drain operation and completes all request processing). At - * this point emulated devices are still associated with their IOThreads - * (if any) but no longer have any work to do. Only then can we close - * block devices safely because we know there is no more I/O coming. - */ - pause_all_vcpus(); - iothread_stop_all(); + + /* No more vcpu or device emulation activity beyond this point */ + vm_shutdown(); + bdrv_close_all(); res_free(); |