diff options
82 files changed, 12130 insertions, 1435 deletions
@@ -152,6 +152,9 @@ dummy := $(call unnest-vars,, \ qga-vss-dll-obj-y \ block-obj-y \ block-obj-m \ + crypto-obj-y \ + crypto-aes-obj-y \ + qom-obj-y \ common-obj-y \ common-obj-m) @@ -173,6 +176,8 @@ SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS)) SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES)) $(SOFTMMU_SUBDIR_RULES): $(block-obj-y) +$(SOFTMMU_SUBDIR_RULES): $(crypto-obj-y) +$(SOFTMMU_SUBDIR_RULES): $(qom-obj-y) $(SOFTMMU_SUBDIR_RULES): config-all-devices.mak subdir-%: @@ -227,9 +232,9 @@ util/module.o-cflags = -D'CONFIG_BLOCK_MODULES=$(block-modules)' qemu-img.o: qemu-img-cmds.h -qemu-img$(EXESUF): qemu-img.o $(block-obj-y) libqemuutil.a libqemustub.a -qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) libqemuutil.a libqemustub.a -qemu-io$(EXESUF): qemu-io.o $(block-obj-y) libqemuutil.a libqemustub.a +qemu-img$(EXESUF): qemu-img.o $(block-obj-y) $(crypto-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a +qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) $(crypto-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a +qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o diff --git a/Makefile.objs b/Makefile.objs index f094eff15e..3df2efca1c 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -2,7 +2,6 @@ # Common libraries for tools and emulators stub-obj-y = stubs/ util-obj-y = util/ qobject/ qapi/ qapi-types.o qapi-visit.o qapi-event.o -util-obj-y += crypto/ ####################################################################### # block-obj-y is code used by both qemu system emulation and qemu-img @@ -21,6 +20,16 @@ block-obj-y += coroutine-$(CONFIG_COROUTINE_BACKEND).o block-obj-m = block/ +####################################################################### +# crypto-obj-y is code used by both qemu system emulation and qemu-img + +crypto-obj-y = crypto/ +crypto-aes-obj-y = crypto/ + +####################################################################### +# qom-obj-y is code used by both qemu system emulation and qemu-img + +qom-obj-y = qom/ ###################################################################### # smartcard diff --git a/Makefile.target b/Makefile.target index 04538e49e1..962d0045ff 100644 --- a/Makefile.target +++ b/Makefile.target @@ -173,12 +173,18 @@ target-obj-y-save := $(target-obj-y) dummy := $(call unnest-vars,.., \ block-obj-y \ block-obj-m \ + crypto-obj-y \ + crypto-aes-obj-y \ + qom-obj-y \ common-obj-y \ common-obj-m) target-obj-y := $(target-obj-y-save) all-obj-y += $(common-obj-y) all-obj-y += $(target-obj-y) +all-obj-y += $(qom-obj-y) all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) +all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y) +all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y) $(QEMU_PROG_BUILD): config-devices.mak @@ -242,7 +242,6 @@ vnc="yes" sparse="no" uuid="" vde="" -vnc_tls="" vnc_sasl="" vnc_jpeg="" vnc_png="" @@ -416,6 +415,9 @@ if test "$debug_info" = "yes"; then LDFLAGS="-g $LDFLAGS" fi +test_cflags="" +test_libs="" + # make source path absolute source_path=`cd "$source_path"; pwd` @@ -880,10 +882,6 @@ for opt do ;; --disable-strip) strip_opt="no" ;; - --disable-vnc-tls) vnc_tls="no" - ;; - --enable-vnc-tls) vnc_tls="yes" - ;; --disable-vnc-sasl) vnc_sasl="no" ;; --enable-vnc-sasl) vnc_sasl="yes" @@ -2249,6 +2247,19 @@ if test "$gnutls_nettle" != "no"; then fi fi +########################################## +# libtasn1 - only for the TLS creds/session test suite + +tasn1=yes +if $pkg_config --exists "libtasn1"; then + tasn1_cflags=`$pkg_config --cflags libtasn1` + tasn1_libs=`$pkg_config --libs libtasn1` + test_cflags="$test_cflags $tasn1_cflags" + test_libs="$test_libs $tasn1_libs" +else + tasn1=no +fi + ########################################## # VTE probe @@ -2393,28 +2404,6 @@ EOF fi fi -########################################## -# VNC TLS/WS detection -if test "$vnc" = "yes" -a "$vnc_tls" != "no" ; then - cat > $TMPC <<EOF -#include <gnutls/gnutls.h> -int main(void) { gnutls_session_t s; gnutls_init(&s, GNUTLS_SERVER); return 0; } -EOF - vnc_tls_cflags=`$pkg_config --cflags gnutls 2> /dev/null` - vnc_tls_libs=`$pkg_config --libs gnutls 2> /dev/null` - if compile_prog "$vnc_tls_cflags" "$vnc_tls_libs" ; then - if test "$vnc_tls" != "no" ; then - vnc_tls=yes - fi - libs_softmmu="$vnc_tls_libs $libs_softmmu" - QEMU_CFLAGS="$QEMU_CFLAGS $vnc_tls_cflags" - else - if test "$vnc_tls" = "yes" ; then - feature_not_found "vnc-tls" "Install gnutls devel" - fi - vnc_tls=no - fi -fi ########################################## # VNC SASL detection @@ -4574,6 +4563,7 @@ echo "GNUTLS support $gnutls" echo "GNUTLS hash $gnutls_hash" echo "GNUTLS gcrypt $gnutls_gcrypt" echo "GNUTLS nettle $gnutls_nettle ${gnutls_nettle+($nettle_version)}" +echo "libtasn1 $tasn1" echo "VTE support $vte" echo "curses support $curses" echo "curl support $curl" @@ -4584,7 +4574,6 @@ echo "Block whitelist (ro) $block_drv_ro_whitelist" echo "VirtFS support $virtfs" echo "VNC support $vnc" if test "$vnc" = "yes" ; then - echo "VNC TLS support $vnc_tls" echo "VNC SASL support $vnc_sasl" echo "VNC JPEG support $vnc_jpeg" echo "VNC PNG support $vnc_png" @@ -4793,9 +4782,6 @@ echo "CONFIG_BDRV_RO_WHITELIST=$block_drv_ro_whitelist" >> $config_host_mak if test "$vnc" = "yes" ; then echo "CONFIG_VNC=y" >> $config_host_mak fi -if test "$vnc_tls" = "yes" ; then - echo "CONFIG_VNC_TLS=y" >> $config_host_mak -fi if test "$vnc_sasl" = "yes" ; then echo "CONFIG_VNC_SASL=y" >> $config_host_mak fi @@ -4945,6 +4931,9 @@ if test "$gnutls_nettle" = "yes" ; then echo "CONFIG_GNUTLS_NETTLE=y" >> $config_host_mak echo "CONFIG_NETTLE_VERSION_MAJOR=${nettle_version%%.*}" >> $config_host_mak fi +if test "$tasn1" = "yes" ; then + echo "CONFIG_TASN1=y" >> $config_host_mak +fi if test "$vte" = "yes" ; then echo "CONFIG_VTE=y" >> $config_host_mak echo "VTE_CFLAGS=$vte_cflags" >> $config_host_mak @@ -5268,6 +5257,8 @@ echo "EXESUF=$EXESUF" >> $config_host_mak echo "DSOSUF=$DSOSUF" >> $config_host_mak echo "LDFLAGS_SHARED=$LDFLAGS_SHARED" >> $config_host_mak echo "LIBS_QGA+=$libs_qga" >> $config_host_mak +echo "TEST_LIBS=$test_libs" >> $config_host_mak +echo "TEST_CFLAGS=$test_cflags" >> $config_host_mak echo "POD2MAN=$POD2MAN" >> $config_host_mak echo "TRANSLATE_OPT_CFLAGS=$TRANSLATE_OPT_CFLAGS" >> $config_host_mak if test "$gcov" = "yes" ; then @@ -5450,6 +5441,8 @@ case "$target_name" in s390x) gdb_xml_files="s390x-core64.xml s390-acr.xml s390-fpr.xml s390-vx.xml s390-cr.xml" ;; + tilegx) + ;; tricore) ;; unicore32) diff --git a/crypto/Makefile.objs b/crypto/Makefile.objs index b05013831b..b2a0e0b38e 100644 --- a/crypto/Makefile.objs +++ b/crypto/Makefile.objs @@ -1,5 +1,12 @@ -util-obj-y += init.o -util-obj-y += hash.o -util-obj-y += aes.o -util-obj-y += desrfb.o -util-obj-y += cipher.o +crypto-obj-y = init.o +crypto-obj-y += hash.o +crypto-obj-y += aes.o +crypto-obj-y += desrfb.o +crypto-obj-y += cipher.o +crypto-obj-y += tlscreds.o +crypto-obj-y += tlscredsanon.o +crypto-obj-y += tlscredsx509.o +crypto-obj-y += tlssession.o + +# Let the userspace emulators avoid linking gnutls/etc +crypto-aes-obj-y = aes.o diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c new file mode 100644 index 0000000000..5ec982c6ee --- /dev/null +++ b/crypto/tlscreds.c @@ -0,0 +1,251 @@ +/* + * QEMU crypto TLS credential support + * + * Copyright (c) 2015 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 "crypto/tlscredspriv.h" +#include "trace.h" + +#define DH_BITS 2048 + +#ifdef CONFIG_GNUTLS +int +qcrypto_tls_creds_get_dh_params_file(QCryptoTLSCreds *creds, + const char *filename, + gnutls_dh_params_t *dh_params, + Error **errp) +{ + int ret; + + trace_qcrypto_tls_creds_load_dh(creds, filename ? filename : "<generated>"); + + if (filename == NULL) { + ret = gnutls_dh_params_init(dh_params); + if (ret < 0) { + error_setg(errp, "Unable to initialize DH parameters: %s", + gnutls_strerror(ret)); + return -1; + } + ret = gnutls_dh_params_generate2(*dh_params, DH_BITS); + if (ret < 0) { + gnutls_dh_params_deinit(*dh_params); + *dh_params = NULL; + error_setg(errp, "Unable to generate DH parameters: %s", + gnutls_strerror(ret)); + return -1; + } + } else { + GError *gerr = NULL; + gchar *contents; + gsize len; + gnutls_datum_t data; + if (!g_file_get_contents(filename, + &contents, + &len, + &gerr)) { + + error_setg(errp, "%s", gerr->message); + g_error_free(gerr); + return -1; + } + data.data = (unsigned char *)contents; + data.size = len; + ret = gnutls_dh_params_init(dh_params); + if (ret < 0) { + g_free(contents); + error_setg(errp, "Unable to initialize DH parameters: %s", + gnutls_strerror(ret)); + return -1; + } + ret = gnutls_dh_params_import_pkcs3(*dh_params, + &data, + GNUTLS_X509_FMT_PEM); + g_free(contents); + if (ret < 0) { + gnutls_dh_params_deinit(*dh_params); + *dh_params = NULL; + error_setg(errp, "Unable to load DH parameters from %s: %s", + filename, gnutls_strerror(ret)); + return -1; + } + } + + return 0; +} + + +int +qcrypto_tls_creds_get_path(QCryptoTLSCreds *creds, + const char *filename, + bool required, + char **cred, + Error **errp) +{ + struct stat sb; + int ret = -1; + + if (!creds->dir) { + if (required) { + error_setg(errp, "Missing 'dir' property value"); + return -1; + } else { + return 0; + } + } + + *cred = g_strdup_printf("%s/%s", creds->dir, filename); + + if (stat(*cred, &sb) < 0) { + if (errno == ENOENT && !required) { + ret = 0; + } else { + error_setg_errno(errp, errno, + "Unable to access credentials %s", + *cred); + } + g_free(*cred); + *cred = NULL; + goto cleanup; + } + + trace_qcrypto_tls_creds_get_path(creds, filename, + *cred ? *cred : "<none>"); + ret = 0; + cleanup: + return ret; +} + + +#endif /* ! CONFIG_GNUTLS */ + + +static void +qcrypto_tls_creds_prop_set_verify(Object *obj, + bool value, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); + + creds->verifyPeer = value; +} + + +static bool +qcrypto_tls_creds_prop_get_verify(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); + + return creds->verifyPeer; +} + + +static void +qcrypto_tls_creds_prop_set_dir(Object *obj, + const char *value, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); + + creds->dir = g_strdup(value); +} + + +static char * +qcrypto_tls_creds_prop_get_dir(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); + + return g_strdup(creds->dir); +} + + +static void +qcrypto_tls_creds_prop_set_endpoint(Object *obj, + int value, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); + + creds->endpoint = value; +} + + +static int +qcrypto_tls_creds_prop_get_endpoint(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); + + return creds->endpoint; +} + + +static void +qcrypto_tls_creds_init(Object *obj) +{ + QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); + + creds->verifyPeer = true; + + object_property_add_bool(obj, "verify-peer", + qcrypto_tls_creds_prop_get_verify, + qcrypto_tls_creds_prop_set_verify, + NULL); + object_property_add_str(obj, "dir", + qcrypto_tls_creds_prop_get_dir, + qcrypto_tls_creds_prop_set_dir, + NULL); + object_property_add_enum(obj, "endpoint", + "QCryptoTLSCredsEndpoint", + QCryptoTLSCredsEndpoint_lookup, + qcrypto_tls_creds_prop_get_endpoint, + qcrypto_tls_creds_prop_set_endpoint, + NULL); +} + + +static void +qcrypto_tls_creds_finalize(Object *obj) +{ + QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); + + g_free(creds->dir); +} + + +static const TypeInfo qcrypto_tls_creds_info = { + .parent = TYPE_OBJECT, + .name = TYPE_QCRYPTO_TLS_CREDS, + .instance_size = sizeof(QCryptoTLSCreds), + .instance_init = qcrypto_tls_creds_init, + .instance_finalize = qcrypto_tls_creds_finalize, + .class_size = sizeof(QCryptoTLSCredsClass), + .abstract = true, +}; + + +static void +qcrypto_tls_creds_register_types(void) +{ + type_register_static(&qcrypto_tls_creds_info); +} + + +type_init(qcrypto_tls_creds_register_types); diff --git a/crypto/tlscredsanon.c b/crypto/tlscredsanon.c new file mode 100644 index 0000000000..c3fcdaff06 --- /dev/null +++ b/crypto/tlscredsanon.c @@ -0,0 +1,223 @@ +/* + * QEMU crypto TLS anonymous credential support + * + * Copyright (c) 2015 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 "crypto/tlscredsanon.h" +#include "crypto/tlscredspriv.h" +#include "qom/object_interfaces.h" +#include "trace.h" + + +#ifdef CONFIG_GNUTLS + + +static int +qcrypto_tls_creds_anon_load(QCryptoTLSCredsAnon *creds, + Error **errp) +{ + char *dhparams = NULL; + int ret; + int rv = -1; + + trace_qcrypto_tls_creds_anon_load(creds, + creds->parent_obj.dir ? creds->parent_obj.dir : "<nodir>"); + + if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { + if (qcrypto_tls_creds_get_path(&creds->parent_obj, + QCRYPTO_TLS_CREDS_DH_PARAMS, + false, &dhparams, errp) < 0) { + goto cleanup; + } + + ret = gnutls_anon_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_anon_set_server_dh_params(creds->data.server, + creds->parent_obj.dh_params); + } else { + ret = gnutls_anon_allocate_client_credentials(&creds->data.client); + if (ret < 0) { + error_setg(errp, "Cannot allocate credentials: %s", + gnutls_strerror(ret)); + goto cleanup; + } + } + + rv = 0; + cleanup: + g_free(dhparams); + return rv; +} + + +static void +qcrypto_tls_creds_anon_unload(QCryptoTLSCredsAnon *creds) +{ + if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) { + if (creds->data.client) { + gnutls_anon_free_client_credentials(creds->data.client); + creds->data.client = NULL; + } + } else { + if (creds->data.server) { + gnutls_anon_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_anon_load(QCryptoTLSCredsAnon *creds G_GNUC_UNUSED, + Error **errp) +{ + error_setg(errp, "TLS credentials support requires GNUTLS"); +} + + +static void +qcrypto_tls_creds_anon_unload(QCryptoTLSCredsAnon *creds G_GNUC_UNUSED) +{ + /* nada */ +} + + +#endif /* ! CONFIG_GNUTLS */ + + +static void +qcrypto_tls_creds_anon_prop_set_loaded(Object *obj, + bool value, + Error **errp) +{ + QCryptoTLSCredsAnon *creds = QCRYPTO_TLS_CREDS_ANON(obj); + + if (value) { + qcrypto_tls_creds_anon_load(creds, errp); + } else { + qcrypto_tls_creds_anon_unload(creds); + } +} + + +#ifdef CONFIG_GNUTLS + + +static bool +qcrypto_tls_creds_anon_prop_get_loaded(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCredsAnon *creds = QCRYPTO_TLS_CREDS_ANON(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_anon_prop_get_loaded(Object *obj G_GNUC_UNUSED, + Error **errp G_GNUC_UNUSED) +{ + return false; +} + + +#endif /* ! CONFIG_GNUTLS */ + + +static void +qcrypto_tls_creds_anon_complete(UserCreatable *uc, Error **errp) +{ + object_property_set_bool(OBJECT(uc), true, "loaded", errp); +} + + +static void +qcrypto_tls_creds_anon_init(Object *obj) +{ + object_property_add_bool(obj, "loaded", + qcrypto_tls_creds_anon_prop_get_loaded, + qcrypto_tls_creds_anon_prop_set_loaded, + NULL); +} + + +static void +qcrypto_tls_creds_anon_finalize(Object *obj) +{ + QCryptoTLSCredsAnon *creds = QCRYPTO_TLS_CREDS_ANON(obj); + + qcrypto_tls_creds_anon_unload(creds); +} + + +static void +qcrypto_tls_creds_anon_class_init(ObjectClass *oc, void *data) +{ + UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); + + ucc->complete = qcrypto_tls_creds_anon_complete; +} + + +static const TypeInfo qcrypto_tls_creds_anon_info = { + .parent = TYPE_QCRYPTO_TLS_CREDS, + .name = TYPE_QCRYPTO_TLS_CREDS_ANON, + .instance_size = sizeof(QCryptoTLSCredsAnon), + .instance_init = qcrypto_tls_creds_anon_init, + .instance_finalize = qcrypto_tls_creds_anon_finalize, + .class_size = sizeof(QCryptoTLSCredsAnonClass), + .class_init = qcrypto_tls_creds_anon_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + + +static void +qcrypto_tls_creds_anon_register_types(void) +{ + type_register_static(&qcrypto_tls_creds_anon_info); +} + + +type_init(qcrypto_tls_creds_anon_register_types); diff --git a/crypto/tlscredspriv.h b/crypto/tlscredspriv.h new file mode 100644 index 0000000000..9222be4a9e --- /dev/null +++ b/crypto/tlscredspriv.h @@ -0,0 +1,42 @@ +/* + * QEMU crypto TLS credential support private helpers + * + * Copyright (c) 2015 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_TLSCRED_PRIV_H__ +#define QCRYPTO_TLSCRED_PRIV_H__ + +#include "crypto/tlscreds.h" + +#ifdef CONFIG_GNUTLS + +int qcrypto_tls_creds_get_path(QCryptoTLSCreds *creds, + const char *filename, + bool required, + char **cred, + Error **errp); + +int qcrypto_tls_creds_get_dh_params_file(QCryptoTLSCreds *creds, + const char *filename, + gnutls_dh_params_t *dh_params, + Error **errp); + +#endif + +#endif /* QCRYPTO_TLSCRED_PRIV_H__ */ + diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c new file mode 100644 index 0000000000..dc46bc40f7 --- /dev/null +++ b/crypto/tlscredsx509.c @@ -0,0 +1,809 @@ +/* + * QEMU crypto TLS x509 credential support + * + * Copyright (c) 2015 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 "crypto/tlscredsx509.h" +#include "crypto/tlscredspriv.h" +#include "qom/object_interfaces.h" +#include "trace.h" + + +#ifdef CONFIG_GNUTLS + +#include <gnutls/x509.h> + + +static int +qcrypto_tls_creds_check_cert_times(gnutls_x509_crt_t cert, + const char *certFile, + bool isServer, + bool isCA, + Error **errp) +{ + time_t now = time(NULL); + + if (now == ((time_t)-1)) { + error_setg_errno(errp, errno, "cannot get current time"); + return -1; + } + + if (gnutls_x509_crt_get_expiration_time(cert) < now) { + error_setg(errp, + (isCA ? + "The CA certificate %s has expired" : + (isServer ? + "The server certificate %s has expired" : + "The client certificate %s has expired")), + certFile); + return -1; + } + + if (gnutls_x509_crt_get_activation_time(cert) > now) { + error_setg(errp, + (isCA ? + "The CA certificate %s is not yet active" : + (isServer ? + "The server certificate %s is not yet active" : + "The client certificate %s is not yet active")), + certFile); + return -1; + } + + return 0; +} + + +#if LIBGNUTLS_VERSION_NUMBER >= 2 +/* + * The gnutls_x509_crt_get_basic_constraints function isn't + * available in GNUTLS 1.0.x branches. This isn't critical + * though, since gnutls_certificate_verify_peers2 will do + * pretty much the same check at runtime, so we can just + * disable this code + */ +static int +qcrypto_tls_creds_check_cert_basic_constraints(QCryptoTLSCredsX509 *creds, + gnutls_x509_crt_t cert, + const char *certFile, + bool isServer, + bool isCA, + Error **errp) +{ + int status; + + status = gnutls_x509_crt_get_basic_constraints(cert, NULL, NULL, NULL); + trace_qcrypto_tls_creds_x509_check_basic_constraints( + creds, certFile, status); + + if (status > 0) { /* It is a CA cert */ + if (!isCA) { + error_setg(errp, isServer ? + "The certificate %s basic constraints show a CA, " + "but we need one for a server" : + "The certificate %s basic constraints show a CA, " + "but we need one for a client", + certFile); + return -1; + } + } else if (status == 0) { /* It is not a CA cert */ + if (isCA) { + error_setg(errp, + "The certificate %s basic constraints do not " + "show a CA", + certFile); + return -1; + } + } else if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + /* Missing basicConstraints */ + if (isCA) { + error_setg(errp, + "The certificate %s is missing basic constraints " + "for a CA", + certFile); + return -1; + } + } else { /* General error */ + error_setg(errp, + "Unable to query certificate %s basic constraints: %s", + certFile, gnutls_strerror(status)); + return -1; + } + + return 0; +} +#endif + + +static int +qcrypto_tls_creds_check_cert_key_usage(QCryptoTLSCredsX509 *creds, + gnutls_x509_crt_t cert, + const char *certFile, + bool isCA, + Error **errp) +{ + int status; + unsigned int usage = 0; + unsigned int critical = 0; + + status = gnutls_x509_crt_get_key_usage(cert, &usage, &critical); + trace_qcrypto_tls_creds_x509_check_key_usage( + creds, certFile, status, usage, critical); + + if (status < 0) { + if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + usage = isCA ? GNUTLS_KEY_KEY_CERT_SIGN : + GNUTLS_KEY_DIGITAL_SIGNATURE|GNUTLS_KEY_KEY_ENCIPHERMENT; + } else { + error_setg(errp, + "Unable to query certificate %s key usage: %s", + certFile, gnutls_strerror(status)); + return -1; + } + } + + if (isCA) { + if (!(usage & GNUTLS_KEY_KEY_CERT_SIGN)) { + if (critical) { + error_setg(errp, + "Certificate %s usage does not permit " + "certificate signing", certFile); + return -1; + } + } + } else { + if (!(usage & GNUTLS_KEY_DIGITAL_SIGNATURE)) { + if (critical) { + error_setg(errp, + "Certificate %s usage does not permit digital " + "signature", certFile); + return -1; + } + } + if (!(usage & GNUTLS_KEY_KEY_ENCIPHERMENT)) { + if (critical) { + error_setg(errp, + "Certificate %s usage does not permit key " + "encipherment", certFile); + return -1; + } + } + } + + return 0; +} + + +static int +qcrypto_tls_creds_check_cert_key_purpose(QCryptoTLSCredsX509 *creds, + gnutls_x509_crt_t cert, + const char *certFile, + bool isServer, + Error **errp) +{ + int status; + size_t i; + unsigned int purposeCritical; + unsigned int critical; + char *buffer = NULL; + size_t size; + bool allowClient = false, allowServer = false; + + critical = 0; + for (i = 0; ; i++) { + size = 0; + status = gnutls_x509_crt_get_key_purpose_oid(cert, i, buffer, + &size, NULL); + + if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + + /* If there is no data at all, then we must allow + client/server to pass */ + if (i == 0) { + allowServer = allowClient = true; + } + break; + } + if (status != GNUTLS_E_SHORT_MEMORY_BUFFER) { + error_setg(errp, + "Unable to query certificate %s key purpose: %s", + certFile, gnutls_strerror(status)); + return -1; + } + + buffer = g_new0(char, size); + + status = gnutls_x509_crt_get_key_purpose_oid(cert, i, buffer, + &size, &purposeCritical); + + if (status < 0) { + trace_qcrypto_tls_creds_x509_check_key_purpose( + creds, certFile, status, "<none>", purposeCritical); + g_free(buffer); + error_setg(errp, + "Unable to query certificate %s key purpose: %s", + certFile, gnutls_strerror(status)); + return -1; + } + trace_qcrypto_tls_creds_x509_check_key_purpose( + creds, certFile, status, buffer, purposeCritical); + if (purposeCritical) { + critical = true; + } + + if (g_str_equal(buffer, GNUTLS_KP_TLS_WWW_SERVER)) { + allowServer = true; + } else if (g_str_equal(buffer, GNUTLS_KP_TLS_WWW_CLIENT)) { + allowClient = true; + } else if (g_str_equal(buffer, GNUTLS_KP_ANY)) { + allowServer = allowClient = true; + } + + g_free(buffer); + } + + if (isServer) { + if (!allowServer) { + if (critical) { + error_setg(errp, + "Certificate %s purpose does not allow " + "use with a TLS server", certFile); + return -1; + } + } + } else { + if (!allowClient) { + if (critical) { + error_setg(errp, + "Certificate %s purpose does not allow use " + "with a TLS client", certFile); + return -1; + } + } + } + + return 0; +} + + +static int +qcrypto_tls_creds_check_cert(QCryptoTLSCredsX509 *creds, + gnutls_x509_crt_t cert, + const char *certFile, + bool isServer, + bool isCA, + Error **errp) +{ + if (qcrypto_tls_creds_check_cert_times(cert, certFile, + isServer, isCA, + errp) < 0) { + return -1; + } + +#if LIBGNUTLS_VERSION_NUMBER >= 2 + if (qcrypto_tls_creds_check_cert_basic_constraints(creds, + cert, certFile, + isServer, isCA, + errp) < 0) { + return -1; + } +#endif + + if (qcrypto_tls_creds_check_cert_key_usage(creds, + cert, certFile, + isCA, errp) < 0) { + return -1; + } + + if (!isCA && + qcrypto_tls_creds_check_cert_key_purpose(creds, + cert, certFile, + isServer, errp) < 0) { + return -1; + } + + return 0; +} + + +static int +qcrypto_tls_creds_check_cert_pair(gnutls_x509_crt_t cert, + const char *certFile, + gnutls_x509_crt_t *cacerts, + size_t ncacerts, + const char *cacertFile, + bool isServer, + Error **errp) +{ + unsigned int status; + + if (gnutls_x509_crt_list_verify(&cert, 1, + cacerts, ncacerts, + NULL, 0, + 0, &status) < 0) { + error_setg(errp, isServer ? + "Unable to verify server certificate %s against " + "CA certificate %s" : + "Unable to verify client certificate %s against " + "CA certificate %s", + certFile, cacertFile); + return -1; + } + + if (status != 0) { + const char *reason = "Invalid certificate"; + + if (status & GNUTLS_CERT_INVALID) { + reason = "The certificate is not trusted"; + } + + if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { + reason = "The certificate hasn't got a known issuer"; + } + + if (status & GNUTLS_CERT_REVOKED) { + reason = "The certificate has been revoked"; + } + +#ifndef GNUTLS_1_0_COMPAT + if (status & GNUTLS_CERT_INSECURE_ALGORITHM) { + reason = "The certificate uses an insecure algorithm"; + } +#endif + + error_setg(errp, + "Our own certificate %s failed validation against %s: %s", + certFile, cacertFile, reason); + return -1; + } + + return 0; +} + + +static gnutls_x509_crt_t +qcrypto_tls_creds_load_cert(QCryptoTLSCredsX509 *creds, + const char *certFile, + bool isServer, + Error **errp) +{ + gnutls_datum_t data; + gnutls_x509_crt_t cert = NULL; + char *buf = NULL; + gsize buflen; + GError *gerr; + int ret = -1; + + trace_qcrypto_tls_creds_x509_load_cert(creds, isServer, certFile); + + if (gnutls_x509_crt_init(&cert) < 0) { + error_setg(errp, "Unable to initialize certificate"); + goto cleanup; + } + + if (!g_file_get_contents(certFile, &buf, &buflen, &gerr)) { + error_setg(errp, "Cannot load CA cert list %s: %s", + certFile, gerr->message); + g_error_free(gerr); + goto cleanup; + } + + data.data = (unsigned char *)buf; + data.size = strlen(buf); + + if (gnutls_x509_crt_import(cert, &data, GNUTLS_X509_FMT_PEM) < 0) { + error_setg(errp, isServer ? + "Unable to import server certificate %s" : + "Unable to import client certificate %s", + certFile); + goto cleanup; + } + + ret = 0; + + cleanup: + if (ret != 0) { + gnutls_x509_crt_deinit(cert); + cert = NULL; + } + g_free(buf); + return cert; +} + + +static int +qcrypto_tls_creds_load_ca_cert_list(QCryptoTLSCredsX509 *creds, + const char *certFile, + gnutls_x509_crt_t *certs, + unsigned int certMax, + size_t *ncerts, + Error **errp) +{ + gnutls_datum_t data; + char *buf = NULL; + gsize buflen; + int ret = -1; + GError *gerr = NULL; + + *ncerts = 0; + trace_qcrypto_tls_creds_x509_load_cert_list(creds, certFile); + + if (!g_file_get_contents(certFile, &buf, &buflen, &gerr)) { + error_setg(errp, "Cannot load CA cert list %s: %s", + certFile, gerr->message); + g_error_free(gerr); + goto cleanup; + } + + data.data = (unsigned char *)buf; + data.size = strlen(buf); + + if (gnutls_x509_crt_list_import(certs, &certMax, &data, + GNUTLS_X509_FMT_PEM, 0) < 0) { + error_setg(errp, + "Unable to import CA certificate list %s", + certFile); + goto cleanup; + } + *ncerts = certMax; + + ret = 0; + + cleanup: + g_free(buf); + return ret; +} + + +#define MAX_CERTS 16 +static int +qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds, + bool isServer, + const char *cacertFile, + const char *certFile, + Error **errp) +{ + gnutls_x509_crt_t cert = NULL; + gnutls_x509_crt_t cacerts[MAX_CERTS]; + size_t ncacerts = 0; + size_t i; + int ret = -1; + + memset(cacerts, 0, sizeof(cacerts)); + if (access(certFile, R_OK) == 0) { + cert = qcrypto_tls_creds_load_cert(creds, + certFile, isServer, + errp); + if (!cert) { + goto cleanup; + } + } + if (access(cacertFile, R_OK) == 0) { + if (qcrypto_tls_creds_load_ca_cert_list(creds, + cacertFile, cacerts, + MAX_CERTS, &ncacerts, + errp) < 0) { + goto cleanup; + } + } + + if (cert && + qcrypto_tls_creds_check_cert(creds, + cert, certFile, isServer, + false, errp) < 0) { + goto cleanup; + } + + for (i = 0; i < ncacerts; i++) { + if (qcrypto_tls_creds_check_cert(creds, + cacerts[i], cacertFile, + isServer, true, errp) < 0) { + goto cleanup; + } + } + + if (cert && ncacerts && + qcrypto_tls_creds_check_cert_pair(cert, certFile, cacerts, + ncacerts, cacertFile, + isServer, errp) < 0) { + goto cleanup; + } + + ret = 0; + + cleanup: + if (cert) { + gnutls_x509_crt_deinit(cert); + } + for (i = 0; i < ncacerts; i++) { + gnutls_x509_crt_deinit(cacerts[i]); + } + return ret; +} + + +static int +qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds, + Error **errp) +{ + char *cacert = NULL, *cacrl = NULL, *cert = NULL, + *key = NULL, *dhparams = NULL; + int ret; + int rv = -1; + + trace_qcrypto_tls_creds_x509_load(creds, + creds->parent_obj.dir ? creds->parent_obj.dir : "<nodir>"); + + if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { + if (qcrypto_tls_creds_get_path(&creds->parent_obj, + QCRYPTO_TLS_CREDS_X509_CA_CERT, + true, &cacert, errp) < 0 || + qcrypto_tls_creds_get_path(&creds->parent_obj, + QCRYPTO_TLS_CREDS_X509_CA_CRL, + false, &cacrl, errp) < 0 || + qcrypto_tls_creds_get_path(&creds->parent_obj, + QCRYPTO_TLS_CREDS_X509_SERVER_CERT, + true, &cert, errp) < 0 || + qcrypto_tls_creds_get_path(&creds->parent_obj, + QCRYPTO_TLS_CREDS_X509_SERVER_KEY, + true, &key, errp) < 0 || + qcrypto_tls_creds_get_path(&creds->parent_obj, + QCRYPTO_TLS_CREDS_DH_PARAMS, + false, &dhparams, errp) < 0) { + goto cleanup; + } + } else { + if (qcrypto_tls_creds_get_path(&creds->parent_obj, + QCRYPTO_TLS_CREDS_X509_CA_CERT, + true, &cacert, errp) < 0 || + qcrypto_tls_creds_get_path(&creds->parent_obj, + QCRYPTO_TLS_CREDS_X509_CLIENT_CERT, + false, &cert, errp) < 0 || + qcrypto_tls_creds_get_path(&creds->parent_obj, + QCRYPTO_TLS_CREDS_X509_CLIENT_KEY, + false, &key, errp) < 0) { + goto cleanup; + } + } + + if (creds->sanityCheck && + qcrypto_tls_creds_x509_sanity_check(creds, + creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, + cacert, cert, errp) < 0) { + goto cleanup; + } + + ret = gnutls_certificate_allocate_credentials(&creds->data); + if (ret < 0) { + error_setg(errp, "Cannot allocate credentials: '%s'", + gnutls_strerror(ret)); + goto cleanup; + } + + ret = gnutls_certificate_set_x509_trust_file(creds->data, + cacert, + GNUTLS_X509_FMT_PEM); + if (ret < 0) { + error_setg(errp, "Cannot load CA certificate '%s': %s", + cacert, gnutls_strerror(ret)); + goto cleanup; + } + + if (cert != NULL && key != NULL) { + ret = gnutls_certificate_set_x509_key_file(creds->data, + cert, key, + GNUTLS_X509_FMT_PEM); + if (ret < 0) { + error_setg(errp, "Cannot load certificate '%s' & key '%s': %s", + cert, key, gnutls_strerror(ret)); + goto cleanup; + } + } + + if (cacrl != NULL) { + ret = gnutls_certificate_set_x509_crl_file(creds->data, + cacrl, + GNUTLS_X509_FMT_PEM); + if (ret < 0) { + error_setg(errp, "Cannot load CRL '%s': %s", + cacrl, gnutls_strerror(ret)); + goto cleanup; + } + } + + if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { + if (qcrypto_tls_creds_get_dh_params_file(&creds->parent_obj, dhparams, + &creds->parent_obj.dh_params, + errp) < 0) { + goto cleanup; + } + gnutls_certificate_set_dh_params(creds->data, + creds->parent_obj.dh_params); + } + + rv = 0; + cleanup: + g_free(cacert); + g_free(cacrl); + g_free(cert); + g_free(key); + g_free(dhparams); + return rv; +} + + +static void +qcrypto_tls_creds_x509_unload(QCryptoTLSCredsX509 *creds) +{ + if (creds->data) { + gnutls_certificate_free_credentials(creds->data); + creds->data = NULL; + } +} + + +#else /* ! CONFIG_GNUTLS */ + + +static void +qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds G_GNUC_UNUSED, + Error **errp) +{ + error_setg(errp, "TLS credentials support requires GNUTLS"); +} + + +static void +qcrypto_tls_creds_x509_unload(QCryptoTLSCredsX509 *creds G_GNUC_UNUSED) +{ + /* nada */ +} + + +#endif /* ! CONFIG_GNUTLS */ + + +static void +qcrypto_tls_creds_x509_prop_set_loaded(Object *obj, + bool value, + Error **errp) +{ + QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); + + if (value) { + qcrypto_tls_creds_x509_load(creds, errp); + } else { + qcrypto_tls_creds_x509_unload(creds); + } +} + + +#ifdef CONFIG_GNUTLS + + +static bool +qcrypto_tls_creds_x509_prop_get_loaded(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); + + return creds->data != NULL; +} + + +#else /* ! CONFIG_GNUTLS */ + + +static bool +qcrypto_tls_creds_x509_prop_get_loaded(Object *obj G_GNUC_UNUSED, + Error **errp G_GNUC_UNUSED) +{ + return false; +} + + +#endif /* ! CONFIG_GNUTLS */ + + +static void +qcrypto_tls_creds_x509_prop_set_sanity(Object *obj, + bool value, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); + + creds->sanityCheck = value; +} + + +static bool +qcrypto_tls_creds_x509_prop_get_sanity(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); + + return creds->sanityCheck; +} + + +static void +qcrypto_tls_creds_x509_complete(UserCreatable *uc, Error **errp) +{ + object_property_set_bool(OBJECT(uc), true, "loaded", errp); +} + + +static void +qcrypto_tls_creds_x509_init(Object *obj) +{ + QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); + + creds->sanityCheck = true; + + object_property_add_bool(obj, "loaded", + qcrypto_tls_creds_x509_prop_get_loaded, + qcrypto_tls_creds_x509_prop_set_loaded, + NULL); + object_property_add_bool(obj, "sanity-check", + qcrypto_tls_creds_x509_prop_get_sanity, + qcrypto_tls_creds_x509_prop_set_sanity, + NULL); +} + + +static void +qcrypto_tls_creds_x509_finalize(Object *obj) +{ + QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); + + qcrypto_tls_creds_x509_unload(creds); +} + + +static void +qcrypto_tls_creds_x509_class_init(ObjectClass *oc, void *data) +{ + UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); + + ucc->complete = qcrypto_tls_creds_x509_complete; +} + + +static const TypeInfo qcrypto_tls_creds_x509_info = { + .parent = TYPE_QCRYPTO_TLS_CREDS, + .name = TYPE_QCRYPTO_TLS_CREDS_X509, + .instance_size = sizeof(QCryptoTLSCredsX509), + .instance_init = qcrypto_tls_creds_x509_init, + .instance_finalize = qcrypto_tls_creds_x509_finalize, + .class_size = sizeof(QCryptoTLSCredsX509Class), + .class_init = qcrypto_tls_creds_x509_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + + +static void +qcrypto_tls_creds_x509_register_types(void) +{ + type_register_static(&qcrypto_tls_creds_x509_info); +} + + +type_init(qcrypto_tls_creds_x509_register_types); diff --git a/crypto/tlssession.c b/crypto/tlssession.c new file mode 100644 index 0000000000..ffc5c47949 --- /dev/null +++ b/crypto/tlssession.c @@ -0,0 +1,574 @@ +/* + * QEMU crypto TLS session support + * + * Copyright (c) 2015 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 "crypto/tlssession.h" +#include "crypto/tlscredsanon.h" +#include "crypto/tlscredsx509.h" +#include "qemu/acl.h" +#include "trace.h" + +#ifdef CONFIG_GNUTLS + + +#include <gnutls/x509.h> + + +struct QCryptoTLSSession { + QCryptoTLSCreds *creds; + gnutls_session_t handle; + char *hostname; + char *aclname; + bool handshakeComplete; + QCryptoTLSSessionWriteFunc writeFunc; + QCryptoTLSSessionReadFunc readFunc; + void *opaque; + char *peername; +}; + + +void +qcrypto_tls_session_free(QCryptoTLSSession *session) +{ + if (!session) { + return; + } + + gnutls_deinit(session->handle); + g_free(session->hostname); + g_free(session->peername); + g_free(session->aclname); + object_unref(OBJECT(session->creds)); + g_free(session); +} + + +static ssize_t +qcrypto_tls_session_push(void *opaque, const void *buf, size_t len) +{ + QCryptoTLSSession *session = opaque; + + if (!session->writeFunc) { + errno = EIO; + return -1; + }; + + return session->writeFunc(buf, len, session->opaque); +} + + +static ssize_t +qcrypto_tls_session_pull(void *opaque, void *buf, size_t len) +{ + QCryptoTLSSession *session = opaque; + + if (!session->readFunc) { + errno = EIO; + return -1; + }; + + return session->readFunc(buf, len, session->opaque); +} + + +QCryptoTLSSession * +qcrypto_tls_session_new(QCryptoTLSCreds *creds, + const char *hostname, + const char *aclname, + QCryptoTLSCredsEndpoint endpoint, + Error **errp) +{ + QCryptoTLSSession *session; + int ret; + + session = g_new0(QCryptoTLSSession, 1); + trace_qcrypto_tls_session_new( + session, creds, hostname ? hostname : "<none>", + aclname ? aclname : "<none>", endpoint); + + if (hostname) { + session->hostname = g_strdup(hostname); + } + if (aclname) { + session->aclname = g_strdup(aclname); + } + session->creds = creds; + object_ref(OBJECT(creds)); + + if (creds->endpoint != endpoint) { + error_setg(errp, "Credentials endpoint doesn't match session"); + goto error; + } + + if (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { + ret = gnutls_init(&session->handle, GNUTLS_SERVER); + } else { + ret = gnutls_init(&session->handle, GNUTLS_CLIENT); + } + if (ret < 0) { + error_setg(errp, "Cannot initialize TLS session: %s", + gnutls_strerror(ret)); + goto error; + } + + if (object_dynamic_cast(OBJECT(creds), + TYPE_QCRYPTO_TLS_CREDS_ANON)) { + QCryptoTLSCredsAnon *acreds = QCRYPTO_TLS_CREDS_ANON(creds); + + ret = gnutls_priority_set_direct(session->handle, + "NORMAL:+ANON-DH", NULL); + if (ret < 0) { + error_setg(errp, "Unable to set TLS session priority: %s", + gnutls_strerror(ret)); + goto error; + } + if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { + ret = gnutls_credentials_set(session->handle, + GNUTLS_CRD_ANON, + acreds->data.server); + } else { + ret = gnutls_credentials_set(session->handle, + GNUTLS_CRD_ANON, + acreds->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); + + ret = gnutls_set_default_priority(session->handle); + if (ret < 0) { + error_setg(errp, "Cannot set default TLS session priority: %s", + gnutls_strerror(ret)); + goto error; + } + ret = gnutls_credentials_set(session->handle, + GNUTLS_CRD_CERTIFICATE, + tcreds->data); + if (ret < 0) { + error_setg(errp, "Cannot set session credentials: %s", + gnutls_strerror(ret)); + goto error; + } + + if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { + /* This requests, but does not enforce a client cert. + * The cert checking code later does enforcement */ + gnutls_certificate_server_set_request(session->handle, + GNUTLS_CERT_REQUEST); + } + } else { + error_setg(errp, "Unsupported TLS credentials type %s", + object_get_typename(OBJECT(creds))); + goto error; + } + + gnutls_transport_set_ptr(session->handle, session); + gnutls_transport_set_push_function(session->handle, + qcrypto_tls_session_push); + gnutls_transport_set_pull_function(session->handle, + qcrypto_tls_session_pull); + + return session; + + error: + qcrypto_tls_session_free(session); + return NULL; +} + +static int +qcrypto_tls_session_check_certificate(QCryptoTLSSession *session, + Error **errp) +{ + int ret; + unsigned int status; + const gnutls_datum_t *certs; + unsigned int nCerts, i; + time_t now; + gnutls_x509_crt_t cert = NULL; + + now = time(NULL); + if (now == ((time_t)-1)) { + error_setg_errno(errp, errno, "Cannot get current time"); + return -1; + } + + ret = gnutls_certificate_verify_peers2(session->handle, &status); + if (ret < 0) { + error_setg(errp, "Verify failed: %s", gnutls_strerror(ret)); + return -1; + } + + if (status != 0) { + const char *reason = "Invalid certificate"; + + if (status & GNUTLS_CERT_INVALID) { + reason = "The certificate is not trusted"; + } + + if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { + reason = "The certificate hasn't got a known issuer"; + } + + if (status & GNUTLS_CERT_REVOKED) { + reason = "The certificate has been revoked"; + } + + if (status & GNUTLS_CERT_INSECURE_ALGORITHM) { + reason = "The certificate uses an insecure algorithm"; + } + + error_setg(errp, "%s", reason); + return -1; + } + + certs = gnutls_certificate_get_peers(session->handle, &nCerts); + if (!certs) { + error_setg(errp, "No certificate peers"); + return -1; + } + + for (i = 0; i < nCerts; i++) { + ret = gnutls_x509_crt_init(&cert); + if (ret < 0) { + error_setg(errp, "Cannot initialize certificate: %s", + gnutls_strerror(ret)); + return -1; + } + + ret = gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER); + if (ret < 0) { + error_setg(errp, "Cannot import certificate: %s", + gnutls_strerror(ret)); + goto error; + } + + if (gnutls_x509_crt_get_expiration_time(cert) < now) { + error_setg(errp, "The certificate has expired"); + goto error; + } + + if (gnutls_x509_crt_get_activation_time(cert) > now) { + error_setg(errp, "The certificate is not yet activated"); + goto error; + } + + if (gnutls_x509_crt_get_activation_time(cert) > now) { + error_setg(errp, "The certificate is not yet activated"); + goto error; + } + + if (i == 0) { + size_t dnameSize = 1024; + session->peername = g_malloc(dnameSize); + requery: + ret = gnutls_x509_crt_get_dn(cert, session->peername, &dnameSize); + if (ret < 0) { + if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) { + session->peername = g_realloc(session->peername, + dnameSize); + goto requery; + } + error_setg(errp, "Cannot get client distinguished name: %s", + gnutls_strerror(ret)); + goto error; + } + if (session->aclname) { + qemu_acl *acl = qemu_acl_find(session->aclname); + int allow; + if (!acl) { + error_setg(errp, "Cannot find ACL %s", + session->aclname); + goto error; + } + + allow = qemu_acl_party_is_allowed(acl, session->peername); + + error_setg(errp, "TLS x509 ACL check for %s is %s", + session->peername, allow ? "allowed" : "denied"); + if (!allow) { + goto error; + } + } + if (session->hostname) { + if (!gnutls_x509_crt_check_hostname(cert, session->hostname)) { + error_setg(errp, + "Certificate does not match the hostname %s", + session->hostname); + goto error; + } + } + } + + gnutls_x509_crt_deinit(cert); + } + + return 0; + + error: + gnutls_x509_crt_deinit(cert); + return -1; +} + + +int +qcrypto_tls_session_check_credentials(QCryptoTLSSession *session, + Error **errp) +{ + if (object_dynamic_cast(OBJECT(session->creds), + TYPE_QCRYPTO_TLS_CREDS_ANON)) { + return 0; + } else if (object_dynamic_cast(OBJECT(session->creds), + TYPE_QCRYPTO_TLS_CREDS_X509)) { + if (session->creds->verifyPeer) { + return qcrypto_tls_session_check_certificate(session, + errp); + } else { + return 0; + } + } else { + error_setg(errp, "Unexpected credential type %s", + object_get_typename(OBJECT(session->creds))); + return -1; + } +} + + +void +qcrypto_tls_session_set_callbacks(QCryptoTLSSession *session, + QCryptoTLSSessionWriteFunc writeFunc, + QCryptoTLSSessionReadFunc readFunc, + void *opaque) +{ + session->writeFunc = writeFunc; + session->readFunc = readFunc; + session->opaque = opaque; +} + + +ssize_t +qcrypto_tls_session_write(QCryptoTLSSession *session, + const char *buf, + size_t len) +{ + ssize_t ret = gnutls_record_send(session->handle, buf, len); + + if (ret < 0) { + switch (ret) { + case GNUTLS_E_AGAIN: + errno = EAGAIN; + break; + case GNUTLS_E_INTERRUPTED: + errno = EINTR; + break; + default: + errno = EIO; + break; + } + ret = -1; + } + + return ret; +} + + +ssize_t +qcrypto_tls_session_read(QCryptoTLSSession *session, + char *buf, + size_t len) +{ + ssize_t ret = gnutls_record_recv(session->handle, buf, len); + + if (ret < 0) { + switch (ret) { + case GNUTLS_E_AGAIN: + errno = EAGAIN; + break; + case GNUTLS_E_INTERRUPTED: + errno = EINTR; + break; + default: + errno = EIO; + break; + } + ret = -1; + } + + return ret; +} + + +int +qcrypto_tls_session_handshake(QCryptoTLSSession *session, + Error **errp) +{ + int ret = gnutls_handshake(session->handle); + if (ret == 0) { + session->handshakeComplete = true; + } else { + if (ret == GNUTLS_E_INTERRUPTED || + ret == GNUTLS_E_AGAIN) { + ret = 1; + } else { + error_setg(errp, "TLS handshake failed: %s", + gnutls_strerror(ret)); + ret = -1; + } + } + + return ret; +} + + +QCryptoTLSSessionHandshakeStatus +qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *session) +{ + if (session->handshakeComplete) { + return QCRYPTO_TLS_HANDSHAKE_COMPLETE; + } else if (gnutls_record_get_direction(session->handle) == 0) { + return QCRYPTO_TLS_HANDSHAKE_RECVING; + } else { + return QCRYPTO_TLS_HANDSHAKE_SENDING; + } +} + + +int +qcrypto_tls_session_get_key_size(QCryptoTLSSession *session, + Error **errp) +{ + gnutls_cipher_algorithm_t cipher; + int ssf; + + cipher = gnutls_cipher_get(session->handle); + ssf = gnutls_cipher_get_key_size(cipher); + if (!ssf) { + error_setg(errp, "Cannot get TLS cipher key size"); + return -1; + } + return ssf; +} + + +char * +qcrypto_tls_session_get_peer_name(QCryptoTLSSession *session) +{ + if (session->peername) { + return g_strdup(session->peername); + } + return NULL; +} + + +#else /* ! CONFIG_GNUTLS */ + + +QCryptoTLSSession * +qcrypto_tls_session_new(QCryptoTLSCreds *creds G_GNUC_UNUSED, + const char *hostname G_GNUC_UNUSED, + const char *aclname G_GNUC_UNUSED, + QCryptoTLSCredsEndpoint endpoint G_GNUC_UNUSED, + Error **errp) +{ + error_setg(errp, "TLS requires GNUTLS support"); + return NULL; +} + + +void +qcrypto_tls_session_free(QCryptoTLSSession *sess G_GNUC_UNUSED) +{ +} + + +int +qcrypto_tls_session_check_credentials(QCryptoTLSSession *sess G_GNUC_UNUSED, + Error **errp) +{ + error_setg(errp, "TLS requires GNUTLS support"); + return -1; +} + + +void +qcrypto_tls_session_set_callbacks( + QCryptoTLSSession *sess G_GNUC_UNUSED, + QCryptoTLSSessionWriteFunc writeFunc G_GNUC_UNUSED, + QCryptoTLSSessionReadFunc readFunc G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ +} + + +ssize_t +qcrypto_tls_session_write(QCryptoTLSSession *sess, + const char *buf, + size_t len) +{ + errno = -EIO; + return -1; +} + + +ssize_t +qcrypto_tls_session_read(QCryptoTLSSession *sess, + char *buf, + size_t len) +{ + errno = -EIO; + return -1; +} + + +int +qcrypto_tls_session_handshake(QCryptoTLSSession *sess, + Error **errp) +{ + error_setg(errp, "TLS requires GNUTLS support"); + return -1; +} + + +QCryptoTLSSessionHandshakeStatus +qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *sess) +{ + return QCRYPTO_TLS_HANDSHAKE_COMPLETE; +} + + +int +qcrypto_tls_session_get_key_size(QCryptoTLSSession *sess, + Error **errp) +{ + error_setg(errp, "TLS requires GNUTLS support"); + return -1; +} + + +char * +qcrypto_tls_session_get_peer_name(QCryptoTLSSession *sess) +{ + return NULL; +} + +#endif diff --git a/default-configs/tilegx-linux-user.mak b/default-configs/tilegx-linux-user.mak new file mode 100644 index 0000000000..3e47493af0 --- /dev/null +++ b/default-configs/tilegx-linux-user.mak @@ -0,0 +1 @@ +# Default configuration for tilegx-linux-user diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt index ff16df2456..d960dc0234 100644 --- a/docs/qapi-code-gen.txt +++ b/docs/qapi-code-gen.txt @@ -236,6 +236,7 @@ both fields like this: === Enumeration types === Usage: { 'enum': STRING, 'data': ARRAY-OF-STRING } + { 'enum': STRING, '*prefix': STRING, 'data': ARRAY-OF-STRING } An enumeration type is a dictionary containing a single 'data' key whose value is a list of strings. An example enumeration is: @@ -247,6 +248,13 @@ useful. The list of strings should be lower case; if an enum name represents multiple words, use '-' between words. The string 'max' is not allowed as an enum value, and values should not be repeated. +The enum constants will be named by using a heuristic to turn the +type name into a set of underscore separated words. For the example +above, 'MyEnum' will turn into 'MY_ENUM' giving a constant name +of 'MY_ENUM_VALUE1' for the first value. If the default heuristic +does not result in a desirable name, the optional 'prefix' field +can be used when defining the enum. + The enumeration values are passed as strings over the Client JSON Protocol, but are encoded as C enum integral values in generated code. While the C code starts numbering at 0, it is better to use explicit diff --git a/hw/net/e1000.c b/hw/net/e1000.c index 5c6bcd0014..09c9e9d53b 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -740,7 +740,8 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp) memmove(tp->data, tp->header, tp->hdr_len); tp->size = tp->hdr_len; } - } while (split_size -= bytes); + split_size -= bytes; + } while (bytes && split_size); } else if (!tp->tse && tp->cptse) { // context descriptor TSE is not set, while data descriptor TSE is set DBGOUT(TXERR, "TCP segmentation error\n"); diff --git a/hw/net/ne2000.c b/hw/net/ne2000.c index 53c704ad41..010f9efccd 100644 --- a/hw/net/ne2000.c +++ b/hw/net/ne2000.c @@ -221,6 +221,9 @@ ssize_t ne2000_receive(NetClientState *nc, const uint8_t *buf, size_t size_) } index = s->curpag << 8; + if (index >= NE2000_PMEM_END) { + index = s->start; + } /* 4 bytes for header */ total_len = size + 4; /* address for next packet (4 bytes for CRC) */ @@ -244,7 +247,7 @@ ssize_t ne2000_receive(NetClientState *nc, const uint8_t *buf, size_t size_) if (index <= s->stop) avail = s->stop - index; else - avail = 0; + break; len = size; if (len > avail) len = avail; @@ -306,13 +309,19 @@ static void ne2000_ioport_write(void *opaque, uint32_t addr, uint32_t val) offset = addr | (page << 4); switch(offset) { case EN0_STARTPG: - s->start = val << 8; + if (val << 8 <= NE2000_PMEM_END) { + s->start = val << 8; + } break; case EN0_STOPPG: - s->stop = val << 8; + if (val << 8 <= NE2000_PMEM_END) { + s->stop = val << 8; + } break; case EN0_BOUNDARY: - s->boundary = val; + if (val << 8 < NE2000_PMEM_END) { + s->boundary = val; + } break; case EN0_IMR: s->imr = val; @@ -353,7 +362,9 @@ static void ne2000_ioport_write(void *opaque, uint32_t addr, uint32_t val) s->phys[offset - EN1_PHYS] = val; break; case EN1_CURPAG: - s->curpag = val; + if (val << 8 < NE2000_PMEM_END) { + s->curpag = val; + } break; case EN1_MULT ... EN1_MULT + 7: s->mult[offset - EN1_MULT] = val; diff --git a/include/crypto/tlscreds.h b/include/crypto/tlscreds.h new file mode 100644 index 0000000000..21761b7ce1 --- /dev/null +++ b/include/crypto/tlscreds.h @@ -0,0 +1,68 @@ +/* + * QEMU crypto TLS credential support + * + * Copyright (c) 2015 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_TLSCRED_H__ +#define QCRYPTO_TLSCRED_H__ + +#include "qemu-common.h" +#include "qapi/error.h" +#include "qom/object.h" + +#ifdef CONFIG_GNUTLS +#include <gnutls/gnutls.h> +#endif + +#define TYPE_QCRYPTO_TLS_CREDS "tls-creds" +#define QCRYPTO_TLS_CREDS(obj) \ + OBJECT_CHECK(QCryptoTLSCreds, (obj), TYPE_QCRYPTO_TLS_CREDS) + +typedef struct QCryptoTLSCreds QCryptoTLSCreds; +typedef struct QCryptoTLSCredsClass QCryptoTLSCredsClass; + +#define QCRYPTO_TLS_CREDS_DH_PARAMS "dh-params.pem" + + +/** + * QCryptoTLSCreds: + * + * The QCryptoTLSCreds object is an abstract base for different + * types of TLS handshake credentials. Most commonly the + * QCryptoTLSCredsX509 subclass will be used to provide x509 + * certificate credentials. + */ + +struct QCryptoTLSCreds { + Object parent_obj; + char *dir; + QCryptoTLSCredsEndpoint endpoint; +#ifdef CONFIG_GNUTLS + gnutls_dh_params_t dh_params; +#endif + bool verifyPeer; +}; + + +struct QCryptoTLSCredsClass { + ObjectClass parent_class; +}; + + +#endif /* QCRYPTO_TLSCRED_H__ */ + diff --git a/include/crypto/tlscredsanon.h b/include/crypto/tlscredsanon.h new file mode 100644 index 0000000000..d3976b84b9 --- /dev/null +++ b/include/crypto/tlscredsanon.h @@ -0,0 +1,112 @@ +/* + * QEMU crypto TLS anonymous credential support + * + * Copyright (c) 2015 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_TLSCRED_ANON_H__ +#define QCRYPTO_TLSCRED_ANON_H__ + +#include "crypto/tlscreds.h" + +#define TYPE_QCRYPTO_TLS_CREDS_ANON "tls-creds-anon" +#define QCRYPTO_TLS_CREDS_ANON(obj) \ + OBJECT_CHECK(QCryptoTLSCredsAnon, (obj), TYPE_QCRYPTO_TLS_CREDS_ANON) + + +typedef struct QCryptoTLSCredsAnon QCryptoTLSCredsAnon; +typedef struct QCryptoTLSCredsAnonClass QCryptoTLSCredsAnonClass; + +/** + * QCryptoTLSCredsAnon: + * + * The QCryptoTLSCredsAnon object provides a representation + * of anonymous credentials used perform a TLS handshake. + * This is primarily provided for backwards compatibility and + * its use is discouraged as it has poor security characteristics + * due to lacking MITM attack protection amongst other problems. + * + * This is a user creatable object, which can be instantiated + * via object_new_propv(): + * + * <example> + * <title>Creating anonymous TLS credential objects in code</title> + * <programlisting> + * Object *obj; + * Error *err = NULL; + * obj = object_new_propv(TYPE_QCRYPTO_TLS_CREDS_ANON, + * "tlscreds0", + * &err, + * "endpoint", "server", + * "dir", "/path/x509/cert/dir", + * "verify-peer", "yes", + * NULL); + * </programlisting> + * </example> + * + * Or via QMP: + * + * <example> + * <title>Creating anonymous TLS credential objects via QMP</title> + * <programlisting> + * { + * "execute": "object-add", "arguments": { + * "id": "tlscreds0", + * "qom-type": "tls-creds-anon", + * "props": { + * "endpoint": "server", + * "dir": "/path/to/x509/cert/dir", + * "verify-peer": false + * } + * } + * } + * </programlisting> + * </example> + * + * + * Or via the CLI: + * + * <example> + * <title>Creating anonymous TLS credential objects via CLI</title> + * <programlisting> + * qemu-system-x86_64 -object tls-creds-anon,id=tlscreds0,\ + * endpoint=server,verify-peer=off,\ + * dir=/path/to/x509/certdir/ + * </programlisting> + * </example> + * + */ + + +struct QCryptoTLSCredsAnon { + QCryptoTLSCreds parent_obj; +#ifdef CONFIG_GNUTLS + union { + gnutls_anon_server_credentials_t server; + gnutls_anon_client_credentials_t client; + } data; +#endif +}; + + +struct QCryptoTLSCredsAnonClass { + QCryptoTLSCredsClass parent_class; +}; + + +#endif /* QCRYPTO_TLSCRED_H__ */ + diff --git a/include/crypto/tlscredsx509.h b/include/crypto/tlscredsx509.h new file mode 100644 index 0000000000..b9785fddcf --- /dev/null +++ b/include/crypto/tlscredsx509.h @@ -0,0 +1,113 @@ +/* + * QEMU crypto TLS x509 credential support + * + * Copyright (c) 2015 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_TLSCRED_X509_H__ +#define QCRYPTO_TLSCRED_X509_H__ + +#include "crypto/tlscreds.h" + +#define TYPE_QCRYPTO_TLS_CREDS_X509 "tls-creds-x509" +#define QCRYPTO_TLS_CREDS_X509(obj) \ + OBJECT_CHECK(QCryptoTLSCredsX509, (obj), TYPE_QCRYPTO_TLS_CREDS_X509) + +typedef struct QCryptoTLSCredsX509 QCryptoTLSCredsX509; +typedef struct QCryptoTLSCredsX509Class QCryptoTLSCredsX509Class; + +#define QCRYPTO_TLS_CREDS_X509_CA_CERT "ca-cert.pem" +#define QCRYPTO_TLS_CREDS_X509_CA_CRL "ca-crl.pem" +#define QCRYPTO_TLS_CREDS_X509_SERVER_KEY "server-key.pem" +#define QCRYPTO_TLS_CREDS_X509_SERVER_CERT "server-cert.pem" +#define QCRYPTO_TLS_CREDS_X509_CLIENT_KEY "client-key.pem" +#define QCRYPTO_TLS_CREDS_X509_CLIENT_CERT "client-cert.pem" + + +/** + * QCryptoTLSCredsX509: + * + * The QCryptoTLSCredsX509 object provides a representation + * of x509 credentials used to perform a TLS handshake. + * + * This is a user creatable object, which can be instantiated + * via object_new_propv(): + * + * <example> + * <title>Creating x509 TLS credential objects in code</title> + * <programlisting> + * Object *obj; + * Error *err = NULL; + * obj = object_new_propv(TYPE_QCRYPTO_TLS_CREDS_X509, + * "tlscreds0", + * &err, + * "endpoint", "server", + * "dir", "/path/x509/cert/dir", + * "verify-peer", "yes", + * NULL); + * </programlisting> + * </example> + * + * Or via QMP: + * + * <example> + * <title>Creating x509 TLS credential objects via QMP</title> + * <programlisting> + * { + * "execute": "object-add", "arguments": { + * "id": "tlscreds0", + * "qom-type": "tls-creds-x509", + * "props": { + * "endpoint": "server", + * "dir": "/path/to/x509/cert/dir", + * "verify-peer": false + * } + * } + * } + * </programlisting> + * </example> + * + * + * Or via the CLI: + * + * <example> + * <title>Creating x509 TLS credential objects via CLI</title> + * <programlisting> + * qemu-system-x86_64 -object tls-creds-x509,id=tlscreds0,\ + * endpoint=server,verify-peer=off,\ + * dir=/path/to/x509/certdir/ + * </programlisting> + * </example> + * + */ + +struct QCryptoTLSCredsX509 { + QCryptoTLSCreds parent_obj; +#ifdef CONFIG_GNUTLS + gnutls_certificate_credentials_t data; +#endif + bool sanityCheck; +}; + + +struct QCryptoTLSCredsX509Class { + QCryptoTLSCredsClass parent_class; +}; + + +#endif /* QCRYPTO_TLSCRED_X509_H__ */ + diff --git a/include/crypto/tlssession.h b/include/crypto/tlssession.h new file mode 100644 index 0000000000..b38fe6954d --- /dev/null +++ b/include/crypto/tlssession.h @@ -0,0 +1,322 @@ +/* + * QEMU crypto TLS session support + * + * Copyright (c) 2015 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_TLS_SESSION_H__ +#define QCRYPTO_TLS_SESSION_H__ + +#include "crypto/tlscreds.h" + +/** + * QCryptoTLSSession: + * + * The QCryptoTLSSession object encapsulates the + * logic to integrate with a TLS providing library such + * as GNUTLS, to setup and run TLS sessions. + * + * The API is designed such that it has no assumption about + * the type of transport it is running over. It may be a + * traditional TCP socket, or something else entirely. The + * only requirement is a full-duplex stream of some kind. + * + * <example> + * <title>Using TLS session objects</title> + * <programlisting> + * static ssize_t mysock_send(const char *buf, size_t len, + * void *opaque) + * { + * int fd = GPOINTER_TO_INT(opaque); + * + * return write(*fd, buf, len); + * } + * + * static ssize_t mysock_recv(const char *buf, size_t len, + * void *opaque) + * { + * int fd = GPOINTER_TO_INT(opaque); + * + * return read(*fd, buf, len); + * } + * + * static int mysock_run_tls(int sockfd, + * QCryptoTLSCreds *creds, + * Error *erp) + * { + * QCryptoTLSSession *sess; + * + * sess = qcrypto_tls_session_new(creds, + * "vnc.example.com", + * NULL, + * QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, + * errp); + * if (sess == NULL) { + * return -1; + * } + * + * qcrypto_tls_session_set_callbacks(sess, + * mysock_send, + * mysock_recv + * GINT_TO_POINTER(fd)); + * + * while (1) { + * if (qcrypto_tls_session_handshake(sess, errp) < 0) { + * qcrypto_tls_session_free(sess); + * return -1; + * } + * + * switch(qcrypto_tls_session_get_handshake_status(sess)) { + * case QCRYPTO_TLS_HANDSHAKE_COMPLETE: + * if (qcrypto_tls_session_check_credentials(sess, errp) < )) { + * qcrypto_tls_session_free(sess); + * return -1; + * } + * goto done; + * case QCRYPTO_TLS_HANDSHAKE_RECVING: + * ...wait for GIO_IN event on fd... + * break; + * case QCRYPTO_TLS_HANDSHAKE_SENDING: + * ...wait for GIO_OUT event on fd... + * break; + * } + * } + * done: + * + * ....send/recv payload data on sess... + * + * qcrypto_tls_session_free(sess): + * } + * </programlisting> + * </example> + */ + +typedef struct QCryptoTLSSession QCryptoTLSSession; + + +/** + * qcrypto_tls_session_new: + * @creds: pointer to a TLS credentials object + * @hostname: optional hostname to validate + * @aclname: optional ACL to validate peer credentials against + * @endpoint: role of the TLS session, client or server + * @errp: pointer to an uninitialized error object + * + * Create a new TLS session object that will be used to + * negotiate a TLS session over an arbitrary data channel. + * The session object can operate as either the server or + * client, according to the value of the @endpoint argument. + * + * For clients, the @hostname parameter should hold the full + * unmodified hostname as requested by the user. This will + * be used to verify the against the hostname reported in + * the server's credentials (aka x509 certificate). + * + * The @aclname parameter (optionally) specifies the name + * of an access control list that will be used to validate + * the peer's credentials. For x509 credentials, the ACL + * will be matched against the CommonName shown in the peer's + * certificate. If the session is acting as a server, setting + * an ACL will require that the client provide a validate + * x509 client certificate. + * + * After creating the session object, the I/O callbacks + * must be set using the qcrypto_tls_session_set_callbacks() + * method. A TLS handshake sequence must then be completed + * using qcrypto_tls_session_handshake(), before payload + * data is permitted to be sent/received. + * + * The session object must be released by calling + * qcrypto_tls_session_free() when no longer required + * + * Returns: a TLS session object, or NULL on error. + */ +QCryptoTLSSession *qcrypto_tls_session_new(QCryptoTLSCreds *creds, + const char *hostname, + const char *aclname, + QCryptoTLSCredsEndpoint endpoint, + Error **errp); + +/** + * qcrypto_tls_session_free: + * @sess: the TLS session object + * + * Release all memory associated with the TLS session + * object previously allocated by qcrypto_tls_session_new() + */ +void qcrypto_tls_session_free(QCryptoTLSSession *sess); + +/** + * qcrypto_tls_session_check_credentials: + * @sess: the TLS session object + * @errp: pointer to an uninitialized error object + * + * Validate the peer's credentials after a successful + * TLS handshake. It is an error to call this before + * qcrypto_tls_session_get_handshake_status() returns + * QCRYPTO_TLS_HANDSHAKE_COMPLETE + * + * Returns 0 if the credentials validated, -1 on error + */ +int qcrypto_tls_session_check_credentials(QCryptoTLSSession *sess, + Error **errp); + +typedef ssize_t (*QCryptoTLSSessionWriteFunc)(const char *buf, + size_t len, + void *opaque); +typedef ssize_t (*QCryptoTLSSessionReadFunc)(char *buf, + size_t len, + void *opaque); + +/** + * qcrypto_tls_session_set_callbacks: + * @sess: the TLS session object + * @writeFunc: callback for sending data + * @readFunc: callback to receiving data + * @opaque: data to pass to callbacks + * + * Sets the callback functions that are to be used for sending + * and receiving data on the underlying data channel. Typically + * the callbacks to write/read to/from a TCP socket, but there + * is no assumption made about the type of channel used. + * + * The @writeFunc callback will be passed the encrypted + * data to send to the remote peer. + * + * The @readFunc callback will be passed a pointer to fill + * with encrypted data received from the remote peer + */ +void qcrypto_tls_session_set_callbacks(QCryptoTLSSession *sess, + QCryptoTLSSessionWriteFunc writeFunc, + QCryptoTLSSessionReadFunc readFunc, + void *opaque); + +/** + * qcrypto_tls_session_write: + * @sess: the TLS session object + * @buf: the plain text to send + * @len: the length of @buf + * + * Encrypt @len bytes of the data in @buf and send + * it to the remote peer using the callback previously + * registered with qcrypto_tls_session_set_callbacks() + * + * It is an error to call this before + * qcrypto_tls_session_get_handshake_status() returns + * QCRYPTO_TLS_HANDSHAKE_COMPLETE + * + * Returns: the number of bytes sent, or -1 on error + */ +ssize_t qcrypto_tls_session_write(QCryptoTLSSession *sess, + const char *buf, + size_t len); + +/** + * qcrypto_tls_session_read: + * @sess: the TLS session object + * @buf: to fill with plain text received + * @len: the length of @buf + * + * Receive up to @len bytes of data from the remote peer + * using the callback previously registered with + * qcrypto_tls_session_set_callbacks(), decrypt it and + * store it in @buf. + * + * It is an error to call this before + * qcrypto_tls_session_get_handshake_status() returns + * QCRYPTO_TLS_HANDSHAKE_COMPLETE + * + * Returns: the number of bytes received, or -1 on error + */ +ssize_t qcrypto_tls_session_read(QCryptoTLSSession *sess, + char *buf, + size_t len); + +/** + * qcrypto_tls_session_handshake: + * @sess: the TLS session object + * @errp: pointer to an uninitialized error object + * + * Start, or continue, a TLS handshake sequence. If + * the underlying data channel is non-blocking, then + * this method may return control before the handshake + * is complete. On non-blocking channels the + * qcrypto_tls_session_get_handshake_status() method + * should be used to determine whether the handshake + * has completed, or is waiting to send or receive + * data. In the latter cases, the caller should setup + * an event loop watch and call this method again + * once the underlying data channel is ready to read + * or write again + */ +int qcrypto_tls_session_handshake(QCryptoTLSSession *sess, + Error **errp); + +typedef enum { + QCRYPTO_TLS_HANDSHAKE_COMPLETE, + QCRYPTO_TLS_HANDSHAKE_SENDING, + QCRYPTO_TLS_HANDSHAKE_RECVING, +} QCryptoTLSSessionHandshakeStatus; + +/** + * qcrypto_tls_session_get_handshake_status: + * @sess: the TLS session object + * + * Check the status of the TLS handshake. This + * is used with non-blocking data channels to + * determine whether the handshake is waiting + * to send or receive further data to/from the + * remote peer. + * + * Once this returns QCRYPTO_TLS_HANDSHAKE_COMPLETE + * it is permitted to send/receive payload data on + * the channel + */ +QCryptoTLSSessionHandshakeStatus +qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *sess); + +/** + * qcrypto_tls_session_get_key_size: + * @sess: the TLS session object + * @errp: pointer to an uninitialized error object + * + * Check the size of the data channel encryption key + * + * Returns: the length in bytes of the encryption key + * or -1 on error + */ +int qcrypto_tls_session_get_key_size(QCryptoTLSSession *sess, + Error **errp); + +/** + * qcrypto_tls_session_get_peer_name: + * @sess: the TLS session object + * + * Get the identified name of the remote peer. If the + * TLS session was negotiated using x509 certificate + * credentials, this will return the CommonName from + * the peer's certificate. If no identified name is + * available it will return NULL. + * + * The returned data must be released with g_free() + * when no longer required. + * + * Returns: the peer's name or NULL. + */ +char *qcrypto_tls_session_get_peer_name(QCryptoTLSSession *sess); + +#endif /* QCRYPTO_TLS_SESSION_H__ */ diff --git a/include/elf.h b/include/elf.h index 4afd474d5a..79859f0cd8 100644 --- a/include/elf.h +++ b/include/elf.h @@ -133,6 +133,8 @@ typedef int64_t Elf64_Sxword; #define EM_AARCH64 183 +#define EM_TILEGX 191 /* TILE-Gx */ + /* This is the info that is needed to parse the dynamic section of the file */ #define DT_NULL 0 #define DT_NEEDED 1 diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h index 7d36ebfd5b..3ef97d503d 100644 --- a/include/qemu/host-utils.h +++ b/include/qemu/host-utils.h @@ -26,6 +26,7 @@ #define HOST_UTILS_H 1 #include "qemu/compiler.h" /* QEMU_GNUC_PREREQ */ +#include "qemu/bswap.h" #include <limits.h> #include <stdbool.h> @@ -391,6 +392,80 @@ static inline int ctpop64(uint64_t val) #endif } +/** + * revbit8 - reverse the bits in an 8-bit value. + * @x: The value to modify. + */ +static inline uint8_t revbit8(uint8_t x) +{ + /* Assign the correct nibble position. */ + x = ((x & 0xf0) >> 4) + | ((x & 0x0f) << 4); + /* Assign the correct bit position. */ + x = ((x & 0x88) >> 3) + | ((x & 0x44) >> 1) + | ((x & 0x22) << 1) + | ((x & 0x11) << 3); + return x; +} + +/** + * revbit16 - reverse the bits in a 16-bit value. + * @x: The value to modify. + */ +static inline uint16_t revbit16(uint16_t x) +{ + /* Assign the correct byte position. */ + x = bswap16(x); + /* Assign the correct nibble position. */ + x = ((x & 0xf0f0) >> 4) + | ((x & 0x0f0f) << 4); + /* Assign the correct bit position. */ + x = ((x & 0x8888) >> 3) + | ((x & 0x4444) >> 1) + | ((x & 0x2222) << 1) + | ((x & 0x1111) << 3); + return x; +} + +/** + * revbit32 - reverse the bits in a 32-bit value. + * @x: The value to modify. + */ +static inline uint32_t revbit32(uint32_t x) +{ + /* Assign the correct byte position. */ + x = bswap32(x); + /* Assign the correct nibble position. */ + x = ((x & 0xf0f0f0f0u) >> 4) + | ((x & 0x0f0f0f0fu) << 4); + /* Assign the correct bit position. */ + x = ((x & 0x88888888u) >> 3) + | ((x & 0x44444444u) >> 1) + | ((x & 0x22222222u) << 1) + | ((x & 0x11111111u) << 3); + return x; +} + +/** + * revbit64 - reverse the bits in a 64-bit value. + * @x: The value to modify. + */ +static inline uint64_t revbit64(uint64_t x) +{ + /* Assign the correct byte position. */ + x = bswap64(x); + /* Assign the correct nibble position. */ + x = ((x & 0xf0f0f0f0f0f0f0f0ull) >> 4) + | ((x & 0x0f0f0f0f0f0f0f0full) << 4); + /* Assign the correct bit position. */ + x = ((x & 0x8888888888888888ull) >> 3) + | ((x & 0x4444444444444444ull) >> 1) + | ((x & 0x2222222222222222ull) << 1) + | ((x & 0x1111111111111111ull) << 3); + return x; +} + /* Host type specific sizes of these routines. */ #if ULONG_MAX == UINT32_MAX @@ -399,12 +474,14 @@ static inline int ctpop64(uint64_t val) # define clol clo32 # define ctol cto32 # define ctpopl ctpop32 +# define revbitl revbit32 #elif ULONG_MAX == UINT64_MAX # define clzl clz64 # define ctzl ctz64 # define clol clo64 # define ctol cto64 # define ctpopl ctpop64 +# define revbitl revbit64 #else # error Unknown sizeof long #endif diff --git a/linux-user/elfload.c b/linux-user/elfload.c index eca0c7f0af..39f32826ba 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1218,6 +1218,29 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i #endif /* TARGET_S390X */ +#ifdef TARGET_TILEGX + +/* 42 bits real used address, a half for user mode */ +#define ELF_START_MMAP (0x00000020000000000ULL) + +#define elf_check_arch(x) ((x) == EM_TILEGX) + +#define ELF_CLASS ELFCLASS64 +#define ELF_DATA ELFDATA2LSB +#define ELF_ARCH EM_TILEGX + +static inline void init_thread(struct target_pt_regs *regs, + struct image_info *infop) +{ + regs->pc = infop->entry; + regs->sp = infop->start_stack; + +} + +#define ELF_EXEC_PAGESIZE 65536 /* TILE-Gx page size is 64KB */ + +#endif /* TARGET_TILEGX */ + #ifndef ELF_PLATFORM #define ELF_PLATFORM (NULL) #endif diff --git a/linux-user/main.c b/linux-user/main.c index 4ba7228e7f..782037da84 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -3412,6 +3412,233 @@ void cpu_loop(CPUS390XState *env) #endif /* TARGET_S390X */ +#ifdef TARGET_TILEGX + +static void gen_sigsegv_maperr(CPUTLGState *env, target_ulong addr) +{ + target_siginfo_t info; + + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = addr; + queue_signal(env, info.si_signo, &info); +} + +static void gen_sigill_reg(CPUTLGState *env) +{ + target_siginfo_t info; + + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_PRVREG; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, &info); +} + +static void set_regval(CPUTLGState *env, uint8_t reg, uint64_t val) +{ + if (unlikely(reg >= TILEGX_R_COUNT)) { + switch (reg) { + case TILEGX_R_SN: + case TILEGX_R_ZERO: + return; + case TILEGX_R_IDN0: + case TILEGX_R_IDN1: + case TILEGX_R_UDN0: + case TILEGX_R_UDN1: + case TILEGX_R_UDN2: + case TILEGX_R_UDN3: + gen_sigill_reg(env); + return; + default: + g_assert_not_reached(); + } + } + env->regs[reg] = val; +} + +/* + * Compare the 8-byte contents of the CmpValue SPR with the 8-byte value in + * memory at the address held in the first source register. If the values are + * not equal, then no memory operation is performed. If the values are equal, + * the 8-byte quantity from the second source register is written into memory + * at the address held in the first source register. In either case, the result + * of the instruction is the value read from memory. The compare and write to + * memory are atomic and thus can be used for synchronization purposes. This + * instruction only operates for addresses aligned to a 8-byte boundary. + * Unaligned memory access causes an Unaligned Data Reference interrupt. + * + * Functional Description (64-bit) + * uint64_t memVal = memoryReadDoubleWord (rf[SrcA]); + * rf[Dest] = memVal; + * if (memVal == SPR[CmpValueSPR]) + * memoryWriteDoubleWord (rf[SrcA], rf[SrcB]); + * + * Functional Description (32-bit) + * uint64_t memVal = signExtend32 (memoryReadWord (rf[SrcA])); + * rf[Dest] = memVal; + * if (memVal == signExtend32 (SPR[CmpValueSPR])) + * memoryWriteWord (rf[SrcA], rf[SrcB]); + * + * + * This function also processes exch and exch4 which need not process SPR. + */ +static void do_exch(CPUTLGState *env, bool quad, bool cmp) +{ + target_ulong addr; + target_long val, sprval; + + start_exclusive(); + + addr = env->atomic_srca; + if (quad ? get_user_s64(val, addr) : get_user_s32(val, addr)) { + goto sigsegv_maperr; + } + + if (cmp) { + if (quad) { + sprval = env->spregs[TILEGX_SPR_CMPEXCH]; + } else { + sprval = sextract64(env->spregs[TILEGX_SPR_CMPEXCH], 0, 32); + } + } + + if (!cmp || val == sprval) { + target_long valb = env->atomic_srcb; + if (quad ? put_user_u64(valb, addr) : put_user_u32(valb, addr)) { + goto sigsegv_maperr; + } + } + + set_regval(env, env->atomic_dstr, val); + end_exclusive(); + return; + + sigsegv_maperr: + end_exclusive(); + gen_sigsegv_maperr(env, addr); +} + +static void do_fetch(CPUTLGState *env, int trapnr, bool quad) +{ + int8_t write = 1; + target_ulong addr; + target_long val, valb; + + start_exclusive(); + + addr = env->atomic_srca; + valb = env->atomic_srcb; + if (quad ? get_user_s64(val, addr) : get_user_s32(val, addr)) { + goto sigsegv_maperr; + } + + switch (trapnr) { + case TILEGX_EXCP_OPCODE_FETCHADD: + case TILEGX_EXCP_OPCODE_FETCHADD4: + valb += val; + break; + case TILEGX_EXCP_OPCODE_FETCHADDGEZ: + valb += val; + if (valb < 0) { + write = 0; + } + break; + case TILEGX_EXCP_OPCODE_FETCHADDGEZ4: + valb += val; + if ((int32_t)valb < 0) { + write = 0; + } + break; + case TILEGX_EXCP_OPCODE_FETCHAND: + case TILEGX_EXCP_OPCODE_FETCHAND4: + valb &= val; + break; + case TILEGX_EXCP_OPCODE_FETCHOR: + case TILEGX_EXCP_OPCODE_FETCHOR4: + valb |= val; + break; + default: + g_assert_not_reached(); + } + + if (write) { + if (quad ? put_user_u64(valb, addr) : put_user_u32(valb, addr)) { + goto sigsegv_maperr; + } + } + + set_regval(env, env->atomic_dstr, val); + end_exclusive(); + return; + + sigsegv_maperr: + end_exclusive(); + gen_sigsegv_maperr(env, addr); +} + +void cpu_loop(CPUTLGState *env) +{ + CPUState *cs = CPU(tilegx_env_get_cpu(env)); + int trapnr; + + while (1) { + cpu_exec_start(cs); + trapnr = cpu_tilegx_exec(cs); + cpu_exec_end(cs); + switch (trapnr) { + case TILEGX_EXCP_SYSCALL: + env->regs[TILEGX_R_RE] = do_syscall(env, env->regs[TILEGX_R_NR], + env->regs[0], env->regs[1], + env->regs[2], env->regs[3], + env->regs[4], env->regs[5], + env->regs[6], env->regs[7]); + env->regs[TILEGX_R_ERR] = TILEGX_IS_ERRNO(env->regs[TILEGX_R_RE]) + ? - env->regs[TILEGX_R_RE] + : 0; + break; + case TILEGX_EXCP_OPCODE_EXCH: + do_exch(env, true, false); + break; + case TILEGX_EXCP_OPCODE_EXCH4: + do_exch(env, false, false); + break; + case TILEGX_EXCP_OPCODE_CMPEXCH: + do_exch(env, true, true); + break; + case TILEGX_EXCP_OPCODE_CMPEXCH4: + do_exch(env, false, true); + break; + case TILEGX_EXCP_OPCODE_FETCHADD: + case TILEGX_EXCP_OPCODE_FETCHADDGEZ: + case TILEGX_EXCP_OPCODE_FETCHAND: + case TILEGX_EXCP_OPCODE_FETCHOR: + do_fetch(env, trapnr, true); + break; + case TILEGX_EXCP_OPCODE_FETCHADD4: + case TILEGX_EXCP_OPCODE_FETCHADDGEZ4: + case TILEGX_EXCP_OPCODE_FETCHAND4: + case TILEGX_EXCP_OPCODE_FETCHOR4: + do_fetch(env, trapnr, false); + break; + case TILEGX_EXCP_REG_IDN_ACCESS: + case TILEGX_EXCP_REG_UDN_ACCESS: + gen_sigill_reg(env); + break; + case TILEGX_EXCP_SEGV: + gen_sigsegv_maperr(env, env->excaddr); + break; + default: + fprintf(stderr, "trapnr is %d[0x%x].\n", trapnr, trapnr); + g_assert_not_reached(); + } + process_pending_signals(env); + } +} + +#endif + THREAD CPUState *thread_cpu; void task_settid(TaskState *ts) @@ -4377,6 +4604,17 @@ int main(int argc, char **argv, char **envp) env->psw.mask = regs->psw.mask; env->psw.addr = regs->psw.addr; } +#elif defined(TARGET_TILEGX) + { + int i; + for (i = 0; i < TILEGX_R_COUNT; i++) { + env->regs[i] = regs->regs[i]; + } + for (i = 0; i < TILEGX_SPR_COUNT; i++) { + env->spregs[i] = 0; + } + env->pc = regs->pc; + } #else #error unsupported target CPU #endif diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 973cc2fd49..d1d3eb2d78 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -213,7 +213,7 @@ static int gettid(void) { return -ENOSYS; } #endif -#ifdef __NR_getdents +#if defined(TARGET_NR_getdents) && defined(__NR_getdents) _syscall3(int, sys_getdents, uint, fd, struct linux_dirent *, dirp, uint, count); #endif #if !defined(__NR_getdents) || \ @@ -5640,6 +5640,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(write(arg1, p, arg3)); unlock_user(p, arg2, 0); break; +#ifdef TARGET_NR_open case TARGET_NR_open: if (!(p = lock_user_string(arg1))) goto efault; @@ -5648,6 +5649,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, arg3)); unlock_user(p, arg1, 0); break; +#endif case TARGET_NR_openat: if (!(p = lock_user_string(arg2))) goto efault; @@ -5662,9 +5664,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, case TARGET_NR_brk: ret = do_brk(arg1); break; +#ifdef TARGET_NR_fork case TARGET_NR_fork: ret = get_errno(do_fork(cpu_env, SIGCHLD, 0, 0, 0, 0)); break; +#endif #ifdef TARGET_NR_waitpid case TARGET_NR_waitpid: { @@ -5699,6 +5703,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p, arg1, 0); break; #endif +#ifdef TARGET_NR_link case TARGET_NR_link: { void * p2; @@ -5712,6 +5717,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p, arg1, 0); } break; +#endif #if defined(TARGET_NR_linkat) case TARGET_NR_linkat: { @@ -5729,12 +5735,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } break; #endif +#ifdef TARGET_NR_unlink case TARGET_NR_unlink: if (!(p = lock_user_string(arg1))) goto efault; ret = get_errno(unlink(p)); unlock_user(p, arg1, 0); break; +#endif #if defined(TARGET_NR_unlinkat) case TARGET_NR_unlinkat: if (!(p = lock_user_string(arg2))) @@ -5851,12 +5859,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } break; #endif +#ifdef TARGET_NR_mknod case TARGET_NR_mknod: if (!(p = lock_user_string(arg1))) goto efault; ret = get_errno(mknod(p, arg2, arg3)); unlock_user(p, arg1, 0); break; +#endif #if defined(TARGET_NR_mknodat) case TARGET_NR_mknodat: if (!(p = lock_user_string(arg2))) @@ -5865,12 +5875,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p, arg2, 0); break; #endif +#ifdef TARGET_NR_chmod case TARGET_NR_chmod: if (!(p = lock_user_string(arg1))) goto efault; ret = get_errno(chmod(p, arg2)); unlock_user(p, arg1, 0); break; +#endif #ifdef TARGET_NR_break case TARGET_NR_break: goto unimplemented; @@ -6005,6 +6017,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } break; #endif +#ifdef TARGET_NR_utimes case TARGET_NR_utimes: { struct timeval *tvp, tv[2]; @@ -6023,6 +6036,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p, arg1, 0); } break; +#endif #if defined(TARGET_NR_futimesat) case TARGET_NR_futimesat: { @@ -6051,12 +6065,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, case TARGET_NR_gtty: goto unimplemented; #endif +#ifdef TARGET_NR_access case TARGET_NR_access: if (!(p = lock_user_string(arg1))) goto efault; ret = get_errno(access(path(p), arg2)); unlock_user(p, arg1, 0); break; +#endif #if defined(TARGET_NR_faccessat) && defined(__NR_faccessat) case TARGET_NR_faccessat: if (!(p = lock_user_string(arg2))) @@ -6081,6 +6097,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, case TARGET_NR_kill: ret = get_errno(kill(arg1, target_to_host_signal(arg2))); break; +#ifdef TARGET_NR_rename case TARGET_NR_rename: { void *p2; @@ -6094,6 +6111,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p, arg1, 0); } break; +#endif #if defined(TARGET_NR_renameat) case TARGET_NR_renameat: { @@ -6109,12 +6127,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } break; #endif +#ifdef TARGET_NR_mkdir case TARGET_NR_mkdir: if (!(p = lock_user_string(arg1))) goto efault; ret = get_errno(mkdir(p, arg2)); unlock_user(p, arg1, 0); break; +#endif #if defined(TARGET_NR_mkdirat) case TARGET_NR_mkdirat: if (!(p = lock_user_string(arg2))) @@ -6123,18 +6143,22 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p, arg2, 0); break; #endif +#ifdef TARGET_NR_rmdir case TARGET_NR_rmdir: if (!(p = lock_user_string(arg1))) goto efault; ret = get_errno(rmdir(p)); unlock_user(p, arg1, 0); break; +#endif case TARGET_NR_dup: ret = get_errno(dup(arg1)); break; +#ifdef TARGET_NR_pipe case TARGET_NR_pipe: ret = do_pipe(cpu_env, arg1, 0, 0); break; +#endif #ifdef TARGET_NR_pipe2 case TARGET_NR_pipe2: ret = do_pipe(cpu_env, arg1, @@ -6219,11 +6243,15 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(chroot(p)); unlock_user(p, arg1, 0); break; +#ifdef TARGET_NR_ustat case TARGET_NR_ustat: goto unimplemented; +#endif +#ifdef TARGET_NR_dup2 case TARGET_NR_dup2: ret = get_errno(dup2(arg1, arg2)); break; +#endif #if defined(CONFIG_DUP3) && defined(TARGET_NR_dup3) case TARGET_NR_dup3: ret = get_errno(dup3(arg1, arg2, arg3)); @@ -6234,9 +6262,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(getppid()); break; #endif +#ifdef TARGET_NR_getpgrp case TARGET_NR_getpgrp: ret = get_errno(getpgrp()); break; +#endif case TARGET_NR_setsid: ret = get_errno(setsid()); break; @@ -6812,6 +6842,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } break; #endif +#ifdef TARGET_NR_symlink case TARGET_NR_symlink: { void *p2; @@ -6825,6 +6856,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p, arg1, 0); } break; +#endif #if defined(TARGET_NR_symlinkat) case TARGET_NR_symlinkat: { @@ -6844,6 +6876,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, case TARGET_NR_oldlstat: goto unimplemented; #endif +#ifdef TARGET_NR_readlink case TARGET_NR_readlink: { void *p2; @@ -6874,6 +6907,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p, arg1, 0); } break; +#endif #if defined(TARGET_NR_readlinkat) case TARGET_NR_readlinkat: { @@ -7273,22 +7307,28 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } } break; +#ifdef TARGET_NR_stat case TARGET_NR_stat: if (!(p = lock_user_string(arg1))) goto efault; ret = get_errno(stat(path(p), &st)); unlock_user(p, arg1, 0); goto do_stat; +#endif +#ifdef TARGET_NR_lstat case TARGET_NR_lstat: if (!(p = lock_user_string(arg1))) goto efault; ret = get_errno(lstat(path(p), &st)); unlock_user(p, arg1, 0); goto do_stat; +#endif case TARGET_NR_fstat: { ret = get_errno(fstat(arg1, &st)); +#if defined(TARGET_NR_stat) || defined(TARGET_NR_lstat) do_stat: +#endif if (!is_error(ret)) { struct target_stat *target_st; @@ -7576,6 +7616,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } break; #endif +#ifdef TARGET_NR_getdents case TARGET_NR_getdents: #ifdef __NR_getdents #if TARGET_ABI_BITS == 32 && HOST_LONG_BITS == 64 @@ -7706,6 +7747,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } #endif break; +#endif /* TARGET_NR_getdents */ #if defined(TARGET_NR_getdents64) && defined(__NR_getdents64) case TARGET_NR_getdents64: { @@ -7845,11 +7887,13 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(fdatasync(arg1)); break; #endif +#ifdef TARGET_NR__sysctl case TARGET_NR__sysctl: /* We don't implement this, but ENOTDIR is always a safe return value. */ ret = -TARGET_ENOTDIR; break; +#endif case TARGET_NR_sched_getaffinity: { unsigned int mask_size; @@ -8296,12 +8340,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = host_to_target_stat64(cpu_env, arg3, &st); break; #endif +#ifdef TARGET_NR_lchown case TARGET_NR_lchown: if (!(p = lock_user_string(arg1))) goto efault; ret = get_errno(lchown(p, low2highuid(arg2), low2highgid(arg3))); unlock_user(p, arg1, 0); break; +#endif #ifdef TARGET_NR_getuid case TARGET_NR_getuid: ret = get_errno(high2lowuid(getuid())); @@ -8424,12 +8470,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } break; #endif +#ifdef TARGET_NR_chown case TARGET_NR_chown: if (!(p = lock_user_string(arg1))) goto efault; ret = get_errno(chown(p, low2highuid(arg2), low2highgid(arg3))); unlock_user(p, arg1, 0); break; +#endif case TARGET_NR_setuid: ret = get_errno(setuid(low2highuid(arg1))); break; diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 5256fe5b59..cdc8db421c 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -64,8 +64,9 @@ #endif #if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_SH4) \ - || defined(TARGET_M68K) || defined(TARGET_CRIS) || defined(TARGET_UNICORE32) \ - || defined(TARGET_S390X) || defined(TARGET_OPENRISC) + || defined(TARGET_M68K) || defined(TARGET_CRIS) \ + || defined(TARGET_UNICORE32) || defined(TARGET_S390X) \ + || defined(TARGET_OPENRISC) || defined(TARGET_TILEGX) #define TARGET_IOC_SIZEBITS 14 #define TARGET_IOC_DIRBITS 2 @@ -365,7 +366,8 @@ int do_sigaction(int sig, const struct target_sigaction *act, || defined(TARGET_PPC) || defined(TARGET_MIPS) || defined(TARGET_SH4) \ || defined(TARGET_M68K) || defined(TARGET_ALPHA) || defined(TARGET_CRIS) \ || defined(TARGET_MICROBLAZE) || defined(TARGET_UNICORE32) \ - || defined(TARGET_S390X) || defined(TARGET_OPENRISC) + || defined(TARGET_S390X) || defined(TARGET_OPENRISC) \ + || defined(TARGET_TILEGX) #if defined(TARGET_SPARC) #define TARGET_SA_NOCLDSTOP 8u @@ -1871,7 +1873,7 @@ struct target_stat { abi_ulong target_st_ctime_nsec; unsigned int __unused[2]; }; -#elif defined(TARGET_OPENRISC) +#elif defined(TARGET_OPENRISC) || defined(TARGET_TILEGX) /* These are the asm-generic versions of the stat and stat64 structures */ @@ -2264,7 +2266,9 @@ struct target_flock { struct target_flock64 { short l_type; short l_whence; -#if defined(TARGET_PPC) || defined(TARGET_X86_64) || defined(TARGET_MIPS) || defined(TARGET_SPARC) || defined(TARGET_HPPA) || defined (TARGET_MICROBLAZE) +#if defined(TARGET_PPC) || defined(TARGET_X86_64) || defined(TARGET_MIPS) \ + || defined(TARGET_SPARC) || defined(TARGET_HPPA) \ + || defined(TARGET_MICROBLAZE) || defined(TARGET_TILEGX) int __pad; #endif unsigned long long l_start; diff --git a/linux-user/tilegx/syscall.h b/linux-user/tilegx/syscall.h new file mode 100644 index 0000000000..653ece13d8 --- /dev/null +++ b/linux-user/tilegx/syscall.h @@ -0,0 +1,40 @@ +#ifndef TILEGX_SYSCALLS_H +#define TILEGX_SYSCALLS_H + +#define UNAME_MACHINE "tilegx" +#define UNAME_MINIMUM_RELEASE "3.19" + +#define MMAP_SHIFT TARGET_PAGE_BITS + +#define TILEGX_IS_ERRNO(ret) \ + ((ret) > 0xfffffffffffff000ULL) /* errno is 0 -- 4096 */ + +typedef uint64_t tilegx_reg_t; + +struct target_pt_regs { + + union { + /* Saved main processor registers; 56..63 are special. */ + tilegx_reg_t regs[56]; + struct { + tilegx_reg_t __regs[53]; + tilegx_reg_t tp; /* aliases regs[TREG_TP] */ + tilegx_reg_t sp; /* aliases regs[TREG_SP] */ + tilegx_reg_t lr; /* aliases regs[TREG_LR] */ + }; + }; + + /* Saved special registers. */ + tilegx_reg_t pc; /* stored in EX_CONTEXT_K_0 */ + tilegx_reg_t ex1; /* stored in EX_CONTEXT_K_1 (PL and ICS bit) */ + tilegx_reg_t faultnum; /* fault number (INT_SWINT_1 for syscall) */ + tilegx_reg_t orig_r0; /* r0 at syscall entry, else zero */ + tilegx_reg_t flags; /* flags (see below) */ + tilegx_reg_t cmpexch; /* value of CMPEXCH_VALUE SPR at interrupt */ + tilegx_reg_t pad[2]; +}; + +#define TARGET_MLOCKALL_MCL_CURRENT 1 +#define TARGET_MLOCKALL_MCL_FUTURE 2 + +#endif diff --git a/linux-user/tilegx/syscall_nr.h b/linux-user/tilegx/syscall_nr.h new file mode 100644 index 0000000000..1dca348378 --- /dev/null +++ b/linux-user/tilegx/syscall_nr.h @@ -0,0 +1,324 @@ +#ifndef TILEGX_SYSCALL_NR +#define TILEGX_SYSCALL_NR + +/* + * Copy from linux kernel asm-generic/unistd.h, which tilegx uses. + */ +#define TARGET_NR_io_setup 0 +#define TARGET_NR_io_destroy 1 +#define TARGET_NR_io_submit 2 +#define TARGET_NR_io_cancel 3 +#define TARGET_NR_io_getevents 4 +#define TARGET_NR_setxattr 5 +#define TARGET_NR_lsetxattr 6 +#define TARGET_NR_fsetxattr 7 +#define TARGET_NR_getxattr 8 +#define TARGET_NR_lgetxattr 9 +#define TARGET_NR_fgetxattr 10 +#define TARGET_NR_listxattr 11 +#define TARGET_NR_llistxattr 12 +#define TARGET_NR_flistxattr 13 +#define TARGET_NR_removexattr 14 +#define TARGET_NR_lremovexattr 15 +#define TARGET_NR_fremovexattr 16 +#define TARGET_NR_getcwd 17 +#define TARGET_NR_lookup_dcookie 18 +#define TARGET_NR_eventfd2 19 +#define TARGET_NR_epoll_create1 20 +#define TARGET_NR_epoll_ctl 21 +#define TARGET_NR_epoll_pwait 22 +#define TARGET_NR_dup 23 +#define TARGET_NR_dup3 24 +#define TARGET_NR_fcntl 25 +#define TARGET_NR_inotify_init1 26 +#define TARGET_NR_inotify_add_watch 27 +#define TARGET_NR_inotify_rm_watch 28 +#define TARGET_NR_ioctl 29 +#define TARGET_NR_ioprio_set 30 +#define TARGET_NR_ioprio_get 31 +#define TARGET_NR_flock 32 +#define TARGET_NR_mknodat 33 +#define TARGET_NR_mkdirat 34 +#define TARGET_NR_unlinkat 35 +#define TARGET_NR_symlinkat 36 +#define TARGET_NR_linkat 37 +#define TARGET_NR_renameat 38 +#define TARGET_NR_umount2 39 +#define TARGET_NR_mount 40 +#define TARGET_NR_pivot_root 41 +#define TARGET_NR_nfsservctl 42 +#define TARGET_NR_statfs 43 +#define TARGET_NR_fstatfs 44 +#define TARGET_NR_truncate 45 +#define TARGET_NR_ftruncate 46 +#define TARGET_NR_fallocate 47 +#define TARGET_NR_faccessat 48 +#define TARGET_NR_chdir 49 +#define TARGET_NR_fchdir 50 +#define TARGET_NR_chroot 51 +#define TARGET_NR_fchmod 52 +#define TARGET_NR_fchmodat 53 +#define TARGET_NR_fchownat 54 +#define TARGET_NR_fchown 55 +#define TARGET_NR_openat 56 +#define TARGET_NR_close 57 +#define TARGET_NR_vhangup 58 +#define TARGET_NR_pipe2 59 +#define TARGET_NR_quotactl 60 +#define TARGET_NR_getdents64 61 +#define TARGET_NR_lseek 62 +#define TARGET_NR_read 63 +#define TARGET_NR_write 64 +#define TARGET_NR_readv 65 +#define TARGET_NR_writev 66 +#define TARGET_NR_pread64 67 +#define TARGET_NR_pwrite64 68 +#define TARGET_NR_preadv 69 +#define TARGET_NR_pwritev 70 +#define TARGET_NR_sendfile 71 +#define TARGET_NR_pselect6 72 +#define TARGET_NR_ppoll 73 +#define TARGET_NR_signalfd4 74 +#define TARGET_NR_vmsplice 75 +#define TARGET_NR_splice 76 +#define TARGET_NR_tee 77 +#define TARGET_NR_readlinkat 78 +#define TARGET_NR_fstatat64 79 /* let syscall.c known */ +#define TARGET_NR_fstat 80 +#define TARGET_NR_sync 81 +#define TARGET_NR_fsync 82 +#define TARGET_NR_fdatasync 83 +#define TARGET_NR_sync_file_range 84 /* For tilegx, no range2 */ +#define TARGET_NR_timerfd_create 85 +#define TARGET_NR_timerfd_settime 86 +#define TARGET_NR_timerfd_gettime 87 +#define TARGET_NR_utimensat 88 +#define TARGET_NR_acct 89 +#define TARGET_NR_capget 90 +#define TARGET_NR_capset 91 +#define TARGET_NR_personality 92 +#define TARGET_NR_exit 93 +#define TARGET_NR_exit_group 94 +#define TARGET_NR_waitid 95 +#define TARGET_NR_set_tid_address 96 +#define TARGET_NR_unshare 97 +#define TARGET_NR_futex 98 +#define TARGET_NR_set_robust_list 99 +#define TARGET_NR_get_robust_list 100 +#define TARGET_NR_nanosleep 101 +#define TARGET_NR_getitimer 102 +#define TARGET_NR_setitimer 103 +#define TARGET_NR_kexec_load 104 +#define TARGET_NR_init_module 105 +#define TARGET_NR_delete_module 106 +#define TARGET_NR_timer_create 107 +#define TARGET_NR_timer_gettime 108 +#define TARGET_NR_timer_getoverrun 109 +#define TARGET_NR_timer_settime 110 +#define TARGET_NR_timer_delete 111 +#define TARGET_NR_clock_settime 112 +#define TARGET_NR_clock_gettime 113 +#define TARGET_NR_clock_getres 114 +#define TARGET_NR_clock_nanosleep 115 +#define TARGET_NR_syslog 116 +#define TARGET_NR_ptrace 117 +#define TARGET_NR_sched_setparam 118 +#define TARGET_NR_sched_setscheduler 119 +#define TARGET_NR_sched_getscheduler 120 +#define TARGET_NR_sched_getparam 121 +#define TARGET_NR_sched_setaffinity 122 +#define TARGET_NR_sched_getaffinity 123 +#define TARGET_NR_sched_yield 124 +#define TARGET_NR_sched_get_priority_max 125 +#define TARGET_NR_sched_get_priority_min 126 +#define TARGET_NR_sched_rr_get_interval 127 +#define TARGET_NR_restart_syscall 128 +#define TARGET_NR_kill 129 +#define TARGET_NR_tkill 130 +#define TARGET_NR_tgkill 131 +#define TARGET_NR_sigaltstack 132 +#define TARGET_NR_rt_sigsuspend 133 +#define TARGET_NR_rt_sigaction 134 +#define TARGET_NR_rt_sigprocmask 135 +#define TARGET_NR_rt_sigpending 136 +#define TARGET_NR_rt_sigtimedwait 137 +#define TARGET_NR_rt_sigqueueinfo 138 +#define TARGET_NR_rt_sigreturn 139 +#define TARGET_NR_setpriority 140 +#define TARGET_NR_getpriority 141 +#define TARGET_NR_reboot 142 +#define TARGET_NR_setregid 143 +#define TARGET_NR_setgid 144 +#define TARGET_NR_setreuid 145 +#define TARGET_NR_setuid 146 +#define TARGET_NR_setresuid 147 +#define TARGET_NR_getresuid 148 +#define TARGET_NR_setresgid 149 +#define TARGET_NR_getresgid 150 +#define TARGET_NR_setfsuid 151 +#define TARGET_NR_setfsgid 152 +#define TARGET_NR_times 153 +#define TARGET_NR_setpgid 154 +#define TARGET_NR_getpgid 155 +#define TARGET_NR_getsid 156 +#define TARGET_NR_setsid 157 +#define TARGET_NR_getgroups 158 +#define TARGET_NR_setgroups 159 +#define TARGET_NR_uname 160 +#define TARGET_NR_sethostname 161 +#define TARGET_NR_setdomainname 162 +#define TARGET_NR_getrlimit 163 +#define TARGET_NR_setrlimit 164 +#define TARGET_NR_getrusage 165 +#define TARGET_NR_umask 166 +#define TARGET_NR_prctl 167 +#define TARGET_NR_getcpu 168 +#define TARGET_NR_gettimeofday 169 +#define TARGET_NR_settimeofday 170 +#define TARGET_NR_adjtimex 171 +#define TARGET_NR_getpid 172 +#define TARGET_NR_getppid 173 +#define TARGET_NR_getuid 174 +#define TARGET_NR_geteuid 175 +#define TARGET_NR_getgid 176 +#define TARGET_NR_getegid 177 +#define TARGET_NR_gettid 178 +#define TARGET_NR_sysinfo 179 +#define TARGET_NR_mq_open 180 +#define TARGET_NR_mq_unlink 181 +#define TARGET_NR_mq_timedsend 182 +#define TARGET_NR_mq_timedreceive 183 +#define TARGET_NR_mq_notify 184 +#define TARGET_NR_mq_getsetattr 185 +#define TARGET_NR_msgget 186 +#define TARGET_NR_msgctl 187 +#define TARGET_NR_msgrcv 188 +#define TARGET_NR_msgsnd 189 +#define TARGET_NR_semget 190 +#define TARGET_NR_semctl 191 +#define TARGET_NR_semtimedop 192 +#define TARGET_NR_semop 193 +#define TARGET_NR_shmget 194 +#define TARGET_NR_shmctl 195 +#define TARGET_NR_shmat 196 +#define TARGET_NR_shmdt 197 +#define TARGET_NR_socket 198 +#define TARGET_NR_socketpair 199 +#define TARGET_NR_bind 200 +#define TARGET_NR_listen 201 +#define TARGET_NR_accept 202 +#define TARGET_NR_connect 203 +#define TARGET_NR_getsockname 204 +#define TARGET_NR_getpeername 205 +#define TARGET_NR_sendto 206 +#define TARGET_NR_recvfrom 207 +#define TARGET_NR_setsockopt 208 +#define TARGET_NR_getsockopt 209 +#define TARGET_NR_shutdown 210 +#define TARGET_NR_sendmsg 211 +#define TARGET_NR_recvmsg 212 +#define TARGET_NR_readahead 213 +#define TARGET_NR_brk 214 +#define TARGET_NR_munmap 215 +#define TARGET_NR_mremap 216 +#define TARGET_NR_add_key 217 +#define TARGET_NR_request_key 218 +#define TARGET_NR_keyctl 219 +#define TARGET_NR_clone 220 +#define TARGET_NR_execve 221 +#define TARGET_NR_mmap 222 +#define TARGET_NR_fadvise64 223 +#define TARGET_NR_swapon 224 +#define TARGET_NR_swapoff 225 +#define TARGET_NR_mprotect 226 +#define TARGET_NR_msync 227 +#define TARGET_NR_mlock 228 +#define TARGET_NR_munlock 229 +#define TARGET_NR_mlockall 230 +#define TARGET_NR_munlockall 231 +#define TARGET_NR_mincore 232 +#define TARGET_NR_madvise 233 +#define TARGET_NR_remap_file_pages 234 +#define TARGET_NR_mbind 235 +#define TARGET_NR_get_mempolicy 236 +#define TARGET_NR_set_mempolicy 237 +#define TARGET_NR_migrate_pages 238 +#define TARGET_NR_move_pages 239 +#define TARGET_NR_rt_tgsigqueueinfo 240 +#define TARGET_NR_perf_event_open 241 +#define TARGET_NR_accept4 242 +#define TARGET_NR_recvmmsg 243 + +#define TARGET_NR_arch_specific_syscall 244 +#define TARGET_NR_cacheflush 245 /* tilegx own syscall */ + +#define TARGET_NR_wait4 260 +#define TARGET_NR_prlimit64 261 +#define TARGET_NR_fanotify_init 262 +#define TARGET_NR_fanotify_mark 263 +#define TARGET_NR_name_to_handle_at 264 +#define TARGET_NR_open_by_handle_at 265 +#define TARGET_NR_clock_adjtime 266 +#define TARGET_NR_syncfs 267 +#define TARGET_NR_setns 268 +#define TARGET_NR_sendmmsg 269 +#define TARGET_NR_process_vm_readv 270 +#define TARGET_NR_process_vm_writev 271 +#define TARGET_NR_kcmp 272 +#define TARGET_NR_finit_module 273 +#define TARGET_NR_sched_setattr 274 +#define TARGET_NR_sched_getattr 275 +#define TARGET_NR_renameat2 276 +#define TARGET_NR_seccomp 277 +#define TARGET_NR_getrandom 278 +#define TARGET_NR_memfd_create 279 +#define TARGET_NR_bpf 280 +#define TARGET_NR_execveat 281 + +#define TARGET_NR_open 1024 +#define TARGET_NR_link 1025 +#define TARGET_NR_unlink 1026 +#define TARGET_NR_mknod 1027 +#define TARGET_NR_chmod 1028 +#define TARGET_NR_chown 1029 +#define TARGET_NR_mkdir 1030 +#define TARGET_NR_rmdir 1031 +#define TARGET_NR_lchown 1032 +#define TARGET_NR_access 1033 +#define TARGET_NR_rename 1034 +#define TARGET_NR_readlink 1035 +#define TARGET_NR_symlink 1036 +#define TARGET_NR_utimes 1037 +#define TARGET_NR_stat64 1038 /* let syscall.c known */ +#define TARGET_NR_lstat 1039 + +#define TARGET_NR_pipe 1040 +#define TARGET_NR_dup2 1041 +#define TARGET_NR_epoll_create 1042 +#define TARGET_NR_inotify_init 1043 +#define TARGET_NR_eventfd 1044 +#define TARGET_NR_signalfd 1045 + +#define TARGET_NR_alarm 1059 +#define TARGET_NR_getpgrp 1060 +#define TARGET_NR_pause 1061 +#define TARGET_NR_time 1062 +#define TARGET_NR_utime 1063 +#define TARGET_NR_creat 1064 +#define TARGET_NR_getdents 1065 +#define TARGET_NR_futimesat 1066 +#define TARGET_NR_select 1067 +#define TARGET_NR_poll 1068 +#define TARGET_NR_epoll_wait 1069 +#define TARGET_NR_ustat 1070 +#define TARGET_NR_vfork 1071 +#define TARGET_NR_oldwait4 1072 +#define TARGET_NR_recv 1073 +#define TARGET_NR_send 1074 +#define TARGET_NR_bdflush 1075 +#define TARGET_NR_umount 1076 +#define TARGET_NR_uselib 1077 +#define TARGET_NR__sysctl 1078 +#define TARGET_NR_fork 1079 + +#endif diff --git a/linux-user/tilegx/target_cpu.h b/linux-user/tilegx/target_cpu.h new file mode 100644 index 0000000000..c96e81d05e --- /dev/null +++ b/linux-user/tilegx/target_cpu.h @@ -0,0 +1,35 @@ +/* + * TILE-Gx specific CPU ABI and functions for linux-user + * + * Copyright (c) 2015 Chen Gang + * + * 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 + * 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 TARGET_CPU_H +#define TARGET_CPU_H + +static inline void cpu_clone_regs(CPUTLGState *env, target_ulong newsp) +{ + if (newsp) { + env->regs[TILEGX_R_SP] = newsp; + } + env->regs[TILEGX_R_RE] = 0; +} + +static inline void cpu_set_tls(CPUTLGState *env, target_ulong newtls) +{ + env->regs[TILEGX_R_TP] = newtls; +} + +#endif diff --git a/linux-user/tilegx/target_signal.h b/linux-user/tilegx/target_signal.h new file mode 100644 index 0000000000..b595f985cf --- /dev/null +++ b/linux-user/tilegx/target_signal.h @@ -0,0 +1,28 @@ +#ifndef TARGET_SIGNAL_H +#define TARGET_SIGNAL_H + +#include "cpu.h" + +/* this struct defines a stack used during syscall handling */ + +typedef struct target_sigaltstack { + abi_ulong ss_sp; + abi_int ss_flags; + abi_ulong ss_size; +} target_stack_t; + +/* + * sigaltstack controls + */ +#define TARGET_SS_ONSTACK 1 +#define TARGET_SS_DISABLE 2 + +#define TARGET_MINSIGSTKSZ 2048 +#define TARGET_SIGSTKSZ 8192 + +static inline abi_ulong get_sp_from_cpustate(CPUTLGState *state) +{ + return state->regs[TILEGX_R_SP]; +} + +#endif /* TARGET_SIGNAL_H */ diff --git a/linux-user/tilegx/target_structs.h b/linux-user/tilegx/target_structs.h new file mode 100644 index 0000000000..7d3ff782fd --- /dev/null +++ b/linux-user/tilegx/target_structs.h @@ -0,0 +1,46 @@ +/* + * TILE-Gx specific structures for linux-user + * + * Copyright (c) 2015 Chen Gang + * + * 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 TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ + abi_uint mode; /* Read/write permission. */ + abi_ushort __seq; /* Sequence number. */ +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_atime; /* time of last shmat() */ + abi_ulong shm_dtime; /* time of last shmdt() */ + abi_ulong shm_ctime; /* time of last change by shmctl() */ + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ushort shm_nattch; /* number of current attaches */ + abi_ushort shm_unused; /* compatibility */ + abi_ulong __unused4; + abi_ulong __unused5; +}; + +#endif diff --git a/linux-user/tilegx/termbits.h b/linux-user/tilegx/termbits.h new file mode 100644 index 0000000000..91ec23654e --- /dev/null +++ b/linux-user/tilegx/termbits.h @@ -0,0 +1,274 @@ +#ifndef TILEGX_TERMBITS_H +#define TILEGX_TERMBITS_H + +/* From asm-generic/termbits.h, which is used by tilegx */ + +#define TARGET_NCCS 19 +struct target_termios { + unsigned int c_iflag; /* input mode flags */ + unsigned int c_oflag; /* output mode flags */ + unsigned int c_cflag; /* control mode flags */ + unsigned int c_lflag; /* local mode flags */ + unsigned char c_line; /* line discipline */ + unsigned char c_cc[TARGET_NCCS]; /* control characters */ +}; + +struct target_termios2 { + unsigned int c_iflag; /* input mode flags */ + unsigned int c_oflag; /* output mode flags */ + unsigned int c_cflag; /* control mode flags */ + unsigned int c_lflag; /* local mode flags */ + unsigned char c_line; /* line discipline */ + unsigned char c_cc[TARGET_NCCS]; /* control characters */ + unsigned int c_ispeed; /* input speed */ + unsigned int c_ospeed; /* output speed */ +}; + +/* c_cc characters */ +#define TARGET_VINTR 0 +#define TARGET_VQUIT 1 +#define TARGET_VERASE 2 +#define TARGET_VKILL 3 +#define TARGET_VEOF 4 +#define TARGET_VTIME 5 +#define TARGET_VMIN 6 +#define TARGET_VSWTC 7 +#define TARGET_VSTART 8 +#define TARGET_VSTOP 9 +#define TARGET_VSUSP 10 +#define TARGET_VEOL 11 +#define TARGET_VREPRINT 12 +#define TARGET_VDISCARD 13 +#define TARGET_VWERASE 14 +#define TARGET_VLNEXT 15 +#define TARGET_VEOL2 16 + +/* c_iflag bits */ +#define TARGET_IGNBRK 0000001 +#define TARGET_BRKINT 0000002 +#define TARGET_IGNPAR 0000004 +#define TARGET_PARMRK 0000010 +#define TARGET_INPCK 0000020 +#define TARGET_ISTRIP 0000040 +#define TARGET_INLCR 0000100 +#define TARGET_IGNCR 0000200 +#define TARGET_ICRNL 0000400 +#define TARGET_IUCLC 0001000 +#define TARGET_IXON 0002000 +#define TARGET_IXANY 0004000 +#define TARGET_IXOFF 0010000 +#define TARGET_IMAXBEL 0020000 +#define TARGET_IUTF8 0040000 + +/* c_oflag bits */ +#define TARGET_OPOST 0000001 +#define TARGET_OLCUC 0000002 +#define TARGET_ONLCR 0000004 +#define TARGET_OCRNL 0000010 +#define TARGET_ONOCR 0000020 +#define TARGET_ONLRET 0000040 +#define TARGET_OFILL 0000100 +#define TARGET_OFDEL 0000200 +#define TARGET_NLDLY 0000400 +#define TARGET_NL0 0000000 +#define TARGET_NL1 0000400 +#define TARGET_CRDLY 0003000 +#define TARGET_CR0 0000000 +#define TARGET_CR1 0001000 +#define TARGET_CR2 0002000 +#define TARGET_CR3 0003000 +#define TARGET_TABDLY 0014000 +#define TARGET_TAB0 0000000 +#define TARGET_TAB1 0004000 +#define TARGET_TAB2 0010000 +#define TARGET_TAB3 0014000 +#define TARGET_XTABS 0014000 +#define TARGET_BSDLY 0020000 +#define TARGET_BS0 0000000 +#define TARGET_BS1 0020000 +#define TARGET_VTDLY 0040000 +#define TARGET_VT0 0000000 +#define TARGET_VT1 0040000 +#define TARGET_FFDLY 0100000 +#define TARGET_FF0 0000000 +#define TARGET_FF1 0100000 + +/* c_cflag bit meaning */ +#define TARGET_CBAUD 0010017 +#define TARGET_B0 0000000 /* hang up */ +#define TARGET_B50 0000001 +#define TARGET_B75 0000002 +#define TARGET_B110 0000003 +#define TARGET_B134 0000004 +#define TARGET_B150 0000005 +#define TARGET_B200 0000006 +#define TARGET_B300 0000007 +#define TARGET_B600 0000010 +#define TARGET_B1200 0000011 +#define TARGET_B1800 0000012 +#define TARGET_B2400 0000013 +#define TARGET_B4800 0000014 +#define TARGET_B9600 0000015 +#define TARGET_B19200 0000016 +#define TARGET_B38400 0000017 +#define TARGET_EXTA TARGET_B19200 +#define TARGET_EXTB TARGET_B38400 +#define TARGET_CSIZE 0000060 +#define TARGET_CS5 0000000 +#define TARGET_CS6 0000020 +#define TARGET_CS7 0000040 +#define TARGET_CS8 0000060 +#define TARGET_CSTOPB 0000100 +#define TARGET_CREAD 0000200 +#define TARGET_PARENB 0000400 +#define TARGET_PARODD 0001000 +#define TARGET_HUPCL 0002000 +#define TARGET_CLOCAL 0004000 +#define TARGET_CBAUDEX 0010000 +#define TARGET_BOTHER 0010000 +#define TARGET_B57600 0010001 +#define TARGET_B115200 0010002 +#define TARGET_B230400 0010003 +#define TARGET_B460800 0010004 +#define TARGET_B500000 0010005 +#define TARGET_B576000 0010006 +#define TARGET_B921600 0010007 +#define TARGET_B1000000 0010010 +#define TARGET_B1152000 0010011 +#define TARGET_B1500000 0010012 +#define TARGET_B2000000 0010013 +#define TARGET_B2500000 0010014 +#define TARGET_B3000000 0010015 +#define TARGET_B3500000 0010016 +#define TARGET_B4000000 0010017 +#define TARGET_CIBAUD 002003600000 /* input baud rate */ +#define TARGET_CMSPAR 010000000000 /* mark or space (stick) parity */ +#define TARGET_CRTSCTS 020000000000 /* flow control */ + +#define TARGET_IBSHIFT 16 /* Shift from CBAUD to CIBAUD */ + +/* c_lflag bits */ +#define TARGET_ISIG 0000001 +#define TARGET_ICANON 0000002 +#define TARGET_XCASE 0000004 +#define TARGET_ECHO 0000010 +#define TARGET_ECHOE 0000020 +#define TARGET_ECHOK 0000040 +#define TARGET_ECHONL 0000100 +#define TARGET_NOFLSH 0000200 +#define TARGET_TOSTOP 0000400 +#define TARGET_ECHOCTL 0001000 +#define TARGET_ECHOPRT 0002000 +#define TARGET_ECHOKE 0004000 +#define TARGET_FLUSHO 0010000 +#define TARGET_PENDIN 0040000 +#define TARGET_IEXTEN 0100000 +#define TARGET_EXTPROC 0200000 + +/* tcflow() and TCXONC use these */ +#define TARGET_TCOOFF 0 +#define TARGET_TCOON 1 +#define TARGET_TCIOFF 2 +#define TARGET_TCION 3 + +/* tcflush() and TCFLSH use these */ +#define TARGET_TCIFLUSH 0 +#define TARGET_TCOFLUSH 1 +#define TARGET_TCIOFLUSH 2 + +/* tcsetattr uses these */ +#define TARGET_TCSANOW 0 +#define TARGET_TCSADRAIN 1 +#define TARGET_TCSAFLUSH 2 + +/* From asm-generic/ioctls.h, which is used by tilegx */ + +#define TARGET_TCGETS 0x5401 +#define TARGET_TCSETS 0x5402 +#define TARGET_TCSETSW 0x5403 +#define TARGET_TCSETSF 0x5404 +#define TARGET_TCGETA 0x5405 +#define TARGET_TCSETA 0x5406 +#define TARGET_TCSETAW 0x5407 +#define TARGET_TCSETAF 0x5408 +#define TARGET_TCSBRK 0x5409 +#define TARGET_TCXONC 0x540A +#define TARGET_TCFLSH 0x540B +#define TARGET_TIOCEXCL 0x540C +#define TARGET_TIOCNXCL 0x540D +#define TARGET_TIOCSCTTY 0x540E +#define TARGET_TIOCGPGRP 0x540F +#define TARGET_TIOCSPGRP 0x5410 +#define TARGET_TIOCOUTQ 0x5411 +#define TARGET_TIOCSTI 0x5412 +#define TARGET_TIOCGWINSZ 0x5413 +#define TARGET_TIOCSWINSZ 0x5414 +#define TARGET_TIOCMGET 0x5415 +#define TARGET_TIOCMBIS 0x5416 +#define TARGET_TIOCMBIC 0x5417 +#define TARGET_TIOCMSET 0x5418 +#define TARGET_TIOCGSOFTCAR 0x5419 +#define TARGET_TIOCSSOFTCAR 0x541A +#define TARGET_FIONREAD 0x541B +#define TARGET_TIOCINQ TARGET_FIONREAD +#define TARGET_TIOCLINUX 0x541C +#define TARGET_TIOCCONS 0x541D +#define TARGET_TIOCGSERIAL 0x541E +#define TARGET_TIOCSSERIAL 0x541F +#define TARGET_TIOCPKT 0x5420 +#define TARGET_FIONBIO 0x5421 +#define TARGET_TIOCNOTTY 0x5422 +#define TARGET_TIOCSETD 0x5423 +#define TARGET_TIOCGETD 0x5424 +#define TARGET_TCSBRKP 0x5425 +#define TARGET_TIOCSBRK 0x5427 +#define TARGET_TIOCCBRK 0x5428 +#define TARGET_TIOCGSID 0x5429 +#define TARGET_TCGETS2 TARGET_IOR('T', 0x2A, struct termios2) +#define TARGET_TCSETS2 TARGET_IOW('T', 0x2B, struct termios2) +#define TARGET_TCSETSW2 TARGET_IOW('T', 0x2C, struct termios2) +#define TARGET_TCSETSF2 TARGET_IOW('T', 0x2D, struct termios2) +#define TARGET_TIOCGRS485 0x542E +#define TARGET_TIOCSRS485 0x542F +#define TARGET_TIOCGPTN TARGET_IOR('T', 0x30, unsigned int) +#define TARGET_TIOCSPTLCK TARGET_IOW('T', 0x31, int) +#define TARGET_TIOCGDEV TARGET_IOR('T', 0x32, unsigned int) +#define TARGET_TCGETX 0x5432 +#define TARGET_TCSETX 0x5433 +#define TARGET_TCSETXF 0x5434 +#define TARGET_TCSETXW 0x5435 +#define TARGET_TIOCSIG TARGET_IOW('T', 0x36, int) +#define TARGET_TIOCVHANGUP 0x5437 +#define TARGET_TIOCGPKT TARGET_IOR('T', 0x38, int) +#define TARGET_TIOCGPTLCK TARGET_IOR('T', 0x39, int) +#define TARGET_TIOCGEXCL TARGET_IOR('T', 0x40, int) + +#define TARGET_FIONCLEX 0x5450 +#define TARGET_FIOCLEX 0x5451 +#define TARGET_FIOASYNC 0x5452 +#define TARGET_TIOCSERCONFIG 0x5453 +#define TARGET_TIOCSERGWILD 0x5454 +#define TARGET_TIOCSERSWILD 0x5455 +#define TARGET_TIOCGLCKTRMIOS 0x5456 +#define TARGET_TIOCSLCKTRMIOS 0x5457 +#define TARGET_TIOCSERGSTRUCT 0x5458 +#define TARGET_TIOCSERGETLSR 0x5459 +#define TARGET_TIOCSERGETMULTI 0x545A +#define TARGET_TIOCSERSETMULTI 0x545B + +#define TARGET_TIOCMIWAIT 0x545C +#define TARGET_TIOCGICOUNT 0x545D +#define TARGET_FIOQSIZE 0x5460 + +#define TARGET_TIOCPKT_DATA 0 +#define TARGET_TIOCPKT_FLUSHREAD 1 +#define TARGET_TIOCPKT_FLUSHWRITE 2 +#define TARGET_TIOCPKT_STOP 4 +#define TARGET_TIOCPKT_START 8 +#define TARGET_TIOCPKT_NOSTOP 16 +#define TARGET_TIOCPKT_DOSTOP 32 +#define TARGET_TIOCPKT_IOCTL 64 + +#define TARGET_TIOCSER_TEMT 0x01 + +#endif diff --git a/qapi-schema.json b/qapi-schema.json index 4060b78386..2bada607f1 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -5,6 +5,9 @@ # QAPI common definitions { 'include': 'qapi/common.json' } +# QAPI crypto definitions +{ 'include': 'qapi/crypto.json' } + # QAPI block definitions { 'include': 'qapi/block.json' } diff --git a/qapi/crypto.json b/qapi/crypto.json new file mode 100644 index 0000000000..b058b14d7b --- /dev/null +++ b/qapi/crypto.json @@ -0,0 +1,21 @@ +# -*- Mode: Python -*- +# +# QAPI crypto definitions + +## +# QCryptoTLSCredsEndpoint: +# +# The type of network endpoint that will be using the credentials. +# Most types of credential require different setup / structures +# depending on whether they will be used in a server versus a +# client. +# +# @client: the network endpoint is acting as the client +# +# @server: the network endpoint is acting as the server +# +# Since: 2.5 +## +{ 'enum': 'QCryptoTLSCredsEndpoint', + 'prefix': 'QCRYPTO_TLS_CREDS_ENDPOINT', + 'data': ['client', 'server']} diff --git a/qemu-options.hx b/qemu-options.hx index 166eae6784..7e147b8aac 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1217,8 +1217,9 @@ By definition the Websocket port is 5700+@var{display}. If @var{host} is specified connections will only be allowed from this host. As an alternative the Websocket port could be specified by using @code{websocket}=@var{port}. -TLS encryption for the Websocket connection is supported if the required -certificates are specified with the VNC option @option{x509}. +If no TLS credentials are provided, the websocket connection runs in +unencrypted mode. If TLS credentials are provided, the websocket connection +requires encrypted client connections. @item password @@ -1239,6 +1240,20 @@ date and time). You can also use keywords "now" or "never" for the expiration time to allow <protocol> password to expire immediately or never expire. +@item tls-creds=@var{ID} + +Provides the ID of a set of TLS credentials to use to secure the +VNC server. They will apply to both the normal VNC server socket +and the websocket socket (if enabled). Setting TLS credentials +will cause the VNC server socket to enable the VeNCrypt auth +mechanism. The credentials should have been previously created +using the @option{-object tls-creds} argument. + +The @option{tls-creds} parameter obsoletes the @option{tls}, +@option{x509}, and @option{x509verify} options, and as such +it is not permitted to set both new and old type options at +the same time. + @item tls Require that client use TLS when communicating with the VNC server. This @@ -1246,6 +1261,9 @@ uses anonymous TLS credentials so is susceptible to a man-in-the-middle attack. It is recommended that this option be combined with either the @option{x509} or @option{x509verify} options. +This option is now deprecated in favor of using the @option{tls-creds} +argument. + @item x509=@var{/path/to/certificate/dir} Valid if @option{tls} is specified. Require that x509 credentials are used @@ -1255,6 +1273,9 @@ to provide authentication of the client when this is used. The path following this option specifies where the x509 certificates are to be loaded from. See the @ref{vnc_security} section for details on generating certificates. +This option is now deprecated in favour of using the @option{tls-creds} +argument. + @item x509verify=@var{/path/to/certificate/dir} Valid if @option{tls} is specified. Require that x509 credentials are used @@ -1268,6 +1289,9 @@ path following this option specifies where the x509 certificates are to be loaded from. See the @ref{vnc_security} section for details on generating certificates. +This option is now deprecated in favour of using the @option{tls-creds} +argument. + @item sasl Require that the client use SASL to authenticate with the VNC server. @@ -3571,6 +3595,53 @@ the @option{virtio-rng} device. The @option{chardev} parameter is the unique ID of a character device backend that provides the connection to the RNG daemon. +@item -object tls-creds-anon,id=@var{id},endpoint=@var{endpoint},dir=@var{/path/to/cred/dir},verify-peer=@var{on|off} + +Creates a TLS anonymous 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. If @option{verify-peer} is enabled +(the default) then once the handshake is completed, the peer credentials +will be verified, though this is a no-op for anonymous credentials. + +The @var{dir} parameter tells QEMU where to find the credential +files. For server endpoints, this directory may 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 +upfront and saved. + +@item -object tls-creds-x509,id=@var{id},endpoint=@var{endpoint},dir=@var{/path/to/cred/dir},verify-peer=@var{on|off} + +Creates a TLS anonymous 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. If @option{verify-peer} is enabled +(the default) then once the handshake is completed, the peer credentials +will be verified. With x509 certificates, this implies that the clients +must be provided with valid client certificates too. + +The @var{dir} parameter tells QEMU where to find the credential +files. For server endpoints, this directory may 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 +upfront and saved. + +For x509 certificate credentials the directory will contain further files +providing the x509 certificates. The certificates must be stored +in PEM format, in filenames @var{ca-cert.pem}, @var{ca-crl.pem} (optional), +@var{server-cert.pem} (only servers), @var{server-key.pem} (only servers), +@var{client-cert.pem} (only clients), and @var{client-key.pem} (only clients). + @end table ETEXI diff --git a/qom/Makefile.objs b/qom/Makefile.objs index 985003bd03..516349eec3 100644 --- a/qom/Makefile.objs +++ b/qom/Makefile.objs @@ -1,3 +1,4 @@ -common-obj-y = object.o container.o qom-qobject.o -common-obj-y += cpu.o -common-obj-y += object_interfaces.o +qom-obj-y = object.o container.o qom-qobject.o +qom-obj-y += object_interfaces.o + +common-obj-y = cpu.o diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py index f2428f3807..a8453d1365 100644 --- a/scripts/qapi-types.py +++ b/scripts/qapi-types.py @@ -101,20 +101,20 @@ struct %(name)s { return ret -def generate_enum_lookup(name, values): +def generate_enum_lookup(name, values, prefix=None): ret = mcgen(''' const char *const %(name)s_lookup[] = { ''', name=c_name(name)) for value in values: - index = c_enum_const(name, value) + index = c_enum_const(name, value, prefix) ret += mcgen(''' [%(index)s] = "%(value)s", ''', index = index, value = value) - max_index = c_enum_const(name, 'MAX') + max_index = c_enum_const(name, 'MAX', prefix) ret += mcgen(''' [%(max_index)s] = NULL, }; @@ -122,7 +122,7 @@ const char *const %(name)s_lookup[] = { max_index=max_index) return ret -def generate_enum(name, values): +def generate_enum(name, values, prefix=None): name = c_name(name) lookup_decl = mcgen(''' @@ -141,7 +141,7 @@ typedef enum %(name)s { i = 0 for value in enum_values: - enum_full_value = c_enum_const(name, value) + enum_full_value = c_enum_const(name, value, prefix) enum_decl += mcgen(''' %(enum_full_value)s = %(i)d, ''', @@ -348,9 +348,11 @@ for expr in exprs: if expr.has_key('struct'): ret += generate_fwd_struct(expr['struct']) elif expr.has_key('enum'): - ret += generate_enum(expr['enum'], expr['data']) + ret += generate_enum(expr['enum'], expr['data'], + expr.get('prefix')) ret += generate_fwd_enum_struct(expr['enum']) - fdef.write(generate_enum_lookup(expr['enum'], expr['data'])) + fdef.write(generate_enum_lookup(expr['enum'], expr['data'], + expr.get('prefix'))) elif expr.has_key('union'): ret += generate_fwd_struct(expr['union']) enum_define = discriminator_find_enum_define(expr) diff --git a/scripts/qapi.py b/scripts/qapi.py index 88fa073184..c4423b740c 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -634,11 +634,15 @@ def check_alternate(expr, expr_info): def check_enum(expr, expr_info): name = expr['enum'] members = expr.get('data') + prefix = expr.get('prefix') values = { 'MAX': '(automatic)' } if not isinstance(members, list): raise QAPIExprError(expr_info, "Enum '%s' requires an array for 'data'" % name) + if prefix is not None and not isinstance(prefix, str): + raise QAPIExprError(expr_info, + "Enum '%s' requires a string for 'prefix'" % name) for member in members: check_name(expr_info, "Member of enum '%s'" %name, member, enum_member=True) @@ -693,7 +697,7 @@ def check_exprs(exprs): expr = expr_elem['expr'] info = expr_elem['info'] if expr.has_key('enum'): - check_keys(expr_elem, 'enum', ['data']) + check_keys(expr_elem, 'enum', ['data'], ['prefix']) add_enum(expr['enum'], info, expr['data']) elif expr.has_key('union'): check_keys(expr_elem, 'union', ['data'], @@ -813,7 +817,9 @@ def camel_to_upper(value): new_name += c return new_name.lstrip('_').upper() -def c_enum_const(type_name, const_name): +def c_enum_const(type_name, const_name, prefix=None): + if prefix is not None: + type_name = prefix return camel_to_upper(type_name + '_' + const_name) c_name_trans = string.maketrans('.-', '__') diff --git a/target-arm/helper-a64.c b/target-arm/helper-a64.c index 2c7a645f66..deb8dbe489 100644 --- a/target-arm/helper-a64.c +++ b/target-arm/helper-a64.c @@ -70,20 +70,7 @@ uint32_t HELPER(clz32)(uint32_t x) uint64_t HELPER(rbit64)(uint64_t x) { - /* assign the correct byte position */ - x = bswap64(x); - - /* assign the correct nibble position */ - x = ((x & 0xf0f0f0f0f0f0f0f0ULL) >> 4) - | ((x & 0x0f0f0f0f0f0f0f0fULL) << 4); - - /* assign the correct bit position */ - x = ((x & 0x8888888888888888ULL) >> 3) - | ((x & 0x4444444444444444ULL) >> 1) - | ((x & 0x2222222222222222ULL) << 1) - | ((x & 0x1111111111111111ULL) << 3); - - return x; + return revbit64(x); } /* Convert a softfloat float_relation_ (as returned by diff --git a/target-arm/helper.c b/target-arm/helper.c index 65b9ff5ad0..12ea88fd52 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -5044,17 +5044,7 @@ uint32_t HELPER(udiv)(uint32_t num, uint32_t den) uint32_t HELPER(rbit)(uint32_t x) { - x = ((x & 0xff000000) >> 24) - | ((x & 0x00ff0000) >> 8) - | ((x & 0x0000ff00) << 8) - | ((x & 0x000000ff) << 24); - x = ((x & 0xf0f0f0f0) >> 4) - | ((x & 0x0f0f0f0f) << 4); - x = ((x & 0x88888888) >> 3) - | ((x & 0x44444444) >> 1) - | ((x & 0x22222222) << 1) - | ((x & 0x11111111) << 3); - return x; + return revbit32(x); } #if defined(CONFIG_USER_ONLY) diff --git a/target-i386/cc_helper.c b/target-i386/cc_helper.c index ecbf0ec09c..d5b7c7b17a 100644 --- a/target-i386/cc_helper.c +++ b/target-i386/cc_helper.c @@ -378,7 +378,7 @@ void helper_sti_vm(CPUX86State *env) { env->eflags |= VIF_MASK; if (env->eflags & VIP_MASK) { - raise_exception(env, EXCP0D_GPF); + raise_exception_ra(env, EXCP0D_GPF, GETPC()); } } #endif diff --git a/target-i386/cpu.h b/target-i386/cpu.h index 5d515f69c8..5231e8c300 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -1269,8 +1269,12 @@ void cpu_x86_inject_mce(Monitor *mon, X86CPU *cpu, int bank, /* excp_helper.c */ void QEMU_NORETURN raise_exception(CPUX86State *env, int exception_index); +void QEMU_NORETURN raise_exception_ra(CPUX86State *env, int exception_index, + uintptr_t retaddr); void QEMU_NORETURN raise_exception_err(CPUX86State *env, int exception_index, int error_code); +void QEMU_NORETURN raise_exception_err_ra(CPUX86State *env, int exception_index, + int error_code, uintptr_t retaddr); void QEMU_NORETURN raise_interrupt(CPUX86State *nenv, int intno, int is_int, int error_code, int next_eip_addend); diff --git a/target-i386/excp_helper.c b/target-i386/excp_helper.c index 99fca847dd..5e347bc46c 100644 --- a/target-i386/excp_helper.c +++ b/target-i386/excp_helper.c @@ -22,14 +22,6 @@ #include "sysemu/sysemu.h" #include "exec/helper-proto.h" -#if 0 -#define raise_exception_err(env, a, b) \ - do { \ - qemu_log("raise_exception line=%d\n", __LINE__); \ - (raise_exception_err)(env, a, b); \ - } while (0) -#endif - void helper_raise_interrupt(CPUX86State *env, int intno, int next_eip_addend) { raise_interrupt(env, intno, 1, 0, next_eip_addend); @@ -92,7 +84,8 @@ static int check_exception(CPUX86State *env, int intno, int *error_code) */ static void QEMU_NORETURN raise_interrupt2(CPUX86State *env, int intno, int is_int, int error_code, - int next_eip_addend) + int next_eip_addend, + uintptr_t retaddr) { CPUState *cs = CPU(x86_env_get_cpu(env)); @@ -108,7 +101,7 @@ static void QEMU_NORETURN raise_interrupt2(CPUX86State *env, int intno, env->error_code = error_code; env->exception_is_int = is_int; env->exception_next_eip = env->eip + next_eip_addend; - cpu_loop_exit(cs); + cpu_loop_exit_restore(cs, retaddr); } /* shortcuts to generate exceptions */ @@ -116,16 +109,27 @@ static void QEMU_NORETURN raise_interrupt2(CPUX86State *env, int intno, void QEMU_NORETURN raise_interrupt(CPUX86State *env, int intno, int is_int, int error_code, int next_eip_addend) { - raise_interrupt2(env, intno, is_int, error_code, next_eip_addend); + raise_interrupt2(env, intno, is_int, error_code, next_eip_addend, 0); } void raise_exception_err(CPUX86State *env, int exception_index, int error_code) { - raise_interrupt2(env, exception_index, 0, error_code, 0); + raise_interrupt2(env, exception_index, 0, error_code, 0, 0); +} + +void raise_exception_err_ra(CPUX86State *env, int exception_index, + int error_code, uintptr_t retaddr) +{ + raise_interrupt2(env, exception_index, 0, error_code, 0, retaddr); } void raise_exception(CPUX86State *env, int exception_index) { - raise_interrupt2(env, exception_index, 0, 0, 0); + raise_interrupt2(env, exception_index, 0, 0, 0, 0); +} + +void raise_exception_ra(CPUX86State *env, int exception_index, uintptr_t retaddr) +{ + raise_interrupt2(env, exception_index, 0, 0, 0, retaddr); } diff --git a/target-i386/fpu_helper.c b/target-i386/fpu_helper.c index 1f954e0c5c..d421a475ff 100644 --- a/target-i386/fpu_helper.c +++ b/target-i386/fpu_helper.c @@ -67,22 +67,24 @@ static inline void fpop(CPUX86State *env) env->fpstt = (env->fpstt + 1) & 7; } -static inline floatx80 helper_fldt(CPUX86State *env, target_ulong ptr) +static inline floatx80 helper_fldt(CPUX86State *env, target_ulong ptr, + uintptr_t retaddr) { CPU_LDoubleU temp; - temp.l.lower = cpu_ldq_data(env, ptr); - temp.l.upper = cpu_lduw_data(env, ptr + 8); + temp.l.lower = cpu_ldq_data_ra(env, ptr, retaddr); + temp.l.upper = cpu_lduw_data_ra(env, ptr + 8, retaddr); return temp.d; } -static inline void helper_fstt(CPUX86State *env, floatx80 f, target_ulong ptr) +static inline void helper_fstt(CPUX86State *env, floatx80 f, target_ulong ptr, + uintptr_t retaddr) { CPU_LDoubleU temp; temp.d = f; - cpu_stq_data(env, ptr, temp.l.lower); - cpu_stw_data(env, ptr + 8, temp.l.upper); + cpu_stq_data_ra(env, ptr, temp.l.lower, retaddr); + cpu_stw_data_ra(env, ptr + 8, temp.l.upper, retaddr); } /* x87 FPU helpers */ @@ -125,10 +127,10 @@ static inline floatx80 helper_fdiv(CPUX86State *env, floatx80 a, floatx80 b) return floatx80_div(a, b, &env->fp_status); } -static void fpu_raise_exception(CPUX86State *env) +static void fpu_raise_exception(CPUX86State *env, uintptr_t retaddr) { if (env->cr[0] & CR0_NE_MASK) { - raise_exception(env, EXCP10_COPR); + raise_exception_ra(env, EXCP10_COPR, retaddr); } #if !defined(CONFIG_USER_ONLY) else { @@ -313,14 +315,14 @@ void helper_fldt_ST0(CPUX86State *env, target_ulong ptr) int new_fpstt; new_fpstt = (env->fpstt - 1) & 7; - env->fpregs[new_fpstt].d = helper_fldt(env, ptr); + env->fpregs[new_fpstt].d = helper_fldt(env, ptr, GETPC()); env->fpstt = new_fpstt; env->fptags[new_fpstt] = 0; /* validate stack entry */ } void helper_fstt_ST0(CPUX86State *env, target_ulong ptr) { - helper_fstt(env, ST0, ptr); + helper_fstt(env, ST0, ptr, GETPC()); } void helper_fpush(CPUX86State *env) @@ -603,7 +605,7 @@ void helper_fclex(CPUX86State *env) void helper_fwait(CPUX86State *env) { if (env->fpus & FPUS_SE) { - fpu_raise_exception(env); + fpu_raise_exception(env, GETPC()); } } @@ -633,11 +635,11 @@ void helper_fbld_ST0(CPUX86State *env, target_ulong ptr) val = 0; for (i = 8; i >= 0; i--) { - v = cpu_ldub_data(env, ptr + i); + v = cpu_ldub_data_ra(env, ptr + i, GETPC()); val = (val * 100) + ((v >> 4) * 10) + (v & 0xf); } tmp = int64_to_floatx80(val, &env->fp_status); - if (cpu_ldub_data(env, ptr + 9) & 0x80) { + if (cpu_ldub_data_ra(env, ptr + 9, GETPC()) & 0x80) { tmp = floatx80_chs(tmp); } fpush(env); @@ -654,10 +656,10 @@ void helper_fbst_ST0(CPUX86State *env, target_ulong ptr) mem_ref = ptr; mem_end = mem_ref + 9; if (val < 0) { - cpu_stb_data(env, mem_end, 0x80); + cpu_stb_data_ra(env, mem_end, 0x80, GETPC()); val = -val; } else { - cpu_stb_data(env, mem_end, 0x00); + cpu_stb_data_ra(env, mem_end, 0x00, GETPC()); } while (mem_ref < mem_end) { if (val == 0) { @@ -666,10 +668,10 @@ void helper_fbst_ST0(CPUX86State *env, target_ulong ptr) v = val % 100; val = val / 100; v = ((v / 10) << 4) | (v % 10); - cpu_stb_data(env, mem_ref++, v); + cpu_stb_data_ra(env, mem_ref++, v, GETPC()); } while (mem_ref < mem_end) { - cpu_stb_data(env, mem_ref++, 0); + cpu_stb_data_ra(env, mem_ref++, 0, GETPC()); } } @@ -977,7 +979,8 @@ void helper_fxam_ST0(CPUX86State *env) } } -void helper_fstenv(CPUX86State *env, target_ulong ptr, int data32) +static void do_fstenv(CPUX86State *env, target_ulong ptr, int data32, + uintptr_t retaddr) { int fpus, fptag, exp, i; uint64_t mant; @@ -1005,37 +1008,43 @@ void helper_fstenv(CPUX86State *env, target_ulong ptr, int data32) } if (data32) { /* 32 bit */ - cpu_stl_data(env, ptr, env->fpuc); - cpu_stl_data(env, ptr + 4, fpus); - cpu_stl_data(env, ptr + 8, fptag); - cpu_stl_data(env, ptr + 12, 0); /* fpip */ - cpu_stl_data(env, ptr + 16, 0); /* fpcs */ - cpu_stl_data(env, ptr + 20, 0); /* fpoo */ - cpu_stl_data(env, ptr + 24, 0); /* fpos */ + cpu_stl_data_ra(env, ptr, env->fpuc, retaddr); + cpu_stl_data_ra(env, ptr + 4, fpus, retaddr); + cpu_stl_data_ra(env, ptr + 8, fptag, retaddr); + cpu_stl_data_ra(env, ptr + 12, 0, retaddr); /* fpip */ + cpu_stl_data_ra(env, ptr + 16, 0, retaddr); /* fpcs */ + cpu_stl_data_ra(env, ptr + 20, 0, retaddr); /* fpoo */ + cpu_stl_data_ra(env, ptr + 24, 0, retaddr); /* fpos */ } else { /* 16 bit */ - cpu_stw_data(env, ptr, env->fpuc); - cpu_stw_data(env, ptr + 2, fpus); - cpu_stw_data(env, ptr + 4, fptag); - cpu_stw_data(env, ptr + 6, 0); - cpu_stw_data(env, ptr + 8, 0); - cpu_stw_data(env, ptr + 10, 0); - cpu_stw_data(env, ptr + 12, 0); + cpu_stw_data_ra(env, ptr, env->fpuc, retaddr); + cpu_stw_data_ra(env, ptr + 2, fpus, retaddr); + cpu_stw_data_ra(env, ptr + 4, fptag, retaddr); + cpu_stw_data_ra(env, ptr + 6, 0, retaddr); + cpu_stw_data_ra(env, ptr + 8, 0, retaddr); + cpu_stw_data_ra(env, ptr + 10, 0, retaddr); + cpu_stw_data_ra(env, ptr + 12, 0, retaddr); } } -void helper_fldenv(CPUX86State *env, target_ulong ptr, int data32) +void helper_fstenv(CPUX86State *env, target_ulong ptr, int data32) +{ + do_fstenv(env, ptr, data32, GETPC()); +} + +static void do_fldenv(CPUX86State *env, target_ulong ptr, int data32, + uintptr_t retaddr) { int i, fpus, fptag; if (data32) { - cpu_set_fpuc(env, cpu_lduw_data(env, ptr)); - fpus = cpu_lduw_data(env, ptr + 4); - fptag = cpu_lduw_data(env, ptr + 8); + cpu_set_fpuc(env, cpu_lduw_data_ra(env, ptr, retaddr)); + fpus = cpu_lduw_data_ra(env, ptr + 4, retaddr); + fptag = cpu_lduw_data_ra(env, ptr + 8, retaddr); } else { - cpu_set_fpuc(env, cpu_lduw_data(env, ptr)); - fpus = cpu_lduw_data(env, ptr + 2); - fptag = cpu_lduw_data(env, ptr + 4); + cpu_set_fpuc(env, cpu_lduw_data_ra(env, ptr, retaddr)); + fpus = cpu_lduw_data_ra(env, ptr + 2, retaddr); + fptag = cpu_lduw_data_ra(env, ptr + 4, retaddr); } env->fpstt = (fpus >> 11) & 7; env->fpus = fpus & ~0x3800; @@ -1045,17 +1054,22 @@ void helper_fldenv(CPUX86State *env, target_ulong ptr, int data32) } } +void helper_fldenv(CPUX86State *env, target_ulong ptr, int data32) +{ + do_fldenv(env, ptr, data32, GETPC()); +} + void helper_fsave(CPUX86State *env, target_ulong ptr, int data32) { floatx80 tmp; int i; - helper_fstenv(env, ptr, data32); + do_fstenv(env, ptr, data32, GETPC()); ptr += (14 << data32); for (i = 0; i < 8; i++) { tmp = ST(i); - helper_fstt(env, tmp, ptr); + helper_fstt(env, tmp, ptr, GETPC()); ptr += 10; } @@ -1078,11 +1092,11 @@ void helper_frstor(CPUX86State *env, target_ulong ptr, int data32) floatx80 tmp; int i; - helper_fldenv(env, ptr, data32); + do_fldenv(env, ptr, data32, GETPC()); ptr += (14 << data32); for (i = 0; i < 8; i++) { - tmp = helper_fldt(env, ptr); + tmp = helper_fldt(env, ptr, GETPC()); ST(i) = tmp; ptr += 10; } @@ -1100,7 +1114,8 @@ void cpu_x86_frstor(CPUX86State *env, target_ulong ptr, int data32) } #endif -void helper_fxsave(CPUX86State *env, target_ulong ptr, int data64) +static void do_fxsave(CPUX86State *env, target_ulong ptr, int data64, + uintptr_t retaddr) { int fpus, fptag, i, nb_xmm_regs; floatx80 tmp; @@ -1108,7 +1123,7 @@ void helper_fxsave(CPUX86State *env, target_ulong ptr, int data64) /* The operand must be 16 byte aligned */ if (ptr & 0xf) { - raise_exception(env, EXCP0D_GPF); + raise_exception_ra(env, EXCP0D_GPF, retaddr); } fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; @@ -1116,33 +1131,33 @@ void helper_fxsave(CPUX86State *env, target_ulong ptr, int data64) for (i = 0; i < 8; i++) { fptag |= (env->fptags[i] << i); } - cpu_stw_data(env, ptr, env->fpuc); - cpu_stw_data(env, ptr + 2, fpus); - cpu_stw_data(env, ptr + 4, fptag ^ 0xff); + cpu_stw_data_ra(env, ptr, env->fpuc, retaddr); + cpu_stw_data_ra(env, ptr + 2, fpus, retaddr); + cpu_stw_data_ra(env, ptr + 4, fptag ^ 0xff, retaddr); #ifdef TARGET_X86_64 if (data64) { - cpu_stq_data(env, ptr + 0x08, 0); /* rip */ - cpu_stq_data(env, ptr + 0x10, 0); /* rdp */ + cpu_stq_data_ra(env, ptr + 0x08, 0, retaddr); /* rip */ + cpu_stq_data_ra(env, ptr + 0x10, 0, retaddr); /* rdp */ } else #endif { - cpu_stl_data(env, ptr + 0x08, 0); /* eip */ - cpu_stl_data(env, ptr + 0x0c, 0); /* sel */ - cpu_stl_data(env, ptr + 0x10, 0); /* dp */ - cpu_stl_data(env, ptr + 0x14, 0); /* sel */ + cpu_stl_data_ra(env, ptr + 0x08, 0, retaddr); /* eip */ + cpu_stl_data_ra(env, ptr + 0x0c, 0, retaddr); /* sel */ + cpu_stl_data_ra(env, ptr + 0x10, 0, retaddr); /* dp */ + cpu_stl_data_ra(env, ptr + 0x14, 0, retaddr); /* sel */ } addr = ptr + 0x20; for (i = 0; i < 8; i++) { tmp = ST(i); - helper_fstt(env, tmp, addr); + helper_fstt(env, tmp, addr, retaddr); addr += 16; } if (env->cr[4] & CR4_OSFXSR_MASK) { /* XXX: finish it */ - cpu_stl_data(env, ptr + 0x18, env->mxcsr); /* mxcsr */ - cpu_stl_data(env, ptr + 0x1c, 0x0000ffff); /* mxcsr_mask */ + cpu_stl_data_ra(env, ptr + 0x18, env->mxcsr, retaddr); /* mxcsr */ + cpu_stl_data_ra(env, ptr + 0x1c, 0x0000ffff, retaddr); /* mxcsr_mask */ if (env->hflags & HF_CS64_MASK) { nb_xmm_regs = 16; } else { @@ -1154,15 +1169,21 @@ void helper_fxsave(CPUX86State *env, target_ulong ptr, int data64) || (env->hflags & HF_CPL_MASK) || !(env->hflags & HF_LMA_MASK)) { for (i = 0; i < nb_xmm_regs; i++) { - cpu_stq_data(env, addr, env->xmm_regs[i].XMM_Q(0)); - cpu_stq_data(env, addr + 8, env->xmm_regs[i].XMM_Q(1)); + cpu_stq_data_ra(env, addr, env->xmm_regs[i].XMM_Q(0), retaddr); + cpu_stq_data_ra(env, addr + 8, env->xmm_regs[i].XMM_Q(1), retaddr); addr += 16; } } } } -void helper_fxrstor(CPUX86State *env, target_ulong ptr, int data64) +void helper_fxsave(CPUX86State *env, target_ulong ptr, int data64) +{ + do_fxsave(env, ptr, data64, GETPC()); +} + +static void do_fxrstor(CPUX86State *env, target_ulong ptr, int data64, + uintptr_t retaddr) { int i, fpus, fptag, nb_xmm_regs; floatx80 tmp; @@ -1170,12 +1191,12 @@ void helper_fxrstor(CPUX86State *env, target_ulong ptr, int data64) /* The operand must be 16 byte aligned */ if (ptr & 0xf) { - raise_exception(env, EXCP0D_GPF); + raise_exception_ra(env, EXCP0D_GPF, retaddr); } - cpu_set_fpuc(env, cpu_lduw_data(env, ptr)); - fpus = cpu_lduw_data(env, ptr + 2); - fptag = cpu_lduw_data(env, ptr + 4); + cpu_set_fpuc(env, cpu_lduw_data_ra(env, ptr, retaddr)); + fpus = cpu_lduw_data_ra(env, ptr + 2, retaddr); + fptag = cpu_lduw_data_ra(env, ptr + 4, retaddr); env->fpstt = (fpus >> 11) & 7; env->fpus = fpus & ~0x3800; fptag ^= 0xff; @@ -1185,15 +1206,15 @@ void helper_fxrstor(CPUX86State *env, target_ulong ptr, int data64) addr = ptr + 0x20; for (i = 0; i < 8; i++) { - tmp = helper_fldt(env, addr); + tmp = helper_fldt(env, addr, retaddr); ST(i) = tmp; addr += 16; } if (env->cr[4] & CR4_OSFXSR_MASK) { /* XXX: finish it */ - cpu_set_mxcsr(env, cpu_ldl_data(env, ptr + 0x18)); - /* cpu_ldl_data(env, ptr + 0x1c); */ + cpu_set_mxcsr(env, cpu_ldl_data_ra(env, ptr + 0x18, retaddr)); + /* cpu_ldl_data_ra(env, ptr + 0x1c, retaddr); */ if (env->hflags & HF_CS64_MASK) { nb_xmm_regs = 16; } else { @@ -1205,14 +1226,19 @@ void helper_fxrstor(CPUX86State *env, target_ulong ptr, int data64) || (env->hflags & HF_CPL_MASK) || !(env->hflags & HF_LMA_MASK)) { for (i = 0; i < nb_xmm_regs; i++) { - env->xmm_regs[i].XMM_Q(0) = cpu_ldq_data(env, addr); - env->xmm_regs[i].XMM_Q(1) = cpu_ldq_data(env, addr + 8); + env->xmm_regs[i].XMM_Q(0) = cpu_ldq_data_ra(env, addr, retaddr); + env->xmm_regs[i].XMM_Q(1) = cpu_ldq_data_ra(env, addr + 8, retaddr); addr += 16; } } } } +void helper_fxrstor(CPUX86State *env, target_ulong ptr, int data64) +{ + do_fxrstor(env, ptr, data64, GETPC()); +} + void cpu_get_fp80(uint64_t *pmant, uint16_t *pexp, floatx80 f) { CPU_LDoubleU temp; diff --git a/target-i386/helper.h b/target-i386/helper.h index 74308f442e..8454a048d2 100644 --- a/target-i386/helper.h +++ b/target-i386/helper.h @@ -30,9 +30,9 @@ DEF_HELPER_2(verw, void, env, tl) DEF_HELPER_2(lldt, void, env, int) DEF_HELPER_2(ltr, void, env, int) DEF_HELPER_3(load_seg, void, env, int, int) -DEF_HELPER_4(ljmp_protected, void, env, int, tl, int) +DEF_HELPER_4(ljmp_protected, void, env, int, tl, tl) DEF_HELPER_5(lcall_real, void, env, int, tl, int, int) -DEF_HELPER_5(lcall_protected, void, env, int, tl, int, int) +DEF_HELPER_5(lcall_protected, void, env, int, tl, int, tl) DEF_HELPER_2(iret_real, void, env, int) DEF_HELPER_3(iret_protected, void, env, int, int) DEF_HELPER_3(lret_protected, void, env, int, int) diff --git a/target-i386/int_helper.c b/target-i386/int_helper.c index b0d78e6eee..3dcd25fd6e 100644 --- a/target-i386/int_helper.c +++ b/target-i386/int_helper.c @@ -48,11 +48,11 @@ void helper_divb_AL(CPUX86State *env, target_ulong t0) num = (env->regs[R_EAX] & 0xffff); den = (t0 & 0xff); if (den == 0) { - raise_exception(env, EXCP00_DIVZ); + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); } q = (num / den); if (q > 0xff) { - raise_exception(env, EXCP00_DIVZ); + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); } q &= 0xff; r = (num % den) & 0xff; @@ -66,11 +66,11 @@ void helper_idivb_AL(CPUX86State *env, target_ulong t0) num = (int16_t)env->regs[R_EAX]; den = (int8_t)t0; if (den == 0) { - raise_exception(env, EXCP00_DIVZ); + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); } q = (num / den); if (q != (int8_t)q) { - raise_exception(env, EXCP00_DIVZ); + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); } q &= 0xff; r = (num % den) & 0xff; @@ -84,11 +84,11 @@ void helper_divw_AX(CPUX86State *env, target_ulong t0) num = (env->regs[R_EAX] & 0xffff) | ((env->regs[R_EDX] & 0xffff) << 16); den = (t0 & 0xffff); if (den == 0) { - raise_exception(env, EXCP00_DIVZ); + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); } q = (num / den); if (q > 0xffff) { - raise_exception(env, EXCP00_DIVZ); + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); } q &= 0xffff; r = (num % den) & 0xffff; @@ -103,11 +103,11 @@ void helper_idivw_AX(CPUX86State *env, target_ulong t0) num = (env->regs[R_EAX] & 0xffff) | ((env->regs[R_EDX] & 0xffff) << 16); den = (int16_t)t0; if (den == 0) { - raise_exception(env, EXCP00_DIVZ); + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); } q = (num / den); if (q != (int16_t)q) { - raise_exception(env, EXCP00_DIVZ); + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); } q &= 0xffff; r = (num % den) & 0xffff; @@ -123,12 +123,12 @@ void helper_divl_EAX(CPUX86State *env, target_ulong t0) num = ((uint32_t)env->regs[R_EAX]) | ((uint64_t)((uint32_t)env->regs[R_EDX]) << 32); den = t0; if (den == 0) { - raise_exception(env, EXCP00_DIVZ); + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); } q = (num / den); r = (num % den); if (q > 0xffffffff) { - raise_exception(env, EXCP00_DIVZ); + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); } env->regs[R_EAX] = (uint32_t)q; env->regs[R_EDX] = (uint32_t)r; @@ -142,12 +142,12 @@ void helper_idivl_EAX(CPUX86State *env, target_ulong t0) num = ((uint32_t)env->regs[R_EAX]) | ((uint64_t)((uint32_t)env->regs[R_EDX]) << 32); den = t0; if (den == 0) { - raise_exception(env, EXCP00_DIVZ); + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); } q = (num / den); r = (num % den); if (q != (int32_t)q) { - raise_exception(env, EXCP00_DIVZ); + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); } env->regs[R_EAX] = (uint32_t)q; env->regs[R_EDX] = (uint32_t)r; @@ -379,12 +379,12 @@ void helper_divq_EAX(CPUX86State *env, target_ulong t0) uint64_t r0, r1; if (t0 == 0) { - raise_exception(env, EXCP00_DIVZ); + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); } r0 = env->regs[R_EAX]; r1 = env->regs[R_EDX]; if (div64(&r0, &r1, t0)) { - raise_exception(env, EXCP00_DIVZ); + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); } env->regs[R_EAX] = r0; env->regs[R_EDX] = r1; @@ -395,12 +395,12 @@ void helper_idivq_EAX(CPUX86State *env, target_ulong t0) uint64_t r0, r1; if (t0 == 0) { - raise_exception(env, EXCP00_DIVZ); + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); } r0 = env->regs[R_EAX]; r1 = env->regs[R_EDX]; if (idiv64(&r0, &r1, t0)) { - raise_exception(env, EXCP00_DIVZ); + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); } env->regs[R_EAX] = r0; env->regs[R_EDX] = r1; diff --git a/target-i386/mem_helper.c b/target-i386/mem_helper.c index 8bf0da2433..0e20df2f75 100644 --- a/target-i386/mem_helper.c +++ b/target-i386/mem_helper.c @@ -60,13 +60,14 @@ void helper_cmpxchg8b(CPUX86State *env, target_ulong a0) int eflags; eflags = cpu_cc_compute_all(env, CC_OP); - d = cpu_ldq_data(env, a0); + d = cpu_ldq_data_ra(env, a0, GETPC()); if (d == (((uint64_t)env->regs[R_EDX] << 32) | (uint32_t)env->regs[R_EAX])) { - cpu_stq_data(env, a0, ((uint64_t)env->regs[R_ECX] << 32) | (uint32_t)env->regs[R_EBX]); + cpu_stq_data_ra(env, a0, ((uint64_t)env->regs[R_ECX] << 32) + | (uint32_t)env->regs[R_EBX], GETPC()); eflags |= CC_Z; } else { /* always do the store */ - cpu_stq_data(env, a0, d); + cpu_stq_data_ra(env, a0, d, GETPC()); env->regs[R_EDX] = (uint32_t)(d >> 32); env->regs[R_EAX] = (uint32_t)d; eflags &= ~CC_Z; @@ -81,19 +82,19 @@ void helper_cmpxchg16b(CPUX86State *env, target_ulong a0) int eflags; if ((a0 & 0xf) != 0) { - raise_exception(env, EXCP0D_GPF); + raise_exception_ra(env, EXCP0D_GPF, GETPC()); } eflags = cpu_cc_compute_all(env, CC_OP); - d0 = cpu_ldq_data(env, a0); - d1 = cpu_ldq_data(env, a0 + 8); + d0 = cpu_ldq_data_ra(env, a0, GETPC()); + d1 = cpu_ldq_data_ra(env, a0 + 8, GETPC()); if (d0 == env->regs[R_EAX] && d1 == env->regs[R_EDX]) { - cpu_stq_data(env, a0, env->regs[R_EBX]); - cpu_stq_data(env, a0 + 8, env->regs[R_ECX]); + cpu_stq_data_ra(env, a0, env->regs[R_EBX], GETPC()); + cpu_stq_data_ra(env, a0 + 8, env->regs[R_ECX], GETPC()); eflags |= CC_Z; } else { /* always do the store */ - cpu_stq_data(env, a0, d0); - cpu_stq_data(env, a0 + 8, d1); + cpu_stq_data_ra(env, a0, d0, GETPC()); + cpu_stq_data_ra(env, a0 + 8, d1, GETPC()); env->regs[R_EDX] = d1; env->regs[R_EAX] = d0; eflags &= ~CC_Z; @@ -106,11 +107,11 @@ void helper_boundw(CPUX86State *env, target_ulong a0, int v) { int low, high; - low = cpu_ldsw_data(env, a0); - high = cpu_ldsw_data(env, a0 + 2); + low = cpu_ldsw_data_ra(env, a0, GETPC()); + high = cpu_ldsw_data_ra(env, a0 + 2, GETPC()); v = (int16_t)v; if (v < low || v > high) { - raise_exception(env, EXCP05_BOUND); + raise_exception_ra(env, EXCP05_BOUND, GETPC()); } } @@ -118,10 +119,10 @@ void helper_boundl(CPUX86State *env, target_ulong a0, int v) { int low, high; - low = cpu_ldl_data(env, a0); - high = cpu_ldl_data(env, a0 + 4); + low = cpu_ldl_data_ra(env, a0, GETPC()); + high = cpu_ldl_data_ra(env, a0 + 4, GETPC()); if (v < low || v > high) { - raise_exception(env, EXCP05_BOUND); + raise_exception_ra(env, EXCP05_BOUND, GETPC()); } } @@ -141,11 +142,7 @@ void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx, X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; - if (retaddr) { - /* now we have a real cpu fault */ - cpu_restore_state(cs, retaddr); - } - raise_exception_err(env, cs->exception_index, env->error_code); + raise_exception_err_ra(env, cs->exception_index, env->error_code, retaddr); } } #endif diff --git a/target-i386/misc_helper.c b/target-i386/misc_helper.c index 52c5d65e91..6bfc7dd24e 100644 --- a/target-i386/misc_helper.c +++ b/target-i386/misc_helper.c @@ -220,7 +220,7 @@ void helper_rdtsc(CPUX86State *env) uint64_t val; if ((env->cr[4] & CR4_TSD_MASK) && ((env->hflags & HF_CPL_MASK) != 0)) { - raise_exception(env, EXCP0D_GPF); + raise_exception_ra(env, EXCP0D_GPF, GETPC()); } cpu_svm_check_intercept_param(env, SVM_EXIT_RDTSC, 0); @@ -238,7 +238,7 @@ void helper_rdtscp(CPUX86State *env) void helper_rdpmc(CPUX86State *env) { if ((env->cr[4] & CR4_PCE_MASK) && ((env->hflags & HF_CPL_MASK) != 0)) { - raise_exception(env, EXCP0D_GPF); + raise_exception_ra(env, EXCP0D_GPF, GETPC()); } cpu_svm_check_intercept_param(env, SVM_EXIT_RDPMC, 0); @@ -589,7 +589,7 @@ void helper_hlt(CPUX86State *env, int next_eip_addend) void helper_monitor(CPUX86State *env, target_ulong ptr) { if ((uint32_t)env->regs[R_ECX] != 0) { - raise_exception(env, EXCP0D_GPF); + raise_exception_ra(env, EXCP0D_GPF, GETPC()); } /* XXX: store address? */ cpu_svm_check_intercept_param(env, SVM_EXIT_MONITOR, 0); @@ -601,7 +601,7 @@ void helper_mwait(CPUX86State *env, int next_eip_addend) X86CPU *cpu; if ((uint32_t)env->regs[R_ECX] != 0) { - raise_exception(env, EXCP0D_GPF); + raise_exception_ra(env, EXCP0D_GPF, GETPC()); } cpu_svm_check_intercept_param(env, SVM_EXIT_MWAIT, 0); env->eip += next_eip_addend; diff --git a/target-i386/ops_sse.h b/target-i386/ops_sse.h index bee134bae1..7aa693aee9 100644 --- a/target-i386/ops_sse.h +++ b/target-i386/ops_sse.h @@ -483,7 +483,7 @@ void glue(helper_maskmov, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, for (i = 0; i < (8 << SHIFT); i++) { if (s->B(i) & 0x80) { - cpu_stb_data(env, a0 + i, d->B(i)); + cpu_stb_data_ra(env, a0 + i, d->B(i), GETPC()); } } } diff --git a/target-i386/seg_helper.c b/target-i386/seg_helper.c index 8a4271ebe2..1a3a2e7573 100644 --- a/target-i386/seg_helper.c +++ b/target-i386/seg_helper.c @@ -67,8 +67,9 @@ #endif /* return non zero if error */ -static inline int load_segment(CPUX86State *env, uint32_t *e1_ptr, - uint32_t *e2_ptr, int selector) +static inline int load_segment_ra(CPUX86State *env, uint32_t *e1_ptr, + uint32_t *e2_ptr, int selector, + uintptr_t retaddr) { SegmentCache *dt; int index; @@ -84,11 +85,17 @@ static inline int load_segment(CPUX86State *env, uint32_t *e1_ptr, return -1; } ptr = dt->base + index; - *e1_ptr = cpu_ldl_kernel(env, ptr); - *e2_ptr = cpu_ldl_kernel(env, ptr + 4); + *e1_ptr = cpu_ldl_kernel_ra(env, ptr, retaddr); + *e2_ptr = cpu_ldl_kernel_ra(env, ptr + 4, retaddr); return 0; } +static inline int load_segment(CPUX86State *env, uint32_t *e1_ptr, + uint32_t *e2_ptr, int selector) +{ + return load_segment_ra(env, e1_ptr, e2_ptr, selector, 0); +} + static inline unsigned int get_seg_limit(uint32_t e1, uint32_t e2) { unsigned int limit; @@ -124,7 +131,8 @@ static inline void load_seg_vm(CPUX86State *env, int seg, int selector) } static inline void get_ss_esp_from_tss(CPUX86State *env, uint32_t *ss_ptr, - uint32_t *esp_ptr, int dpl) + uint32_t *esp_ptr, int dpl, + uintptr_t retaddr) { X86CPU *cpu = x86_env_get_cpu(env); int type, index, shift; @@ -153,60 +161,61 @@ static inline void get_ss_esp_from_tss(CPUX86State *env, uint32_t *ss_ptr, shift = type >> 3; index = (dpl * 4 + 2) << shift; if (index + (4 << shift) - 1 > env->tr.limit) { - raise_exception_err(env, EXCP0A_TSS, env->tr.selector & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, env->tr.selector & 0xfffc, retaddr); } if (shift == 0) { - *esp_ptr = cpu_lduw_kernel(env, env->tr.base + index); - *ss_ptr = cpu_lduw_kernel(env, env->tr.base + index + 2); + *esp_ptr = cpu_lduw_kernel_ra(env, env->tr.base + index, retaddr); + *ss_ptr = cpu_lduw_kernel_ra(env, env->tr.base + index + 2, retaddr); } else { - *esp_ptr = cpu_ldl_kernel(env, env->tr.base + index); - *ss_ptr = cpu_lduw_kernel(env, env->tr.base + index + 4); + *esp_ptr = cpu_ldl_kernel_ra(env, env->tr.base + index, retaddr); + *ss_ptr = cpu_lduw_kernel_ra(env, env->tr.base + index + 4, retaddr); } } -static void tss_load_seg(CPUX86State *env, int seg_reg, int selector, int cpl) +static void tss_load_seg(CPUX86State *env, int seg_reg, int selector, int cpl, + uintptr_t retaddr) { uint32_t e1, e2; int rpl, dpl; if ((selector & 0xfffc) != 0) { - if (load_segment(env, &e1, &e2, selector) != 0) { - raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc); + if (load_segment_ra(env, &e1, &e2, selector, retaddr) != 0) { + raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); } if (!(e2 & DESC_S_MASK)) { - raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); } rpl = selector & 3; dpl = (e2 >> DESC_DPL_SHIFT) & 3; if (seg_reg == R_CS) { if (!(e2 & DESC_CS_MASK)) { - raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); } if (dpl != rpl) { - raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); } } else if (seg_reg == R_SS) { /* SS must be writable data */ if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK)) { - raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); } if (dpl != cpl || dpl != rpl) { - raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); } } else { /* not readable code */ if ((e2 & DESC_CS_MASK) && !(e2 & DESC_R_MASK)) { - raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); } /* if data or non conforming code, checks the rights */ if (((e2 >> DESC_TYPE_SHIFT) & 0xf) < 12) { if (dpl < cpl || dpl < rpl) { - raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); } } } if (!(e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0B_NOSEG, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0B_NOSEG, selector & 0xfffc, retaddr); } cpu_x86_load_seg_cache(env, seg_reg, selector, get_seg_base(e1, e2), @@ -214,7 +223,7 @@ static void tss_load_seg(CPUX86State *env, int seg_reg, int selector, int cpl) e2); } else { if (seg_reg == R_SS || seg_reg == R_CS) { - raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); } } } @@ -224,9 +233,9 @@ static void tss_load_seg(CPUX86State *env, int seg_reg, int selector, int cpl) #define SWITCH_TSS_CALL 2 /* XXX: restore CPU state in registers (PowerPC case) */ -static void switch_tss(CPUX86State *env, int tss_selector, - uint32_t e1, uint32_t e2, int source, - uint32_t next_eip) +static void switch_tss_ra(CPUX86State *env, int tss_selector, + uint32_t e1, uint32_t e2, int source, + uint32_t next_eip, uintptr_t retaddr) { int tss_limit, tss_limit_max, type, old_tss_limit_max, old_type, v1, v2, i; target_ulong tss_base; @@ -244,26 +253,26 @@ static void switch_tss(CPUX86State *env, int tss_selector, /* if task gate, we read the TSS segment and we load it */ if (type == 5) { if (!(e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0B_NOSEG, tss_selector & 0xfffc); + raise_exception_err_ra(env, EXCP0B_NOSEG, tss_selector & 0xfffc, retaddr); } tss_selector = e1 >> 16; if (tss_selector & 4) { - raise_exception_err(env, EXCP0A_TSS, tss_selector & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, retaddr); } - if (load_segment(env, &e1, &e2, tss_selector) != 0) { - raise_exception_err(env, EXCP0D_GPF, tss_selector & 0xfffc); + if (load_segment_ra(env, &e1, &e2, tss_selector, retaddr) != 0) { + raise_exception_err_ra(env, EXCP0D_GPF, tss_selector & 0xfffc, retaddr); } if (e2 & DESC_S_MASK) { - raise_exception_err(env, EXCP0D_GPF, tss_selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, tss_selector & 0xfffc, retaddr); } type = (e2 >> DESC_TYPE_SHIFT) & 0xf; if ((type & 7) != 1) { - raise_exception_err(env, EXCP0D_GPF, tss_selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, tss_selector & 0xfffc, retaddr); } } if (!(e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0B_NOSEG, tss_selector & 0xfffc); + raise_exception_err_ra(env, EXCP0B_NOSEG, tss_selector & 0xfffc, retaddr); } if (type & 8) { @@ -275,7 +284,7 @@ static void switch_tss(CPUX86State *env, int tss_selector, tss_base = get_seg_base(e1, e2); if ((tss_selector & 4) != 0 || tss_limit < tss_limit_max) { - raise_exception_err(env, EXCP0A_TSS, tss_selector & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, retaddr); } old_type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf; if (old_type & 8) { @@ -287,30 +296,33 @@ static void switch_tss(CPUX86State *env, int tss_selector, /* read all the registers from the new TSS */ if (type & 8) { /* 32 bit */ - new_cr3 = cpu_ldl_kernel(env, tss_base + 0x1c); - new_eip = cpu_ldl_kernel(env, tss_base + 0x20); - new_eflags = cpu_ldl_kernel(env, tss_base + 0x24); + new_cr3 = cpu_ldl_kernel_ra(env, tss_base + 0x1c, retaddr); + new_eip = cpu_ldl_kernel_ra(env, tss_base + 0x20, retaddr); + new_eflags = cpu_ldl_kernel_ra(env, tss_base + 0x24, retaddr); for (i = 0; i < 8; i++) { - new_regs[i] = cpu_ldl_kernel(env, tss_base + (0x28 + i * 4)); + new_regs[i] = cpu_ldl_kernel_ra(env, tss_base + (0x28 + i * 4), + retaddr); } for (i = 0; i < 6; i++) { - new_segs[i] = cpu_lduw_kernel(env, tss_base + (0x48 + i * 4)); + new_segs[i] = cpu_lduw_kernel_ra(env, tss_base + (0x48 + i * 4), + retaddr); } - new_ldt = cpu_lduw_kernel(env, tss_base + 0x60); - new_trap = cpu_ldl_kernel(env, tss_base + 0x64); + new_ldt = cpu_lduw_kernel_ra(env, tss_base + 0x60, retaddr); + new_trap = cpu_ldl_kernel_ra(env, tss_base + 0x64, retaddr); } else { /* 16 bit */ new_cr3 = 0; - new_eip = cpu_lduw_kernel(env, tss_base + 0x0e); - new_eflags = cpu_lduw_kernel(env, tss_base + 0x10); + new_eip = cpu_lduw_kernel_ra(env, tss_base + 0x0e, retaddr); + new_eflags = cpu_lduw_kernel_ra(env, tss_base + 0x10, retaddr); for (i = 0; i < 8; i++) { - new_regs[i] = cpu_lduw_kernel(env, tss_base + (0x12 + i * 2)) | - 0xffff0000; + new_regs[i] = cpu_lduw_kernel_ra(env, tss_base + (0x12 + i * 2), + retaddr) | 0xffff0000; } for (i = 0; i < 4; i++) { - new_segs[i] = cpu_lduw_kernel(env, tss_base + (0x22 + i * 4)); + new_segs[i] = cpu_lduw_kernel_ra(env, tss_base + (0x22 + i * 4), + retaddr); } - new_ldt = cpu_lduw_kernel(env, tss_base + 0x2a); + new_ldt = cpu_lduw_kernel_ra(env, tss_base + 0x2a, retaddr); new_segs[R_FS] = 0; new_segs[R_GS] = 0; new_trap = 0; @@ -325,10 +337,10 @@ static void switch_tss(CPUX86State *env, int tss_selector, /* XXX: it can still fail in some cases, so a bigger hack is necessary to valid the TLB after having done the accesses */ - v1 = cpu_ldub_kernel(env, env->tr.base); - v2 = cpu_ldub_kernel(env, env->tr.base + old_tss_limit_max); - cpu_stb_kernel(env, env->tr.base, v1); - cpu_stb_kernel(env, env->tr.base + old_tss_limit_max, v2); + v1 = cpu_ldub_kernel_ra(env, env->tr.base, retaddr); + v2 = cpu_ldub_kernel_ra(env, env->tr.base + old_tss_limit_max, retaddr); + cpu_stb_kernel_ra(env, env->tr.base, v1, retaddr); + cpu_stb_kernel_ra(env, env->tr.base + old_tss_limit_max, v2, retaddr); /* clear busy bit (it is restartable) */ if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_IRET) { @@ -336,9 +348,9 @@ static void switch_tss(CPUX86State *env, int tss_selector, uint32_t e2; ptr = env->gdt.base + (env->tr.selector & ~7); - e2 = cpu_ldl_kernel(env, ptr + 4); + e2 = cpu_ldl_kernel_ra(env, ptr + 4, retaddr); e2 &= ~DESC_TSS_BUSY_MASK; - cpu_stl_kernel(env, ptr + 4, e2); + cpu_stl_kernel_ra(env, ptr + 4, e2, retaddr); } old_eflags = cpu_compute_eflags(env); if (source == SWITCH_TSS_IRET) { @@ -348,35 +360,35 @@ static void switch_tss(CPUX86State *env, int tss_selector, /* save the current state in the old TSS */ if (type & 8) { /* 32 bit */ - cpu_stl_kernel(env, env->tr.base + 0x20, next_eip); - cpu_stl_kernel(env, env->tr.base + 0x24, old_eflags); - cpu_stl_kernel(env, env->tr.base + (0x28 + 0 * 4), env->regs[R_EAX]); - cpu_stl_kernel(env, env->tr.base + (0x28 + 1 * 4), env->regs[R_ECX]); - cpu_stl_kernel(env, env->tr.base + (0x28 + 2 * 4), env->regs[R_EDX]); - cpu_stl_kernel(env, env->tr.base + (0x28 + 3 * 4), env->regs[R_EBX]); - cpu_stl_kernel(env, env->tr.base + (0x28 + 4 * 4), env->regs[R_ESP]); - cpu_stl_kernel(env, env->tr.base + (0x28 + 5 * 4), env->regs[R_EBP]); - cpu_stl_kernel(env, env->tr.base + (0x28 + 6 * 4), env->regs[R_ESI]); - cpu_stl_kernel(env, env->tr.base + (0x28 + 7 * 4), env->regs[R_EDI]); + cpu_stl_kernel_ra(env, env->tr.base + 0x20, next_eip, retaddr); + cpu_stl_kernel_ra(env, env->tr.base + 0x24, old_eflags, retaddr); + cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 0 * 4), env->regs[R_EAX], retaddr); + cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 1 * 4), env->regs[R_ECX], retaddr); + cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 2 * 4), env->regs[R_EDX], retaddr); + cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 3 * 4), env->regs[R_EBX], retaddr); + cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 4 * 4), env->regs[R_ESP], retaddr); + cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 5 * 4), env->regs[R_EBP], retaddr); + cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 6 * 4), env->regs[R_ESI], retaddr); + cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 7 * 4), env->regs[R_EDI], retaddr); for (i = 0; i < 6; i++) { - cpu_stw_kernel(env, env->tr.base + (0x48 + i * 4), - env->segs[i].selector); + cpu_stw_kernel_ra(env, env->tr.base + (0x48 + i * 4), + env->segs[i].selector, retaddr); } } else { /* 16 bit */ - cpu_stw_kernel(env, env->tr.base + 0x0e, next_eip); - cpu_stw_kernel(env, env->tr.base + 0x10, old_eflags); - cpu_stw_kernel(env, env->tr.base + (0x12 + 0 * 2), env->regs[R_EAX]); - cpu_stw_kernel(env, env->tr.base + (0x12 + 1 * 2), env->regs[R_ECX]); - cpu_stw_kernel(env, env->tr.base + (0x12 + 2 * 2), env->regs[R_EDX]); - cpu_stw_kernel(env, env->tr.base + (0x12 + 3 * 2), env->regs[R_EBX]); - cpu_stw_kernel(env, env->tr.base + (0x12 + 4 * 2), env->regs[R_ESP]); - cpu_stw_kernel(env, env->tr.base + (0x12 + 5 * 2), env->regs[R_EBP]); - cpu_stw_kernel(env, env->tr.base + (0x12 + 6 * 2), env->regs[R_ESI]); - cpu_stw_kernel(env, env->tr.base + (0x12 + 7 * 2), env->regs[R_EDI]); + cpu_stw_kernel_ra(env, env->tr.base + 0x0e, next_eip, retaddr); + cpu_stw_kernel_ra(env, env->tr.base + 0x10, old_eflags, retaddr); + cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 0 * 2), env->regs[R_EAX], retaddr); + cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 1 * 2), env->regs[R_ECX], retaddr); + cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 2 * 2), env->regs[R_EDX], retaddr); + cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 3 * 2), env->regs[R_EBX], retaddr); + cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 4 * 2), env->regs[R_ESP], retaddr); + cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 5 * 2), env->regs[R_EBP], retaddr); + cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 6 * 2), env->regs[R_ESI], retaddr); + cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 7 * 2), env->regs[R_EDI], retaddr); for (i = 0; i < 4; i++) { - cpu_stw_kernel(env, env->tr.base + (0x22 + i * 4), - env->segs[i].selector); + cpu_stw_kernel_ra(env, env->tr.base + (0x22 + i * 4), + env->segs[i].selector, retaddr); } } @@ -384,7 +396,7 @@ static void switch_tss(CPUX86State *env, int tss_selector, context */ if (source == SWITCH_TSS_CALL) { - cpu_stw_kernel(env, tss_base, env->tr.selector); + cpu_stw_kernel_ra(env, tss_base, env->tr.selector, retaddr); new_eflags |= NT_MASK; } @@ -394,9 +406,9 @@ static void switch_tss(CPUX86State *env, int tss_selector, uint32_t e2; ptr = env->gdt.base + (tss_selector & ~7); - e2 = cpu_ldl_kernel(env, ptr + 4); + e2 = cpu_ldl_kernel_ra(env, ptr + 4, retaddr); e2 |= DESC_TSS_BUSY_MASK; - cpu_stl_kernel(env, ptr + 4, e2); + cpu_stl_kernel_ra(env, ptr + 4, e2, retaddr); } /* set the new CPU state */ @@ -448,23 +460,23 @@ static void switch_tss(CPUX86State *env, int tss_selector, /* load the LDT */ if (new_ldt & 4) { - raise_exception_err(env, EXCP0A_TSS, new_ldt & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, new_ldt & 0xfffc, retaddr); } if ((new_ldt & 0xfffc) != 0) { dt = &env->gdt; index = new_ldt & ~7; if ((index + 7) > dt->limit) { - raise_exception_err(env, EXCP0A_TSS, new_ldt & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, new_ldt & 0xfffc, retaddr); } ptr = dt->base + index; - e1 = cpu_ldl_kernel(env, ptr); - e2 = cpu_ldl_kernel(env, ptr + 4); + e1 = cpu_ldl_kernel_ra(env, ptr, retaddr); + e2 = cpu_ldl_kernel_ra(env, ptr + 4, retaddr); if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2) { - raise_exception_err(env, EXCP0A_TSS, new_ldt & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, new_ldt & 0xfffc, retaddr); } if (!(e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0A_TSS, new_ldt & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, new_ldt & 0xfffc, retaddr); } load_seg_cache_raw_dt(&env->ldt, e1, e2); } @@ -472,18 +484,18 @@ static void switch_tss(CPUX86State *env, int tss_selector, /* load the segments */ if (!(new_eflags & VM_MASK)) { int cpl = new_segs[R_CS] & 3; - tss_load_seg(env, R_CS, new_segs[R_CS], cpl); - tss_load_seg(env, R_SS, new_segs[R_SS], cpl); - tss_load_seg(env, R_ES, new_segs[R_ES], cpl); - tss_load_seg(env, R_DS, new_segs[R_DS], cpl); - tss_load_seg(env, R_FS, new_segs[R_FS], cpl); - tss_load_seg(env, R_GS, new_segs[R_GS], cpl); + tss_load_seg(env, R_CS, new_segs[R_CS], cpl, retaddr); + tss_load_seg(env, R_SS, new_segs[R_SS], cpl, retaddr); + tss_load_seg(env, R_ES, new_segs[R_ES], cpl, retaddr); + tss_load_seg(env, R_DS, new_segs[R_DS], cpl, retaddr); + tss_load_seg(env, R_FS, new_segs[R_FS], cpl, retaddr); + tss_load_seg(env, R_GS, new_segs[R_GS], cpl, retaddr); } /* check that env->eip is in the CS segment limits */ if (new_eip > env->segs[R_CS].limit) { /* XXX: different exception if CALL? */ - raise_exception_err(env, EXCP0D_GPF, 0); + raise_exception_err_ra(env, EXCP0D_GPF, 0, retaddr); } #ifndef CONFIG_USER_ONLY @@ -500,6 +512,13 @@ static void switch_tss(CPUX86State *env, int tss_selector, #endif } +static void switch_tss(CPUX86State *env, int tss_selector, + uint32_t e1, uint32_t e2, int source, + uint32_t next_eip) +{ + switch_tss_ra(env, tss_selector, e1, e2, source, next_eip, 0); +} + static inline unsigned int get_sp_mask(unsigned int e2) { if (e2 & DESC_B_MASK) { @@ -549,30 +568,35 @@ static int exception_has_error_code(int intno) #define SEG_ADDL(ssp, sp, sp_mask) ((uint32_t)((ssp) + (sp & (sp_mask)))) /* XXX: add a is_user flag to have proper security support */ -#define PUSHW(ssp, sp, sp_mask, val) \ +#define PUSHW_RA(ssp, sp, sp_mask, val, ra) \ { \ sp -= 2; \ - cpu_stw_kernel(env, (ssp) + (sp & (sp_mask)), (val)); \ + cpu_stw_kernel_ra(env, (ssp) + (sp & (sp_mask)), (val), ra); \ } -#define PUSHL(ssp, sp, sp_mask, val) \ +#define PUSHL_RA(ssp, sp, sp_mask, val, ra) \ { \ sp -= 4; \ - cpu_stl_kernel(env, SEG_ADDL(ssp, sp, sp_mask), (uint32_t)(val)); \ + cpu_stl_kernel_ra(env, SEG_ADDL(ssp, sp, sp_mask), (uint32_t)(val), ra); \ } -#define POPW(ssp, sp, sp_mask, val) \ +#define POPW_RA(ssp, sp, sp_mask, val, ra) \ { \ - val = cpu_lduw_kernel(env, (ssp) + (sp & (sp_mask))); \ + val = cpu_lduw_kernel_ra(env, (ssp) + (sp & (sp_mask)), ra); \ sp += 2; \ } -#define POPL(ssp, sp, sp_mask, val) \ +#define POPL_RA(ssp, sp, sp_mask, val, ra) \ { \ - val = (uint32_t)cpu_ldl_kernel(env, SEG_ADDL(ssp, sp, sp_mask)); \ + val = (uint32_t)cpu_ldl_kernel_ra(env, SEG_ADDL(ssp, sp, sp_mask), ra); \ sp += 4; \ } +#define PUSHW(ssp, sp, sp_mask, val) PUSHW_RA(ssp, sp, sp_mask, val, 0) +#define PUSHL(ssp, sp, sp_mask, val) PUSHL_RA(ssp, sp, sp_mask, val, 0) +#define POPW(ssp, sp, sp_mask, val) POPW_RA(ssp, sp, sp_mask, val, 0) +#define POPL(ssp, sp, sp_mask, val) POPL_RA(ssp, sp, sp_mask, val, 0) + /* protected mode interrupt */ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, int error_code, unsigned int next_eip, @@ -673,7 +697,7 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, } if (!(e2 & DESC_C_MASK) && dpl < cpl) { /* to inner privilege */ - get_ss_esp_from_tss(env, &ss, &esp, dpl); + get_ss_esp_from_tss(env, &ss, &esp, dpl, 0); if ((ss & 0xfffc) == 0) { raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); } @@ -791,18 +815,21 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, #ifdef TARGET_X86_64 -#define PUSHQ(sp, val) \ +#define PUSHQ_RA(sp, val, ra) \ { \ sp -= 8; \ - cpu_stq_kernel(env, sp, (val)); \ + cpu_stq_kernel_ra(env, sp, (val), ra); \ } -#define POPQ(sp, val) \ +#define POPQ_RA(sp, val, ra) \ { \ - val = cpu_ldq_kernel(env, sp); \ + val = cpu_ldq_kernel_ra(env, sp, ra); \ sp += 8; \ } +#define PUSHQ(sp, val) PUSHQ_RA(sp, val, 0) +#define POPQ(sp, val) POPQ_RA(sp, val, 0) + static inline target_ulong get_rsp_from_tss(CPUX86State *env, int level) { X86CPU *cpu = x86_env_get_cpu(env); @@ -961,7 +988,7 @@ void helper_syscall(CPUX86State *env, int next_eip_addend) int selector; if (!(env->efer & MSR_EFER_SCE)) { - raise_exception_err(env, EXCP06_ILLOP, 0); + raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC()); } selector = (env->star >> 32) & 0xffff; if (env->hflags & HF_LMA_MASK) { @@ -1016,11 +1043,11 @@ void helper_sysret(CPUX86State *env, int dflag) int cpl, selector; if (!(env->efer & MSR_EFER_SCE)) { - raise_exception_err(env, EXCP06_ILLOP, 0); + raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC()); } cpl = env->hflags & HF_CPL_MASK; if (!(env->cr[0] & CR0_PE_MASK) || cpl != 0) { - raise_exception_err(env, EXCP0D_GPF, 0); + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); } selector = (env->star >> 48) & 0xffff; if (env->hflags & HF_LMA_MASK) { @@ -1369,22 +1396,26 @@ void helper_enter_level(CPUX86State *env, int level, int data32, while (--level) { esp -= 4; ebp -= 4; - cpu_stl_data(env, ssp + (esp & esp_mask), - cpu_ldl_data(env, ssp + (ebp & esp_mask))); + cpu_stl_data_ra(env, ssp + (esp & esp_mask), + cpu_ldl_data_ra(env, ssp + (ebp & esp_mask), + GETPC()), + GETPC()); } esp -= 4; - cpu_stl_data(env, ssp + (esp & esp_mask), t1); + cpu_stl_data_ra(env, ssp + (esp & esp_mask), t1, GETPC()); } else { /* 16 bit */ esp -= 2; while (--level) { esp -= 2; ebp -= 2; - cpu_stw_data(env, ssp + (esp & esp_mask), - cpu_lduw_data(env, ssp + (ebp & esp_mask))); + cpu_stw_data_ra(env, ssp + (esp & esp_mask), + cpu_lduw_data_ra(env, ssp + (ebp & esp_mask), + GETPC()), + GETPC()); } esp -= 2; - cpu_stw_data(env, ssp + (esp & esp_mask), t1); + cpu_stw_data_ra(env, ssp + (esp & esp_mask), t1, GETPC()); } } @@ -1403,20 +1434,22 @@ void helper_enter64_level(CPUX86State *env, int level, int data64, while (--level) { esp -= 8; ebp -= 8; - cpu_stq_data(env, esp, cpu_ldq_data(env, ebp)); + cpu_stq_data_ra(env, esp, cpu_ldq_data_ra(env, ebp, GETPC()), + GETPC()); } esp -= 8; - cpu_stq_data(env, esp, t1); + cpu_stq_data_ra(env, esp, t1, GETPC()); } else { /* 16 bit */ esp -= 2; while (--level) { esp -= 2; ebp -= 2; - cpu_stw_data(env, esp, cpu_lduw_data(env, ebp)); + cpu_stw_data_ra(env, esp, cpu_lduw_data_ra(env, ebp, GETPC()), + GETPC()); } esp -= 2; - cpu_stw_data(env, esp, t1); + cpu_stw_data_ra(env, esp, t1, GETPC()); } } #endif @@ -1435,7 +1468,7 @@ void helper_lldt(CPUX86State *env, int selector) env->ldt.limit = 0; } else { if (selector & 0x4) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } dt = &env->gdt; index = selector & ~7; @@ -1448,22 +1481,22 @@ void helper_lldt(CPUX86State *env, int selector) entry_limit = 7; } if ((index + entry_limit) > dt->limit) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } ptr = dt->base + index; - e1 = cpu_ldl_kernel(env, ptr); - e2 = cpu_ldl_kernel(env, ptr + 4); + e1 = cpu_ldl_kernel_ra(env, ptr, GETPC()); + e2 = cpu_ldl_kernel_ra(env, ptr + 4, GETPC()); if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } if (!(e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0B_NOSEG, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0B_NOSEG, selector & 0xfffc, GETPC()); } #ifdef TARGET_X86_64 if (env->hflags & HF_LMA_MASK) { uint32_t e3; - e3 = cpu_ldl_kernel(env, ptr + 8); + e3 = cpu_ldl_kernel_ra(env, ptr + 8, GETPC()); load_seg_cache_raw_dt(&env->ldt, e1, e2); env->ldt.base |= (target_ulong)e3 << 32; } else @@ -1490,7 +1523,7 @@ void helper_ltr(CPUX86State *env, int selector) env->tr.flags = 0; } else { if (selector & 0x4) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } dt = &env->gdt; index = selector & ~7; @@ -1503,27 +1536,27 @@ void helper_ltr(CPUX86State *env, int selector) entry_limit = 7; } if ((index + entry_limit) > dt->limit) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } ptr = dt->base + index; - e1 = cpu_ldl_kernel(env, ptr); - e2 = cpu_ldl_kernel(env, ptr + 4); + e1 = cpu_ldl_kernel_ra(env, ptr, GETPC()); + e2 = cpu_ldl_kernel_ra(env, ptr + 4, GETPC()); type = (e2 >> DESC_TYPE_SHIFT) & 0xf; if ((e2 & DESC_S_MASK) || (type != 1 && type != 9)) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } if (!(e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0B_NOSEG, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0B_NOSEG, selector & 0xfffc, GETPC()); } #ifdef TARGET_X86_64 if (env->hflags & HF_LMA_MASK) { uint32_t e3, e4; - e3 = cpu_ldl_kernel(env, ptr + 8); - e4 = cpu_ldl_kernel(env, ptr + 12); + e3 = cpu_ldl_kernel_ra(env, ptr + 8, GETPC()); + e4 = cpu_ldl_kernel_ra(env, ptr + 12, GETPC()); if ((e4 >> DESC_TYPE_SHIFT) & 0xf) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } load_seg_cache_raw_dt(&env->tr, e1, e2); env->tr.base |= (target_ulong)e3 << 32; @@ -1533,7 +1566,7 @@ void helper_ltr(CPUX86State *env, int selector) load_seg_cache_raw_dt(&env->tr, e1, e2); } e2 |= DESC_TSS_BUSY_MASK; - cpu_stl_kernel(env, ptr + 4, e2); + cpu_stl_kernel_ra(env, ptr + 4, e2, GETPC()); } env->tr.selector = selector; } @@ -1556,7 +1589,7 @@ void helper_load_seg(CPUX86State *env, int seg_reg, int selector) && (!(env->hflags & HF_CS64_MASK) || cpl == 3) #endif ) { - raise_exception_err(env, EXCP0D_GPF, 0); + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); } cpu_x86_load_seg_cache(env, seg_reg, selector, 0, 0, 0); } else { @@ -1568,51 +1601,51 @@ void helper_load_seg(CPUX86State *env, int seg_reg, int selector) } index = selector & ~7; if ((index + 7) > dt->limit) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } ptr = dt->base + index; - e1 = cpu_ldl_kernel(env, ptr); - e2 = cpu_ldl_kernel(env, ptr + 4); + e1 = cpu_ldl_kernel_ra(env, ptr, GETPC()); + e2 = cpu_ldl_kernel_ra(env, ptr + 4, GETPC()); if (!(e2 & DESC_S_MASK)) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } rpl = selector & 3; dpl = (e2 >> DESC_DPL_SHIFT) & 3; if (seg_reg == R_SS) { /* must be writable segment */ if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK)) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } if (rpl != cpl || dpl != cpl) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } } else { /* must be readable segment */ if ((e2 & (DESC_CS_MASK | DESC_R_MASK)) == DESC_CS_MASK) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } if (!(e2 & DESC_CS_MASK) || !(e2 & DESC_C_MASK)) { /* if not conforming code, test rights */ if (dpl < cpl || dpl < rpl) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } } } if (!(e2 & DESC_P_MASK)) { if (seg_reg == R_SS) { - raise_exception_err(env, EXCP0C_STACK, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0C_STACK, selector & 0xfffc, GETPC()); } else { - raise_exception_err(env, EXCP0B_NOSEG, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0B_NOSEG, selector & 0xfffc, GETPC()); } } /* set the access bit if not already set */ if (!(e2 & DESC_A_MASK)) { e2 |= DESC_A_MASK; - cpu_stl_kernel(env, ptr + 4, e2); + cpu_stl_kernel_ra(env, ptr + 4, e2, GETPC()); } cpu_x86_load_seg_cache(env, seg_reg, selector, @@ -1628,46 +1661,45 @@ void helper_load_seg(CPUX86State *env, int seg_reg, int selector) /* protected mode jump */ void helper_ljmp_protected(CPUX86State *env, int new_cs, target_ulong new_eip, - int next_eip_addend) + target_ulong next_eip) { int gate_cs, type; uint32_t e1, e2, cpl, dpl, rpl, limit; - target_ulong next_eip; if ((new_cs & 0xfffc) == 0) { - raise_exception_err(env, EXCP0D_GPF, 0); + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); } - if (load_segment(env, &e1, &e2, new_cs) != 0) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + if (load_segment_ra(env, &e1, &e2, new_cs, GETPC()) != 0) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } cpl = env->hflags & HF_CPL_MASK; if (e2 & DESC_S_MASK) { if (!(e2 & DESC_CS_MASK)) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } dpl = (e2 >> DESC_DPL_SHIFT) & 3; if (e2 & DESC_C_MASK) { /* conforming code segment */ if (dpl > cpl) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } } else { /* non conforming code segment */ rpl = new_cs & 3; if (rpl > cpl) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } if (dpl != cpl) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } } if (!(e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0B_NOSEG, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, GETPC()); } limit = get_seg_limit(e1, e2); if (new_eip > limit && !(env->hflags & HF_LMA_MASK) && !(e2 & DESC_L_MASK)) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl, get_seg_base(e1, e2), limit, e2); @@ -1683,50 +1715,49 @@ void helper_ljmp_protected(CPUX86State *env, int new_cs, target_ulong new_eip, case 9: /* 386 TSS */ case 5: /* task gate */ if (dpl < cpl || dpl < rpl) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } - next_eip = env->eip + next_eip_addend; - switch_tss(env, new_cs, e1, e2, SWITCH_TSS_JMP, next_eip); + switch_tss_ra(env, new_cs, e1, e2, SWITCH_TSS_JMP, next_eip, GETPC()); break; case 4: /* 286 call gate */ case 12: /* 386 call gate */ if ((dpl < cpl) || (dpl < rpl)) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } if (!(e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0B_NOSEG, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, GETPC()); } gate_cs = e1 >> 16; new_eip = (e1 & 0xffff); if (type == 12) { new_eip |= (e2 & 0xffff0000); } - if (load_segment(env, &e1, &e2, gate_cs) != 0) { - raise_exception_err(env, EXCP0D_GPF, gate_cs & 0xfffc); + if (load_segment_ra(env, &e1, &e2, gate_cs, GETPC()) != 0) { + raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC()); } dpl = (e2 >> DESC_DPL_SHIFT) & 3; /* must be code segment */ if (((e2 & (DESC_S_MASK | DESC_CS_MASK)) != (DESC_S_MASK | DESC_CS_MASK))) { - raise_exception_err(env, EXCP0D_GPF, gate_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC()); } if (((e2 & DESC_C_MASK) && (dpl > cpl)) || (!(e2 & DESC_C_MASK) && (dpl != cpl))) { - raise_exception_err(env, EXCP0D_GPF, gate_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC()); } if (!(e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0D_GPF, gate_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC()); } limit = get_seg_limit(e1, e2); if (new_eip > limit) { - raise_exception_err(env, EXCP0D_GPF, 0); + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); } cpu_x86_load_seg_cache(env, R_CS, (gate_cs & 0xfffc) | cpl, get_seg_base(e1, e2), limit, e2); env->eip = new_eip; break; default: - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); break; } } @@ -1745,11 +1776,11 @@ void helper_lcall_real(CPUX86State *env, int new_cs, target_ulong new_eip1, esp_mask = get_sp_mask(env->segs[R_SS].flags); ssp = env->segs[R_SS].base; if (shift) { - PUSHL(ssp, esp, esp_mask, env->segs[R_CS].selector); - PUSHL(ssp, esp, esp_mask, next_eip); + PUSHL_RA(ssp, esp, esp_mask, env->segs[R_CS].selector, GETPC()); + PUSHL_RA(ssp, esp, esp_mask, next_eip, GETPC()); } else { - PUSHW(ssp, esp, esp_mask, env->segs[R_CS].selector); - PUSHW(ssp, esp, esp_mask, next_eip); + PUSHW_RA(ssp, esp, esp_mask, env->segs[R_CS].selector, GETPC()); + PUSHW_RA(ssp, esp, esp_mask, next_eip, GETPC()); } SET_ESP(esp, esp_mask); @@ -1760,47 +1791,46 @@ void helper_lcall_real(CPUX86State *env, int new_cs, target_ulong new_eip1, /* protected mode call */ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, - int shift, int next_eip_addend) + int shift, target_ulong next_eip) { int new_stack, i; uint32_t e1, e2, cpl, dpl, rpl, selector, offset, param_count; uint32_t ss = 0, ss_e1 = 0, ss_e2 = 0, sp, type, ss_dpl, sp_mask; uint32_t val, limit, old_sp_mask; - target_ulong ssp, old_ssp, next_eip; + target_ulong ssp, old_ssp; - next_eip = env->eip + next_eip_addend; LOG_PCALL("lcall %04x:%08x s=%d\n", new_cs, (uint32_t)new_eip, shift); LOG_PCALL_STATE(CPU(x86_env_get_cpu(env))); if ((new_cs & 0xfffc) == 0) { - raise_exception_err(env, EXCP0D_GPF, 0); + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); } - if (load_segment(env, &e1, &e2, new_cs) != 0) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + if (load_segment_ra(env, &e1, &e2, new_cs, GETPC()) != 0) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } cpl = env->hflags & HF_CPL_MASK; LOG_PCALL("desc=%08x:%08x\n", e1, e2); if (e2 & DESC_S_MASK) { if (!(e2 & DESC_CS_MASK)) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } dpl = (e2 >> DESC_DPL_SHIFT) & 3; if (e2 & DESC_C_MASK) { /* conforming code segment */ if (dpl > cpl) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } } else { /* non conforming code segment */ rpl = new_cs & 3; if (rpl > cpl) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } if (dpl != cpl) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } } if (!(e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0B_NOSEG, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, GETPC()); } #ifdef TARGET_X86_64 @@ -1810,8 +1840,8 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, /* 64 bit case */ rsp = env->regs[R_ESP]; - PUSHQ(rsp, env->segs[R_CS].selector); - PUSHQ(rsp, next_eip); + PUSHQ_RA(rsp, env->segs[R_CS].selector, GETPC()); + PUSHQ_RA(rsp, next_eip, GETPC()); /* from this point, not restartable */ env->regs[R_ESP] = rsp; cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl, @@ -1825,16 +1855,16 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, sp_mask = get_sp_mask(env->segs[R_SS].flags); ssp = env->segs[R_SS].base; if (shift) { - PUSHL(ssp, sp, sp_mask, env->segs[R_CS].selector); - PUSHL(ssp, sp, sp_mask, next_eip); + PUSHL_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC()); + PUSHL_RA(ssp, sp, sp_mask, next_eip, GETPC()); } else { - PUSHW(ssp, sp, sp_mask, env->segs[R_CS].selector); - PUSHW(ssp, sp, sp_mask, next_eip); + PUSHW_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC()); + PUSHW_RA(ssp, sp, sp_mask, next_eip, GETPC()); } limit = get_seg_limit(e1, e2); if (new_eip > limit) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } /* from this point, not restartable */ SET_ESP(sp, sp_mask); @@ -1852,73 +1882,73 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, case 9: /* available 386 TSS */ case 5: /* task gate */ if (dpl < cpl || dpl < rpl) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } - switch_tss(env, new_cs, e1, e2, SWITCH_TSS_CALL, next_eip); + switch_tss_ra(env, new_cs, e1, e2, SWITCH_TSS_CALL, next_eip, GETPC()); return; case 4: /* 286 call gate */ case 12: /* 386 call gate */ break; default: - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); break; } shift = type >> 3; if (dpl < cpl || dpl < rpl) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } /* check valid bit */ if (!(e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0B_NOSEG, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, GETPC()); } selector = e1 >> 16; offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff); param_count = e2 & 0x1f; if ((selector & 0xfffc) == 0) { - raise_exception_err(env, EXCP0D_GPF, 0); + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); } - if (load_segment(env, &e1, &e2, selector) != 0) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + if (load_segment_ra(env, &e1, &e2, selector, GETPC()) != 0) { + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK))) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } dpl = (e2 >> DESC_DPL_SHIFT) & 3; if (dpl > cpl) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } if (!(e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0B_NOSEG, selector & 0xfffc); + raise_exception_err_ra(env, EXCP0B_NOSEG, selector & 0xfffc, GETPC()); } if (!(e2 & DESC_C_MASK) && dpl < cpl) { /* to inner privilege */ - get_ss_esp_from_tss(env, &ss, &sp, dpl); + get_ss_esp_from_tss(env, &ss, &sp, dpl, GETPC()); LOG_PCALL("new ss:esp=%04x:%08x param_count=%d env->regs[R_ESP]=" TARGET_FMT_lx "\n", ss, sp, param_count, env->regs[R_ESP]); if ((ss & 0xfffc) == 0) { - raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); } if ((ss & 3) != dpl) { - raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); } - if (load_segment(env, &ss_e1, &ss_e2, ss) != 0) { - raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); + if (load_segment_ra(env, &ss_e1, &ss_e2, ss, GETPC()) != 0) { + raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); } ss_dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3; if (ss_dpl != dpl) { - raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); } if (!(ss_e2 & DESC_S_MASK) || (ss_e2 & DESC_CS_MASK) || !(ss_e2 & DESC_W_MASK)) { - raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); } if (!(ss_e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); } /* push_size = ((param_count * 2) + 8) << shift; */ @@ -1929,22 +1959,22 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, sp_mask = get_sp_mask(ss_e2); ssp = get_seg_base(ss_e1, ss_e2); if (shift) { - PUSHL(ssp, sp, sp_mask, env->segs[R_SS].selector); - PUSHL(ssp, sp, sp_mask, env->regs[R_ESP]); + PUSHL_RA(ssp, sp, sp_mask, env->segs[R_SS].selector, GETPC()); + PUSHL_RA(ssp, sp, sp_mask, env->regs[R_ESP], GETPC()); for (i = param_count - 1; i >= 0; i--) { - val = cpu_ldl_kernel(env, old_ssp + - ((env->regs[R_ESP] + i * 4) & - old_sp_mask)); - PUSHL(ssp, sp, sp_mask, val); + val = cpu_ldl_kernel_ra(env, old_ssp + + ((env->regs[R_ESP] + i * 4) & + old_sp_mask), GETPC()); + PUSHL_RA(ssp, sp, sp_mask, val, GETPC()); } } else { - PUSHW(ssp, sp, sp_mask, env->segs[R_SS].selector); - PUSHW(ssp, sp, sp_mask, env->regs[R_ESP]); + PUSHW_RA(ssp, sp, sp_mask, env->segs[R_SS].selector, GETPC()); + PUSHW_RA(ssp, sp, sp_mask, env->regs[R_ESP], GETPC()); for (i = param_count - 1; i >= 0; i--) { - val = cpu_lduw_kernel(env, old_ssp + - ((env->regs[R_ESP] + i * 2) & - old_sp_mask)); - PUSHW(ssp, sp, sp_mask, val); + val = cpu_lduw_kernel_ra(env, old_ssp + + ((env->regs[R_ESP] + i * 2) & + old_sp_mask), GETPC()); + PUSHW_RA(ssp, sp, sp_mask, val, GETPC()); } } new_stack = 1; @@ -1958,11 +1988,11 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, } if (shift) { - PUSHL(ssp, sp, sp_mask, env->segs[R_CS].selector); - PUSHL(ssp, sp, sp_mask, next_eip); + PUSHL_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC()); + PUSHL_RA(ssp, sp, sp_mask, next_eip, GETPC()); } else { - PUSHW(ssp, sp, sp_mask, env->segs[R_CS].selector); - PUSHW(ssp, sp, sp_mask, next_eip); + PUSHW_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC()); + PUSHW_RA(ssp, sp, sp_mask, next_eip, GETPC()); } /* from this point, not restartable */ @@ -1997,15 +2027,15 @@ void helper_iret_real(CPUX86State *env, int shift) ssp = env->segs[R_SS].base; if (shift == 1) { /* 32 bits */ - POPL(ssp, sp, sp_mask, new_eip); - POPL(ssp, sp, sp_mask, new_cs); + POPL_RA(ssp, sp, sp_mask, new_eip, GETPC()); + POPL_RA(ssp, sp, sp_mask, new_cs, GETPC()); new_cs &= 0xffff; - POPL(ssp, sp, sp_mask, new_eflags); + POPL_RA(ssp, sp, sp_mask, new_eflags, GETPC()); } else { /* 16 bits */ - POPW(ssp, sp, sp_mask, new_eip); - POPW(ssp, sp, sp_mask, new_cs); - POPW(ssp, sp, sp_mask, new_eflags); + POPW_RA(ssp, sp, sp_mask, new_eip, GETPC()); + POPW_RA(ssp, sp, sp_mask, new_cs, GETPC()); + POPW_RA(ssp, sp, sp_mask, new_eflags, GETPC()); } env->regs[R_ESP] = (env->regs[R_ESP] & ~sp_mask) | (sp & sp_mask); env->segs[R_CS].selector = new_cs; @@ -2050,7 +2080,8 @@ static inline void validate_seg(CPUX86State *env, int seg_reg, int cpl) /* protected mode iret */ static inline void helper_ret_protected(CPUX86State *env, int shift, - int is_iret, int addend) + int is_iret, int addend, + uintptr_t retaddr) { uint32_t new_cs, new_eflags, new_ss; uint32_t new_es, new_ds, new_fs, new_gs; @@ -2071,32 +2102,32 @@ static inline void helper_ret_protected(CPUX86State *env, int shift, new_eflags = 0; /* avoid warning */ #ifdef TARGET_X86_64 if (shift == 2) { - POPQ(sp, new_eip); - POPQ(sp, new_cs); + POPQ_RA(sp, new_eip, retaddr); + POPQ_RA(sp, new_cs, retaddr); new_cs &= 0xffff; if (is_iret) { - POPQ(sp, new_eflags); + POPQ_RA(sp, new_eflags, retaddr); } } else #endif { if (shift == 1) { /* 32 bits */ - POPL(ssp, sp, sp_mask, new_eip); - POPL(ssp, sp, sp_mask, new_cs); + POPL_RA(ssp, sp, sp_mask, new_eip, retaddr); + POPL_RA(ssp, sp, sp_mask, new_cs, retaddr); new_cs &= 0xffff; if (is_iret) { - POPL(ssp, sp, sp_mask, new_eflags); + POPL_RA(ssp, sp, sp_mask, new_eflags, retaddr); if (new_eflags & VM_MASK) { goto return_to_vm86; } } } else { /* 16 bits */ - POPW(ssp, sp, sp_mask, new_eip); - POPW(ssp, sp, sp_mask, new_cs); + POPW_RA(ssp, sp, sp_mask, new_eip, retaddr); + POPW_RA(ssp, sp, sp_mask, new_cs, retaddr); if (is_iret) { - POPW(ssp, sp, sp_mask, new_eflags); + POPW_RA(ssp, sp, sp_mask, new_eflags, retaddr); } } } @@ -2104,32 +2135,32 @@ static inline void helper_ret_protected(CPUX86State *env, int shift, new_cs, new_eip, shift, addend); LOG_PCALL_STATE(CPU(x86_env_get_cpu(env))); if ((new_cs & 0xfffc) == 0) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr); } - if (load_segment(env, &e1, &e2, new_cs) != 0) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + if (load_segment_ra(env, &e1, &e2, new_cs, retaddr) != 0) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr); } if (!(e2 & DESC_S_MASK) || !(e2 & DESC_CS_MASK)) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr); } cpl = env->hflags & HF_CPL_MASK; rpl = new_cs & 3; if (rpl < cpl) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr); } dpl = (e2 >> DESC_DPL_SHIFT) & 3; if (e2 & DESC_C_MASK) { if (dpl > rpl) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr); } } else { if (dpl != rpl) { - raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr); } } if (!(e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0B_NOSEG, new_cs & 0xfffc); + raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, retaddr); } sp += addend; @@ -2144,21 +2175,21 @@ static inline void helper_ret_protected(CPUX86State *env, int shift, /* return to different privilege level */ #ifdef TARGET_X86_64 if (shift == 2) { - POPQ(sp, new_esp); - POPQ(sp, new_ss); + POPQ_RA(sp, new_esp, retaddr); + POPQ_RA(sp, new_ss, retaddr); new_ss &= 0xffff; } else #endif { if (shift == 1) { /* 32 bits */ - POPL(ssp, sp, sp_mask, new_esp); - POPL(ssp, sp, sp_mask, new_ss); + POPL_RA(ssp, sp, sp_mask, new_esp, retaddr); + POPL_RA(ssp, sp, sp_mask, new_ss, retaddr); new_ss &= 0xffff; } else { /* 16 bits */ - POPW(ssp, sp, sp_mask, new_esp); - POPW(ssp, sp, sp_mask, new_ss); + POPW_RA(ssp, sp, sp_mask, new_esp, retaddr); + POPW_RA(ssp, sp, sp_mask, new_ss, retaddr); } } LOG_PCALL("new ss:esp=%04x:" TARGET_FMT_lx "\n", @@ -2177,26 +2208,26 @@ static inline void helper_ret_protected(CPUX86State *env, int shift, } else #endif { - raise_exception_err(env, EXCP0D_GPF, 0); + raise_exception_err_ra(env, EXCP0D_GPF, 0, retaddr); } } else { if ((new_ss & 3) != rpl) { - raise_exception_err(env, EXCP0D_GPF, new_ss & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_ss & 0xfffc, retaddr); } - if (load_segment(env, &ss_e1, &ss_e2, new_ss) != 0) { - raise_exception_err(env, EXCP0D_GPF, new_ss & 0xfffc); + if (load_segment_ra(env, &ss_e1, &ss_e2, new_ss, retaddr) != 0) { + raise_exception_err_ra(env, EXCP0D_GPF, new_ss & 0xfffc, retaddr); } if (!(ss_e2 & DESC_S_MASK) || (ss_e2 & DESC_CS_MASK) || !(ss_e2 & DESC_W_MASK)) { - raise_exception_err(env, EXCP0D_GPF, new_ss & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_ss & 0xfffc, retaddr); } dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3; if (dpl != rpl) { - raise_exception_err(env, EXCP0D_GPF, new_ss & 0xfffc); + raise_exception_err_ra(env, EXCP0D_GPF, new_ss & 0xfffc, retaddr); } if (!(ss_e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0B_NOSEG, new_ss & 0xfffc); + raise_exception_err_ra(env, EXCP0B_NOSEG, new_ss & 0xfffc, retaddr); } cpu_x86_load_seg_cache(env, R_SS, new_ss, get_seg_base(ss_e1, ss_e2), @@ -2246,12 +2277,12 @@ static inline void helper_ret_protected(CPUX86State *env, int shift, return; return_to_vm86: - POPL(ssp, sp, sp_mask, new_esp); - POPL(ssp, sp, sp_mask, new_ss); - POPL(ssp, sp, sp_mask, new_es); - POPL(ssp, sp, sp_mask, new_ds); - POPL(ssp, sp, sp_mask, new_fs); - POPL(ssp, sp, sp_mask, new_gs); + POPL_RA(ssp, sp, sp_mask, new_esp, retaddr); + POPL_RA(ssp, sp, sp_mask, new_ss, retaddr); + POPL_RA(ssp, sp, sp_mask, new_es, retaddr); + POPL_RA(ssp, sp, sp_mask, new_ds, retaddr); + POPL_RA(ssp, sp, sp_mask, new_fs, retaddr); + POPL_RA(ssp, sp, sp_mask, new_gs, retaddr); /* modify processor state */ cpu_load_eflags(env, new_eflags, TF_MASK | AC_MASK | ID_MASK | @@ -2277,37 +2308,37 @@ void helper_iret_protected(CPUX86State *env, int shift, int next_eip) if (env->eflags & NT_MASK) { #ifdef TARGET_X86_64 if (env->hflags & HF_LMA_MASK) { - raise_exception_err(env, EXCP0D_GPF, 0); + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); } #endif - tss_selector = cpu_lduw_kernel(env, env->tr.base + 0); + tss_selector = cpu_lduw_kernel_ra(env, env->tr.base + 0, GETPC()); if (tss_selector & 4) { - raise_exception_err(env, EXCP0A_TSS, tss_selector & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, GETPC()); } - if (load_segment(env, &e1, &e2, tss_selector) != 0) { - raise_exception_err(env, EXCP0A_TSS, tss_selector & 0xfffc); + if (load_segment_ra(env, &e1, &e2, tss_selector, GETPC()) != 0) { + raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, GETPC()); } type = (e2 >> DESC_TYPE_SHIFT) & 0x17; /* NOTE: we check both segment and busy TSS */ if (type != 3) { - raise_exception_err(env, EXCP0A_TSS, tss_selector & 0xfffc); + raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, GETPC()); } - switch_tss(env, tss_selector, e1, e2, SWITCH_TSS_IRET, next_eip); + switch_tss_ra(env, tss_selector, e1, e2, SWITCH_TSS_IRET, next_eip, GETPC()); } else { - helper_ret_protected(env, shift, 1, 0); + helper_ret_protected(env, shift, 1, 0, GETPC()); } env->hflags2 &= ~HF2_NMI_MASK; } void helper_lret_protected(CPUX86State *env, int shift, int addend) { - helper_ret_protected(env, shift, 0, addend); + helper_ret_protected(env, shift, 0, addend, GETPC()); } void helper_sysenter(CPUX86State *env) { if (env->sysenter_cs == 0) { - raise_exception_err(env, EXCP0D_GPF, 0); + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); } env->eflags &= ~(VM_MASK | IF_MASK | RF_MASK); @@ -2343,7 +2374,7 @@ void helper_sysexit(CPUX86State *env, int dflag) cpl = env->hflags & HF_CPL_MASK; if (env->sysenter_cs == 0 || cpl != 0) { - raise_exception_err(env, EXCP0D_GPF, 0); + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); } #ifdef TARGET_X86_64 if (dflag == 2) { @@ -2387,7 +2418,7 @@ target_ulong helper_lsl(CPUX86State *env, target_ulong selector1) if ((selector & 0xfffc) == 0) { goto fail; } - if (load_segment(env, &e1, &e2, selector) != 0) { + if (load_segment_ra(env, &e1, &e2, selector, GETPC()) != 0) { goto fail; } rpl = selector & 3; @@ -2434,7 +2465,7 @@ target_ulong helper_lar(CPUX86State *env, target_ulong selector1) if ((selector & 0xfffc) == 0) { goto fail; } - if (load_segment(env, &e1, &e2, selector) != 0) { + if (load_segment_ra(env, &e1, &e2, selector, GETPC()) != 0) { goto fail; } rpl = selector & 3; @@ -2483,7 +2514,7 @@ void helper_verr(CPUX86State *env, target_ulong selector1) if ((selector & 0xfffc) == 0) { goto fail; } - if (load_segment(env, &e1, &e2, selector) != 0) { + if (load_segment_ra(env, &e1, &e2, selector, GETPC()) != 0) { goto fail; } if (!(e2 & DESC_S_MASK)) { @@ -2521,7 +2552,7 @@ void helper_verw(CPUX86State *env, target_ulong selector1) if ((selector & 0xfffc) == 0) { goto fail; } - if (load_segment(env, &e1, &e2, selector) != 0) { + if (load_segment_ra(env, &e1, &e2, selector, GETPC()) != 0) { goto fail; } if (!(e2 & DESC_S_MASK)) { @@ -2562,7 +2593,8 @@ void cpu_x86_load_seg(CPUX86State *env, int seg_reg, int selector) #endif /* check if Port I/O is allowed in TSS */ -static inline void check_io(CPUX86State *env, int addr, int size) +static inline void check_io(CPUX86State *env, int addr, int size, + uintptr_t retaddr) { int io_offset, val, mask; @@ -2572,33 +2604,33 @@ static inline void check_io(CPUX86State *env, int addr, int size) env->tr.limit < 103) { goto fail; } - io_offset = cpu_lduw_kernel(env, env->tr.base + 0x66); + io_offset = cpu_lduw_kernel_ra(env, env->tr.base + 0x66, retaddr); io_offset += (addr >> 3); /* Note: the check needs two bytes */ if ((io_offset + 1) > env->tr.limit) { goto fail; } - val = cpu_lduw_kernel(env, env->tr.base + io_offset); + val = cpu_lduw_kernel_ra(env, env->tr.base + io_offset, retaddr); val >>= (addr & 7); mask = (1 << size) - 1; /* all bits must be zero to allow the I/O */ if ((val & mask) != 0) { fail: - raise_exception_err(env, EXCP0D_GPF, 0); + raise_exception_err_ra(env, EXCP0D_GPF, 0, retaddr); } } void helper_check_iob(CPUX86State *env, uint32_t t0) { - check_io(env, t0, 1); + check_io(env, t0, 1, GETPC()); } void helper_check_iow(CPUX86State *env, uint32_t t0) { - check_io(env, t0, 2); + check_io(env, t0, 2, GETPC()); } void helper_check_iol(CPUX86State *env, uint32_t t0) { - check_io(env, t0, 4); + check_io(env, t0, 4, GETPC()); } diff --git a/target-i386/translate.c b/target-i386/translate.c index 3ce9c654d7..8b35de1a1a 100644 --- a/target-i386/translate.c +++ b/target-i386/translate.c @@ -663,14 +663,9 @@ static void gen_helper_out_func(TCGMemOp ot, TCGv_i32 v, TCGv_i32 n) static void gen_check_io(DisasContext *s, TCGMemOp ot, target_ulong cur_eip, uint32_t svm_flags) { - int state_saved; target_ulong next_eip; - state_saved = 0; if (s->pe && (s->cpl > s->iopl || s->vm86)) { - gen_update_cc_op(s); - gen_jmp_im(cur_eip); - state_saved = 1; tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); switch (ot) { case MO_8: @@ -687,10 +682,8 @@ static void gen_check_io(DisasContext *s, TCGMemOp ot, target_ulong cur_eip, } } if(s->flags & HF_SVMI_MASK) { - if (!state_saved) { - gen_update_cc_op(s); - gen_jmp_im(cur_eip); - } + gen_update_cc_op(s); + gen_jmp_im(cur_eip); svm_flags |= (1 << (4 + ot)); next_eip = s->pc - s->cs_base; tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); @@ -2297,12 +2290,9 @@ static inline void gen_op_movl_seg_T0_vm(int seg_reg) /* move T0 to seg_reg and compute if the CPU state may change. Never call this function with seg_reg == R_CS */ -static void gen_movl_seg_T0(DisasContext *s, int seg_reg, target_ulong cur_eip) +static void gen_movl_seg_T0(DisasContext *s, int seg_reg) { if (s->pe && !s->vm86) { - /* XXX: optimize by finding processor state dynamically */ - gen_update_cc_op(s); - gen_jmp_im(cur_eip); tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); gen_helper_load_seg(cpu_env, tcg_const_i32(seg_reg), cpu_tmp2_i32); /* abort translation because the addseg value may change or @@ -4841,21 +4831,17 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, case 6: /* div */ switch(ot) { case MO_8: - gen_jmp_im(pc_start - s->cs_base); gen_helper_divb_AL(cpu_env, cpu_T[0]); break; case MO_16: - gen_jmp_im(pc_start - s->cs_base); gen_helper_divw_AX(cpu_env, cpu_T[0]); break; default: case MO_32: - gen_jmp_im(pc_start - s->cs_base); gen_helper_divl_EAX(cpu_env, cpu_T[0]); break; #ifdef TARGET_X86_64 case MO_64: - gen_jmp_im(pc_start - s->cs_base); gen_helper_divq_EAX(cpu_env, cpu_T[0]); break; #endif @@ -4864,21 +4850,17 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, case 7: /* idiv */ switch(ot) { case MO_8: - gen_jmp_im(pc_start - s->cs_base); gen_helper_idivb_AL(cpu_env, cpu_T[0]); break; case MO_16: - gen_jmp_im(pc_start - s->cs_base); gen_helper_idivw_AX(cpu_env, cpu_T[0]); break; default: case MO_32: - gen_jmp_im(pc_start - s->cs_base); gen_helper_idivl_EAX(cpu_env, cpu_T[0]); break; #ifdef TARGET_X86_64 case MO_64: - gen_jmp_im(pc_start - s->cs_base); gen_helper_idivq_EAX(cpu_env, cpu_T[0]); break; #endif @@ -4951,12 +4933,10 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_op_ld_v(s, MO_16, cpu_T[0], cpu_A0); do_lcall: if (s->pe && !s->vm86) { - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); gen_helper_lcall_protected(cpu_env, cpu_tmp2_i32, cpu_T[1], tcg_const_i32(dflag - 1), - tcg_const_i32(s->pc - pc_start)); + tcg_const_tl(s->pc - s->cs_base)); } else { tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); gen_helper_lcall_real(cpu_env, cpu_tmp2_i32, cpu_T[1], @@ -4978,11 +4958,9 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_op_ld_v(s, MO_16, cpu_T[0], cpu_A0); do_ljmp: if (s->pe && !s->vm86) { - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); gen_helper_ljmp_protected(cpu_env, cpu_tmp2_i32, cpu_T[1], - tcg_const_i32(s->pc - pc_start)); + tcg_const_tl(s->pc - s->cs_base)); } else { gen_op_movl_seg_T0_vm(R_CS); gen_op_jmp_v(cpu_T[1]); @@ -5211,8 +5189,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (dflag == MO_64) { if (!(s->cpuid_ext_features & CPUID_EXT_CX16)) goto illegal_op; - gen_jmp_im(pc_start - s->cs_base); - gen_update_cc_op(s); gen_lea_modrm(env, s, modrm); gen_helper_cmpxchg16b(cpu_env, cpu_A0); } else @@ -5220,8 +5196,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, { if (!(s->cpuid_features & CPUID_CX8)) goto illegal_op; - gen_jmp_im(pc_start - s->cs_base); - gen_update_cc_op(s); gen_lea_modrm(env, s, modrm); gen_helper_cmpxchg8b(cpu_env, cpu_A0); } @@ -5323,7 +5297,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, goto illegal_op; reg = b >> 3; ot = gen_pop_T0(s); - gen_movl_seg_T0(s, reg, pc_start - s->cs_base); + gen_movl_seg_T0(s, reg); gen_pop_update(s, ot); if (reg == R_SS) { /* if reg == SS, inhibit interrupts/trace. */ @@ -5341,7 +5315,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, case 0x1a1: /* pop fs */ case 0x1a9: /* pop gs */ ot = gen_pop_T0(s); - gen_movl_seg_T0(s, (b >> 3) & 7, pc_start - s->cs_base); + gen_movl_seg_T0(s, (b >> 3) & 7); gen_pop_update(s, ot); if (s->is_jmp) { gen_jmp_im(s->pc - s->cs_base); @@ -5392,7 +5366,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (reg >= 6 || reg == R_CS) goto illegal_op; gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); - gen_movl_seg_T0(s, reg, pc_start - s->cs_base); + gen_movl_seg_T0(s, reg); if (reg == R_SS) { /* if reg == SS, inhibit interrupts/trace */ /* If several instructions disable interrupts, only the @@ -5604,7 +5578,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_add_A0_im(s, 1 << ot); /* load the segment first to handle exceptions properly */ gen_op_ld_v(s, MO_16, cpu_T[0], cpu_A0); - gen_movl_seg_T0(s, op, pc_start - s->cs_base); + gen_movl_seg_T0(s, op); /* then put the data */ gen_op_mov_reg_v(ot, reg, cpu_T[1]); if (s->is_jmp) { @@ -5836,8 +5810,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } break; case 0x0c: /* fldenv mem */ - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); gen_helper_fldenv(cpu_env, cpu_A0, tcg_const_i32(dflag - 1)); break; case 0x0d: /* fldcw mem */ @@ -5846,8 +5818,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_helper_fldcw(cpu_env, cpu_tmp2_i32); break; case 0x0e: /* fnstenv mem */ - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); gen_helper_fstenv(cpu_env, cpu_A0, tcg_const_i32(dflag - 1)); break; case 0x0f: /* fnstcw mem */ @@ -5856,24 +5826,16 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, s->mem_index, MO_LEUW); break; case 0x1d: /* fldt mem */ - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); gen_helper_fldt_ST0(cpu_env, cpu_A0); break; case 0x1f: /* fstpt mem */ - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); gen_helper_fstt_ST0(cpu_env, cpu_A0); gen_helper_fpop(cpu_env); break; case 0x2c: /* frstor mem */ - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); gen_helper_frstor(cpu_env, cpu_A0, tcg_const_i32(dflag - 1)); break; case 0x2e: /* fnsave mem */ - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); gen_helper_fsave(cpu_env, cpu_A0, tcg_const_i32(dflag - 1)); break; case 0x2f: /* fnstsw mem */ @@ -5882,13 +5844,9 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, s->mem_index, MO_LEUW); break; case 0x3c: /* fbld */ - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); gen_helper_fbld_ST0(cpu_env, cpu_A0); break; case 0x3e: /* fbstp */ - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); gen_helper_fbst_ST0(cpu_env, cpu_A0); gen_helper_fpop(cpu_env); break; @@ -5923,8 +5881,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, switch(rm) { case 0: /* fnop */ /* check exceptions (FreeBSD FPU probe) */ - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); gen_helper_fwait(cpu_env); break; default: @@ -6440,8 +6396,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, set_cc_op(s, CC_OP_EFLAGS); } } else { - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); gen_helper_iret_protected(cpu_env, tcg_const_i32(dflag - 1), tcg_const_i32(s->pc - s->cs_base)); set_cc_op(s, CC_OP_EFLAGS); @@ -6894,8 +6848,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, (HF_MP_MASK | HF_TS_MASK)) { gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); } else { - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); gen_helper_fwait(cpu_env); } break; @@ -6979,7 +6931,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, goto illegal_op; gen_op_mov_v_reg(ot, cpu_T[0], reg); gen_lea_modrm(env, s, modrm); - gen_jmp_im(pc_start - s->cs_base); tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); if (ot == MO_16) { gen_helper_boundw(cpu_env, cpu_A0, cpu_tmp2_i32); @@ -7093,8 +7044,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (!s->pe) { gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); } else { - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); gen_helper_sysenter(cpu_env); gen_eob(s); } @@ -7106,8 +7055,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (!s->pe) { gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); } else { - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); gen_helper_sysexit(cpu_env, tcg_const_i32(dflag - 1)); gen_eob(s); } @@ -7124,8 +7071,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (!s->pe) { gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); } else { - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); gen_helper_sysret(cpu_env, tcg_const_i32(dflag - 1)); /* condition codes are modified only in long mode */ if (s->lma) { @@ -7171,7 +7116,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } else { gen_svm_check_intercept(s, pc_start, SVM_EXIT_LDTR_WRITE); gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); - gen_jmp_im(pc_start - s->cs_base); tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); gen_helper_lldt(cpu_env, cpu_tmp2_i32); } @@ -7192,7 +7136,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } else { gen_svm_check_intercept(s, pc_start, SVM_EXIT_TR_WRITE); gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); - gen_jmp_im(pc_start - s->cs_base); tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); gen_helper_ltr(cpu_env, cpu_tmp2_i32); } @@ -7725,8 +7668,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, break; } gen_lea_modrm(env, s, modrm); - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); gen_helper_fxsave(cpu_env, cpu_A0, tcg_const_i32(dflag == MO_64)); break; case 1: /* fxrstor */ @@ -7738,8 +7679,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, break; } gen_lea_modrm(env, s, modrm); - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); gen_helper_fxrstor(cpu_env, cpu_A0, tcg_const_i32(dflag == MO_64)); break; case 2: /* ldmxcsr */ diff --git a/target-tilegx/Makefile.objs b/target-tilegx/Makefile.objs new file mode 100644 index 0000000000..0db778f407 --- /dev/null +++ b/target-tilegx/Makefile.objs @@ -0,0 +1 @@ +obj-y += cpu.o translate.o helper.o simd_helper.o diff --git a/target-tilegx/cpu.c b/target-tilegx/cpu.c new file mode 100644 index 0000000000..78b73e45c4 --- /dev/null +++ b/target-tilegx/cpu.c @@ -0,0 +1,173 @@ +/* + * QEMU TILE-Gx CPU + * + * Copyright (c) 2015 Chen Gang + * + * 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/lgpl-2.1.html> + */ + +#include "cpu.h" +#include "qemu-common.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" + +static void tilegx_cpu_dump_state(CPUState *cs, FILE *f, + fprintf_function cpu_fprintf, int flags) +{ + static const char * const reg_names[TILEGX_R_COUNT] = { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", + "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", + "r32", "r33", "r34", "r35", "r36", "r37", "r38", "r39", + "r40", "r41", "r42", "r43", "r44", "r45", "r46", "r47", + "r48", "r49", "r50", "r51", "bp", "tp", "sp", "lr" + }; + + TileGXCPU *cpu = TILEGX_CPU(cs); + CPUTLGState *env = &cpu->env; + int i; + + for (i = 0; i < TILEGX_R_COUNT; i++) { + cpu_fprintf(f, "%-4s" TARGET_FMT_lx "%s", + reg_names[i], env->regs[i], + (i % 4) == 3 ? "\n" : " "); + } + cpu_fprintf(f, "PC " TARGET_FMT_lx " CEX " TARGET_FMT_lx "\n\n", + env->pc, env->spregs[TILEGX_SPR_CMPEXCH]); +} + +TileGXCPU *cpu_tilegx_init(const char *cpu_model) +{ + TileGXCPU *cpu; + + cpu = TILEGX_CPU(object_new(TYPE_TILEGX_CPU)); + + object_property_set_bool(OBJECT(cpu), true, "realized", NULL); + + return cpu; +} + +static void tilegx_cpu_set_pc(CPUState *cs, vaddr value) +{ + TileGXCPU *cpu = TILEGX_CPU(cs); + + cpu->env.pc = value; +} + +static bool tilegx_cpu_has_work(CPUState *cs) +{ + return true; +} + +static void tilegx_cpu_reset(CPUState *s) +{ + TileGXCPU *cpu = TILEGX_CPU(s); + TileGXCPUClass *tcc = TILEGX_CPU_GET_CLASS(cpu); + CPUTLGState *env = &cpu->env; + + tcc->parent_reset(s); + + memset(env, 0, sizeof(CPUTLGState)); + tlb_flush(s, 1); +} + +static void tilegx_cpu_realizefn(DeviceState *dev, Error **errp) +{ + CPUState *cs = CPU(dev); + TileGXCPUClass *tcc = TILEGX_CPU_GET_CLASS(dev); + + cpu_reset(cs); + qemu_init_vcpu(cs); + + tcc->parent_realize(dev, errp); +} + +static void tilegx_cpu_initfn(Object *obj) +{ + CPUState *cs = CPU(obj); + TileGXCPU *cpu = TILEGX_CPU(obj); + CPUTLGState *env = &cpu->env; + static bool tcg_initialized; + + cs->env_ptr = env; + cpu_exec_init(cs, &error_abort); + + if (tcg_enabled() && !tcg_initialized) { + tcg_initialized = true; + tilegx_tcg_init(); + } +} + +static void tilegx_cpu_do_interrupt(CPUState *cs) +{ + cs->exception_index = -1; +} + +static int tilegx_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, + int mmu_idx) +{ + TileGXCPU *cpu = TILEGX_CPU(cs); + + cs->exception_index = TILEGX_EXCP_SEGV; + cpu->env.excaddr = address; + return 1; +} + +static bool tilegx_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +{ + if (interrupt_request & CPU_INTERRUPT_HARD) { + tilegx_cpu_do_interrupt(cs); + return true; + } + return false; +} + +static void tilegx_cpu_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + CPUClass *cc = CPU_CLASS(oc); + TileGXCPUClass *tcc = TILEGX_CPU_CLASS(oc); + + tcc->parent_realize = dc->realize; + dc->realize = tilegx_cpu_realizefn; + + tcc->parent_reset = cc->reset; + cc->reset = tilegx_cpu_reset; + + cc->has_work = tilegx_cpu_has_work; + cc->do_interrupt = tilegx_cpu_do_interrupt; + cc->cpu_exec_interrupt = tilegx_cpu_exec_interrupt; + cc->dump_state = tilegx_cpu_dump_state; + cc->set_pc = tilegx_cpu_set_pc; + cc->handle_mmu_fault = tilegx_cpu_handle_mmu_fault; + cc->gdb_num_core_regs = 0; +} + +static const TypeInfo tilegx_cpu_type_info = { + .name = TYPE_TILEGX_CPU, + .parent = TYPE_CPU, + .instance_size = sizeof(TileGXCPU), + .instance_init = tilegx_cpu_initfn, + .class_size = sizeof(TileGXCPUClass), + .class_init = tilegx_cpu_class_init, +}; + +static void tilegx_cpu_register_types(void) +{ + type_register_static(&tilegx_cpu_type_info); +} + +type_init(tilegx_cpu_register_types) diff --git a/target-tilegx/cpu.h b/target-tilegx/cpu.h new file mode 100644 index 0000000000..b9f5082b95 --- /dev/null +++ b/target-tilegx/cpu.h @@ -0,0 +1,179 @@ +/* + * TILE-Gx virtual CPU header + * + * Copyright (c) 2015 Chen Gang + * + * 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 + * 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 CPU_TILEGX_H +#define CPU_TILEGX_H + +#include "config.h" +#include "qemu-common.h" + +#define TARGET_LONG_BITS 64 + +#define CPUArchState struct CPUTLGState + +#include "exec/cpu-defs.h" + + +/* TILE-Gx common register alias */ +#define TILEGX_R_RE 0 /* 0 register, for function/syscall return value */ +#define TILEGX_R_ERR 1 /* 1 register, for syscall errno flag */ +#define TILEGX_R_NR 10 /* 10 register, for syscall number */ +#define TILEGX_R_BP 52 /* 52 register, optional frame pointer */ +#define TILEGX_R_TP 53 /* TP register, thread local storage data */ +#define TILEGX_R_SP 54 /* SP register, stack pointer */ +#define TILEGX_R_LR 55 /* LR register, may save pc, but it is not pc */ +#define TILEGX_R_COUNT 56 /* Only 56 registers are really useful */ +#define TILEGX_R_SN 56 /* SN register, obsoleted, it likes zero register */ +#define TILEGX_R_IDN0 57 /* IDN0 register, cause IDN_ACCESS exception */ +#define TILEGX_R_IDN1 58 /* IDN1 register, cause IDN_ACCESS exception */ +#define TILEGX_R_UDN0 59 /* UDN0 register, cause UDN_ACCESS exception */ +#define TILEGX_R_UDN1 60 /* UDN1 register, cause UDN_ACCESS exception */ +#define TILEGX_R_UDN2 61 /* UDN2 register, cause UDN_ACCESS exception */ +#define TILEGX_R_UDN3 62 /* UDN3 register, cause UDN_ACCESS exception */ +#define TILEGX_R_ZERO 63 /* Zero register, always zero */ +#define TILEGX_R_NOREG 255 /* Invalid register value */ + +/* TILE-Gx special registers used by outside */ +enum { + TILEGX_SPR_CMPEXCH = 0, + TILEGX_SPR_CRITICAL_SEC = 1, + TILEGX_SPR_SIM_CONTROL = 2, + TILEGX_SPR_COUNT +}; + +/* Exception numbers */ +typedef enum { + TILEGX_EXCP_NONE = 0, + TILEGX_EXCP_SYSCALL = 1, + TILEGX_EXCP_SEGV = 2, + TILEGX_EXCP_OPCODE_UNKNOWN = 0x101, + TILEGX_EXCP_OPCODE_UNIMPLEMENTED = 0x102, + TILEGX_EXCP_OPCODE_CMPEXCH = 0x103, + TILEGX_EXCP_OPCODE_CMPEXCH4 = 0x104, + TILEGX_EXCP_OPCODE_EXCH = 0x105, + TILEGX_EXCP_OPCODE_EXCH4 = 0x106, + TILEGX_EXCP_OPCODE_FETCHADD = 0x107, + TILEGX_EXCP_OPCODE_FETCHADD4 = 0x108, + TILEGX_EXCP_OPCODE_FETCHADDGEZ = 0x109, + TILEGX_EXCP_OPCODE_FETCHADDGEZ4 = 0x10a, + TILEGX_EXCP_OPCODE_FETCHAND = 0x10b, + TILEGX_EXCP_OPCODE_FETCHAND4 = 0x10c, + TILEGX_EXCP_OPCODE_FETCHOR = 0x10d, + TILEGX_EXCP_OPCODE_FETCHOR4 = 0x10e, + TILEGX_EXCP_REG_IDN_ACCESS = 0x181, + TILEGX_EXCP_REG_UDN_ACCESS = 0x182, + TILEGX_EXCP_UNALIGNMENT = 0x201, + TILEGX_EXCP_DBUG_BREAK = 0x301 +} TileExcp; + +typedef struct CPUTLGState { + uint64_t regs[TILEGX_R_COUNT]; /* Common used registers by outside */ + uint64_t spregs[TILEGX_SPR_COUNT]; /* Special used registers by outside */ + uint64_t pc; /* Current pc */ + +#if defined(CONFIG_USER_ONLY) + uint64_t atomic_srca; /* Arguments to atomic "exceptions" */ + uint64_t atomic_srcb; + uint32_t atomic_dstr; + uint64_t excaddr; /* exception address */ +#endif + + CPU_COMMON +} CPUTLGState; + +#include "qom/cpu.h" + +#define TYPE_TILEGX_CPU "tilegx-cpu" + +#define TILEGX_CPU_CLASS(klass) \ + OBJECT_CLASS_CHECK(TileGXCPUClass, (klass), TYPE_TILEGX_CPU) +#define TILEGX_CPU(obj) \ + OBJECT_CHECK(TileGXCPU, (obj), TYPE_TILEGX_CPU) +#define TILEGX_CPU_GET_CLASS(obj) \ + OBJECT_GET_CLASS(TileGXCPUClass, (obj), TYPE_TILEGX_CPU) + +/** + * TileGXCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_reset: The parent class' reset handler. + * + * A Tile-Gx CPU model. + */ +typedef struct TileGXCPUClass { + /*< private >*/ + CPUClass parent_class; + /*< public >*/ + + DeviceRealize parent_realize; + void (*parent_reset)(CPUState *cpu); +} TileGXCPUClass; + +/** + * TileGXCPU: + * @env: #CPUTLGState + * + * A Tile-GX CPU. + */ +typedef struct TileGXCPU { + /*< private >*/ + CPUState parent_obj; + /*< public >*/ + + CPUTLGState env; +} TileGXCPU; + +static inline TileGXCPU *tilegx_env_get_cpu(CPUTLGState *env) +{ + return container_of(env, TileGXCPU, env); +} + +#define ENV_GET_CPU(e) CPU(tilegx_env_get_cpu(e)) + +#define ENV_OFFSET offsetof(TileGXCPU, env) + +/* TILE-Gx memory attributes */ +#define TARGET_PAGE_BITS 16 /* TILE-Gx uses 64KB page size */ +#define TARGET_PHYS_ADDR_SPACE_BITS 42 +#define TARGET_VIRT_ADDR_SPACE_BITS 64 +#define MMU_USER_IDX 0 /* Current memory operation is in user mode */ + +#include "exec/cpu-all.h" + +void tilegx_tcg_init(void); +int cpu_tilegx_exec(CPUState *s); +int cpu_tilegx_signal_handler(int host_signum, void *pinfo, void *puc); + +TileGXCPU *cpu_tilegx_init(const char *cpu_model); + +#define cpu_init(cpu_model) CPU(cpu_tilegx_init(cpu_model)) + +#define cpu_exec cpu_tilegx_exec +#define cpu_gen_code cpu_tilegx_gen_code +#define cpu_signal_handler cpu_tilegx_signal_handler + +static inline void cpu_get_tb_cpu_state(CPUTLGState *env, target_ulong *pc, + target_ulong *cs_base, int *flags) +{ + *pc = env->pc; + *cs_base = 0; + *flags = 0; +} + +#include "exec/exec-all.h" + +#endif diff --git a/target-tilegx/helper.c b/target-tilegx/helper.c new file mode 100644 index 0000000000..a01bb8d513 --- /dev/null +++ b/target-tilegx/helper.c @@ -0,0 +1,80 @@ +/* + * QEMU TILE-Gx helpers + * + * Copyright (c) 2015 Chen Gang + * + * 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/lgpl-2.1.html> + */ + +#include "cpu.h" +#include "qemu-common.h" +#include "exec/helper-proto.h" + +void helper_exception(CPUTLGState *env, uint32_t excp) +{ + CPUState *cs = CPU(tilegx_env_get_cpu(env)); + + cs->exception_index = excp; + cpu_loop_exit(cs); +} + +uint64_t helper_cntlz(uint64_t arg) +{ + return clz64(arg); +} + +uint64_t helper_cnttz(uint64_t arg) +{ + return ctz64(arg); +} + +uint64_t helper_pcnt(uint64_t arg) +{ + return ctpop64(arg); +} + +uint64_t helper_revbits(uint64_t arg) +{ + return revbit64(arg); +} + +/* + * Functional Description + * uint64_t a = rf[SrcA]; + * uint64_t b = rf[SrcB]; + * uint64_t d = rf[Dest]; + * uint64_t output = 0; + * unsigned int counter; + * for (counter = 0; counter < (WORD_SIZE / BYTE_SIZE); counter++) + * { + * int sel = getByte (b, counter) & 0xf; + * uint8_t byte = (sel < 8) ? getByte (d, sel) : getByte (a, (sel - 8)); + * output = setByte (output, counter, byte); + * } + * rf[Dest] = output; + */ +uint64_t helper_shufflebytes(uint64_t dest, uint64_t srca, uint64_t srcb) +{ + uint64_t vdst = 0; + int count; + + for (count = 0; count < 64; count += 8) { + uint64_t sel = srcb >> count; + uint64_t src = (sel & 8) ? srca : dest; + vdst |= extract64(src, (sel & 7) * 8, 8) << count; + } + + return vdst; +} diff --git a/target-tilegx/helper.h b/target-tilegx/helper.h new file mode 100644 index 0000000000..766f5f2f9c --- /dev/null +++ b/target-tilegx/helper.h @@ -0,0 +1,10 @@ +DEF_HELPER_2(exception, noreturn, env, i32) +DEF_HELPER_FLAGS_1(cntlz, TCG_CALL_NO_RWG_SE, i64, i64) +DEF_HELPER_FLAGS_1(cnttz, TCG_CALL_NO_RWG_SE, i64, i64) +DEF_HELPER_FLAGS_1(pcnt, TCG_CALL_NO_RWG_SE, i64, i64) +DEF_HELPER_FLAGS_1(revbits, TCG_CALL_NO_RWG_SE, i64, i64) +DEF_HELPER_FLAGS_3(shufflebytes, TCG_CALL_NO_RWG_SE, i64, i64, i64, i64) + +DEF_HELPER_FLAGS_2(v1shl, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(v1shru, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(v1shrs, TCG_CALL_NO_RWG_SE, i64, i64, i64) diff --git a/target-tilegx/opcode_tilegx.h b/target-tilegx/opcode_tilegx.h new file mode 100644 index 0000000000..989436d2f8 --- /dev/null +++ b/target-tilegx/opcode_tilegx.h @@ -0,0 +1,1406 @@ +/* TILE-Gx opcode information. + * + * Copyright 2011 Tilera Corporation. All Rights Reserved. + * + * 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, version 2. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for + * more details. + * + * + * + * + * + */ + +#ifndef __ARCH_OPCODE_H__ +#define __ARCH_OPCODE_H__ + +#ifndef __ASSEMBLER__ + +typedef uint64_t tilegx_bundle_bits; + +/* These are the bits that determine if a bundle is in the X encoding. */ +#define TILEGX_BUNDLE_MODE_MASK ((tilegx_bundle_bits)3 << 62) + +enum +{ + /* Maximum number of instructions in a bundle (2 for X, 3 for Y). */ + TILEGX_MAX_INSTRUCTIONS_PER_BUNDLE = 3, + + /* How many different pipeline encodings are there? X0, X1, Y0, Y1, Y2. */ + TILEGX_NUM_PIPELINE_ENCODINGS = 5, + + /* Log base 2 of TILEGX_BUNDLE_SIZE_IN_BYTES. */ + TILEGX_LOG2_BUNDLE_SIZE_IN_BYTES = 3, + + /* Instructions take this many bytes. */ + TILEGX_BUNDLE_SIZE_IN_BYTES = 1 << TILEGX_LOG2_BUNDLE_SIZE_IN_BYTES, + + /* Log base 2 of TILEGX_BUNDLE_ALIGNMENT_IN_BYTES. */ + TILEGX_LOG2_BUNDLE_ALIGNMENT_IN_BYTES = 3, + + /* Bundles should be aligned modulo this number of bytes. */ + TILEGX_BUNDLE_ALIGNMENT_IN_BYTES = + (1 << TILEGX_LOG2_BUNDLE_ALIGNMENT_IN_BYTES), + + /* Number of registers (some are magic, such as network I/O). */ + TILEGX_NUM_REGISTERS = 64, +}; + +/* Make a few "tile_" variables to simplify common code between + architectures. */ + +typedef tilegx_bundle_bits tile_bundle_bits; +#define TILE_BUNDLE_SIZE_IN_BYTES TILEGX_BUNDLE_SIZE_IN_BYTES +#define TILE_BUNDLE_ALIGNMENT_IN_BYTES TILEGX_BUNDLE_ALIGNMENT_IN_BYTES +#define TILE_LOG2_BUNDLE_ALIGNMENT_IN_BYTES \ + TILEGX_LOG2_BUNDLE_ALIGNMENT_IN_BYTES +#define TILE_BPT_BUNDLE TILEGX_BPT_BUNDLE + +/* 64-bit pattern for a { bpt ; nop } bundle. */ +#define TILEGX_BPT_BUNDLE 0x286a44ae51485000ULL + +static inline unsigned int +get_BFEnd_X0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 12)) & 0x3f); +} + +static inline unsigned int +get_BFOpcodeExtension_X0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 24)) & 0xf); +} + +static inline unsigned int +get_BFStart_X0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 18)) & 0x3f); +} + +static inline unsigned int +get_BrOff_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 31)) & 0x0000003f) | + (((unsigned int)(n >> 37)) & 0x0001ffc0); +} + +static inline unsigned int +get_BrType_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 54)) & 0x1f); +} + +static inline unsigned int +get_Dest_Imm8_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 31)) & 0x0000003f) | + (((unsigned int)(n >> 43)) & 0x000000c0); +} + +static inline unsigned int +get_Dest_X0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 0)) & 0x3f); +} + +static inline unsigned int +get_Dest_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 31)) & 0x3f); +} + +static inline unsigned int +get_Dest_Y0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 0)) & 0x3f); +} + +static inline unsigned int +get_Dest_Y1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 31)) & 0x3f); +} + +static inline unsigned int +get_Imm16_X0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 12)) & 0xffff); +} + +static inline unsigned int +get_Imm16_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 43)) & 0xffff); +} + +static inline unsigned int +get_Imm8OpcodeExtension_X0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 20)) & 0xff); +} + +static inline unsigned int +get_Imm8OpcodeExtension_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 51)) & 0xff); +} + +static inline unsigned int +get_Imm8_X0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 12)) & 0xff); +} + +static inline unsigned int +get_Imm8_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 43)) & 0xff); +} + +static inline unsigned int +get_Imm8_Y0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 12)) & 0xff); +} + +static inline unsigned int +get_Imm8_Y1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 43)) & 0xff); +} + +static inline unsigned int +get_JumpOff_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 31)) & 0x7ffffff); +} + +static inline unsigned int +get_JumpOpcodeExtension_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 58)) & 0x1); +} + +static inline unsigned int +get_MF_Imm14_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 37)) & 0x3fff); +} + +static inline unsigned int +get_MT_Imm14_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 31)) & 0x0000003f) | + (((unsigned int)(n >> 37)) & 0x00003fc0); +} + +static inline unsigned int +get_Mode(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 62)) & 0x3); +} + +static inline unsigned int +get_Opcode_X0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 28)) & 0x7); +} + +static inline unsigned int +get_Opcode_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 59)) & 0x7); +} + +static inline unsigned int +get_Opcode_Y0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 27)) & 0xf); +} + +static inline unsigned int +get_Opcode_Y1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 58)) & 0xf); +} + +static inline unsigned int +get_Opcode_Y2(tilegx_bundle_bits n) +{ + return (((n >> 26)) & 0x00000001) | + (((unsigned int)(n >> 56)) & 0x00000002); +} + +static inline unsigned int +get_RRROpcodeExtension_X0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 18)) & 0x3ff); +} + +static inline unsigned int +get_RRROpcodeExtension_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 49)) & 0x3ff); +} + +static inline unsigned int +get_RRROpcodeExtension_Y0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 18)) & 0x3); +} + +static inline unsigned int +get_RRROpcodeExtension_Y1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 49)) & 0x3); +} + +static inline unsigned int +get_ShAmt_X0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 12)) & 0x3f); +} + +static inline unsigned int +get_ShAmt_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 43)) & 0x3f); +} + +static inline unsigned int +get_ShAmt_Y0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 12)) & 0x3f); +} + +static inline unsigned int +get_ShAmt_Y1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 43)) & 0x3f); +} + +static inline unsigned int +get_ShiftOpcodeExtension_X0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 18)) & 0x3ff); +} + +static inline unsigned int +get_ShiftOpcodeExtension_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 49)) & 0x3ff); +} + +static inline unsigned int +get_ShiftOpcodeExtension_Y0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 18)) & 0x3); +} + +static inline unsigned int +get_ShiftOpcodeExtension_Y1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 49)) & 0x3); +} + +static inline unsigned int +get_SrcA_X0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 6)) & 0x3f); +} + +static inline unsigned int +get_SrcA_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 37)) & 0x3f); +} + +static inline unsigned int +get_SrcA_Y0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 6)) & 0x3f); +} + +static inline unsigned int +get_SrcA_Y1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 37)) & 0x3f); +} + +static inline unsigned int +get_SrcA_Y2(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 20)) & 0x3f); +} + +static inline unsigned int +get_SrcBDest_Y2(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 51)) & 0x3f); +} + +static inline unsigned int +get_SrcB_X0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 12)) & 0x3f); +} + +static inline unsigned int +get_SrcB_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 43)) & 0x3f); +} + +static inline unsigned int +get_SrcB_Y0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 12)) & 0x3f); +} + +static inline unsigned int +get_SrcB_Y1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 43)) & 0x3f); +} + +static inline unsigned int +get_UnaryOpcodeExtension_X0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 12)) & 0x3f); +} + +static inline unsigned int +get_UnaryOpcodeExtension_X1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 43)) & 0x3f); +} + +static inline unsigned int +get_UnaryOpcodeExtension_Y0(tilegx_bundle_bits num) +{ + const unsigned int n = (unsigned int)num; + return (((n >> 12)) & 0x3f); +} + +static inline unsigned int +get_UnaryOpcodeExtension_Y1(tilegx_bundle_bits n) +{ + return (((unsigned int)(n >> 43)) & 0x3f); +} + + +static inline int +sign_extend(int n, int num_bits) +{ + int shift = (int)(sizeof(int) * 8 - num_bits); + return (n << shift) >> shift; +} + + + +static inline tilegx_bundle_bits +create_BFEnd_X0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3f) << 12); +} + +static inline tilegx_bundle_bits +create_BFOpcodeExtension_X0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0xf) << 24); +} + +static inline tilegx_bundle_bits +create_BFStart_X0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3f) << 18); +} + +static inline tilegx_bundle_bits +create_BrOff_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x0000003f)) << 31) | + (((tilegx_bundle_bits)(n & 0x0001ffc0)) << 37); +} + +static inline tilegx_bundle_bits +create_BrType_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x1f)) << 54); +} + +static inline tilegx_bundle_bits +create_Dest_Imm8_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x0000003f)) << 31) | + (((tilegx_bundle_bits)(n & 0x000000c0)) << 43); +} + +static inline tilegx_bundle_bits +create_Dest_X0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3f) << 0); +} + +static inline tilegx_bundle_bits +create_Dest_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3f)) << 31); +} + +static inline tilegx_bundle_bits +create_Dest_Y0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3f) << 0); +} + +static inline tilegx_bundle_bits +create_Dest_Y1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3f)) << 31); +} + +static inline tilegx_bundle_bits +create_Imm16_X0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0xffff) << 12); +} + +static inline tilegx_bundle_bits +create_Imm16_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0xffff)) << 43); +} + +static inline tilegx_bundle_bits +create_Imm8OpcodeExtension_X0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0xff) << 20); +} + +static inline tilegx_bundle_bits +create_Imm8OpcodeExtension_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0xff)) << 51); +} + +static inline tilegx_bundle_bits +create_Imm8_X0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0xff) << 12); +} + +static inline tilegx_bundle_bits +create_Imm8_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0xff)) << 43); +} + +static inline tilegx_bundle_bits +create_Imm8_Y0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0xff) << 12); +} + +static inline tilegx_bundle_bits +create_Imm8_Y1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0xff)) << 43); +} + +static inline tilegx_bundle_bits +create_JumpOff_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x7ffffff)) << 31); +} + +static inline tilegx_bundle_bits +create_JumpOpcodeExtension_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x1)) << 58); +} + +static inline tilegx_bundle_bits +create_MF_Imm14_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3fff)) << 37); +} + +static inline tilegx_bundle_bits +create_MT_Imm14_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x0000003f)) << 31) | + (((tilegx_bundle_bits)(n & 0x00003fc0)) << 37); +} + +static inline tilegx_bundle_bits +create_Mode(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3)) << 62); +} + +static inline tilegx_bundle_bits +create_Opcode_X0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x7) << 28); +} + +static inline tilegx_bundle_bits +create_Opcode_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x7)) << 59); +} + +static inline tilegx_bundle_bits +create_Opcode_Y0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0xf) << 27); +} + +static inline tilegx_bundle_bits +create_Opcode_Y1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0xf)) << 58); +} + +static inline tilegx_bundle_bits +create_Opcode_Y2(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x00000001) << 26) | + (((tilegx_bundle_bits)(n & 0x00000002)) << 56); +} + +static inline tilegx_bundle_bits +create_RRROpcodeExtension_X0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3ff) << 18); +} + +static inline tilegx_bundle_bits +create_RRROpcodeExtension_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3ff)) << 49); +} + +static inline tilegx_bundle_bits +create_RRROpcodeExtension_Y0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3) << 18); +} + +static inline tilegx_bundle_bits +create_RRROpcodeExtension_Y1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3)) << 49); +} + +static inline tilegx_bundle_bits +create_ShAmt_X0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3f) << 12); +} + +static inline tilegx_bundle_bits +create_ShAmt_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3f)) << 43); +} + +static inline tilegx_bundle_bits +create_ShAmt_Y0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3f) << 12); +} + +static inline tilegx_bundle_bits +create_ShAmt_Y1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3f)) << 43); +} + +static inline tilegx_bundle_bits +create_ShiftOpcodeExtension_X0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3ff) << 18); +} + +static inline tilegx_bundle_bits +create_ShiftOpcodeExtension_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3ff)) << 49); +} + +static inline tilegx_bundle_bits +create_ShiftOpcodeExtension_Y0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3) << 18); +} + +static inline tilegx_bundle_bits +create_ShiftOpcodeExtension_Y1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3)) << 49); +} + +static inline tilegx_bundle_bits +create_SrcA_X0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3f) << 6); +} + +static inline tilegx_bundle_bits +create_SrcA_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3f)) << 37); +} + +static inline tilegx_bundle_bits +create_SrcA_Y0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3f) << 6); +} + +static inline tilegx_bundle_bits +create_SrcA_Y1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3f)) << 37); +} + +static inline tilegx_bundle_bits +create_SrcA_Y2(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3f) << 20); +} + +static inline tilegx_bundle_bits +create_SrcBDest_Y2(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3f)) << 51); +} + +static inline tilegx_bundle_bits +create_SrcB_X0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3f) << 12); +} + +static inline tilegx_bundle_bits +create_SrcB_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3f)) << 43); +} + +static inline tilegx_bundle_bits +create_SrcB_Y0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3f) << 12); +} + +static inline tilegx_bundle_bits +create_SrcB_Y1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3f)) << 43); +} + +static inline tilegx_bundle_bits +create_UnaryOpcodeExtension_X0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3f) << 12); +} + +static inline tilegx_bundle_bits +create_UnaryOpcodeExtension_X1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3f)) << 43); +} + +static inline tilegx_bundle_bits +create_UnaryOpcodeExtension_Y0(int num) +{ + const unsigned int n = (unsigned int)num; + return ((n & 0x3f) << 12); +} + +static inline tilegx_bundle_bits +create_UnaryOpcodeExtension_Y1(int num) +{ + const unsigned int n = (unsigned int)num; + return (((tilegx_bundle_bits)(n & 0x3f)) << 43); +} + + +enum +{ + ADDI_IMM8_OPCODE_X0 = 1, + ADDI_IMM8_OPCODE_X1 = 1, + ADDI_OPCODE_Y0 = 0, + ADDI_OPCODE_Y1 = 1, + ADDLI_OPCODE_X0 = 1, + ADDLI_OPCODE_X1 = 0, + ADDXI_IMM8_OPCODE_X0 = 2, + ADDXI_IMM8_OPCODE_X1 = 2, + ADDXI_OPCODE_Y0 = 1, + ADDXI_OPCODE_Y1 = 2, + ADDXLI_OPCODE_X0 = 2, + ADDXLI_OPCODE_X1 = 1, + ADDXSC_RRR_0_OPCODE_X0 = 1, + ADDXSC_RRR_0_OPCODE_X1 = 1, + ADDX_RRR_0_OPCODE_X0 = 2, + ADDX_RRR_0_OPCODE_X1 = 2, + ADDX_RRR_0_OPCODE_Y0 = 0, + ADDX_RRR_0_OPCODE_Y1 = 0, + ADD_RRR_0_OPCODE_X0 = 3, + ADD_RRR_0_OPCODE_X1 = 3, + ADD_RRR_0_OPCODE_Y0 = 1, + ADD_RRR_0_OPCODE_Y1 = 1, + ANDI_IMM8_OPCODE_X0 = 3, + ANDI_IMM8_OPCODE_X1 = 3, + ANDI_OPCODE_Y0 = 2, + ANDI_OPCODE_Y1 = 3, + AND_RRR_0_OPCODE_X0 = 4, + AND_RRR_0_OPCODE_X1 = 4, + AND_RRR_5_OPCODE_Y0 = 0, + AND_RRR_5_OPCODE_Y1 = 0, + BEQZT_BRANCH_OPCODE_X1 = 16, + BEQZ_BRANCH_OPCODE_X1 = 17, + BFEXTS_BF_OPCODE_X0 = 4, + BFEXTU_BF_OPCODE_X0 = 5, + BFINS_BF_OPCODE_X0 = 6, + BF_OPCODE_X0 = 3, + BGEZT_BRANCH_OPCODE_X1 = 18, + BGEZ_BRANCH_OPCODE_X1 = 19, + BGTZT_BRANCH_OPCODE_X1 = 20, + BGTZ_BRANCH_OPCODE_X1 = 21, + BLBCT_BRANCH_OPCODE_X1 = 22, + BLBC_BRANCH_OPCODE_X1 = 23, + BLBST_BRANCH_OPCODE_X1 = 24, + BLBS_BRANCH_OPCODE_X1 = 25, + BLEZT_BRANCH_OPCODE_X1 = 26, + BLEZ_BRANCH_OPCODE_X1 = 27, + BLTZT_BRANCH_OPCODE_X1 = 28, + BLTZ_BRANCH_OPCODE_X1 = 29, + BNEZT_BRANCH_OPCODE_X1 = 30, + BNEZ_BRANCH_OPCODE_X1 = 31, + BRANCH_OPCODE_X1 = 2, + CMOVEQZ_RRR_0_OPCODE_X0 = 5, + CMOVEQZ_RRR_4_OPCODE_Y0 = 0, + CMOVNEZ_RRR_0_OPCODE_X0 = 6, + CMOVNEZ_RRR_4_OPCODE_Y0 = 1, + CMPEQI_IMM8_OPCODE_X0 = 4, + CMPEQI_IMM8_OPCODE_X1 = 4, + CMPEQI_OPCODE_Y0 = 3, + CMPEQI_OPCODE_Y1 = 4, + CMPEQ_RRR_0_OPCODE_X0 = 7, + CMPEQ_RRR_0_OPCODE_X1 = 5, + CMPEQ_RRR_3_OPCODE_Y0 = 0, + CMPEQ_RRR_3_OPCODE_Y1 = 2, + CMPEXCH4_RRR_0_OPCODE_X1 = 6, + CMPEXCH_RRR_0_OPCODE_X1 = 7, + CMPLES_RRR_0_OPCODE_X0 = 8, + CMPLES_RRR_0_OPCODE_X1 = 8, + CMPLES_RRR_2_OPCODE_Y0 = 0, + CMPLES_RRR_2_OPCODE_Y1 = 0, + CMPLEU_RRR_0_OPCODE_X0 = 9, + CMPLEU_RRR_0_OPCODE_X1 = 9, + CMPLEU_RRR_2_OPCODE_Y0 = 1, + CMPLEU_RRR_2_OPCODE_Y1 = 1, + CMPLTSI_IMM8_OPCODE_X0 = 5, + CMPLTSI_IMM8_OPCODE_X1 = 5, + CMPLTSI_OPCODE_Y0 = 4, + CMPLTSI_OPCODE_Y1 = 5, + CMPLTS_RRR_0_OPCODE_X0 = 10, + CMPLTS_RRR_0_OPCODE_X1 = 10, + CMPLTS_RRR_2_OPCODE_Y0 = 2, + CMPLTS_RRR_2_OPCODE_Y1 = 2, + CMPLTUI_IMM8_OPCODE_X0 = 6, + CMPLTUI_IMM8_OPCODE_X1 = 6, + CMPLTU_RRR_0_OPCODE_X0 = 11, + CMPLTU_RRR_0_OPCODE_X1 = 11, + CMPLTU_RRR_2_OPCODE_Y0 = 3, + CMPLTU_RRR_2_OPCODE_Y1 = 3, + CMPNE_RRR_0_OPCODE_X0 = 12, + CMPNE_RRR_0_OPCODE_X1 = 12, + CMPNE_RRR_3_OPCODE_Y0 = 1, + CMPNE_RRR_3_OPCODE_Y1 = 3, + CMULAF_RRR_0_OPCODE_X0 = 13, + CMULA_RRR_0_OPCODE_X0 = 14, + CMULFR_RRR_0_OPCODE_X0 = 15, + CMULF_RRR_0_OPCODE_X0 = 16, + CMULHR_RRR_0_OPCODE_X0 = 17, + CMULH_RRR_0_OPCODE_X0 = 18, + CMUL_RRR_0_OPCODE_X0 = 19, + CNTLZ_UNARY_OPCODE_X0 = 1, + CNTLZ_UNARY_OPCODE_Y0 = 1, + CNTTZ_UNARY_OPCODE_X0 = 2, + CNTTZ_UNARY_OPCODE_Y0 = 2, + CRC32_32_RRR_0_OPCODE_X0 = 20, + CRC32_8_RRR_0_OPCODE_X0 = 21, + DBLALIGN2_RRR_0_OPCODE_X0 = 22, + DBLALIGN2_RRR_0_OPCODE_X1 = 13, + DBLALIGN4_RRR_0_OPCODE_X0 = 23, + DBLALIGN4_RRR_0_OPCODE_X1 = 14, + DBLALIGN6_RRR_0_OPCODE_X0 = 24, + DBLALIGN6_RRR_0_OPCODE_X1 = 15, + DBLALIGN_RRR_0_OPCODE_X0 = 25, + DRAIN_UNARY_OPCODE_X1 = 1, + DTLBPR_UNARY_OPCODE_X1 = 2, + EXCH4_RRR_0_OPCODE_X1 = 16, + EXCH_RRR_0_OPCODE_X1 = 17, + FDOUBLE_ADDSUB_RRR_0_OPCODE_X0 = 26, + FDOUBLE_ADD_FLAGS_RRR_0_OPCODE_X0 = 27, + FDOUBLE_MUL_FLAGS_RRR_0_OPCODE_X0 = 28, + FDOUBLE_PACK1_RRR_0_OPCODE_X0 = 29, + FDOUBLE_PACK2_RRR_0_OPCODE_X0 = 30, + FDOUBLE_SUB_FLAGS_RRR_0_OPCODE_X0 = 31, + FDOUBLE_UNPACK_MAX_RRR_0_OPCODE_X0 = 32, + FDOUBLE_UNPACK_MIN_RRR_0_OPCODE_X0 = 33, + FETCHADD4_RRR_0_OPCODE_X1 = 18, + FETCHADDGEZ4_RRR_0_OPCODE_X1 = 19, + FETCHADDGEZ_RRR_0_OPCODE_X1 = 20, + FETCHADD_RRR_0_OPCODE_X1 = 21, + FETCHAND4_RRR_0_OPCODE_X1 = 22, + FETCHAND_RRR_0_OPCODE_X1 = 23, + FETCHOR4_RRR_0_OPCODE_X1 = 24, + FETCHOR_RRR_0_OPCODE_X1 = 25, + FINV_UNARY_OPCODE_X1 = 3, + FLUSHWB_UNARY_OPCODE_X1 = 4, + FLUSH_UNARY_OPCODE_X1 = 5, + FNOP_UNARY_OPCODE_X0 = 3, + FNOP_UNARY_OPCODE_X1 = 6, + FNOP_UNARY_OPCODE_Y0 = 3, + FNOP_UNARY_OPCODE_Y1 = 8, + FSINGLE_ADD1_RRR_0_OPCODE_X0 = 34, + FSINGLE_ADDSUB2_RRR_0_OPCODE_X0 = 35, + FSINGLE_MUL1_RRR_0_OPCODE_X0 = 36, + FSINGLE_MUL2_RRR_0_OPCODE_X0 = 37, + FSINGLE_PACK1_UNARY_OPCODE_X0 = 4, + FSINGLE_PACK1_UNARY_OPCODE_Y0 = 4, + FSINGLE_PACK2_RRR_0_OPCODE_X0 = 38, + FSINGLE_SUB1_RRR_0_OPCODE_X0 = 39, + ICOH_UNARY_OPCODE_X1 = 7, + ILL_UNARY_OPCODE_X1 = 8, + ILL_UNARY_OPCODE_Y1 = 9, + IMM8_OPCODE_X0 = 4, + IMM8_OPCODE_X1 = 3, + INV_UNARY_OPCODE_X1 = 9, + IRET_UNARY_OPCODE_X1 = 10, + JALRP_UNARY_OPCODE_X1 = 11, + JALRP_UNARY_OPCODE_Y1 = 10, + JALR_UNARY_OPCODE_X1 = 12, + JALR_UNARY_OPCODE_Y1 = 11, + JAL_JUMP_OPCODE_X1 = 0, + JRP_UNARY_OPCODE_X1 = 13, + JRP_UNARY_OPCODE_Y1 = 12, + JR_UNARY_OPCODE_X1 = 14, + JR_UNARY_OPCODE_Y1 = 13, + JUMP_OPCODE_X1 = 4, + J_JUMP_OPCODE_X1 = 1, + LD1S_ADD_IMM8_OPCODE_X1 = 7, + LD1S_OPCODE_Y2 = 0, + LD1S_UNARY_OPCODE_X1 = 15, + LD1U_ADD_IMM8_OPCODE_X1 = 8, + LD1U_OPCODE_Y2 = 1, + LD1U_UNARY_OPCODE_X1 = 16, + LD2S_ADD_IMM8_OPCODE_X1 = 9, + LD2S_OPCODE_Y2 = 2, + LD2S_UNARY_OPCODE_X1 = 17, + LD2U_ADD_IMM8_OPCODE_X1 = 10, + LD2U_OPCODE_Y2 = 3, + LD2U_UNARY_OPCODE_X1 = 18, + LD4S_ADD_IMM8_OPCODE_X1 = 11, + LD4S_OPCODE_Y2 = 1, + LD4S_UNARY_OPCODE_X1 = 19, + LD4U_ADD_IMM8_OPCODE_X1 = 12, + LD4U_OPCODE_Y2 = 2, + LD4U_UNARY_OPCODE_X1 = 20, + LDNA_UNARY_OPCODE_X1 = 21, + LDNT1S_ADD_IMM8_OPCODE_X1 = 13, + LDNT1S_UNARY_OPCODE_X1 = 22, + LDNT1U_ADD_IMM8_OPCODE_X1 = 14, + LDNT1U_UNARY_OPCODE_X1 = 23, + LDNT2S_ADD_IMM8_OPCODE_X1 = 15, + LDNT2S_UNARY_OPCODE_X1 = 24, + LDNT2U_ADD_IMM8_OPCODE_X1 = 16, + LDNT2U_UNARY_OPCODE_X1 = 25, + LDNT4S_ADD_IMM8_OPCODE_X1 = 17, + LDNT4S_UNARY_OPCODE_X1 = 26, + LDNT4U_ADD_IMM8_OPCODE_X1 = 18, + LDNT4U_UNARY_OPCODE_X1 = 27, + LDNT_ADD_IMM8_OPCODE_X1 = 19, + LDNT_UNARY_OPCODE_X1 = 28, + LD_ADD_IMM8_OPCODE_X1 = 20, + LD_OPCODE_Y2 = 3, + LD_UNARY_OPCODE_X1 = 29, + LNK_UNARY_OPCODE_X1 = 30, + LNK_UNARY_OPCODE_Y1 = 14, + LDNA_ADD_IMM8_OPCODE_X1 = 21, + MFSPR_IMM8_OPCODE_X1 = 22, + MF_UNARY_OPCODE_X1 = 31, + MM_BF_OPCODE_X0 = 7, + MNZ_RRR_0_OPCODE_X0 = 40, + MNZ_RRR_0_OPCODE_X1 = 26, + MNZ_RRR_4_OPCODE_Y0 = 2, + MNZ_RRR_4_OPCODE_Y1 = 2, + MODE_OPCODE_YA2 = 1, + MODE_OPCODE_YB2 = 2, + MODE_OPCODE_YC2 = 3, + MTSPR_IMM8_OPCODE_X1 = 23, + MULAX_RRR_0_OPCODE_X0 = 41, + MULAX_RRR_3_OPCODE_Y0 = 2, + MULA_HS_HS_RRR_0_OPCODE_X0 = 42, + MULA_HS_HS_RRR_9_OPCODE_Y0 = 0, + MULA_HS_HU_RRR_0_OPCODE_X0 = 43, + MULA_HS_LS_RRR_0_OPCODE_X0 = 44, + MULA_HS_LU_RRR_0_OPCODE_X0 = 45, + MULA_HU_HU_RRR_0_OPCODE_X0 = 46, + MULA_HU_HU_RRR_9_OPCODE_Y0 = 1, + MULA_HU_LS_RRR_0_OPCODE_X0 = 47, + MULA_HU_LU_RRR_0_OPCODE_X0 = 48, + MULA_LS_LS_RRR_0_OPCODE_X0 = 49, + MULA_LS_LS_RRR_9_OPCODE_Y0 = 2, + MULA_LS_LU_RRR_0_OPCODE_X0 = 50, + MULA_LU_LU_RRR_0_OPCODE_X0 = 51, + MULA_LU_LU_RRR_9_OPCODE_Y0 = 3, + MULX_RRR_0_OPCODE_X0 = 52, + MULX_RRR_3_OPCODE_Y0 = 3, + MUL_HS_HS_RRR_0_OPCODE_X0 = 53, + MUL_HS_HS_RRR_8_OPCODE_Y0 = 0, + MUL_HS_HU_RRR_0_OPCODE_X0 = 54, + MUL_HS_LS_RRR_0_OPCODE_X0 = 55, + MUL_HS_LU_RRR_0_OPCODE_X0 = 56, + MUL_HU_HU_RRR_0_OPCODE_X0 = 57, + MUL_HU_HU_RRR_8_OPCODE_Y0 = 1, + MUL_HU_LS_RRR_0_OPCODE_X0 = 58, + MUL_HU_LU_RRR_0_OPCODE_X0 = 59, + MUL_LS_LS_RRR_0_OPCODE_X0 = 60, + MUL_LS_LS_RRR_8_OPCODE_Y0 = 2, + MUL_LS_LU_RRR_0_OPCODE_X0 = 61, + MUL_LU_LU_RRR_0_OPCODE_X0 = 62, + MUL_LU_LU_RRR_8_OPCODE_Y0 = 3, + MZ_RRR_0_OPCODE_X0 = 63, + MZ_RRR_0_OPCODE_X1 = 27, + MZ_RRR_4_OPCODE_Y0 = 3, + MZ_RRR_4_OPCODE_Y1 = 3, + NAP_UNARY_OPCODE_X1 = 32, + NOP_UNARY_OPCODE_X0 = 5, + NOP_UNARY_OPCODE_X1 = 33, + NOP_UNARY_OPCODE_Y0 = 5, + NOP_UNARY_OPCODE_Y1 = 15, + NOR_RRR_0_OPCODE_X0 = 64, + NOR_RRR_0_OPCODE_X1 = 28, + NOR_RRR_5_OPCODE_Y0 = 1, + NOR_RRR_5_OPCODE_Y1 = 1, + ORI_IMM8_OPCODE_X0 = 7, + ORI_IMM8_OPCODE_X1 = 24, + OR_RRR_0_OPCODE_X0 = 65, + OR_RRR_0_OPCODE_X1 = 29, + OR_RRR_5_OPCODE_Y0 = 2, + OR_RRR_5_OPCODE_Y1 = 2, + PCNT_UNARY_OPCODE_X0 = 6, + PCNT_UNARY_OPCODE_Y0 = 6, + REVBITS_UNARY_OPCODE_X0 = 7, + REVBITS_UNARY_OPCODE_Y0 = 7, + REVBYTES_UNARY_OPCODE_X0 = 8, + REVBYTES_UNARY_OPCODE_Y0 = 8, + ROTLI_SHIFT_OPCODE_X0 = 1, + ROTLI_SHIFT_OPCODE_X1 = 1, + ROTLI_SHIFT_OPCODE_Y0 = 0, + ROTLI_SHIFT_OPCODE_Y1 = 0, + ROTL_RRR_0_OPCODE_X0 = 66, + ROTL_RRR_0_OPCODE_X1 = 30, + ROTL_RRR_6_OPCODE_Y0 = 0, + ROTL_RRR_6_OPCODE_Y1 = 0, + RRR_0_OPCODE_X0 = 5, + RRR_0_OPCODE_X1 = 5, + RRR_0_OPCODE_Y0 = 5, + RRR_0_OPCODE_Y1 = 6, + RRR_1_OPCODE_Y0 = 6, + RRR_1_OPCODE_Y1 = 7, + RRR_2_OPCODE_Y0 = 7, + RRR_2_OPCODE_Y1 = 8, + RRR_3_OPCODE_Y0 = 8, + RRR_3_OPCODE_Y1 = 9, + RRR_4_OPCODE_Y0 = 9, + RRR_4_OPCODE_Y1 = 10, + RRR_5_OPCODE_Y0 = 10, + RRR_5_OPCODE_Y1 = 11, + RRR_6_OPCODE_Y0 = 11, + RRR_6_OPCODE_Y1 = 12, + RRR_7_OPCODE_Y0 = 12, + RRR_7_OPCODE_Y1 = 13, + RRR_8_OPCODE_Y0 = 13, + RRR_9_OPCODE_Y0 = 14, + SHIFT_OPCODE_X0 = 6, + SHIFT_OPCODE_X1 = 6, + SHIFT_OPCODE_Y0 = 15, + SHIFT_OPCODE_Y1 = 14, + SHL16INSLI_OPCODE_X0 = 7, + SHL16INSLI_OPCODE_X1 = 7, + SHL1ADDX_RRR_0_OPCODE_X0 = 67, + SHL1ADDX_RRR_0_OPCODE_X1 = 31, + SHL1ADDX_RRR_7_OPCODE_Y0 = 1, + SHL1ADDX_RRR_7_OPCODE_Y1 = 1, + SHL1ADD_RRR_0_OPCODE_X0 = 68, + SHL1ADD_RRR_0_OPCODE_X1 = 32, + SHL1ADD_RRR_1_OPCODE_Y0 = 0, + SHL1ADD_RRR_1_OPCODE_Y1 = 0, + SHL2ADDX_RRR_0_OPCODE_X0 = 69, + SHL2ADDX_RRR_0_OPCODE_X1 = 33, + SHL2ADDX_RRR_7_OPCODE_Y0 = 2, + SHL2ADDX_RRR_7_OPCODE_Y1 = 2, + SHL2ADD_RRR_0_OPCODE_X0 = 70, + SHL2ADD_RRR_0_OPCODE_X1 = 34, + SHL2ADD_RRR_1_OPCODE_Y0 = 1, + SHL2ADD_RRR_1_OPCODE_Y1 = 1, + SHL3ADDX_RRR_0_OPCODE_X0 = 71, + SHL3ADDX_RRR_0_OPCODE_X1 = 35, + SHL3ADDX_RRR_7_OPCODE_Y0 = 3, + SHL3ADDX_RRR_7_OPCODE_Y1 = 3, + SHL3ADD_RRR_0_OPCODE_X0 = 72, + SHL3ADD_RRR_0_OPCODE_X1 = 36, + SHL3ADD_RRR_1_OPCODE_Y0 = 2, + SHL3ADD_RRR_1_OPCODE_Y1 = 2, + SHLI_SHIFT_OPCODE_X0 = 2, + SHLI_SHIFT_OPCODE_X1 = 2, + SHLI_SHIFT_OPCODE_Y0 = 1, + SHLI_SHIFT_OPCODE_Y1 = 1, + SHLXI_SHIFT_OPCODE_X0 = 3, + SHLXI_SHIFT_OPCODE_X1 = 3, + SHLX_RRR_0_OPCODE_X0 = 73, + SHLX_RRR_0_OPCODE_X1 = 37, + SHL_RRR_0_OPCODE_X0 = 74, + SHL_RRR_0_OPCODE_X1 = 38, + SHL_RRR_6_OPCODE_Y0 = 1, + SHL_RRR_6_OPCODE_Y1 = 1, + SHRSI_SHIFT_OPCODE_X0 = 4, + SHRSI_SHIFT_OPCODE_X1 = 4, + SHRSI_SHIFT_OPCODE_Y0 = 2, + SHRSI_SHIFT_OPCODE_Y1 = 2, + SHRS_RRR_0_OPCODE_X0 = 75, + SHRS_RRR_0_OPCODE_X1 = 39, + SHRS_RRR_6_OPCODE_Y0 = 2, + SHRS_RRR_6_OPCODE_Y1 = 2, + SHRUI_SHIFT_OPCODE_X0 = 5, + SHRUI_SHIFT_OPCODE_X1 = 5, + SHRUI_SHIFT_OPCODE_Y0 = 3, + SHRUI_SHIFT_OPCODE_Y1 = 3, + SHRUXI_SHIFT_OPCODE_X0 = 6, + SHRUXI_SHIFT_OPCODE_X1 = 6, + SHRUX_RRR_0_OPCODE_X0 = 76, + SHRUX_RRR_0_OPCODE_X1 = 40, + SHRU_RRR_0_OPCODE_X0 = 77, + SHRU_RRR_0_OPCODE_X1 = 41, + SHRU_RRR_6_OPCODE_Y0 = 3, + SHRU_RRR_6_OPCODE_Y1 = 3, + SHUFFLEBYTES_RRR_0_OPCODE_X0 = 78, + ST1_ADD_IMM8_OPCODE_X1 = 25, + ST1_OPCODE_Y2 = 0, + ST1_RRR_0_OPCODE_X1 = 42, + ST2_ADD_IMM8_OPCODE_X1 = 26, + ST2_OPCODE_Y2 = 1, + ST2_RRR_0_OPCODE_X1 = 43, + ST4_ADD_IMM8_OPCODE_X1 = 27, + ST4_OPCODE_Y2 = 2, + ST4_RRR_0_OPCODE_X1 = 44, + STNT1_ADD_IMM8_OPCODE_X1 = 28, + STNT1_RRR_0_OPCODE_X1 = 45, + STNT2_ADD_IMM8_OPCODE_X1 = 29, + STNT2_RRR_0_OPCODE_X1 = 46, + STNT4_ADD_IMM8_OPCODE_X1 = 30, + STNT4_RRR_0_OPCODE_X1 = 47, + STNT_ADD_IMM8_OPCODE_X1 = 31, + STNT_RRR_0_OPCODE_X1 = 48, + ST_ADD_IMM8_OPCODE_X1 = 32, + ST_OPCODE_Y2 = 3, + ST_RRR_0_OPCODE_X1 = 49, + SUBXSC_RRR_0_OPCODE_X0 = 79, + SUBXSC_RRR_0_OPCODE_X1 = 50, + SUBX_RRR_0_OPCODE_X0 = 80, + SUBX_RRR_0_OPCODE_X1 = 51, + SUBX_RRR_0_OPCODE_Y0 = 2, + SUBX_RRR_0_OPCODE_Y1 = 2, + SUB_RRR_0_OPCODE_X0 = 81, + SUB_RRR_0_OPCODE_X1 = 52, + SUB_RRR_0_OPCODE_Y0 = 3, + SUB_RRR_0_OPCODE_Y1 = 3, + SWINT0_UNARY_OPCODE_X1 = 34, + SWINT1_UNARY_OPCODE_X1 = 35, + SWINT2_UNARY_OPCODE_X1 = 36, + SWINT3_UNARY_OPCODE_X1 = 37, + TBLIDXB0_UNARY_OPCODE_X0 = 9, + TBLIDXB0_UNARY_OPCODE_Y0 = 9, + TBLIDXB1_UNARY_OPCODE_X0 = 10, + TBLIDXB1_UNARY_OPCODE_Y0 = 10, + TBLIDXB2_UNARY_OPCODE_X0 = 11, + TBLIDXB2_UNARY_OPCODE_Y0 = 11, + TBLIDXB3_UNARY_OPCODE_X0 = 12, + TBLIDXB3_UNARY_OPCODE_Y0 = 12, + UNARY_RRR_0_OPCODE_X0 = 82, + UNARY_RRR_0_OPCODE_X1 = 53, + UNARY_RRR_1_OPCODE_Y0 = 3, + UNARY_RRR_1_OPCODE_Y1 = 3, + V1ADDI_IMM8_OPCODE_X0 = 8, + V1ADDI_IMM8_OPCODE_X1 = 33, + V1ADDUC_RRR_0_OPCODE_X0 = 83, + V1ADDUC_RRR_0_OPCODE_X1 = 54, + V1ADD_RRR_0_OPCODE_X0 = 84, + V1ADD_RRR_0_OPCODE_X1 = 55, + V1ADIFFU_RRR_0_OPCODE_X0 = 85, + V1AVGU_RRR_0_OPCODE_X0 = 86, + V1CMPEQI_IMM8_OPCODE_X0 = 9, + V1CMPEQI_IMM8_OPCODE_X1 = 34, + V1CMPEQ_RRR_0_OPCODE_X0 = 87, + V1CMPEQ_RRR_0_OPCODE_X1 = 56, + V1CMPLES_RRR_0_OPCODE_X0 = 88, + V1CMPLES_RRR_0_OPCODE_X1 = 57, + V1CMPLEU_RRR_0_OPCODE_X0 = 89, + V1CMPLEU_RRR_0_OPCODE_X1 = 58, + V1CMPLTSI_IMM8_OPCODE_X0 = 10, + V1CMPLTSI_IMM8_OPCODE_X1 = 35, + V1CMPLTS_RRR_0_OPCODE_X0 = 90, + V1CMPLTS_RRR_0_OPCODE_X1 = 59, + V1CMPLTUI_IMM8_OPCODE_X0 = 11, + V1CMPLTUI_IMM8_OPCODE_X1 = 36, + V1CMPLTU_RRR_0_OPCODE_X0 = 91, + V1CMPLTU_RRR_0_OPCODE_X1 = 60, + V1CMPNE_RRR_0_OPCODE_X0 = 92, + V1CMPNE_RRR_0_OPCODE_X1 = 61, + V1DDOTPUA_RRR_0_OPCODE_X0 = 161, + V1DDOTPUSA_RRR_0_OPCODE_X0 = 93, + V1DDOTPUS_RRR_0_OPCODE_X0 = 94, + V1DDOTPU_RRR_0_OPCODE_X0 = 162, + V1DOTPA_RRR_0_OPCODE_X0 = 95, + V1DOTPUA_RRR_0_OPCODE_X0 = 163, + V1DOTPUSA_RRR_0_OPCODE_X0 = 96, + V1DOTPUS_RRR_0_OPCODE_X0 = 97, + V1DOTPU_RRR_0_OPCODE_X0 = 164, + V1DOTP_RRR_0_OPCODE_X0 = 98, + V1INT_H_RRR_0_OPCODE_X0 = 99, + V1INT_H_RRR_0_OPCODE_X1 = 62, + V1INT_L_RRR_0_OPCODE_X0 = 100, + V1INT_L_RRR_0_OPCODE_X1 = 63, + V1MAXUI_IMM8_OPCODE_X0 = 12, + V1MAXUI_IMM8_OPCODE_X1 = 37, + V1MAXU_RRR_0_OPCODE_X0 = 101, + V1MAXU_RRR_0_OPCODE_X1 = 64, + V1MINUI_IMM8_OPCODE_X0 = 13, + V1MINUI_IMM8_OPCODE_X1 = 38, + V1MINU_RRR_0_OPCODE_X0 = 102, + V1MINU_RRR_0_OPCODE_X1 = 65, + V1MNZ_RRR_0_OPCODE_X0 = 103, + V1MNZ_RRR_0_OPCODE_X1 = 66, + V1MULTU_RRR_0_OPCODE_X0 = 104, + V1MULUS_RRR_0_OPCODE_X0 = 105, + V1MULU_RRR_0_OPCODE_X0 = 106, + V1MZ_RRR_0_OPCODE_X0 = 107, + V1MZ_RRR_0_OPCODE_X1 = 67, + V1SADAU_RRR_0_OPCODE_X0 = 108, + V1SADU_RRR_0_OPCODE_X0 = 109, + V1SHLI_SHIFT_OPCODE_X0 = 7, + V1SHLI_SHIFT_OPCODE_X1 = 7, + V1SHL_RRR_0_OPCODE_X0 = 110, + V1SHL_RRR_0_OPCODE_X1 = 68, + V1SHRSI_SHIFT_OPCODE_X0 = 8, + V1SHRSI_SHIFT_OPCODE_X1 = 8, + V1SHRS_RRR_0_OPCODE_X0 = 111, + V1SHRS_RRR_0_OPCODE_X1 = 69, + V1SHRUI_SHIFT_OPCODE_X0 = 9, + V1SHRUI_SHIFT_OPCODE_X1 = 9, + V1SHRU_RRR_0_OPCODE_X0 = 112, + V1SHRU_RRR_0_OPCODE_X1 = 70, + V1SUBUC_RRR_0_OPCODE_X0 = 113, + V1SUBUC_RRR_0_OPCODE_X1 = 71, + V1SUB_RRR_0_OPCODE_X0 = 114, + V1SUB_RRR_0_OPCODE_X1 = 72, + V2ADDI_IMM8_OPCODE_X0 = 14, + V2ADDI_IMM8_OPCODE_X1 = 39, + V2ADDSC_RRR_0_OPCODE_X0 = 115, + V2ADDSC_RRR_0_OPCODE_X1 = 73, + V2ADD_RRR_0_OPCODE_X0 = 116, + V2ADD_RRR_0_OPCODE_X1 = 74, + V2ADIFFS_RRR_0_OPCODE_X0 = 117, + V2AVGS_RRR_0_OPCODE_X0 = 118, + V2CMPEQI_IMM8_OPCODE_X0 = 15, + V2CMPEQI_IMM8_OPCODE_X1 = 40, + V2CMPEQ_RRR_0_OPCODE_X0 = 119, + V2CMPEQ_RRR_0_OPCODE_X1 = 75, + V2CMPLES_RRR_0_OPCODE_X0 = 120, + V2CMPLES_RRR_0_OPCODE_X1 = 76, + V2CMPLEU_RRR_0_OPCODE_X0 = 121, + V2CMPLEU_RRR_0_OPCODE_X1 = 77, + V2CMPLTSI_IMM8_OPCODE_X0 = 16, + V2CMPLTSI_IMM8_OPCODE_X1 = 41, + V2CMPLTS_RRR_0_OPCODE_X0 = 122, + V2CMPLTS_RRR_0_OPCODE_X1 = 78, + V2CMPLTUI_IMM8_OPCODE_X0 = 17, + V2CMPLTUI_IMM8_OPCODE_X1 = 42, + V2CMPLTU_RRR_0_OPCODE_X0 = 123, + V2CMPLTU_RRR_0_OPCODE_X1 = 79, + V2CMPNE_RRR_0_OPCODE_X0 = 124, + V2CMPNE_RRR_0_OPCODE_X1 = 80, + V2DOTPA_RRR_0_OPCODE_X0 = 125, + V2DOTP_RRR_0_OPCODE_X0 = 126, + V2INT_H_RRR_0_OPCODE_X0 = 127, + V2INT_H_RRR_0_OPCODE_X1 = 81, + V2INT_L_RRR_0_OPCODE_X0 = 128, + V2INT_L_RRR_0_OPCODE_X1 = 82, + V2MAXSI_IMM8_OPCODE_X0 = 18, + V2MAXSI_IMM8_OPCODE_X1 = 43, + V2MAXS_RRR_0_OPCODE_X0 = 129, + V2MAXS_RRR_0_OPCODE_X1 = 83, + V2MINSI_IMM8_OPCODE_X0 = 19, + V2MINSI_IMM8_OPCODE_X1 = 44, + V2MINS_RRR_0_OPCODE_X0 = 130, + V2MINS_RRR_0_OPCODE_X1 = 84, + V2MNZ_RRR_0_OPCODE_X0 = 131, + V2MNZ_RRR_0_OPCODE_X1 = 85, + V2MULFSC_RRR_0_OPCODE_X0 = 132, + V2MULS_RRR_0_OPCODE_X0 = 133, + V2MULTS_RRR_0_OPCODE_X0 = 134, + V2MZ_RRR_0_OPCODE_X0 = 135, + V2MZ_RRR_0_OPCODE_X1 = 86, + V2PACKH_RRR_0_OPCODE_X0 = 136, + V2PACKH_RRR_0_OPCODE_X1 = 87, + V2PACKL_RRR_0_OPCODE_X0 = 137, + V2PACKL_RRR_0_OPCODE_X1 = 88, + V2PACKUC_RRR_0_OPCODE_X0 = 138, + V2PACKUC_RRR_0_OPCODE_X1 = 89, + V2SADAS_RRR_0_OPCODE_X0 = 139, + V2SADAU_RRR_0_OPCODE_X0 = 140, + V2SADS_RRR_0_OPCODE_X0 = 141, + V2SADU_RRR_0_OPCODE_X0 = 142, + V2SHLI_SHIFT_OPCODE_X0 = 10, + V2SHLI_SHIFT_OPCODE_X1 = 10, + V2SHLSC_RRR_0_OPCODE_X0 = 143, + V2SHLSC_RRR_0_OPCODE_X1 = 90, + V2SHL_RRR_0_OPCODE_X0 = 144, + V2SHL_RRR_0_OPCODE_X1 = 91, + V2SHRSI_SHIFT_OPCODE_X0 = 11, + V2SHRSI_SHIFT_OPCODE_X1 = 11, + V2SHRS_RRR_0_OPCODE_X0 = 145, + V2SHRS_RRR_0_OPCODE_X1 = 92, + V2SHRUI_SHIFT_OPCODE_X0 = 12, + V2SHRUI_SHIFT_OPCODE_X1 = 12, + V2SHRU_RRR_0_OPCODE_X0 = 146, + V2SHRU_RRR_0_OPCODE_X1 = 93, + V2SUBSC_RRR_0_OPCODE_X0 = 147, + V2SUBSC_RRR_0_OPCODE_X1 = 94, + V2SUB_RRR_0_OPCODE_X0 = 148, + V2SUB_RRR_0_OPCODE_X1 = 95, + V4ADDSC_RRR_0_OPCODE_X0 = 149, + V4ADDSC_RRR_0_OPCODE_X1 = 96, + V4ADD_RRR_0_OPCODE_X0 = 150, + V4ADD_RRR_0_OPCODE_X1 = 97, + V4INT_H_RRR_0_OPCODE_X0 = 151, + V4INT_H_RRR_0_OPCODE_X1 = 98, + V4INT_L_RRR_0_OPCODE_X0 = 152, + V4INT_L_RRR_0_OPCODE_X1 = 99, + V4PACKSC_RRR_0_OPCODE_X0 = 153, + V4PACKSC_RRR_0_OPCODE_X1 = 100, + V4SHLSC_RRR_0_OPCODE_X0 = 154, + V4SHLSC_RRR_0_OPCODE_X1 = 101, + V4SHL_RRR_0_OPCODE_X0 = 155, + V4SHL_RRR_0_OPCODE_X1 = 102, + V4SHRS_RRR_0_OPCODE_X0 = 156, + V4SHRS_RRR_0_OPCODE_X1 = 103, + V4SHRU_RRR_0_OPCODE_X0 = 157, + V4SHRU_RRR_0_OPCODE_X1 = 104, + V4SUBSC_RRR_0_OPCODE_X0 = 158, + V4SUBSC_RRR_0_OPCODE_X1 = 105, + V4SUB_RRR_0_OPCODE_X0 = 159, + V4SUB_RRR_0_OPCODE_X1 = 106, + WH64_UNARY_OPCODE_X1 = 38, + XORI_IMM8_OPCODE_X0 = 20, + XORI_IMM8_OPCODE_X1 = 45, + XOR_RRR_0_OPCODE_X0 = 160, + XOR_RRR_0_OPCODE_X1 = 107, + XOR_RRR_5_OPCODE_Y0 = 3, + XOR_RRR_5_OPCODE_Y1 = 3 +}; + + +#endif /* __ASSEMBLER__ */ + +#endif /* __ARCH_OPCODE_H__ */ diff --git a/target-tilegx/simd_helper.c b/target-tilegx/simd_helper.c new file mode 100644 index 0000000000..b9319292f3 --- /dev/null +++ b/target-tilegx/simd_helper.c @@ -0,0 +1,55 @@ +/* + * QEMU TILE-Gx helpers + * + * Copyright (c) 2015 Chen Gang + * + * 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/lgpl-2.1.html> + */ + +#include "cpu.h" +#include "qemu-common.h" +#include "exec/helper-proto.h" + + +uint64_t helper_v1shl(uint64_t a, uint64_t b) +{ + uint64_t m; + + b &= 7; + m = 0x0101010101010101ULL * (0xff >> b); + return (a & m) << b; +} + +uint64_t helper_v1shru(uint64_t a, uint64_t b) +{ + uint64_t m; + + b &= 7; + m = 0x0101010101010101ULL * ((0xff << b) & 0xff); + return (a & m) >> b; +} + +uint64_t helper_v1shrs(uint64_t a, uint64_t b) +{ + uint64_t r = 0; + int i; + + b &= 7; + for (i = 0; i < 64; i += 8) { + int64_t ae = (int8_t)(a >> i); + r |= ((ae >> b) & 0xff) << i; + } + return r; +} diff --git a/target-tilegx/spr_def_64.h b/target-tilegx/spr_def_64.h new file mode 100644 index 0000000000..67a6c1751e --- /dev/null +++ b/target-tilegx/spr_def_64.h @@ -0,0 +1,216 @@ +/* + * Copyright 2011 Tilera Corporation. All Rights Reserved. + * + * 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, version 2. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for + * more details. + */ + +#ifndef __DOXYGEN__ + +#ifndef __ARCH_SPR_DEF_64_H__ +#define __ARCH_SPR_DEF_64_H__ + +#define SPR_AUX_PERF_COUNT_0 0x2105 +#define SPR_AUX_PERF_COUNT_1 0x2106 +#define SPR_AUX_PERF_COUNT_CTL 0x2107 +#define SPR_AUX_PERF_COUNT_STS 0x2108 +#define SPR_CMPEXCH_VALUE 0x2780 +#define SPR_CYCLE 0x2781 +#define SPR_DONE 0x2705 +#define SPR_DSTREAM_PF 0x2706 +#define SPR_EVENT_BEGIN 0x2782 +#define SPR_EVENT_END 0x2783 +#define SPR_EX_CONTEXT_0_0 0x2580 +#define SPR_EX_CONTEXT_0_1 0x2581 +#define SPR_EX_CONTEXT_0_1__PL_SHIFT 0 +#define SPR_EX_CONTEXT_0_1__PL_RMASK 0x3 +#define SPR_EX_CONTEXT_0_1__PL_MASK 0x3 +#define SPR_EX_CONTEXT_0_1__ICS_SHIFT 2 +#define SPR_EX_CONTEXT_0_1__ICS_RMASK 0x1 +#define SPR_EX_CONTEXT_0_1__ICS_MASK 0x4 +#define SPR_EX_CONTEXT_1_0 0x2480 +#define SPR_EX_CONTEXT_1_1 0x2481 +#define SPR_EX_CONTEXT_1_1__PL_SHIFT 0 +#define SPR_EX_CONTEXT_1_1__PL_RMASK 0x3 +#define SPR_EX_CONTEXT_1_1__PL_MASK 0x3 +#define SPR_EX_CONTEXT_1_1__ICS_SHIFT 2 +#define SPR_EX_CONTEXT_1_1__ICS_RMASK 0x1 +#define SPR_EX_CONTEXT_1_1__ICS_MASK 0x4 +#define SPR_EX_CONTEXT_2_0 0x2380 +#define SPR_EX_CONTEXT_2_1 0x2381 +#define SPR_EX_CONTEXT_2_1__PL_SHIFT 0 +#define SPR_EX_CONTEXT_2_1__PL_RMASK 0x3 +#define SPR_EX_CONTEXT_2_1__PL_MASK 0x3 +#define SPR_EX_CONTEXT_2_1__ICS_SHIFT 2 +#define SPR_EX_CONTEXT_2_1__ICS_RMASK 0x1 +#define SPR_EX_CONTEXT_2_1__ICS_MASK 0x4 +#define SPR_FAIL 0x2707 +#define SPR_IDN_AVAIL_EN 0x1a05 +#define SPR_IDN_DATA_AVAIL 0x0a80 +#define SPR_IDN_DEADLOCK_TIMEOUT 0x1806 +#define SPR_IDN_DEMUX_COUNT_0 0x0a05 +#define SPR_IDN_DEMUX_COUNT_1 0x0a06 +#define SPR_IDN_DIRECTION_PROTECT 0x1405 +#define SPR_IDN_PENDING 0x0a08 +#define SPR_ILL_TRANS_REASON__I_STREAM_VA_RMASK 0x1 +#define SPR_INTCTRL_0_STATUS 0x2505 +#define SPR_INTCTRL_1_STATUS 0x2405 +#define SPR_INTCTRL_2_STATUS 0x2305 +#define SPR_INTERRUPT_CRITICAL_SECTION 0x2708 +#define SPR_INTERRUPT_MASK_0 0x2506 +#define SPR_INTERRUPT_MASK_1 0x2406 +#define SPR_INTERRUPT_MASK_2 0x2306 +#define SPR_INTERRUPT_MASK_RESET_0 0x2507 +#define SPR_INTERRUPT_MASK_RESET_1 0x2407 +#define SPR_INTERRUPT_MASK_RESET_2 0x2307 +#define SPR_INTERRUPT_MASK_SET_0 0x2508 +#define SPR_INTERRUPT_MASK_SET_1 0x2408 +#define SPR_INTERRUPT_MASK_SET_2 0x2308 +#define SPR_INTERRUPT_VECTOR_BASE_0 0x2509 +#define SPR_INTERRUPT_VECTOR_BASE_1 0x2409 +#define SPR_INTERRUPT_VECTOR_BASE_2 0x2309 +#define SPR_INTERRUPT_VECTOR_BASE_3 0x2209 +#define SPR_IPI_EVENT_0 0x1f05 +#define SPR_IPI_EVENT_1 0x1e05 +#define SPR_IPI_EVENT_2 0x1d05 +#define SPR_IPI_EVENT_RESET_0 0x1f06 +#define SPR_IPI_EVENT_RESET_1 0x1e06 +#define SPR_IPI_EVENT_RESET_2 0x1d06 +#define SPR_IPI_EVENT_SET_0 0x1f07 +#define SPR_IPI_EVENT_SET_1 0x1e07 +#define SPR_IPI_EVENT_SET_2 0x1d07 +#define SPR_IPI_MASK_0 0x1f08 +#define SPR_IPI_MASK_1 0x1e08 +#define SPR_IPI_MASK_2 0x1d08 +#define SPR_IPI_MASK_RESET_0 0x1f09 +#define SPR_IPI_MASK_RESET_1 0x1e09 +#define SPR_IPI_MASK_RESET_2 0x1d09 +#define SPR_IPI_MASK_SET_0 0x1f0a +#define SPR_IPI_MASK_SET_1 0x1e0a +#define SPR_IPI_MASK_SET_2 0x1d0a +#define SPR_MPL_AUX_PERF_COUNT_SET_0 0x2100 +#define SPR_MPL_AUX_PERF_COUNT_SET_1 0x2101 +#define SPR_MPL_AUX_PERF_COUNT_SET_2 0x2102 +#define SPR_MPL_AUX_TILE_TIMER_SET_0 0x1700 +#define SPR_MPL_AUX_TILE_TIMER_SET_1 0x1701 +#define SPR_MPL_AUX_TILE_TIMER_SET_2 0x1702 +#define SPR_MPL_IDN_ACCESS_SET_0 0x0a00 +#define SPR_MPL_IDN_ACCESS_SET_1 0x0a01 +#define SPR_MPL_IDN_ACCESS_SET_2 0x0a02 +#define SPR_MPL_IDN_AVAIL_SET_0 0x1a00 +#define SPR_MPL_IDN_AVAIL_SET_1 0x1a01 +#define SPR_MPL_IDN_AVAIL_SET_2 0x1a02 +#define SPR_MPL_IDN_COMPLETE_SET_0 0x0500 +#define SPR_MPL_IDN_COMPLETE_SET_1 0x0501 +#define SPR_MPL_IDN_COMPLETE_SET_2 0x0502 +#define SPR_MPL_IDN_FIREWALL_SET_0 0x1400 +#define SPR_MPL_IDN_FIREWALL_SET_1 0x1401 +#define SPR_MPL_IDN_FIREWALL_SET_2 0x1402 +#define SPR_MPL_IDN_TIMER_SET_0 0x1800 +#define SPR_MPL_IDN_TIMER_SET_1 0x1801 +#define SPR_MPL_IDN_TIMER_SET_2 0x1802 +#define SPR_MPL_INTCTRL_0_SET_0 0x2500 +#define SPR_MPL_INTCTRL_0_SET_1 0x2501 +#define SPR_MPL_INTCTRL_0_SET_2 0x2502 +#define SPR_MPL_INTCTRL_1_SET_0 0x2400 +#define SPR_MPL_INTCTRL_1_SET_1 0x2401 +#define SPR_MPL_INTCTRL_1_SET_2 0x2402 +#define SPR_MPL_INTCTRL_2_SET_0 0x2300 +#define SPR_MPL_INTCTRL_2_SET_1 0x2301 +#define SPR_MPL_INTCTRL_2_SET_2 0x2302 +#define SPR_MPL_IPI_0 0x1f04 +#define SPR_MPL_IPI_0_SET_0 0x1f00 +#define SPR_MPL_IPI_0_SET_1 0x1f01 +#define SPR_MPL_IPI_0_SET_2 0x1f02 +#define SPR_MPL_IPI_1 0x1e04 +#define SPR_MPL_IPI_1_SET_0 0x1e00 +#define SPR_MPL_IPI_1_SET_1 0x1e01 +#define SPR_MPL_IPI_1_SET_2 0x1e02 +#define SPR_MPL_IPI_2 0x1d04 +#define SPR_MPL_IPI_2_SET_0 0x1d00 +#define SPR_MPL_IPI_2_SET_1 0x1d01 +#define SPR_MPL_IPI_2_SET_2 0x1d02 +#define SPR_MPL_PERF_COUNT_SET_0 0x2000 +#define SPR_MPL_PERF_COUNT_SET_1 0x2001 +#define SPR_MPL_PERF_COUNT_SET_2 0x2002 +#define SPR_MPL_UDN_ACCESS_SET_0 0x0b00 +#define SPR_MPL_UDN_ACCESS_SET_1 0x0b01 +#define SPR_MPL_UDN_ACCESS_SET_2 0x0b02 +#define SPR_MPL_UDN_AVAIL_SET_0 0x1b00 +#define SPR_MPL_UDN_AVAIL_SET_1 0x1b01 +#define SPR_MPL_UDN_AVAIL_SET_2 0x1b02 +#define SPR_MPL_UDN_COMPLETE_SET_0 0x0600 +#define SPR_MPL_UDN_COMPLETE_SET_1 0x0601 +#define SPR_MPL_UDN_COMPLETE_SET_2 0x0602 +#define SPR_MPL_UDN_FIREWALL_SET_0 0x1500 +#define SPR_MPL_UDN_FIREWALL_SET_1 0x1501 +#define SPR_MPL_UDN_FIREWALL_SET_2 0x1502 +#define SPR_MPL_UDN_TIMER_SET_0 0x1900 +#define SPR_MPL_UDN_TIMER_SET_1 0x1901 +#define SPR_MPL_UDN_TIMER_SET_2 0x1902 +#define SPR_MPL_WORLD_ACCESS_SET_0 0x2700 +#define SPR_MPL_WORLD_ACCESS_SET_1 0x2701 +#define SPR_MPL_WORLD_ACCESS_SET_2 0x2702 +#define SPR_PASS 0x2709 +#define SPR_PERF_COUNT_0 0x2005 +#define SPR_PERF_COUNT_1 0x2006 +#define SPR_PERF_COUNT_CTL 0x2007 +#define SPR_PERF_COUNT_DN_CTL 0x2008 +#define SPR_PERF_COUNT_STS 0x2009 +#define SPR_PROC_STATUS 0x2784 +#define SPR_SIM_CONTROL 0x2785 +#define SPR_SINGLE_STEP_CONTROL_0 0x0405 +#define SPR_SINGLE_STEP_CONTROL_0__CANCELED_MASK 0x1 +#define SPR_SINGLE_STEP_CONTROL_0__INHIBIT_MASK 0x2 +#define SPR_SINGLE_STEP_CONTROL_1 0x0305 +#define SPR_SINGLE_STEP_CONTROL_1__CANCELED_MASK 0x1 +#define SPR_SINGLE_STEP_CONTROL_1__INHIBIT_MASK 0x2 +#define SPR_SINGLE_STEP_CONTROL_2 0x0205 +#define SPR_SINGLE_STEP_CONTROL_2__CANCELED_MASK 0x1 +#define SPR_SINGLE_STEP_CONTROL_2__INHIBIT_MASK 0x2 +#define SPR_SINGLE_STEP_EN_0_0 0x250a +#define SPR_SINGLE_STEP_EN_0_1 0x240a +#define SPR_SINGLE_STEP_EN_0_2 0x230a +#define SPR_SINGLE_STEP_EN_1_0 0x250b +#define SPR_SINGLE_STEP_EN_1_1 0x240b +#define SPR_SINGLE_STEP_EN_1_2 0x230b +#define SPR_SINGLE_STEP_EN_2_0 0x250c +#define SPR_SINGLE_STEP_EN_2_1 0x240c +#define SPR_SINGLE_STEP_EN_2_2 0x230c +#define SPR_SYSTEM_SAVE_0_0 0x2582 +#define SPR_SYSTEM_SAVE_0_1 0x2583 +#define SPR_SYSTEM_SAVE_0_2 0x2584 +#define SPR_SYSTEM_SAVE_0_3 0x2585 +#define SPR_SYSTEM_SAVE_1_0 0x2482 +#define SPR_SYSTEM_SAVE_1_1 0x2483 +#define SPR_SYSTEM_SAVE_1_2 0x2484 +#define SPR_SYSTEM_SAVE_1_3 0x2485 +#define SPR_SYSTEM_SAVE_2_0 0x2382 +#define SPR_SYSTEM_SAVE_2_1 0x2383 +#define SPR_SYSTEM_SAVE_2_2 0x2384 +#define SPR_SYSTEM_SAVE_2_3 0x2385 +#define SPR_TILE_COORD 0x270b +#define SPR_TILE_RTF_HWM 0x270c +#define SPR_TILE_TIMER_CONTROL 0x1605 +#define SPR_UDN_AVAIL_EN 0x1b05 +#define SPR_UDN_DATA_AVAIL 0x0b80 +#define SPR_UDN_DEADLOCK_TIMEOUT 0x1906 +#define SPR_UDN_DEMUX_COUNT_0 0x0b05 +#define SPR_UDN_DEMUX_COUNT_1 0x0b06 +#define SPR_UDN_DEMUX_COUNT_2 0x0b07 +#define SPR_UDN_DEMUX_COUNT_3 0x0b08 +#define SPR_UDN_DIRECTION_PROTECT 0x1505 +#define SPR_UDN_PENDING 0x0b0a +#define SPR_WATCH_MASK 0x200a +#define SPR_WATCH_VAL 0x200b + +#endif /* !defined(__ARCH_SPR_DEF_64_H__) */ + +#endif /* !defined(__DOXYGEN__) */ diff --git a/target-tilegx/translate.c b/target-tilegx/translate.c new file mode 100644 index 0000000000..e70c3e5ab7 --- /dev/null +++ b/target-tilegx/translate.c @@ -0,0 +1,2161 @@ +/* + * QEMU TILE-Gx CPU + * + * Copyright (c) 2015 Chen Gang + * + * 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/lgpl-2.1.html> + */ + +#include "cpu.h" +#include "qemu/log.h" +#include "disas/disas.h" +#include "tcg-op.h" +#include "exec/cpu_ldst.h" +#include "opcode_tilegx.h" +#include "spr_def_64.h" + +#define FMT64X "%016" PRIx64 + +static TCGv_ptr cpu_env; +static TCGv cpu_pc; +static TCGv cpu_regs[TILEGX_R_COUNT]; + +static const char * const reg_names[64] = { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", + "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", + "r32", "r33", "r34", "r35", "r36", "r37", "r38", "r39", + "r40", "r41", "r42", "r43", "r44", "r45", "r46", "r47", + "r48", "r49", "r50", "r51", "bp", "tp", "sp", "lr", + "sn", "idn0", "idn1", "udn0", "udn1", "udn2", "udn2", "zero" +}; + +/* Modified registers are cached in temporaries until the end of the bundle. */ +typedef struct { + unsigned reg; + TCGv val; +} DisasContextTemp; + +#define MAX_WRITEBACK 4 + +/* This is the state at translation time. */ +typedef struct { + uint64_t pc; /* Current pc */ + + TCGv zero; /* For zero register */ + + DisasContextTemp wb[MAX_WRITEBACK]; + int num_wb; + int mmuidx; + bool exit_tb; + TileExcp atomic_excp; + + struct { + TCGCond cond; /* branch condition */ + TCGv dest; /* branch destination */ + TCGv val1; /* value to be compared against zero, for cond */ + } jmp; /* Jump object, only once in each TB block */ +} DisasContext; + +#include "exec/gen-icount.h" + +/* Differentiate the various pipe encodings. */ +#define TY_X0 0 +#define TY_X1 1 +#define TY_Y0 2 +#define TY_Y1 3 + +/* Remerge the base opcode and extension fields for switching. + The X opcode fields are 3 bits; Y0/Y1 opcode fields are 4 bits; + Y2 opcode field is 2 bits. */ +#define OE(OP, EXT, XY) (TY_##XY + OP * 4 + EXT * 64) + +/* Similar, but for Y2 only. */ +#define OEY2(OP, MODE) (OP + MODE * 4) + +/* Similar, but make sure opcode names match up. */ +#define OE_RR_X0(E) OE(RRR_0_OPCODE_X0, E##_UNARY_OPCODE_X0, X0) +#define OE_RR_X1(E) OE(RRR_0_OPCODE_X1, E##_UNARY_OPCODE_X1, X1) +#define OE_RR_Y0(E) OE(RRR_1_OPCODE_Y0, E##_UNARY_OPCODE_Y0, Y0) +#define OE_RR_Y1(E) OE(RRR_1_OPCODE_Y1, E##_UNARY_OPCODE_Y1, Y1) +#define OE_RRR(E,N,XY) OE(RRR_##N##_OPCODE_##XY, E##_RRR_##N##_OPCODE_##XY, XY) +#define OE_IM(E,XY) OE(IMM8_OPCODE_##XY, E##_IMM8_OPCODE_##XY, XY) +#define OE_SH(E,XY) OE(SHIFT_OPCODE_##XY, E##_SHIFT_OPCODE_##XY, XY) + +#define V1_IMM(X) (((X) & 0xff) * 0x0101010101010101ull) + + +static void gen_exception(DisasContext *dc, TileExcp num) +{ + TCGv_i32 tmp; + + tcg_gen_movi_tl(cpu_pc, dc->pc + TILEGX_BUNDLE_SIZE_IN_BYTES); + + tmp = tcg_const_i32(num); + gen_helper_exception(cpu_env, tmp); + tcg_temp_free_i32(tmp); + dc->exit_tb = true; +} + +static bool check_gr(DisasContext *dc, uint8_t reg) +{ + if (likely(reg < TILEGX_R_COUNT)) { + return true; + } + + switch (reg) { + case TILEGX_R_SN: + case TILEGX_R_ZERO: + break; + case TILEGX_R_IDN0: + case TILEGX_R_IDN1: + gen_exception(dc, TILEGX_EXCP_REG_IDN_ACCESS); + break; + case TILEGX_R_UDN0: + case TILEGX_R_UDN1: + case TILEGX_R_UDN2: + case TILEGX_R_UDN3: + gen_exception(dc, TILEGX_EXCP_REG_UDN_ACCESS); + break; + default: + g_assert_not_reached(); + } + return false; +} + +static TCGv load_zero(DisasContext *dc) +{ + if (TCGV_IS_UNUSED_I64(dc->zero)) { + dc->zero = tcg_const_i64(0); + } + return dc->zero; +} + +static TCGv load_gr(DisasContext *dc, unsigned reg) +{ + if (check_gr(dc, reg)) { + return cpu_regs[reg]; + } + return load_zero(dc); +} + +static TCGv dest_gr(DisasContext *dc, unsigned reg) +{ + int n; + + /* Skip the result, mark the exception if necessary, and continue */ + check_gr(dc, reg); + + n = dc->num_wb++; + dc->wb[n].reg = reg; + return dc->wb[n].val = tcg_temp_new_i64(); +} + +static void gen_saturate_op(TCGv tdest, TCGv tsrca, TCGv tsrcb, + void (*operate)(TCGv, TCGv, TCGv)) +{ + TCGv t0 = tcg_temp_new(); + + tcg_gen_ext32s_tl(tdest, tsrca); + tcg_gen_ext32s_tl(t0, tsrcb); + operate(tdest, tdest, t0); + + tcg_gen_movi_tl(t0, 0x7fffffff); + tcg_gen_movcond_tl(TCG_COND_GT, tdest, tdest, t0, t0, tdest); + tcg_gen_movi_tl(t0, -0x80000000LL); + tcg_gen_movcond_tl(TCG_COND_LT, tdest, tdest, t0, t0, tdest); + + tcg_temp_free(t0); +} + +static void gen_atomic_excp(DisasContext *dc, unsigned dest, TCGv tdest, + TCGv tsrca, TCGv tsrcb, TileExcp excp) +{ +#ifdef CONFIG_USER_ONLY + TCGv_i32 t; + + tcg_gen_st_tl(tsrca, cpu_env, offsetof(CPUTLGState, atomic_srca)); + tcg_gen_st_tl(tsrcb, cpu_env, offsetof(CPUTLGState, atomic_srcb)); + t = tcg_const_i32(dest); + tcg_gen_st_i32(t, cpu_env, offsetof(CPUTLGState, atomic_dstr)); + tcg_temp_free_i32(t); + + /* We're going to write the real result in the exception. But in + the meantime we've already created a writeback register, and + we don't want that to remain uninitialized. */ + tcg_gen_movi_tl(tdest, 0); + + /* Note that we need to delay issuing the exception that implements + the atomic operation until after writing back the results of the + instruction occupying the X0 pipe. */ + dc->atomic_excp = excp; +#else + gen_exception(dc, TILEGX_EXCP_OPCODE_UNIMPLEMENTED); +#endif +} + +/* Shift the 128-bit value TSRCA:TSRCD right by the number of bytes + specified by the bottom 3 bits of TSRCB, and set TDEST to the + low 64 bits of the resulting value. */ +static void gen_dblalign(TCGv tdest, TCGv tsrcd, TCGv tsrca, TCGv tsrcb) +{ + TCGv t0 = tcg_temp_new(); + + tcg_gen_andi_tl(t0, tsrcb, 7); + tcg_gen_shli_tl(t0, t0, 3); + tcg_gen_shr_tl(tdest, tsrcd, t0); + + /* We want to do "t0 = tsrca << (64 - t0)". Two's complement + arithmetic on a 6-bit field tells us that 64 - t0 is equal + to (t0 ^ 63) + 1. So we can do the shift in two parts, + neither of which will be an invalid shift by 64. */ + tcg_gen_xori_tl(t0, t0, 63); + tcg_gen_shl_tl(t0, tsrca, t0); + tcg_gen_shli_tl(t0, t0, 1); + tcg_gen_or_tl(tdest, tdest, t0); + + tcg_temp_free(t0); +} + +/* Similarly, except that the 128-bit value is TSRCA:TSRCB, and the + right shift is an immediate. */ +static void gen_dblaligni(TCGv tdest, TCGv tsrca, TCGv tsrcb, int shr) +{ + TCGv t0 = tcg_temp_new(); + + tcg_gen_shri_tl(t0, tsrcb, shr); + tcg_gen_shli_tl(tdest, tsrca, 64 - shr); + tcg_gen_or_tl(tdest, tdest, t0); + + tcg_temp_free(t0); +} + +typedef enum { + LU, LS, HU, HS +} MulHalf; + +static void gen_ext_half(TCGv d, TCGv s, MulHalf h) +{ + switch (h) { + case LU: + tcg_gen_ext32u_tl(d, s); + break; + case LS: + tcg_gen_ext32s_tl(d, s); + break; + case HU: + tcg_gen_shri_tl(d, s, 32); + break; + case HS: + tcg_gen_sari_tl(d, s, 32); + break; + } +} + +static void gen_mul_half(TCGv tdest, TCGv tsrca, TCGv tsrcb, + MulHalf ha, MulHalf hb) +{ + TCGv t = tcg_temp_new(); + gen_ext_half(t, tsrca, ha); + gen_ext_half(tdest, tsrcb, hb); + tcg_gen_mul_tl(tdest, tdest, t); + tcg_temp_free(t); +} + +/* Equality comparison with zero can be done quickly and efficiently. */ +static void gen_v1cmpeq0(TCGv v) +{ + TCGv m = tcg_const_tl(V1_IMM(0x7f)); + TCGv c = tcg_temp_new(); + + /* ~(((v & m) + m) | m | v). Sets the msb for each byte == 0. */ + tcg_gen_and_tl(c, v, m); + tcg_gen_add_tl(c, c, m); + tcg_gen_or_tl(c, c, m); + tcg_gen_nor_tl(c, c, v); + tcg_temp_free(m); + + /* Shift the msb down to form the lsb boolean result. */ + tcg_gen_shri_tl(v, c, 7); + tcg_temp_free(c); +} + +static void gen_v1cmpne0(TCGv v) +{ + TCGv m = tcg_const_tl(V1_IMM(0x7f)); + TCGv c = tcg_temp_new(); + + /* (((v & m) + m) | v) & ~m. Sets the msb for each byte != 0. */ + tcg_gen_and_tl(c, v, m); + tcg_gen_add_tl(c, c, m); + tcg_gen_or_tl(c, c, v); + tcg_gen_andc_tl(c, c, m); + tcg_temp_free(m); + + /* Shift the msb down to form the lsb boolean result. */ + tcg_gen_shri_tl(v, c, 7); + tcg_temp_free(c); +} + +static TileExcp gen_st_opcode(DisasContext *dc, unsigned dest, unsigned srca, + unsigned srcb, TCGMemOp memop, const char *name) +{ + if (dest) { + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + } + + tcg_gen_qemu_st_tl(load_gr(dc, srcb), load_gr(dc, srca), + dc->mmuidx, memop); + + qemu_log_mask(CPU_LOG_TB_IN_ASM, "%s %s, %s", name, + reg_names[srca], reg_names[srcb]); + return TILEGX_EXCP_NONE; +} + +static TileExcp gen_st_add_opcode(DisasContext *dc, unsigned srca, unsigned srcb, + int imm, TCGMemOp memop, const char *name) +{ + TCGv tsrca = load_gr(dc, srca); + TCGv tsrcb = load_gr(dc, srcb); + + tcg_gen_qemu_st_tl(tsrcb, tsrca, dc->mmuidx, memop); + tcg_gen_addi_tl(dest_gr(dc, srca), tsrca, imm); + + qemu_log_mask(CPU_LOG_TB_IN_ASM, "%s %s, %s, %d", name, + reg_names[srca], reg_names[srcb], imm); + return TILEGX_EXCP_NONE; +} + +static TileExcp gen_rr_opcode(DisasContext *dc, unsigned opext, + unsigned dest, unsigned srca) +{ + TCGv tdest, tsrca; + const char *mnemonic; + TCGMemOp memop; + TileExcp ret = TILEGX_EXCP_NONE; + + /* Eliminate instructions with no output before doing anything else. */ + switch (opext) { + case OE_RR_Y0(NOP): + case OE_RR_Y1(NOP): + case OE_RR_X0(NOP): + case OE_RR_X1(NOP): + mnemonic = "nop"; + goto done0; + case OE_RR_Y0(FNOP): + case OE_RR_Y1(FNOP): + case OE_RR_X0(FNOP): + case OE_RR_X1(FNOP): + mnemonic = "fnop"; + goto done0; + case OE_RR_X1(DRAIN): + mnemonic = "drain"; + goto done0; + case OE_RR_X1(FLUSHWB): + mnemonic = "flushwb"; + goto done0; + case OE_RR_X1(ILL): + case OE_RR_Y1(ILL): + mnemonic = (dest == 0x1c && srca == 0x25 ? "bpt" : "ill"); + qemu_log_mask(CPU_LOG_TB_IN_ASM, "%s", mnemonic); + return TILEGX_EXCP_OPCODE_UNKNOWN; + case OE_RR_X1(MF): + mnemonic = "mf"; + goto done0; + case OE_RR_X1(NAP): + /* ??? This should yield, especially in system mode. */ + mnemonic = "nap"; + goto done0; + case OE_RR_X1(SWINT0): + case OE_RR_X1(SWINT2): + case OE_RR_X1(SWINT3): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_RR_X1(SWINT1): + ret = TILEGX_EXCP_SYSCALL; + mnemonic = "swint1"; + done0: + if (srca || dest) { + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + } + qemu_log_mask(CPU_LOG_TB_IN_ASM, "%s", mnemonic); + return ret; + + case OE_RR_X1(DTLBPR): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_RR_X1(FINV): + mnemonic = "finv"; + goto done1; + case OE_RR_X1(FLUSH): + mnemonic = "flush"; + goto done1; + case OE_RR_X1(ICOH): + mnemonic = "icoh"; + goto done1; + case OE_RR_X1(INV): + mnemonic = "inv"; + goto done1; + case OE_RR_X1(WH64): + mnemonic = "wh64"; + goto done1; + case OE_RR_X1(JRP): + case OE_RR_Y1(JRP): + mnemonic = "jrp"; + goto do_jr; + case OE_RR_X1(JR): + case OE_RR_Y1(JR): + mnemonic = "jr"; + goto do_jr; + case OE_RR_X1(JALRP): + case OE_RR_Y1(JALRP): + mnemonic = "jalrp"; + goto do_jalr; + case OE_RR_X1(JALR): + case OE_RR_Y1(JALR): + mnemonic = "jalr"; + do_jalr: + tcg_gen_movi_tl(dest_gr(dc, TILEGX_R_LR), + dc->pc + TILEGX_BUNDLE_SIZE_IN_BYTES); + do_jr: + dc->jmp.cond = TCG_COND_ALWAYS; + dc->jmp.dest = tcg_temp_new(); + tcg_gen_andi_tl(dc->jmp.dest, load_gr(dc, srca), ~7); + done1: + if (dest) { + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + } + qemu_log_mask(CPU_LOG_TB_IN_ASM, "%s %s", mnemonic, reg_names[srca]); + return ret; + } + + tdest = dest_gr(dc, dest); + tsrca = load_gr(dc, srca); + + switch (opext) { + case OE_RR_X0(CNTLZ): + case OE_RR_Y0(CNTLZ): + gen_helper_cntlz(tdest, tsrca); + mnemonic = "cntlz"; + break; + case OE_RR_X0(CNTTZ): + case OE_RR_Y0(CNTTZ): + gen_helper_cnttz(tdest, tsrca); + mnemonic = "cnttz"; + break; + case OE_RR_X0(FSINGLE_PACK1): + case OE_RR_Y0(FSINGLE_PACK1): + case OE_RR_X1(IRET): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_RR_X1(LD1S): + memop = MO_SB; + mnemonic = "ld1s"; + goto do_load; + case OE_RR_X1(LD1U): + memop = MO_UB; + mnemonic = "ld1u"; + goto do_load; + case OE_RR_X1(LD2S): + memop = MO_TESW; + mnemonic = "ld2s"; + goto do_load; + case OE_RR_X1(LD2U): + memop = MO_TEUW; + mnemonic = "ld2u"; + goto do_load; + case OE_RR_X1(LD4S): + memop = MO_TESL; + mnemonic = "ld4s"; + goto do_load; + case OE_RR_X1(LD4U): + memop = MO_TEUL; + mnemonic = "ld4u"; + goto do_load; + case OE_RR_X1(LDNT1S): + memop = MO_SB; + mnemonic = "ldnt1s"; + goto do_load; + case OE_RR_X1(LDNT1U): + memop = MO_UB; + mnemonic = "ldnt1u"; + goto do_load; + case OE_RR_X1(LDNT2S): + memop = MO_TESW; + mnemonic = "ldnt2s"; + goto do_load; + case OE_RR_X1(LDNT2U): + memop = MO_TEUW; + mnemonic = "ldnt2u"; + goto do_load; + case OE_RR_X1(LDNT4S): + memop = MO_TESL; + mnemonic = "ldnt4s"; + goto do_load; + case OE_RR_X1(LDNT4U): + memop = MO_TEUL; + mnemonic = "ldnt4u"; + goto do_load; + case OE_RR_X1(LDNT): + memop = MO_TEQ; + mnemonic = "ldnt"; + goto do_load; + case OE_RR_X1(LD): + memop = MO_TEQ; + mnemonic = "ld"; + do_load: + tcg_gen_qemu_ld_tl(tdest, tsrca, dc->mmuidx, memop); + break; + case OE_RR_X1(LDNA): + tcg_gen_andi_tl(tdest, tsrca, ~7); + tcg_gen_qemu_ld_tl(tdest, tdest, dc->mmuidx, MO_TEQ); + mnemonic = "ldna"; + break; + case OE_RR_X1(LNK): + case OE_RR_Y1(LNK): + if (srca) { + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + } + tcg_gen_movi_tl(tdest, dc->pc + TILEGX_BUNDLE_SIZE_IN_BYTES); + mnemonic = "lnk"; + break; + case OE_RR_X0(PCNT): + case OE_RR_Y0(PCNT): + gen_helper_pcnt(tdest, tsrca); + mnemonic = "pcnt"; + break; + case OE_RR_X0(REVBITS): + case OE_RR_Y0(REVBITS): + gen_helper_revbits(tdest, tsrca); + mnemonic = "revbits"; + break; + case OE_RR_X0(REVBYTES): + case OE_RR_Y0(REVBYTES): + tcg_gen_bswap64_tl(tdest, tsrca); + mnemonic = "revbytes"; + break; + case OE_RR_X0(TBLIDXB0): + case OE_RR_Y0(TBLIDXB0): + case OE_RR_X0(TBLIDXB1): + case OE_RR_Y0(TBLIDXB1): + case OE_RR_X0(TBLIDXB2): + case OE_RR_Y0(TBLIDXB2): + case OE_RR_X0(TBLIDXB3): + case OE_RR_Y0(TBLIDXB3): + default: + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + } + + qemu_log_mask(CPU_LOG_TB_IN_ASM, "%s %s, %s", mnemonic, + reg_names[dest], reg_names[srca]); + return ret; +} + +static TileExcp gen_rrr_opcode(DisasContext *dc, unsigned opext, + unsigned dest, unsigned srca, unsigned srcb) +{ + TCGv tdest = dest_gr(dc, dest); + TCGv tsrca = load_gr(dc, srca); + TCGv tsrcb = load_gr(dc, srcb); + TCGv t0; + const char *mnemonic; + + switch (opext) { + case OE_RRR(ADDXSC, 0, X0): + case OE_RRR(ADDXSC, 0, X1): + gen_saturate_op(tdest, tsrca, tsrcb, tcg_gen_add_tl); + mnemonic = "addxsc"; + break; + case OE_RRR(ADDX, 0, X0): + case OE_RRR(ADDX, 0, X1): + case OE_RRR(ADDX, 0, Y0): + case OE_RRR(ADDX, 0, Y1): + tcg_gen_add_tl(tdest, tsrca, tsrcb); + tcg_gen_ext32s_tl(tdest, tdest); + mnemonic = "addx"; + break; + case OE_RRR(ADD, 0, X0): + case OE_RRR(ADD, 0, X1): + case OE_RRR(ADD, 0, Y0): + case OE_RRR(ADD, 0, Y1): + tcg_gen_add_tl(tdest, tsrca, tsrcb); + mnemonic = "add"; + break; + case OE_RRR(AND, 0, X0): + case OE_RRR(AND, 0, X1): + case OE_RRR(AND, 5, Y0): + case OE_RRR(AND, 5, Y1): + tcg_gen_and_tl(tdest, tsrca, tsrcb); + mnemonic = "and"; + break; + case OE_RRR(CMOVEQZ, 0, X0): + case OE_RRR(CMOVEQZ, 4, Y0): + tcg_gen_movcond_tl(TCG_COND_EQ, tdest, tsrca, load_zero(dc), + tsrcb, load_gr(dc, dest)); + mnemonic = "cmoveqz"; + break; + case OE_RRR(CMOVNEZ, 0, X0): + case OE_RRR(CMOVNEZ, 4, Y0): + tcg_gen_movcond_tl(TCG_COND_NE, tdest, tsrca, load_zero(dc), + tsrcb, load_gr(dc, dest)); + mnemonic = "cmovnez"; + break; + case OE_RRR(CMPEQ, 0, X0): + case OE_RRR(CMPEQ, 0, X1): + case OE_RRR(CMPEQ, 3, Y0): + case OE_RRR(CMPEQ, 3, Y1): + tcg_gen_setcond_tl(TCG_COND_EQ, tdest, tsrca, tsrcb); + mnemonic = "cmpeq"; + break; + case OE_RRR(CMPEXCH4, 0, X1): + gen_atomic_excp(dc, dest, tdest, tsrca, tsrcb, + TILEGX_EXCP_OPCODE_CMPEXCH4); + mnemonic = "cmpexch4"; + break; + case OE_RRR(CMPEXCH, 0, X1): + gen_atomic_excp(dc, dest, tdest, tsrca, tsrcb, + TILEGX_EXCP_OPCODE_CMPEXCH); + mnemonic = "cmpexch"; + break; + case OE_RRR(CMPLES, 0, X0): + case OE_RRR(CMPLES, 0, X1): + case OE_RRR(CMPLES, 2, Y0): + case OE_RRR(CMPLES, 2, Y1): + tcg_gen_setcond_tl(TCG_COND_LE, tdest, tsrca, tsrcb); + mnemonic = "cmples"; + break; + case OE_RRR(CMPLEU, 0, X0): + case OE_RRR(CMPLEU, 0, X1): + case OE_RRR(CMPLEU, 2, Y0): + case OE_RRR(CMPLEU, 2, Y1): + tcg_gen_setcond_tl(TCG_COND_LEU, tdest, tsrca, tsrcb); + mnemonic = "cmpleu"; + break; + case OE_RRR(CMPLTS, 0, X0): + case OE_RRR(CMPLTS, 0, X1): + case OE_RRR(CMPLTS, 2, Y0): + case OE_RRR(CMPLTS, 2, Y1): + tcg_gen_setcond_tl(TCG_COND_LT, tdest, tsrca, tsrcb); + mnemonic = "cmplts"; + break; + case OE_RRR(CMPLTU, 0, X0): + case OE_RRR(CMPLTU, 0, X1): + case OE_RRR(CMPLTU, 2, Y0): + case OE_RRR(CMPLTU, 2, Y1): + tcg_gen_setcond_tl(TCG_COND_LTU, tdest, tsrca, tsrcb); + mnemonic = "cmpltu"; + break; + case OE_RRR(CMPNE, 0, X0): + case OE_RRR(CMPNE, 0, X1): + case OE_RRR(CMPNE, 3, Y0): + case OE_RRR(CMPNE, 3, Y1): + tcg_gen_setcond_tl(TCG_COND_NE, tdest, tsrca, tsrcb); + mnemonic = "cmpne"; + break; + case OE_RRR(CMULAF, 0, X0): + case OE_RRR(CMULA, 0, X0): + case OE_RRR(CMULFR, 0, X0): + case OE_RRR(CMULF, 0, X0): + case OE_RRR(CMULHR, 0, X0): + case OE_RRR(CMULH, 0, X0): + case OE_RRR(CMUL, 0, X0): + case OE_RRR(CRC32_32, 0, X0): + case OE_RRR(CRC32_8, 0, X0): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_RRR(DBLALIGN2, 0, X0): + case OE_RRR(DBLALIGN2, 0, X1): + gen_dblaligni(tdest, tsrca, tsrcb, 16); + mnemonic = "dblalign2"; + break; + case OE_RRR(DBLALIGN4, 0, X0): + case OE_RRR(DBLALIGN4, 0, X1): + gen_dblaligni(tdest, tsrca, tsrcb, 32); + mnemonic = "dblalign4"; + break; + case OE_RRR(DBLALIGN6, 0, X0): + case OE_RRR(DBLALIGN6, 0, X1): + gen_dblaligni(tdest, tsrca, tsrcb, 48); + mnemonic = "dblalign6"; + break; + case OE_RRR(DBLALIGN, 0, X0): + gen_dblalign(tdest, load_gr(dc, dest), tsrca, tsrcb); + mnemonic = "dblalign"; + break; + case OE_RRR(EXCH4, 0, X1): + gen_atomic_excp(dc, dest, tdest, tsrca, tsrcb, + TILEGX_EXCP_OPCODE_EXCH4); + mnemonic = "exch4"; + break; + case OE_RRR(EXCH, 0, X1): + gen_atomic_excp(dc, dest, tdest, tsrca, tsrcb, + TILEGX_EXCP_OPCODE_EXCH); + mnemonic = "exch"; + break; + case OE_RRR(FDOUBLE_ADDSUB, 0, X0): + case OE_RRR(FDOUBLE_ADD_FLAGS, 0, X0): + case OE_RRR(FDOUBLE_MUL_FLAGS, 0, X0): + case OE_RRR(FDOUBLE_PACK1, 0, X0): + case OE_RRR(FDOUBLE_PACK2, 0, X0): + case OE_RRR(FDOUBLE_SUB_FLAGS, 0, X0): + case OE_RRR(FDOUBLE_UNPACK_MAX, 0, X0): + case OE_RRR(FDOUBLE_UNPACK_MIN, 0, X0): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_RRR(FETCHADD4, 0, X1): + gen_atomic_excp(dc, dest, tdest, tsrca, tsrcb, + TILEGX_EXCP_OPCODE_FETCHADD4); + mnemonic = "fetchadd4"; + break; + case OE_RRR(FETCHADDGEZ4, 0, X1): + gen_atomic_excp(dc, dest, tdest, tsrca, tsrcb, + TILEGX_EXCP_OPCODE_FETCHADDGEZ4); + mnemonic = "fetchaddgez4"; + break; + case OE_RRR(FETCHADDGEZ, 0, X1): + gen_atomic_excp(dc, dest, tdest, tsrca, tsrcb, + TILEGX_EXCP_OPCODE_FETCHADDGEZ); + mnemonic = "fetchaddgez"; + break; + case OE_RRR(FETCHADD, 0, X1): + gen_atomic_excp(dc, dest, tdest, tsrca, tsrcb, + TILEGX_EXCP_OPCODE_FETCHADD); + mnemonic = "fetchadd"; + break; + case OE_RRR(FETCHAND4, 0, X1): + gen_atomic_excp(dc, dest, tdest, tsrca, tsrcb, + TILEGX_EXCP_OPCODE_FETCHAND4); + mnemonic = "fetchand4"; + break; + case OE_RRR(FETCHAND, 0, X1): + gen_atomic_excp(dc, dest, tdest, tsrca, tsrcb, + TILEGX_EXCP_OPCODE_FETCHAND); + mnemonic = "fetchand"; + break; + case OE_RRR(FETCHOR4, 0, X1): + gen_atomic_excp(dc, dest, tdest, tsrca, tsrcb, + TILEGX_EXCP_OPCODE_FETCHOR4); + mnemonic = "fetchor4"; + break; + case OE_RRR(FETCHOR, 0, X1): + gen_atomic_excp(dc, dest, tdest, tsrca, tsrcb, + TILEGX_EXCP_OPCODE_FETCHOR); + mnemonic = "fetchor"; + break; + case OE_RRR(FSINGLE_ADD1, 0, X0): + case OE_RRR(FSINGLE_ADDSUB2, 0, X0): + case OE_RRR(FSINGLE_MUL1, 0, X0): + case OE_RRR(FSINGLE_MUL2, 0, X0): + case OE_RRR(FSINGLE_PACK2, 0, X0): + case OE_RRR(FSINGLE_SUB1, 0, X0): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_RRR(MNZ, 0, X0): + case OE_RRR(MNZ, 0, X1): + case OE_RRR(MNZ, 4, Y0): + case OE_RRR(MNZ, 4, Y1): + t0 = load_zero(dc); + tcg_gen_movcond_tl(TCG_COND_NE, tdest, tsrca, t0, tsrcb, t0); + mnemonic = "mnz"; + break; + case OE_RRR(MULAX, 0, X0): + case OE_RRR(MULAX, 3, Y0): + tcg_gen_mul_tl(tdest, tsrca, tsrcb); + tcg_gen_add_tl(tdest, tdest, load_gr(dc, dest)); + tcg_gen_ext32s_tl(tdest, tdest); + mnemonic = "mulax"; + break; + case OE_RRR(MULA_HS_HS, 0, X0): + case OE_RRR(MULA_HS_HS, 9, Y0): + gen_mul_half(tdest, tsrca, tsrcb, HS, HS); + tcg_gen_add_tl(tdest, tdest, load_gr(dc, dest)); + mnemonic = "mula_hs_hs"; + break; + case OE_RRR(MULA_HS_HU, 0, X0): + gen_mul_half(tdest, tsrca, tsrcb, HS, HU); + tcg_gen_add_tl(tdest, tdest, load_gr(dc, dest)); + mnemonic = "mula_hs_hu"; + break; + case OE_RRR(MULA_HS_LS, 0, X0): + gen_mul_half(tdest, tsrca, tsrcb, HS, LS); + tcg_gen_add_tl(tdest, tdest, load_gr(dc, dest)); + mnemonic = "mula_hs_ls"; + break; + case OE_RRR(MULA_HS_LU, 0, X0): + gen_mul_half(tdest, tsrca, tsrcb, HS, LU); + tcg_gen_add_tl(tdest, tdest, load_gr(dc, dest)); + mnemonic = "mula_hs_lu"; + break; + case OE_RRR(MULA_HU_HU, 0, X0): + case OE_RRR(MULA_HU_HU, 9, Y0): + gen_mul_half(tdest, tsrca, tsrcb, HU, HU); + tcg_gen_add_tl(tdest, tdest, load_gr(dc, dest)); + mnemonic = "mula_hu_hu"; + break; + case OE_RRR(MULA_HU_LS, 0, X0): + gen_mul_half(tdest, tsrca, tsrcb, HU, LS); + tcg_gen_add_tl(tdest, tdest, load_gr(dc, dest)); + mnemonic = "mula_hu_ls"; + break; + case OE_RRR(MULA_HU_LU, 0, X0): + gen_mul_half(tdest, tsrca, tsrcb, HU, LU); + tcg_gen_add_tl(tdest, tdest, load_gr(dc, dest)); + mnemonic = "mula_hu_lu"; + break; + case OE_RRR(MULA_LS_LS, 0, X0): + case OE_RRR(MULA_LS_LS, 9, Y0): + gen_mul_half(tdest, tsrca, tsrcb, LS, LS); + tcg_gen_add_tl(tdest, tdest, load_gr(dc, dest)); + mnemonic = "mula_ls_ls"; + break; + case OE_RRR(MULA_LS_LU, 0, X0): + gen_mul_half(tdest, tsrca, tsrcb, LS, LU); + tcg_gen_add_tl(tdest, tdest, load_gr(dc, dest)); + mnemonic = "mula_ls_lu"; + break; + case OE_RRR(MULA_LU_LU, 0, X0): + case OE_RRR(MULA_LU_LU, 9, Y0): + gen_mul_half(tdest, tsrca, tsrcb, LU, LU); + tcg_gen_add_tl(tdest, tdest, load_gr(dc, dest)); + mnemonic = "mula_lu_lu"; + break; + case OE_RRR(MULX, 0, X0): + case OE_RRR(MULX, 3, Y0): + tcg_gen_mul_tl(tdest, tsrca, tsrcb); + tcg_gen_ext32s_tl(tdest, tdest); + mnemonic = "mulx"; + break; + case OE_RRR(MUL_HS_HS, 0, X0): + case OE_RRR(MUL_HS_HS, 8, Y0): + gen_mul_half(tdest, tsrca, tsrcb, HS, HS); + mnemonic = "mul_hs_hs"; + break; + case OE_RRR(MUL_HS_HU, 0, X0): + gen_mul_half(tdest, tsrca, tsrcb, HS, HU); + mnemonic = "mul_hs_hu"; + break; + case OE_RRR(MUL_HS_LS, 0, X0): + gen_mul_half(tdest, tsrca, tsrcb, HS, LS); + mnemonic = "mul_hs_ls"; + break; + case OE_RRR(MUL_HS_LU, 0, X0): + gen_mul_half(tdest, tsrca, tsrcb, HS, LU); + mnemonic = "mul_hs_lu"; + break; + case OE_RRR(MUL_HU_HU, 0, X0): + case OE_RRR(MUL_HU_HU, 8, Y0): + gen_mul_half(tdest, tsrca, tsrcb, HU, HU); + mnemonic = "mul_hu_hu"; + break; + case OE_RRR(MUL_HU_LS, 0, X0): + gen_mul_half(tdest, tsrca, tsrcb, HU, LS); + mnemonic = "mul_hu_ls"; + break; + case OE_RRR(MUL_HU_LU, 0, X0): + gen_mul_half(tdest, tsrca, tsrcb, HU, LU); + mnemonic = "mul_hu_lu"; + break; + case OE_RRR(MUL_LS_LS, 0, X0): + case OE_RRR(MUL_LS_LS, 8, Y0): + gen_mul_half(tdest, tsrca, tsrcb, LS, LS); + mnemonic = "mul_ls_ls"; + break; + case OE_RRR(MUL_LS_LU, 0, X0): + gen_mul_half(tdest, tsrca, tsrcb, LS, LU); + mnemonic = "mul_ls_lu"; + break; + case OE_RRR(MUL_LU_LU, 0, X0): + case OE_RRR(MUL_LU_LU, 8, Y0): + gen_mul_half(tdest, tsrca, tsrcb, LU, LU); + mnemonic = "mul_lu_lu"; + break; + case OE_RRR(MZ, 0, X0): + case OE_RRR(MZ, 0, X1): + case OE_RRR(MZ, 4, Y0): + case OE_RRR(MZ, 4, Y1): + t0 = load_zero(dc); + tcg_gen_movcond_tl(TCG_COND_EQ, tdest, tsrca, t0, tsrcb, t0); + mnemonic = "mz"; + break; + case OE_RRR(NOR, 0, X0): + case OE_RRR(NOR, 0, X1): + case OE_RRR(NOR, 5, Y0): + case OE_RRR(NOR, 5, Y1): + tcg_gen_nor_tl(tdest, tsrca, tsrcb); + mnemonic = "nor"; + break; + case OE_RRR(OR, 0, X0): + case OE_RRR(OR, 0, X1): + case OE_RRR(OR, 5, Y0): + case OE_RRR(OR, 5, Y1): + tcg_gen_or_tl(tdest, tsrca, tsrcb); + mnemonic = "or"; + break; + case OE_RRR(ROTL, 0, X0): + case OE_RRR(ROTL, 0, X1): + case OE_RRR(ROTL, 6, Y0): + case OE_RRR(ROTL, 6, Y1): + tcg_gen_andi_tl(tdest, tsrcb, 63); + tcg_gen_rotl_tl(tdest, tsrca, tdest); + mnemonic = "rotl"; + break; + case OE_RRR(SHL1ADDX, 0, X0): + case OE_RRR(SHL1ADDX, 0, X1): + case OE_RRR(SHL1ADDX, 7, Y0): + case OE_RRR(SHL1ADDX, 7, Y1): + tcg_gen_shli_tl(tdest, tsrca, 1); + tcg_gen_add_tl(tdest, tdest, tsrcb); + tcg_gen_ext32s_tl(tdest, tdest); + mnemonic = "shl1addx"; + break; + case OE_RRR(SHL1ADD, 0, X0): + case OE_RRR(SHL1ADD, 0, X1): + case OE_RRR(SHL1ADD, 1, Y0): + case OE_RRR(SHL1ADD, 1, Y1): + tcg_gen_shli_tl(tdest, tsrca, 1); + tcg_gen_add_tl(tdest, tdest, tsrcb); + mnemonic = "shl1add"; + break; + case OE_RRR(SHL2ADDX, 0, X0): + case OE_RRR(SHL2ADDX, 0, X1): + case OE_RRR(SHL2ADDX, 7, Y0): + case OE_RRR(SHL2ADDX, 7, Y1): + tcg_gen_shli_tl(tdest, tsrca, 2); + tcg_gen_add_tl(tdest, tdest, tsrcb); + tcg_gen_ext32s_tl(tdest, tdest); + mnemonic = "shl2addx"; + break; + case OE_RRR(SHL2ADD, 0, X0): + case OE_RRR(SHL2ADD, 0, X1): + case OE_RRR(SHL2ADD, 1, Y0): + case OE_RRR(SHL2ADD, 1, Y1): + tcg_gen_shli_tl(tdest, tsrca, 2); + tcg_gen_add_tl(tdest, tdest, tsrcb); + mnemonic = "shl2add"; + break; + case OE_RRR(SHL3ADDX, 0, X0): + case OE_RRR(SHL3ADDX, 0, X1): + case OE_RRR(SHL3ADDX, 7, Y0): + case OE_RRR(SHL3ADDX, 7, Y1): + tcg_gen_shli_tl(tdest, tsrca, 3); + tcg_gen_add_tl(tdest, tdest, tsrcb); + tcg_gen_ext32s_tl(tdest, tdest); + mnemonic = "shl3addx"; + break; + case OE_RRR(SHL3ADD, 0, X0): + case OE_RRR(SHL3ADD, 0, X1): + case OE_RRR(SHL3ADD, 1, Y0): + case OE_RRR(SHL3ADD, 1, Y1): + tcg_gen_shli_tl(tdest, tsrca, 3); + tcg_gen_add_tl(tdest, tdest, tsrcb); + mnemonic = "shl3add"; + break; + case OE_RRR(SHLX, 0, X0): + case OE_RRR(SHLX, 0, X1): + tcg_gen_andi_tl(tdest, tsrcb, 31); + tcg_gen_shl_tl(tdest, tsrca, tdest); + tcg_gen_ext32s_tl(tdest, tdest); + mnemonic = "shlx"; + break; + case OE_RRR(SHL, 0, X0): + case OE_RRR(SHL, 0, X1): + case OE_RRR(SHL, 6, Y0): + case OE_RRR(SHL, 6, Y1): + tcg_gen_andi_tl(tdest, tsrcb, 63); + tcg_gen_shl_tl(tdest, tsrca, tdest); + mnemonic = "shl"; + break; + case OE_RRR(SHRS, 0, X0): + case OE_RRR(SHRS, 0, X1): + case OE_RRR(SHRS, 6, Y0): + case OE_RRR(SHRS, 6, Y1): + tcg_gen_andi_tl(tdest, tsrcb, 63); + tcg_gen_sar_tl(tdest, tsrca, tdest); + mnemonic = "shrs"; + break; + case OE_RRR(SHRUX, 0, X0): + case OE_RRR(SHRUX, 0, X1): + t0 = tcg_temp_new(); + tcg_gen_andi_tl(t0, tsrcb, 31); + tcg_gen_ext32u_tl(tdest, tsrca); + tcg_gen_shr_tl(tdest, tdest, t0); + tcg_gen_ext32s_tl(tdest, tdest); + tcg_temp_free(t0); + mnemonic = "shrux"; + break; + case OE_RRR(SHRU, 0, X0): + case OE_RRR(SHRU, 0, X1): + case OE_RRR(SHRU, 6, Y0): + case OE_RRR(SHRU, 6, Y1): + tcg_gen_andi_tl(tdest, tsrcb, 63); + tcg_gen_shr_tl(tdest, tsrca, tdest); + mnemonic = "shru"; + break; + case OE_RRR(SHUFFLEBYTES, 0, X0): + gen_helper_shufflebytes(tdest, load_gr(dc, dest), tsrca, tsrca); + mnemonic = "shufflebytes"; + break; + case OE_RRR(SUBXSC, 0, X0): + case OE_RRR(SUBXSC, 0, X1): + gen_saturate_op(tdest, tsrca, tsrcb, tcg_gen_sub_tl); + mnemonic = "subxsc"; + break; + case OE_RRR(SUBX, 0, X0): + case OE_RRR(SUBX, 0, X1): + case OE_RRR(SUBX, 0, Y0): + case OE_RRR(SUBX, 0, Y1): + tcg_gen_sub_tl(tdest, tsrca, tsrcb); + tcg_gen_ext32s_tl(tdest, tdest); + mnemonic = "subx"; + break; + case OE_RRR(SUB, 0, X0): + case OE_RRR(SUB, 0, X1): + case OE_RRR(SUB, 0, Y0): + case OE_RRR(SUB, 0, Y1): + tcg_gen_sub_tl(tdest, tsrca, tsrcb); + mnemonic = "sub"; + break; + case OE_RRR(V1ADDUC, 0, X0): + case OE_RRR(V1ADDUC, 0, X1): + case OE_RRR(V1ADD, 0, X0): + case OE_RRR(V1ADD, 0, X1): + case OE_RRR(V1ADIFFU, 0, X0): + case OE_RRR(V1AVGU, 0, X0): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_RRR(V1CMPEQ, 0, X0): + case OE_RRR(V1CMPEQ, 0, X1): + tcg_gen_xor_tl(tdest, tsrca, tsrcb); + gen_v1cmpeq0(tdest); + mnemonic = "v1cmpeq"; + break; + case OE_RRR(V1CMPLES, 0, X0): + case OE_RRR(V1CMPLES, 0, X1): + case OE_RRR(V1CMPLEU, 0, X0): + case OE_RRR(V1CMPLEU, 0, X1): + case OE_RRR(V1CMPLTS, 0, X0): + case OE_RRR(V1CMPLTS, 0, X1): + case OE_RRR(V1CMPLTU, 0, X0): + case OE_RRR(V1CMPLTU, 0, X1): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_RRR(V1CMPNE, 0, X0): + case OE_RRR(V1CMPNE, 0, X1): + tcg_gen_xor_tl(tdest, tsrca, tsrcb); + gen_v1cmpne0(tdest); + mnemonic = "v1cmpne"; + break; + case OE_RRR(V1DDOTPUA, 0, X0): + case OE_RRR(V1DDOTPUSA, 0, X0): + case OE_RRR(V1DDOTPUS, 0, X0): + case OE_RRR(V1DDOTPU, 0, X0): + case OE_RRR(V1DOTPA, 0, X0): + case OE_RRR(V1DOTPUA, 0, X0): + case OE_RRR(V1DOTPUSA, 0, X0): + case OE_RRR(V1DOTPUS, 0, X0): + case OE_RRR(V1DOTPU, 0, X0): + case OE_RRR(V1DOTP, 0, X0): + case OE_RRR(V1INT_H, 0, X0): + case OE_RRR(V1INT_H, 0, X1): + case OE_RRR(V1INT_L, 0, X0): + case OE_RRR(V1INT_L, 0, X1): + case OE_RRR(V1MAXU, 0, X0): + case OE_RRR(V1MAXU, 0, X1): + case OE_RRR(V1MINU, 0, X0): + case OE_RRR(V1MINU, 0, X1): + case OE_RRR(V1MNZ, 0, X0): + case OE_RRR(V1MNZ, 0, X1): + case OE_RRR(V1MULTU, 0, X0): + case OE_RRR(V1MULUS, 0, X0): + case OE_RRR(V1MULU, 0, X0): + case OE_RRR(V1MZ, 0, X0): + case OE_RRR(V1MZ, 0, X1): + case OE_RRR(V1SADAU, 0, X0): + case OE_RRR(V1SADU, 0, X0): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_RRR(V1SHL, 0, X0): + case OE_RRR(V1SHL, 0, X1): + gen_helper_v1shl(tdest, tsrca, tsrcb); + mnemonic = "v1shl"; + break; + case OE_RRR(V1SHRS, 0, X0): + case OE_RRR(V1SHRS, 0, X1): + gen_helper_v1shrs(tdest, tsrca, tsrcb); + mnemonic = "v1shrs"; + break; + case OE_RRR(V1SHRU, 0, X0): + case OE_RRR(V1SHRU, 0, X1): + gen_helper_v1shru(tdest, tsrca, tsrcb); + mnemonic = "v1shru"; + break; + case OE_RRR(V1SUBUC, 0, X0): + case OE_RRR(V1SUBUC, 0, X1): + case OE_RRR(V1SUB, 0, X0): + case OE_RRR(V1SUB, 0, X1): + case OE_RRR(V2ADDSC, 0, X0): + case OE_RRR(V2ADDSC, 0, X1): + case OE_RRR(V2ADD, 0, X0): + case OE_RRR(V2ADD, 0, X1): + case OE_RRR(V2ADIFFS, 0, X0): + case OE_RRR(V2AVGS, 0, X0): + case OE_RRR(V2CMPEQ, 0, X0): + case OE_RRR(V2CMPEQ, 0, X1): + case OE_RRR(V2CMPLES, 0, X0): + case OE_RRR(V2CMPLES, 0, X1): + case OE_RRR(V2CMPLEU, 0, X0): + case OE_RRR(V2CMPLEU, 0, X1): + case OE_RRR(V2CMPLTS, 0, X0): + case OE_RRR(V2CMPLTS, 0, X1): + case OE_RRR(V2CMPLTU, 0, X0): + case OE_RRR(V2CMPLTU, 0, X1): + case OE_RRR(V2CMPNE, 0, X0): + case OE_RRR(V2CMPNE, 0, X1): + case OE_RRR(V2DOTPA, 0, X0): + case OE_RRR(V2DOTP, 0, X0): + case OE_RRR(V2INT_H, 0, X0): + case OE_RRR(V2INT_H, 0, X1): + case OE_RRR(V2INT_L, 0, X0): + case OE_RRR(V2INT_L, 0, X1): + case OE_RRR(V2MAXS, 0, X0): + case OE_RRR(V2MAXS, 0, X1): + case OE_RRR(V2MINS, 0, X0): + case OE_RRR(V2MINS, 0, X1): + case OE_RRR(V2MNZ, 0, X0): + case OE_RRR(V2MNZ, 0, X1): + case OE_RRR(V2MULFSC, 0, X0): + case OE_RRR(V2MULS, 0, X0): + case OE_RRR(V2MULTS, 0, X0): + case OE_RRR(V2MZ, 0, X0): + case OE_RRR(V2MZ, 0, X1): + case OE_RRR(V2PACKH, 0, X0): + case OE_RRR(V2PACKH, 0, X1): + case OE_RRR(V2PACKL, 0, X0): + case OE_RRR(V2PACKL, 0, X1): + case OE_RRR(V2PACKUC, 0, X0): + case OE_RRR(V2PACKUC, 0, X1): + case OE_RRR(V2SADAS, 0, X0): + case OE_RRR(V2SADAU, 0, X0): + case OE_RRR(V2SADS, 0, X0): + case OE_RRR(V2SADU, 0, X0): + case OE_RRR(V2SHLSC, 0, X0): + case OE_RRR(V2SHLSC, 0, X1): + case OE_RRR(V2SHL, 0, X0): + case OE_RRR(V2SHL, 0, X1): + case OE_RRR(V2SHRS, 0, X0): + case OE_RRR(V2SHRS, 0, X1): + case OE_RRR(V2SHRU, 0, X0): + case OE_RRR(V2SHRU, 0, X1): + case OE_RRR(V2SUBSC, 0, X0): + case OE_RRR(V2SUBSC, 0, X1): + case OE_RRR(V2SUB, 0, X0): + case OE_RRR(V2SUB, 0, X1): + case OE_RRR(V4ADDSC, 0, X0): + case OE_RRR(V4ADDSC, 0, X1): + case OE_RRR(V4ADD, 0, X0): + case OE_RRR(V4ADD, 0, X1): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_RRR(V4INT_H, 0, X0): + case OE_RRR(V4INT_H, 0, X1): + tcg_gen_shri_tl(tdest, tsrcb, 32); + tcg_gen_deposit_tl(tdest, tsrca, tdest, 0, 32); + mnemonic = "v4int_h"; + break; + case OE_RRR(V4INT_L, 0, X0): + case OE_RRR(V4INT_L, 0, X1): + tcg_gen_deposit_tl(tdest, tsrcb, tsrca, 32, 32); + mnemonic = "v4int_l"; + break; + case OE_RRR(V4PACKSC, 0, X0): + case OE_RRR(V4PACKSC, 0, X1): + case OE_RRR(V4SHLSC, 0, X0): + case OE_RRR(V4SHLSC, 0, X1): + case OE_RRR(V4SHL, 0, X0): + case OE_RRR(V4SHL, 0, X1): + case OE_RRR(V4SHRS, 0, X0): + case OE_RRR(V4SHRS, 0, X1): + case OE_RRR(V4SHRU, 0, X0): + case OE_RRR(V4SHRU, 0, X1): + case OE_RRR(V4SUBSC, 0, X0): + case OE_RRR(V4SUBSC, 0, X1): + case OE_RRR(V4SUB, 0, X0): + case OE_RRR(V4SUB, 0, X1): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_RRR(XOR, 0, X0): + case OE_RRR(XOR, 0, X1): + case OE_RRR(XOR, 5, Y0): + case OE_RRR(XOR, 5, Y1): + tcg_gen_xor_tl(tdest, tsrca, tsrcb); + mnemonic = "xor"; + break; + default: + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + } + + qemu_log_mask(CPU_LOG_TB_IN_ASM, "%s %s, %s, %s", mnemonic, + reg_names[dest], reg_names[srca], reg_names[srcb]); + return TILEGX_EXCP_NONE; +} + +static TileExcp gen_rri_opcode(DisasContext *dc, unsigned opext, + unsigned dest, unsigned srca, int imm) +{ + TCGv tdest = dest_gr(dc, dest); + TCGv tsrca = load_gr(dc, srca); + const char *mnemonic; + TCGMemOp memop; + int i2, i3; + TCGv t0; + + switch (opext) { + case OE(ADDI_OPCODE_Y0, 0, Y0): + case OE(ADDI_OPCODE_Y1, 0, Y1): + case OE_IM(ADDI, X0): + case OE_IM(ADDI, X1): + tcg_gen_addi_tl(tdest, tsrca, imm); + mnemonic = "addi"; + break; + case OE(ADDXI_OPCODE_Y0, 0, Y0): + case OE(ADDXI_OPCODE_Y1, 0, Y1): + case OE_IM(ADDXI, X0): + case OE_IM(ADDXI, X1): + tcg_gen_addi_tl(tdest, tsrca, imm); + tcg_gen_ext32s_tl(tdest, tdest); + mnemonic = "addxi"; + break; + case OE(ANDI_OPCODE_Y0, 0, Y0): + case OE(ANDI_OPCODE_Y1, 0, Y1): + case OE_IM(ANDI, X0): + case OE_IM(ANDI, X1): + tcg_gen_andi_tl(tdest, tsrca, imm); + mnemonic = "andi"; + break; + case OE(CMPEQI_OPCODE_Y0, 0, Y0): + case OE(CMPEQI_OPCODE_Y1, 0, Y1): + case OE_IM(CMPEQI, X0): + case OE_IM(CMPEQI, X1): + tcg_gen_setcondi_tl(TCG_COND_EQ, tdest, tsrca, imm); + mnemonic = "cmpeqi"; + break; + case OE(CMPLTSI_OPCODE_Y0, 0, Y0): + case OE(CMPLTSI_OPCODE_Y1, 0, Y1): + case OE_IM(CMPLTSI, X0): + case OE_IM(CMPLTSI, X1): + tcg_gen_setcondi_tl(TCG_COND_LT, tdest, tsrca, imm); + mnemonic = "cmpltsi"; + break; + case OE_IM(CMPLTUI, X0): + case OE_IM(CMPLTUI, X1): + tcg_gen_setcondi_tl(TCG_COND_LTU, tdest, tsrca, imm); + mnemonic = "cmpltui"; + break; + case OE_IM(LD1S_ADD, X1): + memop = MO_SB; + mnemonic = "ld1s_add"; + goto do_load_add; + case OE_IM(LD1U_ADD, X1): + memop = MO_UB; + mnemonic = "ld1u_add"; + goto do_load_add; + case OE_IM(LD2S_ADD, X1): + memop = MO_TESW; + mnemonic = "ld2s_add"; + goto do_load_add; + case OE_IM(LD2U_ADD, X1): + memop = MO_TEUW; + mnemonic = "ld2u_add"; + goto do_load_add; + case OE_IM(LD4S_ADD, X1): + memop = MO_TESL; + mnemonic = "ld4s_add"; + goto do_load_add; + case OE_IM(LD4U_ADD, X1): + memop = MO_TEUL; + mnemonic = "ld4u_add"; + goto do_load_add; + case OE_IM(LDNT1S_ADD, X1): + memop = MO_SB; + mnemonic = "ldnt1s_add"; + goto do_load_add; + case OE_IM(LDNT1U_ADD, X1): + memop = MO_UB; + mnemonic = "ldnt1u_add"; + goto do_load_add; + case OE_IM(LDNT2S_ADD, X1): + memop = MO_TESW; + mnemonic = "ldnt2s_add"; + goto do_load_add; + case OE_IM(LDNT2U_ADD, X1): + memop = MO_TEUW; + mnemonic = "ldnt2u_add"; + goto do_load_add; + case OE_IM(LDNT4S_ADD, X1): + memop = MO_TESL; + mnemonic = "ldnt4s_add"; + goto do_load_add; + case OE_IM(LDNT4U_ADD, X1): + memop = MO_TEUL; + mnemonic = "ldnt4u_add"; + goto do_load_add; + case OE_IM(LDNT_ADD, X1): + memop = MO_TEQ; + mnemonic = "ldnt_add"; + goto do_load_add; + case OE_IM(LD_ADD, X1): + memop = MO_TEQ; + mnemonic = "ldnt_add"; + do_load_add: + tcg_gen_qemu_ld_tl(tdest, tsrca, dc->mmuidx, memop); + tcg_gen_addi_tl(dest_gr(dc, srca), tsrca, imm); + break; + case OE_IM(LDNA_ADD, X1): + tcg_gen_andi_tl(tdest, tsrca, ~7); + tcg_gen_qemu_ld_tl(tdest, tdest, dc->mmuidx, MO_TEQ); + tcg_gen_addi_tl(dest_gr(dc, srca), tsrca, imm); + mnemonic = "ldna_add"; + break; + case OE_IM(ORI, X0): + case OE_IM(ORI, X1): + tcg_gen_ori_tl(tdest, tsrca, imm); + mnemonic = "ori"; + break; + case OE_IM(V1ADDI, X0): + case OE_IM(V1ADDI, X1): + case OE_IM(V1CMPEQI, X0): + case OE_IM(V1CMPEQI, X1): + tcg_gen_xori_tl(tdest, tsrca, V1_IMM(imm)); + gen_v1cmpeq0(tdest); + mnemonic = "v1cmpeqi"; + break; + case OE_IM(V1CMPLTSI, X0): + case OE_IM(V1CMPLTSI, X1): + case OE_IM(V1CMPLTUI, X0): + case OE_IM(V1CMPLTUI, X1): + case OE_IM(V1MAXUI, X0): + case OE_IM(V1MAXUI, X1): + case OE_IM(V1MINUI, X0): + case OE_IM(V1MINUI, X1): + case OE_IM(V2ADDI, X0): + case OE_IM(V2ADDI, X1): + case OE_IM(V2CMPEQI, X0): + case OE_IM(V2CMPEQI, X1): + case OE_IM(V2CMPLTSI, X0): + case OE_IM(V2CMPLTSI, X1): + case OE_IM(V2CMPLTUI, X0): + case OE_IM(V2CMPLTUI, X1): + case OE_IM(V2MAXSI, X0): + case OE_IM(V2MAXSI, X1): + case OE_IM(V2MINSI, X0): + case OE_IM(V2MINSI, X1): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + case OE_IM(XORI, X0): + case OE_IM(XORI, X1): + tcg_gen_xori_tl(tdest, tsrca, imm); + mnemonic = "xori"; + break; + + case OE_SH(ROTLI, X0): + case OE_SH(ROTLI, X1): + case OE_SH(ROTLI, Y0): + case OE_SH(ROTLI, Y1): + tcg_gen_rotli_tl(tdest, tsrca, imm); + mnemonic = "rotli"; + break; + case OE_SH(SHLI, X0): + case OE_SH(SHLI, X1): + case OE_SH(SHLI, Y0): + case OE_SH(SHLI, Y1): + tcg_gen_shli_tl(tdest, tsrca, imm); + mnemonic = "shli"; + break; + case OE_SH(SHLXI, X0): + case OE_SH(SHLXI, X1): + tcg_gen_shli_tl(tdest, tsrca, imm & 31); + tcg_gen_ext32s_tl(tdest, tdest); + mnemonic = "shlxi"; + break; + case OE_SH(SHRSI, X0): + case OE_SH(SHRSI, X1): + case OE_SH(SHRSI, Y0): + case OE_SH(SHRSI, Y1): + tcg_gen_sari_tl(tdest, tsrca, imm); + mnemonic = "shrsi"; + break; + case OE_SH(SHRUI, X0): + case OE_SH(SHRUI, X1): + case OE_SH(SHRUI, Y0): + case OE_SH(SHRUI, Y1): + tcg_gen_shri_tl(tdest, tsrca, imm); + mnemonic = "shrui"; + break; + case OE_SH(SHRUXI, X0): + case OE_SH(SHRUXI, X1): + if ((imm & 31) == 0) { + tcg_gen_ext32s_tl(tdest, tsrca); + } else { + tcg_gen_ext32u_tl(tdest, tsrca); + tcg_gen_shri_tl(tdest, tdest, imm & 31); + } + mnemonic = "shlxi"; + break; + case OE_SH(V1SHLI, X0): + case OE_SH(V1SHLI, X1): + i2 = imm & 7; + i3 = 0xff >> i2; + tcg_gen_andi_tl(tdest, tsrca, V1_IMM(i3)); + tcg_gen_shli_tl(tdest, tdest, i2); + mnemonic = "v1shli"; + break; + case OE_SH(V1SHRSI, X0): + case OE_SH(V1SHRSI, X1): + t0 = tcg_const_tl(imm & 7); + gen_helper_v1shrs(tdest, tsrca, t0); + tcg_temp_free(t0); + mnemonic = "v1shrsi"; + break; + case OE_SH(V1SHRUI, X0): + case OE_SH(V1SHRUI, X1): + i2 = imm & 7; + i3 = (0xff << i2) & 0xff; + tcg_gen_andi_tl(tdest, tsrca, V1_IMM(i3)); + tcg_gen_shri_tl(tdest, tdest, i2); + mnemonic = "v1shrui"; + break; + case OE_SH(V2SHLI, X0): + case OE_SH(V2SHLI, X1): + case OE_SH(V2SHRSI, X0): + case OE_SH(V2SHRSI, X1): + case OE_SH(V2SHRUI, X0): + case OE_SH(V2SHRUI, X1): + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + + case OE(ADDLI_OPCODE_X0, 0, X0): + case OE(ADDLI_OPCODE_X1, 0, X1): + tcg_gen_addi_tl(tdest, tsrca, imm); + mnemonic = "addli"; + break; + case OE(ADDXLI_OPCODE_X0, 0, X0): + case OE(ADDXLI_OPCODE_X1, 0, X1): + tcg_gen_addi_tl(tdest, tsrca, imm); + tcg_gen_ext32s_tl(tdest, tdest); + mnemonic = "addxli"; + break; + case OE(SHL16INSLI_OPCODE_X0, 0, X0): + case OE(SHL16INSLI_OPCODE_X1, 0, X1): + tcg_gen_shli_tl(tdest, tsrca, 16); + tcg_gen_ori_tl(tdest, tdest, imm & 0xffff); + mnemonic = "shl16insli"; + break; + + default: + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + } + + qemu_log_mask(CPU_LOG_TB_IN_ASM, "%s %s, %s, %d", mnemonic, + reg_names[dest], reg_names[srca], imm); + return TILEGX_EXCP_NONE; +} + +static TileExcp gen_bf_opcode_x0(DisasContext *dc, unsigned ext, + unsigned dest, unsigned srca, + unsigned bfs, unsigned bfe) +{ + TCGv tdest = dest_gr(dc, dest); + TCGv tsrca = load_gr(dc, srca); + TCGv tsrcd; + int len; + const char *mnemonic; + + /* The bitfield is either between E and S inclusive, + or up from S and down from E inclusive. */ + if (bfs <= bfe) { + len = bfe - bfs + 1; + } else { + len = (64 - bfs) + (bfe + 1); + } + + switch (ext) { + case BFEXTU_BF_OPCODE_X0: + if (bfs == 0 && bfe == 7) { + tcg_gen_ext8u_tl(tdest, tsrca); + } else if (bfs == 0 && bfe == 15) { + tcg_gen_ext16u_tl(tdest, tsrca); + } else if (bfs == 0 && bfe == 31) { + tcg_gen_ext32u_tl(tdest, tsrca); + } else { + int rol = 63 - bfe; + if (bfs <= bfe) { + tcg_gen_shli_tl(tdest, tsrca, rol); + } else { + tcg_gen_rotli_tl(tdest, tsrca, rol); + } + tcg_gen_shri_tl(tdest, tdest, (bfs + rol) & 63); + } + mnemonic = "bfextu"; + break; + + case BFEXTS_BF_OPCODE_X0: + if (bfs == 0 && bfe == 7) { + tcg_gen_ext8s_tl(tdest, tsrca); + } else if (bfs == 0 && bfe == 15) { + tcg_gen_ext16s_tl(tdest, tsrca); + } else if (bfs == 0 && bfe == 31) { + tcg_gen_ext32s_tl(tdest, tsrca); + } else { + int rol = 63 - bfe; + if (bfs <= bfe) { + tcg_gen_shli_tl(tdest, tsrca, rol); + } else { + tcg_gen_rotli_tl(tdest, tsrca, rol); + } + tcg_gen_sari_tl(tdest, tdest, (bfs + rol) & 63); + } + mnemonic = "bfexts"; + break; + + case BFINS_BF_OPCODE_X0: + tsrcd = load_gr(dc, dest); + if (bfs <= bfe) { + tcg_gen_deposit_tl(tdest, tsrcd, tsrca, bfs, len); + } else { + tcg_gen_rotri_tl(tdest, tsrcd, bfs); + tcg_gen_deposit_tl(tdest, tdest, tsrca, 0, len); + tcg_gen_rotli_tl(tdest, tdest, bfs); + } + mnemonic = "bfins"; + break; + + case MM_BF_OPCODE_X0: + tsrcd = load_gr(dc, dest); + if (bfs == 0) { + tcg_gen_deposit_tl(tdest, tsrca, tsrcd, 0, len); + } else { + uint64_t mask = len == 64 ? -1 : rol64((1ULL << len) - 1, bfs); + TCGv tmp = tcg_const_tl(mask); + + tcg_gen_and_tl(tdest, tsrcd, tmp); + tcg_gen_andc_tl(tmp, tsrca, tmp); + tcg_gen_or_tl(tdest, tdest, tmp); + tcg_temp_free(tmp); + } + mnemonic = "mm"; + break; + + default: + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + } + + qemu_log_mask(CPU_LOG_TB_IN_ASM, "%s %s, %s, %u, %u", mnemonic, + reg_names[dest], reg_names[srca], bfs, bfe); + return TILEGX_EXCP_NONE; +} + +static TileExcp gen_branch_opcode_x1(DisasContext *dc, unsigned ext, + unsigned srca, int off) +{ + target_ulong tgt = dc->pc + off * TILEGX_BUNDLE_SIZE_IN_BYTES; + const char *mnemonic; + + dc->jmp.dest = tcg_const_tl(tgt); + dc->jmp.val1 = tcg_temp_new(); + tcg_gen_mov_tl(dc->jmp.val1, load_gr(dc, srca)); + + /* Note that the "predict taken" opcodes have bit 0 clear. + Therefore, fold the two cases together by setting bit 0. */ + switch (ext | 1) { + case BEQZ_BRANCH_OPCODE_X1: + dc->jmp.cond = TCG_COND_EQ; + mnemonic = "beqz"; + break; + case BNEZ_BRANCH_OPCODE_X1: + dc->jmp.cond = TCG_COND_NE; + mnemonic = "bnez"; + break; + case BGEZ_BRANCH_OPCODE_X1: + dc->jmp.cond = TCG_COND_GE; + mnemonic = "bgez"; + break; + case BGTZ_BRANCH_OPCODE_X1: + dc->jmp.cond = TCG_COND_GT; + mnemonic = "bgtz"; + break; + case BLEZ_BRANCH_OPCODE_X1: + dc->jmp.cond = TCG_COND_LE; + mnemonic = "blez"; + break; + case BLTZ_BRANCH_OPCODE_X1: + dc->jmp.cond = TCG_COND_LT; + mnemonic = "bltz"; + break; + case BLBC_BRANCH_OPCODE_X1: + dc->jmp.cond = TCG_COND_EQ; + tcg_gen_andi_tl(dc->jmp.val1, dc->jmp.val1, 1); + mnemonic = "blbc"; + break; + case BLBS_BRANCH_OPCODE_X1: + dc->jmp.cond = TCG_COND_NE; + tcg_gen_andi_tl(dc->jmp.val1, dc->jmp.val1, 1); + mnemonic = "blbs"; + break; + default: + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + } + + if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { + qemu_log("%s%s %s, " TARGET_FMT_lx " <%s>", + mnemonic, ext & 1 ? "" : "t", + reg_names[srca], tgt, lookup_symbol(tgt)); + } + return TILEGX_EXCP_NONE; +} + +static TileExcp gen_jump_opcode_x1(DisasContext *dc, unsigned ext, int off) +{ + target_ulong tgt = dc->pc + off * TILEGX_BUNDLE_SIZE_IN_BYTES; + const char *mnemonic = "j"; + + /* The extension field is 1 bit, therefore we only have JAL and J. */ + if (ext == JAL_JUMP_OPCODE_X1) { + tcg_gen_movi_tl(dest_gr(dc, TILEGX_R_LR), + dc->pc + TILEGX_BUNDLE_SIZE_IN_BYTES); + mnemonic = "jal"; + } + dc->jmp.cond = TCG_COND_ALWAYS; + dc->jmp.dest = tcg_const_tl(tgt); + + if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { + qemu_log("%s " TARGET_FMT_lx " <%s>", + mnemonic, tgt, lookup_symbol(tgt)); + } + return TILEGX_EXCP_NONE; +} + +typedef struct { + const char *name; + intptr_t offset; + void (*get)(TCGv, TCGv_ptr); + void (*put)(TCGv_ptr, TCGv); +} TileSPR; + +static const TileSPR *find_spr(unsigned spr) +{ + /* Allow the compiler to construct the binary search tree. */ +#define D(N, O, G, P) \ + case SPR_##N: { static const TileSPR x = { #N, O, G, P }; return &x; } + + switch (spr) { + D(CMPEXCH_VALUE, + offsetof(CPUTLGState, spregs[TILEGX_SPR_CMPEXCH]), 0, 0) + D(INTERRUPT_CRITICAL_SECTION, + offsetof(CPUTLGState, spregs[TILEGX_SPR_CRITICAL_SEC]), 0, 0) + D(SIM_CONTROL, + offsetof(CPUTLGState, spregs[TILEGX_SPR_SIM_CONTROL]), 0, 0) + } + +#undef D + + qemu_log_mask(LOG_UNIMP, "UNIMP SPR %u\n", spr); + return NULL; +} + +static TileExcp gen_mtspr_x1(DisasContext *dc, unsigned spr, unsigned srca) +{ + const TileSPR *def = find_spr(spr); + TCGv tsrca; + + if (def == NULL) { + qemu_log_mask(CPU_LOG_TB_IN_ASM, "mtspr spr[%u], %s", spr, reg_names[srca]); + return TILEGX_EXCP_OPCODE_UNKNOWN; + } + + tsrca = load_gr(dc, srca); + if (def->put) { + def->put(cpu_env, tsrca); + } else { + tcg_gen_st_tl(tsrca, cpu_env, def->offset); + } + qemu_log_mask(CPU_LOG_TB_IN_ASM, "mtspr %s, %s", def->name, reg_names[srca]); + return TILEGX_EXCP_NONE; +} + +static TileExcp gen_mfspr_x1(DisasContext *dc, unsigned dest, unsigned spr) +{ + const TileSPR *def = find_spr(spr); + TCGv tdest; + + if (def == NULL) { + qemu_log_mask(CPU_LOG_TB_IN_ASM, "mtspr %s, spr[%u]", reg_names[dest], spr); + return TILEGX_EXCP_OPCODE_UNKNOWN; + } + + tdest = dest_gr(dc, dest); + if (def->get) { + def->get(tdest, cpu_env); + } else { + tcg_gen_ld_tl(tdest, cpu_env, def->offset); + } + qemu_log_mask(CPU_LOG_TB_IN_ASM, "mfspr %s, %s", reg_names[dest], def->name); + return TILEGX_EXCP_NONE; +} + +static TileExcp decode_y0(DisasContext *dc, tilegx_bundle_bits bundle) +{ + unsigned opc = get_Opcode_Y0(bundle); + unsigned ext = get_RRROpcodeExtension_Y0(bundle); + unsigned dest = get_Dest_Y0(bundle); + unsigned srca = get_SrcA_Y0(bundle); + unsigned srcb; + int imm; + + switch (opc) { + case RRR_1_OPCODE_Y0: + if (ext == UNARY_RRR_1_OPCODE_Y0) { + ext = get_UnaryOpcodeExtension_Y0(bundle); + return gen_rr_opcode(dc, OE(opc, ext, Y0), dest, srca); + } + /* fallthru */ + case RRR_0_OPCODE_Y0: + case RRR_2_OPCODE_Y0: + case RRR_3_OPCODE_Y0: + case RRR_4_OPCODE_Y0: + case RRR_5_OPCODE_Y0: + case RRR_6_OPCODE_Y0: + case RRR_7_OPCODE_Y0: + case RRR_8_OPCODE_Y0: + case RRR_9_OPCODE_Y0: + srcb = get_SrcB_Y0(bundle); + return gen_rrr_opcode(dc, OE(opc, ext, Y0), dest, srca, srcb); + + case SHIFT_OPCODE_Y0: + ext = get_ShiftOpcodeExtension_Y0(bundle); + imm = get_ShAmt_Y0(bundle); + return gen_rri_opcode(dc, OE(opc, ext, Y0), dest, srca, imm); + + case ADDI_OPCODE_Y0: + case ADDXI_OPCODE_Y0: + case ANDI_OPCODE_Y0: + case CMPEQI_OPCODE_Y0: + case CMPLTSI_OPCODE_Y0: + imm = (int8_t)get_Imm8_Y0(bundle); + return gen_rri_opcode(dc, OE(opc, 0, Y0), dest, srca, imm); + + default: + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + } +} + +static TileExcp decode_y1(DisasContext *dc, tilegx_bundle_bits bundle) +{ + unsigned opc = get_Opcode_Y1(bundle); + unsigned ext = get_RRROpcodeExtension_Y1(bundle); + unsigned dest = get_Dest_Y1(bundle); + unsigned srca = get_SrcA_Y1(bundle); + unsigned srcb; + int imm; + + switch (get_Opcode_Y1(bundle)) { + case RRR_1_OPCODE_Y1: + if (ext == UNARY_RRR_1_OPCODE_Y0) { + ext = get_UnaryOpcodeExtension_Y1(bundle); + return gen_rr_opcode(dc, OE(opc, ext, Y1), dest, srca); + } + /* fallthru */ + case RRR_0_OPCODE_Y1: + case RRR_2_OPCODE_Y1: + case RRR_3_OPCODE_Y1: + case RRR_4_OPCODE_Y1: + case RRR_5_OPCODE_Y1: + case RRR_6_OPCODE_Y1: + case RRR_7_OPCODE_Y1: + srcb = get_SrcB_Y1(bundle); + return gen_rrr_opcode(dc, OE(opc, ext, Y1), dest, srca, srcb); + + case SHIFT_OPCODE_Y1: + ext = get_ShiftOpcodeExtension_Y1(bundle); + imm = get_ShAmt_Y1(bundle); + return gen_rri_opcode(dc, OE(opc, ext, Y1), dest, srca, imm); + + case ADDI_OPCODE_Y1: + case ADDXI_OPCODE_Y1: + case ANDI_OPCODE_Y1: + case CMPEQI_OPCODE_Y1: + case CMPLTSI_OPCODE_Y1: + imm = (int8_t)get_Imm8_Y1(bundle); + return gen_rri_opcode(dc, OE(opc, 0, Y1), dest, srca, imm); + + default: + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + } +} + +static TileExcp decode_y2(DisasContext *dc, tilegx_bundle_bits bundle) +{ + unsigned mode = get_Mode(bundle); + unsigned opc = get_Opcode_Y2(bundle); + unsigned srca = get_SrcA_Y2(bundle); + unsigned srcbdest = get_SrcBDest_Y2(bundle); + const char *mnemonic; + TCGMemOp memop; + + switch (OEY2(opc, mode)) { + case OEY2(LD1S_OPCODE_Y2, MODE_OPCODE_YA2): + memop = MO_SB; + mnemonic = "ld1s"; + goto do_load; + case OEY2(LD1U_OPCODE_Y2, MODE_OPCODE_YA2): + memop = MO_UB; + mnemonic = "ld1u"; + goto do_load; + case OEY2(LD2S_OPCODE_Y2, MODE_OPCODE_YA2): + memop = MO_TESW; + mnemonic = "ld2s"; + goto do_load; + case OEY2(LD2U_OPCODE_Y2, MODE_OPCODE_YA2): + memop = MO_TEUW; + mnemonic = "ld2u"; + goto do_load; + case OEY2(LD4S_OPCODE_Y2, MODE_OPCODE_YB2): + memop = MO_TESL; + mnemonic = "ld4s"; + goto do_load; + case OEY2(LD4U_OPCODE_Y2, MODE_OPCODE_YB2): + memop = MO_TEUL; + mnemonic = "ld4u"; + goto do_load; + case OEY2(LD_OPCODE_Y2, MODE_OPCODE_YB2): + memop = MO_TEQ; + mnemonic = "ld"; + do_load: + tcg_gen_qemu_ld_tl(dest_gr(dc, srcbdest), load_gr(dc, srca), + dc->mmuidx, memop); + qemu_log_mask(CPU_LOG_TB_IN_ASM, "%s %s, %s", mnemonic, + reg_names[srcbdest], reg_names[srca]); + return TILEGX_EXCP_NONE; + + case OEY2(ST1_OPCODE_Y2, MODE_OPCODE_YC2): + return gen_st_opcode(dc, 0, srca, srcbdest, MO_UB, "st1"); + case OEY2(ST2_OPCODE_Y2, MODE_OPCODE_YC2): + return gen_st_opcode(dc, 0, srca, srcbdest, MO_TEUW, "st2"); + case OEY2(ST4_OPCODE_Y2, MODE_OPCODE_YC2): + return gen_st_opcode(dc, 0, srca, srcbdest, MO_TEUL, "st4"); + case OEY2(ST_OPCODE_Y2, MODE_OPCODE_YC2): + return gen_st_opcode(dc, 0, srca, srcbdest, MO_TEQ, "st"); + + default: + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + } +} + +static TileExcp decode_x0(DisasContext *dc, tilegx_bundle_bits bundle) +{ + unsigned opc = get_Opcode_X0(bundle); + unsigned dest = get_Dest_X0(bundle); + unsigned srca = get_SrcA_X0(bundle); + unsigned ext, srcb, bfs, bfe; + int imm; + + switch (opc) { + case RRR_0_OPCODE_X0: + ext = get_RRROpcodeExtension_X0(bundle); + if (ext == UNARY_RRR_0_OPCODE_X0) { + ext = get_UnaryOpcodeExtension_X0(bundle); + return gen_rr_opcode(dc, OE(opc, ext, X0), dest, srca); + } + srcb = get_SrcB_X0(bundle); + return gen_rrr_opcode(dc, OE(opc, ext, X0), dest, srca, srcb); + + case SHIFT_OPCODE_X0: + ext = get_ShiftOpcodeExtension_X0(bundle); + imm = get_ShAmt_X0(bundle); + return gen_rri_opcode(dc, OE(opc, ext, X0), dest, srca, imm); + + case IMM8_OPCODE_X0: + ext = get_Imm8OpcodeExtension_X0(bundle); + imm = (int8_t)get_Imm8_X0(bundle); + return gen_rri_opcode(dc, OE(opc, ext, X0), dest, srca, imm); + + case BF_OPCODE_X0: + ext = get_BFOpcodeExtension_X0(bundle); + bfs = get_BFStart_X0(bundle); + bfe = get_BFEnd_X0(bundle); + return gen_bf_opcode_x0(dc, ext, dest, srca, bfs, bfe); + + case ADDLI_OPCODE_X0: + case SHL16INSLI_OPCODE_X0: + case ADDXLI_OPCODE_X0: + imm = (int16_t)get_Imm16_X0(bundle); + return gen_rri_opcode(dc, OE(opc, 0, X0), dest, srca, imm); + + default: + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + } +} + +static TileExcp decode_x1(DisasContext *dc, tilegx_bundle_bits bundle) +{ + unsigned opc = get_Opcode_X1(bundle); + unsigned dest = get_Dest_X1(bundle); + unsigned srca = get_SrcA_X1(bundle); + unsigned ext, srcb; + int imm; + + switch (opc) { + case RRR_0_OPCODE_X1: + ext = get_RRROpcodeExtension_X1(bundle); + srcb = get_SrcB_X1(bundle); + switch (ext) { + case UNARY_RRR_0_OPCODE_X1: + ext = get_UnaryOpcodeExtension_X1(bundle); + return gen_rr_opcode(dc, OE(opc, ext, X1), dest, srca); + case ST1_RRR_0_OPCODE_X1: + return gen_st_opcode(dc, dest, srca, srcb, MO_UB, "st1"); + case ST2_RRR_0_OPCODE_X1: + return gen_st_opcode(dc, dest, srca, srcb, MO_TEUW, "st2"); + case ST4_RRR_0_OPCODE_X1: + return gen_st_opcode(dc, dest, srca, srcb, MO_TEUL, "st4"); + case STNT1_RRR_0_OPCODE_X1: + return gen_st_opcode(dc, dest, srca, srcb, MO_UB, "stnt1"); + case STNT2_RRR_0_OPCODE_X1: + return gen_st_opcode(dc, dest, srca, srcb, MO_TEUW, "stnt2"); + case STNT4_RRR_0_OPCODE_X1: + return gen_st_opcode(dc, dest, srca, srcb, MO_TEUL, "stnt4"); + case STNT_RRR_0_OPCODE_X1: + return gen_st_opcode(dc, dest, srca, srcb, MO_TEQ, "stnt"); + case ST_RRR_0_OPCODE_X1: + return gen_st_opcode(dc, dest, srca, srcb, MO_TEQ, "st"); + } + return gen_rrr_opcode(dc, OE(opc, ext, X1), dest, srca, srcb); + + case SHIFT_OPCODE_X1: + ext = get_ShiftOpcodeExtension_X1(bundle); + imm = get_ShAmt_X1(bundle); + return gen_rri_opcode(dc, OE(opc, ext, X1), dest, srca, imm); + + case IMM8_OPCODE_X1: + ext = get_Imm8OpcodeExtension_X1(bundle); + imm = (int8_t)get_Dest_Imm8_X1(bundle); + srcb = get_SrcB_X1(bundle); + switch (ext) { + case ST1_ADD_IMM8_OPCODE_X1: + return gen_st_add_opcode(dc, srca, srcb, imm, MO_UB, "st1_add"); + case ST2_ADD_IMM8_OPCODE_X1: + return gen_st_add_opcode(dc, srca, srcb, imm, MO_TEUW, "st2_add"); + case ST4_ADD_IMM8_OPCODE_X1: + return gen_st_add_opcode(dc, srca, srcb, imm, MO_TEUL, "st4_add"); + case STNT1_ADD_IMM8_OPCODE_X1: + return gen_st_add_opcode(dc, srca, srcb, imm, MO_UB, "stnt1_add"); + case STNT2_ADD_IMM8_OPCODE_X1: + return gen_st_add_opcode(dc, srca, srcb, imm, MO_TEUW, "stnt2_add"); + case STNT4_ADD_IMM8_OPCODE_X1: + return gen_st_add_opcode(dc, srca, srcb, imm, MO_TEUL, "stnt4_add"); + case STNT_ADD_IMM8_OPCODE_X1: + return gen_st_add_opcode(dc, srca, srcb, imm, MO_TEQ, "stnt_add"); + case ST_ADD_IMM8_OPCODE_X1: + return gen_st_add_opcode(dc, srca, srcb, imm, MO_TEQ, "st_add"); + case MFSPR_IMM8_OPCODE_X1: + return gen_mfspr_x1(dc, dest, get_MF_Imm14_X1(bundle)); + case MTSPR_IMM8_OPCODE_X1: + return gen_mtspr_x1(dc, get_MT_Imm14_X1(bundle), srca); + } + imm = (int8_t)get_Imm8_X1(bundle); + return gen_rri_opcode(dc, OE(opc, ext, X1), dest, srca, imm); + + case BRANCH_OPCODE_X1: + ext = get_BrType_X1(bundle); + imm = sextract32(get_BrOff_X1(bundle), 0, 17); + return gen_branch_opcode_x1(dc, ext, srca, imm); + + case JUMP_OPCODE_X1: + ext = get_JumpOpcodeExtension_X1(bundle); + imm = sextract32(get_JumpOff_X1(bundle), 0, 27); + return gen_jump_opcode_x1(dc, ext, imm); + + case ADDLI_OPCODE_X1: + case SHL16INSLI_OPCODE_X1: + case ADDXLI_OPCODE_X1: + imm = (int16_t)get_Imm16_X1(bundle); + return gen_rri_opcode(dc, OE(opc, 0, X1), dest, srca, imm); + + default: + return TILEGX_EXCP_OPCODE_UNIMPLEMENTED; + } +} + +static void notice_excp(DisasContext *dc, uint64_t bundle, + const char *type, TileExcp excp) +{ + if (likely(excp == TILEGX_EXCP_NONE)) { + return; + } + gen_exception(dc, excp); + if (excp == TILEGX_EXCP_OPCODE_UNIMPLEMENTED) { + qemu_log_mask(LOG_UNIMP, "UNIMP %s, [" FMT64X "]\n", type, bundle); + } +} + +static void translate_one_bundle(DisasContext *dc, uint64_t bundle) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dc->wb); i++) { + DisasContextTemp *wb = &dc->wb[i]; + wb->reg = TILEGX_R_NOREG; + TCGV_UNUSED_I64(wb->val); + } + dc->num_wb = 0; + + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { + tcg_gen_debug_insn_start(dc->pc); + } + + qemu_log_mask(CPU_LOG_TB_IN_ASM, " %" PRIx64 ": { ", dc->pc); + if (get_Mode(bundle)) { + notice_excp(dc, bundle, "y0", decode_y0(dc, bundle)); + qemu_log_mask(CPU_LOG_TB_IN_ASM, " ; "); + notice_excp(dc, bundle, "y1", decode_y1(dc, bundle)); + qemu_log_mask(CPU_LOG_TB_IN_ASM, " ; "); + notice_excp(dc, bundle, "y2", decode_y2(dc, bundle)); + } else { + notice_excp(dc, bundle, "x0", decode_x0(dc, bundle)); + qemu_log_mask(CPU_LOG_TB_IN_ASM, " ; "); + notice_excp(dc, bundle, "x1", decode_x1(dc, bundle)); + } + qemu_log_mask(CPU_LOG_TB_IN_ASM, " }\n"); + + for (i = dc->num_wb - 1; i >= 0; --i) { + DisasContextTemp *wb = &dc->wb[i]; + if (wb->reg < TILEGX_R_COUNT) { + tcg_gen_mov_i64(cpu_regs[wb->reg], wb->val); + } + tcg_temp_free_i64(wb->val); + } + + if (dc->jmp.cond != TCG_COND_NEVER) { + if (dc->jmp.cond == TCG_COND_ALWAYS) { + tcg_gen_mov_i64(cpu_pc, dc->jmp.dest); + } else { + TCGv next = tcg_const_i64(dc->pc + TILEGX_BUNDLE_SIZE_IN_BYTES); + tcg_gen_movcond_i64(dc->jmp.cond, cpu_pc, + dc->jmp.val1, load_zero(dc), + dc->jmp.dest, next); + tcg_temp_free_i64(dc->jmp.val1); + tcg_temp_free_i64(next); + } + tcg_temp_free_i64(dc->jmp.dest); + tcg_gen_exit_tb(0); + dc->exit_tb = true; + } else if (dc->atomic_excp != TILEGX_EXCP_NONE) { + gen_exception(dc, dc->atomic_excp); + } +} + +static inline void gen_intermediate_code_internal(TileGXCPU *cpu, + TranslationBlock *tb, + bool search_pc) +{ + DisasContext ctx; + DisasContext *dc = &ctx; + CPUState *cs = CPU(cpu); + CPUTLGState *env = &cpu->env; + uint64_t pc_start = tb->pc; + uint64_t next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + int j, lj = -1; + int num_insns = 0; + int max_insns = tb->cflags & CF_COUNT_MASK; + + dc->pc = pc_start; + dc->mmuidx = 0; + dc->exit_tb = false; + dc->atomic_excp = TILEGX_EXCP_NONE; + dc->jmp.cond = TCG_COND_NEVER; + TCGV_UNUSED_I64(dc->jmp.dest); + TCGV_UNUSED_I64(dc->jmp.val1); + TCGV_UNUSED_I64(dc->zero); + + if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { + qemu_log("IN: %s\n", lookup_symbol(pc_start)); + } + if (!max_insns) { + max_insns = CF_COUNT_MASK; + } + if (cs->singlestep_enabled || singlestep) { + max_insns = 1; + } + gen_tb_start(tb); + + while (1) { + if (search_pc) { + j = tcg_op_buf_count(); + if (lj < j) { + lj++; + while (lj < j) { + tcg_ctx.gen_opc_instr_start[lj++] = 0; + } + } + tcg_ctx.gen_opc_pc[lj] = dc->pc; + tcg_ctx.gen_opc_instr_start[lj] = 1; + tcg_ctx.gen_opc_icount[lj] = num_insns; + } + translate_one_bundle(dc, cpu_ldq_data(env, dc->pc)); + + if (dc->exit_tb) { + /* PC updated and EXIT_TB/GOTO_TB/exception emitted. */ + break; + } + dc->pc += TILEGX_BUNDLE_SIZE_IN_BYTES; + if (++num_insns >= max_insns + || dc->pc >= next_page_start + || tcg_op_buf_full()) { + /* Ending the TB due to TB size or page boundary. Set PC. */ + tcg_gen_movi_tl(cpu_pc, dc->pc); + tcg_gen_exit_tb(0); + break; + } + } + + gen_tb_end(tb, num_insns); + if (search_pc) { + j = tcg_op_buf_count(); + lj++; + while (lj <= j) { + tcg_ctx.gen_opc_instr_start[lj++] = 0; + } + } else { + tb->size = dc->pc - pc_start; + tb->icount = num_insns; + } + + qemu_log_mask(CPU_LOG_TB_IN_ASM, "\n"); +} + +void gen_intermediate_code(CPUTLGState *env, struct TranslationBlock *tb) +{ + gen_intermediate_code_internal(tilegx_env_get_cpu(env), tb, false); +} + +void gen_intermediate_code_pc(CPUTLGState *env, struct TranslationBlock *tb) +{ + gen_intermediate_code_internal(tilegx_env_get_cpu(env), tb, true); +} + +void restore_state_to_opc(CPUTLGState *env, TranslationBlock *tb, int pc_pos) +{ + env->pc = tcg_ctx.gen_opc_pc[pc_pos]; +} + +void tilegx_tcg_init(void) +{ + int i; + + cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); + cpu_pc = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUTLGState, pc), "pc"); + for (i = 0; i < TILEGX_R_COUNT; i++) { + cpu_regs[i] = tcg_global_mem_new_i64(TCG_AREG0, + offsetof(CPUTLGState, regs[i]), + reg_names[i]); + } +} diff --git a/tests/.gitignore b/tests/.gitignore index ccc92e4761..2c5e2c31e4 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -12,6 +12,13 @@ test-bitops test-coroutine test-crypto-cipher test-crypto-hash +test-crypto-tlscredsx509 +test-crypto-tlscredsx509-work/ +test-crypto-tlscredsx509-certs/ +test-crypto-tlssession +test-crypto-tlssession-work/ +test-crypto-tlssession-client/ +test-crypto-tlssession-server/ test-cutils test-hbitmap test-int128 diff --git a/tests/Makefile b/tests/Makefile index 34c6136db3..7c6025a2c5 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,5 +1,7 @@ export SRC_PATH +qapi-py = $(SRC_PATH)/scripts/qapi.py $(SRC_PATH)/scripts/ordereddict.py + # Get the list of all supported sysemu targets SYSEMU_TARGET_LIST := $(subst -softmmu.mak,,$(notdir \ $(wildcard $(SRC_PATH)/default-configs/*-softmmu.mak))) @@ -76,6 +78,8 @@ check-unit-y += tests/test-write-threshold$(EXESUF) gcov-files-test-write-threshold-y = block/write-threshold.c check-unit-$(CONFIG_GNUTLS_HASH) += tests/test-crypto-hash$(EXESUF) check-unit-y += tests/test-crypto-cipher$(EXESUF) +check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlscredsx509$(EXESUF) +check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlssession$(EXESUF) check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh @@ -221,7 +225,8 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \ comments.json empty.json enum-empty.json enum-missing-data.json \ enum-wrong-data.json enum-int-member.json enum-dict-member.json \ enum-clash-member.json enum-max-member.json enum-union-clash.json \ - enum-bad-name.json funny-char.json indented-expr.json \ + enum-bad-name.json enum-bad-prefix.json \ + funny-char.json indented-expr.json \ missing-type.json bad-ident.json ident-with-escape.json \ escape-outside-string.json unknown-escape.json \ escape-too-short.json escape-too-big.json unicode-str.json \ @@ -275,47 +280,50 @@ test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \ tests/test-opts-visitor.o tests/test-qmp-event.o \ tests/rcutorture.o tests/test-rcu-list.o -test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o \ - tests/test-qapi-event.o - $(test-obj-y): QEMU_INCLUDES += -Itests QEMU_CFLAGS += -I$(SRC_PATH)/tests -qom-core-obj = qom/object.o qom/qom-qobject.o qom/container.o qom/object_interfaces.o - -tests/check-qint$(EXESUF): tests/check-qint.o libqemuutil.a -tests/check-qstring$(EXESUF): tests/check-qstring.o libqemuutil.a -tests/check-qdict$(EXESUF): tests/check-qdict.o libqemuutil.a -tests/check-qlist$(EXESUF): tests/check-qlist.o libqemuutil.a -tests/check-qfloat$(EXESUF): tests/check-qfloat.o libqemuutil.a -tests/check-qjson$(EXESUF): tests/check-qjson.o libqemuutil.a libqemustub.a -tests/check-qom-interface$(EXESUF): tests/check-qom-interface.o $(qom-core-obj) libqemuutil.a libqemustub.a -tests/check-qom-proplist$(EXESUF): tests/check-qom-proplist.o $(qom-core-obj) libqemuutil.a libqemustub.a -tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(block-obj-y) libqemuutil.a libqemustub.a -tests/test-aio$(EXESUF): tests/test-aio.o $(block-obj-y) libqemuutil.a libqemustub.a -tests/test-rfifolock$(EXESUF): tests/test-rfifolock.o libqemuutil.a libqemustub.a -tests/test-throttle$(EXESUF): tests/test-throttle.o $(block-obj-y) libqemuutil.a libqemustub.a -tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(block-obj-y) libqemuutil.a libqemustub.a -tests/test-iov$(EXESUF): tests/test-iov.o libqemuutil.a -tests/test-hbitmap$(EXESUF): tests/test-hbitmap.o libqemuutil.a libqemustub.a + + +# Deps that are common to various different sets of tests below +test-util-obj-y = libqemuutil.a libqemustub.a +test-qom-obj-y = $(qom-obj-y) $(test-util-obj-y) +test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o \ + tests/test-qapi-event.o \ + $(test-qom-obj-y) +test-crypto-obj-y = $(crypto-obj-y) $(test-qom-obj-y) +test-block-obj-y = $(block-obj-y) $(test-crypto-obj-y) + +tests/check-qint$(EXESUF): tests/check-qint.o $(test-util-obj-y) +tests/check-qstring$(EXESUF): tests/check-qstring.o $(test-util-obj-y) +tests/check-qdict$(EXESUF): tests/check-qdict.o $(test-util-obj-y) +tests/check-qlist$(EXESUF): tests/check-qlist.o $(test-util-obj-y) +tests/check-qfloat$(EXESUF): tests/check-qfloat.o $(test-util-obj-y) +tests/check-qjson$(EXESUF): tests/check-qjson.o $(test-util-obj-y) +tests/check-qom-interface$(EXESUF): tests/check-qom-interface.o $(test-qom-obj-y) +tests/check-qom-proplist$(EXESUF): tests/check-qom-proplist.o $(test-qom-obj-y) +tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y) +tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y) +tests/test-rfifolock$(EXESUF): tests/test-rfifolock.o $(test-util-obj-y) +tests/test-throttle$(EXESUF): tests/test-throttle.o $(test-block-obj-y) +tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(test-block-obj-y) +tests/test-iov$(EXESUF): tests/test-iov.o $(test-util-obj-y) +tests/test-hbitmap$(EXESUF): tests/test-hbitmap.o $(test-util-obj-y) tests/test-x86-cpuid$(EXESUF): tests/test-x86-cpuid.o -tests/test-xbzrle$(EXESUF): tests/test-xbzrle.o migration/xbzrle.o page_cache.o libqemuutil.a +tests/test-xbzrle$(EXESUF): tests/test-xbzrle.o migration/xbzrle.o page_cache.o $(test-util-obj-y) tests/test-cutils$(EXESUF): tests/test-cutils.o util/cutils.o tests/test-int128$(EXESUF): tests/test-int128.o -tests/rcutorture$(EXESUF): tests/rcutorture.o libqemuutil.a libqemustub.a -tests/test-rcu-list$(EXESUF): tests/test-rcu-list.o libqemuutil.a libqemustub.a +tests/rcutorture$(EXESUF): tests/rcutorture.o $(test-util-obj-y) +tests/test-rcu-list$(EXESUF): tests/test-rcu-list.o $(test-util-obj-y) tests/test-qdev-global-props$(EXESUF): tests/test-qdev-global-props.o \ hw/core/qdev.o hw/core/qdev-properties.o hw/core/hotplug.o\ hw/core/irq.o \ hw/core/fw-path-provider.o \ - $(qom-core-obj) \ - $(test-qapi-obj-y) \ - libqemuutil.a libqemustub.a + $(test-qapi-obj-y) tests/test-vmstate$(EXESUF): tests/test-vmstate.o \ migration/vmstate.o migration/qemu-file.o migration/qemu-file-buf.o \ migration/qemu-file-unix.o qjson.o \ - $(qom-core-obj) \ - libqemuutil.a libqemustub.a + $(test-qom-obj-y) tests/test-qapi-types.c tests/test-qapi-types.h :\ $(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py) @@ -338,20 +346,24 @@ $(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-eve $(gen-out-type) -o tests -p "test-" $<, \ " GEN $@") -tests/test-string-output-visitor$(EXESUF): tests/test-string-output-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a -tests/test-string-input-visitor$(EXESUF): tests/test-string-input-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a -tests/test-qmp-event$(EXESUF): tests/test-qmp-event.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a -tests/test-qmp-output-visitor$(EXESUF): tests/test-qmp-output-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a -tests/test-qmp-input-visitor$(EXESUF): tests/test-qmp-input-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a -tests/test-qmp-input-strict$(EXESUF): tests/test-qmp-input-strict.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a -tests/test-qmp-commands$(EXESUF): tests/test-qmp-commands.o tests/test-qmp-marshal.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a -tests/test-visitor-serialization$(EXESUF): tests/test-visitor-serialization.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a -tests/test-opts-visitor$(EXESUF): tests/test-opts-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a - -tests/test-mul64$(EXESUF): tests/test-mul64.o libqemuutil.a -tests/test-bitops$(EXESUF): tests/test-bitops.o libqemuutil.a -tests/test-crypto-hash$(EXESUF): tests/test-crypto-hash.o libqemuutil.a libqemustub.a -tests/test-crypto-cipher$(EXESUF): tests/test-crypto-cipher.o libqemuutil.a libqemustub.a +tests/test-string-output-visitor$(EXESUF): tests/test-string-output-visitor.o $(test-qapi-obj-y) +tests/test-string-input-visitor$(EXESUF): tests/test-string-input-visitor.o $(test-qapi-obj-y) +tests/test-qmp-event$(EXESUF): tests/test-qmp-event.o $(test-qapi-obj-y) +tests/test-qmp-output-visitor$(EXESUF): tests/test-qmp-output-visitor.o $(test-qapi-obj-y) +tests/test-qmp-input-visitor$(EXESUF): tests/test-qmp-input-visitor.o $(test-qapi-obj-y) +tests/test-qmp-input-strict$(EXESUF): tests/test-qmp-input-strict.o $(test-qapi-obj-y) +tests/test-qmp-commands$(EXESUF): tests/test-qmp-commands.o tests/test-qmp-marshal.o $(test-qapi-obj-y) +tests/test-visitor-serialization$(EXESUF): tests/test-visitor-serialization.o $(test-qapi-obj-y) +tests/test-opts-visitor$(EXESUF): tests/test-opts-visitor.o $(test-qapi-obj-y) + +tests/test-mul64$(EXESUF): tests/test-mul64.o $(test-util-obj-y) +tests/test-bitops$(EXESUF): tests/test-bitops.o $(test-util-obj-y) +tests/test-crypto-hash$(EXESUF): tests/test-crypto-hash.o $(test-crypto-obj-y) +tests/test-crypto-cipher$(EXESUF): tests/test-crypto-cipher.o $(test-crypto-obj-y) +tests/test-crypto-tlscredsx509$(EXESUF): tests/test-crypto-tlscredsx509.o \ + tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o $(test-crypto-obj-y) +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) libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o @@ -414,12 +426,14 @@ tests/usb-hcd-xhci-test$(EXESUF): tests/usb-hcd-xhci-test.o $(libqos-usb-obj-y) tests/pc-cpu-test$(EXESUF): tests/pc-cpu-test.o tests/vhost-user-test$(EXESUF): tests/vhost-user-test.o qemu-char.o qemu-timer.o $(qtest-obj-y) tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o -tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o libqemuutil.a libqemustub.a -tests/test-write-threshold$(EXESUF): tests/test-write-threshold.o $(block-obj-y) libqemuutil.a libqemustub.a +tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o $(test-util-obj-y) +tests/test-write-threshold$(EXESUF): tests/test-write-threshold.o $(test-block-obj-y) ifeq ($(CONFIG_POSIX),y) LIBS += -lutil endif +LIBS += $(TEST_LIBS) +CFLAGS += $(TEST_CFLAGS) # QTest rules @@ -429,7 +443,7 @@ QTEST_TARGETS=$(foreach TARGET,$(TARGETS), $(if $(check-qtest-$(TARGET)-y), $(TA check-qtest-y=$(foreach TARGET,$(TARGETS), $(check-qtest-$(TARGET)-y)) endif -qtest-obj-y = tests/libqtest.o libqemuutil.a libqemustub.a +qtest-obj-y = tests/libqtest.o $(test-util-obj-y) $(check-qtest-y): $(qtest-obj-y) .PHONY: check-help diff --git a/tests/crypto-tls-x509-helpers.c b/tests/crypto-tls-x509-helpers.c new file mode 100644 index 0000000000..c5de67baaf --- /dev/null +++ b/tests/crypto-tls-x509-helpers.c @@ -0,0 +1,485 @@ +/* + * Copyright (C) 2015 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: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <stdlib.h> +#include <fcntl.h> + +#include "config-host.h" +#include "crypto-tls-x509-helpers.h" +#include "qemu/sockets.h" + +#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT + +/* + * This stores some static data that is needed when + * encoding extensions in the x509 certs + */ +ASN1_TYPE pkix_asn1; + +/* + * To avoid consuming random entropy to generate keys, + * here's one we prepared earlier :-) + */ +gnutls_x509_privkey_t privkey; +# define PRIVATE_KEY \ + "-----BEGIN PRIVATE KEY-----\n" \ + "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBALVcr\n" \ + "BL40Tm6yq88FBhJNw1aaoCjmtg0l4dWQZ/e9Fimx4ARxFpT+ji4FE\n" \ + "Cgl9s/SGqC+1nvlkm9ViSo0j7MKDbnDB+VRHDvMAzQhA2X7e8M0n9\n" \ + "rPolUY2lIVC83q0BBaOBkCj2RSmT2xTEbbC2xLukSrg2WP/ihVOxc\n" \ + "kXRuyFtzAgMBAAECgYB7slBexDwXrtItAMIH6m/U+LUpNe0Xx48OL\n" \ + "IOn4a4whNgO/o84uIwygUK27ZGFZT0kAGAk8CdF9hA6ArcbQ62s1H\n" \ + "myxrUbF9/mrLsQw1NEqpuUk9Ay2Tx5U/wPx35S3W/X2AvR/ZpTnCn\n" \ + "2q/7ym9fyiSoj86drD7BTvmKXlOnOwQJBAPOFMp4mMa9NGpGuEssO\n" \ + "m3Uwbp6lhcP0cA9MK+iOmeANpoKWfBdk5O34VbmeXnGYWEkrnX+9J\n" \ + "bM4wVhnnBWtgBMCQQC+qAEmvwcfhauERKYznMVUVksyeuhxhCe7EK\n" \ + "mPh+U2+g0WwdKvGDgO0PPt1gq0ILEjspMDeMHVdTwkaVBo/uMhAkA\n" \ + "Z5SsZyCP2aTOPFDypXRdI4eqRcjaEPOUBq27r3uYb/jeboVb2weLa\n" \ + "L1MmVuHiIHoa5clswPdWVI2y0em2IGoDAkBPSp/v9VKJEZabk9Frd\n" \ + "a+7u4fanrM9QrEjY3KhduslSilXZZSxrWjjAJPyPiqFb3M8XXA26W\n" \ + "nz1KYGnqYKhLcBAkB7dt57n9xfrhDpuyVEv+Uv1D3VVAhZlsaZ5Pp\n" \ + "dcrhrkJn2sa/+O8OKvdrPSeeu/N5WwYhJf61+CPoenMp7IFci\n" \ + "-----END PRIVATE KEY-----\n" + +/* + * This loads the private key we defined earlier + */ +static gnutls_x509_privkey_t test_tls_load_key(void) +{ + gnutls_x509_privkey_t key; + const gnutls_datum_t data = { (unsigned char *)PRIVATE_KEY, + strlen(PRIVATE_KEY) }; + int err; + + err = gnutls_x509_privkey_init(&key); + if (err < 0) { + g_critical("Failed to init key %s", gnutls_strerror(err)); + abort(); + } + + err = gnutls_x509_privkey_import(key, &data, + GNUTLS_X509_FMT_PEM); + if (err < 0) { + if (err != GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR && + err != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + g_critical("Failed to import key %s", gnutls_strerror(err)); + abort(); + } + + err = gnutls_x509_privkey_import_pkcs8( + key, &data, GNUTLS_X509_FMT_PEM, NULL, 0); + if (err < 0) { + g_critical("Failed to import PKCS8 key %s", gnutls_strerror(err)); + abort(); + } + } + + return key; +} + + +void test_tls_init(const char *keyfile) +{ + gnutls_global_init(); + + if (asn1_array2tree(pkix_asn1_tab, &pkix_asn1, NULL) != ASN1_SUCCESS) { + abort(); + } + + privkey = test_tls_load_key(); + if (!g_file_set_contents(keyfile, PRIVATE_KEY, -1, NULL)) { + abort(); + } +} + + +void test_tls_cleanup(const char *keyfile) +{ + asn1_delete_structure(&pkix_asn1); + unlink(keyfile); +} + +/* + * Turns an ASN1 object into a DER encoded byte array + */ +static void test_tls_der_encode(ASN1_TYPE src, + const char *src_name, + gnutls_datum_t *res) +{ + int size; + char *data = NULL; + + size = 0; + asn1_der_coding(src, src_name, NULL, &size, NULL); + + data = g_new0(char, size); + + asn1_der_coding(src, src_name, data, &size, NULL); + + res->data = (unsigned char *)data; + res->size = size; +} + + +static void +test_tls_get_ipaddr(const char *addrstr, + char **data, + int *datalen) +{ + struct addrinfo *res; + struct addrinfo hints; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + g_assert(getaddrinfo(addrstr, NULL, &hints, &res) == 0); + + *datalen = res->ai_addrlen; + *data = g_new(char, *datalen); + memcpy(*data, res->ai_addr, *datalen); +} + +/* + * This is a fairly lame x509 certificate generator. + * + * Do not copy/use this code for generating real certificates + * since it leaves out many things that you would want in + * certificates for real world usage. + * + * This is good enough only for doing tests of the QEMU + * TLS certificate code + */ +void +test_tls_generate_cert(QCryptoTLSTestCertReq *req, + gnutls_x509_crt_t ca) +{ + gnutls_x509_crt_t crt; + int err; + static char buffer[1024 * 1024]; + size_t size = sizeof(buffer); + char serial[5] = { 1, 2, 3, 4, 0 }; + gnutls_datum_t der; + time_t start = time(NULL) + (60 * 60 * req->start_offset); + time_t expire = time(NULL) + (60 * 60 * (req->expire_offset + ? req->expire_offset : 24)); + + /* + * Prepare our new certificate object + */ + err = gnutls_x509_crt_init(&crt); + if (err < 0) { + g_critical("Failed to initialize certificate %s", gnutls_strerror(err)); + abort(); + } + err = gnutls_x509_crt_set_key(crt, privkey); + if (err < 0) { + g_critical("Failed to set certificate key %s", gnutls_strerror(err)); + abort(); + } + + /* + * A v3 certificate is required in order to be able + * set any of the basic constraints, key purpose and + * key usage data + */ + gnutls_x509_crt_set_version(crt, 3); + + if (req->country) { + err = gnutls_x509_crt_set_dn_by_oid( + crt, GNUTLS_OID_X520_COUNTRY_NAME, 0, + req->country, strlen(req->country)); + if (err < 0) { + g_critical("Failed to set certificate country name %s", + gnutls_strerror(err)); + abort(); + } + } + if (req->cn) { + err = gnutls_x509_crt_set_dn_by_oid( + crt, GNUTLS_OID_X520_COMMON_NAME, 0, + req->cn, strlen(req->cn)); + if (err < 0) { + g_critical("Failed to set certificate common name %s", + gnutls_strerror(err)); + abort(); + } + } + + /* + * Setup the subject altnames, which are used + * for hostname checks in live sessions + */ + if (req->altname1) { + err = gnutls_x509_crt_set_subject_alt_name( + crt, GNUTLS_SAN_DNSNAME, + req->altname1, + strlen(req->altname1), + GNUTLS_FSAN_APPEND); + if (err < 0) { + g_critical("Failed to set certificate alt name %s", + gnutls_strerror(err)); + abort(); + } + } + if (req->altname2) { + err = gnutls_x509_crt_set_subject_alt_name( + crt, GNUTLS_SAN_DNSNAME, + req->altname2, + strlen(req->altname2), + GNUTLS_FSAN_APPEND); + if (err < 0) { + g_critical("Failed to set certificate %s alt name", + gnutls_strerror(err)); + abort(); + } + } + + /* + * IP address need to be put into the cert in their + * raw byte form, not strings, hence this is a little + * more complicated + */ + if (req->ipaddr1) { + char *data; + int len; + + test_tls_get_ipaddr(req->ipaddr1, &data, &len); + + err = gnutls_x509_crt_set_subject_alt_name( + crt, GNUTLS_SAN_IPADDRESS, + data, len, GNUTLS_FSAN_APPEND); + if (err < 0) { + g_critical("Failed to set certificate alt name %s", + gnutls_strerror(err)); + abort(); + } + g_free(data); + } + if (req->ipaddr2) { + char *data; + int len; + + test_tls_get_ipaddr(req->ipaddr2, &data, &len); + + err = gnutls_x509_crt_set_subject_alt_name( + crt, GNUTLS_SAN_IPADDRESS, + data, len, GNUTLS_FSAN_APPEND); + if (err < 0) { + g_critical("Failed to set certificate alt name %s", + gnutls_strerror(err)); + abort(); + } + g_free(data); + } + + + /* + * Basic constraints are used to decide if the cert + * is for a CA or not. We can't use the convenient + * gnutls API for setting this, since it hardcodes + * the 'critical' field which we want control over + */ + if (req->basicConstraintsEnable) { + ASN1_TYPE ext = ASN1_TYPE_EMPTY; + + asn1_create_element(pkix_asn1, "PKIX1.BasicConstraints", &ext); + asn1_write_value(ext, "cA", + req->basicConstraintsIsCA ? "TRUE" : "FALSE", 1); + asn1_write_value(ext, "pathLenConstraint", NULL, 0); + test_tls_der_encode(ext, "", &der); + err = gnutls_x509_crt_set_extension_by_oid( + crt, "2.5.29.19", + der.data, der.size, + req->basicConstraintsCritical); + if (err < 0) { + g_critical("Failed to set certificate basic constraints %s", + gnutls_strerror(err)); + g_free(der.data); + abort(); + } + asn1_delete_structure(&ext); + g_free(der.data); + } + + /* + * Next up the key usage extension. Again we can't + * use the gnutls API since it hardcodes the extension + * to be 'critical' + */ + if (req->keyUsageEnable) { + ASN1_TYPE ext = ASN1_TYPE_EMPTY; + char str[2]; + + str[0] = req->keyUsageValue & 0xff; + str[1] = (req->keyUsageValue >> 8) & 0xff; + + asn1_create_element(pkix_asn1, "PKIX1.KeyUsage", &ext); + asn1_write_value(ext, "", str, 9); + test_tls_der_encode(ext, "", &der); + err = gnutls_x509_crt_set_extension_by_oid( + crt, "2.5.29.15", + der.data, der.size, + req->keyUsageCritical); + if (err < 0) { + g_critical("Failed to set certificate key usage %s", + gnutls_strerror(err)); + g_free(der.data); + abort(); + } + asn1_delete_structure(&ext); + g_free(der.data); + } + + /* + * Finally the key purpose extension. This time + * gnutls has the opposite problem, always hardcoding + * it to be non-critical. So once again we have to + * set this the hard way building up ASN1 data ourselves + */ + if (req->keyPurposeEnable) { + ASN1_TYPE ext = ASN1_TYPE_EMPTY; + + asn1_create_element(pkix_asn1, "PKIX1.ExtKeyUsageSyntax", &ext); + if (req->keyPurposeOID1) { + asn1_write_value(ext, "", "NEW", 1); + asn1_write_value(ext, "?LAST", req->keyPurposeOID1, 1); + } + if (req->keyPurposeOID2) { + asn1_write_value(ext, "", "NEW", 1); + asn1_write_value(ext, "?LAST", req->keyPurposeOID2, 1); + } + test_tls_der_encode(ext, "", &der); + err = gnutls_x509_crt_set_extension_by_oid( + crt, "2.5.29.37", + der.data, der.size, + req->keyPurposeCritical); + if (err < 0) { + g_critical("Failed to set certificate key purpose %s", + gnutls_strerror(err)); + g_free(der.data); + abort(); + } + asn1_delete_structure(&ext); + g_free(der.data); + } + + /* + * Any old serial number will do, so lets pick 5 + */ + err = gnutls_x509_crt_set_serial(crt, serial, 5); + if (err < 0) { + g_critical("Failed to set certificate serial %s", + gnutls_strerror(err)); + abort(); + } + + err = gnutls_x509_crt_set_activation_time(crt, start); + if (err < 0) { + g_critical("Failed to set certificate activation %s", + gnutls_strerror(err)); + abort(); + } + err = gnutls_x509_crt_set_expiration_time(crt, expire); + if (err < 0) { + g_critical("Failed to set certificate expiration %s", + gnutls_strerror(err)); + abort(); + } + + + /* + * If no 'ca' is set then we are self signing + * the cert. This is done for the root CA certs + */ + err = gnutls_x509_crt_sign(crt, ca ? ca : crt, privkey); + if (err < 0) { + g_critical("Failed to sign certificate %s", + gnutls_strerror(err)); + abort(); + } + + /* + * Finally write the new cert out to disk + */ + err = gnutls_x509_crt_export( + crt, GNUTLS_X509_FMT_PEM, buffer, &size); + if (err < 0) { + g_critical("Failed to export certificate %s: %d", + gnutls_strerror(err), err); + abort(); + } + + if (!g_file_set_contents(req->filename, buffer, -1, NULL)) { + g_critical("Failed to write certificate %s", + req->filename); + abort(); + } + + req->crt = crt; +} + + +void test_tls_write_cert_chain(const char *filename, + gnutls_x509_crt_t *certs, + size_t ncerts) +{ + size_t i; + size_t capacity = 1024, offset = 0; + char *buffer = g_new0(char, capacity); + int err; + + for (i = 0; i < ncerts; i++) { + size_t len = capacity - offset; + retry: + err = gnutls_x509_crt_export(certs[i], GNUTLS_X509_FMT_PEM, + buffer + offset, &len); + if (err < 0) { + if (err == GNUTLS_E_SHORT_MEMORY_BUFFER) { + buffer = g_renew(char, buffer, offset + len); + capacity = offset + len; + goto retry; + } + g_critical("Failed to export certificate chain %s: %d", + gnutls_strerror(err), err); + abort(); + } + offset += len; + } + + if (!g_file_set_contents(filename, buffer, offset, NULL)) { + abort(); + } +} + + +void test_tls_discard_cert(QCryptoTLSTestCertReq *req) +{ + if (!req->crt) { + return; + } + + gnutls_x509_crt_deinit(req->crt); + req->crt = NULL; + + if (getenv("QEMU_TEST_DEBUG_CERTS") == NULL) { + unlink(req->filename); + } +} + +#endif /* QCRYPTO_HAVE_TLS_TEST_SUPPORT */ diff --git a/tests/crypto-tls-x509-helpers.h b/tests/crypto-tls-x509-helpers.h new file mode 100644 index 0000000000..356b49cd5a --- /dev/null +++ b/tests/crypto-tls-x509-helpers.h @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2015 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: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <gnutls/gnutls.h> +#include <gnutls/x509.h> + +#include <gnutls/gnutls.h> +#include <gnutls/x509.h> + +#if !(defined WIN32) && \ + defined(CONFIG_TASN1) && \ + defined(LIBGNUTLS_VERSION_NUMBER) && \ + (LIBGNUTLS_VERSION_NUMBER >= 0x020600) +# define QCRYPTO_HAVE_TLS_TEST_SUPPORT +#endif + +#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT +# include <libtasn1.h> + +# include "qemu-common.h" + +/* + * This contains parameter about how to generate + * certificates. + */ +typedef struct QCryptoTLSTestCertReq QCryptoTLSTestCertReq; +struct QCryptoTLSTestCertReq { + gnutls_x509_crt_t crt; + + const char *filename; + + /* Identifying information */ + const char *country; + const char *cn; + const char *altname1; + const char *altname2; + const char *ipaddr1; + const char *ipaddr2; + + /* Basic constraints */ + bool basicConstraintsEnable; + bool basicConstraintsCritical; + bool basicConstraintsIsCA; + + /* Key usage */ + bool keyUsageEnable; + bool keyUsageCritical; + int keyUsageValue; + + /* Key purpose (aka Extended key usage) */ + bool keyPurposeEnable; + bool keyPurposeCritical; + const char *keyPurposeOID1; + const char *keyPurposeOID2; + + /* zero for current time, or non-zero for hours from now */ + int start_offset; + /* zero for 24 hours from now, or non-zero for hours from now */ + int expire_offset; +}; + +void test_tls_generate_cert(QCryptoTLSTestCertReq *req, + gnutls_x509_crt_t ca); +void test_tls_write_cert_chain(const char *filename, + gnutls_x509_crt_t *certs, + size_t ncerts); +void test_tls_discard_cert(QCryptoTLSTestCertReq *req); + +void test_tls_init(const char *keyfile); +void test_tls_cleanup(const char *keyfile); + +# define TLS_CERT_REQ(varname, cavarname, \ + country, commonname, \ + altname1, altname2, \ + ipaddr1, ipaddr2, \ + basicconsenable, basicconscritical, basicconsca, \ + keyusageenable, keyusagecritical, keyusagevalue, \ + keypurposeenable, keypurposecritical, \ + keypurposeoid1, keypurposeoid2, \ + startoffset, endoffset) \ + static QCryptoTLSTestCertReq varname = { \ + NULL, WORKDIR #varname "-ctx.pem", \ + country, commonname, altname1, altname2, \ + ipaddr1, ipaddr2, \ + basicconsenable, basicconscritical, basicconsca, \ + keyusageenable, keyusagecritical, keyusagevalue, \ + keypurposeenable, keypurposecritical, \ + keypurposeoid1, keypurposeoid2, \ + startoffset, endoffset \ + }; \ + test_tls_generate_cert(&varname, cavarname.crt) + +# define TLS_ROOT_REQ(varname, \ + country, commonname, \ + altname1, altname2, \ + ipaddr1, ipaddr2, \ + basicconsenable, basicconscritical, basicconsca, \ + keyusageenable, keyusagecritical, keyusagevalue, \ + keypurposeenable, keypurposecritical, \ + keypurposeoid1, keypurposeoid2, \ + startoffset, endoffset) \ + static QCryptoTLSTestCertReq varname = { \ + NULL, WORKDIR #varname "-ctx.pem", \ + country, commonname, altname1, altname2, \ + ipaddr1, ipaddr2, \ + basicconsenable, basicconscritical, basicconsca, \ + keyusageenable, keyusagecritical, keyusagevalue, \ + keypurposeenable, keypurposecritical, \ + keypurposeoid1, keypurposeoid2, \ + startoffset, endoffset \ + }; \ + test_tls_generate_cert(&varname, NULL) + +extern const ASN1_ARRAY_TYPE pkix_asn1_tab[]; + +#endif /* QCRYPTO_HAVE_TLS_TEST_SUPPORT */ diff --git a/tests/pkix_asn1_tab.c b/tests/pkix_asn1_tab.c new file mode 100644 index 0000000000..5b4e6b9e2d --- /dev/null +++ b/tests/pkix_asn1_tab.c @@ -0,0 +1,1104 @@ +/* + * This file is taken from gnutls 1.6.3 under the GPLv2+ + * and is under copyright of various GNUTLS contributors. + */ + +#include <libtasn1.h> + +const ASN1_ARRAY_TYPE pkix_asn1_tab[] = { + {"PKIX1", 536875024, 0}, + {0, 1073741836, 0}, + {"id-ce", 1879048204, 0}, + {"joint-iso-ccitt", 1073741825, "2"}, + {"ds", 1073741825, "5"}, + {0, 1, "29"}, + {"id-ce-authorityKeyIdentifier", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "35"}, + {"AuthorityKeyIdentifier", 1610612741, 0}, + {"keyIdentifier", 1610637314, "KeyIdentifier"}, + {0, 4104, "0"}, + {"authorityCertIssuer", 1610637314, "GeneralNames"}, + {0, 4104, "1"}, + {"authorityCertSerialNumber", 536895490, "CertificateSerialNumber"}, + {0, 4104, "2"}, + {"KeyIdentifier", 1073741831, 0}, + {"id-ce-subjectKeyIdentifier", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "14"}, + {"SubjectKeyIdentifier", 1073741826, "KeyIdentifier"}, + {"id-ce-keyUsage", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "15"}, + {"KeyUsage", 1610874886, 0}, + {"digitalSignature", 1073741825, "0"}, + {"nonRepudiation", 1073741825, "1"}, + {"keyEncipherment", 1073741825, "2"}, + {"dataEncipherment", 1073741825, "3"}, + {"keyAgreement", 1073741825, "4"}, + {"keyCertSign", 1073741825, "5"}, + {"cRLSign", 1073741825, "6"}, + {"encipherOnly", 1073741825, "7"}, + {"decipherOnly", 1, "8"}, + {"id-ce-privateKeyUsagePeriod", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "16"}, + {"PrivateKeyUsagePeriod", 1610612741, 0}, + {"notBefore", 1619025937, 0}, + {0, 4104, "0"}, + {"notAfter", 545284113, 0}, + {0, 4104, "1"}, + {"id-ce-certificatePolicies", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "32"}, + {"CertificatePolicies", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "PolicyInformation"}, + {"PolicyInformation", 1610612741, 0}, + {"policyIdentifier", 1073741826, "CertPolicyId"}, + {"policyQualifiers", 538984459, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "PolicyQualifierInfo"}, + {"CertPolicyId", 1073741836, 0}, + {"PolicyQualifierInfo", 1610612741, 0}, + {"policyQualifierId", 1073741826, "PolicyQualifierId"}, + {"qualifier", 541065229, 0}, + {"policyQualifierId", 1, 0}, + {"PolicyQualifierId", 1073741836, 0}, + {"CPSuri", 1073741826, "IA5String"}, + {"UserNotice", 1610612741, 0}, + {"noticeRef", 1073758210, "NoticeReference"}, + {"explicitText", 16386, "DisplayText"}, + {"NoticeReference", 1610612741, 0}, + {"organization", 1073741826, "DisplayText"}, + {"noticeNumbers", 536870923, 0}, + {0, 3, 0}, + {"DisplayText", 1610612754, 0}, + {"visibleString", 1612709890, "VisibleString"}, + {"200", 524298, "1"}, + {"bmpString", 1612709890, "BMPString"}, + {"200", 524298, "1"}, + {"utf8String", 538968066, "UTF8String"}, + {"200", 524298, "1"}, + {"id-ce-policyMappings", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "33"}, + {"PolicyMappings", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 536870917, 0}, + {"issuerDomainPolicy", 1073741826, "CertPolicyId"}, + {"subjectDomainPolicy", 2, "CertPolicyId"}, + {"DirectoryString", 1610612754, 0}, + {"teletexString", 1612709890, "TeletexString"}, + {"MAX", 524298, "1"}, + {"printableString", 1612709890, "PrintableString"}, + {"MAX", 524298, "1"}, + {"universalString", 1612709890, "UniversalString"}, + {"MAX", 524298, "1"}, + {"utf8String", 1612709890, "UTF8String"}, + {"MAX", 524298, "1"}, + {"bmpString", 1612709890, "BMPString"}, + {"MAX", 524298, "1"}, + {"ia5String", 538968066, "IA5String"}, + {"MAX", 524298, "1"}, + {"id-ce-subjectAltName", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "17"}, + {"SubjectAltName", 1073741826, "GeneralNames"}, + {"GeneralNames", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "GeneralName"}, + {"GeneralName", 1610612754, 0}, + {"otherName", 1610620930, "AnotherName"}, + {0, 4104, "0"}, + {"rfc822Name", 1610620930, "IA5String"}, + {0, 4104, "1"}, + {"dNSName", 1610620930, "IA5String"}, + {0, 4104, "2"}, + {"x400Address", 1610620930, "ORAddress"}, + {0, 4104, "3"}, + {"directoryName", 1610620930, "RDNSequence"}, + {0, 2056, "4"}, + {"ediPartyName", 1610620930, "EDIPartyName"}, + {0, 4104, "5"}, + {"uniformResourceIdentifier", 1610620930, "IA5String"}, + {0, 4104, "6"}, + {"iPAddress", 1610620935, 0}, + {0, 4104, "7"}, + {"registeredID", 536879116, 0}, + {0, 4104, "8"}, + {"AnotherName", 1610612741, 0}, + {"type-id", 1073741836, 0}, + {"value", 541073421, 0}, + {0, 1073743880, "0"}, + {"type-id", 1, 0}, + {"EDIPartyName", 1610612741, 0}, + {"nameAssigner", 1610637314, "DirectoryString"}, + {0, 4104, "0"}, + {"partyName", 536879106, "DirectoryString"}, + {0, 4104, "1"}, + {"id-ce-issuerAltName", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "18"}, + {"IssuerAltName", 1073741826, "GeneralNames"}, + {"id-ce-subjectDirectoryAttributes", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "9"}, + {"SubjectDirectoryAttributes", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "Attribute"}, + {"id-ce-basicConstraints", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "19"}, + {"BasicConstraints", 1610612741, 0}, + {"cA", 1610645508, 0}, + {0, 131081, 0}, + {"pathLenConstraint", 537411587, 0}, + {"0", 10, "MAX"}, + {"id-ce-nameConstraints", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "30"}, + {"NameConstraints", 1610612741, 0}, + {"permittedSubtrees", 1610637314, "GeneralSubtrees"}, + {0, 4104, "0"}, + {"excludedSubtrees", 536895490, "GeneralSubtrees"}, + {0, 4104, "1"}, + {"GeneralSubtrees", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "GeneralSubtree"}, + {"GeneralSubtree", 1610612741, 0}, + {"base", 1073741826, "GeneralName"}, + {"minimum", 1610653698, "BaseDistance"}, + {0, 1073741833, "0"}, + {0, 4104, "0"}, + {"maximum", 536895490, "BaseDistance"}, + {0, 4104, "1"}, + {"BaseDistance", 1611137027, 0}, + {"0", 10, "MAX"}, + {"id-ce-policyConstraints", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "36"}, + {"PolicyConstraints", 1610612741, 0}, + {"requireExplicitPolicy", 1610637314, "SkipCerts"}, + {0, 4104, "0"}, + {"inhibitPolicyMapping", 536895490, "SkipCerts"}, + {0, 4104, "1"}, + {"SkipCerts", 1611137027, 0}, + {"0", 10, "MAX"}, + {"id-ce-cRLDistributionPoints", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "31"}, + {"CRLDistributionPoints", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "DistributionPoint"}, + {"DistributionPoint", 1610612741, 0}, + {"distributionPoint", 1610637314, "DistributionPointName"}, + {0, 2056, "0"}, + {"reasons", 1610637314, "ReasonFlags"}, + {0, 4104, "1"}, + {"cRLIssuer", 536895490, "GeneralNames"}, + {0, 4104, "2"}, + {"DistributionPointName", 1610612754, 0}, + {"fullName", 1610620930, "GeneralNames"}, + {0, 4104, "0"}, + {"nameRelativeToCRLIssuer", 536879106, "RelativeDistinguishedName"}, + {0, 4104, "1"}, + {"ReasonFlags", 1610874886, 0}, + {"unused", 1073741825, "0"}, + {"keyCompromise", 1073741825, "1"}, + {"cACompromise", 1073741825, "2"}, + {"affiliationChanged", 1073741825, "3"}, + {"superseded", 1073741825, "4"}, + {"cessationOfOperation", 1073741825, "5"}, + {"certificateHold", 1073741825, "6"}, + {"privilegeWithdrawn", 1073741825, "7"}, + {"aACompromise", 1, "8"}, + {"id-ce-extKeyUsage", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "37"}, + {"ExtKeyUsageSyntax", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "KeyPurposeId"}, + {"KeyPurposeId", 1073741836, 0}, + {"id-kp-serverAuth", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "1"}, + {"id-kp-clientAuth", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "2"}, + {"id-kp-codeSigning", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "3"}, + {"id-kp-emailProtection", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "4"}, + {"id-kp-ipsecEndSystem", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "5"}, + {"id-kp-ipsecTunnel", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "6"}, + {"id-kp-ipsecUser", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "7"}, + {"id-kp-timeStamping", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "8"}, + {"id-pe-authorityInfoAccess", 1879048204, 0}, + {0, 1073741825, "id-pe"}, + {0, 1, "1"}, + {"AuthorityInfoAccessSyntax", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "AccessDescription"}, + {"AccessDescription", 1610612741, 0}, + {"accessMethod", 1073741836, 0}, + {"accessLocation", 2, "GeneralName"}, + {"id-ce-cRLNumber", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "20"}, + {"CRLNumber", 1611137027, 0}, + {"0", 10, "MAX"}, + {"id-ce-issuingDistributionPoint", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "28"}, + {"IssuingDistributionPoint", 1610612741, 0}, + {"distributionPoint", 1610637314, "DistributionPointName"}, + {0, 4104, "0"}, + {"onlyContainsUserCerts", 1610653700, 0}, + {0, 1073872905, 0}, + {0, 4104, "1"}, + {"onlyContainsCACerts", 1610653700, 0}, + {0, 1073872905, 0}, + {0, 4104, "2"}, + {"onlySomeReasons", 1610637314, "ReasonFlags"}, + {0, 4104, "3"}, + {"indirectCRL", 536911876, 0}, + {0, 1073872905, 0}, + {0, 4104, "4"}, + {"id-ce-deltaCRLIndicator", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "27"}, + {"BaseCRLNumber", 1073741826, "CRLNumber"}, + {"id-ce-cRLReasons", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "21"}, + {"CRLReason", 1610874901, 0}, + {"unspecified", 1073741825, "0"}, + {"keyCompromise", 1073741825, "1"}, + {"cACompromise", 1073741825, "2"}, + {"affiliationChanged", 1073741825, "3"}, + {"superseded", 1073741825, "4"}, + {"cessationOfOperation", 1073741825, "5"}, + {"certificateHold", 1073741825, "6"}, + {"removeFromCRL", 1, "8"}, + {"id-ce-certificateIssuer", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "29"}, + {"CertificateIssuer", 1073741826, "GeneralNames"}, + {"id-ce-holdInstructionCode", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "23"}, + {"HoldInstructionCode", 1073741836, 0}, + {"holdInstruction", 1879048204, 0}, + {"joint-iso-itu-t", 1073741825, "2"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"x9cm", 1073741825, "10040"}, + {0, 1, "2"}, + {"id-holdinstruction-none", 1879048204, 0}, + {0, 1073741825, "holdInstruction"}, + {0, 1, "1"}, + {"id-holdinstruction-callissuer", 1879048204, 0}, + {0, 1073741825, "holdInstruction"}, + {0, 1, "2"}, + {"id-holdinstruction-reject", 1879048204, 0}, + {0, 1073741825, "holdInstruction"}, + {0, 1, "3"}, + {"id-ce-invalidityDate", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "24"}, + {"InvalidityDate", 1082130449, 0}, + {"VisibleString", 1610620935, 0}, + {0, 4360, "26"}, + {"NumericString", 1610620935, 0}, + {0, 4360, "18"}, + {"IA5String", 1610620935, 0}, + {0, 4360, "22"}, + {"TeletexString", 1610620935, 0}, + {0, 4360, "20"}, + {"PrintableString", 1610620935, 0}, + {0, 4360, "19"}, + {"UniversalString", 1610620935, 0}, + {0, 4360, "28"}, + {"BMPString", 1610620935, 0}, + {0, 4360, "30"}, + {"UTF8String", 1610620935, 0}, + {0, 4360, "12"}, + {"id-pkix", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"identified-organization", 1073741825, "3"}, + {"dod", 1073741825, "6"}, + {"internet", 1073741825, "1"}, + {"security", 1073741825, "5"}, + {"mechanisms", 1073741825, "5"}, + {"pkix", 1, "7"}, + {"id-pe", 1879048204, 0}, + {0, 1073741825, "id-pkix"}, + {0, 1, "1"}, + {"id-qt", 1879048204, 0}, + {0, 1073741825, "id-pkix"}, + {0, 1, "2"}, + {"id-kp", 1879048204, 0}, + {0, 1073741825, "id-pkix"}, + {0, 1, "3"}, + {"id-ad", 1879048204, 0}, + {0, 1073741825, "id-pkix"}, + {0, 1, "48"}, + {"id-qt-cps", 1879048204, 0}, + {0, 1073741825, "id-qt"}, + {0, 1, "1"}, + {"id-qt-unotice", 1879048204, 0}, + {0, 1073741825, "id-qt"}, + {0, 1, "2"}, + {"id-ad-ocsp", 1879048204, 0}, + {0, 1073741825, "id-ad"}, + {0, 1, "1"}, + {"id-ad-caIssuers", 1879048204, 0}, + {0, 1073741825, "id-ad"}, + {0, 1, "2"}, + {"Attribute", 1610612741, 0}, + {"type", 1073741826, "AttributeType"}, + {"values", 536870927, 0}, + {0, 2, "AttributeValue"}, + {"AttributeType", 1073741836, 0}, + {"AttributeValue", 1614807053, 0}, + {"type", 1, 0}, + {"AttributeTypeAndValue", 1610612741, 0}, + {"type", 1073741826, "AttributeType"}, + {"value", 2, "AttributeValue"}, + {"id-at", 1879048204, 0}, + {"joint-iso-ccitt", 1073741825, "2"}, + {"ds", 1073741825, "5"}, + {0, 1, "4"}, + {"id-at-initials", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "43"}, + {"X520initials", 1073741826, "DirectoryString"}, + {"id-at-generationQualifier", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "44"}, + {"X520generationQualifier", 1073741826, "DirectoryString"}, + {"id-at-surname", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "4"}, + {"X520surName", 1073741826, "DirectoryString"}, + {"id-at-givenName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "42"}, + {"X520givenName", 1073741826, "DirectoryString"}, + {"id-at-name", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "41"}, + {"X520name", 1073741826, "DirectoryString"}, + {"id-at-commonName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "3"}, + {"X520CommonName", 1073741826, "DirectoryString"}, + {"id-at-localityName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "7"}, + {"X520LocalityName", 1073741826, "DirectoryString"}, + {"id-at-stateOrProvinceName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "8"}, + {"X520StateOrProvinceName", 1073741826, "DirectoryString"}, + {"id-at-organizationName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "10"}, + {"X520OrganizationName", 1073741826, "DirectoryString"}, + {"id-at-organizationalUnitName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "11"}, + {"X520OrganizationalUnitName", 1073741826, "DirectoryString"}, + {"id-at-title", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "12"}, + {"X520Title", 1073741826, "DirectoryString"}, + {"id-at-description", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "13"}, + {"X520Description", 1073741826, "DirectoryString"}, + {"id-at-dnQualifier", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "46"}, + {"X520dnQualifier", 1073741826, "PrintableString"}, + {"id-at-countryName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "6"}, + {"X520countryName", 1612709890, "PrintableString"}, + {0, 1048586, "2"}, + {"id-at-serialNumber", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "5"}, + {"X520serialNumber", 1073741826, "PrintableString"}, + {"id-at-telephoneNumber", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "20"}, + {"X520telephoneNumber", 1073741826, "PrintableString"}, + {"id-at-facsimileTelephoneNumber", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "23"}, + {"X520facsimileTelephoneNumber", 1073741826, "PrintableString"}, + {"id-at-pseudonym", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "65"}, + {"X520pseudonym", 1073741826, "DirectoryString"}, + {"id-at-name", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "41"}, + {"X520name", 1073741826, "DirectoryString"}, + {"id-at-streetAddress", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "9"}, + {"X520streetAddress", 1073741826, "DirectoryString"}, + {"id-at-postalAddress", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "16"}, + {"X520postalAddress", 1073741826, "PostalAddress"}, + {"PostalAddress", 1610612747, 0}, + {0, 2, "DirectoryString"}, + {"pkcs", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"rsadsi", 1073741825, "113549"}, + {"pkcs", 1, "1"}, + {"pkcs-9", 1879048204, 0}, + {0, 1073741825, "pkcs"}, + {0, 1, "9"}, + {"emailAddress", 1880096780, "AttributeType"}, + {0, 1073741825, "pkcs-9"}, + {0, 1, "1"}, + {"Pkcs9email", 1612709890, "IA5String"}, + {"ub-emailaddress-length", 524298, "1"}, + {"Name", 1610612754, 0}, + {"rdnSequence", 2, "RDNSequence"}, + {"RDNSequence", 1610612747, 0}, + {0, 2, "RelativeDistinguishedName"}, + {"DistinguishedName", 1073741826, "RDNSequence"}, + {"RelativeDistinguishedName", 1612709903, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "AttributeTypeAndValue"}, + {"Certificate", 1610612741, 0}, + {"tbsCertificate", 1073741826, "TBSCertificate"}, + {"signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + {"signature", 6, 0}, + {"TBSCertificate", 1610612741, 0}, + {"version", 1610653698, "Version"}, + {0, 1073741833, "v1"}, + {0, 2056, "0"}, + {"serialNumber", 1073741826, "CertificateSerialNumber"}, + {"signature", 1073741826, "AlgorithmIdentifier"}, + {"issuer", 1073741826, "Name"}, + {"validity", 1073741826, "Validity"}, + {"subject", 1073741826, "Name"}, + {"subjectPublicKeyInfo", 1073741826, "SubjectPublicKeyInfo"}, + {"issuerUniqueID", 1610637314, "UniqueIdentifier"}, + {0, 4104, "1"}, + {"subjectUniqueID", 1610637314, "UniqueIdentifier"}, + {0, 4104, "2"}, + {"extensions", 536895490, "Extensions"}, + {0, 2056, "3"}, + {"Version", 1610874883, 0}, + {"v1", 1073741825, "0"}, + {"v2", 1073741825, "1"}, + {"v3", 1, "2"}, + {"CertificateSerialNumber", 1073741827, 0}, + {"Validity", 1610612741, 0}, + {"notBefore", 1073741826, "Time"}, + {"notAfter", 2, "Time"}, + {"Time", 1610612754, 0}, + {"utcTime", 1090519057, 0}, + {"generalTime", 8388625, 0}, + {"UniqueIdentifier", 1073741830, 0}, + {"SubjectPublicKeyInfo", 1610612741, 0}, + {"algorithm", 1073741826, "AlgorithmIdentifier"}, + {"subjectPublicKey", 6, 0}, + {"Extensions", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "Extension"}, + {"Extension", 1610612741, 0}, + {"extnID", 1073741836, 0}, + {"critical", 1610645508, 0}, + {0, 131081, 0}, + {"extnValue", 7, 0}, + {"CertificateList", 1610612741, 0}, + {"tbsCertList", 1073741826, "TBSCertList"}, + {"signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + {"signature", 6, 0}, + {"TBSCertList", 1610612741, 0}, + {"version", 1073758210, "Version"}, + {"signature", 1073741826, "AlgorithmIdentifier"}, + {"issuer", 1073741826, "Name"}, + {"thisUpdate", 1073741826, "Time"}, + {"nextUpdate", 1073758210, "Time"}, + {"revokedCertificates", 1610629131, 0}, + {0, 536870917, 0}, + {"userCertificate", 1073741826, "CertificateSerialNumber"}, + {"revocationDate", 1073741826, "Time"}, + {"crlEntryExtensions", 16386, "Extensions"}, + {"crlExtensions", 536895490, "Extensions"}, + {0, 2056, "0"}, + {"AlgorithmIdentifier", 1610612741, 0}, + {"algorithm", 1073741836, 0}, + {"parameters", 541081613, 0}, + {"algorithm", 1, 0}, + {"pkcs-1", 1879048204, 0}, + {0, 1073741825, "pkcs"}, + {0, 1, "1"}, + {"rsaEncryption", 1879048204, 0}, + {0, 1073741825, "pkcs-1"}, + {0, 1, "1"}, + {"md2WithRSAEncryption", 1879048204, 0}, + {0, 1073741825, "pkcs-1"}, + {0, 1, "2"}, + {"md5WithRSAEncryption", 1879048204, 0}, + {0, 1073741825, "pkcs-1"}, + {0, 1, "4"}, + {"sha1WithRSAEncryption", 1879048204, 0}, + {0, 1073741825, "pkcs-1"}, + {0, 1, "5"}, + {"id-dsa-with-sha1", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"x9-57", 1073741825, "10040"}, + {"x9algorithm", 1073741825, "4"}, + {0, 1, "3"}, + {"Dss-Sig-Value", 1610612741, 0}, + {"r", 1073741827, 0}, + {"s", 3, 0}, + {"dhpublicnumber", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"ansi-x942", 1073741825, "10046"}, + {"number-type", 1073741825, "2"}, + {0, 1, "1"}, + {"DomainParameters", 1610612741, 0}, + {"p", 1073741827, 0}, + {"g", 1073741827, 0}, + {"q", 1073741827, 0}, + {"j", 1073758211, 0}, + {"validationParms", 16386, "ValidationParms"}, + {"ValidationParms", 1610612741, 0}, + {"seed", 1073741830, 0}, + {"pgenCounter", 3, 0}, + {"id-dsa", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"x9-57", 1073741825, "10040"}, + {"x9algorithm", 1073741825, "4"}, + {0, 1, "1"}, + {"Dss-Parms", 1610612741, 0}, + {"p", 1073741827, 0}, + {"q", 1073741827, 0}, + {"g", 3, 0}, + {"ORAddress", 1610612741, 0}, + {"built-in-standard-attributes", 1073741826, "BuiltInStandardAttributes"}, + {"built-in-domain-defined-attributes", 1073758210, + "BuiltInDomainDefinedAttributes"}, + {"extension-attributes", 16386, "ExtensionAttributes"}, + {"BuiltInStandardAttributes", 1610612741, 0}, + {"country-name", 1073758210, "CountryName"}, + {"administration-domain-name", 1073758210, "AdministrationDomainName"}, + {"network-address", 1610637314, "NetworkAddress"}, + {0, 2056, "0"}, + {"terminal-identifier", 1610637314, "TerminalIdentifier"}, + {0, 2056, "1"}, + {"private-domain-name", 1610637314, "PrivateDomainName"}, + {0, 2056, "2"}, + {"organization-name", 1610637314, "OrganizationName"}, + {0, 2056, "3"}, + {"numeric-user-identifier", 1610637314, "NumericUserIdentifier"}, + {0, 2056, "4"}, + {"personal-name", 1610637314, "PersonalName"}, + {0, 2056, "5"}, + {"organizational-unit-names", 536895490, "OrganizationalUnitNames"}, + {0, 2056, "6"}, + {"CountryName", 1610620946, 0}, + {0, 1073746952, "1"}, + {"x121-dcc-code", 1612709890, "NumericString"}, + {0, 1048586, "ub-country-name-numeric-length"}, + {"iso-3166-alpha2-code", 538968066, "PrintableString"}, + {0, 1048586, "ub-country-name-alpha-length"}, + {"AdministrationDomainName", 1610620946, 0}, + {0, 1073744904, "2"}, + {"numeric", 1612709890, "NumericString"}, + {"ub-domain-name-length", 524298, "0"}, + {"printable", 538968066, "PrintableString"}, + {"ub-domain-name-length", 524298, "0"}, + {"NetworkAddress", 1073741826, "X121Address"}, + {"X121Address", 1612709890, "NumericString"}, + {"ub-x121-address-length", 524298, "1"}, + {"TerminalIdentifier", 1612709890, "PrintableString"}, + {"ub-terminal-id-length", 524298, "1"}, + {"PrivateDomainName", 1610612754, 0}, + {"numeric", 1612709890, "NumericString"}, + {"ub-domain-name-length", 524298, "1"}, + {"printable", 538968066, "PrintableString"}, + {"ub-domain-name-length", 524298, "1"}, + {"OrganizationName", 1612709890, "PrintableString"}, + {"ub-organization-name-length", 524298, "1"}, + {"NumericUserIdentifier", 1612709890, "NumericString"}, + {"ub-numeric-user-id-length", 524298, "1"}, + {"PersonalName", 1610612750, 0}, + {"surname", 1814044674, "PrintableString"}, + {0, 1073745928, "0"}, + {"ub-surname-length", 524298, "1"}, + {"given-name", 1814061058, "PrintableString"}, + {0, 1073745928, "1"}, + {"ub-given-name-length", 524298, "1"}, + {"initials", 1814061058, "PrintableString"}, + {0, 1073745928, "2"}, + {"ub-initials-length", 524298, "1"}, + {"generation-qualifier", 740319234, "PrintableString"}, + {0, 1073745928, "3"}, + {"ub-generation-qualifier-length", 524298, "1"}, + {"OrganizationalUnitNames", 1612709899, 0}, + {"ub-organizational-units", 1074266122, "1"}, + {0, 2, "OrganizationalUnitName"}, + {"OrganizationalUnitName", 1612709890, "PrintableString"}, + {"ub-organizational-unit-name-length", 524298, "1"}, + {"BuiltInDomainDefinedAttributes", 1612709899, 0}, + {"ub-domain-defined-attributes", 1074266122, "1"}, + {0, 2, "BuiltInDomainDefinedAttribute"}, + {"BuiltInDomainDefinedAttribute", 1610612741, 0}, + {"type", 1612709890, "PrintableString"}, + {"ub-domain-defined-attribute-type-length", 524298, "1"}, + {"value", 538968066, "PrintableString"}, + {"ub-domain-defined-attribute-value-length", 524298, "1"}, + {"ExtensionAttributes", 1612709903, 0}, + {"ub-extension-attributes", 1074266122, "1"}, + {0, 2, "ExtensionAttribute"}, + {"ExtensionAttribute", 1610612741, 0}, + {"extension-attribute-type", 1611145219, 0}, + {0, 1073743880, "0"}, + {"0", 10, "ub-extension-attributes"}, + {"extension-attribute-value", 541073421, 0}, + {0, 1073743880, "1"}, + {"extension-attribute-type", 1, 0}, + {"common-name", 1342177283, "1"}, + {"CommonName", 1612709890, "PrintableString"}, + {"ub-common-name-length", 524298, "1"}, + {"teletex-common-name", 1342177283, "2"}, + {"TeletexCommonName", 1612709890, "TeletexString"}, + {"ub-common-name-length", 524298, "1"}, + {"teletex-organization-name", 1342177283, "3"}, + {"TeletexOrganizationName", 1612709890, "TeletexString"}, + {"ub-organization-name-length", 524298, "1"}, + {"teletex-personal-name", 1342177283, "4"}, + {"TeletexPersonalName", 1610612750, 0}, + {"surname", 1814044674, "TeletexString"}, + {0, 1073743880, "0"}, + {"ub-surname-length", 524298, "1"}, + {"given-name", 1814061058, "TeletexString"}, + {0, 1073743880, "1"}, + {"ub-given-name-length", 524298, "1"}, + {"initials", 1814061058, "TeletexString"}, + {0, 1073743880, "2"}, + {"ub-initials-length", 524298, "1"}, + {"generation-qualifier", 740319234, "TeletexString"}, + {0, 1073743880, "3"}, + {"ub-generation-qualifier-length", 524298, "1"}, + {"teletex-organizational-unit-names", 1342177283, "5"}, + {"TeletexOrganizationalUnitNames", 1612709899, 0}, + {"ub-organizational-units", 1074266122, "1"}, + {0, 2, "TeletexOrganizationalUnitName"}, + {"TeletexOrganizationalUnitName", 1612709890, "TeletexString"}, + {"ub-organizational-unit-name-length", 524298, "1"}, + {"pds-name", 1342177283, "7"}, + {"PDSName", 1612709890, "PrintableString"}, + {"ub-pds-name-length", 524298, "1"}, + {"physical-delivery-country-name", 1342177283, "8"}, + {"PhysicalDeliveryCountryName", 1610612754, 0}, + {"x121-dcc-code", 1612709890, "NumericString"}, + {0, 1048586, "ub-country-name-numeric-length"}, + {"iso-3166-alpha2-code", 538968066, "PrintableString"}, + {0, 1048586, "ub-country-name-alpha-length"}, + {"postal-code", 1342177283, "9"}, + {"PostalCode", 1610612754, 0}, + {"numeric-code", 1612709890, "NumericString"}, + {"ub-postal-code-length", 524298, "1"}, + {"printable-code", 538968066, "PrintableString"}, + {"ub-postal-code-length", 524298, "1"}, + {"physical-delivery-office-name", 1342177283, "10"}, + {"PhysicalDeliveryOfficeName", 1073741826, "PDSParameter"}, + {"physical-delivery-office-number", 1342177283, "11"}, + {"PhysicalDeliveryOfficeNumber", 1073741826, "PDSParameter"}, + {"extension-OR-address-components", 1342177283, "12"}, + {"ExtensionORAddressComponents", 1073741826, "PDSParameter"}, + {"physical-delivery-personal-name", 1342177283, "13"}, + {"PhysicalDeliveryPersonalName", 1073741826, "PDSParameter"}, + {"physical-delivery-organization-name", 1342177283, "14"}, + {"PhysicalDeliveryOrganizationName", 1073741826, "PDSParameter"}, + {"extension-physical-delivery-address-components", 1342177283, "15"}, + {"ExtensionPhysicalDeliveryAddressComponents", 1073741826, "PDSParameter"}, + {"unformatted-postal-address", 1342177283, "16"}, + {"UnformattedPostalAddress", 1610612750, 0}, + {"printable-address", 1814052875, 0}, + {"ub-pds-physical-address-lines", 1074266122, "1"}, + {0, 538968066, "PrintableString"}, + {"ub-pds-parameter-length", 524298, "1"}, + {"teletex-string", 740311042, "TeletexString"}, + {"ub-unformatted-address-length", 524298, "1"}, + {"street-address", 1342177283, "17"}, + {"StreetAddress", 1073741826, "PDSParameter"}, + {"post-office-box-address", 1342177283, "18"}, + {"PostOfficeBoxAddress", 1073741826, "PDSParameter"}, + {"poste-restante-address", 1342177283, "19"}, + {"PosteRestanteAddress", 1073741826, "PDSParameter"}, + {"unique-postal-name", 1342177283, "20"}, + {"UniquePostalName", 1073741826, "PDSParameter"}, + {"local-postal-attributes", 1342177283, "21"}, + {"LocalPostalAttributes", 1073741826, "PDSParameter"}, + {"PDSParameter", 1610612750, 0}, + {"printable-string", 1814052866, "PrintableString"}, + {"ub-pds-parameter-length", 524298, "1"}, + {"teletex-string", 740311042, "TeletexString"}, + {"ub-pds-parameter-length", 524298, "1"}, + {"extended-network-address", 1342177283, "22"}, + {"ExtendedNetworkAddress", 1610612754, 0}, + {"e163-4-address", 1610612741, 0}, + {"number", 1612718082, "NumericString"}, + {0, 1073743880, "0"}, + {"ub-e163-4-number-length", 524298, "1"}, + {"sub-address", 538992642, "NumericString"}, + {0, 1073743880, "1"}, + {"ub-e163-4-sub-address-length", 524298, "1"}, + {"psap-address", 536879106, "PresentationAddress"}, + {0, 2056, "0"}, + {"PresentationAddress", 1610612741, 0}, + {"pSelector", 1610637319, 0}, + {0, 2056, "0"}, + {"sSelector", 1610637319, 0}, + {0, 2056, "1"}, + {"tSelector", 1610637319, 0}, + {0, 2056, "2"}, + {"nAddresses", 538976271, 0}, + {0, 1073743880, "3"}, + {"MAX", 1074266122, "1"}, + {0, 7, 0}, + {"terminal-type", 1342177283, "23"}, + {"TerminalType", 1610874883, 0}, + {"telex", 1073741825, "3"}, + {"teletex", 1073741825, "4"}, + {"g3-facsimile", 1073741825, "5"}, + {"g4-facsimile", 1073741825, "6"}, + {"ia5-terminal", 1073741825, "7"}, + {"videotex", 1, "8"}, + {"teletex-domain-defined-attributes", 1342177283, "6"}, + {"TeletexDomainDefinedAttributes", 1612709899, 0}, + {"ub-domain-defined-attributes", 1074266122, "1"}, + {0, 2, "TeletexDomainDefinedAttribute"}, + {"TeletexDomainDefinedAttribute", 1610612741, 0}, + {"type", 1612709890, "TeletexString"}, + {"ub-domain-defined-attribute-type-length", 524298, "1"}, + {"value", 538968066, "TeletexString"}, + {"ub-domain-defined-attribute-value-length", 524298, "1"}, + {"ub-name", 1342177283, "32768"}, + {"ub-common-name", 1342177283, "64"}, + {"ub-locality-name", 1342177283, "128"}, + {"ub-state-name", 1342177283, "128"}, + {"ub-organization-name", 1342177283, "64"}, + {"ub-organizational-unit-name", 1342177283, "64"}, + {"ub-title", 1342177283, "64"}, + {"ub-match", 1342177283, "128"}, + {"ub-emailaddress-length", 1342177283, "128"}, + {"ub-common-name-length", 1342177283, "64"}, + {"ub-country-name-alpha-length", 1342177283, "2"}, + {"ub-country-name-numeric-length", 1342177283, "3"}, + {"ub-domain-defined-attributes", 1342177283, "4"}, + {"ub-domain-defined-attribute-type-length", 1342177283, "8"}, + {"ub-domain-defined-attribute-value-length", 1342177283, "128"}, + {"ub-domain-name-length", 1342177283, "16"}, + {"ub-extension-attributes", 1342177283, "256"}, + {"ub-e163-4-number-length", 1342177283, "15"}, + {"ub-e163-4-sub-address-length", 1342177283, "40"}, + {"ub-generation-qualifier-length", 1342177283, "3"}, + {"ub-given-name-length", 1342177283, "16"}, + {"ub-initials-length", 1342177283, "5"}, + {"ub-integer-options", 1342177283, "256"}, + {"ub-numeric-user-id-length", 1342177283, "32"}, + {"ub-organization-name-length", 1342177283, "64"}, + {"ub-organizational-unit-name-length", 1342177283, "32"}, + {"ub-organizational-units", 1342177283, "4"}, + {"ub-pds-name-length", 1342177283, "16"}, + {"ub-pds-parameter-length", 1342177283, "30"}, + {"ub-pds-physical-address-lines", 1342177283, "6"}, + {"ub-postal-code-length", 1342177283, "16"}, + {"ub-surname-length", 1342177283, "40"}, + {"ub-terminal-id-length", 1342177283, "24"}, + {"ub-unformatted-address-length", 1342177283, "180"}, + {"ub-x121-address-length", 1342177283, "16"}, + {"pkcs-7-ContentInfo", 1610612741, 0}, + {"contentType", 1073741826, "pkcs-7-ContentType"}, + {"content", 541073421, 0}, + {0, 1073743880, "0"}, + {"contentType", 1, 0}, + {"pkcs-7-DigestInfo", 1610612741, 0}, + {"digestAlgorithm", 1073741826, "pkcs-7-DigestAlgorithmIdentifier"}, + {"digest", 2, "pkcs-7-Digest"}, + {"pkcs-7-Digest", 1073741831, 0}, + {"pkcs-7-ContentType", 1073741836, 0}, + {"pkcs-7-SignedData", 1610612741, 0}, + {"version", 1073741826, "pkcs-7-CMSVersion"}, + {"digestAlgorithms", 1073741826, "pkcs-7-DigestAlgorithmIdentifiers"}, + {"encapContentInfo", 1073741826, "pkcs-7-EncapsulatedContentInfo"}, + {"certificates", 1610637314, "pkcs-7-CertificateSet"}, + {0, 4104, "0"}, + {"crls", 1610637314, "pkcs-7-CertificateRevocationLists"}, + {0, 4104, "1"}, + {"signerInfos", 2, "pkcs-7-SignerInfos"}, + {"pkcs-7-CMSVersion", 1610874883, 0}, + {"v0", 1073741825, "0"}, + {"v1", 1073741825, "1"}, + {"v2", 1073741825, "2"}, + {"v3", 1073741825, "3"}, + {"v4", 1, "4"}, + {"pkcs-7-DigestAlgorithmIdentifiers", 1610612751, 0}, + {0, 2, "pkcs-7-DigestAlgorithmIdentifier"}, + {"pkcs-7-DigestAlgorithmIdentifier", 1073741826, "AlgorithmIdentifier"}, + {"pkcs-7-EncapsulatedContentInfo", 1610612741, 0}, + {"eContentType", 1073741826, "pkcs-7-ContentType"}, + {"eContent", 536895495, 0}, + {0, 2056, "0"}, + {"pkcs-7-CertificateRevocationLists", 1610612751, 0}, + {0, 13, 0}, + {"pkcs-7-CertificateChoices", 1610612754, 0}, + {"certificate", 13, 0}, + {"pkcs-7-CertificateSet", 1610612751, 0}, + {0, 2, "pkcs-7-CertificateChoices"}, + {"pkcs-7-SignerInfos", 1610612751, 0}, + {0, 13, 0}, + {"pkcs-10-CertificationRequestInfo", 1610612741, 0}, + {"version", 1610874883, 0}, + {"v1", 1, "0"}, + {"subject", 1073741826, "Name"}, + {"subjectPKInfo", 1073741826, "SubjectPublicKeyInfo"}, + {"attributes", 536879106, "Attributes"}, + {0, 4104, "0"}, + {"Attributes", 1610612751, 0}, + {0, 2, "Attribute"}, + {"pkcs-10-CertificationRequest", 1610612741, 0}, + {"certificationRequestInfo", 1073741826, "pkcs-10-CertificationRequestInfo"}, + {"signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + {"signature", 6, 0}, + {"pkcs-9-ub-challengePassword", 1342177283, "255"}, + {"pkcs-9-certTypes", 1879048204, 0}, + {0, 1073741825, "pkcs-9"}, + {0, 1, "22"}, + {"pkcs-9-crlTypes", 1879048204, 0}, + {0, 1073741825, "pkcs-9"}, + {0, 1, "23"}, + {"pkcs-9-at-challengePassword", 1879048204, 0}, + {0, 1073741825, "pkcs-9"}, + {0, 1, "7"}, + {"pkcs-9-challengePassword", 1610612754, 0}, + {"printableString", 1612709890, "PrintableString"}, + {"pkcs-9-ub-challengePassword", 524298, "1"}, + {"utf8String", 538968066, "UTF8String"}, + {"pkcs-9-ub-challengePassword", 524298, "1"}, + {"pkcs-9-at-localKeyId", 1879048204, 0}, + {0, 1073741825, "pkcs-9"}, + {0, 1, "21"}, + {"pkcs-9-localKeyId", 1073741831, 0}, + {"pkcs-9-at-friendlyName", 1879048204, 0}, + {0, 1073741825, "pkcs-9"}, + {0, 1, "20"}, + {"pkcs-9-friendlyName", 1612709890, "BMPString"}, + {"255", 524298, "1"}, + {"pkcs-8-PrivateKeyInfo", 1610612741, 0}, + {"version", 1073741826, "pkcs-8-Version"}, + {"privateKeyAlgorithm", 1073741826, "AlgorithmIdentifier"}, + {"privateKey", 1073741826, "pkcs-8-PrivateKey"}, + {"attributes", 536895490, "Attributes"}, + {0, 4104, "0"}, + {"pkcs-8-Version", 1610874883, 0}, + {"v1", 1, "0"}, + {"pkcs-8-PrivateKey", 1073741831, 0}, + {"pkcs-8-Attributes", 1610612751, 0}, + {0, 2, "Attribute"}, + {"pkcs-8-EncryptedPrivateKeyInfo", 1610612741, 0}, + {"encryptionAlgorithm", 1073741826, "AlgorithmIdentifier"}, + {"encryptedData", 2, "pkcs-8-EncryptedData"}, + {"pkcs-8-EncryptedData", 1073741831, 0}, + {"pkcs-5", 1879048204, 0}, + {0, 1073741825, "pkcs"}, + {0, 1, "5"}, + {"pkcs-5-encryptionAlgorithm", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"rsadsi", 1073741825, "113549"}, + {0, 1, "3"}, + {"pkcs-5-des-EDE3-CBC", 1879048204, 0}, + {0, 1073741825, "pkcs-5-encryptionAlgorithm"}, + {0, 1, "7"}, + {"pkcs-5-des-EDE3-CBC-params", 1612709895, 0}, + {0, 1048586, "8"}, + {"pkcs-5-id-PBES2", 1879048204, 0}, + {0, 1073741825, "pkcs-5"}, + {0, 1, "13"}, + {"pkcs-5-PBES2-params", 1610612741, 0}, + {"keyDerivationFunc", 1073741826, "AlgorithmIdentifier"}, + {"encryptionScheme", 2, "AlgorithmIdentifier"}, + {"pkcs-5-id-PBKDF2", 1879048204, 0}, + {0, 1073741825, "pkcs-5"}, + {0, 1, "12"}, + {"pkcs-5-PBKDF2-params", 1610612741, 0}, + {"salt", 1610612754, 0}, + {"specified", 1073741831, 0}, + {"otherSource", 2, "AlgorithmIdentifier"}, + {"iterationCount", 1611137027, 0}, + {"1", 10, "MAX"}, + {"keyLength", 1611153411, 0}, + {"1", 10, "MAX"}, + {"prf", 16386, "AlgorithmIdentifier"}, + {"pkcs-12", 1879048204, 0}, + {0, 1073741825, "pkcs"}, + {0, 1, "12"}, + {"pkcs-12-PFX", 1610612741, 0}, + {"version", 1610874883, 0}, + {"v3", 1, "3"}, + {"authSafe", 1073741826, "pkcs-7-ContentInfo"}, + {"macData", 16386, "pkcs-12-MacData"}, + {"pkcs-12-PbeParams", 1610612741, 0}, + {"salt", 1073741831, 0}, + {"iterations", 3, 0}, + {"pkcs-12-MacData", 1610612741, 0}, + {"mac", 1073741826, "pkcs-7-DigestInfo"}, + {"macSalt", 1073741831, 0}, + {"iterations", 536903683, 0}, + {0, 9, "1"}, + {"pkcs-12-AuthenticatedSafe", 1610612747, 0}, + {0, 2, "pkcs-7-ContentInfo"}, + {"pkcs-12-SafeContents", 1610612747, 0}, + {0, 2, "pkcs-12-SafeBag"}, + {"pkcs-12-SafeBag", 1610612741, 0}, + {"bagId", 1073741836, 0}, + {"bagValue", 1614815245, 0}, + {0, 1073743880, "0"}, + {"badId", 1, 0}, + {"bagAttributes", 536887311, 0}, + {0, 2, "pkcs-12-PKCS12Attribute"}, + {"pkcs-12-bagtypes", 1879048204, 0}, + {0, 1073741825, "pkcs-12"}, + {0, 1073741825, "10"}, + {0, 1, "1"}, + {"pkcs-12-keyBag", 1879048204, 0}, + {0, 1073741825, "pkcs-12-bagtypes"}, + {0, 1, "1"}, + {"pkcs-12-pkcs8ShroudedKeyBag", 1879048204, 0}, + {0, 1073741825, "pkcs-12-bagtypes"}, + {0, 1, "2"}, + {"pkcs-12-certBag", 1879048204, 0}, + {0, 1073741825, "pkcs-12-bagtypes"}, + {0, 1, "3"}, + {"pkcs-12-crlBag", 1879048204, 0}, + {0, 1073741825, "pkcs-12-bagtypes"}, + {0, 1, "4"}, + {"pkcs-12-KeyBag", 1073741826, "pkcs-8-PrivateKeyInfo"}, + {"pkcs-12-PKCS8ShroudedKeyBag", 1073741826, "pkcs-8-EncryptedPrivateKeyInfo"}, + {"pkcs-12-CertBag", 1610612741, 0}, + {"certId", 1073741836, 0}, + {"certValue", 541073421, 0}, + {0, 1073743880, "0"}, + {"certId", 1, 0}, + {"pkcs-12-CRLBag", 1610612741, 0}, + {"crlId", 1073741836, 0}, + {"crlValue", 541073421, 0}, + {0, 1073743880, "0"}, + {"crlId", 1, 0}, + {"pkcs-12-PKCS12Attribute", 1073741826, "Attribute"}, + {"pkcs-7-data", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"rsadsi", 1073741825, "113549"}, + {"pkcs", 1073741825, "1"}, + {"pkcs7", 1073741825, "7"}, + {0, 1, "1"}, + {"pkcs-7-encryptedData", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"rsadsi", 1073741825, "113549"}, + {"pkcs", 1073741825, "1"}, + {"pkcs7", 1073741825, "7"}, + {0, 1, "6"}, + {"pkcs-7-Data", 1073741831, 0}, + {"pkcs-7-EncryptedData", 1610612741, 0}, + {"version", 1073741826, "pkcs-7-CMSVersion"}, + {"encryptedContentInfo", 1073741826, "pkcs-7-EncryptedContentInfo"}, + {"unprotectedAttrs", 536895490, "pkcs-7-UnprotectedAttributes"}, + {0, 4104, "1"}, + {"pkcs-7-EncryptedContentInfo", 1610612741, 0}, + {"contentType", 1073741826, "pkcs-7-ContentType"}, + {"contentEncryptionAlgorithm", 1073741826, + "pkcs-7-ContentEncryptionAlgorithmIdentifier"}, + {"encryptedContent", 536895490, "pkcs-7-EncryptedContent"}, + {0, 4104, "0"}, + {"pkcs-7-ContentEncryptionAlgorithmIdentifier", 1073741826, + "AlgorithmIdentifier"}, + {"pkcs-7-EncryptedContent", 1073741831, 0}, + {"pkcs-7-UnprotectedAttributes", 1612709903, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "Attribute"}, + {"id-at-ldap-DC", 1880096780, "AttributeType"}, + {0, 1073741825, "0"}, + {0, 1073741825, "9"}, + {0, 1073741825, "2342"}, + {0, 1073741825, "19200300"}, + {0, 1073741825, "100"}, + {0, 1073741825, "1"}, + {0, 1, "25"}, + {"ldap-DC", 1073741826, "IA5String"}, + {"id-at-ldap-UID", 1880096780, "AttributeType"}, + {0, 1073741825, "0"}, + {0, 1073741825, "9"}, + {0, 1073741825, "2342"}, + {0, 1073741825, "19200300"}, + {0, 1073741825, "100"}, + {0, 1073741825, "1"}, + {0, 1, "1"}, + {"ldap-UID", 1073741826, "DirectoryString"}, + {"id-pda", 1879048204, 0}, + {0, 1073741825, "id-pkix"}, + {0, 1, "9"}, + {"id-pda-dateOfBirth", 1880096780, "AttributeType"}, + {0, 1073741825, "id-pda"}, + {0, 1, "1"}, + {"DateOfBirth", 1082130449, 0}, + {"id-pda-placeOfBirth", 1880096780, "AttributeType"}, + {0, 1073741825, "id-pda"}, + {0, 1, "2"}, + {"PlaceOfBirth", 1073741826, "DirectoryString"}, + {"id-pda-gender", 1880096780, "AttributeType"}, + {0, 1073741825, "id-pda"}, + {0, 1, "3"}, + {"Gender", 1612709890, "PrintableString"}, + {0, 1048586, "1"}, + {"id-pda-countryOfCitizenship", 1880096780, "AttributeType"}, + {0, 1073741825, "id-pda"}, + {0, 1, "4"}, + {"CountryOfCitizenship", 1612709890, "PrintableString"}, + {0, 1048586, "2"}, + {"id-pda-countryOfResidence", 1880096780, "AttributeType"}, + {0, 1073741825, "id-pda"}, + {0, 1, "5"}, + {"CountryOfResidence", 538968066, "PrintableString"}, + {0, 1048586, "2"}, + {0, 0, 0} +}; diff --git a/tests/qapi-schema/enum-bad-prefix.err b/tests/qapi-schema/enum-bad-prefix.err new file mode 100644 index 0000000000..399f5f7af5 --- /dev/null +++ b/tests/qapi-schema/enum-bad-prefix.err @@ -0,0 +1 @@ +tests/qapi-schema/enum-bad-prefix.json:2: Enum 'MyEnum' requires a string for 'prefix' diff --git a/tests/qapi-schema/enum-bad-prefix.exit b/tests/qapi-schema/enum-bad-prefix.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/enum-bad-prefix.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/enum-bad-prefix.json b/tests/qapi-schema/enum-bad-prefix.json new file mode 100644 index 0000000000..996f628f6d --- /dev/null +++ b/tests/qapi-schema/enum-bad-prefix.json @@ -0,0 +1,2 @@ +# The prefix must be a string type +{ 'enum': 'MyEnum', 'data': [ 'one' ], 'prefix': [ 'fish' ] } diff --git a/tests/qapi-schema/enum-bad-prefix.out b/tests/qapi-schema/enum-bad-prefix.out new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/qapi-schema/enum-bad-prefix.out diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index a9e5aab927..677190c1ef 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -6,6 +6,11 @@ { 'struct': 'NestedEnumsOne', 'data': { 'enum1': 'EnumOne', '*enum2': 'EnumOne', 'enum3': 'EnumOne', '*enum4': 'EnumOne' } } +# for testing override of default naming heuristic +{ 'enum': 'QEnumTwo', + 'prefix': 'QENUM_TWO', + 'data': [ 'value1', 'value2' ] } + # for testing nested structs { 'struct': 'UserDefOne', 'base': 'UserDefZero', # intentional forward reference diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index b0b7187226..32f17c5278 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -1,5 +1,6 @@ [OrderedDict([('enum', 'EnumOne'), ('data', ['value1', 'value2', 'value3'])]), OrderedDict([('struct', 'NestedEnumsOne'), ('data', OrderedDict([('enum1', 'EnumOne'), ('*enum2', 'EnumOne'), ('enum3', 'EnumOne'), ('*enum4', 'EnumOne')]))]), + OrderedDict([('enum', 'QEnumTwo'), ('prefix', 'QENUM_TWO'), ('data', ['value1', 'value2'])]), OrderedDict([('struct', 'UserDefOne'), ('base', 'UserDefZero'), ('data', OrderedDict([('string', 'str'), ('*enum1', 'EnumOne')]))]), OrderedDict([('struct', 'UserDefZero'), ('data', OrderedDict([('integer', 'int')]))]), OrderedDict([('struct', 'UserDefTwoDictDict'), ('data', OrderedDict([('userdef', 'UserDefOne'), ('string', 'str')]))]), @@ -33,6 +34,7 @@ OrderedDict([('event', '__ORG.QEMU_X-EVENT'), ('data', '__org.qemu_x-Struct')]), OrderedDict([('command', '__org.qemu_x-command'), ('data', OrderedDict([('a', ['__org.qemu_x-Enum']), ('b', ['__org.qemu_x-Struct']), ('c', '__org.qemu_x-Union2'), ('d', '__org.qemu_x-Alt')])), ('returns', '__org.qemu_x-Union1')])] [{'enum_name': 'EnumOne', 'enum_values': ['value1', 'value2', 'value3']}, + {'enum_name': 'QEnumTwo', 'enum_values': ['value1', 'value2']}, {'enum_name': '__org.qemu_x-Enum', 'enum_values': ['__org.qemu_x-value']}, {'enum_name': 'UserDefAlternateKind', 'enum_values': None}, {'enum_name': 'UserDefNativeListUnionKind', 'enum_values': None}, diff --git a/tests/test-crypto-tlscredsx509.c b/tests/test-crypto-tlscredsx509.c new file mode 100644 index 0000000000..c70aa5563a --- /dev/null +++ b/tests/test-crypto-tlscredsx509.c @@ -0,0 +1,731 @@ +/* + * Copyright (C) 2015 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: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <stdlib.h> +#include <fcntl.h> + +#include "config-host.h" +#include "crypto-tls-x509-helpers.h" +#include "crypto/tlscredsx509.h" + +#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT + +#define WORKDIR "tests/test-crypto-tlscredsx509-work/" +#define KEYFILE WORKDIR "key-ctx.pem" + +struct QCryptoTLSCredsTestData { + bool isServer; + const char *cacrt; + const char *crt; + bool expectFail; +}; + + +static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint, + const char *certdir, + Error **errp) +{ + Object *parent = object_get_objects_root(); + Object *creds = object_new_with_props( + TYPE_QCRYPTO_TLS_CREDS_X509, + parent, + "testtlscreds", + errp, + "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? + "server" : "client"), + "dir", certdir, + "verify-peer", "yes", + "sanity-check", "yes", + NULL); + + if (*errp) { + return NULL; + } + return QCRYPTO_TLS_CREDS(creds); +} + +/* + * This tests sanity checking of our own certificates + * + * The code being tested is used when TLS creds are created, + * and aim to ensure QMEU has been configured with sane + * certificates. This allows us to give much much much + * clearer error messages to the admin when they misconfigure + * things. + */ +static void test_tls_creds(const void *opaque) +{ + struct QCryptoTLSCredsTestData *data = + (struct QCryptoTLSCredsTestData *)opaque; + QCryptoTLSCreds *creds; + Error *err = NULL; + +#define CERT_DIR "tests/test-crypto-tlscredsx509-certs/" + mkdir(CERT_DIR, 0700); + + unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); + if (data->isServer) { + unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT); + unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY); + } else { + unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT); + unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY); + } + + if (access(data->cacrt, R_OK) == 0) { + g_assert(link(data->cacrt, + CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT) == 0); + } + if (data->isServer) { + if (access(data->crt, R_OK) == 0) { + g_assert(link(data->crt, + CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT) == 0); + } + g_assert(link(KEYFILE, + CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY) == 0); + } else { + if (access(data->crt, R_OK) == 0) { + g_assert(link(data->crt, + CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT) == 0); + } + g_assert(link(KEYFILE, + CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY) == 0); + } + + creds = test_tls_creds_create( + (data->isServer ? + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER : + QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT), + CERT_DIR, + &err); + + if (data->expectFail) { + error_free(err); + g_assert(creds == NULL); + } else { + if (err) { + g_printerr("Failed to generate creds: %s\n", + error_get_pretty(err)); + error_free(err); + } + g_assert(creds != NULL); + } + + unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); + if (data->isServer) { + unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT); + unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY); + } else { + unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT); + unlink(CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY); + } + rmdir(CERT_DIR); + if (creds) { + object_unparent(OBJECT(creds)); + } +} + +int main(int argc, char **argv) +{ + int ret; + + module_call_init(MODULE_INIT_QOM); + g_test_init(&argc, &argv, NULL); + setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1); + + mkdir(WORKDIR, 0700); + + test_tls_init(KEYFILE); + +# define TLS_TEST_REG(name, isServer, caCrt, crt, expectFail) \ + struct QCryptoTLSCredsTestData name = { \ + isServer, caCrt, crt, expectFail \ + }; \ + g_test_add_data_func("/qcrypto/tlscredsx509/" # name, \ + &name, test_tls_creds); \ + + /* A perfect CA, perfect client & perfect server */ + + /* Basic:CA:critical */ + TLS_ROOT_REQ(cacertreq, + "UK", "qemu CA", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + + TLS_CERT_REQ(servercertreq, cacertreq, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + TLS_CERT_REQ(clientcertreq, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, 0); + + TLS_TEST_REG(perfectserver, true, + cacertreq.filename, servercertreq.filename, false); + TLS_TEST_REG(perfectclient, false, + cacertreq.filename, clientcertreq.filename, false); + + + /* Some other CAs which are good */ + + /* Basic:CA:critical */ + TLS_ROOT_REQ(cacert1req, + "UK", "qemu CA 1", NULL, NULL, NULL, NULL, + true, true, true, + false, false, 0, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercert1req, cacert1req, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + + /* Basic:CA:not-critical */ + TLS_ROOT_REQ(cacert2req, + "UK", "qemu CA 2", NULL, NULL, NULL, NULL, + true, false, true, + false, false, 0, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercert2req, cacert2req, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + + /* Key usage:cert-sign:critical */ + TLS_ROOT_REQ(cacert3req, + "UK", "qemu CA 3", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercert3req, cacert3req, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + + TLS_TEST_REG(goodca1, true, + cacert1req.filename, servercert1req.filename, false); + TLS_TEST_REG(goodca2, true, + cacert2req.filename, servercert2req.filename, false); + TLS_TEST_REG(goodca3, true, + cacert3req.filename, servercert3req.filename, false); + + /* Now some bad certs */ + + /* Key usage:dig-sig:not-critical */ + TLS_ROOT_REQ(cacert4req, + "UK", "qemu CA 4", NULL, NULL, NULL, NULL, + true, true, true, + true, false, GNUTLS_KEY_DIGITAL_SIGNATURE, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercert4req, cacert4req, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + /* no-basic */ + TLS_ROOT_REQ(cacert5req, + "UK", "qemu CA 5", NULL, NULL, NULL, NULL, + false, false, false, + false, false, 0, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercert5req, cacert5req, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + /* Key usage:dig-sig:critical */ + TLS_ROOT_REQ(cacert6req, + "UK", "qemu CA 6", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_DIGITAL_SIGNATURE, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercert6req, cacert6req, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + + /* Technically a CA cert with basic constraints + * key purpose == key signing + non-critical should + * be rejected. GNUTLS < 3.1 does not reject it and + * we don't anticipate them changing this behaviour + */ + TLS_TEST_REG(badca1, true, cacert4req.filename, servercert4req.filename, + (GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR >= 1) || + GNUTLS_VERSION_MAJOR > 3); + TLS_TEST_REG(badca2, true, + cacert5req.filename, servercert5req.filename, true); + TLS_TEST_REG(badca3, true, + cacert6req.filename, servercert6req.filename, true); + + + /* Various good servers */ + /* no usage or purpose */ + TLS_CERT_REQ(servercert7req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + false, false, NULL, NULL, + 0, 0); + /* usage:cert-sign+dig-sig+encipher:critical */ + TLS_CERT_REQ(servercert8req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT | + GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + /* usage:cert-sign:not-critical */ + TLS_CERT_REQ(servercert9req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, false, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + /* purpose:server:critical */ + TLS_CERT_REQ(servercert10req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + /* purpose:server:not-critical */ + TLS_CERT_REQ(servercert11req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, false, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + /* purpose:client+server:critical */ + TLS_CERT_REQ(servercert12req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, true, + GNUTLS_KP_TLS_WWW_CLIENT, GNUTLS_KP_TLS_WWW_SERVER, + 0, 0); + /* purpose:client+server:not-critical */ + TLS_CERT_REQ(servercert13req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, false, + GNUTLS_KP_TLS_WWW_CLIENT, GNUTLS_KP_TLS_WWW_SERVER, + 0, 0); + + TLS_TEST_REG(goodserver1, true, + cacertreq.filename, servercert7req.filename, false); + TLS_TEST_REG(goodserver2, true, + cacertreq.filename, servercert8req.filename, false); + TLS_TEST_REG(goodserver3, true, + cacertreq.filename, servercert9req.filename, false); + TLS_TEST_REG(goodserver4, true, + cacertreq.filename, servercert10req.filename, false); + TLS_TEST_REG(goodserver5, true, + cacertreq.filename, servercert11req.filename, false); + TLS_TEST_REG(goodserver6, true, + cacertreq.filename, servercert12req.filename, false); + TLS_TEST_REG(goodserver7, true, + cacertreq.filename, servercert13req.filename, false); + + /* Bad servers */ + + /* usage:cert-sign:critical */ + TLS_CERT_REQ(servercert14req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + /* purpose:client:critical */ + TLS_CERT_REQ(servercert15req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, 0); + /* usage: none:critical */ + TLS_CERT_REQ(servercert16req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, 0, + false, false, NULL, NULL, + 0, 0); + + TLS_TEST_REG(badserver1, true, + cacertreq.filename, servercert14req.filename, true); + TLS_TEST_REG(badserver2, true, + cacertreq.filename, servercert15req.filename, true); + TLS_TEST_REG(badserver3, true, + cacertreq.filename, servercert16req.filename, true); + + + + /* Various good clients */ + /* no usage or purpose */ + TLS_CERT_REQ(clientcert1req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + false, false, NULL, NULL, + 0, 0); + /* usage:cert-sign+dig-sig+encipher:critical */ + TLS_CERT_REQ(clientcert2req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT | + GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + /* usage:cert-sign:not-critical */ + TLS_CERT_REQ(clientcert3req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, false, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + /* purpose:client:critical */ + TLS_CERT_REQ(clientcert4req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, 0); + /* purpose:client:not-critical */ + TLS_CERT_REQ(clientcert5req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, false, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, 0); + /* purpose:client+client:critical */ + TLS_CERT_REQ(clientcert6req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, true, + GNUTLS_KP_TLS_WWW_CLIENT, GNUTLS_KP_TLS_WWW_SERVER, + 0, 0); + /* purpose:client+client:not-critical */ + TLS_CERT_REQ(clientcert7req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, false, + GNUTLS_KP_TLS_WWW_CLIENT, GNUTLS_KP_TLS_WWW_SERVER, + 0, 0); + + TLS_TEST_REG(goodclient1, false, + cacertreq.filename, clientcert1req.filename, false); + TLS_TEST_REG(goodclient2, false, + cacertreq.filename, clientcert2req.filename, false); + TLS_TEST_REG(goodclient3, false, + cacertreq.filename, clientcert3req.filename, false); + TLS_TEST_REG(goodclient4, false, + cacertreq.filename, clientcert4req.filename, false); + TLS_TEST_REG(goodclient5, false, + cacertreq.filename, clientcert5req.filename, false); + TLS_TEST_REG(goodclient6, false, + cacertreq.filename, clientcert6req.filename, false); + TLS_TEST_REG(goodclient7, false, + cacertreq.filename, clientcert7req.filename, false); + + /* Bad clients */ + + /* usage:cert-sign:critical */ + TLS_CERT_REQ(clientcert8req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + /* purpose:client:critical */ + TLS_CERT_REQ(clientcert9req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + false, false, 0, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + /* usage: none:critical */ + TLS_CERT_REQ(clientcert10req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, 0, + false, false, NULL, NULL, + 0, 0); + + TLS_TEST_REG(badclient1, false, + cacertreq.filename, clientcert8req.filename, true); + TLS_TEST_REG(badclient2, false, + cacertreq.filename, clientcert9req.filename, true); + TLS_TEST_REG(badclient3, false, + cacertreq.filename, clientcert10req.filename, true); + + + + /* Expired stuff */ + + TLS_ROOT_REQ(cacertexpreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, -1); + TLS_CERT_REQ(servercertexpreq, cacertexpreq, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + TLS_CERT_REQ(servercertexp1req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, -1); + TLS_CERT_REQ(clientcertexp1req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, -1); + + TLS_TEST_REG(expired1, true, + cacertexpreq.filename, servercertexpreq.filename, true); + TLS_TEST_REG(expired2, true, + cacertreq.filename, servercertexp1req.filename, true); + TLS_TEST_REG(expired3, false, + cacertreq.filename, clientcertexp1req.filename, true); + + + /* Not activated stuff */ + + TLS_ROOT_REQ(cacertnewreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 1, 2); + TLS_CERT_REQ(servercertnewreq, cacertnewreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + TLS_CERT_REQ(servercertnew1req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 1, 2); + TLS_CERT_REQ(clientcertnew1req, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 1, 2); + + TLS_TEST_REG(inactive1, true, + cacertnewreq.filename, servercertnewreq.filename, true); + TLS_TEST_REG(inactive2, true, + cacertreq.filename, servercertnew1req.filename, true); + TLS_TEST_REG(inactive3, false, + cacertreq.filename, clientcertnew1req.filename, true); + + TLS_ROOT_REQ(cacertrootreq, + "UK", "qemu root", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(cacertlevel1areq, cacertrootreq, + "UK", "qemu level 1a", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(cacertlevel1breq, cacertrootreq, + "UK", "qemu level 1b", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(cacertlevel2areq, cacertlevel1areq, + "UK", "qemu level 2a", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercertlevel3areq, cacertlevel2areq, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + TLS_CERT_REQ(clientcertlevel2breq, cacertlevel1breq, + "UK", "qemu client level 2b", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, 0); + + gnutls_x509_crt_t certchain[] = { + cacertrootreq.crt, + cacertlevel1areq.crt, + cacertlevel1breq.crt, + cacertlevel2areq.crt, + }; + + test_tls_write_cert_chain(WORKDIR "cacertchain-ctx.pem", + certchain, + G_N_ELEMENTS(certchain)); + + TLS_TEST_REG(chain1, true, + WORKDIR "cacertchain-ctx.pem", + servercertlevel3areq.filename, false); + TLS_TEST_REG(chain2, false, + WORKDIR "cacertchain-ctx.pem", + clientcertlevel2breq.filename, false); + + /* Some missing certs - first two are fatal, the last + * is ok + */ + TLS_TEST_REG(missingca, true, + "cacertdoesnotexist.pem", + servercert1req.filename, true); + TLS_TEST_REG(missingserver, true, + cacert1req.filename, + "servercertdoesnotexist.pem", true); + TLS_TEST_REG(missingclient, false, + cacert1req.filename, + "clientcertdoesnotexist.pem", false); + + ret = g_test_run(); + + test_tls_discard_cert(&cacertreq); + test_tls_discard_cert(&cacert1req); + test_tls_discard_cert(&cacert2req); + test_tls_discard_cert(&cacert3req); + test_tls_discard_cert(&cacert4req); + test_tls_discard_cert(&cacert5req); + test_tls_discard_cert(&cacert6req); + + test_tls_discard_cert(&servercertreq); + test_tls_discard_cert(&servercert1req); + test_tls_discard_cert(&servercert2req); + test_tls_discard_cert(&servercert3req); + test_tls_discard_cert(&servercert4req); + test_tls_discard_cert(&servercert5req); + test_tls_discard_cert(&servercert6req); + test_tls_discard_cert(&servercert7req); + test_tls_discard_cert(&servercert8req); + test_tls_discard_cert(&servercert9req); + test_tls_discard_cert(&servercert10req); + test_tls_discard_cert(&servercert11req); + test_tls_discard_cert(&servercert12req); + test_tls_discard_cert(&servercert13req); + test_tls_discard_cert(&servercert14req); + test_tls_discard_cert(&servercert15req); + test_tls_discard_cert(&servercert16req); + + test_tls_discard_cert(&clientcertreq); + test_tls_discard_cert(&clientcert1req); + test_tls_discard_cert(&clientcert2req); + test_tls_discard_cert(&clientcert3req); + test_tls_discard_cert(&clientcert4req); + test_tls_discard_cert(&clientcert5req); + test_tls_discard_cert(&clientcert6req); + test_tls_discard_cert(&clientcert7req); + test_tls_discard_cert(&clientcert8req); + test_tls_discard_cert(&clientcert9req); + test_tls_discard_cert(&clientcert10req); + + test_tls_discard_cert(&cacertexpreq); + test_tls_discard_cert(&servercertexpreq); + test_tls_discard_cert(&servercertexp1req); + test_tls_discard_cert(&clientcertexp1req); + + test_tls_discard_cert(&cacertnewreq); + test_tls_discard_cert(&servercertnewreq); + test_tls_discard_cert(&servercertnew1req); + test_tls_discard_cert(&clientcertnew1req); + + test_tls_discard_cert(&cacertrootreq); + test_tls_discard_cert(&cacertlevel1areq); + test_tls_discard_cert(&cacertlevel1breq); + test_tls_discard_cert(&cacertlevel2areq); + test_tls_discard_cert(&servercertlevel3areq); + test_tls_discard_cert(&clientcertlevel2breq); + unlink(WORKDIR "cacertchain-ctx.pem"); + + test_tls_cleanup(KEYFILE); + rmdir(WORKDIR); + + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +#else /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */ + +int +main(void) +{ + return EXIT_SUCCESS; +} + +#endif /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */ diff --git a/tests/test-crypto-tlssession.c b/tests/test-crypto-tlssession.c new file mode 100644 index 0000000000..4524128fca --- /dev/null +++ b/tests/test-crypto-tlssession.c @@ -0,0 +1,535 @@ +/* + * Copyright (C) 2015 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: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <stdlib.h> +#include <fcntl.h> + +#include "config-host.h" +#include "crypto-tls-x509-helpers.h" +#include "crypto/tlscredsx509.h" +#include "crypto/tlssession.h" +#include "qom/object_interfaces.h" +#include "qemu/sockets.h" +#include "qemu/acl.h" + +#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT + +#define WORKDIR "tests/test-crypto-tlssession-work/" +#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; + + return write(*fd, buf, len); +} + +static ssize_t testRead(char *buf, size_t len, void *opaque) +{ + int *fd = opaque; + + return read(*fd, buf, len); +} + +static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint, + const char *certdir, + Error **errp) +{ + Error *err = NULL; + Object *parent = object_get_objects_root(); + Object *creds = object_new_with_props( + TYPE_QCRYPTO_TLS_CREDS_X509, + parent, + (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? + "testtlscredsserver" : "testtlscredsclient"), + &err, + "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? + "server" : "client"), + "dir", certdir, + "verify-peer", "yes", + /* We skip initial sanity checks here because we + * want to make sure that problems are being + * detected at the TLS session validation stage, + * and the test-crypto-tlscreds test already + * validate the sanity check code. + */ + "sanity-check", "no", + NULL + ); + + if (err) { + error_propagate(errp, err); + return NULL; + } + return QCRYPTO_TLS_CREDS(creds); +} + + +/* + * This tests validation checking of peer certificates + * + * This is replicating the checks that are done for an + * active TLS session after handshake completes. To + * simulate that we create our TLS contexts, skipping + * sanity checks. We then get a socketpair, and + * initiate a TLS session across them. Finally do + * do actual cert validation tests + */ +static void test_crypto_tls_session(const void *opaque) +{ + struct QCryptoTLSSessionTestData *data = + (struct QCryptoTLSSessionTestData *)opaque; + QCryptoTLSCreds *clientCreds; + QCryptoTLSCreds *serverCreds; + QCryptoTLSSession *clientSess = NULL; + QCryptoTLSSession *serverSess = NULL; + qemu_acl *acl; + const char * const *wildcards; + 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]); + +#define CLIENT_CERT_DIR "tests/test-crypto-tlssession-client/" +#define SERVER_CERT_DIR "tests/test-crypto-tlssession-server/" + mkdir(CLIENT_CERT_DIR, 0700); + mkdir(SERVER_CERT_DIR, 0700); + + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT); + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY); + + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT); + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY); + + g_assert(link(data->servercacrt, + SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT) == 0); + g_assert(link(data->servercrt, + SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT) == 0); + g_assert(link(KEYFILE, + SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY) == 0); + + g_assert(link(data->clientcacrt, + CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT) == 0); + g_assert(link(data->clientcrt, + CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT) == 0); + g_assert(link(KEYFILE, + CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY) == 0); + + clientCreds = test_tls_creds_create( + QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, + CLIENT_CERT_DIR, + &err); + g_assert(clientCreds != NULL); + + serverCreds = test_tls_creds_create( + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, + SERVER_CERT_DIR, + &err); + g_assert(serverCreds != NULL); + + acl = qemu_acl_init("tlssessionacl"); + qemu_acl_reset(acl); + wildcards = data->wildcards; + while (wildcards && *wildcards) { + qemu_acl_append(acl, 0, *wildcards); + wildcards++; + } + + /* Now the real part of the test, setup the sessions */ + clientSess = qcrypto_tls_session_new( + clientCreds, data->hostname, NULL, + QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, &err); + serverSess = qcrypto_tls_session_new( + serverCreds, NULL, + data->wildcards ? "tlssessionacl" : 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 validation does what + * we were expecting + */ + if (qcrypto_tls_session_check_credentials(serverSess, &err) < 0) { + g_assert(data->expectServerFail); + error_free(err); + err = NULL; + } else { + g_assert(!data->expectServerFail); + } + + /* + * And the same for the client validation check + */ + if (qcrypto_tls_session_check_credentials(clientSess, &err) < 0) { + g_assert(data->expectClientFail); + error_free(err); + err = NULL; + } else { + g_assert(!data->expectClientFail); + } + + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_CERT); + unlink(SERVER_CERT_DIR QCRYPTO_TLS_CREDS_X509_SERVER_KEY); + + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CA_CERT); + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_CERT); + unlink(CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY); + + rmdir(CLIENT_CERT_DIR); + rmdir(SERVER_CERT_DIR); + + object_unparent(OBJECT(serverCreds)); + object_unparent(OBJECT(clientCreds)); + + qcrypto_tls_session_free(serverSess); + qcrypto_tls_session_free(clientSess); + + close(channel[0]); + close(channel[1]); +} + + +int main(int argc, char **argv) +{ + int ret; + + module_call_init(MODULE_INIT_QOM); + g_test_init(&argc, &argv, NULL); + setenv("GNUTLS_FORCE_FIPS_MODE", "2", 1); + + mkdir(WORKDIR, 0700); + + test_tls_init(KEYFILE); + +# define TEST_SESS_REG(name, caCrt, \ + serverCrt, clientCrt, \ + expectServerFail, expectClientFail, \ + hostname, wildcards) \ + struct QCryptoTLSSessionTestData name = { \ + caCrt, caCrt, serverCrt, clientCrt, \ + expectServerFail, expectClientFail, \ + hostname, wildcards \ + }; \ + g_test_add_data_func("/qcrypto/tlssession/" # name, \ + &name, test_crypto_tls_session); \ + + +# define TEST_SESS_REG_EXT(name, serverCaCrt, clientCaCrt, \ + serverCrt, clientCrt, \ + expectServerFail, expectClientFail, \ + hostname, wildcards) \ + struct QCryptoTLSSessionTestData name = { \ + serverCaCrt, clientCaCrt, serverCrt, clientCrt, \ + expectServerFail, expectClientFail, \ + hostname, wildcards \ + }; \ + g_test_add_data_func("/qcrypto/tlssession/" # name, \ + &name, test_crypto_tls_session); \ + + /* A perfect CA, perfect client & perfect server */ + + /* Basic:CA:critical */ + TLS_ROOT_REQ(cacertreq, + "UK", "qemu CA", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + + TLS_ROOT_REQ(altcacertreq, + "UK", "qemu CA 1", NULL, NULL, NULL, NULL, + true, true, true, + false, false, 0, + false, false, NULL, NULL, + 0, 0); + + TLS_CERT_REQ(servercertreq, cacertreq, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + TLS_CERT_REQ(clientcertreq, cacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, 0); + + TLS_CERT_REQ(clientcertaltreq, altcacertreq, + "UK", "qemu", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, 0); + + TEST_SESS_REG(basicca, cacertreq.filename, + servercertreq.filename, clientcertreq.filename, + false, false, "qemu.org", NULL); + TEST_SESS_REG_EXT(differentca, cacertreq.filename, + altcacertreq.filename, servercertreq.filename, + clientcertaltreq.filename, true, true, "qemu.org", NULL); + + + /* When an altname is set, the CN is ignored, so it must be duplicated + * as an altname for it to match */ + TLS_CERT_REQ(servercertalt1req, cacertreq, + "UK", "qemu.org", "www.qemu.org", "qemu.org", + "192.168.122.1", "fec0::dead:beaf", + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + /* This intentionally doesn't replicate */ + TLS_CERT_REQ(servercertalt2req, cacertreq, + "UK", "qemu.org", "www.qemu.org", "wiki.qemu.org", + "192.168.122.1", "fec0::dead:beaf", + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + + TEST_SESS_REG(altname1, cacertreq.filename, + servercertalt1req.filename, clientcertreq.filename, + false, false, "qemu.org", NULL); + TEST_SESS_REG(altname2, cacertreq.filename, + servercertalt1req.filename, clientcertreq.filename, + false, false, "www.qemu.org", NULL); + TEST_SESS_REG(altname3, cacertreq.filename, + servercertalt1req.filename, clientcertreq.filename, + false, true, "wiki.qemu.org", NULL); + + TEST_SESS_REG(altname4, cacertreq.filename, + servercertalt2req.filename, clientcertreq.filename, + false, true, "qemu.org", NULL); + TEST_SESS_REG(altname5, cacertreq.filename, + servercertalt2req.filename, clientcertreq.filename, + false, false, "www.qemu.org", NULL); + TEST_SESS_REG(altname6, cacertreq.filename, + servercertalt2req.filename, clientcertreq.filename, + false, false, "wiki.qemu.org", NULL); + + const char *const wildcards1[] = { + "C=UK,CN=dogfood", + NULL, + }; + const char *const wildcards2[] = { + "C=UK,CN=qemu", + NULL, + }; + const char *const wildcards3[] = { + "C=UK,CN=dogfood", + "C=UK,CN=qemu", + NULL, + }; + const char *const wildcards4[] = { + "C=UK,CN=qemustuff", + NULL, + }; + const char *const wildcards5[] = { + "C=UK,CN=qemu*", + NULL, + }; + const char *const wildcards6[] = { + "C=UK,CN=*emu*", + NULL, + }; + + TEST_SESS_REG(wildcard1, cacertreq.filename, + servercertreq.filename, clientcertreq.filename, + true, false, "qemu.org", wildcards1); + TEST_SESS_REG(wildcard2, cacertreq.filename, + servercertreq.filename, clientcertreq.filename, + false, false, "qemu.org", wildcards2); + TEST_SESS_REG(wildcard3, cacertreq.filename, + servercertreq.filename, clientcertreq.filename, + false, false, "qemu.org", wildcards3); + TEST_SESS_REG(wildcard4, cacertreq.filename, + servercertreq.filename, clientcertreq.filename, + true, false, "qemu.org", wildcards4); + TEST_SESS_REG(wildcard5, cacertreq.filename, + servercertreq.filename, clientcertreq.filename, + false, false, "qemu.org", wildcards5); + TEST_SESS_REG(wildcard6, cacertreq.filename, + servercertreq.filename, clientcertreq.filename, + false, false, "qemu.org", wildcards6); + + TLS_ROOT_REQ(cacertrootreq, + "UK", "qemu root", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(cacertlevel1areq, cacertrootreq, + "UK", "qemu level 1a", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(cacertlevel1breq, cacertrootreq, + "UK", "qemu level 1b", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(cacertlevel2areq, cacertlevel1areq, + "UK", "qemu level 2a", NULL, NULL, NULL, NULL, + true, true, true, + true, true, GNUTLS_KEY_KEY_CERT_SIGN, + false, false, NULL, NULL, + 0, 0); + TLS_CERT_REQ(servercertlevel3areq, cacertlevel2areq, + "UK", "qemu.org", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_SERVER, NULL, + 0, 0); + TLS_CERT_REQ(clientcertlevel2breq, cacertlevel1breq, + "UK", "qemu client level 2b", NULL, NULL, NULL, NULL, + true, true, false, + true, true, + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT, + true, true, GNUTLS_KP_TLS_WWW_CLIENT, NULL, + 0, 0); + + gnutls_x509_crt_t certchain[] = { + cacertrootreq.crt, + cacertlevel1areq.crt, + cacertlevel1breq.crt, + cacertlevel2areq.crt, + }; + + test_tls_write_cert_chain(WORKDIR "cacertchain-sess.pem", + certchain, + G_N_ELEMENTS(certchain)); + + TEST_SESS_REG(cachain, WORKDIR "cacertchain-sess.pem", + servercertlevel3areq.filename, clientcertlevel2breq.filename, + false, false, "qemu.org", NULL); + + ret = g_test_run(); + + test_tls_discard_cert(&clientcertreq); + test_tls_discard_cert(&clientcertaltreq); + + test_tls_discard_cert(&servercertreq); + test_tls_discard_cert(&servercertalt1req); + test_tls_discard_cert(&servercertalt2req); + + test_tls_discard_cert(&cacertreq); + test_tls_discard_cert(&altcacertreq); + + test_tls_discard_cert(&cacertrootreq); + test_tls_discard_cert(&cacertlevel1areq); + test_tls_discard_cert(&cacertlevel1breq); + test_tls_discard_cert(&cacertlevel2areq); + test_tls_discard_cert(&servercertlevel3areq); + test_tls_discard_cert(&clientcertlevel2breq); + unlink(WORKDIR "cacertchain-sess.pem"); + + test_tls_cleanup(KEYFILE); + rmdir(WORKDIR); + + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +#else /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */ + +int +main(void) +{ + return EXIT_SUCCESS; +} + +#endif /* ! QCRYPTO_HAVE_TLS_TEST_SUPPORT */ diff --git a/trace-events b/trace-events index 1927c764a9..88a2f141e5 100644 --- a/trace-events +++ b/trace-events @@ -1150,7 +1150,8 @@ ppm_save(const char *filename, void *display_surface) "%s surface=%p" gd_switch(const char *tab, int width, int height) "tab=%s, width=%d, height=%d" gd_update(const char *tab, int x, int y, int w, int h) "tab=%s, x=%d, y=%d, w=%d, h=%d" gd_key_event(const char *tab, int gdk_keycode, int qemu_keycode, const char *action) "tab=%s, translated GDK keycode %d to QEMU keycode %d (%s)" -gd_grab(const char *tab, const char *device, bool on) "tab=%s, %s %d" +gd_grab(const char *tab, const char *device, const char *reason) "tab=%s, dev=%s, reason=%s" +gd_ungrab(const char *tab, const char *device) "tab=%s, dev=%s" # ui/vnc.c vnc_key_guest_leds(bool caps, bool num, bool scroll) "caps %d, num %d, scroll %d" @@ -1666,3 +1667,21 @@ alsa_no_frames(int state) "No frames available and ALSA state is %d" # audio/ossaudio.c oss_version(int version) "OSS version = %#x" oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d" + +# crypto/tlscreds.c +qcrypto_tls_creds_load_dh(void *creds, const char *filename) "TLS creds load DH creds=%p filename=%s" +qcrypto_tls_creds_get_path(void *creds, const char *filename, const char *path) "TLS creds path creds=%p filename=%s path=%s" + +# crypto/tlscredsanon.c +qcrypto_tls_creds_anon_load(void *creds, const char *dir) "TLS creds anon 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" +qcrypto_tls_creds_x509_check_key_usage(void *creds, const char *file, int status, int usage, int critical) "TLS creds x509 check key usage creds=%p file=%s status=%d usage=%d critical=%d" +qcrypto_tls_creds_x509_check_key_purpose(void *creds, const char *file, int status, const char *usage, int critical) "TLS creds x509 check key usage creds=%p file=%s status=%d usage=%s critical=%d" +qcrypto_tls_creds_x509_load_cert(void *creds, int isServer, const char *file) "TLS creds x509 load cert creds=%p isServer=%d file=%s" +qcrypto_tls_creds_x509_load_cert_list(void *creds, const char *file) "TLS creds x509 load cert list creds=%p file=%s" + +# crypto/tlssession.c +qcrypto_tls_session_new(void *session, void *creds, const char *hostname, const char *aclname, int endpoint) "TLS session new session=%p creds=%p hostname=%s aclname=%s endpoint=%d" diff --git a/ui/Makefile.objs b/ui/Makefile.objs index c62d4d9722..0034fbb49f 100644 --- a/ui/Makefile.objs +++ b/ui/Makefile.objs @@ -2,7 +2,7 @@ vnc-obj-y += vnc.o vnc-obj-y += vnc-enc-zlib.o vnc-enc-hextile.o vnc-obj-y += vnc-enc-tight.o vnc-palette.o vnc-obj-y += vnc-enc-zrle.o -vnc-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o +vnc-obj-y += vnc-auth-vencrypt.o vnc-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o vnc-obj-y += vnc-ws.o vnc-obj-y += vnc-jobs.o @@ -165,8 +165,10 @@ struct GtkDisplayState { bool ignore_keys; }; -static void gd_grab_pointer(VirtualConsole *vc); +static void gd_grab_pointer(VirtualConsole *vc, const char *reason); static void gd_ungrab_pointer(GtkDisplayState *s); +static void gd_grab_keyboard(VirtualConsole *vc, const char *reason); +static void gd_ungrab_keyboard(GtkDisplayState *s); /** Utility Functions **/ @@ -849,13 +851,11 @@ static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button, /* implicitly grab the input at the first click in the relative mode */ if (button->button == 1 && button->type == GDK_BUTTON_PRESS && !qemu_input_is_absolute() && s->ptr_owner != vc) { - gd_ungrab_pointer(s); if (!vc->window) { gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), TRUE); } else { - gd_grab_pointer(vc); - gd_update_caption(s); + gd_grab_pointer(vc, "relative-mode-click"); } return TRUE; } @@ -1092,9 +1092,8 @@ static gboolean gd_win_grab(void *opaque) if (vc->s->ptr_owner) { gd_ungrab_pointer(vc->s); } else { - gd_grab_pointer(vc); + gd_grab_pointer(vc, "user-request-detached-tab"); } - gd_update_caption(vc->s); return TRUE; } @@ -1141,10 +1140,6 @@ static void gd_menu_full_screen(GtkMenuItem *item, void *opaque) gtk_widget_hide(s->menu_bar); if (vc->type == GD_VC_GFX) { gtk_widget_set_size_request(vc->gfx.drawing_area, -1, -1); - if (qemu_console_is_graphic(vc->gfx.dcl.con)) { - gtk_check_menu_item_set_active - (GTK_CHECK_MENU_ITEM(s->grab_item), TRUE); - } } gtk_window_fullscreen(GTK_WINDOW(s->window)); s->full_screen = TRUE; @@ -1157,8 +1152,6 @@ static void gd_menu_full_screen(GtkMenuItem *item, void *opaque) vc->gfx.scale_x = 1.0; vc->gfx.scale_y = 1.0; gd_update_windowsize(vc); - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), - FALSE); } } @@ -1257,8 +1250,16 @@ static void gd_grab_devices(VirtualConsole *vc, bool grab, } #endif -static void gd_grab_keyboard(VirtualConsole *vc) +static void gd_grab_keyboard(VirtualConsole *vc, const char *reason) { + if (vc->s->kbd_owner) { + if (vc->s->kbd_owner == vc) { + return; + } else { + gd_ungrab_keyboard(vc->s); + } + } + #if GTK_CHECK_VERSION(3, 0, 0) gd_grab_devices(vc, true, GDK_SOURCE_KEYBOARD, GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK, @@ -1269,7 +1270,8 @@ static void gd_grab_keyboard(VirtualConsole *vc) GDK_CURRENT_TIME); #endif vc->s->kbd_owner = vc; - trace_gd_grab(vc->label, "kbd", true); + gd_update_caption(vc->s); + trace_gd_grab(vc->label, "kbd", reason); } static void gd_ungrab_keyboard(GtkDisplayState *s) @@ -1286,12 +1288,22 @@ static void gd_ungrab_keyboard(GtkDisplayState *s) #else gdk_keyboard_ungrab(GDK_CURRENT_TIME); #endif - trace_gd_grab(vc->label, "kbd", false); + gd_update_caption(s); + trace_gd_ungrab(vc->label, "kbd"); } -static void gd_grab_pointer(VirtualConsole *vc) +static void gd_grab_pointer(VirtualConsole *vc, const char *reason) { GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area); + + if (vc->s->ptr_owner) { + if (vc->s->ptr_owner == vc) { + return; + } else { + gd_ungrab_pointer(vc->s); + } + } + #if GTK_CHECK_VERSION(3, 0, 0) GdkDeviceManager *mgr = gdk_display_get_device_manager(display); gd_grab_devices(vc, true, GDK_SOURCE_MOUSE, @@ -1318,7 +1330,8 @@ static void gd_grab_pointer(VirtualConsole *vc) &vc->s->grab_x_root, &vc->s->grab_y_root, NULL); #endif vc->s->ptr_owner = vc; - trace_gd_grab(vc->label, "ptr", true); + gd_update_caption(vc->s); + trace_gd_grab(vc->label, "ptr", reason); } static void gd_ungrab_pointer(GtkDisplayState *s) @@ -1343,7 +1356,8 @@ static void gd_ungrab_pointer(GtkDisplayState *s) gtk_widget_get_screen(vc->gfx.drawing_area), vc->s->grab_x_root, vc->s->grab_y_root); #endif - trace_gd_grab(vc->label, "ptr", false); + gd_update_caption(s); + trace_gd_ungrab(vc->label, "ptr"); } static void gd_menu_grab_input(GtkMenuItem *item, void *opaque) @@ -1352,16 +1366,13 @@ static void gd_menu_grab_input(GtkMenuItem *item, void *opaque) VirtualConsole *vc = gd_vc_find_current(s); if (gd_is_grab_active(s)) { - if (!gd_grab_on_hover(s)) { - gd_grab_keyboard(vc); - } - gd_grab_pointer(vc); + gd_grab_keyboard(vc, "user-request-main-window"); + gd_grab_pointer(vc, "user-request-main-window"); } else { gd_ungrab_keyboard(s); gd_ungrab_pointer(s); } - gd_update_caption(s); gd_update_cursor(vc); } @@ -1415,9 +1426,7 @@ static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing, GtkDisplayState *s = vc->s; if (gd_grab_on_hover(s)) { - gd_ungrab_keyboard(s); - gd_grab_keyboard(vc); - gd_update_caption(s); + gd_grab_keyboard(vc, "grab-on-hover"); } return TRUE; } @@ -1430,7 +1439,6 @@ static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing, if (gd_grab_on_hover(s)) { gd_ungrab_keyboard(s); - gd_update_caption(s); } return TRUE; } @@ -1768,6 +1776,7 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, if (dpy_ui_info_supported(vc->gfx.dcl.con)) { gtk_menu_item_activate(GTK_MENU_ITEM(s->zoom_fit_item)); + s->free_scale = true; } return group; @@ -1941,7 +1950,8 @@ void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover) s->free_scale = FALSE; - setlocale(LC_ALL, ""); + /* LC_MESSAGES only. See early_gtk_display_init() for details */ + setlocale(LC_MESSAGES, ""); bindtextdomain("qemu", CONFIG_QEMU_LOCALEDIR); textdomain("qemu"); @@ -2010,6 +2020,24 @@ void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover) void early_gtk_display_init(int opengl) { + /* The QEMU code relies on the assumption that it's always run in + * the C locale. Therefore it is not prepared to deal with + * operations that produce different results depending on the + * locale, such as printf's formatting of decimal numbers, and + * possibly others. + * + * Since GTK+ calls setlocale() by default -importing the locale + * settings from the environment- we must prevent it from doing so + * using gtk_disable_setlocale(). + * + * QEMU's GTK+ UI, however, _does_ have translations for some of + * the menu items. As a trade-off between a functionally correct + * QEMU and a fully internationalized UI we support importing + * LC_MESSAGES from the environment (see the setlocale() call + * earlier in this file). This allows us to display translated + * messages leaving everything else untouched. + */ + gtk_disable_setlocale(); gtkinit = gtk_init_check(NULL, NULL); if (!gtkinit) { /* don't exit yet, that'll break -help */ diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c index 62a5fc4bf1..fc732bdbac 100644 --- a/ui/vnc-auth-sasl.c +++ b/ui/vnc-auth-sasl.c @@ -525,21 +525,24 @@ void start_auth_sasl(VncState *vs) goto authabort; } -#ifdef CONFIG_VNC_TLS /* Inform SASL that we've got an external SSF layer from TLS/x509 */ if (vs->auth == VNC_AUTH_VENCRYPT && vs->subauth == VNC_AUTH_VENCRYPT_X509SASL) { - gnutls_cipher_algorithm_t cipher; + Error *local_err = NULL; + int keysize; sasl_ssf_t ssf; - cipher = gnutls_cipher_get(vs->tls.session); - if (!(ssf = (sasl_ssf_t)gnutls_cipher_get_key_size(cipher))) { - VNC_DEBUG("%s", "cannot TLS get cipher size\n"); + keysize = qcrypto_tls_session_get_key_size(vs->tls, + &local_err); + if (keysize < 0) { + VNC_DEBUG("cannot TLS get cipher size: %s\n", + error_get_pretty(local_err)); + error_free(local_err); sasl_dispose(&vs->sasl.conn); vs->sasl.conn = NULL; goto authabort; } - ssf *= 8; /* tls key size is bytes, sasl wants bits */ + ssf = keysize * CHAR_BIT; /* tls key size is bytes, sasl wants bits */ err = sasl_setprop(vs->sasl.conn, SASL_SSF_EXTERNAL, &ssf); if (err != SASL_OK) { @@ -549,20 +552,19 @@ void start_auth_sasl(VncState *vs) vs->sasl.conn = NULL; goto authabort; } - } else -#endif /* CONFIG_VNC_TLS */ + } else { vs->sasl.wantSSF = 1; + } memset (&secprops, 0, sizeof secprops); - /* Inform SASL that we've got an external SSF layer from TLS */ - if (vs->vd->is_unix -#ifdef CONFIG_VNC_TLS - /* Disable SSF, if using TLS+x509+SASL only. TLS without x509 - is not sufficiently strong */ - || (vs->auth == VNC_AUTH_VENCRYPT && - vs->subauth == VNC_AUTH_VENCRYPT_X509SASL) -#endif /* CONFIG_VNC_TLS */ - ) { + /* Inform SASL that we've got an external SSF layer from TLS. + * + * Disable SSF, if using TLS+x509+SASL only. TLS without x509 + * is not sufficiently strong + */ + if (vs->vd->is_unix || + (vs->auth == VNC_AUTH_VENCRYPT && + vs->subauth == VNC_AUTH_VENCRYPT_X509SASL)) { /* If we've got TLS or UNIX domain sock, we don't care about SSF */ secprops.min_ssf = 0; secprops.max_ssf = 0; diff --git a/ui/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c index 8fc965b4ad..44ac2fae63 100644 --- a/ui/vnc-auth-vencrypt.c +++ b/ui/vnc-auth-vencrypt.c @@ -67,38 +67,42 @@ static void vnc_tls_handshake_io(void *opaque); static int vnc_start_vencrypt_handshake(VncState *vs) { - int ret; - - if ((ret = gnutls_handshake(vs->tls.session)) < 0) { - if (!gnutls_error_is_fatal(ret)) { - VNC_DEBUG("Handshake interrupted (blocking)\n"); - if (!gnutls_record_get_direction(vs->tls.session)) - qemu_set_fd_handler(vs->csock, vnc_tls_handshake_io, NULL, vs); - else - qemu_set_fd_handler(vs->csock, NULL, vnc_tls_handshake_io, vs); - return 0; - } - VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret)); - vnc_client_error(vs); - return -1; + Error *err = NULL; + + if (qcrypto_tls_session_handshake(vs->tls, &err) < 0) { + goto error; } - if (vs->vd->tls.x509verify) { - if (vnc_tls_validate_certificate(vs) < 0) { - VNC_DEBUG("Client verification failed\n"); - vnc_client_error(vs); - return -1; - } else { - VNC_DEBUG("Client verification passed\n"); + switch (qcrypto_tls_session_get_handshake_status(vs->tls)) { + case QCRYPTO_TLS_HANDSHAKE_COMPLETE: + VNC_DEBUG("Handshake done, checking credentials\n"); + if (qcrypto_tls_session_check_credentials(vs->tls, &err) < 0) { + goto error; } - } + VNC_DEBUG("Client verification passed, starting TLS I/O\n"); + qemu_set_fd_handler(vs->csock, vnc_client_read, vnc_client_write, vs); + + start_auth_vencrypt_subauth(vs); + break; - VNC_DEBUG("Handshake done, switching to TLS data mode\n"); - qemu_set_fd_handler(vs->csock, vnc_client_read, vnc_client_write, vs); + case QCRYPTO_TLS_HANDSHAKE_RECVING: + VNC_DEBUG("Handshake interrupted (blocking read)\n"); + qemu_set_fd_handler(vs->csock, vnc_tls_handshake_io, NULL, vs); + break; - start_auth_vencrypt_subauth(vs); + case QCRYPTO_TLS_HANDSHAKE_SENDING: + VNC_DEBUG("Handshake interrupted (blocking write)\n"); + qemu_set_fd_handler(vs->csock, NULL, vnc_tls_handshake_io, vs); + break; + } return 0; + + error: + VNC_DEBUG("Handshake failed %s\n", error_get_pretty(err)); + error_free(err); + vnc_client_error(vs); + return -1; } static void vnc_tls_handshake_io(void *opaque) @@ -110,14 +114,6 @@ static void vnc_tls_handshake_io(void *opaque) } - -#define NEED_X509_AUTH(vs) \ - ((vs)->subauth == VNC_AUTH_VENCRYPT_X509NONE || \ - (vs)->subauth == VNC_AUTH_VENCRYPT_X509VNC || \ - (vs)->subauth == VNC_AUTH_VENCRYPT_X509PLAIN || \ - (vs)->subauth == VNC_AUTH_VENCRYPT_X509SASL) - - static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len) { int auth = read_u32(data, 0); @@ -128,15 +124,29 @@ static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len vnc_flush(vs); vnc_client_error(vs); } else { + Error *err = NULL; VNC_DEBUG("Accepting auth %d, setting up TLS for handshake\n", auth); vnc_write_u8(vs, 1); /* Accept auth */ vnc_flush(vs); - if (vnc_tls_client_setup(vs, NEED_X509_AUTH(vs)) < 0) { - VNC_DEBUG("Failed to setup TLS\n"); + vs->tls = qcrypto_tls_session_new(vs->vd->tlscreds, + NULL, + vs->vd->tlsaclname, + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, + &err); + if (!vs->tls) { + VNC_DEBUG("Failed to setup TLS %s\n", + error_get_pretty(err)); + error_free(err); + vnc_client_error(vs); return 0; } + qcrypto_tls_session_set_callbacks(vs->tls, + vnc_tls_push, + vnc_tls_pull, + vs); + VNC_DEBUG("Start TLS VeNCrypt handshake process\n"); if (vnc_start_vencrypt_handshake(vs) < 0) { VNC_DEBUG("Failed to start TLS handshake\n"); diff --git a/ui/vnc-tls.c b/ui/vnc-tls.c deleted file mode 100644 index 028fc4db1f..0000000000 --- a/ui/vnc-tls.c +++ /dev/null @@ -1,474 +0,0 @@ -/* - * QEMU VNC display driver: TLS helpers - * - * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws> - * Copyright (C) 2006 Fabrice Bellard - * Copyright (C) 2009 Red Hat, Inc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu-x509.h" -#include "vnc.h" -#include "qemu/sockets.h" - -#if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2 -/* Very verbose, so only enabled for _VNC_DEBUG >= 2 */ -static void vnc_debug_gnutls_log(int level, const char* str) { - VNC_DEBUG("%d %s", level, str); -} -#endif /* defined(_VNC_DEBUG) && _VNC_DEBUG >= 2 */ - - -#define DH_BITS 1024 -static gnutls_dh_params_t dh_params; - -static int vnc_tls_initialize(void) -{ - static int tlsinitialized = 0; - - if (tlsinitialized) - return 1; - - if (gnutls_global_init () < 0) - return 0; - - /* XXX ought to re-generate diffie-hellman params periodically */ - if (gnutls_dh_params_init (&dh_params) < 0) - return 0; - if (gnutls_dh_params_generate2 (dh_params, DH_BITS) < 0) - return 0; - -#if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2 - gnutls_global_set_log_level(10); - gnutls_global_set_log_function(vnc_debug_gnutls_log); -#endif - - tlsinitialized = 1; - - return 1; -} - -static ssize_t vnc_tls_push(gnutls_transport_ptr_t transport, - const void *data, - size_t len) { - VncState *vs = (VncState *)transport; - int ret; - - retry: - ret = send(vs->csock, data, len, 0); - if (ret < 0) { - if (errno == EINTR) - goto retry; - return -1; - } - return ret; -} - - -static ssize_t vnc_tls_pull(gnutls_transport_ptr_t transport, - void *data, - size_t len) { - VncState *vs = (VncState *)transport; - int ret; - - retry: - ret = qemu_recv(vs->csock, data, len, 0); - if (ret < 0) { - if (errno == EINTR) - goto retry; - return -1; - } - return ret; -} - - -static gnutls_anon_server_credentials_t vnc_tls_initialize_anon_cred(void) -{ - gnutls_anon_server_credentials_t anon_cred; - int ret; - - if ((ret = gnutls_anon_allocate_server_credentials(&anon_cred)) < 0) { - VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret)); - return NULL; - } - - gnutls_anon_set_server_dh_params(anon_cred, dh_params); - - return anon_cred; -} - - -static gnutls_certificate_credentials_t vnc_tls_initialize_x509_cred(VncDisplay *vd) -{ - gnutls_certificate_credentials_t x509_cred; - int ret; - - if (!vd->tls.x509cacert) { - VNC_DEBUG("No CA x509 certificate specified\n"); - return NULL; - } - if (!vd->tls.x509cert) { - VNC_DEBUG("No server x509 certificate specified\n"); - return NULL; - } - if (!vd->tls.x509key) { - VNC_DEBUG("No server private key specified\n"); - return NULL; - } - - if ((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0) { - VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret)); - return NULL; - } - if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred, - vd->tls.x509cacert, - GNUTLS_X509_FMT_PEM)) < 0) { - VNC_DEBUG("Cannot load CA certificate %s\n", gnutls_strerror(ret)); - gnutls_certificate_free_credentials(x509_cred); - return NULL; - } - - if ((ret = gnutls_certificate_set_x509_key_file (x509_cred, - vd->tls.x509cert, - vd->tls.x509key, - GNUTLS_X509_FMT_PEM)) < 0) { - VNC_DEBUG("Cannot load certificate & key %s\n", gnutls_strerror(ret)); - gnutls_certificate_free_credentials(x509_cred); - return NULL; - } - - if (vd->tls.x509cacrl) { - if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred, - vd->tls.x509cacrl, - GNUTLS_X509_FMT_PEM)) < 0) { - VNC_DEBUG("Cannot load CRL %s\n", gnutls_strerror(ret)); - gnutls_certificate_free_credentials(x509_cred); - return NULL; - } - } - - gnutls_certificate_set_dh_params (x509_cred, dh_params); - - return x509_cred; -} - - -int vnc_tls_validate_certificate(VncState *vs) -{ - int ret; - unsigned int status; - const gnutls_datum_t *certs; - unsigned int nCerts, i; - time_t now; - - VNC_DEBUG("Validating client certificate\n"); - if ((ret = gnutls_certificate_verify_peers2 (vs->tls.session, &status)) < 0) { - VNC_DEBUG("Verify failed %s\n", gnutls_strerror(ret)); - return -1; - } - - if ((now = time(NULL)) == ((time_t)-1)) { - return -1; - } - - if (status != 0) { - if (status & GNUTLS_CERT_INVALID) - VNC_DEBUG("The certificate is not trusted.\n"); - - if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) - VNC_DEBUG("The certificate hasn't got a known issuer.\n"); - - if (status & GNUTLS_CERT_REVOKED) - VNC_DEBUG("The certificate has been revoked.\n"); - - if (status & GNUTLS_CERT_INSECURE_ALGORITHM) - VNC_DEBUG("The certificate uses an insecure algorithm\n"); - - return -1; - } else { - VNC_DEBUG("Certificate is valid!\n"); - } - - /* Only support x509 for now */ - if (gnutls_certificate_type_get(vs->tls.session) != GNUTLS_CRT_X509) - return -1; - - if (!(certs = gnutls_certificate_get_peers(vs->tls.session, &nCerts))) - return -1; - - for (i = 0 ; i < nCerts ; i++) { - gnutls_x509_crt_t cert; - VNC_DEBUG ("Checking certificate chain %d\n", i); - if (gnutls_x509_crt_init (&cert) < 0) - return -1; - - if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) { - gnutls_x509_crt_deinit (cert); - return -1; - } - - if (gnutls_x509_crt_get_expiration_time (cert) < now) { - VNC_DEBUG("The certificate has expired\n"); - gnutls_x509_crt_deinit (cert); - return -1; - } - - if (gnutls_x509_crt_get_activation_time (cert) > now) { - VNC_DEBUG("The certificate is not yet activated\n"); - gnutls_x509_crt_deinit (cert); - return -1; - } - - if (gnutls_x509_crt_get_activation_time (cert) > now) { - VNC_DEBUG("The certificate is not yet activated\n"); - gnutls_x509_crt_deinit (cert); - return -1; - } - - if (i == 0) { - size_t dnameSize = 1024; - vs->tls.dname = g_malloc(dnameSize); - requery: - if ((ret = gnutls_x509_crt_get_dn (cert, vs->tls.dname, &dnameSize)) != 0) { - if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) { - vs->tls.dname = g_realloc(vs->tls.dname, dnameSize); - goto requery; - } - gnutls_x509_crt_deinit (cert); - VNC_DEBUG("Cannot get client distinguished name: %s", - gnutls_strerror (ret)); - return -1; - } - - if (vs->vd->tls.x509verify) { - int allow; - if (!vs->vd->tls.acl) { - VNC_DEBUG("no ACL activated, allowing access"); - gnutls_x509_crt_deinit (cert); - continue; - } - - allow = qemu_acl_party_is_allowed(vs->vd->tls.acl, - vs->tls.dname); - - VNC_DEBUG("TLS x509 ACL check for %s is %s\n", - vs->tls.dname, allow ? "allowed" : "denied"); - if (!allow) { - gnutls_x509_crt_deinit (cert); - return -1; - } - } - } - - gnutls_x509_crt_deinit (cert); - } - - return 0; -} - -#if defined(GNUTLS_VERSION_NUMBER) && \ - GNUTLS_VERSION_NUMBER >= 0x020200 /* 2.2.0 */ - -static int vnc_set_gnutls_priority(gnutls_session_t s, int x509) -{ - const char *priority = x509 ? "NORMAL" : "NORMAL:+ANON-DH"; - int rc; - - rc = gnutls_priority_set_direct(s, priority, NULL); - if (rc != GNUTLS_E_SUCCESS) { - return -1; - } - return 0; -} - -#else - -static int vnc_set_gnutls_priority(gnutls_session_t s, int x509) -{ - static const int cert_types[] = { GNUTLS_CRT_X509, 0 }; - static const int protocols[] = { - GNUTLS_TLS1_1, GNUTLS_TLS1_0, GNUTLS_SSL3, 0 - }; - static const int kx_anon[] = { GNUTLS_KX_ANON_DH, 0 }; - static const int kx_x509[] = { - GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA, - GNUTLS_KX_DHE_RSA, GNUTLS_KX_SRP, 0 - }; - int rc; - - rc = gnutls_kx_set_priority(s, x509 ? kx_x509 : kx_anon); - if (rc != GNUTLS_E_SUCCESS) { - return -1; - } - - rc = gnutls_certificate_type_set_priority(s, cert_types); - if (rc != GNUTLS_E_SUCCESS) { - return -1; - } - - rc = gnutls_protocol_set_priority(s, protocols); - if (rc != GNUTLS_E_SUCCESS) { - return -1; - } - return 0; -} - -#endif - -int vnc_tls_client_setup(VncState *vs, - int needX509Creds) { - VNC_DEBUG("Do TLS setup\n"); - if (vnc_tls_initialize() < 0) { - VNC_DEBUG("Failed to init TLS\n"); - vnc_client_error(vs); - return -1; - } - if (vs->tls.session == NULL) { - if (gnutls_init(&vs->tls.session, GNUTLS_SERVER) < 0) { - vnc_client_error(vs); - return -1; - } - - if (gnutls_set_default_priority(vs->tls.session) < 0) { - gnutls_deinit(vs->tls.session); - vs->tls.session = NULL; - vnc_client_error(vs); - return -1; - } - - if (vnc_set_gnutls_priority(vs->tls.session, needX509Creds) < 0) { - gnutls_deinit(vs->tls.session); - vs->tls.session = NULL; - vnc_client_error(vs); - return -1; - } - - if (needX509Creds) { - gnutls_certificate_server_credentials x509_cred = - vnc_tls_initialize_x509_cred(vs->vd); - if (!x509_cred) { - gnutls_deinit(vs->tls.session); - vs->tls.session = NULL; - vnc_client_error(vs); - return -1; - } - if (gnutls_credentials_set(vs->tls.session, - GNUTLS_CRD_CERTIFICATE, x509_cred) < 0) { - gnutls_deinit(vs->tls.session); - vs->tls.session = NULL; - gnutls_certificate_free_credentials(x509_cred); - vnc_client_error(vs); - return -1; - } - if (vs->vd->tls.x509verify) { - VNC_DEBUG("Requesting a client certificate\n"); - gnutls_certificate_server_set_request(vs->tls.session, - GNUTLS_CERT_REQUEST); - } - - } else { - gnutls_anon_server_credentials_t anon_cred = - vnc_tls_initialize_anon_cred(); - if (!anon_cred) { - gnutls_deinit(vs->tls.session); - vs->tls.session = NULL; - vnc_client_error(vs); - return -1; - } - if (gnutls_credentials_set(vs->tls.session, - GNUTLS_CRD_ANON, anon_cred) < 0) { - gnutls_deinit(vs->tls.session); - vs->tls.session = NULL; - gnutls_anon_free_server_credentials(anon_cred); - vnc_client_error(vs); - return -1; - } - } - - gnutls_transport_set_ptr(vs->tls.session, (gnutls_transport_ptr_t)vs); - gnutls_transport_set_push_function(vs->tls.session, vnc_tls_push); - gnutls_transport_set_pull_function(vs->tls.session, vnc_tls_pull); - } - return 0; -} - - -void vnc_tls_client_cleanup(VncState *vs) -{ - if (vs->tls.session) { - gnutls_deinit(vs->tls.session); - vs->tls.session = NULL; - } - g_free(vs->tls.dname); -} - - - -static int vnc_set_x509_credential(VncDisplay *vd, - const char *certdir, - const char *filename, - char **cred, - int ignoreMissing) -{ - struct stat sb; - - g_free(*cred); - *cred = g_malloc(strlen(certdir) + strlen(filename) + 2); - - strcpy(*cred, certdir); - strcat(*cred, "/"); - strcat(*cred, filename); - - VNC_DEBUG("Check %s\n", *cred); - if (stat(*cred, &sb) < 0) { - g_free(*cred); - *cred = NULL; - if (ignoreMissing && errno == ENOENT) - return 0; - return -1; - } - - return 0; -} - - -int vnc_tls_set_x509_creds_dir(VncDisplay *vd, - const char *certdir) -{ - if (vnc_set_x509_credential(vd, certdir, X509_CA_CERT_FILE, &vd->tls.x509cacert, 0) < 0) - goto cleanup; - if (vnc_set_x509_credential(vd, certdir, X509_CA_CRL_FILE, &vd->tls.x509cacrl, 1) < 0) - goto cleanup; - if (vnc_set_x509_credential(vd, certdir, X509_SERVER_CERT_FILE, &vd->tls.x509cert, 0) < 0) - goto cleanup; - if (vnc_set_x509_credential(vd, certdir, X509_SERVER_KEY_FILE, &vd->tls.x509key, 0) < 0) - goto cleanup; - - return 0; - - cleanup: - g_free(vd->tls.x509cacert); - g_free(vd->tls.x509cacrl); - g_free(vd->tls.x509cert); - g_free(vd->tls.x509key); - vd->tls.x509cacert = vd->tls.x509cacrl = vd->tls.x509cert = vd->tls.x509key = NULL; - return -1; -} - diff --git a/ui/vnc-tls.h b/ui/vnc-tls.h deleted file mode 100644 index f9829c7824..0000000000 --- a/ui/vnc-tls.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * QEMU VNC display driver. TLS helpers - * - * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws> - * Copyright (C) 2006 Fabrice Bellard - * Copyright (C) 2009 Red Hat, Inc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - - -#ifndef __QEMU_VNC_TLS_H__ -#define __QEMU_VNC_TLS_H__ - -#include <gnutls/gnutls.h> -#include <gnutls/x509.h> - -#include "qemu/acl.h" - -typedef struct VncDisplayTLS VncDisplayTLS; -typedef struct VncStateTLS VncStateTLS; - -/* Server state */ -struct VncDisplayTLS { - int x509verify; /* Non-zero if server requests & validates client cert */ - qemu_acl *acl; - - /* Paths to x509 certs/keys */ - char *x509cacert; - char *x509cacrl; - char *x509cert; - char *x509key; -}; - -/* Per client state */ -struct VncStateTLS { - gnutls_session_t session; - - /* Client's Distinguished Name from the x509 cert */ - char *dname; -}; - -int vnc_tls_client_setup(VncState *vs, int x509Creds); -void vnc_tls_client_cleanup(VncState *vs); - -int vnc_tls_validate_certificate(VncState *vs); - -int vnc_tls_set_x509_creds_dir(VncDisplay *vd, - const char *path); - - -#endif /* __QEMU_VNC_TLS_H__ */ - diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c index b4cb6bde70..175ea50b4b 100644 --- a/ui/vnc-ws.c +++ b/ui/vnc-ws.c @@ -22,60 +22,70 @@ #include "qemu/main-loop.h" #include "crypto/hash.h" -#ifdef CONFIG_VNC_TLS -#include "qemu/sockets.h" - static int vncws_start_tls_handshake(VncState *vs) { - int ret = gnutls_handshake(vs->tls.session); - - if (ret < 0) { - if (!gnutls_error_is_fatal(ret)) { - VNC_DEBUG("Handshake interrupted (blocking)\n"); - if (!gnutls_record_get_direction(vs->tls.session)) { - qemu_set_fd_handler(vs->csock, vncws_tls_handshake_io, - NULL, vs); - } else { - qemu_set_fd_handler(vs->csock, NULL, vncws_tls_handshake_io, - vs); - } - return 0; - } - VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret)); - vnc_client_error(vs); - return -1; + Error *err = NULL; + + if (qcrypto_tls_session_handshake(vs->tls, &err) < 0) { + goto error; } - if (vs->vd->tls.x509verify) { - if (vnc_tls_validate_certificate(vs) < 0) { - VNC_DEBUG("Client verification failed\n"); - vnc_client_error(vs); - return -1; - } else { - VNC_DEBUG("Client verification passed\n"); + switch (qcrypto_tls_session_get_handshake_status(vs->tls)) { + case QCRYPTO_TLS_HANDSHAKE_COMPLETE: + VNC_DEBUG("Handshake done, checking credentials\n"); + if (qcrypto_tls_session_check_credentials(vs->tls, &err) < 0) { + goto error; } + VNC_DEBUG("Client verification passed, starting TLS I/O\n"); + qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs); + break; + + case QCRYPTO_TLS_HANDSHAKE_RECVING: + VNC_DEBUG("Handshake interrupted (blocking read)\n"); + qemu_set_fd_handler(vs->csock, vncws_tls_handshake_io, NULL, vs); + break; + + case QCRYPTO_TLS_HANDSHAKE_SENDING: + VNC_DEBUG("Handshake interrupted (blocking write)\n"); + qemu_set_fd_handler(vs->csock, NULL, vncws_tls_handshake_io, vs); + break; } - VNC_DEBUG("Handshake done, switching to TLS data mode\n"); - qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs); - return 0; + + error: + VNC_DEBUG("Handshake failed %s\n", error_get_pretty(err)); + error_free(err); + vnc_client_error(vs); + return -1; } void vncws_tls_handshake_io(void *opaque) { VncState *vs = (VncState *)opaque; + Error *err = NULL; - if (!vs->tls.session) { - VNC_DEBUG("TLS Websocket setup\n"); - if (vnc_tls_client_setup(vs, vs->vd->tls.x509cert != NULL) < 0) { - return; - } + vs->tls = qcrypto_tls_session_new(vs->vd->tlscreds, + NULL, + vs->vd->tlsaclname, + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, + &err); + if (!vs->tls) { + VNC_DEBUG("Failed to setup TLS %s\n", + error_get_pretty(err)); + error_free(err); + vnc_client_error(vs); + return; } - VNC_DEBUG("Handshake IO continue\n"); + + qcrypto_tls_session_set_callbacks(vs->tls, + vnc_tls_push, + vnc_tls_pull, + vs); + + VNC_DEBUG("Start TLS WS handshake process\n"); vncws_start_tls_handshake(vs); } -#endif /* CONFIG_VNC_TLS */ void vncws_handshake_read(void *opaque) { diff --git a/ui/vnc-ws.h b/ui/vnc-ws.h index 94942258ec..4ab0a8c899 100644 --- a/ui/vnc-ws.h +++ b/ui/vnc-ws.h @@ -72,9 +72,7 @@ enum { WS_OPCODE_PONG = 0xA }; -#ifdef CONFIG_VNC_TLS void vncws_tls_handshake_io(void *opaque); -#endif /* CONFIG_VNC_TLS */ void vncws_handshake_read(void *opaque); long vnc_client_write_ws(VncState *vs); long vnc_client_read_ws(VncState *vs); @@ -41,6 +41,9 @@ #include "ui/input.h" #include "qapi-event.h" #include "crypto/hash.h" +#include "crypto/tlscredsanon.h" +#include "crypto/tlscredsx509.h" +#include "qom/object_interfaces.h" #define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT #define VNC_REFRESH_INTERVAL_INC 50 @@ -222,7 +225,6 @@ static const char *vnc_auth_name(VncDisplay *vd) { case VNC_AUTH_TLS: return "tls"; case VNC_AUTH_VENCRYPT: -#ifdef CONFIG_VNC_TLS switch (vd->subauth) { case VNC_AUTH_VENCRYPT_PLAIN: return "vencrypt+plain"; @@ -245,9 +247,6 @@ static const char *vnc_auth_name(VncDisplay *vd) { default: return "vencrypt"; } -#else - return "vencrypt"; -#endif case VNC_AUTH_SASL: return "sasl"; } @@ -275,13 +274,12 @@ static void vnc_client_cache_auth(VncState *client) return; } -#ifdef CONFIG_VNC_TLS - if (client->tls.session && - client->tls.dname) { - client->info->has_x509_dname = true; - client->info->x509_dname = g_strdup(client->tls.dname); + if (client->tls) { + client->info->x509_dname = + qcrypto_tls_session_get_peer_name(client->tls); + client->info->has_x509_dname = + client->info->x509_dname != NULL; } -#endif #ifdef CONFIG_VNC_SASL if (client->sasl.conn && client->sasl.username) { @@ -358,12 +356,10 @@ static VncClientInfo *qmp_query_vnc_client(const VncState *client) info->base->family = inet_netfamily(sa.ss_family); info->base->websocket = client->websocket; -#ifdef CONFIG_VNC_TLS - if (client->tls.session && client->tls.dname) { - info->has_x509_dname = true; - info->x509_dname = g_strdup(client->tls.dname); + if (client->tls) { + info->x509_dname = qcrypto_tls_session_get_peer_name(client->tls); + info->has_x509_dname = info->x509_dname != NULL; } -#endif #ifdef CONFIG_VNC_SASL if (client->sasl.conn && client->sasl.username) { info->has_sasl_username = true; @@ -513,7 +509,6 @@ static void qmp_query_auth(VncDisplay *vd, VncInfo2 *info) break; case VNC_AUTH_VENCRYPT: info->auth = VNC_PRIMARY_AUTH_VENCRYPT; -#ifdef CONFIG_VNC_TLS info->has_vencrypt = true; switch (vd->subauth) { case VNC_AUTH_VENCRYPT_PLAIN: @@ -547,7 +542,6 @@ static void qmp_query_auth(VncDisplay *vd, VncInfo2 *info) info->has_vencrypt = false; break; } -#endif break; case VNC_AUTH_SASL: info->auth = VNC_PRIMARY_AUTH_SASL; @@ -1237,9 +1231,7 @@ void vnc_disconnect_finish(VncState *vs) vnc_tight_clear(vs); vnc_zrle_clear(vs); -#ifdef CONFIG_VNC_TLS - vnc_tls_client_cleanup(vs); -#endif /* CONFIG_VNC_TLS */ + qcrypto_tls_session_free(vs->tls); #ifdef CONFIG_VNC_SASL vnc_sasl_client_cleanup(vs); #endif /* CONFIG_VNC_SASL */ @@ -1268,7 +1260,7 @@ void vnc_disconnect_finish(VncState *vs) g_free(vs); } -int vnc_client_io_error(VncState *vs, int ret, int last_errno) +ssize_t vnc_client_io_error(VncState *vs, ssize_t ret, int last_errno) { if (ret == 0 || ret == -1) { if (ret == -1) { @@ -1284,7 +1276,7 @@ int vnc_client_io_error(VncState *vs, int ret, int last_errno) } } - VNC_DEBUG("Closing down client sock: ret %d, errno %d\n", + VNC_DEBUG("Closing down client sock: ret %zd, errno %d\n", ret, ret < 0 ? last_errno : 0); vnc_disconnect_start(vs); @@ -1300,23 +1292,40 @@ void vnc_client_error(VncState *vs) vnc_disconnect_start(vs); } -#ifdef CONFIG_VNC_TLS -static long vnc_client_write_tls(gnutls_session_t *session, - const uint8_t *data, - size_t datalen) + +ssize_t vnc_tls_pull(char *buf, size_t len, void *opaque) { - long ret = gnutls_write(*session, data, datalen); + VncState *vs = opaque; + ssize_t ret; + + retry: + ret = qemu_recv(vs->csock, buf, len, 0); if (ret < 0) { - if (ret == GNUTLS_E_AGAIN) { - errno = EAGAIN; - } else { - errno = EIO; + if (errno == EINTR) { + goto retry; + } + return -1; + } + return ret; +} + + +ssize_t vnc_tls_push(const char *buf, size_t len, void *opaque) +{ + VncState *vs = opaque; + ssize_t ret; + + retry: + ret = send(vs->csock, buf, len, 0); + if (ret < 0) { + if (errno == EINTR) { + goto retry; } - ret = -1; + return -1; } return ret; } -#endif /* CONFIG_VNC_TLS */ + /* * Called to write a chunk of data to the client socket. The data may @@ -1333,20 +1342,23 @@ static long vnc_client_write_tls(gnutls_session_t *session, * the requested 'datalen' if the socket would block. Returns * -1 on error, and disconnects the client socket. */ -long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen) +ssize_t vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen) { - long ret; -#ifdef CONFIG_VNC_TLS - if (vs->tls.session) { - ret = vnc_client_write_tls(&vs->tls.session, data, datalen); + ssize_t ret; + int err = 0; + if (vs->tls) { + ret = qcrypto_tls_session_write(vs->tls, (const char *)data, datalen); + if (ret < 0) { + err = errno; + } } else { -#endif /* CONFIG_VNC_TLS */ ret = send(vs->csock, (const void *)data, datalen, 0); -#ifdef CONFIG_VNC_TLS + if (ret < 0) { + err = socket_error(); + } } -#endif /* CONFIG_VNC_TLS */ VNC_DEBUG("Wrote wire %p %zd -> %ld\n", data, datalen, ret); - return vnc_client_io_error(vs, ret, socket_error()); + return vnc_client_io_error(vs, ret, err); } @@ -1360,9 +1372,9 @@ long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen) * the buffered output data if the socket would block. Returns * -1 on error, and disconnects the client socket. */ -static long vnc_client_write_plain(VncState *vs) +static ssize_t vnc_client_write_plain(VncState *vs) { - long ret; + ssize_t ret; #ifdef CONFIG_VNC_SASL VNC_DEBUG("Write Plain: Pending output %p size %zd offset %zd. Wait SSF %d\n", @@ -1435,22 +1447,6 @@ void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting) vs->read_handler_expect = expecting; } -#ifdef CONFIG_VNC_TLS -static long vnc_client_read_tls(gnutls_session_t *session, uint8_t *data, - size_t datalen) -{ - long ret = gnutls_read(*session, data, datalen); - if (ret < 0) { - if (ret == GNUTLS_E_AGAIN) { - errno = EAGAIN; - } else { - errno = EIO; - } - ret = -1; - } - return ret; -} -#endif /* CONFIG_VNC_TLS */ /* * Called to read a chunk of data from the client socket. The data may @@ -1467,20 +1463,23 @@ static long vnc_client_read_tls(gnutls_session_t *session, uint8_t *data, * the requested 'datalen' if the socket would block. Returns * -1 on error, and disconnects the client socket. */ -long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen) +ssize_t vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen) { - long ret; -#ifdef CONFIG_VNC_TLS - if (vs->tls.session) { - ret = vnc_client_read_tls(&vs->tls.session, data, datalen); + ssize_t ret; + int err = -1; + if (vs->tls) { + ret = qcrypto_tls_session_read(vs->tls, (char *)data, datalen); + if (ret < 0) { + err = errno; + } } else { -#endif /* CONFIG_VNC_TLS */ ret = qemu_recv(vs->csock, data, datalen, 0); -#ifdef CONFIG_VNC_TLS + if (ret < 0) { + err = socket_error(); + } } -#endif /* CONFIG_VNC_TLS */ VNC_DEBUG("Read wire %p %zd -> %ld\n", data, datalen, ret); - return vnc_client_io_error(vs, ret, socket_error()); + return vnc_client_io_error(vs, ret, err); } @@ -1492,9 +1491,9 @@ long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen) * Returns the number of bytes read. Returns -1 on error, and * disconnects the client socket. */ -static long vnc_client_read_plain(VncState *vs) +static ssize_t vnc_client_read_plain(VncState *vs) { - int ret; + ssize_t ret; VNC_DEBUG("Read plain %p size %zd offset %zd\n", vs->input.buffer, vs->input.capacity, vs->input.offset); buffer_reserve(&vs->input, 4096); @@ -1520,7 +1519,7 @@ static void vnc_jobs_bh(void *opaque) void vnc_client_read(void *opaque) { VncState *vs = opaque; - long ret; + ssize_t ret; #ifdef CONFIG_VNC_SASL if (vs->sasl.conn && vs->sasl.runSSF) @@ -2631,12 +2630,10 @@ static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len) start_auth_vnc(vs); break; -#ifdef CONFIG_VNC_TLS case VNC_AUTH_VENCRYPT: VNC_DEBUG("Accept VeNCrypt auth\n"); start_auth_vencrypt(vs); break; -#endif /* CONFIG_VNC_TLS */ #ifdef CONFIG_VNC_SASL case VNC_AUTH_SASL: @@ -3033,12 +3030,9 @@ static void vnc_connect(VncDisplay *vd, int csock, qemu_set_nonblock(vs->csock); if (websocket) { vs->websocket = 1; -#ifdef CONFIG_VNC_TLS if (vd->ws_tls) { qemu_set_fd_handler(vs->csock, vncws_tls_handshake_io, NULL, vs); - } else -#endif /* CONFIG_VNC_TLS */ - { + } else { qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs); } } else @@ -3194,9 +3188,11 @@ static void vnc_display_close(VncDisplay *vs) } vs->auth = VNC_AUTH_INVALID; vs->subauth = VNC_AUTH_INVALID; -#ifdef CONFIG_VNC_TLS - vs->tls.x509verify = 0; -#endif + if (vs->tlscreds) { + object_unparent(OBJECT(vs->tlscreds)); + } + g_free(vs->tlsaclname); + vs->tlsaclname = NULL; } int vnc_display_password(const char *id, const char *password) @@ -3250,6 +3246,10 @@ static QemuOptsList qemu_vnc_opts = { .name = "websocket", .type = QEMU_OPT_STRING, },{ + .name = "tls-creds", + .type = QEMU_OPT_STRING, + },{ + /* Deprecated in favour of tls-creds */ .name = "x509", .type = QEMU_OPT_STRING, },{ @@ -3286,9 +3286,11 @@ static QemuOptsList qemu_vnc_opts = { .name = "sasl", .type = QEMU_OPT_BOOL, },{ + /* Deprecated in favour of tls-creds */ .name = "tls", .type = QEMU_OPT_BOOL, },{ + /* Deprecated in favour of tls-creds */ .name = "x509verify", .type = QEMU_OPT_STRING, },{ @@ -3306,13 +3308,12 @@ static QemuOptsList qemu_vnc_opts = { }; -static void +static int vnc_display_setup_auth(VncDisplay *vs, bool password, bool sasl, - bool tls, - bool x509, - bool websocket) + bool websocket, + Error **errp) { /* * We have a choice of 3 authentication options @@ -3362,17 +3363,24 @@ vnc_display_setup_auth(VncDisplay *vs, * result has the same security characteristics. */ if (password) { - if (tls) { + if (vs->tlscreds) { vs->auth = VNC_AUTH_VENCRYPT; if (websocket) { vs->ws_tls = true; } - if (x509) { + if (object_dynamic_cast(OBJECT(vs->tlscreds), + TYPE_QCRYPTO_TLS_CREDS_X509)) { VNC_DEBUG("Initializing VNC server with x509 password auth\n"); vs->subauth = VNC_AUTH_VENCRYPT_X509VNC; - } else { + } else if (object_dynamic_cast(OBJECT(vs->tlscreds), + TYPE_QCRYPTO_TLS_CREDS_ANON)) { VNC_DEBUG("Initializing VNC server with TLS password auth\n"); vs->subauth = VNC_AUTH_VENCRYPT_TLSVNC; + } else { + error_setg(errp, + "Unsupported TLS cred type %s", + object_get_typename(OBJECT(vs->tlscreds))); + return -1; } } else { VNC_DEBUG("Initializing VNC server with password auth\n"); @@ -3385,17 +3393,24 @@ vnc_display_setup_auth(VncDisplay *vs, vs->ws_auth = VNC_AUTH_INVALID; } } else if (sasl) { - if (tls) { + if (vs->tlscreds) { vs->auth = VNC_AUTH_VENCRYPT; if (websocket) { vs->ws_tls = true; } - if (x509) { + if (object_dynamic_cast(OBJECT(vs->tlscreds), + TYPE_QCRYPTO_TLS_CREDS_X509)) { VNC_DEBUG("Initializing VNC server with x509 SASL auth\n"); vs->subauth = VNC_AUTH_VENCRYPT_X509SASL; - } else { + } else if (object_dynamic_cast(OBJECT(vs->tlscreds), + TYPE_QCRYPTO_TLS_CREDS_ANON)) { VNC_DEBUG("Initializing VNC server with TLS SASL auth\n"); vs->subauth = VNC_AUTH_VENCRYPT_TLSSASL; + } else { + error_setg(errp, + "Unsupported TLS cred type %s", + object_get_typename(OBJECT(vs->tlscreds))); + return -1; } } else { VNC_DEBUG("Initializing VNC server with SASL auth\n"); @@ -3408,17 +3423,24 @@ vnc_display_setup_auth(VncDisplay *vs, vs->ws_auth = VNC_AUTH_INVALID; } } else { - if (tls) { + if (vs->tlscreds) { vs->auth = VNC_AUTH_VENCRYPT; if (websocket) { vs->ws_tls = true; } - if (x509) { + if (object_dynamic_cast(OBJECT(vs->tlscreds), + TYPE_QCRYPTO_TLS_CREDS_X509)) { VNC_DEBUG("Initializing VNC server with x509 no auth\n"); vs->subauth = VNC_AUTH_VENCRYPT_X509NONE; - } else { + } else if (object_dynamic_cast(OBJECT(vs->tlscreds), + TYPE_QCRYPTO_TLS_CREDS_ANON)) { VNC_DEBUG("Initializing VNC server with TLS no auth\n"); vs->subauth = VNC_AUTH_VENCRYPT_TLSNONE; + } else { + error_setg(errp, + "Unsupported TLS cred type %s", + object_get_typename(OBJECT(vs->tlscreds))); + return -1; } } else { VNC_DEBUG("Initializing VNC server with no auth\n"); @@ -3431,8 +3453,55 @@ vnc_display_setup_auth(VncDisplay *vs, vs->ws_auth = VNC_AUTH_INVALID; } } + return 0; +} + + +/* + * Handle back compat with old CLI syntax by creating some + * suitable QCryptoTLSCreds objects + */ +static QCryptoTLSCreds * +vnc_display_create_creds(bool x509, + bool x509verify, + const char *dir, + const char *id, + Error **errp) +{ + gchar *credsid = g_strdup_printf("tlsvnc%s", id); + Object *parent = object_get_objects_root(); + Object *creds; + Error *err = NULL; + + if (x509) { + creds = object_new_with_props(TYPE_QCRYPTO_TLS_CREDS_X509, + parent, + credsid, + &err, + "endpoint", "server", + "dir", dir, + "verify-peer", x509verify ? "yes" : "no", + NULL); + } else { + creds = object_new_with_props(TYPE_QCRYPTO_TLS_CREDS_ANON, + parent, + credsid, + &err, + "endpoint", "server", + NULL); + } + + g_free(credsid); + + if (err) { + error_propagate(errp, err); + return NULL; + } + + return QCRYPTO_TLS_CREDS(creds); } + void vnc_display_open(const char *id, Error **errp) { VncDisplay *vs = vnc_display_find(id); @@ -3447,18 +3516,13 @@ void vnc_display_open(const char *id, Error **errp) char *h; bool has_ipv4 = false; bool has_ipv6 = false; + const char *credid; const char *websocket; - bool tls = false, x509 = false; -#ifdef CONFIG_VNC_TLS - const char *path; -#endif bool sasl = false; #ifdef CONFIG_VNC_SASL int saslErr; #endif -#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL) int acl = 0; -#endif int lock_key_sync = 1; if (!vs) { @@ -3539,32 +3603,67 @@ void vnc_display_open(const char *id, Error **errp) goto fail; } #endif /* CONFIG_VNC_SASL */ - tls = qemu_opt_get_bool(opts, "tls", false); -#ifdef CONFIG_VNC_TLS - path = qemu_opt_get(opts, "x509"); - if (!path) { - path = qemu_opt_get(opts, "x509verify"); - if (path) { - vs->tls.x509verify = true; - } - } - if (path) { - x509 = true; - if (vnc_tls_set_x509_creds_dir(vs, path) < 0) { - error_setg(errp, "Failed to find x509 certificates/keys in %s", - path); + credid = qemu_opt_get(opts, "tls-creds"); + if (credid) { + Object *creds; + if (qemu_opt_get(opts, "tls") || + qemu_opt_get(opts, "x509") || + qemu_opt_get(opts, "x509verify")) { + error_setg(errp, + "'credid' parameter is mutually exclusive with " + "'tls', 'x509' and 'x509verify' parameters"); goto fail; } + + creds = object_resolve_path_component( + object_get_objects_root(), credid); + if (!creds) { + error_setg(errp, "No TLS credentials with id '%s'", + credid); + goto fail; + } + vs->tlscreds = (QCryptoTLSCreds *) + object_dynamic_cast(creds, + TYPE_QCRYPTO_TLS_CREDS); + if (!vs->tlscreds) { + error_setg(errp, "Object with id '%s' is not TLS credentials", + credid); + goto fail; + } + object_ref(OBJECT(vs->tlscreds)); + + if (vs->tlscreds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { + error_setg(errp, + "Expecting TLS credentials with a server endpoint"); + goto fail; + } + } else { + const char *path; + bool tls = false, x509 = false, x509verify = false; + tls = qemu_opt_get_bool(opts, "tls", false); + if (tls) { + path = qemu_opt_get(opts, "x509"); + + if (path) { + x509 = true; + } else { + path = qemu_opt_get(opts, "x509verify"); + if (path) { + x509 = true; + x509verify = true; + } + } + vs->tlscreds = vnc_display_create_creds(x509, + x509verify, + path, + vs->id, + errp); + if (!vs->tlscreds) { + goto fail; + } + } } -#else /* ! CONFIG_VNC_TLS */ - if (tls) { - error_setg(errp, "VNC TLS auth requires gnutls support"); - goto fail; - } -#endif /* ! CONFIG_VNC_TLS */ -#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL) acl = qemu_opt_get_bool(opts, "acl", false); -#endif share = qemu_opt_get(opts, "share"); if (share) { @@ -3604,19 +3703,14 @@ void vnc_display_open(const char *id, Error **errp) vs->non_adaptive = true; } -#ifdef CONFIG_VNC_TLS - if (acl && x509 && vs->tls.x509verify) { - char *aclname; - + if (acl) { if (strcmp(vs->id, "default") == 0) { - aclname = g_strdup("vnc.x509dname"); + vs->tlsaclname = g_strdup("vnc.x509dname"); } else { - aclname = g_strdup_printf("vnc.%s.x509dname", vs->id); + vs->tlsaclname = g_strdup_printf("vnc.%s.x509dname", vs->id); } - vs->tls.acl = qemu_acl_init(aclname); - g_free(aclname); - } -#endif + qemu_acl_init(vs->tlsaclname); + } #ifdef CONFIG_VNC_SASL if (acl && sasl) { char *aclname; @@ -3631,7 +3725,9 @@ void vnc_display_open(const char *id, Error **errp) } #endif - vnc_display_setup_auth(vs, password, sasl, tls, x509, websocket); + if (vnc_display_setup_auth(vs, password, sasl, websocket, errp) < 0) { + goto fail; + } #ifdef CONFIG_VNC_SASL if ((saslErr = sasl_server_init(NULL, "qemu")) != SASL_OK) { @@ -33,6 +33,7 @@ #include "ui/console.h" #include "audio/audio.h" #include "qemu/bitmap.h" +#include "crypto/tlssession.h" #include <zlib.h> #include <stdbool.h> @@ -101,10 +102,7 @@ typedef void VncSendHextileTile(VncState *vs, typedef struct VncDisplay VncDisplay; -#ifdef CONFIG_VNC_TLS -#include "vnc-tls.h" #include "vnc-auth-vencrypt.h" -#endif #ifdef CONFIG_VNC_SASL #include "vnc-auth-sasl.h" #endif @@ -181,9 +179,8 @@ struct VncDisplay bool ws_tls; /* Used by websockets */ bool lossy; bool non_adaptive; -#ifdef CONFIG_VNC_TLS - VncDisplayTLS tls; -#endif + QCryptoTLSCreds *tlscreds; + char *tlsaclname; #ifdef CONFIG_VNC_SASL VncDisplaySASL sasl; #endif @@ -284,9 +281,7 @@ struct VncState int auth; int subauth; /* Used by VeNCrypt */ char challenge[VNC_AUTH_CHALLENGE_SIZE]; -#ifdef CONFIG_VNC_TLS - VncStateTLS tls; -#endif + QCryptoTLSSession *tls; #ifdef CONFIG_VNC_SASL VncStateSASL sasl; #endif @@ -513,8 +508,10 @@ enum { void vnc_client_read(void *opaque); void vnc_client_write(void *opaque); -long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen); -long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen); +ssize_t vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen); +ssize_t vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen); +ssize_t vnc_tls_pull(char *buf, size_t len, void *opaque); +ssize_t vnc_tls_push(const char *buf, size_t len, void *opaque); /* Protocol I/O functions */ void vnc_write(VncState *vs, const void *data, size_t len); @@ -533,7 +530,7 @@ uint32_t read_u32(uint8_t *data, size_t offset); /* Protocol stage functions */ void vnc_client_error(VncState *vs); -int vnc_client_io_error(VncState *vs, int ret, int last_errno); +ssize_t vnc_client_io_error(VncState *vs, ssize_t ret, int last_errno); void start_client_init(VncState *vs); void start_auth_vnc(VncState *vs); |