diff options
52 files changed, 1877 insertions, 959 deletions
diff --git a/audio/audio.c b/audio/audio.c index d6e91901aa..1ace47f510 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -29,6 +29,7 @@ #include "sysemu/sysemu.h" #include "qemu/cutils.h" #include "sysemu/replay.h" +#include "trace.h" #define AUDIO_CAP "audio" #include "audio_int.h" @@ -1129,6 +1130,10 @@ static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info) /* * Timer */ + +static bool audio_timer_running; +static uint64_t audio_timer_last; + static int audio_is_timer_needed (void) { HWVoiceIn *hwi = NULL; @@ -1148,14 +1153,31 @@ static void audio_reset_timer (AudioState *s) if (audio_is_timer_needed ()) { timer_mod_anticipate_ns(s->ts, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + conf.period.ticks); - } - else { - timer_del (s->ts); + if (!audio_timer_running) { + audio_timer_running = true; + audio_timer_last = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + trace_audio_timer_start(conf.period.ticks / SCALE_MS); + } + } else { + timer_del(s->ts); + if (audio_timer_running) { + audio_timer_running = false; + trace_audio_timer_stop(); + } } } static void audio_timer (void *opaque) { + int64_t now, diff; + + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + diff = now - audio_timer_last; + if (diff > conf.period.ticks * 3 / 2) { + trace_audio_timer_delayed(diff / SCALE_MS); + } + audio_timer_last = now; + audio_run ("timer"); audio_reset_timer (opaque); } diff --git a/audio/trace-events b/audio/trace-events index d37639e611..c986469319 100644 --- a/audio/trace-events +++ b/audio/trace-events @@ -15,3 +15,8 @@ alsa_no_frames(int state) "No frames available and ALSA state is %d" # audio/ossaudio.c oss_version(int version) "OSS version = 0x%x" oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d" + +# audio/audio.c +audio_timer_start(int interval) "interval %d ms" +audio_timer_stop(void) "" +audio_timer_delayed(int interval) "interval %d ms" @@ -3883,7 +3883,7 @@ libs_softmmu="$libs_softmmu $fdt_libs" # opengl probe (for sdl2, gtk, milkymist-tmu2) if test "$opengl" != "no" ; then - opengl_pkgs="epoxy libdrm gbm" + opengl_pkgs="epoxy gbm" if $pkg_config $opengl_pkgs; then opengl_cflags="$($pkg_config --cflags $opengl_pkgs)" opengl_libs="$($pkg_config --libs $opengl_pkgs)" diff --git a/crypto/Makefile.objs b/crypto/Makefile.objs index 2b99e08062..756bab111b 100644 --- a/crypto/Makefile.objs +++ b/crypto/Makefile.objs @@ -15,6 +15,7 @@ crypto-obj-$(CONFIG_AF_ALG) += cipher-afalg.o crypto-obj-$(CONFIG_AF_ALG) += hash-afalg.o crypto-obj-y += tlscreds.o crypto-obj-y += tlscredsanon.o +crypto-obj-y += tlscredspsk.o crypto-obj-y += tlscredsx509.o crypto-obj-y += tlssession.o crypto-obj-y += secret.o diff --git a/crypto/tlscredspsk.c b/crypto/tlscredspsk.c new file mode 100644 index 0000000000..7be7c8efdd --- /dev/null +++ b/crypto/tlscredspsk.c @@ -0,0 +1,308 @@ +/* + * QEMU crypto TLS Pre-Shared Keys (PSK) support + * + * Copyright (c) 2018 Red Hat, Inc. + * + * 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 "crypto/tlscredspsk.h" +#include "tlscredspriv.h" +#include "qapi/error.h" +#include "qom/object_interfaces.h" +#include "trace.h" + + +#ifdef CONFIG_GNUTLS + +static int +lookup_key(const char *pskfile, const char *username, gnutls_datum_t *key, + Error **errp) +{ + const size_t ulen = strlen(username); + GError *gerr = NULL; + char *content = NULL; + char **lines = NULL; + size_t clen = 0, i; + int ret = -1; + + if (!g_file_get_contents(pskfile, &content, &clen, &gerr)) { + error_setg(errp, "Cannot read PSK file %s: %s", + pskfile, gerr->message); + g_error_free(gerr); + return -1; + } + + lines = g_strsplit(content, "\n", -1); + for (i = 0; lines[i] != NULL; ++i) { + if (strncmp(lines[i], username, ulen) == 0 && lines[i][ulen] == ':') { + key->data = (unsigned char *) g_strdup(&lines[i][ulen + 1]); + key->size = strlen(lines[i]) - ulen - 1; + ret = 0; + goto out; + } + } + error_setg(errp, "Username %s not found in PSK file %s", + username, pskfile); + + out: + free(content); + g_strfreev(lines); + return ret; +} + +static int +qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds, + Error **errp) +{ + char *pskfile = NULL, *dhparams = NULL; + const char *username; + int ret; + int rv = -1; + gnutls_datum_t key = { .data = NULL }; + + trace_qcrypto_tls_creds_psk_load(creds, + creds->parent_obj.dir ? creds->parent_obj.dir : "<nodir>"); + + if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { + if (creds->username) { + error_setg(errp, "username should not be set when endpoint=server"); + goto cleanup; + } + + if (qcrypto_tls_creds_get_path(&creds->parent_obj, + QCRYPTO_TLS_CREDS_DH_PARAMS, + false, &dhparams, errp) < 0 || + qcrypto_tls_creds_get_path(&creds->parent_obj, + QCRYPTO_TLS_CREDS_PSKFILE, + true, &pskfile, errp) < 0) { + goto cleanup; + } + + ret = gnutls_psk_allocate_server_credentials(&creds->data.server); + if (ret < 0) { + error_setg(errp, "Cannot allocate credentials: %s", + gnutls_strerror(ret)); + goto cleanup; + } + + if (qcrypto_tls_creds_get_dh_params_file(&creds->parent_obj, dhparams, + &creds->parent_obj.dh_params, + errp) < 0) { + goto cleanup; + } + + gnutls_psk_set_server_credentials_file(creds->data.server, pskfile); + gnutls_psk_set_server_dh_params(creds->data.server, + creds->parent_obj.dh_params); + } else { + if (qcrypto_tls_creds_get_path(&creds->parent_obj, + QCRYPTO_TLS_CREDS_PSKFILE, + true, &pskfile, errp) < 0) { + goto cleanup; + } + + if (creds->username) { + username = creds->username; + } else { + username = "qemu"; + } + if (lookup_key(pskfile, username, &key, errp) != 0) { + goto cleanup; + } + + ret = gnutls_psk_allocate_client_credentials(&creds->data.client); + if (ret < 0) { + error_setg(errp, "Cannot allocate credentials: %s", + gnutls_strerror(ret)); + goto cleanup; + } + + gnutls_psk_set_client_credentials(creds->data.client, + username, &key, GNUTLS_PSK_KEY_HEX); + } + + rv = 0; + cleanup: + g_free(key.data); + g_free(pskfile); + g_free(dhparams); + return rv; +} + + +static void +qcrypto_tls_creds_psk_unload(QCryptoTLSCredsPSK *creds) +{ + if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) { + if (creds->data.client) { + gnutls_psk_free_client_credentials(creds->data.client); + creds->data.client = NULL; + } + } else { + if (creds->data.server) { + gnutls_psk_free_server_credentials(creds->data.server); + creds->data.server = NULL; + } + } + if (creds->parent_obj.dh_params) { + gnutls_dh_params_deinit(creds->parent_obj.dh_params); + creds->parent_obj.dh_params = NULL; + } +} + +#else /* ! CONFIG_GNUTLS */ + + +static void +qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds G_GNUC_UNUSED, + Error **errp) +{ + error_setg(errp, "TLS credentials support requires GNUTLS"); +} + + +static void +qcrypto_tls_creds_psk_unload(QCryptoTLSCredsPSK *creds G_GNUC_UNUSED) +{ + /* nada */ +} + + +#endif /* ! CONFIG_GNUTLS */ + + +static void +qcrypto_tls_creds_psk_prop_set_loaded(Object *obj, + bool value, + Error **errp) +{ + QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj); + + if (value) { + qcrypto_tls_creds_psk_load(creds, errp); + } else { + qcrypto_tls_creds_psk_unload(creds); + } +} + + +#ifdef CONFIG_GNUTLS + + +static bool +qcrypto_tls_creds_psk_prop_get_loaded(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj); + + if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { + return creds->data.server != NULL; + } else { + return creds->data.client != NULL; + } +} + + +#else /* ! CONFIG_GNUTLS */ + + +static bool +qcrypto_tls_creds_psk_prop_get_loaded(Object *obj G_GNUC_UNUSED, + Error **errp G_GNUC_UNUSED) +{ + return false; +} + + +#endif /* ! CONFIG_GNUTLS */ + + +static void +qcrypto_tls_creds_psk_complete(UserCreatable *uc, Error **errp) +{ + object_property_set_bool(OBJECT(uc), true, "loaded", errp); +} + + +static void +qcrypto_tls_creds_psk_finalize(Object *obj) +{ + QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj); + + qcrypto_tls_creds_psk_unload(creds); +} + +static void +qcrypto_tls_creds_psk_prop_set_username(Object *obj, + const char *value, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj); + + creds->username = g_strdup(value); +} + + +static char * +qcrypto_tls_creds_psk_prop_get_username(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj); + + return g_strdup(creds->username); +} + +static void +qcrypto_tls_creds_psk_class_init(ObjectClass *oc, void *data) +{ + UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); + + ucc->complete = qcrypto_tls_creds_psk_complete; + + object_class_property_add_bool(oc, "loaded", + qcrypto_tls_creds_psk_prop_get_loaded, + qcrypto_tls_creds_psk_prop_set_loaded, + NULL); + object_class_property_add_str(oc, "username", + qcrypto_tls_creds_psk_prop_get_username, + qcrypto_tls_creds_psk_prop_set_username, + NULL); +} + + +static const TypeInfo qcrypto_tls_creds_psk_info = { + .parent = TYPE_QCRYPTO_TLS_CREDS, + .name = TYPE_QCRYPTO_TLS_CREDS_PSK, + .instance_size = sizeof(QCryptoTLSCredsPSK), + .instance_finalize = qcrypto_tls_creds_psk_finalize, + .class_size = sizeof(QCryptoTLSCredsPSKClass), + .class_init = qcrypto_tls_creds_psk_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + + +static void +qcrypto_tls_creds_psk_register_types(void) +{ + type_register_static(&qcrypto_tls_creds_psk_info); +} + + +type_init(qcrypto_tls_creds_psk_register_types); diff --git a/crypto/tlssession.c b/crypto/tlssession.c index 96a02deb69..66a6fbe19c 100644 --- a/crypto/tlssession.c +++ b/crypto/tlssession.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "crypto/tlssession.h" #include "crypto/tlscredsanon.h" +#include "crypto/tlscredspsk.h" #include "crypto/tlscredsx509.h" #include "qapi/error.h" #include "qemu/acl.h" @@ -88,6 +89,14 @@ qcrypto_tls_session_pull(void *opaque, void *buf, size_t len) return session->readFunc(buf, len, session->opaque); } +#define TLS_PRIORITY_ADDITIONAL_ANON "+ANON-DH" + +#if GNUTLS_VERSION_MAJOR >= 3 +#define TLS_ECDHE_PSK "+ECDHE-PSK:" +#else +#define TLS_ECDHE_PSK "" +#endif +#define TLS_PRIORITY_ADDITIONAL_PSK TLS_ECDHE_PSK "+DHE-PSK:+PSK" QCryptoTLSSession * qcrypto_tls_session_new(QCryptoTLSCreds *creds, @@ -135,9 +144,12 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds, char *prio; if (creds->priority != NULL) { - prio = g_strdup_printf("%s:+ANON-DH", creds->priority); + prio = g_strdup_printf("%s:%s", + creds->priority, + TLS_PRIORITY_ADDITIONAL_ANON); } else { - prio = g_strdup(CONFIG_TLS_PRIORITY ":+ANON-DH"); + prio = g_strdup(CONFIG_TLS_PRIORITY ":" + TLS_PRIORITY_ADDITIONAL_ANON); } ret = gnutls_priority_set_direct(session->handle, prio, NULL); @@ -163,6 +175,42 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds, goto error; } } else if (object_dynamic_cast(OBJECT(creds), + TYPE_QCRYPTO_TLS_CREDS_PSK)) { + QCryptoTLSCredsPSK *pcreds = QCRYPTO_TLS_CREDS_PSK(creds); + char *prio; + + if (creds->priority != NULL) { + prio = g_strdup_printf("%s:%s", + creds->priority, + TLS_PRIORITY_ADDITIONAL_PSK); + } else { + prio = g_strdup(CONFIG_TLS_PRIORITY ":" + TLS_PRIORITY_ADDITIONAL_PSK); + } + + ret = gnutls_priority_set_direct(session->handle, prio, NULL); + if (ret < 0) { + error_setg(errp, "Unable to set TLS session priority %s: %s", + prio, gnutls_strerror(ret)); + g_free(prio); + goto error; + } + g_free(prio); + if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { + ret = gnutls_credentials_set(session->handle, + GNUTLS_CRD_PSK, + pcreds->data.server); + } else { + ret = gnutls_credentials_set(session->handle, + GNUTLS_CRD_PSK, + pcreds->data.client); + } + if (ret < 0) { + error_setg(errp, "Cannot set session credentials: %s", + gnutls_strerror(ret)); + goto error; + } + } else if (object_dynamic_cast(OBJECT(creds), TYPE_QCRYPTO_TLS_CREDS_X509)) { QCryptoTLSCredsX509 *tcreds = QCRYPTO_TLS_CREDS_X509(creds); const char *prio = creds->priority; @@ -354,6 +402,10 @@ qcrypto_tls_session_check_credentials(QCryptoTLSSession *session, trace_qcrypto_tls_session_check_creds(session, "nop"); return 0; } else if (object_dynamic_cast(OBJECT(session->creds), + TYPE_QCRYPTO_TLS_CREDS_PSK)) { + trace_qcrypto_tls_session_check_creds(session, "nop"); + return 0; + } else if (object_dynamic_cast(OBJECT(session->creds), TYPE_QCRYPTO_TLS_CREDS_X509)) { if (session->creds->verifyPeer) { int ret = qcrypto_tls_session_check_certificate(session, diff --git a/crypto/trace-events b/crypto/trace-events index e589990359..597389b73c 100644 --- a/crypto/trace-events +++ b/crypto/trace-events @@ -7,6 +7,9 @@ qcrypto_tls_creds_get_path(void *creds, const char *filename, const char *path) # crypto/tlscredsanon.c qcrypto_tls_creds_anon_load(void *creds, const char *dir) "TLS creds anon load creds=%p dir=%s" +# crypto/tlscredspsk.c +qcrypto_tls_creds_psk_load(void *creds, const char *dir) "TLS creds psk load creds=%p dir=%s" + # crypto/tlscredsx509.c qcrypto_tls_creds_x509_load(void *creds, const char *dir) "TLS creds x509 load creds=%p dir=%s" qcrypto_tls_creds_x509_check_basic_constraints(void *creds, const char *file, int status) "TLS creds x509 check basic constraints creds=%p file=%s status=%d" diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index 31c66d4255..2b58c3505b 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -189,7 +189,7 @@ struct HDAAudioState { static inline int64_t hda_bytes_per_second(HDAAudioStream *st) { - return 2 * st->as.nchannels * st->as.freq; + return 2LL * st->as.nchannels * st->as.freq; } static inline void hda_timer_sync_adjust(HDAAudioStream *st, int64_t target_pos) @@ -203,6 +203,9 @@ static inline void hda_timer_sync_adjust(HDAAudioStream *st, int64_t target_pos) if (target_pos < -limit) { corr = -HDA_TIMER_TICKS; } + if (target_pos < -(2 * limit)) { + corr = -(4 * HDA_TIMER_TICKS); + } if (corr == 0) { return; } diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c index 5e44f00f3f..7583b18c29 100644 --- a/hw/display/cirrus_vga.c +++ b/hw/display/cirrus_vga.c @@ -3048,7 +3048,8 @@ static void isa_cirrus_vga_realizefn(DeviceState *dev, Error **errp) s->vram_size_mb); return; } - vga_common_init(s, OBJECT(dev), true); + s->global_vmstate = true; + vga_common_init(s, OBJECT(dev)); cirrus_init_common(&d->cirrus_vga, OBJECT(dev), CIRRUS_ID_CLGD5430, 0, isa_address_space(isadev), isa_address_space_io(isadev)); @@ -3062,7 +3063,7 @@ static Property isa_cirrus_vga_properties[] = { DEFINE_PROP_UINT32("vgamem_mb", struct ISACirrusVGAState, cirrus_vga.vga.vram_size_mb, 4), DEFINE_PROP_BOOL("blitter", struct ISACirrusVGAState, - cirrus_vga.enable_blitter, true), + cirrus_vga.enable_blitter, true), DEFINE_PROP_END_OF_LIST(), }; @@ -3105,7 +3106,7 @@ static void pci_cirrus_vga_realize(PCIDevice *dev, Error **errp) return; } /* setup VGA */ - vga_common_init(&s->vga, OBJECT(dev), true); + vga_common_init(&s->vga, OBJECT(dev)); cirrus_init_common(s, OBJECT(dev), device_id, 1, pci_address_space(dev), pci_address_space_io(dev)); s->vga.con = graphic_console_init(DEVICE(dev), 0, s->vga.hw_ops, &s->vga); @@ -3134,6 +3135,8 @@ static Property pci_vga_cirrus_properties[] = { cirrus_vga.vga.vram_size_mb, 4), DEFINE_PROP_BOOL("blitter", struct PCICirrusVGAState, cirrus_vga.enable_blitter, true), + DEFINE_PROP_BOOL("global-vmstate", struct PCICirrusVGAState, + cirrus_vga.vga.global_vmstate, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/display/qxl.c b/hw/display/qxl.c index b09a03997a..830c392c53 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -2168,7 +2168,7 @@ static void qxl_realize_primary(PCIDevice *dev, Error **errp) qxl_init_ramsize(qxl); vga->vbe_size = qxl->vgamem_size; vga->vram_size_mb = qxl->vga.vram_size / MiB; - vga_common_init(vga, OBJECT(dev), true); + vga_common_init(vga, OBJECT(dev)); vga_init(vga, OBJECT(dev), pci_address_space(dev), pci_address_space_io(dev), false); portio_list_init(&qxl->vga_port_list, OBJECT(dev), qxl_vga_portio_list, @@ -2408,6 +2408,7 @@ static Property qxl_properties[] = { #endif DEFINE_PROP_UINT32("xres", PCIQXLDevice, xres, 0), DEFINE_PROP_UINT32("yres", PCIQXLDevice, yres, 0), + DEFINE_PROP_BOOL("global-vmstate", PCIQXLDevice, vga.global_vmstate, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/display/vga-isa-mm.c b/hw/display/vga-isa-mm.c index bd58141117..232216cad0 100644 --- a/hw/display/vga-isa-mm.c +++ b/hw/display/vga-isa-mm.c @@ -132,7 +132,8 @@ int isa_vga_mm_init(hwaddr vram_base, s = g_malloc0(sizeof(*s)); s->vga.vram_size_mb = VGA_RAM_SIZE / MiB; - vga_common_init(&s->vga, NULL, true); + s->vga.global_vmstate = true; + vga_common_init(&s->vga, NULL); vga_mm_init(s, vram_base, ctrl_base, it_shift, address_space); s->vga.con = graphic_console_init(NULL, 0, s->vga.hw_ops, s); diff --git a/hw/display/vga-isa.c b/hw/display/vga-isa.c index 469834add5..fa44242e0d 100644 --- a/hw/display/vga-isa.c +++ b/hw/display/vga-isa.c @@ -58,7 +58,8 @@ static void vga_isa_realizefn(DeviceState *dev, Error **errp) MemoryRegion *vga_io_memory; const MemoryRegionPortio *vga_ports, *vbe_ports; - vga_common_init(s, OBJECT(dev), true); + s->global_vmstate = true; + vga_common_init(s, OBJECT(dev)); s->legacy_address_space = isa_address_space(isadev); vga_io_memory = vga_init_io(s, OBJECT(dev), &vga_ports, &vbe_ports); isa_register_portio_list(isadev, &d->portio_vga, diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c index 1ea559762a..e9e62eac70 100644 --- a/hw/display/vga-pci.c +++ b/hw/display/vga-pci.c @@ -222,7 +222,7 @@ static void pci_std_vga_realize(PCIDevice *dev, Error **errp) bool qext = false; /* vga + console init */ - vga_common_init(s, OBJECT(dev), true); + vga_common_init(s, OBJECT(dev)); vga_init(s, OBJECT(dev), pci_address_space(dev), pci_address_space_io(dev), true); @@ -265,7 +265,7 @@ static void pci_secondary_vga_realize(PCIDevice *dev, Error **errp) bool qext = false; /* vga + console init */ - vga_common_init(s, OBJECT(dev), false); + vga_common_init(s, OBJECT(dev)); s->con = graphic_console_init(DEVICE(dev), 0, s->hw_ops, s); /* mmio bar */ @@ -308,6 +308,7 @@ static Property vga_pci_properties[] = { DEFINE_PROP_BIT("mmio", PCIVGAState, flags, PCI_VGA_FLAG_ENABLE_MMIO, true), DEFINE_PROP_BIT("qemu-extended-regs", PCIVGAState, flags, PCI_VGA_FLAG_ENABLE_QEXT, true), + DEFINE_PROP_BOOL("global-vmstate", PCIVGAState, vga.global_vmstate, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/display/vga.c b/hw/display/vga.c index d7599182a8..802cfd47db 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -2164,7 +2164,7 @@ static inline uint32_t uint_clamp(uint32_t val, uint32_t vmin, uint32_t vmax) return val; } -void vga_common_init(VGACommonState *s, Object *obj, bool global_vmstate) +void vga_common_init(VGACommonState *s, Object *obj) { int i, j, v, b; @@ -2203,7 +2203,7 @@ void vga_common_init(VGACommonState *s, Object *obj, bool global_vmstate) s->is_vbe_vmstate = 1; memory_region_init_ram_nomigrate(&s->vram, obj, "vga.vram", s->vram_size, &error_fatal); - vmstate_register_ram(&s->vram, global_vmstate ? NULL : DEVICE(obj)); + vmstate_register_ram(&s->vram, s->global_vmstate ? NULL : DEVICE(obj)); xen_register_framebuffer(&s->vram); s->vram_ptr = memory_region_get_ram_ptr(&s->vram); s->get_bpp = vga_get_bpp; diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h index f8fcf62a56..339661bc01 100644 --- a/hw/display/vga_int.h +++ b/hw/display/vga_int.h @@ -133,6 +133,7 @@ typedef struct VGACommonState { bool full_update_gfx; bool big_endian_fb; bool default_endian_fb; + bool global_vmstate; /* hardware mouse cursor support */ uint32_t invalidated_y_table[VGA_MAX_HEIGHT / 32]; uint32_t hw_cursor_x; @@ -157,7 +158,7 @@ static inline int c6_to_8(int v) return (v << 2) | (b << 1) | b; } -void vga_common_init(VGACommonState *s, Object *obj, bool global_vmstate); +void vga_common_init(VGACommonState *s, Object *obj); void vga_init(VGACommonState *s, Object *obj, MemoryRegion *address_space, MemoryRegion *address_space_io, bool init_vga_ports); MemoryRegion *vga_init_io(VGACommonState *s, Object *obj, diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 71a00718e6..ec366f4c35 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -400,9 +400,47 @@ static void virtio_gpu_resource_create_2d(VirtIOGPU *g, g->hostmem += res->hostmem; } +static void virtio_gpu_disable_scanout(VirtIOGPU *g, int scanout_id) +{ + struct virtio_gpu_scanout *scanout = &g->scanout[scanout_id]; + struct virtio_gpu_simple_resource *res; + DisplaySurface *ds = NULL; + + if (scanout->resource_id == 0) { + return; + } + + res = virtio_gpu_find_resource(g, scanout->resource_id); + if (res) { + res->scanout_bitmask &= ~(1 << scanout_id); + } + + if (scanout_id == 0) { + /* primary head */ + ds = qemu_create_message_surface(scanout->width ?: 640, + scanout->height ?: 480, + "Guest disabled display."); + } + dpy_gfx_replace_surface(scanout->con, ds); + scanout->resource_id = 0; + scanout->ds = NULL; + scanout->width = 0; + scanout->height = 0; +} + static void virtio_gpu_resource_destroy(VirtIOGPU *g, struct virtio_gpu_simple_resource *res) { + int i; + + if (res->scanout_bitmask) { + for (i = 0; i < g->conf.max_outputs; i++) { + if (res->scanout_bitmask & (1 << i)) { + virtio_gpu_disable_scanout(g, i); + } + } + } + pixman_image_unref(res->image); virtio_gpu_cleanup_mapping(res); QTAILQ_REMOVE(&g->reslist, res, next); @@ -563,7 +601,7 @@ static void virtio_unref_resource(pixman_image_t *image, void *data) static void virtio_gpu_set_scanout(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) { - struct virtio_gpu_simple_resource *res; + struct virtio_gpu_simple_resource *res, *ores; struct virtio_gpu_scanout *scanout; pixman_format_code_t format; uint32_t offset; @@ -584,24 +622,7 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g, g->enable = 1; if (ss.resource_id == 0) { - scanout = &g->scanout[ss.scanout_id]; - if (scanout->resource_id) { - res = virtio_gpu_find_resource(g, scanout->resource_id); - if (res) { - res->scanout_bitmask &= ~(1 << ss.scanout_id); - } - } - if (ss.scanout_id == 0) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: illegal scanout id specified %d", - __func__, ss.scanout_id); - cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID; - return; - } - dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, NULL); - scanout->ds = NULL; - scanout->width = 0; - scanout->height = 0; + virtio_gpu_disable_scanout(g, ss.scanout_id); return; } @@ -654,6 +675,11 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g, dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, scanout->ds); } + ores = virtio_gpu_find_resource(g, scanout->resource_id); + if (ores) { + ores->scanout_bitmask &= ~(1 << ss.scanout_id); + } + res->scanout_bitmask |= (1 << ss.scanout_id); scanout->resource_id = ss.resource_id; scanout->x = ss.r.x; diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c index 97db6c3372..8d3d9e14a7 100644 --- a/hw/display/virtio-vga.c +++ b/hw/display/virtio-vga.c @@ -106,7 +106,7 @@ static void virtio_vga_realize(VirtIOPCIProxy *vpci_dev, Error **errp) /* init vga compat bits */ vga->vram_size_mb = 8; - vga_common_init(vga, OBJECT(vpci_dev), false); + vga_common_init(vga, OBJECT(vpci_dev)); vga_init(vga, OBJECT(vpci_dev), pci_address_space(&vpci_dev->pci_dev), pci_address_space_io(&vpci_dev->pci_dev), true); pci_register_bar(&vpci_dev->pci_dev, 0, diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index 08deb08783..0bbb78b9a6 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -1242,7 +1242,7 @@ static void vmsvga_init(DeviceState *dev, struct vmsvga_state_s *s, &error_fatal); s->fifo_ptr = memory_region_get_ram_ptr(&s->fifo_ram); - vga_common_init(&s->vga, OBJECT(dev), true); + vga_common_init(&s->vga, OBJECT(dev)); vga_init(&s->vga, OBJECT(dev), address_space, io, true); vmstate_register(NULL, 0, &vmstate_vga_common, &s->vga); s->new_depth = 32; @@ -1322,6 +1322,8 @@ static void pci_vmsvga_realize(PCIDevice *dev, Error **errp) static Property vga_vmware_properties[] = { DEFINE_PROP_UINT32("vgamem_mb", struct pci_vmsvga_state_s, chip.vga.vram_size_mb, 16), + DEFINE_PROP_BOOL("global-vmstate", struct pci_vmsvga_state_s, + chip.vga.global_vmstate, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 0134232627..e5acfc5ba5 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -1672,7 +1672,8 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) ehci_set_state(ehci, async, EST_HORIZONTALQH); } else if ((q->qh.token & QTD_TOKEN_ACTIVE) && - (NLPTR_TBIT(q->qh.current_qtd) == 0)) { + (NLPTR_TBIT(q->qh.current_qtd) == 0) && + (q->qh.current_qtd != 0)) { q->qtdaddr = q->qh.current_qtd; ehci_set_state(ehci, async, EST_FETCHQTD); diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 721beb5486..8f1a01a405 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -1954,7 +1954,12 @@ static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid) for (i = 0; i < length; i++) { TRBType type; type = xhci_ring_fetch(xhci, ring, &xfer->trbs[i], NULL); - assert(type); + if (!type) { + xhci_die(xhci); + xhci_ep_free_xfer(xfer); + epctx->kick_active--; + return; + } } xfer->streamid = streamid; diff --git a/include/crypto/tlscredspsk.h b/include/crypto/tlscredspsk.h new file mode 100644 index 0000000000..306d36c67d --- /dev/null +++ b/include/crypto/tlscredspsk.h @@ -0,0 +1,106 @@ +/* + * QEMU crypto TLS Pre-Shared Key (PSK) support + * + * Copyright (c) 2018 Red Hat, Inc. + * + * 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 QCRYPTO_TLSCREDSPSK_H +#define QCRYPTO_TLSCREDSPSK_H + +#include "crypto/tlscreds.h" + +#define TYPE_QCRYPTO_TLS_CREDS_PSK "tls-creds-psk" +#define QCRYPTO_TLS_CREDS_PSK(obj) \ + OBJECT_CHECK(QCryptoTLSCredsPSK, (obj), TYPE_QCRYPTO_TLS_CREDS_PSK) + +typedef struct QCryptoTLSCredsPSK QCryptoTLSCredsPSK; +typedef struct QCryptoTLSCredsPSKClass QCryptoTLSCredsPSKClass; + +#define QCRYPTO_TLS_CREDS_PSKFILE "keys.psk" + +/** + * QCryptoTLSCredsPSK: + * + * The QCryptoTLSCredsPSK object provides a representation + * of the Pre-Shared Key credential used to perform a TLS handshake. + * + * This is a user creatable object, which can be instantiated + * via object_new_propv(): + * + * <example> + * <title>Creating TLS-PSK credential objects in code</title> + * <programlisting> + * Object *obj; + * Error *err = NULL; + * obj = object_new_propv(TYPE_QCRYPTO_TLS_CREDS_PSK, + * "tlscreds0", + * &err, + * "dir", "/path/to/dir", + * "endpoint", "client", + * NULL); + * </programlisting> + * </example> + * + * Or via QMP: + * + * <example> + * <title>Creating TLS-PSK credential objects via QMP</title> + * <programlisting> + * { + * "execute": "object-add", "arguments": { + * "id": "tlscreds0", + * "qom-type": "tls-creds-psk", + * "props": { + * "dir": "/path/to/dir", + * "endpoint": "client" + * } + * } + * } + * </programlisting> + * </example> + * + * Or via the CLI: + * + * <example> + * <title>Creating TLS-PSK credential objects via CLI</title> + * <programlisting> + * qemu-system-x86_64 --object tls-creds-psk,id=tlscreds0,\ + * endpoint=client,dir=/path/to/dir[,username=qemu] + * </programlisting> + * </example> + * + * The PSK file can be created and managed using psktool. + */ + +struct QCryptoTLSCredsPSK { + QCryptoTLSCreds parent_obj; + char *username; +#ifdef CONFIG_GNUTLS + union { + gnutls_psk_server_credentials_t server; + gnutls_psk_client_credentials_t client; + } data; +#endif +}; + + +struct QCryptoTLSCredsPSKClass { + QCryptoTLSCredsClass parent_class; +}; + + +#endif /* QCRYPTO_TLSCREDSPSK_H */ diff --git a/include/hw/compat.h b/include/hw/compat.h index 44d5964060..c08f4040bb 100644 --- a/include/hw/compat.h +++ b/include/hw/compat.h @@ -10,6 +10,22 @@ .driver = "hda-audio",\ .property = "use-timer",\ .value = "false",\ + },{\ + .driver = "cirrus-vga",\ + .property = "global-vmstate",\ + .value = "true",\ + },{\ + .driver = "VGA",\ + .property = "global-vmstate",\ + .value = "true",\ + },{\ + .driver = "vmware-svga",\ + .property = "global-vmstate",\ + .value = "true",\ + },{\ + .driver = "qxl-vga",\ + .property = "global-vmstate",\ + .value = "true",\ }, #define HW_COMPAT_2_11 \ diff --git a/linux-user/openrisc/signal.c b/linux-user/openrisc/signal.c index 8be0b74001..232ad82b98 100644 --- a/linux-user/openrisc/signal.c +++ b/linux-user/openrisc/signal.c @@ -21,124 +21,69 @@ #include "signal-common.h" #include "linux-user/trace.h" -struct target_sigcontext { +typedef struct target_sigcontext { struct target_pt_regs regs; abi_ulong oldmask; - abi_ulong usp; -}; +} target_sigcontext; -struct target_ucontext { +typedef struct target_ucontext { abi_ulong tuc_flags; abi_ulong tuc_link; target_stack_t tuc_stack; - struct target_sigcontext tuc_mcontext; + target_sigcontext tuc_mcontext; target_sigset_t tuc_sigmask; /* mask last for extensibility */ -}; +} target_ucontext; -struct target_rt_sigframe { - abi_ulong pinfo; - uint64_t puc; +typedef struct target_rt_sigframe { struct target_siginfo info; - struct target_sigcontext sc; - struct target_ucontext uc; - unsigned char retcode[16]; /* trampoline code */ -}; - -/* This is the asm-generic/ucontext.h version */ -#if 0 -static int restore_sigcontext(CPUOpenRISCState *regs, - struct target_sigcontext *sc) -{ - unsigned int err = 0; - unsigned long old_usp; - - /* Alwys make any pending restarted system call return -EINTR */ - current_thread_info()->restart_block.fn = do_no_restart_syscall; + target_ucontext uc; + uint32_t retcode[4]; /* trampoline code */ +} target_rt_sigframe; - /* restore the regs from &sc->regs (same as sc, since regs is first) - * (sc is already checked for VERIFY_READ since the sigframe was - * checked in sys_sigreturn previously) - */ +static void restore_sigcontext(CPUOpenRISCState *env, target_sigcontext *sc) +{ + int i; + abi_ulong v; - if (copy_from_user(regs, &sc, sizeof(struct target_pt_regs))) { - goto badframe; + for (i = 0; i < 32; ++i) { + __get_user(v, &sc->regs.gpr[i]); + cpu_set_gpr(env, i, v); } + __get_user(env->pc, &sc->regs.pc); - /* make sure the U-flag is set so user-mode cannot fool us */ - - regs->sr &= ~SR_SM; - - /* restore the old USP as it was before we stacked the sc etc. - * (we cannot just pop the sigcontext since we aligned the sp and - * stuff after pushing it) - */ - - __get_user(old_usp, &sc->usp); - phx_signal("old_usp 0x%lx", old_usp); - - __PHX__ REALLY /* ??? */ - wrusp(old_usp); - regs->gpr[1] = old_usp; - - /* TODO: the other ports use regs->orig_XX to disable syscall checks - * after this completes, but we don't use that mechanism. maybe we can - * use it now ? - */ - - return err; - -badframe: - return 1; + /* Make sure the supervisor flag is clear. */ + __get_user(v, &sc->regs.sr); + cpu_set_sr(env, v & ~SR_SM); } -#endif /* Set up a signal frame. */ -static void setup_sigcontext(struct target_sigcontext *sc, - CPUOpenRISCState *regs, - unsigned long mask) +static void setup_sigcontext(target_sigcontext *sc, CPUOpenRISCState *env) { - unsigned long usp = cpu_get_gpr(regs, 1); - - /* copy the regs. they are first in sc so we can use sc directly */ + int i; - /*copy_to_user(&sc, regs, sizeof(struct target_pt_regs));*/ - - /* Set the frametype to CRIS_FRAME_NORMAL for the execution of - the signal handler. The frametype will be restored to its previous - value in restore_sigcontext. */ - /*regs->frametype = CRIS_FRAME_NORMAL;*/ - - /* then some other stuff */ - __put_user(mask, &sc->oldmask); - __put_user(usp, &sc->usp); -} + for (i = 0; i < 32; ++i) { + __put_user(cpu_get_gpr(env, i), &sc->regs.gpr[i]); + } -static inline unsigned long align_sigframe(unsigned long sp) -{ - return sp & ~3UL; + __put_user(env->pc, &sc->regs.pc); + __put_user(cpu_get_sr(env), &sc->regs.sr); } static inline abi_ulong get_sigframe(struct target_sigaction *ka, - CPUOpenRISCState *regs, + CPUOpenRISCState *env, size_t frame_size) { - unsigned long sp = get_sp_from_cpustate(regs); - int onsigstack = on_sig_stack(sp); - - /* redzone */ - sp = target_sigsp(sp, ka); - - sp = align_sigframe(sp - frame_size); + target_ulong sp = get_sp_from_cpustate(env); - /* - * If we are on the alternate signal stack and would overflow it, don't. - * Return an always-bogus address instead so we will die with SIGSEGV. + /* Honor redzone now. If we swap to signal stack, no need to waste + * the 128 bytes by subtracting afterward. */ + sp -= 128; - if (onsigstack && !likely(on_sig_stack(sp))) { - return -1L; - } + sp = target_sigsp(sp, ka); + sp -= frame_size; + sp = QEMU_ALIGN_DOWN(sp, 4); return sp; } @@ -147,11 +92,9 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, target_siginfo_t *info, target_sigset_t *set, CPUOpenRISCState *env) { - int err = 0; abi_ulong frame_addr; - unsigned long return_ip; - struct target_rt_sigframe *frame; - abi_ulong info_addr, uc_addr; + target_rt_sigframe *frame; + int i; frame_addr = get_sigframe(ka, env, sizeof(*frame)); trace_user_setup_rt_frame(env, frame_addr); @@ -159,47 +102,37 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, goto give_sigsegv; } - info_addr = frame_addr + offsetof(struct target_rt_sigframe, info); - __put_user(info_addr, &frame->pinfo); - uc_addr = frame_addr + offsetof(struct target_rt_sigframe, uc); - __put_user(uc_addr, &frame->puc); - if (ka->sa_flags & SA_SIGINFO) { tswap_siginfo(&frame->info, info); } - /*err |= __clear_user(&frame->uc, offsetof(ucontext_t, uc_mcontext));*/ __put_user(0, &frame->uc.tuc_flags); __put_user(0, &frame->uc.tuc_link); - target_save_altstack(&frame->uc.tuc_stack, env); - setup_sigcontext(&frame->sc, env, set->sig[0]); - /*err |= copy_to_user(frame->uc.tuc_sigmask, set, sizeof(*set));*/ - - /* trampoline - the desired return ip is the retcode itself */ - return_ip = (unsigned long)&frame->retcode; - /* This is l.ori r11,r0,__NR_sigreturn, l.sys 1 */ - __put_user(0xa960, (short *)(frame->retcode + 0)); - __put_user(TARGET_NR_rt_sigreturn, (short *)(frame->retcode + 2)); - __put_user(0x20000001, (unsigned long *)(frame->retcode + 4)); - __put_user(0x15000000, (unsigned long *)(frame->retcode + 8)); - - if (err) { - goto give_sigsegv; + target_save_altstack(&frame->uc.tuc_stack, env); + setup_sigcontext(&frame->uc.tuc_mcontext, env); + for (i = 0; i < TARGET_NSIG_WORDS; ++i) { + __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); } - /* TODO what is the current->exec_domain stuff and invmap ? */ + /* This is l.ori r11,r0,__NR_sigreturn; l.sys 1; l.nop; l.nop */ + __put_user(0xa9600000 | TARGET_NR_rt_sigreturn, frame->retcode + 0); + __put_user(0x20000001, frame->retcode + 1); + __put_user(0x15000000, frame->retcode + 2); + __put_user(0x15000000, frame->retcode + 3); /* Set up registers for signal handler */ - env->pc = (unsigned long)ka->_sa_handler; /* what we enter NOW */ - cpu_set_gpr(env, 9, (unsigned long)return_ip); /* what we enter LATER */ - cpu_set_gpr(env, 3, (unsigned long)sig); /* arg 1: signo */ - cpu_set_gpr(env, 4, (unsigned long)&frame->info); /* arg 2: (siginfo_t*) */ - cpu_set_gpr(env, 5, (unsigned long)&frame->uc); /* arg 3: ucontext */ - - /* actually move the usp to reflect the stacked frame */ - cpu_set_gpr(env, 1, (unsigned long)frame); - + cpu_set_gpr(env, 9, frame_addr + offsetof(target_rt_sigframe, retcode)); + cpu_set_gpr(env, 3, sig); + cpu_set_gpr(env, 4, frame_addr + offsetof(target_rt_sigframe, info)); + cpu_set_gpr(env, 5, frame_addr + offsetof(target_rt_sigframe, uc)); + cpu_set_gpr(env, 1, frame_addr); + + /* For debugging convenience, set ppc to the insn that faulted. */ + env->ppc = env->pc; + /* When setting the PC for the signal handler, exit delay slot. */ + env->pc = ka->_sa_handler; + env->dflag = 0; return; give_sigsegv: @@ -207,16 +140,34 @@ give_sigsegv: force_sigsegv(sig); } -long do_sigreturn(CPUOpenRISCState *env) -{ - trace_user_do_sigreturn(env, 0); - fprintf(stderr, "do_sigreturn: not implemented\n"); - return -TARGET_ENOSYS; -} - long do_rt_sigreturn(CPUOpenRISCState *env) { + abi_ulong frame_addr = get_sp_from_cpustate(env); + target_rt_sigframe *frame; + sigset_t set; + trace_user_do_rt_sigreturn(env, 0); - fprintf(stderr, "do_rt_sigreturn: not implemented\n"); - return -TARGET_ENOSYS; + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + if (frame_addr & 3) { + goto badframe; + } + + target_to_host_sigset(&set, &frame->uc.tuc_sigmask); + set_sigmask(&set); + + restore_sigcontext(env, &frame->uc.tuc_mcontext); + if (do_sigaltstack(frame_addr + offsetof(target_rt_sigframe, uc.tuc_stack), + 0, frame_addr) == -EFAULT) { + goto badframe; + } + + unlock_user_struct(frame, frame_addr, 0); + return cpu_get_gpr(env, 11); + + badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return 0; } diff --git a/linux-user/openrisc/target_signal.h b/linux-user/openrisc/target_signal.h index c352a8b333..8283eaf544 100644 --- a/linux-user/openrisc/target_signal.h +++ b/linux-user/openrisc/target_signal.h @@ -5,8 +5,8 @@ typedef struct target_sigaltstack { abi_long ss_sp; + abi_int ss_flags; abi_ulong ss_size; - abi_long ss_flags; } target_stack_t; /* sigaltstack controls */ diff --git a/linux-user/openrisc/target_syscall.h b/linux-user/openrisc/target_syscall.h index 03104f80af..d586d2a018 100644 --- a/linux-user/openrisc/target_syscall.h +++ b/linux-user/openrisc/target_syscall.h @@ -1,27 +1,15 @@ #ifndef OPENRISC_TARGET_SYSCALL_H #define OPENRISC_TARGET_SYSCALL_H +/* Note that in linux/arch/openrisc/include/uapi/asm/ptrace.h, + * this is called user_regs_struct. Given that this is what + * is used within struct sigcontext we need this definition. + * However, elfload.c wants this name. + */ struct target_pt_regs { - union { - struct { - /* Named registers */ - uint32_t sr; /* Stored in place of r0 */ - target_ulong sp; /* r1 */ - }; - struct { - /* Old style */ - target_ulong offset[2]; - target_ulong gprs[30]; - }; - struct { - /* New style */ - target_ulong gpr[32]; - }; - }; - target_ulong pc; - target_ulong orig_gpr11; /* For restarting system calls */ - uint32_t syscallno; /* Syscall number (used by strace) */ - target_ulong dummy; /* Cheap alignment fix */ + abi_ulong gpr[32]; + abi_ulong pc; + abi_ulong sr; }; #define UNAME_MACHINE "openrisc" diff --git a/linux-user/signal.c b/linux-user/signal.c index be2815b45d..602b631b92 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -236,7 +236,7 @@ int do_sigprocmask(int how, const sigset_t *set, sigset_t *oldset) return 0; } -#if !defined(TARGET_OPENRISC) && !defined(TARGET_NIOS2) +#if !defined(TARGET_NIOS2) /* Just set the guest's signal mask to the specified value; the * caller is assumed to have called block_signals() already. */ diff --git a/linux-user/strace.list b/linux-user/strace.list index 2bc5ba04d4..ff8bb19f5f 100644 --- a/linux-user/strace.list +++ b/linux-user/strace.list @@ -59,10 +59,10 @@ { TARGET_NR_cacheflush, "cacheflush" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_capget -{ TARGET_NR_capget, "capget" , NULL, NULL, NULL }, +{ TARGET_NR_capget, "capget" , "%s(%p,%p)", NULL, NULL }, #endif #ifdef TARGET_NR_capset -{ TARGET_NR_capset, "capset" , NULL, NULL, NULL }, +{ TARGET_NR_capset, "capset" , "%s(%p,%p)", NULL, NULL }, #endif #ifdef TARGET_NR_chdir { TARGET_NR_chdir, "chdir" , NULL, print_chdir, NULL }, @@ -1107,7 +1107,7 @@ { TARGET_NR_recvmmsg, "recvmmsg" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_recvmsg -{ TARGET_NR_recvmsg, "recvmsg" , NULL, NULL, NULL }, +{ TARGET_NR_recvmsg, "recvmsg" , "%s(%d,%p,%#x)", NULL, NULL }, #endif #ifdef TARGET_NR_remap_file_pages { TARGET_NR_remap_file_pages, "remap_file_pages" , NULL, NULL, NULL }, diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 2117fb13b4..50e20fb659 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -2522,6 +2522,8 @@ static abi_long host_to_target_data_link_rtattr(struct rtattr *rtattr) case QEMU_IFLA_NUM_VF: case QEMU_IFLA_GSO_MAX_SEGS: case QEMU_IFLA_GSO_MAX_SIZE: + case QEMU_IFLA_CARRIER_UP_COUNT: + case QEMU_IFLA_CARRIER_DOWN_COUNT: u32 = RTA_DATA(rtattr); *u32 = tswap32(*u32); break; @@ -3017,6 +3019,8 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, case IPV6_V6ONLY: case IPV6_RECVPKTINFO: case IPV6_UNICAST_HOPS: + case IPV6_MULTICAST_HOPS: + case IPV6_MULTICAST_LOOP: case IPV6_RECVERR: case IPV6_RECVHOPLIMIT: case IPV6_2292HOPLIMIT: diff --git a/qemu-doc.texi b/qemu-doc.texi index 1cb3ba4341..d3924e928e 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -1262,6 +1262,7 @@ The recommendation is for the server to keep its certificates in either * tls_generate_server:: * tls_generate_client:: * tls_creds_setup:: +* tls_psk:: @end menu @node tls_generate_ca @subsection Setup the Certificate Authority @@ -1510,6 +1511,42 @@ example with VNC: $QEMU -vnc 0.0.0.0:0,tls-creds=tls0 @end example +@node tls_psk +@subsection TLS Pre-Shared Keys (PSK) + +Instead of using certificates, you may also use TLS Pre-Shared Keys +(TLS-PSK). This can be simpler to set up than certificates but is +less scalable. + +Use the GnuTLS @code{psktool} program to generate a @code{keys.psk} +file containing one or more usernames and random keys: + +@example +mkdir -m 0700 /tmp/keys +psktool -u rich -p /tmp/keys/keys.psk +@end example + +TLS-enabled servers such as qemu-nbd can use this directory like so: + +@example +qemu-nbd \ + -t -x / \ + --object tls-creds-psk,id=tls0,endpoint=server,dir=/tmp/keys \ + --tls-creds tls0 \ + image.qcow2 +@end example + +When connecting from a qemu-based client you must specify the +directory containing @code{keys.psk} and an optional @var{username} +(defaults to ``qemu''): + +@example +qemu-img info \ + --object tls-creds-psk,id=tls0,dir=/tmp/keys,username=rich,endpoint=client \ + --image-opts \ + file.driver=nbd,file.host=localhost,file.port=10809,file.tls-creds=tls0,file.export=/ +@end example + @node gdb_usage @section GDB usage diff --git a/qemu-options.hx b/qemu-options.hx index 81b1e99d58..16208f63f2 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -4123,6 +4123,30 @@ expensive operation that consumes random pool entropy, so it is recommended that a persistent set of parameters be generated upfront and saved. +@item -object tls-creds-psk,id=@var{id},endpoint=@var{endpoint},dir=@var{/path/to/keys/dir}[,username=@var{username}] + +Creates a TLS Pre-Shared Keys (PSK) credentials object, which can be used to provide +TLS support on network backends. The @option{id} parameter is a unique +ID which network backends will use to access the credentials. The +@option{endpoint} is either @option{server} or @option{client} depending +on whether the QEMU network backend that uses the credentials will be +acting as a client or as a server. For clients only, @option{username} +is the username which will be sent to the server. If omitted +it defaults to ``qemu''. + +The @var{dir} parameter tells QEMU where to find the keys file. +It is called ``@var{dir}/keys.psk'' and contains ``username:key'' +pairs. This file can most easily be created using the GnuTLS +@code{psktool} program. + +For server endpoints, @var{dir} may also contain a file +@var{dh-params.pem} providing diffie-hellman parameters to use +for the TLS server. If the file is missing, QEMU will generate +a set of DH parameters at startup. This is a computationally +expensive operation that consumes random pool entropy, so it is +recommended that a persistent set of parameters be generated +up front and saved. + @item -object tls-creds-x509,id=@var{id},endpoint=@var{endpoint},dir=@var{/path/to/cred/dir},priority=@var{priority},verify-peer=@var{on|off},passwordid=@var{id} Creates a TLS anonymous credentials object, which can be used to provide diff --git a/qga/commands-posix.c b/qga/commands-posix.c index eae817191b..233f78a406 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -46,6 +46,7 @@ extern char **environ; #include <arpa/inet.h> #include <sys/socket.h> #include <net/if.h> +#include <sys/statvfs.h> #ifdef FIFREEZE #define CONFIG_FSFREEZE @@ -458,7 +459,7 @@ struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count, if (!has_count) { count = QGA_READ_COUNT_DEFAULT; - } else if (count < 0) { + } else if (count < 0 || count >= UINT32_MAX) { error_setg(errp, "value '%" PRId64 "' is invalid for argument count", count); return NULL; @@ -875,13 +876,28 @@ static void build_guest_fsinfo_for_real_device(char const *syspath, p = strstr(syspath, "/devices/pci"); if (!p || sscanf(p + 12, "%*x:%*x/%x:%x:%x.%x%n", pci, pci + 1, pci + 2, pci + 3, &pcilen) < 4) { - g_debug("only pci device is supported: sysfs path \"%s\"", syspath); + g_debug("only pci device is supported: sysfs path '%s'", syspath); return; } - driver = get_pci_driver(syspath, (p + 12 + pcilen) - syspath, errp); - if (!driver) { - goto cleanup; + p += 12 + pcilen; + while (true) { + driver = get_pci_driver(syspath, p - syspath, errp); + if (driver && (g_str_equal(driver, "ata_piix") || + g_str_equal(driver, "sym53c8xx") || + g_str_equal(driver, "virtio-pci") || + g_str_equal(driver, "ahci"))) { + break; + } + + if (sscanf(p, "/%x:%x:%x.%x%n", + pci, pci + 1, pci + 2, pci + 3, &pcilen) == 4) { + p += pcilen; + continue; + } + + g_debug("unsupported driver or sysfs path '%s'", syspath); + return; } p = strstr(syspath, "/target"); @@ -1072,6 +1088,8 @@ static GuestFilesystemInfo *build_guest_fsinfo(struct FsMount *mount, Error **errp) { GuestFilesystemInfo *fs = g_malloc0(sizeof(*fs)); + struct statvfs buf; + unsigned long used, nonroot_total, fr_size; char *devpath = g_strdup_printf("/sys/dev/block/%u:%u", mount->devmajor, mount->devminor); @@ -1079,7 +1097,19 @@ static GuestFilesystemInfo *build_guest_fsinfo(struct FsMount *mount, fs->type = g_strdup(mount->devtype); build_guest_fsinfo_for_device(devpath, fs, errp); + if (statvfs(fs->mountpoint, &buf) == 0) { + fr_size = buf.f_frsize; + used = buf.f_blocks - buf.f_bfree; + nonroot_total = used + buf.f_bavail; + fs->used_bytes = used * fr_size; + fs->total_bytes = nonroot_total * fr_size; + + fs->has_total_bytes = true; + fs->has_used_bytes = true; + } + g_free(devpath); + return fs; } @@ -1274,6 +1304,12 @@ int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints, } free_fs_mount_list(&mounts); + /* We may not issue any FIFREEZE here. + * Just unset ga_state here and ready for the next call. + */ + if (i == 0) { + ga_unset_frozen(ga_state); + } return i; error: @@ -1439,102 +1475,208 @@ qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) #define SUSPEND_SUPPORTED 0 #define SUSPEND_NOT_SUPPORTED 1 -static void bios_supports_mode(const char *pmutils_bin, const char *pmutils_arg, - const char *sysfile_str, Error **errp) +typedef enum { + SUSPEND_MODE_DISK = 0, + SUSPEND_MODE_RAM = 1, + SUSPEND_MODE_HYBRID = 2, +} SuspendMode; + +/* + * Executes a command in a child process using g_spawn_sync, + * returning an int >= 0 representing the exit status of the + * process. + * + * If the program wasn't found in path, returns -1. + * + * If a problem happened when creating the child process, + * returns -1 and errp is set. + */ +static int run_process_child(const char *command[], Error **errp) +{ + int exit_status, spawn_flag; + GError *g_err = NULL; + bool success; + + spawn_flag = G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL | + G_SPAWN_STDERR_TO_DEV_NULL; + + success = g_spawn_sync(NULL, (char **)command, environ, spawn_flag, + NULL, NULL, NULL, NULL, + &exit_status, &g_err); + + if (success) { + return WEXITSTATUS(exit_status); + } + + if (g_err && (g_err->code != G_SPAWN_ERROR_NOENT)) { + error_setg(errp, "failed to create child process, error '%s'", + g_err->message); + } + + g_error_free(g_err); + return -1; +} + +static bool systemd_supports_mode(SuspendMode mode, Error **errp) { Error *local_err = NULL; - char *pmutils_path; - pid_t pid; + const char *systemctl_args[3] = {"systemd-hibernate", "systemd-suspend", + "systemd-hybrid-sleep"}; + const char *cmd[4] = {"systemctl", "status", systemctl_args[mode], NULL}; int status; - pmutils_path = g_find_program_in_path(pmutils_bin); + status = run_process_child(cmd, &local_err); - pid = fork(); - if (!pid) { - char buf[32]; /* hopefully big enough */ - ssize_t ret; - int fd; + /* + * systemctl status uses LSB return codes so we can expect + * status > 0 and be ok. To assert if the guest has support + * for the selected suspend mode, status should be < 4. 4 is + * the code for unknown service status, the return value when + * the service does not exist. A common value is status = 3 + * (program is not running). + */ + if (status > 0 && status < 4) { + return true; + } - setsid(); - reopen_fd_to_null(0); - reopen_fd_to_null(1); - reopen_fd_to_null(2); + if (local_err) { + error_propagate(errp, local_err); + } - if (pmutils_path) { - execle(pmutils_path, pmutils_bin, pmutils_arg, NULL, environ); - } + return false; +} - /* - * If we get here either pm-utils is not installed or execle() has - * failed. Let's try the manual method if the caller wants it. - */ +static void systemd_suspend(SuspendMode mode, Error **errp) +{ + Error *local_err = NULL; + const char *systemctl_args[3] = {"hibernate", "suspend", "hybrid-sleep"}; + const char *cmd[3] = {"systemctl", systemctl_args[mode], NULL}; + int status; - if (!sysfile_str) { - _exit(SUSPEND_NOT_SUPPORTED); - } + status = run_process_child(cmd, &local_err); - fd = open(LINUX_SYS_STATE_FILE, O_RDONLY); - if (fd < 0) { - _exit(SUSPEND_NOT_SUPPORTED); - } + if (status == 0) { + return; + } - ret = read(fd, buf, sizeof(buf)-1); - if (ret <= 0) { - _exit(SUSPEND_NOT_SUPPORTED); - } - buf[ret] = '\0'; + if ((status == -1) && !local_err) { + error_setg(errp, "the helper program 'systemctl %s' was not found", + systemctl_args[mode]); + return; + } - if (strstr(buf, sysfile_str)) { - _exit(SUSPEND_SUPPORTED); - } + if (local_err) { + error_propagate(errp, local_err); + } else { + error_setg(errp, "the helper program 'systemctl %s' returned an " + "unexpected exit status code (%d)", + systemctl_args[mode], status); + } +} - _exit(SUSPEND_NOT_SUPPORTED); - } else if (pid < 0) { - error_setg_errno(errp, errno, "failed to create child process"); - goto out; +static bool pmutils_supports_mode(SuspendMode mode, Error **errp) +{ + Error *local_err = NULL; + const char *pmutils_args[3] = {"--hibernate", "--suspend", + "--suspend-hybrid"}; + const char *cmd[3] = {"pm-is-supported", pmutils_args[mode], NULL}; + int status; + + status = run_process_child(cmd, &local_err); + + if (status == SUSPEND_SUPPORTED) { + return true; + } + + if ((status == -1) && !local_err) { + return false; } - ga_wait_child(pid, &status, &local_err); if (local_err) { error_propagate(errp, local_err); - goto out; + } else { + error_setg(errp, + "the helper program '%s' returned an unexpected exit" + " status code (%d)", "pm-is-supported", status); } - if (!WIFEXITED(status)) { - error_setg(errp, "child process has terminated abnormally"); - goto out; + return false; +} + +static void pmutils_suspend(SuspendMode mode, Error **errp) +{ + Error *local_err = NULL; + const char *pmutils_binaries[3] = {"pm-hibernate", "pm-suspend", + "pm-suspend-hybrid"}; + const char *cmd[2] = {pmutils_binaries[mode], NULL}; + int status; + + status = run_process_child(cmd, &local_err); + + if (status == 0) { + return; } - switch (WEXITSTATUS(status)) { - case SUSPEND_SUPPORTED: - goto out; - case SUSPEND_NOT_SUPPORTED: - error_setg(errp, - "the requested suspend mode is not supported by the guest"); - goto out; - default: + if ((status == -1) && !local_err) { + error_setg(errp, "the helper program '%s' was not found", + pmutils_binaries[mode]); + return; + } + + if (local_err) { + error_propagate(errp, local_err); + } else { error_setg(errp, - "the helper program '%s' returned an unexpected exit status" - " code (%d)", pmutils_path, WEXITSTATUS(status)); - goto out; + "the helper program '%s' returned an unexpected exit" + " status code (%d)", pmutils_binaries[mode], status); } +} -out: - g_free(pmutils_path); +static bool linux_sys_state_supports_mode(SuspendMode mode, Error **errp) +{ + const char *sysfile_strs[3] = {"disk", "mem", NULL}; + const char *sysfile_str = sysfile_strs[mode]; + char buf[32]; /* hopefully big enough */ + int fd; + ssize_t ret; + + if (!sysfile_str) { + error_setg(errp, "unknown guest suspend mode"); + return false; + } + + fd = open(LINUX_SYS_STATE_FILE, O_RDONLY); + if (fd < 0) { + return false; + } + + ret = read(fd, buf, sizeof(buf) - 1); + if (ret <= 0) { + return false; + } + buf[ret] = '\0'; + + if (strstr(buf, sysfile_str)) { + return true; + } + return false; } -static void guest_suspend(const char *pmutils_bin, const char *sysfile_str, - Error **errp) +static void linux_sys_state_suspend(SuspendMode mode, Error **errp) { Error *local_err = NULL; - char *pmutils_path; + const char *sysfile_strs[3] = {"disk", "mem", NULL}; + const char *sysfile_str = sysfile_strs[mode]; pid_t pid; int status; - pmutils_path = g_find_program_in_path(pmutils_bin); + if (!sysfile_str) { + error_setg(errp, "unknown guest suspend mode"); + return; + } pid = fork(); - if (pid == 0) { + if (!pid) { /* child */ int fd; @@ -1543,19 +1685,6 @@ static void guest_suspend(const char *pmutils_bin, const char *sysfile_str, reopen_fd_to_null(1); reopen_fd_to_null(2); - if (pmutils_path) { - execle(pmutils_path, pmutils_bin, NULL, environ); - } - - /* - * If we get here either pm-utils is not installed or execle() has - * failed. Let's try the manual method if the caller wants it. - */ - - if (!sysfile_str) { - _exit(EXIT_FAILURE); - } - fd = open(LINUX_SYS_STATE_FILE, O_WRONLY); if (fd < 0) { _exit(EXIT_FAILURE); @@ -1568,67 +1697,74 @@ static void guest_suspend(const char *pmutils_bin, const char *sysfile_str, _exit(EXIT_SUCCESS); } else if (pid < 0) { error_setg_errno(errp, errno, "failed to create child process"); - goto out; + return; } ga_wait_child(pid, &status, &local_err); if (local_err) { error_propagate(errp, local_err); - goto out; - } - - if (!WIFEXITED(status)) { - error_setg(errp, "child process has terminated abnormally"); - goto out; + return; } if (WEXITSTATUS(status)) { error_setg(errp, "child process has failed to suspend"); - goto out; } -out: - g_free(pmutils_path); } -void qmp_guest_suspend_disk(Error **errp) +static void guest_suspend(SuspendMode mode, Error **errp) { Error *local_err = NULL; + bool mode_supported = false; - bios_supports_mode("pm-is-supported", "--hibernate", "disk", &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (systemd_supports_mode(mode, &local_err)) { + mode_supported = true; + systemd_suspend(mode, &local_err); + } + + if (!local_err) { return; } - guest_suspend("pm-hibernate", "disk", errp); -} + error_free(local_err); -void qmp_guest_suspend_ram(Error **errp) -{ - Error *local_err = NULL; + if (pmutils_supports_mode(mode, &local_err)) { + mode_supported = true; + pmutils_suspend(mode, &local_err); + } - bios_supports_mode("pm-is-supported", "--suspend", "mem", &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (!local_err) { return; } - guest_suspend("pm-suspend", "mem", errp); -} + error_free(local_err); -void qmp_guest_suspend_hybrid(Error **errp) -{ - Error *local_err = NULL; + if (linux_sys_state_supports_mode(mode, &local_err)) { + mode_supported = true; + linux_sys_state_suspend(mode, &local_err); + } - bios_supports_mode("pm-is-supported", "--suspend-hybrid", NULL, - &local_err); - if (local_err) { + if (!mode_supported) { + error_setg(errp, + "the requested suspend mode is not supported by the guest"); + } else if (local_err) { error_propagate(errp, local_err); - return; } +} - guest_suspend("pm-suspend-hybrid", NULL, errp); +void qmp_guest_suspend_disk(Error **errp) +{ + guest_suspend(SUSPEND_MODE_DISK, errp); +} + +void qmp_guest_suspend_ram(Error **errp) +{ + guest_suspend(SUSPEND_MODE_RAM, errp); +} + +void qmp_guest_suspend_hybrid(Error **errp) +{ + guest_suspend(SUSPEND_MODE_HYBRID, errp); } static GuestNetworkInterfaceList * diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 70ee5379f6..318d760a74 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -318,7 +318,7 @@ GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count, } if (!has_count) { count = QGA_READ_COUNT_DEFAULT; - } else if (count < 0) { + } else if (count < 0 || count >= UINT32_MAX) { error_setg(errp, "value '%" PRId64 "' is invalid for argument count", count); return NULL; @@ -670,6 +670,7 @@ static GuestFilesystemInfo *build_guest_fsinfo(char *guid, Error **errp) char fs_name[32]; char vol_info[MAX_PATH+1]; size_t len; + uint64_t i64FreeBytesToCaller, i64TotalBytes, i64FreeBytes; GuestFilesystemInfo *fs = NULL; GetVolumePathNamesForVolumeName(guid, (LPCH)&mnt, 0, &info_size); @@ -699,10 +700,21 @@ static GuestFilesystemInfo *build_guest_fsinfo(char *guid, Error **errp) fs_name[sizeof(fs_name) - 1] = 0; fs = g_malloc(sizeof(*fs)); fs->name = g_strdup(guid); + fs->has_total_bytes = false; + fs->has_used_bytes = false; if (len == 0) { fs->mountpoint = g_strdup("System Reserved"); } else { fs->mountpoint = g_strndup(mnt_point, len); + if (GetDiskFreeSpaceEx(fs->mountpoint, + (PULARGE_INTEGER) & i64FreeBytesToCaller, + (PULARGE_INTEGER) & i64TotalBytes, + (PULARGE_INTEGER) & i64FreeBytes)) { + fs->used_bytes = i64TotalBytes - i64FreeBytes; + fs->total_bytes = i64TotalBytes; + fs->has_total_bytes = true; + fs->has_used_bytes = true; + } } fs->type = g_strdup(fs_name); fs->disk = build_guest_disk_info(guid, errp); diff --git a/qga/installer/qemu-ga.wxs b/qga/installer/qemu-ga.wxs index 5af11627f8..f751a7e9f7 100644 --- a/qga/installer/qemu-ga.wxs +++ b/qga/installer/qemu-ga.wxs @@ -41,7 +41,7 @@ <Product Name="QEMU guest agent" - Id="{DF9974AD-E41A-4304-81AD-69AA8F299766}" + Id="*" UpgradeCode="{EB6B8302-C06E-4BEC-ADAC-932C68A3A98D}" Manufacturer="$(env.QEMU_GA_MANUFACTURER)" Version="$(env.QEMU_GA_VERSION)" diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 17884c7c70..dfbc4a5e32 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -435,7 +435,9 @@ # for up to 10 seconds by VSS. # # Returns: Number of file systems currently frozen. On error, all filesystems -# will be thawed. +# will be thawed. If no filesystems are frozen as a result of this call, +# then @guest-fsfreeze-status will remain "thawed" and calling +# @guest-fsfreeze-thaw is not necessary. # # Since: 0.15.0 ## @@ -846,6 +848,8 @@ # @name: disk name # @mountpoint: mount point path # @type: file system type string +# @used-bytes: file system used bytes (since 3.0) +# @total-bytes: non-root file system total bytes (since 3.0) # @disk: an array of disk hardware information that the volume lies on, # which may be empty if the disk type is not supported # @@ -853,6 +857,7 @@ ## { 'struct': 'GuestFilesystemInfo', 'data': {'name': 'str', 'mountpoint': 'str', 'type': 'str', + '*used-bytes': 'uint64', '*total-bytes': 'uint64', 'disk': ['GuestDiskAddress']} } ## @@ -1168,10 +1173,10 @@ # # @kernel-release: # * POSIX: release field returned by uname(2) -# * Windows: version number of the OS +# * Windows: build number of the OS # @kernel-version: # * POSIX: version field returned by uname(2) -# * Windows: build number of the OS +# * Windows: version number of the OS # @machine: # * POSIX: machine field returned by uname(2) # * Windows: one of x86, x86_64, arm, ia64 diff --git a/scripts/qemu-binfmt-conf.sh b/scripts/qemu-binfmt-conf.sh index d7eefda0b8..b0dc8a714a 100755 --- a/scripts/qemu-binfmt-conf.sh +++ b/scripts/qemu-binfmt-conf.sh @@ -1,10 +1,10 @@ #!/bin/sh -# enable automatic i386/ARM/M68K/MIPS/SPARC/PPC/s390/HPPA/Xtensa/microblaze -# program execution by the kernel +# Enable automatic program execution by the kernel. qemu_target_list="i386 i486 alpha arm armeb sparc32plus ppc ppc64 ppc64le m68k \ mips mipsel mipsn32 mipsn32el mips64 mips64el \ -sh4 sh4eb s390x aarch64 aarch64_be hppa riscv32 riscv64 xtensa xtensaeb microblaze microblazeel" +sh4 sh4eb s390x aarch64 aarch64_be hppa riscv32 riscv64 xtensa xtensaeb \ +microblaze microblazeel or1k" i386_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03\x00' i386_mask='\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' @@ -124,6 +124,10 @@ microblazeel_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\ microblazeel_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' microblazeel_family=microblazeel +or1k_magic='\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x5c' +or1k_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff' +or1k_family=or1k + qemu_get_family() { cpu=${HOST_ARCH:-$(uname -m)} case "$cpu" in @@ -161,21 +165,26 @@ usage() { cat <<EOF Usage: qemu-binfmt-conf.sh [--qemu-path PATH][--debian][--systemd CPU] [--help][--credential yes|no][--exportdir PATH] + [--persistent yes|no][--qemu-suffix SUFFIX] Configure binfmt_misc to use qemu interpreter - --help: display this usage - --qemu-path: set path to qemu interpreter ($QEMU_PATH) - --debian: don't write into /proc, - instead generate update-binfmts templates - --systemd: don't write into /proc, - instead generate file for systemd-binfmt.service - for the given CPU. If CPU is "ALL", generate a - file for all known cpus - --exportdir: define where to write configuration files - (default: $SYSTEMDDIR or $DEBIANDIR) - --credential: if yes, credential and security tokens are - calculated according to the binary to interpret + --help: display this usage + --qemu-path: set path to qemu interpreter ($QEMU_PATH) + --qemu-suffix: add a suffix to the default interpreter name + --debian: don't write into /proc, + instead generate update-binfmts templates + --systemd: don't write into /proc, + instead generate file for systemd-binfmt.service + for the given CPU. If CPU is "ALL", generate a + file for all known cpus + --exportdir: define where to write configuration files + (default: $SYSTEMDDIR or $DEBIANDIR) + --credential: if yes, credential and security tokens are + calculated according to the binary to interpret + --persistent: if yes, the interpreter is loaded when binfmt is + configured and remains in memory. All future uses + are cloned from the open file. To import templates with update-binfmts, use : @@ -241,7 +250,15 @@ qemu_check_systemd() { } qemu_generate_register() { - echo ":qemu-$cpu:M::$magic:$mask:$qemu:$FLAGS" + flags="" + if [ "$CREDENTIAL" = "yes" ] ; then + flags="OC" + fi + if [ "$PERSISTENT" = "yes" ] ; then + flags="${flags}F" + fi + + echo ":qemu-$cpu:M::$magic:$mask:$qemu:$flags" } qemu_register_interpreter() { @@ -260,10 +277,8 @@ package qemu-$cpu interpreter $qemu magic $magic mask $mask +credential $CREDENTIAL EOF - if [ "$FLAGS" = "OC" ] ; then - echo "credentials yes" >> "$EXPORTDIR/qemu-$cpu" - fi } qemu_set_binfmts() { @@ -287,6 +302,7 @@ qemu_set_binfmts() { qemu="$QEMU_PATH/qemu-i386" fi + qemu="$qemu$QEMU_SUFFIX" if [ "$host_family" != "$family" ] ; then $BINFMT_SET fi @@ -300,9 +316,11 @@ SYSTEMDDIR="/etc/binfmt.d" DEBIANDIR="/usr/share/binfmts" QEMU_PATH=/usr/local/bin -FLAGS="" +CREDENTIAL=no +PERSISTENT=no +QEMU_SUFFIX="" -options=$(getopt -o ds:Q:e:hc: -l debian,systemd:,qemu-path:,exportdir:,help,credential: -- "$@") +options=$(getopt -o ds:Q:S:e:hc:p: -l debian,systemd:,qemu-path:,qemu-suffix:,exportdir:,help,credential:,persistent: -- "$@") eval set -- "$options" while true ; do @@ -338,6 +356,10 @@ while true ; do shift QEMU_PATH="$1" ;; + -F|--qemu-suffix) + shift + QEMU_SUFFIX="$1" + ;; -e|--exportdir) shift EXPORTDIR="$1" @@ -348,11 +370,11 @@ while true ; do ;; -c|--credential) shift - if [ "$1" = "yes" ] ; then - FLAGS="OC" - else - FLAGS="" - fi + CREDENTIAL="$1" + ;; + -p|--persistent) + shift + PERSISTENT="$1" ;; *) break diff --git a/target/openrisc/Makefile.objs b/target/openrisc/Makefile.objs index 1b98a911ea..b5432f4684 100644 --- a/target/openrisc/Makefile.objs +++ b/target/openrisc/Makefile.objs @@ -1,7 +1,7 @@ obj-$(CONFIG_SOFTMMU) += machine.o -obj-y += cpu.o exception.o interrupt.o mmu.o translate.o +obj-y += cpu.o exception.o interrupt.o mmu.o translate.o disas.o obj-y += exception_helper.o fpu_helper.o \ - interrupt_helper.o mmu_helper.o sys_helper.o + interrupt_helper.o sys_helper.o obj-y += gdbstub.o DECODETREE = $(SRC_PATH)/scripts/decodetree.py @@ -12,3 +12,4 @@ target/openrisc/decode.inc.c: \ $(PYTHON) $(DECODETREE) -o $@ $<, "GEN", $(TARGET_DIR)$@) target/openrisc/translate.o: target/openrisc/decode.inc.c +target/openrisc/disas.o: target/openrisc/decode.inc.c diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index a692a98ec0..fb7cb5c507 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -27,6 +27,7 @@ static void openrisc_cpu_set_pc(CPUState *cs, vaddr value) OpenRISCCPU *cpu = OPENRISC_CPU(cs); cpu->env.pc = value; + cpu->env.dflag = 0; } static bool openrisc_cpu_has_work(CPUState *cs) @@ -35,6 +36,11 @@ static bool openrisc_cpu_has_work(CPUState *cs) CPU_INTERRUPT_TIMER); } +static void openrisc_disas_set_info(CPUState *cpu, disassemble_info *info) +{ + info->print_insn = print_insn_or1k; +} + /* CPUClass::reset() */ static void openrisc_cpu_reset(CPUState *s) { @@ -52,8 +58,10 @@ static void openrisc_cpu_reset(CPUState *s) cpu->env.upr = UPR_UP | UPR_DMP | UPR_IMP | UPR_PICP | UPR_TTP | UPR_PMP; - cpu->env.dmmucfgr = (DMMUCFGR_NTW & (0 << 2)) | (DMMUCFGR_NTS & (6 << 2)); - cpu->env.immucfgr = (IMMUCFGR_NTW & (0 << 2)) | (IMMUCFGR_NTS & (6 << 2)); + cpu->env.dmmucfgr = (DMMUCFGR_NTW & (0 << 2)) + | (DMMUCFGR_NTS & (ctz32(TLB_SIZE) << 2)); + cpu->env.immucfgr = (IMMUCFGR_NTW & (0 << 2)) + | (IMMUCFGR_NTS & (ctz32(TLB_SIZE) << 2)); #ifndef CONFIG_USER_ONLY cpu->env.picmr = 0x00000000; @@ -87,10 +95,6 @@ static void openrisc_cpu_initfn(Object *obj) OpenRISCCPU *cpu = OPENRISC_CPU(obj); cs->env_ptr = &cpu->env; - -#ifndef CONFIG_USER_ONLY - cpu_openrisc_mmu_init(cpu); -#endif } /* CPU models */ @@ -152,6 +156,7 @@ static void openrisc_cpu_class_init(ObjectClass *oc, void *data) #endif cc->gdb_num_core_regs = 32 + 3; cc->tcg_initialize = openrisc_translate_init; + cc->disas_set_info = openrisc_disas_set_info; } /* Sort alphabetically by type name, except for "any". */ diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h index 35cab65f11..f1b31bc24a 100644 --- a/target/openrisc/cpu.h +++ b/target/openrisc/cpu.h @@ -222,12 +222,8 @@ enum { /* TLB size */ enum { - DTLB_WAYS = 1, - DTLB_SIZE = 64, - DTLB_MASK = (DTLB_SIZE-1), - ITLB_WAYS = 1, - ITLB_SIZE = 64, - ITLB_MASK = (ITLB_SIZE-1), + TLB_SIZE = 128, + TLB_MASK = TLB_SIZE - 1, }; /* TLB prot */ @@ -241,14 +237,6 @@ enum { UXE = (1 << 7), }; -/* check if tlb available */ -enum { - TLBRET_INVALID = -3, - TLBRET_NOMATCH = -2, - TLBRET_BADADDR = -1, - TLBRET_MATCH = 0 -}; - typedef struct OpenRISCTLBEntry { uint32_t mr; uint32_t tr; @@ -256,8 +244,8 @@ typedef struct OpenRISCTLBEntry { #ifndef CONFIG_USER_ONLY typedef struct CPUOpenRISCTLBContext { - OpenRISCTLBEntry itlb[ITLB_WAYS][ITLB_SIZE]; - OpenRISCTLBEntry dtlb[DTLB_WAYS][DTLB_SIZE]; + OpenRISCTLBEntry itlb[TLB_SIZE]; + OpenRISCTLBEntry dtlb[TLB_SIZE]; int (*cpu_openrisc_map_address_code)(struct OpenRISCCPU *cpu, hwaddr *physical, @@ -301,6 +289,10 @@ typedef struct CPUOpenRISCState { uint32_t dflag; /* In delay slot (boolean) */ +#ifndef CONFIG_USER_ONLY + CPUOpenRISCTLBContext tlb; +#endif + /* Fields up to this point are cleared by a CPU reset */ struct {} end_reset_fields; @@ -310,8 +302,6 @@ typedef struct CPUOpenRISCState { uint32_t cpucfgr; /* CPU configure register */ #ifndef CONFIG_USER_ONLY - CPUOpenRISCTLBContext * tlb; - QEMUTimer *timer; uint32_t ttmr; /* Timer tick mode register */ int is_counting; @@ -358,6 +348,7 @@ void openrisc_translate_init(void); int openrisc_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size, int rw, int mmu_idx); int cpu_openrisc_signal_handler(int host_signum, void *pinfo, void *puc); +int print_insn_or1k(bfd_vma addr, disassemble_info *info); #define cpu_list cpu_openrisc_list #define cpu_signal_handler cpu_openrisc_signal_handler @@ -376,17 +367,6 @@ void cpu_openrisc_count_update(OpenRISCCPU *cpu); void cpu_openrisc_timer_update(OpenRISCCPU *cpu); void cpu_openrisc_count_start(OpenRISCCPU *cpu); void cpu_openrisc_count_stop(OpenRISCCPU *cpu); - -void cpu_openrisc_mmu_init(OpenRISCCPU *cpu); -int cpu_openrisc_get_phys_nommu(OpenRISCCPU *cpu, - hwaddr *physical, - int *prot, target_ulong address, int rw); -int cpu_openrisc_get_phys_code(OpenRISCCPU *cpu, - hwaddr *physical, - int *prot, target_ulong address, int rw); -int cpu_openrisc_get_phys_data(OpenRISCCPU *cpu, - hwaddr *physical, - int *prot, target_ulong address, int rw); #endif #define OPENRISC_CPU_TYPE_SUFFIX "-" TYPE_OPENRISC_CPU @@ -395,9 +375,12 @@ int cpu_openrisc_get_phys_data(OpenRISCCPU *cpu, #include "exec/cpu-all.h" -#define TB_FLAGS_DFLAG 1 -#define TB_FLAGS_R0_0 2 +#define TB_FLAGS_SM SR_SM +#define TB_FLAGS_DME SR_DME +#define TB_FLAGS_IME SR_IME #define TB_FLAGS_OVE SR_OVE +#define TB_FLAGS_DFLAG 2 /* reuse SR_TEE */ +#define TB_FLAGS_R0_0 4 /* reuse SR_IEE */ static inline uint32_t cpu_get_gpr(const CPUOpenRISCState *env, int i) { @@ -415,17 +398,21 @@ static inline void cpu_get_tb_cpu_state(CPUOpenRISCState *env, { *pc = env->pc; *cs_base = 0; - *flags = (env->dflag - | (cpu_get_gpr(env, 0) == 0 ? TB_FLAGS_R0_0 : 0) - | (env->sr & SR_OVE)); + *flags = (env->dflag ? TB_FLAGS_DFLAG : 0) + | (cpu_get_gpr(env, 0) ? 0 : TB_FLAGS_R0_0) + | (env->sr & (SR_SM | SR_DME | SR_IME | SR_OVE)); } static inline int cpu_mmu_index(CPUOpenRISCState *env, bool ifetch) { - if (!(env->sr & SR_IME)) { - return MMU_NOMMU_IDX; + int ret = MMU_NOMMU_IDX; /* mmu is disabled */ + + if (env->sr & (ifetch ? SR_IME : SR_DME)) { + /* The mmu is enabled; test supervisor state. */ + ret = env->sr & SR_SM ? MMU_SUPERVISOR_IDX : MMU_USER_IDX; } - return (env->sr & SR_SM) == 0 ? MMU_USER_IDX : MMU_SUPERVISOR_IDX; + + return ret; } static inline uint32_t cpu_get_sr(const CPUOpenRISCState *env) diff --git a/target/openrisc/disas.c b/target/openrisc/disas.c new file mode 100644 index 0000000000..4bfd2dd8a6 --- /dev/null +++ b/target/openrisc/disas.c @@ -0,0 +1,170 @@ +/* + * OpenRISC disassembler + * + * Copyright (c) 2018 Richard Henderson <rth@twiddle.net> + * + * 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/>. + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "disas/bfd.h" +#include "qemu/bitops.h" +#include "cpu.h" + +typedef disassemble_info DisasContext; + +/* Include the auto-generated decoder. */ +#include "decode.inc.c" + +#define output(mnemonic, format, ...) \ + (info->fprintf_func(info->stream, "%-9s " format, \ + mnemonic, ##__VA_ARGS__)) + +int print_insn_or1k(bfd_vma addr, disassemble_info *info) +{ + bfd_byte buffer[4]; + uint32_t insn; + int status; + + status = info->read_memory_func(addr, buffer, 4, info); + if (status != 0) { + info->memory_error_func(status, addr, info); + return -1; + } + insn = bfd_getb32(buffer); + + if (!decode(info, insn)) { + output(".long", "%#08x", insn); + } + return 4; +} + +#define INSN(opcode, format, ...) \ +static bool trans_l_##opcode(disassemble_info *info, \ + arg_l_##opcode *a, uint32_t insn) \ +{ \ + output("l." #opcode, format, ##__VA_ARGS__); \ + return true; \ +} + +INSN(add, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(addc, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(sub, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(and, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(or, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(xor, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(sll, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(srl, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(sra, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(ror, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(exths, "r%d, r%d", a->d, a->a) +INSN(extbs, "r%d, r%d", a->d, a->a) +INSN(exthz, "r%d, r%d", a->d, a->a) +INSN(extbz, "r%d, r%d", a->d, a->a) +INSN(cmov, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(ff1, "r%d, r%d", a->d, a->a) +INSN(fl1, "r%d, r%d", a->d, a->a) +INSN(mul, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(mulu, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(div, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(divu, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(muld, "r%d, r%d", a->a, a->b) +INSN(muldu, "r%d, r%d", a->a, a->b) +INSN(j, "%d", a->n) +INSN(jal, "%d", a->n) +INSN(bf, "%d", a->n) +INSN(bnf, "%d", a->n) +INSN(jr, "r%d", a->b) +INSN(jalr, "r%d", a->b) +INSN(lwa, "r%d, %d(r%d)", a->d, a->i, a->a) +INSN(lwz, "r%d, %d(r%d)", a->d, a->i, a->a) +INSN(lws, "r%d, %d(r%d)", a->d, a->i, a->a) +INSN(lbz, "r%d, %d(r%d)", a->d, a->i, a->a) +INSN(lbs, "r%d, %d(r%d)", a->d, a->i, a->a) +INSN(lhz, "r%d, %d(r%d)", a->d, a->i, a->a) +INSN(lhs, "r%d, %d(r%d)", a->d, a->i, a->a) +INSN(swa, "%d(r%d), r%d", a->i, a->a, a->b) +INSN(sw, "%d(r%d), r%d", a->i, a->a, a->b) +INSN(sb, "%d(r%d), r%d", a->i, a->a, a->b) +INSN(sh, "%d(r%d), r%d", a->i, a->a, a->b) +INSN(nop, "") +INSN(addi, "r%d, r%d, %d", a->d, a->a, a->i) +INSN(addic, "r%d, r%d, %d", a->d, a->a, a->i) +INSN(muli, "r%d, r%d, %d", a->d, a->a, a->i) +INSN(maci, "r%d, %d", a->a, a->i) +INSN(andi, "r%d, r%d, %d", a->d, a->a, a->k) +INSN(ori, "r%d, r%d, %d", a->d, a->a, a->k) +INSN(xori, "r%d, r%d, %d", a->d, a->a, a->i) +INSN(mfspr, "r%d, r%d, %d", a->d, a->a, a->k) +INSN(mtspr, "r%d, r%d, %d", a->a, a->b, a->k) +INSN(mac, "r%d, r%d", a->a, a->b) +INSN(msb, "r%d, r%d", a->a, a->b) +INSN(macu, "r%d, r%d", a->a, a->b) +INSN(msbu, "r%d, r%d", a->a, a->b) +INSN(slli, "r%d, r%d, %d", a->d, a->a, a->l) +INSN(srli, "r%d, r%d, %d", a->d, a->a, a->l) +INSN(srai, "r%d, r%d, %d", a->d, a->a, a->l) +INSN(rori, "r%d, r%d, %d", a->d, a->a, a->l) +INSN(movhi, "r%d, %d", a->d, a->k) +INSN(macrc, "r%d", a->d) +INSN(sfeq, "r%d, r%d", a->a, a->b) +INSN(sfne, "r%d, r%d", a->a, a->b) +INSN(sfgtu, "r%d, r%d", a->a, a->b) +INSN(sfgeu, "r%d, r%d", a->a, a->b) +INSN(sfltu, "r%d, r%d", a->a, a->b) +INSN(sfleu, "r%d, r%d", a->a, a->b) +INSN(sfgts, "r%d, r%d", a->a, a->b) +INSN(sfges, "r%d, r%d", a->a, a->b) +INSN(sflts, "r%d, r%d", a->a, a->b) +INSN(sfles, "r%d, r%d", a->a, a->b) +INSN(sfeqi, "r%d, %d", a->a, a->i) +INSN(sfnei, "r%d, %d", a->a, a->i) +INSN(sfgtui, "r%d, %d", a->a, a->i) +INSN(sfgeui, "r%d, %d", a->a, a->i) +INSN(sfltui, "r%d, %d", a->a, a->i) +INSN(sfleui, "r%d, %d", a->a, a->i) +INSN(sfgtsi, "r%d, %d", a->a, a->i) +INSN(sfgesi, "r%d, %d", a->a, a->i) +INSN(sfltsi, "r%d, %d", a->a, a->i) +INSN(sflesi, "r%d, %d", a->a, a->i) +INSN(sys, "%d", a->k) +INSN(trap, "%d", a->k) +INSN(msync, "") +INSN(psync, "") +INSN(csync, "") +INSN(rfe, "") + +#define FP_INSN(opcode, suffix, format, ...) \ +static bool trans_lf_##opcode##_##suffix(disassemble_info *info, \ + arg_lf_##opcode##_##suffix *a, uint32_t insn) \ +{ \ + output("lf." #opcode "." #suffix, format, ##__VA_ARGS__); \ + return true; \ +} + +FP_INSN(add, s, "r%d, r%d, r%d", a->d, a->a, a->b) +FP_INSN(sub, s, "r%d, r%d, r%d", a->d, a->a, a->b) +FP_INSN(mul, s, "r%d, r%d, r%d", a->d, a->a, a->b) +FP_INSN(div, s, "r%d, r%d, r%d", a->d, a->a, a->b) +FP_INSN(rem, s, "r%d, r%d, r%d", a->d, a->a, a->b) +FP_INSN(itof, s, "r%d, r%d", a->d, a->a) +FP_INSN(ftoi, s, "r%d, r%d", a->d, a->a) +FP_INSN(madd, s, "r%d, r%d, r%d", a->d, a->a, a->b) +FP_INSN(sfeq, s, "r%d, r%d", a->a, a->b) +FP_INSN(sfne, s, "r%d, r%d", a->a, a->b) +FP_INSN(sfgt, s, "r%d, r%d", a->a, a->b) +FP_INSN(sfge, s, "r%d, r%d", a->a, a->b) +FP_INSN(sflt, s, "r%d, r%d", a->a, a->b) +FP_INSN(sfle, s, "r%d, r%d", a->a, a->b) diff --git a/target/openrisc/helper.h b/target/openrisc/helper.h index e37dabc77a..9db9bf3963 100644 --- a/target/openrisc/helper.h +++ b/target/openrisc/helper.h @@ -56,5 +56,5 @@ FOP_CMP(le) DEF_HELPER_FLAGS_1(rfe, 0, void, env) /* sys */ -DEF_HELPER_FLAGS_4(mtspr, 0, void, env, tl, tl, tl) -DEF_HELPER_FLAGS_4(mfspr, TCG_CALL_NO_WG, tl, env, tl, tl, tl) +DEF_HELPER_FLAGS_3(mtspr, 0, void, env, tl, tl) +DEF_HELPER_FLAGS_3(mfspr, TCG_CALL_NO_WG, tl, env, tl, tl) diff --git a/target/openrisc/interrupt.c b/target/openrisc/interrupt.c index 3959671c59..bbae956361 100644 --- a/target/openrisc/interrupt.c +++ b/target/openrisc/interrupt.c @@ -32,29 +32,22 @@ void openrisc_cpu_do_interrupt(CPUState *cs) #ifndef CONFIG_USER_ONLY OpenRISCCPU *cpu = OPENRISC_CPU(cs); CPUOpenRISCState *env = &cpu->env; + int exception = cs->exception_index; env->epcr = env->pc; - if (env->dflag) { - env->dflag = 0; - env->sr |= SR_DSX; - env->epcr -= 4; - } else { - env->sr &= ~SR_DSX; - } - if (cs->exception_index == EXCP_SYSCALL) { + if (exception == EXCP_SYSCALL) { env->epcr += 4; } /* When we have an illegal instruction the error effective address shall be set to the illegal instruction address. */ - if (cs->exception_index == EXCP_ILLEGAL) { + if (exception == EXCP_ILLEGAL) { env->eear = env->pc; } - /* For machine-state changed between user-mode and supervisor mode, - we need flush TLB when we enter&exit EXCP. */ - tlb_flush(cs); - + /* During exceptions esr is populared with the pre-exception sr. */ env->esr = cpu_get_sr(env); + /* In parallel sr is updated to disable mmu, interrupts, timers and + set the delay slot exception flag. */ env->sr &= ~SR_DME; env->sr &= ~SR_IME; env->sr |= SR_SM; @@ -62,12 +55,38 @@ void openrisc_cpu_do_interrupt(CPUState *cs) env->sr &= ~SR_TEE; env->pmr &= ~PMR_DME; env->pmr &= ~PMR_SME; - env->tlb->cpu_openrisc_map_address_data = &cpu_openrisc_get_phys_nommu; - env->tlb->cpu_openrisc_map_address_code = &cpu_openrisc_get_phys_nommu; env->lock_addr = -1; - if (cs->exception_index > 0 && cs->exception_index < EXCP_NR) { - hwaddr vect_pc = cs->exception_index << 8; + /* Set/clear dsx to indicate if we are in a delay slot exception. */ + if (env->dflag) { + env->dflag = 0; + env->sr |= SR_DSX; + env->epcr -= 4; + } else { + env->sr &= ~SR_DSX; + } + + if (exception > 0 && exception < EXCP_NR) { + static const char * const int_name[EXCP_NR] = { + [EXCP_RESET] = "RESET", + [EXCP_BUSERR] = "BUSERR (bus error)", + [EXCP_DPF] = "DFP (data protection fault)", + [EXCP_IPF] = "IPF (code protection fault)", + [EXCP_TICK] = "TICK (timer interrupt)", + [EXCP_ALIGN] = "ALIGN", + [EXCP_ILLEGAL] = "ILLEGAL", + [EXCP_INT] = "INT (device interrupt)", + [EXCP_DTLBMISS] = "DTLBMISS (data tlb miss)", + [EXCP_ITLBMISS] = "ITLBMISS (code tlb miss)", + [EXCP_RANGE] = "RANGE", + [EXCP_SYSCALL] = "SYSCALL", + [EXCP_FPE] = "FPE", + [EXCP_TRAP] = "TRAP", + }; + + qemu_log_mask(CPU_LOG_INT, "INT: %s\n", int_name[exception]); + + hwaddr vect_pc = exception << 8; if (env->cpucfgr & CPUCFGR_EVBARP) { vect_pc |= env->evbar; } @@ -76,7 +95,7 @@ void openrisc_cpu_do_interrupt(CPUState *cs) } env->pc = vect_pc; } else { - cpu_abort(cs, "Unhandled exception 0x%x\n", cs->exception_index); + cpu_abort(cs, "Unhandled exception 0x%x\n", exception); } #endif diff --git a/target/openrisc/interrupt_helper.c b/target/openrisc/interrupt_helper.c index 56620e0571..9c5489f5f7 100644 --- a/target/openrisc/interrupt_helper.c +++ b/target/openrisc/interrupt_helper.c @@ -25,36 +25,7 @@ void HELPER(rfe)(CPUOpenRISCState *env) { - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); - CPUState *cs = CPU(cpu); -#ifndef CONFIG_USER_ONLY - int need_flush_tlb = (cpu->env.sr & (SR_SM | SR_IME | SR_DME)) ^ - (cpu->env.esr & (SR_SM | SR_IME | SR_DME)); -#endif - cpu->env.pc = cpu->env.epcr; - cpu_set_sr(&cpu->env, cpu->env.esr); - cpu->env.lock_addr = -1; - -#ifndef CONFIG_USER_ONLY - if (cpu->env.sr & SR_DME) { - cpu->env.tlb->cpu_openrisc_map_address_data = - &cpu_openrisc_get_phys_data; - } else { - cpu->env.tlb->cpu_openrisc_map_address_data = - &cpu_openrisc_get_phys_nommu; - } - - if (cpu->env.sr & SR_IME) { - cpu->env.tlb->cpu_openrisc_map_address_code = - &cpu_openrisc_get_phys_code; - } else { - cpu->env.tlb->cpu_openrisc_map_address_code = - &cpu_openrisc_get_phys_nommu; - } - - if (need_flush_tlb) { - tlb_flush(cs); - } -#endif - cs->interrupt_request |= CPU_INTERRUPT_EXITTB; + env->pc = env->epcr; + env->lock_addr = -1; + cpu_set_sr(env, env->esr); } diff --git a/target/openrisc/machine.c b/target/openrisc/machine.c index 0a793eb14d..1eedbf3dbe 100644 --- a/target/openrisc/machine.c +++ b/target/openrisc/machine.c @@ -24,31 +24,6 @@ #include "hw/boards.h" #include "migration/cpu.h" -static int env_post_load(void *opaque, int version_id) -{ - CPUOpenRISCState *env = opaque; - - /* Restore MMU handlers */ - if (env->sr & SR_DME) { - env->tlb->cpu_openrisc_map_address_data = - &cpu_openrisc_get_phys_data; - } else { - env->tlb->cpu_openrisc_map_address_data = - &cpu_openrisc_get_phys_nommu; - } - - if (env->sr & SR_IME) { - env->tlb->cpu_openrisc_map_address_code = - &cpu_openrisc_get_phys_code; - } else { - env->tlb->cpu_openrisc_map_address_code = - &cpu_openrisc_get_phys_nommu; - } - - - return 0; -} - static const VMStateDescription vmstate_tlb_entry = { .name = "tlb_entry", .version_id = 1, @@ -63,24 +38,17 @@ static const VMStateDescription vmstate_tlb_entry = { static const VMStateDescription vmstate_cpu_tlb = { .name = "cpu_tlb", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (VMStateField[]) { - VMSTATE_STRUCT_2DARRAY(itlb, CPUOpenRISCTLBContext, - ITLB_WAYS, ITLB_SIZE, 0, + VMSTATE_STRUCT_ARRAY(itlb, CPUOpenRISCTLBContext, TLB_SIZE, 0, vmstate_tlb_entry, OpenRISCTLBEntry), - VMSTATE_STRUCT_2DARRAY(dtlb, CPUOpenRISCTLBContext, - DTLB_WAYS, DTLB_SIZE, 0, + VMSTATE_STRUCT_ARRAY(dtlb, CPUOpenRISCTLBContext, TLB_SIZE, 0, vmstate_tlb_entry, OpenRISCTLBEntry), VMSTATE_END_OF_LIST() } }; -#define VMSTATE_CPU_TLB(_f, _s) \ - VMSTATE_STRUCT_POINTER(_f, _s, vmstate_cpu_tlb, CPUOpenRISCTLBContext) - - static int get_sr(QEMUFile *f, void *opaque, size_t size, VMStateField *field) { CPUOpenRISCState *env = opaque; @@ -106,7 +74,6 @@ static const VMStateDescription vmstate_env = { .name = "env", .version_id = 6, .minimum_version_id = 6, - .post_load = env_post_load, .fields = (VMStateField[]) { VMSTATE_UINTTL_2DARRAY(shadow_gpr, CPUOpenRISCState, 16, 32), VMSTATE_UINTTL(pc, CPUOpenRISCState), @@ -143,7 +110,8 @@ static const VMStateDescription vmstate_env = { VMSTATE_UINT32(fpcsr, CPUOpenRISCState), VMSTATE_UINT64(mac, CPUOpenRISCState), - VMSTATE_CPU_TLB(tlb, CPUOpenRISCState), + VMSTATE_STRUCT(tlb, CPUOpenRISCState, 1, + vmstate_cpu_tlb, CPUOpenRISCTLBContext), VMSTATE_TIMER_PTR(timer, CPUOpenRISCState), VMSTATE_UINT32(ttmr, CPUOpenRISCState), diff --git a/target/openrisc/mmu.c b/target/openrisc/mmu.c index 2bd782f89b..e7d5219e11 100644 --- a/target/openrisc/mmu.c +++ b/target/openrisc/mmu.c @@ -29,227 +29,156 @@ #endif #ifndef CONFIG_USER_ONLY -int cpu_openrisc_get_phys_nommu(OpenRISCCPU *cpu, - hwaddr *physical, - int *prot, target_ulong address, int rw) +static inline void get_phys_nommu(hwaddr *phys_addr, int *prot, + target_ulong address) { - *physical = address; + *phys_addr = address; *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; - return TLBRET_MATCH; } -int cpu_openrisc_get_phys_code(OpenRISCCPU *cpu, - hwaddr *physical, - int *prot, target_ulong address, int rw) +static int get_phys_mmu(OpenRISCCPU *cpu, hwaddr *phys_addr, int *prot, + target_ulong addr, int need, bool super) { - int vpn = address >> TARGET_PAGE_BITS; - int idx = vpn & ITLB_MASK; - int right = 0; - - if ((cpu->env.tlb->itlb[0][idx].mr >> TARGET_PAGE_BITS) != vpn) { - return TLBRET_NOMATCH; - } - if (!(cpu->env.tlb->itlb[0][idx].mr & 1)) { - return TLBRET_INVALID; - } - - if (cpu->env.sr & SR_SM) { /* supervisor mode */ - if (cpu->env.tlb->itlb[0][idx].tr & SXE) { - right |= PAGE_EXEC; - } - } else { - if (cpu->env.tlb->itlb[0][idx].tr & UXE) { - right |= PAGE_EXEC; + int idx = (addr >> TARGET_PAGE_BITS) & TLB_MASK; + uint32_t imr = cpu->env.tlb.itlb[idx].mr; + uint32_t itr = cpu->env.tlb.itlb[idx].tr; + uint32_t dmr = cpu->env.tlb.dtlb[idx].mr; + uint32_t dtr = cpu->env.tlb.dtlb[idx].tr; + int right, match, valid; + + /* If the ITLB and DTLB indexes map to the same page, we want to + load all permissions all at once. If the destination pages do + not match, zap the one we don't need. */ + if (unlikely((itr ^ dtr) & TARGET_PAGE_MASK)) { + if (need & PAGE_EXEC) { + dmr = dtr = 0; + } else { + imr = itr = 0; } } - if ((rw & 2) && ((right & PAGE_EXEC) == 0)) { - return TLBRET_BADADDR; - } - - *physical = (cpu->env.tlb->itlb[0][idx].tr & TARGET_PAGE_MASK) | - (address & (TARGET_PAGE_SIZE-1)); + /* Check if either of the entries matches the source address. */ + match = (imr ^ addr) & TARGET_PAGE_MASK ? 0 : PAGE_EXEC; + match |= (dmr ^ addr) & TARGET_PAGE_MASK ? 0 : PAGE_READ | PAGE_WRITE; + + /* Check if either of the entries is valid. */ + valid = imr & 1 ? PAGE_EXEC : 0; + valid |= dmr & 1 ? PAGE_READ | PAGE_WRITE : 0; + valid &= match; + + /* Collect the permissions from the entries. */ + right = itr & (super ? SXE : UXE) ? PAGE_EXEC : 0; + right |= dtr & (super ? SRE : URE) ? PAGE_READ : 0; + right |= dtr & (super ? SWE : UWE) ? PAGE_WRITE : 0; + right &= valid; + + /* Note that above we validated that itr and dtr match on page. + So oring them together changes nothing without having to + check which one we needed. We also want to store to these + variables even on failure, as it avoids compiler warnings. */ + *phys_addr = ((itr | dtr) & TARGET_PAGE_MASK) | (addr & ~TARGET_PAGE_MASK); *prot = right; - return TLBRET_MATCH; -} -int cpu_openrisc_get_phys_data(OpenRISCCPU *cpu, - hwaddr *physical, - int *prot, target_ulong address, int rw) -{ - int vpn = address >> TARGET_PAGE_BITS; - int idx = vpn & DTLB_MASK; - int right = 0; + qemu_log_mask(CPU_LOG_MMU, + "MMU lookup: need %d match %d valid %d right %d -> %s\n", + need, match, valid, right, (need & right) ? "OK" : "FAIL"); - if ((cpu->env.tlb->dtlb[0][idx].mr >> TARGET_PAGE_BITS) != vpn) { - return TLBRET_NOMATCH; - } - if (!(cpu->env.tlb->dtlb[0][idx].mr & 1)) { - return TLBRET_INVALID; + /* Check the collective permissions are present. */ + if (likely(need & right)) { + return 0; /* success! */ } - if (cpu->env.sr & SR_SM) { /* supervisor mode */ - if (cpu->env.tlb->dtlb[0][idx].tr & SRE) { - right |= PAGE_READ; - } - if (cpu->env.tlb->dtlb[0][idx].tr & SWE) { - right |= PAGE_WRITE; - } + /* Determine what kind of failure we have. */ + if (need & valid) { + return need & PAGE_EXEC ? EXCP_IPF : EXCP_DPF; } else { - if (cpu->env.tlb->dtlb[0][idx].tr & URE) { - right |= PAGE_READ; - } - if (cpu->env.tlb->dtlb[0][idx].tr & UWE) { - right |= PAGE_WRITE; - } + return need & PAGE_EXEC ? EXCP_ITLBMISS : EXCP_DTLBMISS; } - - if (!(rw & 1) && ((right & PAGE_READ) == 0)) { - return TLBRET_BADADDR; - } - if ((rw & 1) && ((right & PAGE_WRITE) == 0)) { - return TLBRET_BADADDR; - } - - *physical = (cpu->env.tlb->dtlb[0][idx].tr & TARGET_PAGE_MASK) | - (address & (TARGET_PAGE_SIZE-1)); - *prot = right; - return TLBRET_MATCH; -} - -static int cpu_openrisc_get_phys_addr(OpenRISCCPU *cpu, - hwaddr *physical, - int *prot, target_ulong address, - int rw) -{ - int ret = TLBRET_MATCH; - - if (rw == MMU_INST_FETCH) { /* ITLB */ - *physical = 0; - ret = cpu->env.tlb->cpu_openrisc_map_address_code(cpu, physical, - prot, address, rw); - } else { /* DTLB */ - ret = cpu->env.tlb->cpu_openrisc_map_address_data(cpu, physical, - prot, address, rw); - } - - return ret; } #endif -static void cpu_openrisc_raise_mmu_exception(OpenRISCCPU *cpu, - target_ulong address, - int rw, int tlb_error) +static void raise_mmu_exception(OpenRISCCPU *cpu, target_ulong address, + int exception) { CPUState *cs = CPU(cpu); - int exception = 0; - - switch (tlb_error) { - default: - if (rw == 2) { - exception = EXCP_IPF; - } else { - exception = EXCP_DPF; - } - break; -#ifndef CONFIG_USER_ONLY - case TLBRET_BADADDR: - if (rw == 2) { - exception = EXCP_IPF; - } else { - exception = EXCP_DPF; - } - break; - case TLBRET_INVALID: - case TLBRET_NOMATCH: - /* No TLB match for a mapped address */ - if (rw == 2) { - exception = EXCP_ITLBMISS; - } else { - exception = EXCP_DTLBMISS; - } - break; -#endif - } cs->exception_index = exception; cpu->env.eear = address; cpu->env.lock_addr = -1; } -#ifndef CONFIG_USER_ONLY int openrisc_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, int rw, int mmu_idx) { +#ifdef CONFIG_USER_ONLY OpenRISCCPU *cpu = OPENRISC_CPU(cs); - int ret = 0; - hwaddr physical = 0; - int prot = 0; - - ret = cpu_openrisc_get_phys_addr(cpu, &physical, &prot, - address, rw); - - if (ret == TLBRET_MATCH) { - tlb_set_page(cs, address & TARGET_PAGE_MASK, - physical & TARGET_PAGE_MASK, prot, - mmu_idx, TARGET_PAGE_SIZE); - ret = 0; - } else if (ret < 0) { - cpu_openrisc_raise_mmu_exception(cpu, address, rw, ret); - ret = 1; - } - - return ret; -} + raise_mmu_exception(cpu, address, EXCP_DPF); + return 1; #else -int openrisc_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, - int rw, int mmu_idx) -{ - OpenRISCCPU *cpu = OPENRISC_CPU(cs); - int ret = 0; - - cpu_openrisc_raise_mmu_exception(cpu, address, rw, ret); - ret = 1; - - return ret; -} + g_assert_not_reached(); #endif +} #ifndef CONFIG_USER_ONLY hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { OpenRISCCPU *cpu = OPENRISC_CPU(cs); + int prot, excp, sr = cpu->env.sr; hwaddr phys_addr; - int prot; - int miss; - /* Check memory for any kind of address, since during debug the - gdb can ask for anything, check data tlb for address */ - miss = cpu_openrisc_get_phys_addr(cpu, &phys_addr, &prot, addr, 0); - - /* Check instruction tlb */ - if (miss) { - miss = cpu_openrisc_get_phys_addr(cpu, &phys_addr, &prot, addr, - MMU_INST_FETCH); - } + switch (sr & (SR_DME | SR_IME)) { + case SR_DME | SR_IME: + /* The mmu is definitely enabled. */ + excp = get_phys_mmu(cpu, &phys_addr, &prot, addr, + PAGE_EXEC | PAGE_READ | PAGE_WRITE, + (sr & SR_SM) != 0); + return excp ? -1 : phys_addr; - /* Last, fall back to a plain address */ - if (miss) { - miss = cpu_openrisc_get_phys_nommu(cpu, &phys_addr, &prot, addr, 0); - } + default: + /* The mmu is partially enabled, and we don't really have + a "real" access type. Begin by trying the mmu, but if + that fails try again without. */ + excp = get_phys_mmu(cpu, &phys_addr, &prot, addr, + PAGE_EXEC | PAGE_READ | PAGE_WRITE, + (sr & SR_SM) != 0); + if (!excp) { + return phys_addr; + } + /* fallthru */ - if (miss) { - return -1; - } else { + case 0: + /* The mmu is definitely disabled; lookups never fail. */ + get_phys_nommu(&phys_addr, &prot, addr); return phys_addr; } } -void cpu_openrisc_mmu_init(OpenRISCCPU *cpu) +void tlb_fill(CPUState *cs, target_ulong addr, int size, + MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) { - cpu->env.tlb = g_malloc0(sizeof(CPUOpenRISCTLBContext)); + OpenRISCCPU *cpu = OPENRISC_CPU(cs); + int prot, excp; + hwaddr phys_addr; + + if (mmu_idx == MMU_NOMMU_IDX) { + /* The mmu is disabled; lookups never fail. */ + get_phys_nommu(&phys_addr, &prot, addr); + excp = 0; + } else { + bool super = mmu_idx == MMU_SUPERVISOR_IDX; + int need = (access_type == MMU_INST_FETCH ? PAGE_EXEC + : access_type == MMU_DATA_STORE ? PAGE_WRITE + : PAGE_READ); + excp = get_phys_mmu(cpu, &phys_addr, &prot, addr, need, super); + } + + if (unlikely(excp)) { + raise_mmu_exception(cpu, addr, excp); + cpu_loop_exit_restore(cs, retaddr); + } - cpu->env.tlb->cpu_openrisc_map_address_code = &cpu_openrisc_get_phys_nommu; - cpu->env.tlb->cpu_openrisc_map_address_data = &cpu_openrisc_get_phys_nommu; + tlb_set_page(cs, addr & TARGET_PAGE_MASK, + phys_addr & TARGET_PAGE_MASK, prot, + mmu_idx, TARGET_PAGE_SIZE); } #endif diff --git a/target/openrisc/mmu_helper.c b/target/openrisc/mmu_helper.c deleted file mode 100644 index 97e1d17b5a..0000000000 --- a/target/openrisc/mmu_helper.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * OpenRISC MMU helper routines - * - * Copyright (c) 2011-2012 Jia Liu <proljc@gmail.com> - * Zhizhou Zhang <etouzh@gmail.com> - * - * 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 "cpu.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" - -#ifndef CONFIG_USER_ONLY - -void tlb_fill(CPUState *cs, target_ulong addr, int size, - MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) -{ - int ret; - - ret = openrisc_cpu_handle_mmu_fault(cs, addr, size, access_type, mmu_idx); - - if (ret) { - /* Raise Exception. */ - cpu_loop_exit_restore(cs, retaddr); - } -} -#endif diff --git a/target/openrisc/sys_helper.c b/target/openrisc/sys_helper.c index b284064381..b66a45c1e0 100644 --- a/target/openrisc/sys_helper.c +++ b/target/openrisc/sys_helper.c @@ -27,13 +27,12 @@ #define TO_SPR(group, number) (((group) << 11) + (number)) -void HELPER(mtspr)(CPUOpenRISCState *env, - target_ulong ra, target_ulong rb, target_ulong offset) +void HELPER(mtspr)(CPUOpenRISCState *env, target_ulong spr, target_ulong rb) { #ifndef CONFIG_USER_ONLY OpenRISCCPU *cpu = openrisc_env_get_cpu(env); CPUState *cs = CPU(cpu); - int spr = (ra | offset); + target_ulong mr; int idx; switch (spr) { @@ -57,26 +56,7 @@ void HELPER(mtspr)(CPUOpenRISCState *env, break; case TO_SPR(0, 17): /* SR */ - if ((env->sr & (SR_IME | SR_DME | SR_SM)) ^ - (rb & (SR_IME | SR_DME | SR_SM))) { - tlb_flush(cs); - } cpu_set_sr(env, rb); - if (env->sr & SR_DME) { - env->tlb->cpu_openrisc_map_address_data = - &cpu_openrisc_get_phys_data; - } else { - env->tlb->cpu_openrisc_map_address_data = - &cpu_openrisc_get_phys_nommu; - } - - if (env->sr & SR_IME) { - env->tlb->cpu_openrisc_map_address_code = - &cpu_openrisc_get_phys_code; - } else { - env->tlb->cpu_openrisc_map_address_code = - &cpu_openrisc_get_phys_nommu; - } break; case TO_SPR(0, 18): /* PPC */ @@ -98,18 +78,22 @@ void HELPER(mtspr)(CPUOpenRISCState *env, case TO_SPR(0, 1024) ... TO_SPR(0, 1024 + (16 * 32)): /* Shadow GPRs */ idx = (spr - 1024); env->shadow_gpr[idx / 32][idx % 32] = rb; + break; - case TO_SPR(1, 512) ... TO_SPR(1, 512+DTLB_SIZE-1): /* DTLBW0MR 0-127 */ + case TO_SPR(1, 512) ... TO_SPR(1, 512 + TLB_SIZE - 1): /* DTLBW0MR 0-127 */ idx = spr - TO_SPR(1, 512); - if (!(rb & 1)) { - tlb_flush_page(cs, env->tlb->dtlb[0][idx].mr & TARGET_PAGE_MASK); + mr = env->tlb.dtlb[idx].mr; + if (mr & 1) { + tlb_flush_page(cs, mr & TARGET_PAGE_MASK); + } + if (rb & 1) { + tlb_flush_page(cs, rb & TARGET_PAGE_MASK); } - env->tlb->dtlb[0][idx].mr = rb; + env->tlb.dtlb[idx].mr = rb; break; - - case TO_SPR(1, 640) ... TO_SPR(1, 640+DTLB_SIZE-1): /* DTLBW0TR 0-127 */ + case TO_SPR(1, 640) ... TO_SPR(1, 640 + TLB_SIZE - 1): /* DTLBW0TR 0-127 */ idx = spr - TO_SPR(1, 640); - env->tlb->dtlb[0][idx].tr = rb; + env->tlb.dtlb[idx].tr = rb; break; case TO_SPR(1, 768) ... TO_SPR(1, 895): /* DTLBW1MR 0-127 */ case TO_SPR(1, 896) ... TO_SPR(1, 1023): /* DTLBW1TR 0-127 */ @@ -118,17 +102,21 @@ void HELPER(mtspr)(CPUOpenRISCState *env, case TO_SPR(1, 1280) ... TO_SPR(1, 1407): /* DTLBW3MR 0-127 */ case TO_SPR(1, 1408) ... TO_SPR(1, 1535): /* DTLBW3TR 0-127 */ break; - case TO_SPR(2, 512) ... TO_SPR(2, 512+ITLB_SIZE-1): /* ITLBW0MR 0-127 */ + + case TO_SPR(2, 512) ... TO_SPR(2, 512 + TLB_SIZE - 1): /* ITLBW0MR 0-127 */ idx = spr - TO_SPR(2, 512); - if (!(rb & 1)) { - tlb_flush_page(cs, env->tlb->itlb[0][idx].mr & TARGET_PAGE_MASK); + mr = env->tlb.itlb[idx].mr; + if (mr & 1) { + tlb_flush_page(cs, mr & TARGET_PAGE_MASK); + } + if (rb & 1) { + tlb_flush_page(cs, rb & TARGET_PAGE_MASK); } - env->tlb->itlb[0][idx].mr = rb; + env->tlb.itlb[idx].mr = rb; break; - - case TO_SPR(2, 640) ... TO_SPR(2, 640+ITLB_SIZE-1): /* ITLBW0TR 0-127 */ + case TO_SPR(2, 640) ... TO_SPR(2, 640 + TLB_SIZE - 1): /* ITLBW0TR 0-127 */ idx = spr - TO_SPR(2, 640); - env->tlb->itlb[0][idx].tr = rb; + env->tlb.itlb[idx].tr = rb; break; case TO_SPR(2, 768) ... TO_SPR(2, 895): /* ITLBW1MR 0-127 */ case TO_SPR(2, 896) ... TO_SPR(2, 1023): /* ITLBW1TR 0-127 */ @@ -137,6 +125,7 @@ void HELPER(mtspr)(CPUOpenRISCState *env, case TO_SPR(2, 1280) ... TO_SPR(2, 1407): /* ITLBW3MR 0-127 */ case TO_SPR(2, 1408) ... TO_SPR(2, 1535): /* ITLBW3TR 0-127 */ break; + case TO_SPR(5, 1): /* MACLO */ env->mac = deposit64(env->mac, 0, 32, rb); break; @@ -153,7 +142,7 @@ void HELPER(mtspr)(CPUOpenRISCState *env, } break; case TO_SPR(9, 0): /* PICMR */ - env->picmr |= rb; + env->picmr = rb; break; case TO_SPR(9, 2): /* PICSR */ env->picsr &= ~rb; @@ -201,13 +190,12 @@ void HELPER(mtspr)(CPUOpenRISCState *env, #endif } -target_ulong HELPER(mfspr)(CPUOpenRISCState *env, - target_ulong rd, target_ulong ra, uint32_t offset) +target_ulong HELPER(mfspr)(CPUOpenRISCState *env, target_ulong rd, + target_ulong spr) { #ifndef CONFIG_USER_ONLY OpenRISCCPU *cpu = openrisc_env_get_cpu(env); CPUState *cs = CPU(cpu); - int spr = (ra | offset); int idx; switch (spr) { @@ -259,13 +247,13 @@ target_ulong HELPER(mfspr)(CPUOpenRISCState *env, idx = (spr - 1024); return env->shadow_gpr[idx / 32][idx % 32]; - case TO_SPR(1, 512) ... TO_SPR(1, 512+DTLB_SIZE-1): /* DTLBW0MR 0-127 */ + case TO_SPR(1, 512) ... TO_SPR(1, 512 + TLB_SIZE - 1): /* DTLBW0MR 0-127 */ idx = spr - TO_SPR(1, 512); - return env->tlb->dtlb[0][idx].mr; + return env->tlb.dtlb[idx].mr; - case TO_SPR(1, 640) ... TO_SPR(1, 640+DTLB_SIZE-1): /* DTLBW0TR 0-127 */ + case TO_SPR(1, 640) ... TO_SPR(1, 640 + TLB_SIZE - 1): /* DTLBW0TR 0-127 */ idx = spr - TO_SPR(1, 640); - return env->tlb->dtlb[0][idx].tr; + return env->tlb.dtlb[idx].tr; case TO_SPR(1, 768) ... TO_SPR(1, 895): /* DTLBW1MR 0-127 */ case TO_SPR(1, 896) ... TO_SPR(1, 1023): /* DTLBW1TR 0-127 */ @@ -275,13 +263,13 @@ target_ulong HELPER(mfspr)(CPUOpenRISCState *env, case TO_SPR(1, 1408) ... TO_SPR(1, 1535): /* DTLBW3TR 0-127 */ break; - case TO_SPR(2, 512) ... TO_SPR(2, 512+ITLB_SIZE-1): /* ITLBW0MR 0-127 */ + case TO_SPR(2, 512) ... TO_SPR(2, 512 + TLB_SIZE - 1): /* ITLBW0MR 0-127 */ idx = spr - TO_SPR(2, 512); - return env->tlb->itlb[0][idx].mr; + return env->tlb.itlb[idx].mr; - case TO_SPR(2, 640) ... TO_SPR(2, 640+ITLB_SIZE-1): /* ITLBW0TR 0-127 */ + case TO_SPR(2, 640) ... TO_SPR(2, 640 + TLB_SIZE - 1): /* ITLBW0TR 0-127 */ idx = spr - TO_SPR(2, 640); - return env->tlb->itlb[0][idx].tr; + return env->tlb.itlb[idx].tr; case TO_SPR(2, 768) ... TO_SPR(2, 895): /* ITLBW1MR 0-127 */ case TO_SPR(2, 896) ... TO_SPR(2, 1023): /* ITLBW1TR 0-127 */ diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index d69f8d0422..a271cd3903 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -36,22 +36,29 @@ #include "trace-tcg.h" #include "exec/log.h" -#define LOG_DIS(str, ...) \ - qemu_log_mask(CPU_LOG_TB_IN_ASM, "%08x: " str, dc->base.pc_next, \ - ## __VA_ARGS__) - /* is_jmp field values */ -#define DISAS_JUMP DISAS_TARGET_0 /* only pc was modified dynamically */ -#define DISAS_UPDATE DISAS_TARGET_1 /* cpu state was modified dynamically */ -#define DISAS_TB_JUMP DISAS_TARGET_2 /* only pc was modified statically */ +#define DISAS_EXIT DISAS_TARGET_0 /* force exit to main loop */ +#define DISAS_JUMP DISAS_TARGET_1 /* exit via jmp_pc/jmp_pc_imm */ typedef struct DisasContext { DisasContextBase base; uint32_t mem_idx; uint32_t tb_flags; uint32_t delayed_branch; + + /* If not -1, jmp_pc contains this value and so is a direct jump. */ + target_ulong jmp_pc_imm; } DisasContext; +static inline bool is_user(DisasContext *dc) +{ +#ifdef CONFIG_USER_ONLY + return true; +#else + return !(dc->tb_flags & TB_FLAGS_SM); +#endif +} + /* Include the auto-generated decoder. */ #include "decode.inc.c" @@ -165,34 +172,6 @@ static void check_ov64s(DisasContext *dc) } \ } while (0) -static inline bool use_goto_tb(DisasContext *dc, target_ulong dest) -{ - if (unlikely(dc->base.singlestep_enabled)) { - return false; - } - -#ifndef CONFIG_USER_ONLY - return (dc->base.tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); -#else - return true; -#endif -} - -static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) -{ - if (use_goto_tb(dc, dest)) { - tcg_gen_movi_tl(cpu_pc, dest); - tcg_gen_goto_tb(n); - tcg_gen_exit_tb(dc->base.tb, n); - } else { - tcg_gen_movi_tl(cpu_pc, dest); - if (dc->base.singlestep_enabled) { - gen_exception(dc, EXCP_DEBUG); - } - tcg_gen_exit_tb(NULL, 0); - } -} - static void gen_ove_cy(DisasContext *dc) { if (dc->tb_flags & SR_OVE) { @@ -457,7 +436,6 @@ static void gen_msbu(DisasContext *dc, TCGv srca, TCGv srcb) static bool trans_l_add(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("l.add r%d, r%d, r%d\n", a->d, a->a, a->b); check_r0_write(a->d); gen_add(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; @@ -465,7 +443,6 @@ static bool trans_l_add(DisasContext *dc, arg_dab *a, uint32_t insn) static bool trans_l_addc(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("l.addc r%d, r%d, r%d\n", a->d, a->a, a->b); check_r0_write(a->d); gen_addc(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; @@ -473,7 +450,6 @@ static bool trans_l_addc(DisasContext *dc, arg_dab *a, uint32_t insn) static bool trans_l_sub(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("l.sub r%d, r%d, r%d\n", a->d, a->a, a->b); check_r0_write(a->d); gen_sub(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; @@ -481,7 +457,6 @@ static bool trans_l_sub(DisasContext *dc, arg_dab *a, uint32_t insn) static bool trans_l_and(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("l.and r%d, r%d, r%d\n", a->d, a->a, a->b); check_r0_write(a->d); tcg_gen_and_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; @@ -489,7 +464,6 @@ static bool trans_l_and(DisasContext *dc, arg_dab *a, uint32_t insn) static bool trans_l_or(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("l.or r%d, r%d, r%d\n", a->d, a->a, a->b); check_r0_write(a->d); tcg_gen_or_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; @@ -497,7 +471,6 @@ static bool trans_l_or(DisasContext *dc, arg_dab *a, uint32_t insn) static bool trans_l_xor(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("l.xor r%d, r%d, r%d\n", a->d, a->a, a->b); check_r0_write(a->d); tcg_gen_xor_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; @@ -505,7 +478,6 @@ static bool trans_l_xor(DisasContext *dc, arg_dab *a, uint32_t insn) static bool trans_l_sll(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("l.sll r%d, r%d, r%d\n", a->d, a->a, a->b); check_r0_write(a->d); tcg_gen_shl_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; @@ -513,7 +485,6 @@ static bool trans_l_sll(DisasContext *dc, arg_dab *a, uint32_t insn) static bool trans_l_srl(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("l.srl r%d, r%d, r%d\n", a->d, a->a, a->b); check_r0_write(a->d); tcg_gen_shr_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; @@ -521,7 +492,6 @@ static bool trans_l_srl(DisasContext *dc, arg_dab *a, uint32_t insn) static bool trans_l_sra(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("l.sra r%d, r%d, r%d\n", a->d, a->a, a->b); check_r0_write(a->d); tcg_gen_sar_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; @@ -529,7 +499,6 @@ static bool trans_l_sra(DisasContext *dc, arg_dab *a, uint32_t insn) static bool trans_l_ror(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("l.ror r%d, r%d, r%d\n", a->d, a->a, a->b); check_r0_write(a->d); tcg_gen_rotr_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; @@ -537,7 +506,6 @@ static bool trans_l_ror(DisasContext *dc, arg_dab *a, uint32_t insn) static bool trans_l_exths(DisasContext *dc, arg_da *a, uint32_t insn) { - LOG_DIS("l.exths r%d, r%d\n", a->d, a->a); check_r0_write(a->d); tcg_gen_ext16s_tl(cpu_R[a->d], cpu_R[a->a]); return true; @@ -545,7 +513,6 @@ static bool trans_l_exths(DisasContext *dc, arg_da *a, uint32_t insn) static bool trans_l_extbs(DisasContext *dc, arg_da *a, uint32_t insn) { - LOG_DIS("l.extbs r%d, r%d\n", a->d, a->a); check_r0_write(a->d); tcg_gen_ext8s_tl(cpu_R[a->d], cpu_R[a->a]); return true; @@ -553,7 +520,6 @@ static bool trans_l_extbs(DisasContext *dc, arg_da *a, uint32_t insn) static bool trans_l_exthz(DisasContext *dc, arg_da *a, uint32_t insn) { - LOG_DIS("l.exthz r%d, r%d\n", a->d, a->a); check_r0_write(a->d); tcg_gen_ext16u_tl(cpu_R[a->d], cpu_R[a->a]); return true; @@ -561,7 +527,6 @@ static bool trans_l_exthz(DisasContext *dc, arg_da *a, uint32_t insn) static bool trans_l_extbz(DisasContext *dc, arg_da *a, uint32_t insn) { - LOG_DIS("l.extbz r%d, r%d\n", a->d, a->a); check_r0_write(a->d); tcg_gen_ext8u_tl(cpu_R[a->d], cpu_R[a->a]); return true; @@ -570,7 +535,6 @@ static bool trans_l_extbz(DisasContext *dc, arg_da *a, uint32_t insn) static bool trans_l_cmov(DisasContext *dc, arg_dab *a, uint32_t insn) { TCGv zero; - LOG_DIS("l.cmov r%d, r%d, r%d\n", a->d, a->a, a->b); check_r0_write(a->d); zero = tcg_const_tl(0); @@ -582,8 +546,6 @@ static bool trans_l_cmov(DisasContext *dc, arg_dab *a, uint32_t insn) static bool trans_l_ff1(DisasContext *dc, arg_da *a, uint32_t insn) { - LOG_DIS("l.ff1 r%d, r%d\n", a->d, a->a); - check_r0_write(a->d); tcg_gen_ctzi_tl(cpu_R[a->d], cpu_R[a->a], -1); tcg_gen_addi_tl(cpu_R[a->d], cpu_R[a->d], 1); @@ -592,8 +554,6 @@ static bool trans_l_ff1(DisasContext *dc, arg_da *a, uint32_t insn) static bool trans_l_fl1(DisasContext *dc, arg_da *a, uint32_t insn) { - LOG_DIS("l.fl1 r%d, r%d\n", a->d, a->a); - check_r0_write(a->d); tcg_gen_clzi_tl(cpu_R[a->d], cpu_R[a->a], TARGET_LONG_BITS); tcg_gen_subfi_tl(cpu_R[a->d], TARGET_LONG_BITS, cpu_R[a->d]); @@ -602,8 +562,6 @@ static bool trans_l_fl1(DisasContext *dc, arg_da *a, uint32_t insn) static bool trans_l_mul(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("l.mul r%d, r%d, r%d\n", a->d, a->a, a->b); - check_r0_write(a->d); gen_mul(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; @@ -611,8 +569,6 @@ static bool trans_l_mul(DisasContext *dc, arg_dab *a, uint32_t insn) static bool trans_l_mulu(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("l.mulu r%d, r%d, r%d\n", a->d, a->a, a->b); - check_r0_write(a->d); gen_mulu(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; @@ -620,8 +576,6 @@ static bool trans_l_mulu(DisasContext *dc, arg_dab *a, uint32_t insn) static bool trans_l_div(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("l.div r%d, r%d, r%d\n", a->d, a->a, a->b); - check_r0_write(a->d); gen_div(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; @@ -629,8 +583,6 @@ static bool trans_l_div(DisasContext *dc, arg_dab *a, uint32_t insn) static bool trans_l_divu(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("l.divu r%d, r%d, r%d\n", a->d, a->a, a->b); - check_r0_write(a->d); gen_divu(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; @@ -638,14 +590,12 @@ static bool trans_l_divu(DisasContext *dc, arg_dab *a, uint32_t insn) static bool trans_l_muld(DisasContext *dc, arg_ab *a, uint32_t insn) { - LOG_DIS("l.muld r%d, r%d\n", a->a, a->b); gen_muld(dc, cpu_R[a->a], cpu_R[a->b]); return true; } static bool trans_l_muldu(DisasContext *dc, arg_ab *a, uint32_t insn) { - LOG_DIS("l.muldu r%d, r%d\n", a->a, a->b); gen_muldu(dc, cpu_R[a->a], cpu_R[a->b]); return true; } @@ -654,8 +604,8 @@ static bool trans_l_j(DisasContext *dc, arg_l_j *a, uint32_t insn) { target_ulong tmp_pc = dc->base.pc_next + a->n * 4; - LOG_DIS("l.j %d\n", a->n); tcg_gen_movi_tl(jmp_pc, tmp_pc); + dc->jmp_pc_imm = tmp_pc; dc->delayed_branch = 2; return true; } @@ -665,11 +615,11 @@ static bool trans_l_jal(DisasContext *dc, arg_l_jal *a, uint32_t insn) target_ulong tmp_pc = dc->base.pc_next + a->n * 4; target_ulong ret_pc = dc->base.pc_next + 8; - LOG_DIS("l.jal %d\n", a->n); tcg_gen_movi_tl(cpu_R[9], ret_pc); /* Optimize jal being used to load the PC for PIC. */ if (tmp_pc != ret_pc) { tcg_gen_movi_tl(jmp_pc, tmp_pc); + dc->jmp_pc_imm = tmp_pc; dc->delayed_branch = 2; } return true; @@ -692,21 +642,18 @@ static void do_bf(DisasContext *dc, arg_l_bf *a, TCGCond cond) static bool trans_l_bf(DisasContext *dc, arg_l_bf *a, uint32_t insn) { - LOG_DIS("l.bf %d\n", a->n); do_bf(dc, a, TCG_COND_NE); return true; } static bool trans_l_bnf(DisasContext *dc, arg_l_bf *a, uint32_t insn) { - LOG_DIS("l.bnf %d\n", a->n); do_bf(dc, a, TCG_COND_EQ); return true; } static bool trans_l_jr(DisasContext *dc, arg_l_jr *a, uint32_t insn) { - LOG_DIS("l.jr r%d\n", a->b); tcg_gen_mov_tl(jmp_pc, cpu_R[a->b]); dc->delayed_branch = 2; return true; @@ -714,7 +661,6 @@ static bool trans_l_jr(DisasContext *dc, arg_l_jr *a, uint32_t insn) static bool trans_l_jalr(DisasContext *dc, arg_l_jalr *a, uint32_t insn) { - LOG_DIS("l.jalr r%d\n", a->b); tcg_gen_mov_tl(jmp_pc, cpu_R[a->b]); tcg_gen_movi_tl(cpu_R[9], dc->base.pc_next + 8); dc->delayed_branch = 2; @@ -725,8 +671,6 @@ static bool trans_l_lwa(DisasContext *dc, arg_load *a, uint32_t insn) { TCGv ea; - LOG_DIS("l.lwa r%d, r%d, %d\n", a->d, a->a, a->i); - check_r0_write(a->d); ea = tcg_temp_new(); tcg_gen_addi_tl(ea, cpu_R[a->a], a->i); @@ -750,42 +694,36 @@ static void do_load(DisasContext *dc, arg_load *a, TCGMemOp mop) static bool trans_l_lwz(DisasContext *dc, arg_load *a, uint32_t insn) { - LOG_DIS("l.lwz r%d, r%d, %d\n", a->d, a->a, a->i); do_load(dc, a, MO_TEUL); return true; } static bool trans_l_lws(DisasContext *dc, arg_load *a, uint32_t insn) { - LOG_DIS("l.lws r%d, r%d, %d\n", a->d, a->a, a->i); do_load(dc, a, MO_TESL); return true; } static bool trans_l_lbz(DisasContext *dc, arg_load *a, uint32_t insn) { - LOG_DIS("l.lbz r%d, r%d, %d\n", a->d, a->a, a->i); do_load(dc, a, MO_UB); return true; } static bool trans_l_lbs(DisasContext *dc, arg_load *a, uint32_t insn) { - LOG_DIS("l.lbs r%d, r%d, %d\n", a->d, a->a, a->i); do_load(dc, a, MO_SB); return true; } static bool trans_l_lhz(DisasContext *dc, arg_load *a, uint32_t insn) { - LOG_DIS("l.lhz r%d, r%d, %d\n", a->d, a->a, a->i); do_load(dc, a, MO_TEUW); return true; } static bool trans_l_lhs(DisasContext *dc, arg_load *a, uint32_t insn) { - LOG_DIS("l.lhs r%d, r%d, %d\n", a->d, a->a, a->i); do_load(dc, a, MO_TESW); return true; } @@ -795,8 +733,6 @@ static bool trans_l_swa(DisasContext *dc, arg_store *a, uint32_t insn) TCGv ea, val; TCGLabel *lab_fail, *lab_done; - LOG_DIS("l.swa r%d, r%d, %d\n", a->a, a->b, a->i); - ea = tcg_temp_new(); tcg_gen_addi_tl(ea, cpu_R[a->a], a->i); @@ -837,28 +773,24 @@ static void do_store(DisasContext *dc, arg_store *a, TCGMemOp mop) static bool trans_l_sw(DisasContext *dc, arg_store *a, uint32_t insn) { - LOG_DIS("l.sw r%d, r%d, %d\n", a->a, a->b, a->i); do_store(dc, a, MO_TEUL); return true; } static bool trans_l_sb(DisasContext *dc, arg_store *a, uint32_t insn) { - LOG_DIS("l.sb r%d, r%d, %d\n", a->a, a->b, a->i); do_store(dc, a, MO_UB); return true; } static bool trans_l_sh(DisasContext *dc, arg_store *a, uint32_t insn) { - LOG_DIS("l.sh r%d, r%d, %d\n", a->a, a->b, a->i); do_store(dc, a, MO_TEUW); return true; } static bool trans_l_nop(DisasContext *dc, arg_l_nop *a, uint32_t insn) { - LOG_DIS("l.nop %d\n", a->k); return true; } @@ -866,7 +798,6 @@ static bool trans_l_addi(DisasContext *dc, arg_rri *a, uint32_t insn) { TCGv t0; - LOG_DIS("l.addi r%d, r%d, %d\n", a->d, a->a, a->i); check_r0_write(a->d); t0 = tcg_const_tl(a->i); gen_add(dc, cpu_R[a->d], cpu_R[a->a], t0); @@ -878,7 +809,6 @@ static bool trans_l_addic(DisasContext *dc, arg_rri *a, uint32_t insn) { TCGv t0; - LOG_DIS("l.addic r%d, r%d, %d\n", a->d, a->a, a->i); check_r0_write(a->d); t0 = tcg_const_tl(a->i); gen_addc(dc, cpu_R[a->d], cpu_R[a->a], t0); @@ -890,7 +820,6 @@ static bool trans_l_muli(DisasContext *dc, arg_rri *a, uint32_t insn) { TCGv t0; - LOG_DIS("l.muli r%d, r%d, %d\n", a->d, a->a, a->i); check_r0_write(a->d); t0 = tcg_const_tl(a->i); gen_mul(dc, cpu_R[a->d], cpu_R[a->a], t0); @@ -902,7 +831,6 @@ static bool trans_l_maci(DisasContext *dc, arg_l_maci *a, uint32_t insn) { TCGv t0; - LOG_DIS("l.maci r%d, %d\n", a->a, a->i); t0 = tcg_const_tl(a->i); gen_mac(dc, cpu_R[a->a], t0); tcg_temp_free(t0); @@ -911,7 +839,6 @@ static bool trans_l_maci(DisasContext *dc, arg_l_maci *a, uint32_t insn) static bool trans_l_andi(DisasContext *dc, arg_rrk *a, uint32_t insn) { - LOG_DIS("l.andi r%d, r%d, %d\n", a->d, a->a, a->k); check_r0_write(a->d); tcg_gen_andi_tl(cpu_R[a->d], cpu_R[a->a], a->k); return true; @@ -919,7 +846,6 @@ static bool trans_l_andi(DisasContext *dc, arg_rrk *a, uint32_t insn) static bool trans_l_ori(DisasContext *dc, arg_rrk *a, uint32_t insn) { - LOG_DIS("l.ori r%d, r%d, %d\n", a->d, a->a, a->k); check_r0_write(a->d); tcg_gen_ori_tl(cpu_R[a->d], cpu_R[a->a], a->k); return true; @@ -927,7 +853,6 @@ static bool trans_l_ori(DisasContext *dc, arg_rrk *a, uint32_t insn) static bool trans_l_xori(DisasContext *dc, arg_rri *a, uint32_t insn) { - LOG_DIS("l.xori r%d, r%d, %d\n", a->d, a->a, a->i); check_r0_write(a->d); tcg_gen_xori_tl(cpu_R[a->d], cpu_R[a->a], a->i); return true; @@ -935,72 +860,73 @@ static bool trans_l_xori(DisasContext *dc, arg_rri *a, uint32_t insn) static bool trans_l_mfspr(DisasContext *dc, arg_l_mfspr *a, uint32_t insn) { - LOG_DIS("l.mfspr r%d, r%d, %d\n", a->d, a->a, a->k); check_r0_write(a->d); -#ifdef CONFIG_USER_ONLY - gen_illegal_exception(dc); -#else - if (dc->mem_idx == MMU_USER_IDX) { + if (is_user(dc)) { gen_illegal_exception(dc); } else { - TCGv_i32 ti = tcg_const_i32(a->k); - gen_helper_mfspr(cpu_R[a->d], cpu_env, cpu_R[a->d], cpu_R[a->a], ti); - tcg_temp_free_i32(ti); + TCGv spr = tcg_temp_new(); + tcg_gen_ori_tl(spr, cpu_R[a->a], a->k); + gen_helper_mfspr(cpu_R[a->d], cpu_env, cpu_R[a->d], spr); + tcg_temp_free(spr); } -#endif return true; } static bool trans_l_mtspr(DisasContext *dc, arg_l_mtspr *a, uint32_t insn) { - LOG_DIS("l.mtspr r%d, r%d, %d\n", a->a, a->b, a->k); - -#ifdef CONFIG_USER_ONLY - gen_illegal_exception(dc); -#else - if (dc->mem_idx == MMU_USER_IDX) { + if (is_user(dc)) { gen_illegal_exception(dc); } else { - TCGv_i32 ti = tcg_const_i32(a->k); - gen_helper_mtspr(cpu_env, cpu_R[a->a], cpu_R[a->b], ti); - tcg_temp_free_i32(ti); + TCGv spr; + + /* For SR, we will need to exit the TB to recognize the new + * exception state. For NPC, in theory this counts as a branch + * (although the SPR only exists for use by an ICE). Save all + * of the cpu state first, allowing it to be overwritten. + */ + if (dc->delayed_branch) { + tcg_gen_mov_tl(cpu_pc, jmp_pc); + tcg_gen_discard_tl(jmp_pc); + } else { + tcg_gen_movi_tl(cpu_pc, dc->base.pc_next + 4); + } + dc->base.is_jmp = DISAS_EXIT; + + spr = tcg_temp_new(); + tcg_gen_ori_tl(spr, cpu_R[a->a], a->k); + gen_helper_mtspr(cpu_env, spr, cpu_R[a->b]); + tcg_temp_free(spr); } -#endif return true; } static bool trans_l_mac(DisasContext *dc, arg_ab *a, uint32_t insn) { - LOG_DIS("l.mac r%d, r%d\n", a->a, a->b); gen_mac(dc, cpu_R[a->a], cpu_R[a->b]); return true; } static bool trans_l_msb(DisasContext *dc, arg_ab *a, uint32_t insn) { - LOG_DIS("l.msb r%d, r%d\n", a->a, a->b); gen_msb(dc, cpu_R[a->a], cpu_R[a->b]); return true; } static bool trans_l_macu(DisasContext *dc, arg_ab *a, uint32_t insn) { - LOG_DIS("l.mac r%d, r%d\n", a->a, a->b); gen_macu(dc, cpu_R[a->a], cpu_R[a->b]); return true; } static bool trans_l_msbu(DisasContext *dc, arg_ab *a, uint32_t insn) { - LOG_DIS("l.msb r%d, r%d\n", a->a, a->b); gen_msbu(dc, cpu_R[a->a], cpu_R[a->b]); return true; } static bool trans_l_slli(DisasContext *dc, arg_dal *a, uint32_t insn) { - LOG_DIS("l.slli r%d, r%d, %d\n", a->d, a->a, a->l); check_r0_write(a->d); tcg_gen_shli_tl(cpu_R[a->d], cpu_R[a->a], a->l & (TARGET_LONG_BITS - 1)); return true; @@ -1008,7 +934,6 @@ static bool trans_l_slli(DisasContext *dc, arg_dal *a, uint32_t insn) static bool trans_l_srli(DisasContext *dc, arg_dal *a, uint32_t insn) { - LOG_DIS("l.srli r%d, r%d, %d\n", a->d, a->a, a->l); check_r0_write(a->d); tcg_gen_shri_tl(cpu_R[a->d], cpu_R[a->a], a->l & (TARGET_LONG_BITS - 1)); return true; @@ -1016,7 +941,6 @@ static bool trans_l_srli(DisasContext *dc, arg_dal *a, uint32_t insn) static bool trans_l_srai(DisasContext *dc, arg_dal *a, uint32_t insn) { - LOG_DIS("l.srai r%d, r%d, %d\n", a->d, a->a, a->l); check_r0_write(a->d); tcg_gen_sari_tl(cpu_R[a->d], cpu_R[a->a], a->l & (TARGET_LONG_BITS - 1)); return true; @@ -1024,7 +948,6 @@ static bool trans_l_srai(DisasContext *dc, arg_dal *a, uint32_t insn) static bool trans_l_rori(DisasContext *dc, arg_dal *a, uint32_t insn) { - LOG_DIS("l.rori r%d, r%d, %d\n", a->d, a->a, a->l); check_r0_write(a->d); tcg_gen_rotri_tl(cpu_R[a->d], cpu_R[a->a], a->l & (TARGET_LONG_BITS - 1)); return true; @@ -1032,7 +955,6 @@ static bool trans_l_rori(DisasContext *dc, arg_dal *a, uint32_t insn) static bool trans_l_movhi(DisasContext *dc, arg_l_movhi *a, uint32_t insn) { - LOG_DIS("l.movhi r%d, %d\n", a->d, a->k); check_r0_write(a->d); tcg_gen_movi_tl(cpu_R[a->d], a->k << 16); return true; @@ -1040,7 +962,6 @@ static bool trans_l_movhi(DisasContext *dc, arg_l_movhi *a, uint32_t insn) static bool trans_l_macrc(DisasContext *dc, arg_l_macrc *a, uint32_t insn) { - LOG_DIS("l.macrc r%d\n", a->d); check_r0_write(a->d); tcg_gen_trunc_i64_tl(cpu_R[a->d], cpu_mac); tcg_gen_movi_i64(cpu_mac, 0); @@ -1049,147 +970,126 @@ static bool trans_l_macrc(DisasContext *dc, arg_l_macrc *a, uint32_t insn) static bool trans_l_sfeq(DisasContext *dc, arg_ab *a, TCGCond cond) { - LOG_DIS("l.sfeq r%d, r%d\n", a->a, a->b); tcg_gen_setcond_tl(TCG_COND_EQ, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); return true; } static bool trans_l_sfne(DisasContext *dc, arg_ab *a, TCGCond cond) { - LOG_DIS("l.sfne r%d, r%d\n", a->a, a->b); tcg_gen_setcond_tl(TCG_COND_NE, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); return true; } static bool trans_l_sfgtu(DisasContext *dc, arg_ab *a, TCGCond cond) { - LOG_DIS("l.sfgtu r%d, r%d\n", a->a, a->b); tcg_gen_setcond_tl(TCG_COND_GTU, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); return true; } static bool trans_l_sfgeu(DisasContext *dc, arg_ab *a, TCGCond cond) { - LOG_DIS("l.sfgeu r%d, r%d\n", a->a, a->b); tcg_gen_setcond_tl(TCG_COND_GEU, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); return true; } static bool trans_l_sfltu(DisasContext *dc, arg_ab *a, TCGCond cond) { - LOG_DIS("l.sfltu r%d, r%d\n", a->a, a->b); tcg_gen_setcond_tl(TCG_COND_LTU, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); return true; } static bool trans_l_sfleu(DisasContext *dc, arg_ab *a, TCGCond cond) { - LOG_DIS("l.sfleu r%d, r%d\n", a->a, a->b); tcg_gen_setcond_tl(TCG_COND_LEU, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); return true; } static bool trans_l_sfgts(DisasContext *dc, arg_ab *a, TCGCond cond) { - LOG_DIS("l.sfgts r%d, r%d\n", a->a, a->b); tcg_gen_setcond_tl(TCG_COND_GT, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); return true; } static bool trans_l_sfges(DisasContext *dc, arg_ab *a, TCGCond cond) { - LOG_DIS("l.sfges r%d, r%d\n", a->a, a->b); tcg_gen_setcond_tl(TCG_COND_GE, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); return true; } static bool trans_l_sflts(DisasContext *dc, arg_ab *a, TCGCond cond) { - LOG_DIS("l.sflts r%d, r%d\n", a->a, a->b); tcg_gen_setcond_tl(TCG_COND_LT, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); return true; } static bool trans_l_sfles(DisasContext *dc, arg_ab *a, TCGCond cond) { - LOG_DIS("l.sfles r%d, r%d\n", a->a, a->b); tcg_gen_setcond_tl(TCG_COND_LE, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); return true; } static bool trans_l_sfeqi(DisasContext *dc, arg_ai *a, TCGCond cond) { - LOG_DIS("l.sfeqi r%d, %d\n", a->a, a->i); tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_sr_f, cpu_R[a->a], a->i); return true; } static bool trans_l_sfnei(DisasContext *dc, arg_ai *a, TCGCond cond) { - LOG_DIS("l.sfnei r%d, %d\n", a->a, a->i); tcg_gen_setcondi_tl(TCG_COND_NE, cpu_sr_f, cpu_R[a->a], a->i); return true; } static bool trans_l_sfgtui(DisasContext *dc, arg_ai *a, TCGCond cond) { - LOG_DIS("l.sfgtui r%d, %d\n", a->a, a->i); tcg_gen_setcondi_tl(TCG_COND_GTU, cpu_sr_f, cpu_R[a->a], a->i); return true; } static bool trans_l_sfgeui(DisasContext *dc, arg_ai *a, TCGCond cond) { - LOG_DIS("l.sfgeui r%d, %d\n", a->a, a->i); tcg_gen_setcondi_tl(TCG_COND_GEU, cpu_sr_f, cpu_R[a->a], a->i); return true; } static bool trans_l_sfltui(DisasContext *dc, arg_ai *a, TCGCond cond) { - LOG_DIS("l.sfltui r%d, %d\n", a->a, a->i); tcg_gen_setcondi_tl(TCG_COND_LTU, cpu_sr_f, cpu_R[a->a], a->i); return true; } static bool trans_l_sfleui(DisasContext *dc, arg_ai *a, TCGCond cond) { - LOG_DIS("l.sfleui r%d, %d\n", a->a, a->i); tcg_gen_setcondi_tl(TCG_COND_LEU, cpu_sr_f, cpu_R[a->a], a->i); return true; } static bool trans_l_sfgtsi(DisasContext *dc, arg_ai *a, TCGCond cond) { - LOG_DIS("l.sfgtsi r%d, %d\n", a->a, a->i); tcg_gen_setcondi_tl(TCG_COND_GT, cpu_sr_f, cpu_R[a->a], a->i); return true; } static bool trans_l_sfgesi(DisasContext *dc, arg_ai *a, TCGCond cond) { - LOG_DIS("l.sfgesi r%d, %d\n", a->a, a->i); tcg_gen_setcondi_tl(TCG_COND_GE, cpu_sr_f, cpu_R[a->a], a->i); return true; } static bool trans_l_sfltsi(DisasContext *dc, arg_ai *a, TCGCond cond) { - LOG_DIS("l.sfltsi r%d, %d\n", a->a, a->i); tcg_gen_setcondi_tl(TCG_COND_LT, cpu_sr_f, cpu_R[a->a], a->i); return true; } static bool trans_l_sflesi(DisasContext *dc, arg_ai *a, TCGCond cond) { - LOG_DIS("l.sflesi r%d, %d\n", a->a, a->i); tcg_gen_setcondi_tl(TCG_COND_LE, cpu_sr_f, cpu_R[a->a], a->i); return true; } static bool trans_l_sys(DisasContext *dc, arg_l_sys *a, uint32_t insn) { - LOG_DIS("l.sys %d\n", a->k); tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); gen_exception(dc, EXCP_SYSCALL); dc->base.is_jmp = DISAS_NORETURN; @@ -1198,7 +1098,6 @@ static bool trans_l_sys(DisasContext *dc, arg_l_sys *a, uint32_t insn) static bool trans_l_trap(DisasContext *dc, arg_l_trap *a, uint32_t insn) { - LOG_DIS("l.trap %d\n", a->k); tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); gen_exception(dc, EXCP_TRAP); dc->base.is_jmp = DISAS_NORETURN; @@ -1207,37 +1106,28 @@ static bool trans_l_trap(DisasContext *dc, arg_l_trap *a, uint32_t insn) static bool trans_l_msync(DisasContext *dc, arg_l_msync *a, uint32_t insn) { - LOG_DIS("l.msync\n"); tcg_gen_mb(TCG_MO_ALL); return true; } static bool trans_l_psync(DisasContext *dc, arg_l_psync *a, uint32_t insn) { - LOG_DIS("l.psync\n"); return true; } static bool trans_l_csync(DisasContext *dc, arg_l_csync *a, uint32_t insn) { - LOG_DIS("l.csync\n"); return true; } static bool trans_l_rfe(DisasContext *dc, arg_l_rfe *a, uint32_t insn) { - LOG_DIS("l.rfe\n"); - -#ifdef CONFIG_USER_ONLY - gen_illegal_exception(dc); -#else - if (dc->mem_idx == MMU_USER_IDX) { + if (is_user(dc)) { gen_illegal_exception(dc); } else { gen_helper_rfe(cpu_env); - dc->base.is_jmp = DISAS_UPDATE; + dc->base.is_jmp = DISAS_EXIT; } -#endif return true; } @@ -1274,56 +1164,48 @@ static void do_fpcmp(DisasContext *dc, arg_ab *a, static bool trans_lf_add_s(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("lf.add.s r%d, r%d, r%d\n", a->d, a->a, a->b); do_fp3(dc, a, gen_helper_float_add_s); return true; } static bool trans_lf_sub_s(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("lf.sub.s r%d, r%d, r%d\n", a->d, a->a, a->b); do_fp3(dc, a, gen_helper_float_sub_s); return true; } static bool trans_lf_mul_s(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("lf.mul.s r%d, r%d, r%d\n", a->d, a->a, a->b); do_fp3(dc, a, gen_helper_float_mul_s); return true; } static bool trans_lf_div_s(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("lf.div.s r%d, r%d, r%d\n", a->d, a->a, a->b); do_fp3(dc, a, gen_helper_float_div_s); return true; } static bool trans_lf_rem_s(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("lf.rem.s r%d, r%d, r%d\n", a->d, a->a, a->b); do_fp3(dc, a, gen_helper_float_rem_s); return true; } static bool trans_lf_itof_s(DisasContext *dc, arg_da *a, uint32_t insn) { - LOG_DIS("lf.itof.s r%d, r%d\n", a->d, a->a); do_fp2(dc, a, gen_helper_itofs); return true; } static bool trans_lf_ftoi_s(DisasContext *dc, arg_da *a, uint32_t insn) { - LOG_DIS("lf.ftoi.s r%d, r%d\n", a->d, a->a); do_fp2(dc, a, gen_helper_ftois); return true; } static bool trans_lf_madd_s(DisasContext *dc, arg_dab *a, uint32_t insn) { - LOG_DIS("lf.madd.s r%d, r%d, r%d\n", a->d, a->a, a->b); check_r0_write(a->d); gen_helper_float_madd_s(cpu_R[a->d], cpu_env, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); @@ -1333,42 +1215,36 @@ static bool trans_lf_madd_s(DisasContext *dc, arg_dab *a, uint32_t insn) static bool trans_lf_sfeq_s(DisasContext *dc, arg_ab *a, uint32_t insn) { - LOG_DIS("lf.sfeq.s r%d, r%d\n", a->a, a->b); do_fpcmp(dc, a, gen_helper_float_eq_s, false, false); return true; } static bool trans_lf_sfne_s(DisasContext *dc, arg_ab *a, uint32_t insn) { - LOG_DIS("lf.sfne.s r%d, r%d\n", a->a, a->b); do_fpcmp(dc, a, gen_helper_float_eq_s, true, false); return true; } static bool trans_lf_sfgt_s(DisasContext *dc, arg_ab *a, uint32_t insn) { - LOG_DIS("lf.sfgt.s r%d, r%d\n", a->a, a->b); do_fpcmp(dc, a, gen_helper_float_lt_s, false, true); return true; } static bool trans_lf_sfge_s(DisasContext *dc, arg_ab *a, uint32_t insn) { - LOG_DIS("lf.sfge.s r%d, r%d\n", a->a, a->b); do_fpcmp(dc, a, gen_helper_float_le_s, false, true); return true; } static bool trans_lf_sflt_s(DisasContext *dc, arg_ab *a, uint32_t insn) { - LOG_DIS("lf.sflt.s r%d, r%d\n", a->a, a->b); do_fpcmp(dc, a, gen_helper_float_lt_s, false, false); return true; } static bool trans_lf_sfle_s(DisasContext *dc, arg_ab *a, uint32_t insn) { - LOG_DIS("lf.sfle.s r%d, r%d\n", a->a, a->b); do_fpcmp(dc, a, gen_helper_float_le_s, false, false); return true; } @@ -1382,6 +1258,8 @@ static void openrisc_tr_init_disas_context(DisasContextBase *dcb, CPUState *cs) dc->mem_idx = cpu_mmu_index(env, false); dc->tb_flags = dc->base.tb->flags; dc->delayed_branch = (dc->tb_flags & TB_FLAGS_DFLAG) != 0; + dc->jmp_pc_imm = -1; + bound = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4; dc->base.max_insns = MIN(dc->base.max_insns, bound); } @@ -1434,50 +1312,81 @@ static void openrisc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) } dc->base.pc_next += 4; - /* delay slot */ - if (dc->delayed_branch) { - dc->delayed_branch--; - if (!dc->delayed_branch) { - tcg_gen_mov_tl(cpu_pc, jmp_pc); - tcg_gen_discard_tl(jmp_pc); - dc->base.is_jmp = DISAS_UPDATE; - return; - } + /* When exiting the delay slot normally, exit via jmp_pc. + * For DISAS_NORETURN, we have raised an exception and already exited. + * For DISAS_EXIT, we found l.rfe in a delay slot. There's nothing + * in the manual saying this is illegal, but it surely it should. + * At least or1ksim overrides pcnext and ignores the branch. + */ + if (dc->delayed_branch + && --dc->delayed_branch == 0 + && dc->base.is_jmp == DISAS_NEXT) { + dc->base.is_jmp = DISAS_JUMP; } } static void openrisc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) { DisasContext *dc = container_of(dcbase, DisasContext, base); + target_ulong jmp_dest; + + /* If we have already exited the TB, nothing following has effect. */ + if (dc->base.is_jmp == DISAS_NORETURN) { + return; + } + /* Adjust the delayed branch state for the next TB. */ if ((dc->tb_flags & TB_FLAGS_DFLAG ? 1 : 0) != (dc->delayed_branch != 0)) { tcg_gen_movi_i32(cpu_dflag, dc->delayed_branch != 0); } - tcg_gen_movi_tl(cpu_ppc, dc->base.pc_next - 4); - if (dc->base.is_jmp == DISAS_NEXT) { - dc->base.is_jmp = DISAS_UPDATE; - tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); - } - if (unlikely(dc->base.singlestep_enabled)) { - gen_exception(dc, EXCP_DEBUG); - } else { - switch (dc->base.is_jmp) { - case DISAS_TOO_MANY: - gen_goto_tb(dc, 0, dc->base.pc_next); - break; - case DISAS_NORETURN: - case DISAS_JUMP: - case DISAS_TB_JUMP: + /* For DISAS_TOO_MANY, jump to the next insn. */ + jmp_dest = dc->base.pc_next; + tcg_gen_movi_tl(cpu_ppc, jmp_dest - 4); + + switch (dc->base.is_jmp) { + case DISAS_JUMP: + jmp_dest = dc->jmp_pc_imm; + if (jmp_dest == -1) { + /* The jump destination is indirect/computed; use jmp_pc. */ + tcg_gen_mov_tl(cpu_pc, jmp_pc); + tcg_gen_discard_tl(jmp_pc); + if (unlikely(dc->base.singlestep_enabled)) { + gen_exception(dc, EXCP_DEBUG); + } else { + tcg_gen_lookup_and_goto_ptr(); + } break; - case DISAS_UPDATE: - /* indicate that the hash table must be used - to find the next TB */ + } + /* The jump destination is direct; use jmp_pc_imm. + However, we will have stored into jmp_pc as well; + we know now that it wasn't needed. */ + tcg_gen_discard_tl(jmp_pc); + /* fallthru */ + + case DISAS_TOO_MANY: + if (unlikely(dc->base.singlestep_enabled)) { + tcg_gen_movi_tl(cpu_pc, jmp_dest); + gen_exception(dc, EXCP_DEBUG); + } else if ((dc->base.pc_first ^ jmp_dest) & TARGET_PAGE_MASK) { + tcg_gen_movi_tl(cpu_pc, jmp_dest); + tcg_gen_lookup_and_goto_ptr(); + } else { + tcg_gen_goto_tb(0); + tcg_gen_movi_tl(cpu_pc, jmp_dest); + tcg_gen_exit_tb(dc->base.tb, 0); + } + break; + + case DISAS_EXIT: + if (unlikely(dc->base.singlestep_enabled)) { + gen_exception(dc, EXCP_DEBUG); + } else { tcg_gen_exit_tb(NULL, 0); - break; - default: - g_assert_not_reached(); } + break; + default: + g_assert_not_reached(); } } diff --git a/tests/Makefile.include b/tests/Makefile.include index 9faefd7cd2..1affc49ca3 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -727,7 +727,9 @@ tests/test-crypto-tlscredsx509$(EXESUF): tests/test-crypto-tlscredsx509.o \ tests/test-crypto-tlssession.o-cflags := $(TASN1_CFLAGS) tests/test-crypto-tlssession$(EXESUF): tests/test-crypto-tlssession.o \ - tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o $(test-crypto-obj-y) + tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o \ + tests/crypto-tls-psk-helpers.o \ + $(test-crypto-obj-y) tests/test-util-sockets$(EXESUF): tests/test-util-sockets.o \ tests/socket-helpers.o $(test-util-obj-y) tests/test-io-task$(EXESUF): tests/test-io-task.o $(test-io-obj-y) diff --git a/tests/crypto-tls-psk-helpers.c b/tests/crypto-tls-psk-helpers.c new file mode 100644 index 0000000000..a8395477c3 --- /dev/null +++ b/tests/crypto-tls-psk-helpers.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015-2018 Red Hat, Inc. + * + * 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.1 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/>. + * + * Author: Richard W.M. Jones <rjones@redhat.com> + */ + +#include "qemu/osdep.h" + +/* Include this first because it defines QCRYPTO_HAVE_TLS_TEST_SUPPORT */ +#include "crypto-tls-x509-helpers.h" + +#include "crypto-tls-psk-helpers.h" +#include "qemu/sockets.h" + +#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT + +void test_tls_psk_init(const char *pskfile) +{ + FILE *fp; + + fp = fopen(pskfile, "w"); + if (fp == NULL) { + g_critical("Failed to create pskfile %s", pskfile); + abort(); + } + /* Don't do this in real applications! Use psktool. */ + fprintf(fp, "qemu:009d5638c40fde0c\n"); + fclose(fp); +} + +void test_tls_psk_cleanup(const char *pskfile) +{ + unlink(pskfile); +} + +#endif /* QCRYPTO_HAVE_TLS_TEST_SUPPORT */ diff --git a/tests/crypto-tls-psk-helpers.h b/tests/crypto-tls-psk-helpers.h new file mode 100644 index 0000000000..9aec29f1a0 --- /dev/null +++ b/tests/crypto-tls-psk-helpers.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2015-2018 Red Hat, Inc. + * + * 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.1 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/>. + * + * Author: Richard W.M. Jones <rjones@redhat.com> + */ + +#include <gnutls/gnutls.h> + +#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT +# include "qemu-common.h" + +void test_tls_psk_init(const char *keyfile); +void test_tls_psk_cleanup(const char *keyfile); + +#endif /* QCRYPTO_HAVE_TLS_TEST_SUPPORT */ diff --git a/tests/test-crypto-tlssession.c b/tests/test-crypto-tlssession.c index 82f21c27f2..7bd811796e 100644 --- a/tests/test-crypto-tlssession.c +++ b/tests/test-crypto-tlssession.c @@ -21,7 +21,9 @@ #include "qemu/osdep.h" #include "crypto-tls-x509-helpers.h" +#include "crypto-tls-psk-helpers.h" #include "crypto/tlscredsx509.h" +#include "crypto/tlscredspsk.h" #include "crypto/tlssession.h" #include "qom/object_interfaces.h" #include "qapi/error.h" @@ -31,20 +33,9 @@ #ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT #define WORKDIR "tests/test-crypto-tlssession-work/" +#define PSKFILE WORKDIR "keys.psk" #define KEYFILE WORKDIR "key-ctx.pem" -struct QCryptoTLSSessionTestData { - const char *servercacrt; - const char *clientcacrt; - const char *servercrt; - const char *clientcrt; - bool expectServerFail; - bool expectClientFail; - const char *hostname; - const char *const *wildcards; -}; - - static ssize_t testWrite(const char *buf, size_t len, void *opaque) { int *fd = opaque; @@ -59,9 +50,150 @@ static ssize_t testRead(char *buf, size_t len, void *opaque) return read(*fd, buf, len); } -static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint, - const char *certdir, - Error **errp) +static QCryptoTLSCreds *test_tls_creds_psk_create( + QCryptoTLSCredsEndpoint endpoint, + const char *dir, + Error **errp) +{ + Error *err = NULL; + Object *parent = object_get_objects_root(); + Object *creds = object_new_with_props( + TYPE_QCRYPTO_TLS_CREDS_PSK, + parent, + (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? + "testtlscredsserver" : "testtlscredsclient"), + &err, + "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? + "server" : "client"), + "dir", dir, + "priority", "NORMAL", + NULL + ); + + if (err) { + error_propagate(errp, err); + return NULL; + } + return QCRYPTO_TLS_CREDS(creds); +} + + +static void test_crypto_tls_session_psk(void) +{ + QCryptoTLSCreds *clientCreds; + QCryptoTLSCreds *serverCreds; + QCryptoTLSSession *clientSess = NULL; + QCryptoTLSSession *serverSess = NULL; + int channel[2]; + bool clientShake = false; + bool serverShake = false; + Error *err = NULL; + int ret; + + /* We'll use this for our fake client-server connection */ + ret = socketpair(AF_UNIX, SOCK_STREAM, 0, channel); + g_assert(ret == 0); + + /* + * We have an evil loop to do the handshake in a single + * thread, so we need these non-blocking to avoid deadlock + * of ourselves + */ + qemu_set_nonblock(channel[0]); + qemu_set_nonblock(channel[1]); + + clientCreds = test_tls_creds_psk_create( + QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, + WORKDIR, + &err); + g_assert(clientCreds != NULL); + + serverCreds = test_tls_creds_psk_create( + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, + WORKDIR, + &err); + g_assert(serverCreds != NULL); + + /* Now the real part of the test, setup the sessions */ + clientSess = qcrypto_tls_session_new( + clientCreds, NULL, NULL, + QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, &err); + serverSess = qcrypto_tls_session_new( + serverCreds, NULL, NULL, + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, &err); + + g_assert(clientSess != NULL); + g_assert(serverSess != NULL); + + /* For handshake to work, we need to set the I/O callbacks + * to read/write over the socketpair + */ + qcrypto_tls_session_set_callbacks(serverSess, + testWrite, testRead, + &channel[0]); + qcrypto_tls_session_set_callbacks(clientSess, + testWrite, testRead, + &channel[1]); + + /* + * Finally we loop around & around doing handshake on each + * session until we get an error, or the handshake completes. + * This relies on the socketpair being nonblocking to avoid + * deadlocking ourselves upon handshake + */ + do { + int rv; + if (!serverShake) { + rv = qcrypto_tls_session_handshake(serverSess, + &err); + g_assert(rv >= 0); + if (qcrypto_tls_session_get_handshake_status(serverSess) == + QCRYPTO_TLS_HANDSHAKE_COMPLETE) { + serverShake = true; + } + } + if (!clientShake) { + rv = qcrypto_tls_session_handshake(clientSess, + &err); + g_assert(rv >= 0); + if (qcrypto_tls_session_get_handshake_status(clientSess) == + QCRYPTO_TLS_HANDSHAKE_COMPLETE) { + clientShake = true; + } + } + } while (!clientShake && !serverShake); + + + /* Finally make sure the server & client validation is successful. */ + g_assert(qcrypto_tls_session_check_credentials(serverSess, &err) == 0); + g_assert(qcrypto_tls_session_check_credentials(clientSess, &err) == 0); + + object_unparent(OBJECT(serverCreds)); + object_unparent(OBJECT(clientCreds)); + + qcrypto_tls_session_free(serverSess); + qcrypto_tls_session_free(clientSess); + + close(channel[0]); + close(channel[1]); +} + + +struct QCryptoTLSSessionTestData { + const char *servercacrt; + const char *clientcacrt; + const char *servercrt; + const char *clientcrt; + bool expectServerFail; + bool expectClientFail; + const char *hostname; + const char *const *wildcards; +}; + +static QCryptoTLSCreds *test_tls_creds_x509_create( + QCryptoTLSCredsEndpoint endpoint, + const char *certdir, + Error **errp) { Error *err = NULL; Object *parent = object_get_objects_root(); @@ -104,7 +236,7 @@ static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint, * initiate a TLS session across them. Finally do * do actual cert validation tests */ -static void test_crypto_tls_session(const void *opaque) +static void test_crypto_tls_session_x509(const void *opaque) { struct QCryptoTLSSessionTestData *data = (struct QCryptoTLSSessionTestData *)opaque; @@ -159,13 +291,13 @@ static void test_crypto_tls_session(const void *opaque) g_assert(link(KEYFILE, CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY) == 0); - clientCreds = test_tls_creds_create( + clientCreds = test_tls_creds_x509_create( QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, CLIENT_CERT_DIR, &err); g_assert(clientCreds != NULL); - serverCreds = test_tls_creds_create( + serverCreds = test_tls_creds_x509_create( QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, SERVER_CERT_DIR, &err); @@ -285,7 +417,13 @@ int main(int argc, char **argv) mkdir(WORKDIR, 0700); test_tls_init(KEYFILE); + test_tls_psk_init(PSKFILE); + + /* Simple initial test using Pre-Shared Keys. */ + g_test_add_func("/qcrypto/tlssession/psk", + test_crypto_tls_session_psk); + /* More complex tests using X.509 certificates. */ # define TEST_SESS_REG(name, caCrt, \ serverCrt, clientCrt, \ expectServerFail, expectClientFail, \ @@ -296,7 +434,7 @@ int main(int argc, char **argv) hostname, wildcards \ }; \ g_test_add_data_func("/qcrypto/tlssession/" # name, \ - &name, test_crypto_tls_session); \ + &name, test_crypto_tls_session_x509); \ # define TEST_SESS_REG_EXT(name, serverCaCrt, clientCaCrt, \ @@ -309,7 +447,7 @@ int main(int argc, char **argv) hostname, wildcards \ }; \ g_test_add_data_func("/qcrypto/tlssession/" # name, \ - &name, test_crypto_tls_session); \ + &name, test_crypto_tls_session_x509); \ /* A perfect CA, perfect client & perfect server */ @@ -518,6 +656,7 @@ int main(int argc, char **argv) test_tls_discard_cert(&clientcertlevel2breq); unlink(WORKDIR "cacertchain-sess.pem"); + test_tls_psk_cleanup(PSKFILE); test_tls_cleanup(KEYFILE); rmdir(WORKDIR); diff --git a/tests/test-qga.c b/tests/test-qga.c index 30c9643257..350f27812a 100644 --- a/tests/test-qga.c +++ b/tests/test-qga.c @@ -854,6 +854,54 @@ static void test_qga_guest_exec_invalid(gconstpointer fix) qobject_unref(ret); } +static void test_qga_guest_get_host_name(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret, *val; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-host-name'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + val = qdict_get_qdict(ret, "return"); + g_assert(qdict_haskey(val, "host-name")); + + qobject_unref(ret); +} + +static void test_qga_guest_get_timezone(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret, *val; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-timezone'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + /* Make sure there's at least offset */ + val = qdict_get_qdict(ret, "return"); + g_assert(qdict_haskey(val, "offset")); + + qobject_unref(ret); +} + +static void test_qga_guest_get_users(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + QList *val; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-users'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + /* There is not much to test here */ + val = qdict_get_qlist(ret, "return"); + g_assert_nonnull(val); + + qobject_unref(ret); +} + static void test_qga_guest_get_osinfo(gconstpointer data) { TestFixture fixture; @@ -946,6 +994,12 @@ int main(int argc, char **argv) test_qga_guest_exec_invalid); g_test_add_data_func("/qga/guest-get-osinfo", &fix, test_qga_guest_get_osinfo); + g_test_add_data_func("/qga/guest-get-host-name", &fix, + test_qga_guest_get_host_name); + g_test_add_data_func("/qga/guest-get-timezone", &fix, + test_qga_guest_get_timezone); + g_test_add_data_func("/qga/guest-get-users", &fix, + test_qga_guest_get_users); ret = g_test_run(); |