diff options
69 files changed, 3616 insertions, 1641 deletions
@@ -2836,7 +2836,7 @@ fi # curses probe if test "$curses" != "no" ; then if test "$mingw32" = "yes" ; then - curses_list="-lpdcurses" + curses_list="$($pkg_config --libs ncurses 2>/dev/null):-lpdcurses" else curses_list="$($pkg_config --libs ncurses 2>/dev/null):-lncurses:-lcurses" fi diff --git a/contrib/ivshmem-server/ivshmem-server.c b/contrib/ivshmem-server/ivshmem-server.c index bfd0fad49a..172db78b37 100644 --- a/contrib/ivshmem-server/ivshmem-server.c +++ b/contrib/ivshmem-server/ivshmem-server.c @@ -12,9 +12,6 @@ #include <sys/mman.h> #include <sys/socket.h> #include <sys/un.h> -#ifdef CONFIG_LINUX -#include <sys/vfs.h> -#endif #include "ivshmem-server.h" @@ -257,7 +254,8 @@ ivshmem_server_ftruncate(int fd, unsigned shmsize) /* Init a new ivshmem server */ int ivshmem_server_init(IvshmemServer *server, const char *unix_sock_path, - const char *shm_path, size_t shm_size, unsigned n_vectors, + const char *shm_path, bool use_shm_open, + size_t shm_size, unsigned n_vectors, bool verbose) { int ret; @@ -278,6 +276,7 @@ ivshmem_server_init(IvshmemServer *server, const char *unix_sock_path, return -1; } + server->use_shm_open = use_shm_open; server->shm_size = shm_size; server->n_vectors = n_vectors; @@ -286,31 +285,6 @@ ivshmem_server_init(IvshmemServer *server, const char *unix_sock_path, return 0; } -#ifdef CONFIG_LINUX - -#define HUGETLBFS_MAGIC 0x958458f6 - -static long gethugepagesize(const char *path) -{ - struct statfs fs; - int ret; - - do { - ret = statfs(path, &fs); - } while (ret != 0 && errno == EINTR); - - if (ret != 0) { - return -1; - } - - if (fs.f_type != HUGETLBFS_MAGIC) { - return -1; - } - - return fs.f_bsize; -} -#endif - /* open shm, create and bind to the unix socket */ int ivshmem_server_start(IvshmemServer *server) @@ -319,27 +293,17 @@ ivshmem_server_start(IvshmemServer *server) int shm_fd, sock_fd, ret; /* open shm file */ -#ifdef CONFIG_LINUX - long hpagesize; - - hpagesize = gethugepagesize(server->shm_path); - if (hpagesize < 0 && errno != ENOENT) { - IVSHMEM_SERVER_DEBUG(server, "cannot stat shm file %s: %s\n", - server->shm_path, strerror(errno)); - } - - if (hpagesize > 0) { + if (server->use_shm_open) { + IVSHMEM_SERVER_DEBUG(server, "Using POSIX shared memory: %s\n", + server->shm_path); + shm_fd = shm_open(server->shm_path, O_CREAT | O_RDWR, S_IRWXU); + } else { gchar *filename = g_strdup_printf("%s/ivshmem.XXXXXX", server->shm_path); - IVSHMEM_SERVER_DEBUG(server, "Using hugepages: %s\n", server->shm_path); + IVSHMEM_SERVER_DEBUG(server, "Using file-backed shared memory: %s\n", + server->shm_path); shm_fd = mkstemp(filename); unlink(filename); g_free(filename); - } else -#endif - { - IVSHMEM_SERVER_DEBUG(server, "Using POSIX shared memory: %s\n", - server->shm_path); - shm_fd = shm_open(server->shm_path, O_CREAT|O_RDWR, S_IRWXU); } if (shm_fd < 0) { diff --git a/contrib/ivshmem-server/ivshmem-server.h b/contrib/ivshmem-server/ivshmem-server.h index e9de8a369d..3851639618 100644 --- a/contrib/ivshmem-server/ivshmem-server.h +++ b/contrib/ivshmem-server/ivshmem-server.h @@ -66,6 +66,7 @@ typedef struct IvshmemServer { char unix_sock_path[PATH_MAX]; /**< path to unix socket */ int sock_fd; /**< unix sock file descriptor */ char shm_path[PATH_MAX]; /**< path to shm */ + bool use_shm_open; size_t shm_size; /**< size of shm */ int shm_fd; /**< shm file descriptor */ unsigned n_vectors; /**< number of vectors */ @@ -89,7 +90,8 @@ typedef struct IvshmemServer { */ int ivshmem_server_init(IvshmemServer *server, const char *unix_sock_path, - const char *shm_path, size_t shm_size, unsigned n_vectors, + const char *shm_path, bool use_shm_open, + size_t shm_size, unsigned n_vectors, bool verbose); /** diff --git a/contrib/ivshmem-server/main.c b/contrib/ivshmem-server/main.c index 533f501593..45776d8af4 100644 --- a/contrib/ivshmem-server/main.c +++ b/contrib/ivshmem-server/main.c @@ -30,35 +30,38 @@ typedef struct IvshmemServerArgs { const char *pid_file; const char *unix_socket_path; const char *shm_path; + bool use_shm_open; uint64_t shm_size; unsigned n_vectors; } IvshmemServerArgs; -/* show ivshmem_server_usage and exit with given error code */ static void -ivshmem_server_usage(const char *name, int code) +ivshmem_server_usage(const char *progname) { - fprintf(stderr, "%s [opts]\n", name); - fprintf(stderr, " -h: show this help\n"); - fprintf(stderr, " -v: verbose mode\n"); - fprintf(stderr, " -F: foreground mode (default is to daemonize)\n"); - fprintf(stderr, " -p <pid_file>: path to the PID file (used in daemon\n" - " mode only).\n" - " Default=%s\n", IVSHMEM_SERVER_DEFAULT_SHM_PATH); - fprintf(stderr, " -S <unix_socket_path>: path to the unix socket\n" - " to listen to.\n" - " Default=%s\n", IVSHMEM_SERVER_DEFAULT_UNIX_SOCK_PATH); - fprintf(stderr, " -m <shm_path>: path to the shared memory.\n" - " The path corresponds to a POSIX shm name or a\n" - " hugetlbfs mount point.\n" - " default=%s\n", IVSHMEM_SERVER_DEFAULT_SHM_PATH); - fprintf(stderr, " -l <size>: size of shared memory in bytes. The suffix\n" - " K, M and G can be used (ex: 1K means 1024).\n" - " default=%u\n", IVSHMEM_SERVER_DEFAULT_SHM_SIZE); - fprintf(stderr, " -n <n_vects>: number of vectors.\n" - " default=%u\n", IVSHMEM_SERVER_DEFAULT_N_VECTORS); - - exit(code); + printf("Usage: %s [OPTION]...\n" + " -h: show this help\n" + " -v: verbose mode\n" + " -F: foreground mode (default is to daemonize)\n" + " -p <pid-file>: path to the PID file (used in daemon mode only)\n" + " default " IVSHMEM_SERVER_DEFAULT_PID_FILE "\n" + " -S <unix-socket-path>: path to the unix socket to listen to\n" + " default " IVSHMEM_SERVER_DEFAULT_UNIX_SOCK_PATH "\n" + " -M <shm-name>: POSIX shared memory object to use\n" + " default " IVSHMEM_SERVER_DEFAULT_SHM_PATH "\n" + " -m <dir-name>: where to create shared memory\n" + " -l <size>: size of shared memory in bytes\n" + " suffixes K, M and G can be used, e.g. 1K means 1024\n" + " default %u\n" + " -n <nvectors>: number of vectors\n" + " default %u\n", + progname, IVSHMEM_SERVER_DEFAULT_SHM_SIZE, + IVSHMEM_SERVER_DEFAULT_N_VECTORS); +} + +static void +ivshmem_server_help(const char *progname) +{ + fprintf(stderr, "Try '%s -h' for more information.\n", progname); } /* parse the program arguments, exit on error */ @@ -69,20 +72,12 @@ ivshmem_server_parse_args(IvshmemServerArgs *args, int argc, char *argv[]) unsigned long long v; Error *err = NULL; - while ((c = getopt(argc, argv, - "h" /* help */ - "v" /* verbose */ - "F" /* foreground */ - "p:" /* pid_file */ - "S:" /* unix_socket_path */ - "m:" /* shm_path */ - "l:" /* shm_size */ - "n:" /* n_vectors */ - )) != -1) { + while ((c = getopt(argc, argv, "hvFp:S:m:M:l:n:")) != -1) { switch (c) { case 'h': /* help */ - ivshmem_server_usage(argv[0], 0); + ivshmem_server_usage(argv[0]); + exit(0); break; case 'v': /* verbose */ @@ -93,36 +88,41 @@ ivshmem_server_parse_args(IvshmemServerArgs *args, int argc, char *argv[]) args->foreground = 1; break; - case 'p': /* pid_file */ + case 'p': /* pid file */ args->pid_file = optarg; break; - case 'S': /* unix_socket_path */ + case 'S': /* unix socket path */ args->unix_socket_path = optarg; break; - case 'm': /* shm_path */ + case 'M': /* shm name */ + case 'm': /* dir name */ args->shm_path = optarg; + args->use_shm_open = c == 'M'; break; - case 'l': /* shm_size */ + case 'l': /* shm size */ parse_option_size("shm_size", optarg, &args->shm_size, &err); if (err) { error_report_err(err); - ivshmem_server_usage(argv[0], 1); + ivshmem_server_help(argv[0]); + exit(1); } break; - case 'n': /* n_vectors */ + case 'n': /* number of vectors */ if (parse_uint_full(optarg, &v, 0) < 0) { fprintf(stderr, "cannot parse n_vectors\n"); - ivshmem_server_usage(argv[0], 1); + ivshmem_server_help(argv[0]); + exit(1); } args->n_vectors = v; break; default: - ivshmem_server_usage(argv[0], 1); + ivshmem_server_usage(argv[0]); + exit(1); break; } } @@ -130,12 +130,14 @@ ivshmem_server_parse_args(IvshmemServerArgs *args, int argc, char *argv[]) if (args->n_vectors > IVSHMEM_SERVER_MAX_VECTORS) { fprintf(stderr, "too many requested vectors (max is %d)\n", IVSHMEM_SERVER_MAX_VECTORS); - ivshmem_server_usage(argv[0], 1); + ivshmem_server_help(argv[0]); + exit(1); } if (args->verbose == 1 && args->foreground == 0) { fprintf(stderr, "cannot use verbose in daemon mode\n"); - ivshmem_server_usage(argv[0], 1); + ivshmem_server_help(argv[0]); + exit(1); } } @@ -193,11 +195,18 @@ main(int argc, char *argv[]) .pid_file = IVSHMEM_SERVER_DEFAULT_PID_FILE, .unix_socket_path = IVSHMEM_SERVER_DEFAULT_UNIX_SOCK_PATH, .shm_path = IVSHMEM_SERVER_DEFAULT_SHM_PATH, + .use_shm_open = true, .shm_size = IVSHMEM_SERVER_DEFAULT_SHM_SIZE, .n_vectors = IVSHMEM_SERVER_DEFAULT_N_VECTORS, }; int ret = 1; + /* + * Do not remove this notice without adding proper error handling! + * Start with handling ivshmem_server_send_one_msg() failure. + */ + printf("*** Example code, do not use in production ***\n"); + /* parse arguments, will exit on error */ ivshmem_server_parse_args(&args, argc, argv); @@ -220,7 +229,8 @@ main(int argc, char *argv[]) } /* init the ivshms structure */ - if (ivshmem_server_init(&server, args.unix_socket_path, args.shm_path, + if (ivshmem_server_init(&server, args.unix_socket_path, + args.shm_path, args.use_shm_open, args.shm_size, args.n_vectors, args.verbose) < 0) { fprintf(stderr, "cannot init server\n"); goto err; diff --git a/crypto/cipher-nettle.c b/crypto/cipher-nettle.c index 3c982e483a..70909fb7fe 100644 --- a/crypto/cipher-nettle.c +++ b/crypto/cipher-nettle.c @@ -29,83 +29,147 @@ #include <nettle/serpent.h> #include <nettle/twofish.h> -#if CONFIG_NETTLE_VERSION_MAJOR < 3 -typedef nettle_crypt_func nettle_cipher_func; +typedef void (*QCryptoCipherNettleFuncWrapper)(const void *ctx, + size_t length, + uint8_t *dst, + const uint8_t *src); +#if CONFIG_NETTLE_VERSION_MAJOR < 3 +typedef nettle_crypt_func * QCryptoCipherNettleFuncNative; typedef void * cipher_ctx_t; typedef unsigned cipher_length_t; + +#define cast5_set_key cast128_set_key #else +typedef nettle_cipher_func * QCryptoCipherNettleFuncNative; typedef const void * cipher_ctx_t; typedef size_t cipher_length_t; #endif -static nettle_cipher_func aes_encrypt_wrapper; -static nettle_cipher_func aes_decrypt_wrapper; -static nettle_cipher_func des_encrypt_wrapper; -static nettle_cipher_func des_decrypt_wrapper; - typedef struct QCryptoNettleAES { struct aes_ctx enc; struct aes_ctx dec; } QCryptoNettleAES; -static void aes_encrypt_wrapper(cipher_ctx_t ctx, cipher_length_t length, +static void aes_encrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + const QCryptoNettleAES *aesctx = ctx; + aes_encrypt(&aesctx->enc, length, dst, src); +} + +static void aes_decrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + const QCryptoNettleAES *aesctx = ctx; + aes_decrypt(&aesctx->dec, length, dst, src); +} + +static void des_encrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + des_encrypt(ctx, length, dst, src); +} + +static void des_decrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + des_decrypt(ctx, length, dst, src); +} + +static void cast128_encrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + cast128_encrypt(ctx, length, dst, src); +} + +static void cast128_decrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + cast128_decrypt(ctx, length, dst, src); +} + +static void serpent_encrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + serpent_encrypt(ctx, length, dst, src); +} + +static void serpent_decrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + serpent_decrypt(ctx, length, dst, src); +} + +static void twofish_encrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + twofish_encrypt(ctx, length, dst, src); +} + +static void twofish_decrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + twofish_decrypt(ctx, length, dst, src); +} + +static void aes_encrypt_wrapper(const void *ctx, size_t length, uint8_t *dst, const uint8_t *src) { const QCryptoNettleAES *aesctx = ctx; aes_encrypt(&aesctx->enc, length, dst, src); } -static void aes_decrypt_wrapper(cipher_ctx_t ctx, cipher_length_t length, +static void aes_decrypt_wrapper(const void *ctx, size_t length, uint8_t *dst, const uint8_t *src) { const QCryptoNettleAES *aesctx = ctx; aes_decrypt(&aesctx->dec, length, dst, src); } -static void des_encrypt_wrapper(cipher_ctx_t ctx, cipher_length_t length, +static void des_encrypt_wrapper(const void *ctx, size_t length, uint8_t *dst, const uint8_t *src) { des_encrypt(ctx, length, dst, src); } -static void des_decrypt_wrapper(cipher_ctx_t ctx, cipher_length_t length, +static void des_decrypt_wrapper(const void *ctx, size_t length, uint8_t *dst, const uint8_t *src) { des_decrypt(ctx, length, dst, src); } -static void cast128_encrypt_wrapper(cipher_ctx_t ctx, cipher_length_t length, +static void cast128_encrypt_wrapper(const void *ctx, size_t length, uint8_t *dst, const uint8_t *src) { cast128_encrypt(ctx, length, dst, src); } -static void cast128_decrypt_wrapper(cipher_ctx_t ctx, cipher_length_t length, +static void cast128_decrypt_wrapper(const void *ctx, size_t length, uint8_t *dst, const uint8_t *src) { cast128_decrypt(ctx, length, dst, src); } -static void serpent_encrypt_wrapper(cipher_ctx_t ctx, cipher_length_t length, +static void serpent_encrypt_wrapper(const void *ctx, size_t length, uint8_t *dst, const uint8_t *src) { serpent_encrypt(ctx, length, dst, src); } -static void serpent_decrypt_wrapper(cipher_ctx_t ctx, cipher_length_t length, +static void serpent_decrypt_wrapper(const void *ctx, size_t length, uint8_t *dst, const uint8_t *src) { serpent_decrypt(ctx, length, dst, src); } -static void twofish_encrypt_wrapper(cipher_ctx_t ctx, cipher_length_t length, +static void twofish_encrypt_wrapper(const void *ctx, size_t length, uint8_t *dst, const uint8_t *src) { twofish_encrypt(ctx, length, dst, src); } -static void twofish_decrypt_wrapper(cipher_ctx_t ctx, cipher_length_t length, +static void twofish_decrypt_wrapper(const void *ctx, size_t length, uint8_t *dst, const uint8_t *src) { twofish_decrypt(ctx, length, dst, src); @@ -118,8 +182,10 @@ struct QCryptoCipherNettle { /* Second cipher context for XTS mode only */ void *ctx_tweak; /* Cipher callbacks for both contexts */ - nettle_cipher_func *alg_encrypt; - nettle_cipher_func *alg_decrypt; + QCryptoCipherNettleFuncNative alg_encrypt_native; + QCryptoCipherNettleFuncNative alg_decrypt_native; + QCryptoCipherNettleFuncWrapper alg_encrypt_wrapper; + QCryptoCipherNettleFuncWrapper alg_decrypt_wrapper; uint8_t *iv; size_t blocksize; @@ -182,8 +248,10 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, des_set_key(ctx->ctx, rfbkey); g_free(rfbkey); - ctx->alg_encrypt = des_encrypt_wrapper; - ctx->alg_decrypt = des_decrypt_wrapper; + ctx->alg_encrypt_native = des_encrypt_native; + ctx->alg_decrypt_native = des_decrypt_native; + ctx->alg_encrypt_wrapper = des_encrypt_wrapper; + ctx->alg_decrypt_wrapper = des_decrypt_wrapper; ctx->blocksize = DES_BLOCK_SIZE; break; @@ -213,8 +281,10 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, nkey, key); } - ctx->alg_encrypt = aes_encrypt_wrapper; - ctx->alg_decrypt = aes_decrypt_wrapper; + ctx->alg_encrypt_native = aes_encrypt_native; + ctx->alg_decrypt_native = aes_decrypt_native; + ctx->alg_encrypt_wrapper = aes_encrypt_wrapper; + ctx->alg_decrypt_wrapper = aes_decrypt_wrapper; ctx->blocksize = AES_BLOCK_SIZE; break; @@ -232,8 +302,10 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, cast5_set_key(ctx->ctx, nkey, key); } - ctx->alg_encrypt = cast128_encrypt_wrapper; - ctx->alg_decrypt = cast128_decrypt_wrapper; + ctx->alg_encrypt_native = cast128_encrypt_native; + ctx->alg_decrypt_native = cast128_decrypt_native; + ctx->alg_encrypt_wrapper = cast128_encrypt_wrapper; + ctx->alg_decrypt_wrapper = cast128_decrypt_wrapper; ctx->blocksize = CAST128_BLOCK_SIZE; break; @@ -253,8 +325,10 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, serpent_set_key(ctx->ctx, nkey, key); } - ctx->alg_encrypt = serpent_encrypt_wrapper; - ctx->alg_decrypt = serpent_decrypt_wrapper; + ctx->alg_encrypt_native = serpent_encrypt_native; + ctx->alg_decrypt_native = serpent_decrypt_native; + ctx->alg_encrypt_wrapper = serpent_encrypt_wrapper; + ctx->alg_decrypt_wrapper = serpent_decrypt_wrapper; ctx->blocksize = SERPENT_BLOCK_SIZE; break; @@ -274,8 +348,10 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, twofish_set_key(ctx->ctx, nkey, key); } - ctx->alg_encrypt = twofish_encrypt_wrapper; - ctx->alg_decrypt = twofish_decrypt_wrapper; + ctx->alg_encrypt_native = twofish_encrypt_native; + ctx->alg_decrypt_native = twofish_decrypt_native; + ctx->alg_encrypt_wrapper = twofish_encrypt_wrapper; + ctx->alg_decrypt_wrapper = twofish_decrypt_wrapper; ctx->blocksize = TWOFISH_BLOCK_SIZE; break; @@ -330,18 +406,18 @@ int qcrypto_cipher_encrypt(QCryptoCipher *cipher, switch (cipher->mode) { case QCRYPTO_CIPHER_MODE_ECB: - ctx->alg_encrypt(ctx->ctx, len, out, in); + ctx->alg_encrypt_wrapper(ctx->ctx, len, out, in); break; case QCRYPTO_CIPHER_MODE_CBC: - cbc_encrypt(ctx->ctx, ctx->alg_encrypt, + cbc_encrypt(ctx->ctx, ctx->alg_encrypt_native, ctx->blocksize, ctx->iv, len, out, in); break; case QCRYPTO_CIPHER_MODE_XTS: xts_encrypt(ctx->ctx, ctx->ctx_tweak, - ctx->alg_encrypt, ctx->alg_encrypt, + ctx->alg_encrypt_wrapper, ctx->alg_encrypt_wrapper, ctx->iv, len, out, in); break; @@ -370,11 +446,11 @@ int qcrypto_cipher_decrypt(QCryptoCipher *cipher, switch (cipher->mode) { case QCRYPTO_CIPHER_MODE_ECB: - ctx->alg_decrypt(ctx->ctx, len, out, in); + ctx->alg_decrypt_wrapper(ctx->ctx, len, out, in); break; case QCRYPTO_CIPHER_MODE_CBC: - cbc_decrypt(ctx->ctx, ctx->alg_decrypt, + cbc_decrypt(ctx->ctx, ctx->alg_decrypt_native, ctx->blocksize, ctx->iv, len, out, in); break; @@ -386,7 +462,7 @@ int qcrypto_cipher_decrypt(QCryptoCipher *cipher, return -1; } xts_decrypt(ctx->ctx, ctx->ctx_tweak, - ctx->alg_encrypt, ctx->alg_decrypt, + ctx->alg_encrypt_wrapper, ctx->alg_decrypt_wrapper, ctx->iv, len, out, in); break; diff --git a/default-configs/mips-softmmu-common.mak b/default-configs/mips-softmmu-common.mak new file mode 100644 index 0000000000..37009a3637 --- /dev/null +++ b/default-configs/mips-softmmu-common.mak @@ -0,0 +1,32 @@ +# Common mips*-softmmu CONFIG defines + +include pci.mak +include sound.mak +include usb.mak +CONFIG_ESP=y +CONFIG_VGA_ISA=y +CONFIG_VGA_ISA_MM=y +CONFIG_VGA_CIRRUS=y +CONFIG_VMWARE_VGA=y +CONFIG_SERIAL=y +CONFIG_PARALLEL=y +CONFIG_I8254=y +CONFIG_PCSPK=y +CONFIG_PCKBD=y +CONFIG_FDC=y +CONFIG_ACPI=y +CONFIG_ACPI_X86=y +CONFIG_ACPI_MEMORY_HOTPLUG=y +CONFIG_ACPI_CPU_HOTPLUG=y +CONFIG_APM=y +CONFIG_I8257=y +CONFIG_PIIX4=y +CONFIG_IDE_ISA=y +CONFIG_IDE_PIIX=y +CONFIG_NE2000_ISA=y +CONFIG_MIPSNET=y +CONFIG_PFLASH_CFI01=y +CONFIG_I8259=y +CONFIG_MC146818RTC=y +CONFIG_ISA_TESTDEV=y +CONFIG_EMPTY_SLOT=y diff --git a/default-configs/mips-softmmu.mak b/default-configs/mips-softmmu.mak index 44467c37c1..9fede6e00f 100644 --- a/default-configs/mips-softmmu.mak +++ b/default-configs/mips-softmmu.mak @@ -1,32 +1,3 @@ # Default configuration for mips-softmmu -include pci.mak -include sound.mak -include usb.mak -CONFIG_ESP=y -CONFIG_VGA_ISA=y -CONFIG_VGA_ISA_MM=y -CONFIG_VGA_CIRRUS=y -CONFIG_VMWARE_VGA=y -CONFIG_SERIAL=y -CONFIG_PARALLEL=y -CONFIG_I8254=y -CONFIG_PCSPK=y -CONFIG_PCKBD=y -CONFIG_FDC=y -CONFIG_ACPI=y -CONFIG_ACPI_X86=y -CONFIG_ACPI_MEMORY_HOTPLUG=y -CONFIG_ACPI_CPU_HOTPLUG=y -CONFIG_APM=y -CONFIG_I8257=y -CONFIG_PIIX4=y -CONFIG_IDE_ISA=y -CONFIG_IDE_PIIX=y -CONFIG_NE2000_ISA=y -CONFIG_MIPSNET=y -CONFIG_PFLASH_CFI01=y -CONFIG_I8259=y -CONFIG_MC146818RTC=y -CONFIG_ISA_TESTDEV=y -CONFIG_EMPTY_SLOT=y +include mips-softmmu-common.mak diff --git a/default-configs/mips64-softmmu.mak b/default-configs/mips64-softmmu.mak index 66ed5f94c5..bad7496672 100644 --- a/default-configs/mips64-softmmu.mak +++ b/default-configs/mips64-softmmu.mak @@ -1,38 +1,9 @@ # Default configuration for mips64-softmmu -include pci.mak -include sound.mak -include usb.mak -CONFIG_ESP=y -CONFIG_VGA_ISA=y -CONFIG_VGA_ISA_MM=y -CONFIG_VGA_CIRRUS=y -CONFIG_VMWARE_VGA=y -CONFIG_SERIAL=y -CONFIG_PARALLEL=y -CONFIG_I8254=y -CONFIG_PCSPK=y -CONFIG_PCKBD=y -CONFIG_FDC=y -CONFIG_ACPI=y -CONFIG_ACPI_X86=y -CONFIG_ACPI_MEMORY_HOTPLUG=y -CONFIG_ACPI_CPU_HOTPLUG=y -CONFIG_APM=y -CONFIG_I8257=y -CONFIG_PIIX4=y -CONFIG_IDE_ISA=y -CONFIG_IDE_PIIX=y -CONFIG_NE2000_ISA=y +include mips-softmmu-common.mak CONFIG_RC4030=y CONFIG_DP8393X=y CONFIG_DS1225Y=y -CONFIG_MIPSNET=y -CONFIG_PFLASH_CFI01=y CONFIG_JAZZ=y CONFIG_G364FB=y -CONFIG_I8259=y CONFIG_JAZZ_LED=y -CONFIG_MC146818RTC=y -CONFIG_ISA_TESTDEV=y -CONFIG_EMPTY_SLOT=y diff --git a/default-configs/mips64el-softmmu.mak b/default-configs/mips64el-softmmu.mak index bfca2b2b7c..485e218cfc 100644 --- a/default-configs/mips64el-softmmu.mak +++ b/default-configs/mips64el-softmmu.mak @@ -1,41 +1,12 @@ # Default configuration for mips64el-softmmu -include pci.mak -include sound.mak -include usb.mak -CONFIG_ESP=y -CONFIG_VGA_ISA=y -CONFIG_VGA_ISA_MM=y -CONFIG_VGA_CIRRUS=y -CONFIG_VMWARE_VGA=y -CONFIG_SERIAL=y -CONFIG_PARALLEL=y -CONFIG_I8254=y -CONFIG_PCSPK=y -CONFIG_PCKBD=y -CONFIG_FDC=y -CONFIG_ACPI=y -CONFIG_ACPI_X86=y -CONFIG_ACPI_MEMORY_HOTPLUG=y -CONFIG_ACPI_CPU_HOTPLUG=y -CONFIG_APM=y -CONFIG_I8257=y -CONFIG_PIIX4=y -CONFIG_IDE_ISA=y -CONFIG_IDE_PIIX=y +include mips-softmmu-common.mak CONFIG_IDE_VIA=y -CONFIG_NE2000_ISA=y CONFIG_RC4030=y CONFIG_DP8393X=y CONFIG_DS1225Y=y -CONFIG_MIPSNET=y -CONFIG_PFLASH_CFI01=y CONFIG_FULONG=y CONFIG_JAZZ=y CONFIG_G364FB=y -CONFIG_I8259=y CONFIG_JAZZ_LED=y -CONFIG_MC146818RTC=y CONFIG_VT82C686=y -CONFIG_ISA_TESTDEV=y -CONFIG_EMPTY_SLOT=y diff --git a/default-configs/mipsel-softmmu.mak b/default-configs/mipsel-softmmu.mak index 0162ef0249..a7f6059484 100644 --- a/default-configs/mipsel-softmmu.mak +++ b/default-configs/mipsel-softmmu.mak @@ -1,32 +1,3 @@ # Default configuration for mipsel-softmmu -include pci.mak -include sound.mak -include usb.mak -CONFIG_ESP=y -CONFIG_VGA_ISA=y -CONFIG_VGA_ISA_MM=y -CONFIG_VGA_CIRRUS=y -CONFIG_VMWARE_VGA=y -CONFIG_SERIAL=y -CONFIG_PARALLEL=y -CONFIG_I8254=y -CONFIG_PCSPK=y -CONFIG_PCKBD=y -CONFIG_FDC=y -CONFIG_ACPI=y -CONFIG_ACPI_X86=y -CONFIG_ACPI_MEMORY_HOTPLUG=y -CONFIG_ACPI_CPU_HOTPLUG=y -CONFIG_APM=y -CONFIG_I8257=y -CONFIG_PIIX4=y -CONFIG_IDE_ISA=y -CONFIG_IDE_PIIX=y -CONFIG_NE2000_ISA=y -CONFIG_MIPSNET=y -CONFIG_PFLASH_CFI01=y -CONFIG_I8259=y -CONFIG_MC146818RTC=y -CONFIG_ISA_TESTDEV=y -CONFIG_EMPTY_SLOT=y +include mips-softmmu-common.mak diff --git a/default-configs/pci.mak b/default-configs/pci.mak index 4fa9a28ef6..9c8bc68c4c 100644 --- a/default-configs/pci.mak +++ b/default-configs/pci.mak @@ -36,5 +36,5 @@ CONFIG_SDHCI=y CONFIG_EDU=y CONFIG_VGA=y CONFIG_VGA_PCI=y -CONFIG_IVSHMEM=$(CONFIG_POSIX) +CONFIG_IVSHMEM=$(CONFIG_EVENTFD) CONFIG_ROCKER=y diff --git a/disas/i386.c b/disas/i386.c index 894b0a1e0f..c0e717abe3 100644 --- a/disas/i386.c +++ b/disas/i386.c @@ -155,8 +155,6 @@ /* opcodes/i386-dis.c r1.126 */ #include "qemu-common.h" -#include <setjmp.h> - static int fetch_data2(struct disassemble_info *, bfd_byte *); static int fetch_data(struct disassemble_info *, bfd_byte *); static void ckprefix (void); diff --git a/disas/m68k.c b/disas/m68k.c index 0412ecd4b6..8f74ae1157 100644 --- a/disas/m68k.c +++ b/disas/m68k.c @@ -615,8 +615,6 @@ static const char *const reg_half_names[] = /* Maximum length of an instruction. */ #define MAXLEN 22 -#include <setjmp.h> - struct private { /* Points to first byte not fetched. */ diff --git a/docs/specs/ivshmem-spec.txt b/docs/specs/ivshmem-spec.txt new file mode 100644 index 0000000000..a1f5499796 --- /dev/null +++ b/docs/specs/ivshmem-spec.txt @@ -0,0 +1,254 @@ += Device Specification for Inter-VM shared memory device = + +The Inter-VM shared memory device (ivshmem) is designed to share a +memory region between multiple QEMU processes running different guests +and the host. In order for all guests to be able to pick up the +shared memory area, it is modeled by QEMU as a PCI device exposing +said memory to the guest as a PCI BAR. + +The device can use a shared memory object on the host directly, or it +can obtain one from an ivshmem server. + +In the latter case, the device can additionally interrupt its peers, and +get interrupted by its peers. + + +== Configuring the ivshmem PCI device == + +There are two basic configurations: + +- Just shared memory: -device ivshmem-plain,memdev=HMB,... + + This uses host memory backend HMB. It should have option "share" + set. + +- Shared memory plus interrupts: -device ivshmem,chardev=CHR,vectors=N,... + + An ivshmem server must already be running on the host. The device + connects to the server's UNIX domain socket via character device + CHR. + + Each peer gets assigned a unique ID by the server. IDs must be + between 0 and 65535. + + Interrupts are message-signaled (MSI-X). vectors=N configures the + number of vectors to use. + +For more details on ivshmem device properties, see The QEMU Emulator +User Documentation (qemu-doc.*). + + +== The ivshmem PCI device's guest interface == + +The device has vendor ID 1af4, device ID 1110, revision 1. Before +QEMU 2.6.0, it had revision 0. + +=== PCI BARs === + +The ivshmem PCI device has two or three BARs: + +- BAR0 holds device registers (256 Byte MMIO) +- BAR1 holds MSI-X table and PBA (only ivshmem-doorbell) +- BAR2 maps the shared memory object + +There are two ways to use this device: + +- If you only need the shared memory part, BAR2 suffices. This way, + you have access to the shared memory in the guest and can use it as + you see fit. Memnic, for example, uses ivshmem this way from guest + user space (see http://dpdk.org/browse/memnic). + +- If you additionally need the capability for peers to interrupt each + other, you need BAR0 and BAR1. You will most likely want to write a + kernel driver to handle interrupts. Requires the device to be + configured for interrupts, obviously. + +Before QEMU 2.6.0, BAR2 can initially be invalid if the device is +configured for interrupts. It becomes safely accessible only after +the ivshmem server provided the shared memory. These devices have PCI +revision 0 rather than 1. Guest software should wait for the +IVPosition register (described below) to become non-negative before +accessing BAR2. + +Revision 0 of the device is not capable to tell guest software whether +it is configured for interrupts. + +=== PCI device registers === + +BAR 0 contains the following registers: + + Offset Size Access On reset Function + 0 4 read/write 0 Interrupt Mask + bit 0: peer interrupt (rev 0) + reserved (rev 1) + bit 1..31: reserved + 4 4 read/write 0 Interrupt Status + bit 0: peer interrupt (rev 0) + reserved (rev 1) + bit 1..31: reserved + 8 4 read-only 0 or ID IVPosition + 12 4 write-only N/A Doorbell + bit 0..15: vector + bit 16..31: peer ID + 16 240 none N/A reserved + +Software should only access the registers as specified in column +"Access". Reserved bits should be ignored on read, and preserved on +write. + +In revision 0 of the device, Interrupt Status and Mask Register +together control the legacy INTx interrupt when the device has no +MSI-X capability: INTx is asserted when the bit-wise AND of Status and +Mask is non-zero and the device has no MSI-X capability. Interrupt +Status Register bit 0 becomes 1 when an interrupt request from a peer +is received. Reading the register clears it. + +IVPosition Register: if the device is not configured for interrupts, +this is zero. Else, it is the device's ID (between 0 and 65535). + +Before QEMU 2.6.0, the register may read -1 for a short while after +reset. These devices have PCI revision 0 rather than 1. + +There is no good way for software to find out whether the device is +configured for interrupts. A positive IVPosition means interrupts, +but zero could be either. + +Doorbell Register: writing this register requests to interrupt a peer. +The written value's high 16 bits are the ID of the peer to interrupt, +and its low 16 bits select an interrupt vector. + +If the device is not configured for interrupts, the write is ignored. + +If the interrupt hasn't completed setup, the write is ignored. The +device is not capable to tell guest software whether setup is +complete. Interrupts can regress to this state on migration. + +If the peer with the requested ID isn't connected, or it has fewer +interrupt vectors connected, the write is ignored. The device is not +capable to tell guest software what peers are connected, or how many +interrupt vectors are connected. + +The peer's interrupt for this vector then becomes pending. There is +no way for software to clear the pending bit, and a polling mode of +operation is therefore impossible. + +If the peer is a revision 0 device without MSI-X capability, its +Interrupt Status register is set to 1. This asserts INTx unless +masked by the Interrupt Mask register. The device is not capable to +communicate the interrupt vector to guest software then. + +With multiple MSI-X vectors, different vectors can be used to indicate +different events have occurred. The semantics of interrupt vectors +are left to the application. + + +== Interrupt infrastructure == + +When configured for interrupts, the peers share eventfd objects in +addition to shared memory. The shared resources are managed by an +ivshmem server. + +=== The ivshmem server === + +The server listens on a UNIX domain socket. + +For each new client that connects to the server, the server +- picks an ID, +- creates eventfd file descriptors for the interrupt vectors, +- sends the ID and the file descriptor for the shared memory to the + new client, +- sends connect notifications for the new client to the other clients + (these contain file descriptors for sending interrupts), +- sends connect notifications for the other clients to the new client, + and +- sends interrupt setup messages to the new client (these contain file + descriptors for receiving interrupts). + +The first client to connect to the server receives ID zero. + +When a client disconnects from the server, the server sends disconnect +notifications to the other clients. + +The next section describes the protocol in detail. + +If the server terminates without sending disconnect notifications for +its connected clients, the clients can elect to continue. They can +communicate with each other normally, but won't receive disconnect +notification on disconnect, and no new clients can connect. There is +no way for the clients to connect to a restarted server. The device +is not capable to tell guest software whether the server is still up. + +Example server code is in contrib/ivshmem-server/. Not to be used in +production. It assumes all clients use the same number of interrupt +vectors. + +A standalone client is in contrib/ivshmem-client/. It can be useful +for debugging. + +=== The ivshmem Client-Server Protocol === + +An ivshmem device configured for interrupts connects to an ivshmem +server. This section details the protocol between the two. + +The connection is one-way: the server sends messages to the client. +Each message consists of a single 8 byte little-endian signed number, +and may be accompanied by a file descriptor via SCM_RIGHTS. Both +client and server close the connection on error. + +Note: QEMU currently doesn't close the connection right on error, but +only when the character device is destroyed. + +On connect, the server sends the following messages in order: + +1. The protocol version number, currently zero. The client should + close the connection on receipt of versions it can't handle. + +2. The client's ID. This is unique among all clients of this server. + IDs must be between 0 and 65535, because the Doorbell register + provides only 16 bits for them. + +3. The number -1, accompanied by the file descriptor for the shared + memory. + +4. Connect notifications for existing other clients, if any. This is + a peer ID (number between 0 and 65535 other than the client's ID), + repeated N times. Each repetition is accompanied by one file + descriptor. These are for interrupting the peer with that ID using + vector 0,..,N-1, in order. If the client is configured for fewer + vectors, it closes the extra file descriptors. If it is configured + for more, the extra vectors remain unconnected. + +5. Interrupt setup. This is the client's own ID, repeated N times. + Each repetition is accompanied by one file descriptor. These are + for receiving interrupts from peers using vector 0,..,N-1, in + order. If the client is configured for fewer vectors, it closes + the extra file descriptors. If it is configured for more, the + extra vectors remain unconnected. + +From then on, the server sends these kinds of messages: + +6. Connection / disconnection notification. This is a peer ID. + + - If the number comes with a file descriptor, it's a connection + notification, exactly like in step 4. + + - Else, it's a disconnection notification for the peer with that ID. + +Known bugs: + +* The protocol changed incompatibly in QEMU 2.5. Before, messages + were native endian long, and there was no version number. + +* The protocol is poorly designed. + +=== The ivshmem Client-Client Protocol === + +An ivshmem device configured for interrupts receives eventfd file +descriptors for interrupting peers and getting interrupted by peers +from the server, as explained in the previous section. + +To interrupt a peer, the device writes the 8-byte integer 1 in native +byte order to the respective file descriptor. + +To receive an interrupt, the device reads and discards as many 8-byte +integers as it can. diff --git a/docs/specs/ivshmem_device_spec.txt b/docs/specs/ivshmem_device_spec.txt deleted file mode 100644 index d318d65c32..0000000000 --- a/docs/specs/ivshmem_device_spec.txt +++ /dev/null @@ -1,161 +0,0 @@ - -Device Specification for Inter-VM shared memory device ------------------------------------------------------- - -The Inter-VM shared memory device is designed to share a memory region (created -on the host via the POSIX shared memory API) between multiple QEMU processes -running different guests. In order for all guests to be able to pick up the -shared memory area, it is modeled by QEMU as a PCI device exposing said memory -to the guest as a PCI BAR. -The memory region does not belong to any guest, but is a POSIX memory object on -the host. The host can access this shared memory if needed. - -The device also provides an optional communication mechanism between guests -sharing the same memory object. More details about that in the section 'Guest to -guest communication' section. - - -The Inter-VM PCI device ------------------------ - -From the VM point of view, the ivshmem PCI device supports three BARs. - -- BAR0 is a 1 Kbyte MMIO region to support registers and interrupts when MSI is - not used. -- BAR1 is used for MSI-X when it is enabled in the device. -- BAR2 is used to access the shared memory object. - -It is your choice how to use the device but you must choose between two -behaviors : - -- basically, if you only need the shared memory part, you will map BAR2. - This way, you have access to the shared memory in guest and can use it as you - see fit (memnic, for example, uses it in userland - http://dpdk.org/browse/memnic). - -- BAR0 and BAR1 are used to implement an optional communication mechanism - through interrupts in the guests. If you need an event mechanism between the - guests accessing the shared memory, you will most likely want to write a - kernel driver that will handle interrupts. See details in the section 'Guest - to guest communication' section. - -The behavior is chosen when starting your QEMU processes: -- no communication mechanism needed, the first QEMU to start creates the shared - memory on the host, subsequent QEMU processes will use it. - -- communication mechanism needed, an ivshmem server must be started before any - QEMU processes, then each QEMU process connects to the server unix socket. - -For more details on the QEMU ivshmem parameters, see qemu-doc documentation. - - -Guest to guest communication ----------------------------- - -This section details the communication mechanism between the guests accessing -the ivhsmem shared memory. - -*ivshmem server* - -This server code is available in qemu.git/contrib/ivshmem-server. - -The server must be started on the host before any guest. -It creates a shared memory object then waits for clients to connect on a unix -socket. All the messages are little-endian int64_t integer. - -For each client (QEMU process) that connects to the server: -- the server sends a protocol version, if client does not support it, the client - closes the communication, -- the server assigns an ID for this client and sends this ID to him as the first - message, -- the server sends a fd to the shared memory object to this client, -- the server creates a new set of host eventfds associated to the new client and - sends this set to all already connected clients, -- finally, the server sends all the eventfds sets for all clients to the new - client. - -The server signals all clients when one of them disconnects. - -The client IDs are limited to 16 bits because of the current implementation (see -Doorbell register in 'PCI device registers' subsection). Hence only 65536 -clients are supported. - -All the file descriptors (fd to the shared memory, eventfds for each client) -are passed to clients using SCM_RIGHTS over the server unix socket. - -Apart from the current ivshmem implementation in QEMU, an ivshmem client has -been provided in qemu.git/contrib/ivshmem-client for debug. - -*QEMU as an ivshmem client* - -At initialisation, when creating the ivshmem device, QEMU first receives a -protocol version and closes communication with server if it does not match. -Then, QEMU gets its ID from the server then makes it available through BAR0 -IVPosition register for the VM to use (see 'PCI device registers' subsection). -QEMU then uses the fd to the shared memory to map it to BAR2. -eventfds for all other clients received from the server are stored to implement -BAR0 Doorbell register (see 'PCI device registers' subsection). -Finally, eventfds assigned to this QEMU process are used to send interrupts in -this VM. - -*PCI device registers* - -From the VM point of view, the ivshmem PCI device supports 4 registers of -32-bits each. - -enum ivshmem_registers { - IntrMask = 0, - IntrStatus = 4, - IVPosition = 8, - Doorbell = 12 -}; - -The first two registers are the interrupt mask and status registers. Mask and -status are only used with pin-based interrupts. They are unused with MSI -interrupts. - -Status Register: The status register is set to 1 when an interrupt occurs. - -Mask Register: The mask register is bitwise ANDed with the interrupt status -and the result will raise an interrupt if it is non-zero. However, since 1 is -the only value the status will be set to, it is only the first bit of the mask -that has any effect. Therefore interrupts can be masked by setting the first -bit to 0 and unmasked by setting the first bit to 1. - -IVPosition Register: The IVPosition register is read-only and reports the -guest's ID number. The guest IDs are non-negative integers. When using the -server, since the server is a separate process, the VM ID will only be set when -the device is ready (shared memory is received from the server and accessible -via the device). If the device is not ready, the IVPosition will return -1. -Applications should ensure that they have a valid VM ID before accessing the -shared memory. - -Doorbell Register: To interrupt another guest, a guest must write to the -Doorbell register. The doorbell register is 32-bits, logically divided into -two 16-bit fields. The high 16-bits are the guest ID to interrupt and the low -16-bits are the interrupt vector to trigger. The semantics of the value -written to the doorbell depends on whether the device is using MSI or a regular -pin-based interrupt. In short, MSI uses vectors while regular interrupts set -the status register. - -Regular Interrupts - -If regular interrupts are used (due to either a guest not supporting MSI or the -user specifying not to use them on startup) then the value written to the lower -16-bits of the Doorbell register results is arbitrary and will trigger an -interrupt in the destination guest. - -Message Signalled Interrupts - -An ivshmem device may support multiple MSI vectors. If so, the lower 16-bits -written to the Doorbell register must be between 0 and the maximum number of -vectors the guest supports. The lower 16 bits written to the doorbell is the -MSI vector that will be raised in the destination guest. The number of MSI -vectors is configurable but it is set when the VM is started. - -The important thing to remember with MSI is that it is only a signal, no status -is set (since MSI interrupts are not shared). All information other than the -interrupt itself should be communicated via the shared memory region. Devices -supporting multiple MSI vectors can use different vectors to indicate different -events have occurred. The semantics of interrupt vectors are left to the -user's discretion. diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h index 0875436b83..a4cbdad452 100644 --- a/fpu/softfloat-specialize.h +++ b/fpu/softfloat-specialize.h @@ -113,7 +113,7 @@ const float16 float16_default_nan = const_float16(0xFE00); #if defined(TARGET_SPARC) const float32 float32_default_nan = const_float32(0x7FFFFFFF); #elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) || \ - defined(TARGET_XTENSA) || defined(TARGET_S390X) + defined(TARGET_XTENSA) || defined(TARGET_S390X) || defined(TARGET_TRICORE) const float32 float32_default_nan = const_float32(0x7FC00000); #elif SNAN_BIT_IS_ONE const float32 float32_default_nan = const_float32(0x7FBFFFFF); diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index a8c77ec731..737d29c632 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -518,6 +518,16 @@ PropertyInfo qdev_prop_macaddr = { .set = set_mac, }; +/* --- on/off/auto --- */ + +PropertyInfo qdev_prop_on_off_auto = { + .name = "OnOffAuto", + .description = "on/off/auto", + .enum_table = OnOffAuto_lookup, + .get = get_enum, + .set = set_enum, +}; + /* --- lost tick policy --- */ QEMU_BUILD_BUG_ON(sizeof(LostTickPolicy) != sizeof(int)); diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index 30ba1be957..2eb866899a 100644 --- a/hw/misc/ivshmem.c +++ b/hw/misc/ivshmem.c @@ -28,9 +28,10 @@ #include "migration/migration.h" #include "qemu/error-report.h" #include "qemu/event_notifier.h" -#include "qemu/fifo8.h" +#include "qom/object_interfaces.h" #include "sysemu/char.h" #include "sysemu/hostmem.h" +#include "sysemu/qtest.h" #include "qapi/visitor.h" #include "exec/ram_addr.h" @@ -41,22 +42,31 @@ #define PCI_VENDOR_ID_IVSHMEM PCI_VENDOR_ID_REDHAT_QUMRANET #define PCI_DEVICE_ID_IVSHMEM 0x1110 -#define IVSHMEM_MAX_PEERS G_MAXUINT16 +#define IVSHMEM_MAX_PEERS UINT16_MAX #define IVSHMEM_IOEVENTFD 0 #define IVSHMEM_MSI 1 -#define IVSHMEM_PEER 0 -#define IVSHMEM_MASTER 1 - #define IVSHMEM_REG_BAR_SIZE 0x100 -//#define DEBUG_IVSHMEM -#ifdef DEBUG_IVSHMEM -#define IVSHMEM_DPRINTF(fmt, ...) \ - do {printf("IVSHMEM: " fmt, ## __VA_ARGS__); } while (0) -#else -#define IVSHMEM_DPRINTF(fmt, ...) -#endif +#define IVSHMEM_DEBUG 0 +#define IVSHMEM_DPRINTF(fmt, ...) \ + do { \ + if (IVSHMEM_DEBUG) { \ + printf("IVSHMEM: " fmt, ## __VA_ARGS__); \ + } \ + } while (0) + +#define TYPE_IVSHMEM_COMMON "ivshmem-common" +#define IVSHMEM_COMMON(obj) \ + OBJECT_CHECK(IVShmemState, (obj), TYPE_IVSHMEM_COMMON) + +#define TYPE_IVSHMEM_PLAIN "ivshmem-plain" +#define IVSHMEM_PLAIN(obj) \ + OBJECT_CHECK(IVShmemState, (obj), TYPE_IVSHMEM_PLAIN) + +#define TYPE_IVSHMEM_DOORBELL "ivshmem-doorbell" +#define IVSHMEM_DOORBELL(obj) \ + OBJECT_CHECK(IVShmemState, (obj), TYPE_IVSHMEM_DOORBELL) #define TYPE_IVSHMEM "ivshmem" #define IVSHMEM(obj) \ @@ -77,38 +87,40 @@ typedef struct IVShmemState { PCIDevice parent_obj; /*< public >*/ - HostMemoryBackend *hostmem; + uint32_t features; + + /* exactly one of these two may be set */ + HostMemoryBackend *hostmem; /* with interrupts */ + CharDriverState *server_chr; /* without interrupts */ + + /* registers */ uint32_t intrmask; uint32_t intrstatus; + int vm_id; - CharDriverState **eventfd_chr; - CharDriverState *server_chr; - Fifo8 incoming_fifo; - MemoryRegion ivshmem_mmio; - - /* We might need to register the BAR before we actually have the memory. - * So prepare a container MemoryRegion for the BAR immediately and - * add a subregion when we have the memory. - */ - MemoryRegion bar; - MemoryRegion ivshmem; - uint64_t ivshmem_size; /* size of shared memory region */ - uint32_t ivshmem_64bit; + /* BARs */ + MemoryRegion ivshmem_mmio; /* BAR 0 (registers) */ + MemoryRegion *ivshmem_bar2; /* BAR 2 (shared memory) */ + MemoryRegion server_bar2; /* used with server_chr */ + /* interrupt support */ Peer *peers; - int nb_peers; /* how many peers we have space for */ - - int vm_id; + int nb_peers; /* space in @peers[] */ uint32_t vectors; - uint32_t features; MSIVector *msi_vectors; + uint64_t msg_buf; /* buffer for receiving server messages */ + int msg_buffered_bytes; /* #bytes in @msg_buf */ + /* migration stuff */ + OnOffAuto master; Error *migration_blocker; - char * shmobj; - char * sizearg; - char * role; - int role_val; /* scalar to avoid multiple string comparisons */ + /* legacy cruft */ + char *role; + char *shmobj; + char *sizearg; + size_t legacy_size; + uint32_t not_legacy_32bit; } IVShmemState; /* registers for the Inter-VM shared memory device */ @@ -124,12 +136,34 @@ static inline uint32_t ivshmem_has_feature(IVShmemState *ivs, return (ivs->features & (1 << feature)); } -/* accessing registers - based on rtl8139 */ +static inline bool ivshmem_is_master(IVShmemState *s) +{ + assert(s->master != ON_OFF_AUTO_AUTO); + return s->master == ON_OFF_AUTO_ON; +} + static void ivshmem_update_irq(IVShmemState *s) { PCIDevice *d = PCI_DEVICE(s); - int isr; - isr = (s->intrstatus & s->intrmask) & 0xffffffff; + uint32_t isr = s->intrstatus & s->intrmask; + + /* + * Do nothing unless the device actually uses INTx. Here's how + * the device variants signal interrupts, what they put in PCI + * config space: + * Device variant Interrupt Interrupt Pin MSI-X cap. + * ivshmem-plain none 0 no + * ivshmem-doorbell MSI-X 1 yes(1) + * ivshmem,msi=off INTx 1 no + * ivshmem,msi=on MSI-X 1(2) yes(1) + * (1) if guest enabled MSI-X + * (2) the device lies + * Leads to the condition for doing nothing: + */ + if (ivshmem_has_feature(s, IVSHMEM_MSI) + || !d->config[PCI_INTERRUPT_PIN]) { + return; + } /* don't print ISR resets */ if (isr) { @@ -137,7 +171,7 @@ static void ivshmem_update_irq(IVShmemState *s) isr ? 1 : 0, s->intrstatus, s->intrmask); } - pci_set_irq(d, (isr != 0)); + pci_set_irq(d, isr != 0); } static void ivshmem_IntrMask_write(IVShmemState *s, uint32_t val) @@ -145,7 +179,6 @@ static void ivshmem_IntrMask_write(IVShmemState *s, uint32_t val) IVSHMEM_DPRINTF("IntrMask write(w) val = 0x%04x\n", val); s->intrmask = val; - ivshmem_update_irq(s); } @@ -154,7 +187,6 @@ static uint32_t ivshmem_IntrMask_read(IVShmemState *s) uint32_t ret = s->intrmask; IVSHMEM_DPRINTF("intrmask read(w) val = 0x%04x\n", ret); - return ret; } @@ -163,7 +195,6 @@ static void ivshmem_IntrStatus_write(IVShmemState *s, uint32_t val) IVSHMEM_DPRINTF("IntrStatus write(w) val = 0x%04x\n", val); s->intrstatus = val; - ivshmem_update_irq(s); } @@ -173,9 +204,7 @@ static uint32_t ivshmem_IntrStatus_read(IVShmemState *s) /* reading ISR clears all interrupts */ s->intrstatus = 0; - ivshmem_update_irq(s); - return ret; } @@ -239,12 +268,7 @@ static uint64_t ivshmem_io_read(void *opaque, hwaddr addr, break; case IVPOSITION: - /* return my VM ID if the memory is mapped */ - if (memory_region_is_mapped(&s->ivshmem)) { - ret = s->vm_id; - } else { - ret = -1; - } + ret = s->vm_id; break; default: @@ -265,21 +289,11 @@ static const MemoryRegionOps ivshmem_mmio_ops = { }, }; -static int ivshmem_can_receive(void * opaque) -{ - return sizeof(int64_t); -} - -static void ivshmem_event(void *opaque, int event) -{ - IVSHMEM_DPRINTF("ivshmem_event %d\n", event); -} - static void ivshmem_vector_notify(void *opaque) { MSIVector *entry = opaque; PCIDevice *pdev = entry->pdev; - IVShmemState *s = IVSHMEM(pdev); + IVShmemState *s = IVSHMEM_COMMON(pdev); int vector = entry - s->msi_vectors; EventNotifier *n = &s->peers[s->vm_id].eventfds[vector]; @@ -289,7 +303,9 @@ static void ivshmem_vector_notify(void *opaque) IVSHMEM_DPRINTF("interrupt on vector %p %d\n", pdev, vector); if (ivshmem_has_feature(s, IVSHMEM_MSI)) { - msix_notify(pdev, vector); + if (msix_enabled(pdev)) { + msix_notify(pdev, vector); + } } else { ivshmem_IntrStatus_write(s, 1); } @@ -298,7 +314,7 @@ static void ivshmem_vector_notify(void *opaque) static int ivshmem_vector_unmask(PCIDevice *dev, unsigned vector, MSIMessage msg) { - IVShmemState *s = IVSHMEM(dev); + IVShmemState *s = IVSHMEM_COMMON(dev); EventNotifier *n = &s->peers[s->vm_id].eventfds[vector]; MSIVector *v = &s->msi_vectors[vector]; int ret; @@ -315,7 +331,7 @@ static int ivshmem_vector_unmask(PCIDevice *dev, unsigned vector, static void ivshmem_vector_mask(PCIDevice *dev, unsigned vector) { - IVShmemState *s = IVSHMEM(dev); + IVShmemState *s = IVSHMEM_COMMON(dev); EventNotifier *n = &s->peers[s->vm_id].eventfds[vector]; int ret; @@ -332,7 +348,7 @@ static void ivshmem_vector_poll(PCIDevice *dev, unsigned int vector_start, unsigned int vector_end) { - IVShmemState *s = IVSHMEM(dev); + IVShmemState *s = IVSHMEM_COMMON(dev); unsigned int vector; IVSHMEM_DPRINTF("vector poll %p %d-%d\n", dev, vector_start, vector_end); @@ -357,61 +373,13 @@ static void watch_vector_notifier(IVShmemState *s, EventNotifier *n, { int eventfd = event_notifier_get_fd(n); - /* if MSI is supported we need multiple interrupts */ + assert(!s->msi_vectors[vector].pdev); s->msi_vectors[vector].pdev = PCI_DEVICE(s); qemu_set_fd_handler(eventfd, ivshmem_vector_notify, NULL, &s->msi_vectors[vector]); } -static int check_shm_size(IVShmemState *s, int fd, Error **errp) -{ - /* check that the guest isn't going to try and map more memory than the - * the object has allocated return -1 to indicate error */ - - struct stat buf; - - if (fstat(fd, &buf) < 0) { - error_setg(errp, "exiting: fstat on fd %d failed: %s", - fd, strerror(errno)); - return -1; - } - - if (s->ivshmem_size > buf.st_size) { - error_setg(errp, "Requested memory size greater" - " than shared object size (%" PRIu64 " > %" PRIu64")", - s->ivshmem_size, (uint64_t)buf.st_size); - return -1; - } else { - return 0; - } -} - -/* create the shared memory BAR when we are not using the server, so we can - * create the BAR and map the memory immediately */ -static int create_shared_memory_BAR(IVShmemState *s, int fd, uint8_t attr, - Error **errp) -{ - void * ptr; - - ptr = mmap(0, s->ivshmem_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); - if (ptr == MAP_FAILED) { - error_setg_errno(errp, errno, "Failed to mmap shared memory"); - return -1; - } - - memory_region_init_ram_ptr(&s->ivshmem, OBJECT(s), "ivshmem.bar2", - s->ivshmem_size, ptr); - qemu_set_ram_fd(memory_region_get_ram_addr(&s->ivshmem), fd); - vmstate_register_ram(&s->ivshmem, DEVICE(s)); - memory_region_add_subregion(&s->bar, 0, &s->ivshmem); - - /* region for shared memory */ - pci_register_bar(PCI_DEVICE(s), 2, attr, &s->bar); - - return 0; -} - static void ivshmem_add_eventfd(IVShmemState *s, int posn, int i) { memory_region_add_eventfd(&s->ivshmem_mmio, @@ -436,21 +404,17 @@ static void close_peer_eventfds(IVShmemState *s, int posn) { int i, n; - if (!ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) { - return; - } - if (posn < 0 || posn >= s->nb_peers) { - error_report("invalid peer %d", posn); - return; - } - + assert(posn >= 0 && posn < s->nb_peers); n = s->peers[posn].nb_eventfds; - memory_region_transaction_begin(); - for (i = 0; i < n; i++) { - ivshmem_del_eventfd(s, posn, i); + if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) { + memory_region_transaction_begin(); + for (i = 0; i < n; i++) { + ivshmem_del_eventfd(s, posn, i); + } + memory_region_transaction_commit(); } - memory_region_transaction_commit(); + for (i = 0; i < n; i++) { event_notifier_cleanup(&s->peers[posn].eventfds[i]); } @@ -459,286 +423,320 @@ static void close_peer_eventfds(IVShmemState *s, int posn) s->peers[posn].nb_eventfds = 0; } -/* this function increase the dynamic storage need to store data about other - * peers */ -static int resize_peers(IVShmemState *s, int new_min_size) -{ - - int j, old_size; - - /* limit number of max peers */ - if (new_min_size <= 0 || new_min_size > IVSHMEM_MAX_PEERS) { - return -1; - } - if (new_min_size <= s->nb_peers) { - return 0; - } - - old_size = s->nb_peers; - s->nb_peers = new_min_size; - - IVSHMEM_DPRINTF("bumping storage to %d peers\n", s->nb_peers); - - s->peers = g_realloc(s->peers, s->nb_peers * sizeof(Peer)); - - for (j = old_size; j < s->nb_peers; j++) { - s->peers[j].eventfds = g_new0(EventNotifier, s->vectors); - s->peers[j].nb_eventfds = 0; - } - - return 0; -} - -static bool fifo_update_and_get(IVShmemState *s, const uint8_t *buf, int size, - void *data, size_t len) +static void resize_peers(IVShmemState *s, int nb_peers) { - const uint8_t *p; - uint32_t num; - - assert(len <= sizeof(int64_t)); /* limitation of the fifo */ - if (fifo8_is_empty(&s->incoming_fifo) && size == len) { - memcpy(data, buf, size); - return true; - } - - IVSHMEM_DPRINTF("short read of %d bytes\n", size); - - num = MIN(size, sizeof(int64_t) - fifo8_num_used(&s->incoming_fifo)); - fifo8_push_all(&s->incoming_fifo, buf, num); - - if (fifo8_num_used(&s->incoming_fifo) < len) { - assert(num == 0); - return false; - } - - size -= num; - buf += num; - p = fifo8_pop_buf(&s->incoming_fifo, len, &num); - assert(num == len); - - memcpy(data, p, len); + int old_nb_peers = s->nb_peers; + int i; - if (size > 0) { - fifo8_push_all(&s->incoming_fifo, buf, size); - } + assert(nb_peers > old_nb_peers); + IVSHMEM_DPRINTF("bumping storage to %d peers\n", nb_peers); - return true; -} + s->peers = g_realloc(s->peers, nb_peers * sizeof(Peer)); + s->nb_peers = nb_peers; -static bool fifo_update_and_get_i64(IVShmemState *s, - const uint8_t *buf, int size, int64_t *i64) -{ - if (fifo_update_and_get(s, buf, size, i64, sizeof(*i64))) { - *i64 = GINT64_FROM_LE(*i64); - return true; + for (i = old_nb_peers; i < nb_peers; i++) { + s->peers[i].eventfds = g_new0(EventNotifier, s->vectors); + s->peers[i].nb_eventfds = 0; } - - return false; } -static int ivshmem_add_kvm_msi_virq(IVShmemState *s, int vector) +static void ivshmem_add_kvm_msi_virq(IVShmemState *s, int vector, + Error **errp) { PCIDevice *pdev = PCI_DEVICE(s); MSIMessage msg = msix_get_message(pdev, vector); int ret; IVSHMEM_DPRINTF("ivshmem_add_kvm_msi_virq vector:%d\n", vector); - - if (s->msi_vectors[vector].pdev != NULL) { - return 0; - } + assert(!s->msi_vectors[vector].pdev); ret = kvm_irqchip_add_msi_route(kvm_state, msg, pdev); if (ret < 0) { - error_report("ivshmem: kvm_irqchip_add_msi_route failed"); - return -1; + error_setg(errp, "kvm_irqchip_add_msi_route failed"); + return; } s->msi_vectors[vector].virq = ret; s->msi_vectors[vector].pdev = pdev; - - return 0; } -static void setup_interrupt(IVShmemState *s, int vector) +static void setup_interrupt(IVShmemState *s, int vector, Error **errp) { EventNotifier *n = &s->peers[s->vm_id].eventfds[vector]; bool with_irqfd = kvm_msi_via_irqfd_enabled() && ivshmem_has_feature(s, IVSHMEM_MSI); PCIDevice *pdev = PCI_DEVICE(s); + Error *err = NULL; IVSHMEM_DPRINTF("setting up interrupt for vector: %d\n", vector); if (!with_irqfd) { - IVSHMEM_DPRINTF("with eventfd"); + IVSHMEM_DPRINTF("with eventfd\n"); watch_vector_notifier(s, n, vector); } else if (msix_enabled(pdev)) { - IVSHMEM_DPRINTF("with irqfd"); - if (ivshmem_add_kvm_msi_virq(s, vector) < 0) { + IVSHMEM_DPRINTF("with irqfd\n"); + ivshmem_add_kvm_msi_virq(s, vector, &err); + if (err) { + error_propagate(errp, err); return; } if (!msix_is_masked(pdev, vector)) { kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL, s->msi_vectors[vector].virq); + /* TODO handle error */ } } else { /* it will be delayed until msix is enabled, in write_config */ - IVSHMEM_DPRINTF("with irqfd, delayed until msix enabled"); + IVSHMEM_DPRINTF("with irqfd, delayed until msix enabled\n"); } } -static void ivshmem_read(void *opaque, const uint8_t *buf, int size) +static void process_msg_shmem(IVShmemState *s, int fd, Error **errp) { - IVShmemState *s = opaque; - int incoming_fd; - int new_eventfd; - int64_t incoming_posn; - Error *err = NULL; - Peer *peer; + struct stat buf; + size_t size; + void *ptr; - if (!fifo_update_and_get_i64(s, buf, size, &incoming_posn)) { + if (s->ivshmem_bar2) { + error_setg(errp, "server sent unexpected shared memory message"); + close(fd); return; } - if (incoming_posn < -1) { - IVSHMEM_DPRINTF("invalid incoming_posn %" PRId64 "\n", incoming_posn); + if (fstat(fd, &buf) < 0) { + error_setg_errno(errp, errno, + "can't determine size of shared memory sent by server"); + close(fd); return; } - /* pick off s->server_chr->msgfd and store it, posn should accompany msg */ - incoming_fd = qemu_chr_fe_get_msgfd(s->server_chr); - IVSHMEM_DPRINTF("posn is %" PRId64 ", fd is %d\n", - incoming_posn, incoming_fd); - - /* make sure we have enough space for this peer */ - if (incoming_posn >= s->nb_peers) { - if (resize_peers(s, incoming_posn + 1) < 0) { - error_report("failed to resize peers array"); - if (incoming_fd != -1) { - close(incoming_fd); - } + size = buf.st_size; + + /* Legacy cruft */ + if (s->legacy_size != SIZE_MAX) { + if (size < s->legacy_size) { + error_setg(errp, "server sent only %zd bytes of shared memory", + (size_t)buf.st_size); + close(fd); return; } + size = s->legacy_size; } - peer = &s->peers[incoming_posn]; + /* mmap the region and map into the BAR2 */ + ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (ptr == MAP_FAILED) { + error_setg_errno(errp, errno, "Failed to mmap shared memory"); + close(fd); + return; + } + memory_region_init_ram_ptr(&s->server_bar2, OBJECT(s), + "ivshmem.bar2", size, ptr); + qemu_set_ram_fd(memory_region_get_ram_addr(&s->server_bar2), fd); + s->ivshmem_bar2 = &s->server_bar2; +} - if (incoming_fd == -1) { - /* if posn is positive and unseen before then this is our posn*/ - if (incoming_posn >= 0 && s->vm_id == -1) { - /* receive our posn */ - s->vm_id = incoming_posn; - } else { - /* otherwise an fd == -1 means an existing peer has gone away */ - IVSHMEM_DPRINTF("posn %" PRId64 " has gone away\n", incoming_posn); - close_peer_eventfds(s, incoming_posn); - } +static void process_msg_disconnect(IVShmemState *s, uint16_t posn, + Error **errp) +{ + IVSHMEM_DPRINTF("posn %d has gone away\n", posn); + if (posn >= s->nb_peers || posn == s->vm_id) { + error_setg(errp, "invalid peer %d", posn); return; } + close_peer_eventfds(s, posn); +} - /* if the position is -1, then it's shared memory region fd */ - if (incoming_posn == -1) { - void * map_ptr; +static void process_msg_connect(IVShmemState *s, uint16_t posn, int fd, + Error **errp) +{ + Peer *peer = &s->peers[posn]; + int vector; - if (memory_region_is_mapped(&s->ivshmem)) { - error_report("shm already initialized"); - close(incoming_fd); - return; - } + /* + * The N-th connect message for this peer comes with the file + * descriptor for vector N-1. Count messages to find the vector. + */ + if (peer->nb_eventfds >= s->vectors) { + error_setg(errp, "Too many eventfd received, device has %d vectors", + s->vectors); + close(fd); + return; + } + vector = peer->nb_eventfds++; - if (check_shm_size(s, incoming_fd, &err) == -1) { - error_report_err(err); - close(incoming_fd); - return; - } + IVSHMEM_DPRINTF("eventfds[%d][%d] = %d\n", posn, vector, fd); + event_notifier_init_fd(&peer->eventfds[vector], fd); + fcntl_setfl(fd, O_NONBLOCK); /* msix/irqfd poll non block */ - /* mmap the region and map into the BAR2 */ - map_ptr = mmap(0, s->ivshmem_size, PROT_READ|PROT_WRITE, MAP_SHARED, - incoming_fd, 0); - if (map_ptr == MAP_FAILED) { - error_report("Failed to mmap shared memory %s", strerror(errno)); - close(incoming_fd); - return; - } - memory_region_init_ram_ptr(&s->ivshmem, OBJECT(s), - "ivshmem.bar2", s->ivshmem_size, map_ptr); - qemu_set_ram_fd(memory_region_get_ram_addr(&s->ivshmem), - incoming_fd); - vmstate_register_ram(&s->ivshmem, DEVICE(s)); + if (posn == s->vm_id) { + setup_interrupt(s, vector, errp); + /* TODO do we need to handle the error? */ + } - IVSHMEM_DPRINTF("guest h/w addr = %p, size = %" PRIu64 "\n", - map_ptr, s->ivshmem_size); + if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) { + ivshmem_add_eventfd(s, posn, vector); + } +} - memory_region_add_subregion(&s->bar, 0, &s->ivshmem); +static void process_msg(IVShmemState *s, int64_t msg, int fd, Error **errp) +{ + IVSHMEM_DPRINTF("posn is %" PRId64 ", fd is %d\n", msg, fd); + if (msg < -1 || msg > IVSHMEM_MAX_PEERS) { + error_setg(errp, "server sent invalid message %" PRId64, msg); + close(fd); return; } - /* each peer has an associated array of eventfds, and we keep - * track of how many eventfds received so far */ - /* get a new eventfd: */ - if (peer->nb_eventfds >= s->vectors) { - error_report("Too many eventfd received, device has %d vectors", - s->vectors); - close(incoming_fd); + if (msg == -1) { + process_msg_shmem(s, fd, errp); return; } - new_eventfd = peer->nb_eventfds++; + if (msg >= s->nb_peers) { + resize_peers(s, msg + 1); + } - /* this is an eventfd for a particular peer VM */ - IVSHMEM_DPRINTF("eventfds[%" PRId64 "][%d] = %d\n", incoming_posn, - new_eventfd, incoming_fd); - event_notifier_init_fd(&peer->eventfds[new_eventfd], incoming_fd); - fcntl_setfl(incoming_fd, O_NONBLOCK); /* msix/irqfd poll non block */ + if (fd >= 0) { + process_msg_connect(s, msg, fd, errp); + } else { + process_msg_disconnect(s, msg, errp); + } +} + +static int ivshmem_can_receive(void *opaque) +{ + IVShmemState *s = opaque; + + assert(s->msg_buffered_bytes < sizeof(s->msg_buf)); + return sizeof(s->msg_buf) - s->msg_buffered_bytes; +} + +static void ivshmem_read(void *opaque, const uint8_t *buf, int size) +{ + IVShmemState *s = opaque; + Error *err = NULL; + int fd; + int64_t msg; - if (incoming_posn == s->vm_id) { - setup_interrupt(s, new_eventfd); + assert(size >= 0 && s->msg_buffered_bytes + size <= sizeof(s->msg_buf)); + memcpy((unsigned char *)&s->msg_buf + s->msg_buffered_bytes, buf, size); + s->msg_buffered_bytes += size; + if (s->msg_buffered_bytes < sizeof(s->msg_buf)) { + return; } + msg = le64_to_cpu(s->msg_buf); + s->msg_buffered_bytes = 0; - if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) { - ivshmem_add_eventfd(s, incoming_posn, new_eventfd); + fd = qemu_chr_fe_get_msgfd(s->server_chr); + IVSHMEM_DPRINTF("posn is %" PRId64 ", fd is %d\n", msg, fd); + + process_msg(s, msg, fd, &err); + if (err) { + error_report_err(err); } } -static void ivshmem_check_version(void *opaque, const uint8_t * buf, int size) +static int64_t ivshmem_recv_msg(IVShmemState *s, int *pfd, Error **errp) { - IVShmemState *s = opaque; - int tmp; - int64_t version; + int64_t msg; + int n, ret; + + n = 0; + do { + ret = qemu_chr_fe_read_all(s->server_chr, (uint8_t *)&msg + n, + sizeof(msg) - n); + if (ret < 0 && ret != -EINTR) { + error_setg_errno(errp, -ret, "read from server failed"); + return INT64_MIN; + } + n += ret; + } while (n < sizeof(msg)); - if (!fifo_update_and_get_i64(s, buf, size, &version)) { + *pfd = qemu_chr_fe_get_msgfd(s->server_chr); + return msg; +} + +static void ivshmem_recv_setup(IVShmemState *s, Error **errp) +{ + Error *err = NULL; + int64_t msg; + int fd; + + msg = ivshmem_recv_msg(s, &fd, &err); + if (err) { + error_propagate(errp, err); + return; + } + if (msg != IVSHMEM_PROTOCOL_VERSION) { + error_setg(errp, "server sent version %" PRId64 ", expecting %d", + msg, IVSHMEM_PROTOCOL_VERSION); + return; + } + if (fd != -1) { + error_setg(errp, "server sent invalid version message"); return; } - tmp = qemu_chr_fe_get_msgfd(s->server_chr); - if (tmp != -1 || version != IVSHMEM_PROTOCOL_VERSION) { - fprintf(stderr, "incompatible version, you are connecting to a ivshmem-" - "server using a different protocol please check your setup\n"); - qemu_chr_delete(s->server_chr); - s->server_chr = NULL; + /* + * ivshmem-server sends the remaining initial messages in a fixed + * order, but the device has always accepted them in any order. + * Stay as compatible as practical, just in case people use + * servers that behave differently. + */ + + /* + * ivshmem_device_spec.txt has always required the ID message + * right here, and ivshmem-server has always complied. However, + * older versions of the device accepted it out of order, but + * broke when an interrupt setup message arrived before it. + */ + msg = ivshmem_recv_msg(s, &fd, &err); + if (err) { + error_propagate(errp, err); return; } + if (fd != -1 || msg < 0 || msg > IVSHMEM_MAX_PEERS) { + error_setg(errp, "server sent invalid ID message"); + return; + } + s->vm_id = msg; + + /* + * Receive more messages until we got shared memory. + */ + do { + msg = ivshmem_recv_msg(s, &fd, &err); + if (err) { + error_propagate(errp, err); + return; + } + process_msg(s, msg, fd, &err); + if (err) { + error_propagate(errp, err); + return; + } + } while (msg != -1); - IVSHMEM_DPRINTF("version check ok, switch to real chardev handler\n"); - qemu_chr_add_handlers(s->server_chr, ivshmem_can_receive, ivshmem_read, - ivshmem_event, s); + /* + * This function must either map the shared memory or fail. The + * loop above ensures that: it terminates normally only after it + * successfully processed the server's shared memory message. + * Assert that actually mapped the shared memory: + */ + assert(s->ivshmem_bar2); } /* Select the MSI-X vectors used by device. * ivshmem maps events to vectors statically, so * we just enable all vectors on init and after reset. */ -static void ivshmem_use_msix(IVShmemState * s) +static void ivshmem_msix_vector_use(IVShmemState *s) { PCIDevice *d = PCI_DEVICE(s); int i; - IVSHMEM_DPRINTF("%s, msix present: %d\n", __func__, msix_present(d)); - if (!msix_present(d)) { - return; - } - for (i = 0; i < s->vectors; i++) { msix_vector_use(d, i); } @@ -746,11 +744,13 @@ static void ivshmem_use_msix(IVShmemState * s) static void ivshmem_reset(DeviceState *d) { - IVShmemState *s = IVSHMEM(d); + IVShmemState *s = IVSHMEM_COMMON(d); s->intrstatus = 0; s->intrmask = 0; - ivshmem_use_msix(s); + if (ivshmem_has_feature(s, IVSHMEM_MSI)) { + ivshmem_msix_vector_use(s); + } } static int ivshmem_setup_interrupts(IVShmemState *s) @@ -764,7 +764,7 @@ static int ivshmem_setup_interrupts(IVShmemState *s) } IVSHMEM_DPRINTF("msix initialized (%d vectors)\n", s->vectors); - ivshmem_use_msix(s); + ivshmem_msix_vector_use(s); } return 0; @@ -776,7 +776,13 @@ static void ivshmem_enable_irqfd(IVShmemState *s) int i; for (i = 0; i < s->peers[s->vm_id].nb_eventfds; i++) { - ivshmem_add_kvm_msi_virq(s, i); + Error *err = NULL; + + ivshmem_add_kvm_msi_virq(s, i, &err); + if (err) { + error_report_err(err); + /* TODO do we need to handle the error? */ + } } if (msix_set_vector_notifiers(pdev, @@ -816,13 +822,13 @@ static void ivshmem_disable_irqfd(IVShmemState *s) static void ivshmem_write_config(PCIDevice *pdev, uint32_t address, uint32_t val, int len) { - IVShmemState *s = IVSHMEM(pdev); + IVShmemState *s = IVSHMEM_COMMON(pdev); int is_enabled, was_enabled = msix_enabled(pdev); pci_default_write_config(pdev, address, val, len); is_enabled = msix_enabled(pdev); - if (kvm_msi_via_irqfd_enabled() && s->vm_id != -1) { + if (kvm_msi_via_irqfd_enabled()) { if (!was_enabled && is_enabled) { ivshmem_enable_irqfd(s); } else if (was_enabled && !is_enabled) { @@ -831,42 +837,14 @@ static void ivshmem_write_config(PCIDevice *pdev, uint32_t address, } } -static void pci_ivshmem_realize(PCIDevice *dev, Error **errp) +static void ivshmem_common_realize(PCIDevice *dev, Error **errp) { - IVShmemState *s = IVSHMEM(dev); + IVShmemState *s = IVSHMEM_COMMON(dev); + Error *err = NULL; uint8_t *pci_conf; uint8_t attr = PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_PREFETCH; - if (!!s->server_chr + !!s->shmobj + !!s->hostmem != 1) { - error_setg(errp, - "You must specify either 'shm', 'chardev' or 'x-memdev'"); - return; - } - - if (s->hostmem) { - MemoryRegion *mr; - - if (s->sizearg) { - g_warning("size argument ignored with hostmem"); - } - - mr = host_memory_backend_get_memory(s->hostmem, errp); - s->ivshmem_size = memory_region_size(mr); - } else if (s->sizearg == NULL) { - s->ivshmem_size = 4 << 20; /* 4 MB default */ - } else { - char *end; - int64_t size = qemu_strtosz(s->sizearg, &end); - if (size < 0 || *end != '\0' || !is_power_of_2(size)) { - error_setg(errp, "Invalid size %s", s->sizearg); - return; - } - s->ivshmem_size = size; - } - - fifo8_create(&s->incoming_fifo, sizeof(int64_t)); - /* IRQFD requires MSI */ if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD) && !ivshmem_has_feature(s, IVSHMEM_MSI)) { @@ -874,31 +852,9 @@ static void pci_ivshmem_realize(PCIDevice *dev, Error **errp) return; } - /* check that role is reasonable */ - if (s->role) { - if (strncmp(s->role, "peer", 5) == 0) { - s->role_val = IVSHMEM_PEER; - } else if (strncmp(s->role, "master", 7) == 0) { - s->role_val = IVSHMEM_MASTER; - } else { - error_setg(errp, "'role' must be 'peer' or 'master'"); - return; - } - } else { - s->role_val = IVSHMEM_MASTER; /* default */ - } - - if (s->role_val == IVSHMEM_PEER) { - error_setg(&s->migration_blocker, - "Migration is disabled when using feature 'peer mode' in device 'ivshmem'"); - migrate_add_blocker(s->migration_blocker); - } - pci_conf = dev->config; pci_conf[PCI_COMMAND] = PCI_COMMAND_IO | PCI_COMMAND_MEMORY; - pci_config_set_interrupt_pin(pci_conf, 1); - memory_region_init_io(&s->ivshmem_mmio, OBJECT(s), &ivshmem_mmio_ops, s, "ivshmem-mmio", IVSHMEM_REG_BAR_SIZE); @@ -906,116 +862,87 @@ static void pci_ivshmem_realize(PCIDevice *dev, Error **errp) pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->ivshmem_mmio); - memory_region_init(&s->bar, OBJECT(s), "ivshmem-bar2-container", s->ivshmem_size); - if (s->ivshmem_64bit) { + if (!s->not_legacy_32bit) { attr |= PCI_BASE_ADDRESS_MEM_TYPE_64; } if (s->hostmem != NULL) { - MemoryRegion *mr; - IVSHMEM_DPRINTF("using hostmem\n"); - mr = host_memory_backend_get_memory(MEMORY_BACKEND(s->hostmem), errp); - vmstate_register_ram(mr, DEVICE(s)); - memory_region_add_subregion(&s->bar, 0, mr); - pci_register_bar(PCI_DEVICE(s), 2, attr, &s->bar); - } else if (s->server_chr != NULL) { - /* FIXME do not rely on what chr drivers put into filename */ - if (strncmp(s->server_chr->filename, "unix:", 5)) { - error_setg(errp, "chardev is not a unix client socket"); - return; - } - - /* if we get a UNIX socket as the parameter we will talk - * to the ivshmem server to receive the memory region */ - + s->ivshmem_bar2 = host_memory_backend_get_memory(s->hostmem, + &error_abort); + } else { IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n", s->server_chr->filename); - if (ivshmem_setup_interrupts(s) < 0) { - error_setg(errp, "failed to initialize interrupts"); - return; - } - /* we allocate enough space for 16 peers and grow as needed */ resize_peers(s, 16); - s->vm_id = -1; - pci_register_bar(dev, 2, attr, &s->bar); + /* + * Receive setup messages from server synchronously. + * Older versions did it asynchronously, but that creates a + * number of entertaining race conditions. + */ + ivshmem_recv_setup(s, &err); + if (err) { + error_propagate(errp, err); + return; + } - s->eventfd_chr = g_malloc0(s->vectors * sizeof(CharDriverState *)); + if (s->master == ON_OFF_AUTO_ON && s->vm_id != 0) { + error_setg(errp, + "master must connect to the server before any peers"); + return; + } qemu_chr_add_handlers(s->server_chr, ivshmem_can_receive, - ivshmem_check_version, ivshmem_event, s); - } else { - /* just map the file immediately, we're not using a server */ - int fd; - - IVSHMEM_DPRINTF("using shm_open (shm object = %s)\n", s->shmobj); - - /* try opening with O_EXCL and if it succeeds zero the memory - * by truncating to 0 */ - if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR|O_EXCL, - S_IRWXU|S_IRWXG|S_IRWXO)) > 0) { - /* truncate file to length PCI device's memory */ - if (ftruncate(fd, s->ivshmem_size) != 0) { - error_report("could not truncate shared file"); - } + ivshmem_read, NULL, s); - } else if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR, - S_IRWXU|S_IRWXG|S_IRWXO)) < 0) { - error_setg(errp, "could not open shared file"); + if (ivshmem_setup_interrupts(s) < 0) { + error_setg(errp, "failed to initialize interrupts"); return; } + } - if (check_shm_size(s, fd, errp) == -1) { - return; - } + vmstate_register_ram(s->ivshmem_bar2, DEVICE(s)); + pci_register_bar(PCI_DEVICE(s), 2, attr, s->ivshmem_bar2); - create_shared_memory_BAR(s, fd, attr, errp); + if (s->master == ON_OFF_AUTO_AUTO) { + s->master = s->vm_id == 0 ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; + } + + if (!ivshmem_is_master(s)) { + error_setg(&s->migration_blocker, + "Migration is disabled when using feature 'peer mode' in device 'ivshmem'"); + migrate_add_blocker(s->migration_blocker); } } -static void pci_ivshmem_exit(PCIDevice *dev) +static void ivshmem_exit(PCIDevice *dev) { - IVShmemState *s = IVSHMEM(dev); + IVShmemState *s = IVSHMEM_COMMON(dev); int i; - fifo8_destroy(&s->incoming_fifo); - if (s->migration_blocker) { migrate_del_blocker(s->migration_blocker); error_free(s->migration_blocker); } - if (memory_region_is_mapped(&s->ivshmem)) { + if (memory_region_is_mapped(s->ivshmem_bar2)) { if (!s->hostmem) { - void *addr = memory_region_get_ram_ptr(&s->ivshmem); + void *addr = memory_region_get_ram_ptr(s->ivshmem_bar2); int fd; - if (munmap(addr, s->ivshmem_size) == -1) { + if (munmap(addr, memory_region_size(s->ivshmem_bar2) == -1)) { error_report("Failed to munmap shared memory %s", strerror(errno)); } - fd = qemu_get_ram_fd(memory_region_get_ram_addr(&s->ivshmem)); - if (fd != -1) { - close(fd); - } + fd = qemu_get_ram_fd(memory_region_get_ram_addr(s->ivshmem_bar2)); + close(fd); } - vmstate_unregister_ram(&s->ivshmem, DEVICE(dev)); - memory_region_del_subregion(&s->bar, &s->ivshmem); - } - - if (s->eventfd_chr) { - for (i = 0; i < s->vectors; i++) { - if (s->eventfd_chr[i]) { - qemu_chr_free(s->eventfd_chr[i]); - } - } - g_free(s->eventfd_chr); + vmstate_unregister_ram(s->ivshmem_bar2, DEVICE(dev)); } if (s->peers) { @@ -1032,23 +959,11 @@ static void pci_ivshmem_exit(PCIDevice *dev) g_free(s->msi_vectors); } -static bool test_msix(void *opaque, int version_id) -{ - IVShmemState *s = opaque; - - return ivshmem_has_feature(s, IVSHMEM_MSI); -} - -static bool test_no_msix(void *opaque, int version_id) -{ - return !test_msix(opaque, version_id); -} - static int ivshmem_pre_load(void *opaque) { IVShmemState *s = opaque; - if (s->role_val == IVSHMEM_PEER) { + if (!ivshmem_is_master(s)) { error_report("'peer' devices are not migratable"); return -EINVAL; } @@ -1061,12 +976,145 @@ static int ivshmem_post_load(void *opaque, int version_id) IVShmemState *s = opaque; if (ivshmem_has_feature(s, IVSHMEM_MSI)) { - ivshmem_use_msix(s); + ivshmem_msix_vector_use(s); } - return 0; } +static void ivshmem_common_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->realize = ivshmem_common_realize; + k->exit = ivshmem_exit; + k->config_write = ivshmem_write_config; + k->vendor_id = PCI_VENDOR_ID_IVSHMEM; + k->device_id = PCI_DEVICE_ID_IVSHMEM; + k->class_id = PCI_CLASS_MEMORY_RAM; + k->revision = 1; + dc->reset = ivshmem_reset; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + dc->desc = "Inter-VM shared memory"; +} + +static const TypeInfo ivshmem_common_info = { + .name = TYPE_IVSHMEM_COMMON, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(IVShmemState), + .abstract = true, + .class_init = ivshmem_common_class_init, +}; + +static void ivshmem_check_memdev_is_busy(Object *obj, const char *name, + Object *val, Error **errp) +{ + MemoryRegion *mr; + + mr = host_memory_backend_get_memory(MEMORY_BACKEND(val), &error_abort); + if (memory_region_is_mapped(mr)) { + char *path = object_get_canonical_path_component(val); + error_setg(errp, "can't use already busy memdev: %s", path); + g_free(path); + } else { + qdev_prop_allow_set_link_before_realize(obj, name, val, errp); + } +} + +static const VMStateDescription ivshmem_plain_vmsd = { + .name = TYPE_IVSHMEM_PLAIN, + .version_id = 0, + .minimum_version_id = 0, + .pre_load = ivshmem_pre_load, + .post_load = ivshmem_post_load, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(parent_obj, IVShmemState), + VMSTATE_UINT32(intrstatus, IVShmemState), + VMSTATE_UINT32(intrmask, IVShmemState), + VMSTATE_END_OF_LIST() + }, +}; + +static Property ivshmem_plain_properties[] = { + DEFINE_PROP_ON_OFF_AUTO("master", IVShmemState, master, ON_OFF_AUTO_OFF), + DEFINE_PROP_END_OF_LIST(), +}; + +static void ivshmem_plain_init(Object *obj) +{ + IVShmemState *s = IVSHMEM_PLAIN(obj); + + object_property_add_link(obj, "memdev", TYPE_MEMORY_BACKEND, + (Object **)&s->hostmem, + ivshmem_check_memdev_is_busy, + OBJ_PROP_LINK_UNREF_ON_RELEASE, + &error_abort); +} + +static void ivshmem_plain_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->props = ivshmem_plain_properties; + dc->vmsd = &ivshmem_plain_vmsd; +} + +static const TypeInfo ivshmem_plain_info = { + .name = TYPE_IVSHMEM_PLAIN, + .parent = TYPE_IVSHMEM_COMMON, + .instance_size = sizeof(IVShmemState), + .instance_init = ivshmem_plain_init, + .class_init = ivshmem_plain_class_init, +}; + +static const VMStateDescription ivshmem_doorbell_vmsd = { + .name = TYPE_IVSHMEM_DOORBELL, + .version_id = 0, + .minimum_version_id = 0, + .pre_load = ivshmem_pre_load, + .post_load = ivshmem_post_load, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(parent_obj, IVShmemState), + VMSTATE_MSIX(parent_obj, IVShmemState), + VMSTATE_UINT32(intrstatus, IVShmemState), + VMSTATE_UINT32(intrmask, IVShmemState), + VMSTATE_END_OF_LIST() + }, +}; + +static Property ivshmem_doorbell_properties[] = { + DEFINE_PROP_CHR("chardev", IVShmemState, server_chr), + DEFINE_PROP_UINT32("vectors", IVShmemState, vectors, 1), + DEFINE_PROP_BIT("ioeventfd", IVShmemState, features, IVSHMEM_IOEVENTFD, + true), + DEFINE_PROP_ON_OFF_AUTO("master", IVShmemState, master, ON_OFF_AUTO_OFF), + DEFINE_PROP_END_OF_LIST(), +}; + +static void ivshmem_doorbell_init(Object *obj) +{ + IVShmemState *s = IVSHMEM_DOORBELL(obj); + + s->features |= (1 << IVSHMEM_MSI); + s->legacy_size = SIZE_MAX; /* whatever the server sends */ +} + +static void ivshmem_doorbell_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->props = ivshmem_doorbell_properties; + dc->vmsd = &ivshmem_doorbell_vmsd; +} + +static const TypeInfo ivshmem_doorbell_info = { + .name = TYPE_IVSHMEM_DOORBELL, + .parent = TYPE_IVSHMEM_COMMON, + .instance_size = sizeof(IVShmemState), + .instance_init = ivshmem_doorbell_init, + .class_init = ivshmem_doorbell_class_init, +}; + static int ivshmem_load_old(QEMUFile *f, void *opaque, int version_id) { IVShmemState *s = opaque; @@ -1079,9 +1127,9 @@ static int ivshmem_load_old(QEMUFile *f, void *opaque, int version_id) return -EINVAL; } - if (s->role_val == IVSHMEM_PEER) { - error_report("'peer' devices are not migratable"); - return -EINVAL; + ret = ivshmem_pre_load(s); + if (ret) { + return ret; } ret = pci_device_load(pdev, f); @@ -1091,7 +1139,7 @@ static int ivshmem_load_old(QEMUFile *f, void *opaque, int version_id) if (ivshmem_has_feature(s, IVSHMEM_MSI)) { msix_load(pdev, f); - ivshmem_use_msix(s); + ivshmem_msix_vector_use(s); } else { s->intrstatus = qemu_get_be32(f); s->intrmask = qemu_get_be32(f); @@ -1100,6 +1148,18 @@ static int ivshmem_load_old(QEMUFile *f, void *opaque, int version_id) return 0; } +static bool test_msix(void *opaque, int version_id) +{ + IVShmemState *s = opaque; + + return ivshmem_has_feature(s, IVSHMEM_MSI); +} + +static bool test_no_msix(void *opaque, int version_id) +{ + return !test_msix(opaque, version_id); +} + static const VMStateDescription ivshmem_vmsd = { .name = "ivshmem", .version_id = 1, @@ -1123,68 +1183,110 @@ static Property ivshmem_properties[] = { DEFINE_PROP_CHR("chardev", IVShmemState, server_chr), DEFINE_PROP_STRING("size", IVShmemState, sizearg), DEFINE_PROP_UINT32("vectors", IVShmemState, vectors, 1), - DEFINE_PROP_BIT("ioeventfd", IVShmemState, features, IVSHMEM_IOEVENTFD, false), + DEFINE_PROP_BIT("ioeventfd", IVShmemState, features, IVSHMEM_IOEVENTFD, + false), DEFINE_PROP_BIT("msi", IVShmemState, features, IVSHMEM_MSI, true), DEFINE_PROP_STRING("shm", IVShmemState, shmobj), DEFINE_PROP_STRING("role", IVShmemState, role), - DEFINE_PROP_UINT32("use64", IVShmemState, ivshmem_64bit, 1), + DEFINE_PROP_UINT32("use64", IVShmemState, not_legacy_32bit, 1), DEFINE_PROP_END_OF_LIST(), }; -static void ivshmem_class_init(ObjectClass *klass, void *data) +static void desugar_shm(IVShmemState *s) { - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = pci_ivshmem_realize; - k->exit = pci_ivshmem_exit; - k->config_write = ivshmem_write_config; - k->vendor_id = PCI_VENDOR_ID_IVSHMEM; - k->device_id = PCI_DEVICE_ID_IVSHMEM; - k->class_id = PCI_CLASS_MEMORY_RAM; - dc->reset = ivshmem_reset; - dc->props = ivshmem_properties; - dc->vmsd = &ivshmem_vmsd; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); - dc->desc = "Inter-VM shared memory"; + Object *obj; + char *path; + + obj = object_new("memory-backend-file"); + path = g_strdup_printf("/dev/shm/%s", s->shmobj); + object_property_set_str(obj, path, "mem-path", &error_abort); + g_free(path); + object_property_set_int(obj, s->legacy_size, "size", &error_abort); + object_property_set_bool(obj, true, "share", &error_abort); + object_property_add_child(OBJECT(s), "internal-shm-backend", obj, + &error_abort); + user_creatable_complete(obj, &error_abort); + s->hostmem = MEMORY_BACKEND(obj); } -static void ivshmem_check_memdev_is_busy(Object *obj, const char *name, - Object *val, Error **errp) +static void ivshmem_realize(PCIDevice *dev, Error **errp) { - MemoryRegion *mr; + IVShmemState *s = IVSHMEM_COMMON(dev); - mr = host_memory_backend_get_memory(MEMORY_BACKEND(val), errp); - if (memory_region_is_mapped(mr)) { - char *path = object_get_canonical_path_component(val); - error_setg(errp, "can't use already busy memdev: %s", path); - g_free(path); + if (!qtest_enabled()) { + error_report("ivshmem is deprecated, please use ivshmem-plain" + " or ivshmem-doorbell instead"); + } + + if (!!s->server_chr + !!s->shmobj != 1) { + error_setg(errp, "You must specify either 'shm' or 'chardev'"); + return; + } + + if (s->sizearg == NULL) { + s->legacy_size = 4 << 20; /* 4 MB default */ } else { - qdev_prop_allow_set_link_before_realize(obj, name, val, errp); + char *end; + int64_t size = qemu_strtosz(s->sizearg, &end); + if (size < 0 || (size_t)size != size || *end != '\0' + || !is_power_of_2(size)) { + error_setg(errp, "Invalid size %s", s->sizearg); + return; + } + s->legacy_size = size; } + + /* check that role is reasonable */ + if (s->role) { + if (strncmp(s->role, "peer", 5) == 0) { + s->master = ON_OFF_AUTO_OFF; + } else if (strncmp(s->role, "master", 7) == 0) { + s->master = ON_OFF_AUTO_ON; + } else { + error_setg(errp, "'role' must be 'peer' or 'master'"); + return; + } + } else { + s->master = ON_OFF_AUTO_AUTO; + } + + if (s->shmobj) { + desugar_shm(s); + } + + /* + * Note: we don't use INTx with IVSHMEM_MSI at all, so this is a + * bald-faced lie then. But it's a backwards compatible lie. + */ + pci_config_set_interrupt_pin(dev->config, 1); + + ivshmem_common_realize(dev, errp); } -static void ivshmem_init(Object *obj) +static void ivshmem_class_init(ObjectClass *klass, void *data) { - IVShmemState *s = IVSHMEM(obj); + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - object_property_add_link(obj, "x-memdev", TYPE_MEMORY_BACKEND, - (Object **)&s->hostmem, - ivshmem_check_memdev_is_busy, - OBJ_PROP_LINK_UNREF_ON_RELEASE, - &error_abort); + k->realize = ivshmem_realize; + k->revision = 0; + dc->desc = "Inter-VM shared memory (legacy)"; + dc->props = ivshmem_properties; + dc->vmsd = &ivshmem_vmsd; } static const TypeInfo ivshmem_info = { .name = TYPE_IVSHMEM, - .parent = TYPE_PCI_DEVICE, + .parent = TYPE_IVSHMEM_COMMON, .instance_size = sizeof(IVShmemState), - .instance_init = ivshmem_init, .class_init = ivshmem_class_init, }; static void ivshmem_register_types(void) { + type_register_static(&ivshmem_common_info); + type_register_static(&ivshmem_plain_info); + type_register_static(&ivshmem_doorbell_info); type_register_static(&ivshmem_info); } diff --git a/hw/net/spapr_llan.c b/hw/net/spapr_llan.c index 6aa06ccc50..a647f25d96 100644 --- a/hw/net/spapr_llan.c +++ b/hw/net/spapr_llan.c @@ -47,6 +47,10 @@ #define DPRINTF(fmt...) #endif +/* Compatibility flags for migration */ +#define SPAPRVLAN_FLAG_RX_BUF_POOLS_BIT 0 +#define SPAPRVLAN_FLAG_RX_BUF_POOLS (1 << SPAPRVLAN_FLAG_RX_BUF_POOLS_BIT) + /* * Virtual LAN device */ @@ -88,6 +92,15 @@ typedef uint64_t vlan_bd_t; #define VIO_SPAPR_VLAN_DEVICE(obj) \ OBJECT_CHECK(VIOsPAPRVLANDevice, (obj), TYPE_VIO_SPAPR_VLAN_DEVICE) +#define RX_POOL_MAX_BDS 4096 +#define RX_MAX_POOLS 5 + +typedef struct { + int32_t bufsize; + int32_t count; + vlan_bd_t bds[RX_POOL_MAX_BDS]; +} RxBufPool; + typedef struct VIOsPAPRVLANDevice { VIOsPAPRDevice sdev; NICConf nicconf; @@ -96,6 +109,8 @@ typedef struct VIOsPAPRVLANDevice { target_ulong buf_list; uint32_t add_buf_ptr, use_buf_ptr, rx_bufs; target_ulong rxq_ptr; + uint32_t compat_flags; /* Compatability flags for migration */ + RxBufPool *rx_pool[RX_MAX_POOLS]; /* Receive buffer descriptor pools */ } VIOsPAPRVLANDevice; static int spapr_vlan_can_receive(NetClientState *nc) @@ -105,6 +120,73 @@ static int spapr_vlan_can_receive(NetClientState *nc) return (dev->isopen && dev->rx_bufs > 0); } +/** + * Get buffer descriptor from one of our receive buffer pools + */ +static vlan_bd_t spapr_vlan_get_rx_bd_from_pool(VIOsPAPRVLANDevice *dev, + size_t size) +{ + vlan_bd_t bd; + int pool; + + for (pool = 0; pool < RX_MAX_POOLS; pool++) { + if (dev->rx_pool[pool]->count > 0 && + dev->rx_pool[pool]->bufsize >= size + 8) { + break; + } + } + if (pool == RX_MAX_POOLS) { + /* Failed to find a suitable buffer */ + return 0; + } + + DPRINTF("Found buffer: pool=%d count=%d rxbufs=%d\n", pool, + dev->rx_pool[pool]->count, dev->rx_bufs); + + /* Remove the buffer from the pool */ + dev->rx_pool[pool]->count--; + bd = dev->rx_pool[pool]->bds[dev->rx_pool[pool]->count]; + dev->rx_pool[pool]->bds[dev->rx_pool[pool]->count] = 0; + + return bd; +} + +/** + * Get buffer descriptor from the receive buffer list page that has been + * supplied by the guest with the H_REGISTER_LOGICAL_LAN call + */ +static vlan_bd_t spapr_vlan_get_rx_bd_from_page(VIOsPAPRVLANDevice *dev, + size_t size) +{ + int buf_ptr = dev->use_buf_ptr; + vlan_bd_t bd; + + do { + buf_ptr += 8; + if (buf_ptr >= VLAN_RX_BDS_LEN + VLAN_RX_BDS_OFF) { + buf_ptr = VLAN_RX_BDS_OFF; + } + + bd = vio_ldq(&dev->sdev, dev->buf_list + buf_ptr); + DPRINTF("use_buf_ptr=%d bd=0x%016llx\n", + buf_ptr, (unsigned long long)bd); + } while ((!(bd & VLAN_BD_VALID) || VLAN_BD_LEN(bd) < size + 8) + && buf_ptr != dev->use_buf_ptr); + + if (!(bd & VLAN_BD_VALID) || VLAN_BD_LEN(bd) < size + 8) { + /* Failed to find a suitable buffer */ + return 0; + } + + /* Remove the buffer from the pool */ + dev->use_buf_ptr = buf_ptr; + vio_stq(&dev->sdev, dev->buf_list + dev->use_buf_ptr, 0); + + DPRINTF("Found buffer: ptr=%d rxbufs=%d\n", dev->use_buf_ptr, dev->rx_bufs); + + return bd; +} + static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf, size_t size) { @@ -112,7 +194,6 @@ static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf, VIOsPAPRDevice *sdev = VIO_SPAPR_DEVICE(dev); vlan_bd_t rxq_bd = vio_ldq(sdev, dev->buf_list + VLAN_RXQ_BD_OFF); vlan_bd_t bd; - int buf_ptr = dev->use_buf_ptr; uint64_t handle; uint8_t control; @@ -127,29 +208,16 @@ static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf, return -1; } - do { - buf_ptr += 8; - if (buf_ptr >= (VLAN_RX_BDS_LEN + VLAN_RX_BDS_OFF)) { - buf_ptr = VLAN_RX_BDS_OFF; - } - - bd = vio_ldq(sdev, dev->buf_list + buf_ptr); - DPRINTF("use_buf_ptr=%d bd=0x%016llx\n", - buf_ptr, (unsigned long long)bd); - } while ((!(bd & VLAN_BD_VALID) || (VLAN_BD_LEN(bd) < (size + 8))) - && (buf_ptr != dev->use_buf_ptr)); - - if (!(bd & VLAN_BD_VALID) || (VLAN_BD_LEN(bd) < (size + 8))) { - /* Failed to find a suitable buffer */ + if (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) { + bd = spapr_vlan_get_rx_bd_from_pool(dev, size); + } else { + bd = spapr_vlan_get_rx_bd_from_page(dev, size); + } + if (!bd) { return -1; } - /* Remove the buffer from the pool */ dev->rx_bufs--; - dev->use_buf_ptr = buf_ptr; - vio_stq(sdev, dev->buf_list + dev->use_buf_ptr, 0); - - DPRINTF("Found buffer: ptr=%d num=%d\n", dev->use_buf_ptr, dev->rx_bufs); /* Transfer the packet data */ if (spapr_vio_dma_write(sdev, VLAN_BD_ADDR(bd) + 8, buf, size) < 0) { @@ -197,13 +265,31 @@ static NetClientInfo net_spapr_vlan_info = { .receive = spapr_vlan_receive, }; +static void spapr_vlan_reset_rx_pool(RxBufPool *rxp) +{ + /* + * Use INT_MAX as bufsize so that unused buffers are moved to the end + * of the list during the qsort in spapr_vlan_add_rxbuf_to_pool() later. + */ + rxp->bufsize = INT_MAX; + rxp->count = 0; + memset(rxp->bds, 0, sizeof(rxp->bds)); +} + static void spapr_vlan_reset(VIOsPAPRDevice *sdev) { VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(sdev); + int i; dev->buf_list = 0; dev->rx_bufs = 0; dev->isopen = 0; + + if (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) { + for (i = 0; i < RX_MAX_POOLS; i++) { + spapr_vlan_reset_rx_pool(dev->rx_pool[i]); + } + } } static void spapr_vlan_realize(VIOsPAPRDevice *sdev, Error **errp) @@ -220,10 +306,31 @@ static void spapr_vlan_realize(VIOsPAPRDevice *sdev, Error **errp) static void spapr_vlan_instance_init(Object *obj) { VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(obj); + int i; device_add_bootindex_property(obj, &dev->nicconf.bootindex, "bootindex", "", DEVICE(dev), NULL); + + if (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) { + for (i = 0; i < RX_MAX_POOLS; i++) { + dev->rx_pool[i] = g_new(RxBufPool, 1); + spapr_vlan_reset_rx_pool(dev->rx_pool[i]); + } + } +} + +static void spapr_vlan_instance_finalize(Object *obj) +{ + VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(obj); + int i; + + if (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) { + for (i = 0; i < RX_MAX_POOLS; i++) { + g_free(dev->rx_pool[i]); + dev->rx_pool[i] = NULL; + } + } } void spapr_vlan_create(VIOsPAPRBus *bus, NICInfo *nd) @@ -374,6 +481,113 @@ static target_ulong h_free_logical_lan(PowerPCCPU *cpu, return H_SUCCESS; } +/** + * Used for qsort, this function compares two RxBufPools by size. + */ +static int rx_pool_size_compare(const void *p1, const void *p2) +{ + const RxBufPool *pool1 = *(RxBufPool **)p1; + const RxBufPool *pool2 = *(RxBufPool **)p2; + + if (pool1->bufsize < pool2->bufsize) { + return -1; + } + return pool1->bufsize > pool2->bufsize; +} + +/** + * Search for a matching buffer pool with exact matching size, + * or return -1 if no matching pool has been found. + */ +static int spapr_vlan_get_rx_pool_id(VIOsPAPRVLANDevice *dev, int size) +{ + int pool; + + for (pool = 0; pool < RX_MAX_POOLS; pool++) { + if (dev->rx_pool[pool]->bufsize == size) { + return pool; + } + } + + return -1; +} + +/** + * Enqueuing receive buffer by adding it to one of our receive buffer pools + */ +static target_long spapr_vlan_add_rxbuf_to_pool(VIOsPAPRVLANDevice *dev, + target_ulong buf) +{ + int size = VLAN_BD_LEN(buf); + int pool; + + pool = spapr_vlan_get_rx_pool_id(dev, size); + if (pool < 0) { + /* + * No matching pool found? Try to use a new one. If the guest used all + * pools before, but changed the size of one pool inbetween, we might + * need to recycle that pool here (if it's empty already). Thus scan + * all buffer pools now, starting with the last (likely empty) one. + */ + for (pool = RX_MAX_POOLS - 1; pool >= 0 ; pool--) { + if (dev->rx_pool[pool]->count == 0) { + dev->rx_pool[pool]->bufsize = size; + /* + * Sort pools by size so that spapr_vlan_receive() + * can later find the smallest buffer pool easily. + */ + qsort(dev->rx_pool, RX_MAX_POOLS, sizeof(dev->rx_pool[0]), + rx_pool_size_compare); + pool = spapr_vlan_get_rx_pool_id(dev, size); + DPRINTF("created RX pool %d for size %lld\n", pool, + VLAN_BD_LEN(buf)); + break; + } + } + } + /* Still no usable pool? Give up */ + if (pool < 0 || dev->rx_pool[pool]->count >= RX_POOL_MAX_BDS) { + return H_RESOURCE; + } + + DPRINTF("h_add_llan_buf(): Add buf using pool %i (size %lli, count=%i)\n", + pool, VLAN_BD_LEN(buf), dev->rx_pool[pool]->count); + + dev->rx_pool[pool]->bds[dev->rx_pool[pool]->count++] = buf; + + return 0; +} + +/** + * This is the old way of enqueuing receive buffers: Add it to the rx queue + * page that has been supplied by the guest (which is quite limited in size). + */ +static target_long spapr_vlan_add_rxbuf_to_page(VIOsPAPRVLANDevice *dev, + target_ulong buf) +{ + vlan_bd_t bd; + + if (dev->rx_bufs >= VLAN_MAX_BUFS) { + return H_RESOURCE; + } + + do { + dev->add_buf_ptr += 8; + if (dev->add_buf_ptr >= VLAN_RX_BDS_LEN + VLAN_RX_BDS_OFF) { + dev->add_buf_ptr = VLAN_RX_BDS_OFF; + } + + bd = vio_ldq(&dev->sdev, dev->buf_list + dev->add_buf_ptr); + } while (bd & VLAN_BD_VALID); + + vio_stq(&dev->sdev, dev->buf_list + dev->add_buf_ptr, buf); + + DPRINTF("h_add_llan_buf(): Added buf ptr=%d rx_bufs=%d bd=0x%016llx\n", + dev->add_buf_ptr, dev->rx_bufs, (unsigned long long)buf); + + return 0; +} + static target_ulong h_add_logical_lan_buffer(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, @@ -383,7 +597,7 @@ static target_ulong h_add_logical_lan_buffer(PowerPCCPU *cpu, target_ulong buf = args[1]; VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(sdev); - vlan_bd_t bd; + target_long ret; DPRINTF("H_ADD_LOGICAL_LAN_BUFFER(0x" TARGET_FMT_lx ", 0x" TARGET_FMT_lx ")\n", reg, buf); @@ -399,29 +613,23 @@ static target_ulong h_add_logical_lan_buffer(PowerPCCPU *cpu, return H_PARAMETER; } - if (!dev->isopen || dev->rx_bufs >= VLAN_MAX_BUFS) { + if (!dev->isopen) { return H_RESOURCE; } - do { - dev->add_buf_ptr += 8; - if (dev->add_buf_ptr >= (VLAN_RX_BDS_LEN + VLAN_RX_BDS_OFF)) { - dev->add_buf_ptr = VLAN_RX_BDS_OFF; - } - - bd = vio_ldq(sdev, dev->buf_list + dev->add_buf_ptr); - } while (bd & VLAN_BD_VALID); - - vio_stq(sdev, dev->buf_list + dev->add_buf_ptr, buf); + if (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) { + ret = spapr_vlan_add_rxbuf_to_pool(dev, buf); + } else { + ret = spapr_vlan_add_rxbuf_to_page(dev, buf); + } + if (ret) { + return ret; + } dev->rx_bufs++; qemu_flush_queued_packets(qemu_get_queue(dev->nic)); - DPRINTF("h_add_logical_lan_buffer(): Added buf ptr=%d rx_bufs=%d" - " bd=0x%016llx\n", dev->add_buf_ptr, dev->rx_bufs, - (unsigned long long)buf); - return H_SUCCESS; } @@ -511,9 +719,44 @@ static target_ulong h_multicast_ctrl(PowerPCCPU *cpu, sPAPRMachineState *spapr, static Property spapr_vlan_properties[] = { DEFINE_SPAPR_PROPERTIES(VIOsPAPRVLANDevice, sdev), DEFINE_NIC_PROPERTIES(VIOsPAPRVLANDevice, nicconf), + DEFINE_PROP_BIT("use-rx-buffer-pools", VIOsPAPRVLANDevice, + compat_flags, SPAPRVLAN_FLAG_RX_BUF_POOLS_BIT, true), DEFINE_PROP_END_OF_LIST(), }; +static bool spapr_vlan_rx_buffer_pools_needed(void *opaque) +{ + VIOsPAPRVLANDevice *dev = opaque; + + return (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) != 0; +} + +static const VMStateDescription vmstate_rx_buffer_pool = { + .name = "spapr_llan/rx_buffer_pool", + .version_id = 1, + .minimum_version_id = 1, + .needed = spapr_vlan_rx_buffer_pools_needed, + .fields = (VMStateField[]) { + VMSTATE_INT32(bufsize, RxBufPool), + VMSTATE_INT32(count, RxBufPool), + VMSTATE_UINT64_ARRAY(bds, RxBufPool, RX_POOL_MAX_BDS), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_rx_pools = { + .name = "spapr_llan/rx_pools", + .version_id = 1, + .minimum_version_id = 1, + .needed = spapr_vlan_rx_buffer_pools_needed, + .fields = (VMStateField[]) { + VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(rx_pool, VIOsPAPRVLANDevice, + RX_MAX_POOLS, 1, + vmstate_rx_buffer_pool, RxBufPool), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_spapr_llan = { .name = "spapr_llan", .version_id = 1, @@ -530,6 +773,10 @@ static const VMStateDescription vmstate_spapr_llan = { VMSTATE_END_OF_LIST() }, + .subsections = (const VMStateDescription * []) { + &vmstate_rx_pools, + NULL + } }; static void spapr_vlan_class_init(ObjectClass *klass, void *data) @@ -556,6 +803,7 @@ static const TypeInfo spapr_vlan_info = { .instance_size = sizeof(VIOsPAPRVLANDevice), .class_init = spapr_vlan_class_init, .instance_init = spapr_vlan_instance_init, + .instance_finalize = spapr_vlan_instance_finalize, }; static void spapr_vlan_register_types(void) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 2c380c26c5..e7be21e678 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -498,10 +498,11 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base, * Older KVM versions with older guest kernels were broken with the * magic page, don't allow the guest to map it. */ - kvmppc_get_hypercall(first_cpu->env_ptr, hypercall, - sizeof(hypercall)); - _FDT((fdt_property(fdt, "hcall-instructions", hypercall, - sizeof(hypercall)))); + if (!kvmppc_get_hypercall(first_cpu->env_ptr, hypercall, + sizeof(hypercall))) { + _FDT((fdt_property(fdt, "hcall-instructions", hypercall, + sizeof(hypercall)))); + } } _FDT((fdt_end_node(fdt))); } @@ -1613,15 +1614,8 @@ static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, /* Set time-base frequency to 512 MHz */ cpu_ppc_tb_init(env, TIMEBASE_FREQ); - /* PAPR always has exception vectors in RAM not ROM. To ensure this, - * MSR[IP] should never be set. - */ - env->msr_mask &= ~(1 << 6); - - /* Tell KVM that we're in PAPR mode */ - if (kvm_enabled()) { - kvmppc_set_papr(cpu); - } + /* Enable PAPR mode in TCG or KVM */ + cpu_ppc_set_papr(cpu); if (cpu->max_compat) { Error *local_err = NULL; @@ -2363,7 +2357,12 @@ DEFINE_SPAPR_MACHINE(2_6, "2.6", true); * pseries-2.5 */ #define SPAPR_COMPAT_2_5 \ - HW_COMPAT_2_5 + HW_COMPAT_2_5 \ + { \ + .driver = "spapr-vlan", \ + .property = "use-rx-buffer-pools", \ + .value = "off", \ + }, static void spapr_machine_2_5_instance_options(MachineState *machine) { diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index 3e0bdc1bae..bda84a64bd 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -15,7 +15,7 @@ #include <dirent.h> #include <sys/statvfs.h> -#ifdef __linux__ +#ifdef CONFIG_INOTIFY1 #include <sys/inotify.h> #include "qapi/error.h" #include "qemu/main-loop.h" @@ -94,7 +94,7 @@ enum { EP_EVENT, }; -#ifdef __linux__ +#ifdef CONFIG_INOTIFY1 typedef struct MTPMonEntry MTPMonEntry; struct MTPMonEntry { @@ -129,7 +129,7 @@ struct MTPObject { char *name; char *path; struct stat stat; -#ifdef __linux__ +#ifdef CONFIG_INOTIFY1 /* inotify watch cookie */ int watchfd; #endif @@ -154,7 +154,7 @@ struct MTPState { uint32_t next_handle; QTAILQ_HEAD(, MTPObject) objects; -#ifdef __linux__ +#ifdef CONFIG_INOTIFY1 /* inotify descriptor */ int inotifyfd; QTAILQ_HEAD(events, MTPMonEntry) events; @@ -402,7 +402,7 @@ static MTPObject *usb_mtp_add_child(MTPState *s, MTPObject *o, return child; } -#ifdef __linux__ +#ifdef CONFIG_INOTIFY1 static MTPObject *usb_mtp_object_lookup_name(MTPObject *parent, char *name, int len) { @@ -435,12 +435,11 @@ static void inotify_watchfn(void *arg) MTPState *s = arg; ssize_t bytes; /* From the man page: atleast one event can be read */ - int len = sizeof(struct inotify_event) + NAME_MAX + 1; int pos; - char buf[len]; + char buf[sizeof(struct inotify_event) + NAME_MAX + 1]; for (;;) { - bytes = read(s->inotifyfd, buf, len); + bytes = read(s->inotifyfd, buf, sizeof(buf)); pos = 0; if (bytes <= 0) { @@ -595,7 +594,7 @@ static void usb_mtp_object_readdir(MTPState *s, MTPObject *o) if (!dir) { return; } -#ifdef __linux__ +#ifdef CONFIG_INOTIFY1 int watchfd = usb_mtp_add_watch(s->inotifyfd, o->path); if (watchfd == -1) { fprintf(stderr, "usb-mtp: failed to add watch for %s\n", o->path); @@ -720,7 +719,7 @@ static void usb_mtp_add_wstr(MTPData *data, const wchar_t *str) static void usb_mtp_add_str(MTPData *data, const char *str) { uint32_t len = strlen(str)+1; - wchar_t wstr[len]; + wchar_t *wstr = g_new(wchar_t, len); size_t ret; ret = mbstowcs(wstr, str, len); @@ -729,6 +728,8 @@ static void usb_mtp_add_str(MTPData *data, const char *str) } else { usb_mtp_add_wstr(data, wstr); } + + g_free(wstr); } static void usb_mtp_add_time(MTPData *data, time_t time) @@ -997,7 +998,7 @@ static void usb_mtp_command(MTPState *s, MTPControl *c) trace_usb_mtp_op_open_session(s->dev.addr); s->session = c->argv[0]; usb_mtp_object_alloc(s, s->next_handle++, NULL, s->root); -#ifdef __linux__ +#ifdef CONFIG_INOTIFY1 if (usb_mtp_inotify_init(s)) { fprintf(stderr, "usb-mtp: file monitoring init failed\n"); } @@ -1007,7 +1008,7 @@ static void usb_mtp_command(MTPState *s, MTPControl *c) trace_usb_mtp_op_close_session(s->dev.addr); s->session = 0; s->next_handle = 0; -#ifdef __linux__ +#ifdef CONFIG_INOTIFY1 usb_mtp_inotify_cleanup(s); #endif usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects)); @@ -1135,7 +1136,7 @@ static void usb_mtp_handle_reset(USBDevice *dev) trace_usb_mtp_reset(s->dev.addr); -#ifdef __linux__ +#ifdef CONFIG_INOTIFY1 usb_mtp_inotify_cleanup(s); #endif usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects)); @@ -1298,7 +1299,7 @@ static void usb_mtp_handle_data(USBDevice *dev, USBPacket *p) } break; case EP_EVENT: -#ifdef __linux__ +#ifdef CONFIG_INOTIFY1 if (!QTAILQ_EMPTY(&s->events)) { struct MTPMonEntry *e = QTAILQ_LAST(&s->events, events); uint32_t handle; diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 4f3af8f43c..159f58d5a0 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -896,6 +896,11 @@ static uint64_t ehci_caps_read(void *ptr, hwaddr addr, return s->caps[addr]; } +static void ehci_caps_write(void *ptr, hwaddr addr, + uint64_t val, unsigned size) +{ +} + static uint64_t ehci_opreg_read(void *ptr, hwaddr addr, unsigned size) { @@ -2317,6 +2322,7 @@ static void ehci_frame_timer(void *opaque) static const MemoryRegionOps ehci_mmio_caps_ops = { .read = ehci_caps_read, + .write = ehci_caps_write, .valid.min_access_size = 1, .valid.max_access_size = 4, .impl.min_access_size = 1, diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 44b6f8c03d..bcde8a2f48 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -698,11 +698,13 @@ static inline void xhci_dma_write_u32s(XHCIState *xhci, dma_addr_t addr, uint32_t *buf, size_t len) { int i; - uint32_t tmp[len / sizeof(uint32_t)]; + uint32_t tmp[5]; + uint32_t n = len / sizeof(uint32_t); assert((len % sizeof(uint32_t)) == 0); + assert(n <= ARRAY_SIZE(tmp)); - for (i = 0; i < (len / sizeof(uint32_t)); i++) { + for (i = 0; i < n; i++) { tmp[i] = cpu_to_le32(buf[i]); } pci_dma_write(PCI_DEVICE(xhci), addr, tmp, len); diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index dbcc5a737f..8d8054037f 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -35,12 +35,14 @@ #include "qemu/iov.h" #include "sysemu/char.h" -#include <sys/ioctl.h> #include <usbredirparser.h> #include <usbredirfilter.h> #include "hw/usb.h" +/* ERROR is defined below. Remove any previous definition. */ +#undef ERROR + #define MAX_ENDPOINTS 32 #define NO_INTERFACE_INFO 255 /* Valid interface_count always <= 32 */ #define EP2I(ep_address) (((ep_address & 0x80) >> 3) | (ep_address & 0x0f)) diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index 03a1b91f31..0586cacceb 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -18,6 +18,7 @@ extern PropertyInfo qdev_prop_string; extern PropertyInfo qdev_prop_chr; extern PropertyInfo qdev_prop_ptr; extern PropertyInfo qdev_prop_macaddr; +extern PropertyInfo qdev_prop_on_off_auto; extern PropertyInfo qdev_prop_losttickpolicy; extern PropertyInfo qdev_prop_bios_chs_trans; extern PropertyInfo qdev_prop_fdc_drive_type; @@ -155,6 +156,8 @@ extern PropertyInfo qdev_prop_arraylen; DEFINE_PROP(_n, _s, _f, qdev_prop_drive, BlockBackend *) #define DEFINE_PROP_MACADDR(_n, _s, _f) \ DEFINE_PROP(_n, _s, _f, qdev_prop_macaddr, MACAddr) +#define DEFINE_PROP_ON_OFF_AUTO(_n, _s, _f, _d) \ + DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_on_off_auto, OnOffAuto) #define DEFINE_PROP_LOSTTICKPOLICY(_n, _s, _f, _d) \ DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_losttickpolicy, \ LostTickPolicy) diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index 97a7fa22e8..408783f532 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -78,6 +78,9 @@ extern int daemon(int, int); #include <sys/stat.h> #include <sys/time.h> #include <assert.h> +/* setjmp must be declared before sysemu/os-win32.h + * because it is redefined there. */ +#include <setjmp.h> #include <signal.h> #ifdef __OpenBSD__ diff --git a/include/qom/cpu.h b/include/qom/cpu.h index 0b6fa2507c..b7a10f791a 100644 --- a/include/qom/cpu.h +++ b/include/qom/cpu.h @@ -20,7 +20,6 @@ #ifndef QEMU_CPU_H #define QEMU_CPU_H -#include <setjmp.h> #include "hw/qdev-core.h" #include "disas/bfd.h" #include "exec/hwaddr.h" diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img Binary files differindex bd8f21050f..d3978ba050 100644 --- a/pc-bios/s390-ccw.img +++ b/pc-bios/s390-ccw.img diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile index 11c5dd4799..4208cb4295 100644 --- a/pc-bios/s390-ccw/Makefile +++ b/pc-bios/s390-ccw/Makefile @@ -9,7 +9,7 @@ $(call set-vpath, $(SRC_PATH)/pc-bios/s390-ccw) .PHONY : all clean build-all -OBJECTS = start.o main.o bootmap.o sclp-ascii.o virtio.o +OBJECTS = start.o main.o bootmap.o sclp-ascii.o virtio.o virtio-scsi.o CFLAGS += -fPIE -fno-stack-protector -ffreestanding -march=z900 CFLAGS += -fno-delete-null-pointer-checks -msoft-float LDFLAGS += -Wl,-pie -nostdlib diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c index 492530275d..611102e3ef 100644 --- a/pc-bios/s390-ccw/bootmap.c +++ b/pc-bios/s390-ccw/bootmap.c @@ -72,7 +72,7 @@ static void jump_to_IPL_code(uint64_t address) asm volatile("lghi 1,1\n\t" "diag 1,1,0x308\n\t" : : : "1", "memory"); - virtio_panic("\n! IPL returns !\n"); + panic("\n! IPL returns !\n"); } /*********************************************************************** @@ -84,7 +84,7 @@ static const int max_bprs_entries = sizeof(_bprs) / sizeof(ExtEckdBlockPtr); static inline void verify_boot_info(BootInfo *bip) { - IPL_assert(magic_match(bip->magic, ZIPL_MAGIC), "No zIPL magic"); + IPL_assert(magic_match(bip->magic, ZIPL_MAGIC), "No zIPL sig in BootInfo"); IPL_assert(bip->version == BOOT_INFO_VERSION, "Wrong zIPL version"); IPL_assert(bip->bp_type == BOOT_INFO_BP_TYPE_IPL, "DASD is not for IPL"); IPL_assert(bip->dev_type == BOOT_INFO_DEV_TYPE_ECKD, "DASD is not ECKD"); @@ -315,6 +315,40 @@ static void print_eckd_msg(void) sclp_print(msg); } +static void ipl_eckd(void) +{ + ScsiMbr *mbr = (void *)sec; + LDL_VTOC *vlbl = (void *)sec; + + print_eckd_msg(); + + /* Grab the MBR again */ + memset(sec, FREE_SPACE_FILLER, sizeof(sec)); + read_block(0, mbr, "Cannot read block 0 on DASD"); + + if (magic_match(mbr->magic, IPL1_MAGIC)) { + ipl_eckd_cdl(); /* no return */ + } + + /* LDL/CMS? */ + memset(sec, FREE_SPACE_FILLER, sizeof(sec)); + read_block(2, vlbl, "Cannot read block 2"); + + if (magic_match(vlbl->magic, CMS1_MAGIC)) { + ipl_eckd_ldl(ECKD_CMS); /* no return */ + } + if (magic_match(vlbl->magic, LNX1_MAGIC)) { + ipl_eckd_ldl(ECKD_LDL); /* no return */ + } + + ipl_eckd_ldl(ECKD_LDL_UNLABELED); /* it still may return */ + /* + * Ok, it is not a LDL by any means. + * It still might be a CDL with zero record keys for IPL1 and IPL2 + */ + ipl_eckd_cdl(); +} + /*********************************************************************** * IPL a SCSI disk */ @@ -382,7 +416,7 @@ static void zipl_run(ScsiBlockPtr *pte) read_block(pte->blockno, tmp_sec, "Cannot read header"); header = (ComponentHeader *)tmp_sec; - IPL_assert(magic_match(tmp_sec, ZIPL_MAGIC), "No zIPL magic"); + IPL_assert(magic_match(tmp_sec, ZIPL_MAGIC), "No zIPL magic in header"); IPL_assert(header->type == ZIPL_COMP_HEADER_IPL, "Bad header type"); dputs("start loading images\n"); @@ -412,16 +446,26 @@ static void ipl_scsi(void) const int pte_len = sizeof(ScsiBlockPtr); ScsiBlockPtr *prog_table_entry; - /* The 0-th block (MBR) was already read into sec[] */ + /* Grab the MBR */ + memset(sec, FREE_SPACE_FILLER, sizeof(sec)); + read_block(0, mbr, "Cannot read block 0"); + + if (!magic_match(mbr->magic, ZIPL_MAGIC)) { + return; + } sclp_print("Using SCSI scheme.\n"); + debug_print_int("MBR Version", mbr->version_id); + IPL_check(mbr->version_id == 1, + "Unknown MBR layout version, assuming version 1"); debug_print_int("program table", mbr->blockptr.blockno); + IPL_assert(mbr->blockptr.blockno, "No Program Table"); /* Parse the program table */ read_block(mbr->blockptr.blockno, sec, "Error reading Program Table"); - IPL_assert(magic_match(sec, ZIPL_MAGIC), "No zIPL magic"); + IPL_assert(magic_match(sec, ZIPL_MAGIC), "No zIPL magic in PT"); ns_end = sec + virtio_get_block_size(); for (ns = (sec + pte_len); (ns + pte_len) < ns_end; ns += pte_len) { @@ -613,7 +657,7 @@ static IsoBcSection *find_iso_bc_entry(void) if (!is_iso_bc_valid(e)) { /* The validation entry is mandatory */ - virtio_panic("No valid boot catalog found!\n"); + panic("No valid boot catalog found!\n"); return NULL; } @@ -629,7 +673,7 @@ static IsoBcSection *find_iso_bc_entry(void) } } - virtio_panic("No suitable boot entry found on ISO-9660 media!\n"); + panic("No suitable boot entry found on ISO-9660 media!\n"); return NULL; } @@ -645,57 +689,58 @@ static void ipl_iso_el_torito(void) } /*********************************************************************** - * IPL starts here + * Bus specific IPL sequences */ -void zipl_load(void) +static void zipl_load_vblk(void) { - ScsiMbr *mbr = (void *)sec; - LDL_VTOC *vlbl = (void *)sec; - - /* Grab the MBR */ - memset(sec, FREE_SPACE_FILLER, sizeof(sec)); - read_block(0, mbr, "Cannot read block 0"); - - dputs("checking magic\n"); - - if (magic_match(mbr->magic, ZIPL_MAGIC)) { - ipl_scsi(); /* no return */ - } - - /* Check if we can boot as ISO media */ if (virtio_guessed_disk_nature()) { virtio_assume_iso9660(); } ipl_iso_el_torito(); - /* We have failed to follow the SCSI scheme, so */ if (virtio_guessed_disk_nature()) { sclp_print("Using guessed DASD geometry.\n"); virtio_assume_eckd(); } - print_eckd_msg(); - if (magic_match(mbr->magic, IPL1_MAGIC)) { - ipl_eckd_cdl(); /* no return */ + ipl_eckd(); +} + +static void zipl_load_vscsi(void) +{ + if (virtio_get_block_size() == VIRTIO_ISO_BLOCK_SIZE) { + /* Is it an ISO image in non-CD drive? */ + ipl_iso_el_torito(); } - /* LDL/CMS? */ - memset(sec, FREE_SPACE_FILLER, sizeof(sec)); - read_block(2, vlbl, "Cannot read block 2"); + sclp_print("Using guessed DASD geometry.\n"); + virtio_assume_eckd(); + ipl_eckd(); +} - if (magic_match(vlbl->magic, CMS1_MAGIC)) { - ipl_eckd_ldl(ECKD_CMS); /* no return */ - } - if (magic_match(vlbl->magic, LNX1_MAGIC)) { - ipl_eckd_ldl(ECKD_LDL); /* no return */ +/*********************************************************************** + * IPL starts here + */ + +void zipl_load(void) +{ + if (virtio_get_device()->is_cdrom) { + ipl_iso_el_torito(); + panic("\n! Cannot IPL this ISO image !\n"); } - ipl_eckd_ldl(ECKD_LDL_UNLABELED); /* it still may return */ - /* - * Ok, it is not a LDL by any means. - * It still might be a CDL with zero record keys for IPL1 and IPL2 - */ - ipl_eckd_cdl(); + ipl_scsi(); + + switch (virtio_get_device_type()) { + case VIRTIO_ID_BLOCK: + zipl_load_vblk(); + break; + case VIRTIO_ID_SCSI: + zipl_load_vscsi(); + break; + default: + panic("\n! Unknown IPL device type !\n"); + } - virtio_panic("\n* this can never happen *\n"); + panic("\n* this can never happen *\n"); } diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h index f98765b841..bea168714b 100644 --- a/pc-bios/s390-ccw/bootmap.h +++ b/pc-bios/s390-ccw/bootmap.h @@ -264,15 +264,6 @@ typedef enum { /* utility code below */ -static inline void IPL_assert(bool term, const char *message) -{ - if (!term) { - sclp_print("\n! "); - sclp_print(message); - virtio_panic(" !\n"); /* no return */ - } -} - static const unsigned char ebc2asc[256] = /* 0123456789abcdef0123456789abcdef */ "................................" /* 1F */ diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c index d5fe4cea1d..1c9e0791ab 100644 --- a/pc-bios/s390-ccw/main.c +++ b/pc-bios/s390-ccw/main.c @@ -12,9 +12,8 @@ #include "virtio.h" char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); -char ring_area[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); uint64_t boot_value; -static struct subchannel_id blk_schid = { .one = 1 }; +static SubChannelId blk_schid = { .one = 1 }; /* * Priniciples of Operations (SA22-7832-09) chapter 17 requires that @@ -23,7 +22,7 @@ static struct subchannel_id blk_schid = { .one = 1 }; */ void write_subsystem_identification(void) { - struct subchannel_id *schid = (struct subchannel_id *) 184; + SubChannelId *schid = (SubChannelId *) 184; uint32_t *zeroes = (uint32_t *) 188; *schid = blk_schid; @@ -31,14 +30,14 @@ void write_subsystem_identification(void) } -void virtio_panic(const char *string) +void panic(const char *string) { sclp_print(string); disabled_wait(); while (1) { } } -static bool find_dev(struct schib *schib, int dev_no) +static bool find_dev(Schib *schib, int dev_no) { int i, r; @@ -51,7 +50,7 @@ static bool find_dev(struct schib *schib, int dev_no) if (!schib->pmcw.dnv) { continue; } - if (!virtio_is_blk(blk_schid)) { + if (!virtio_is_supported(blk_schid)) { continue; } if ((dev_no < 0) || (schib->pmcw.dev == dev_no)) { @@ -64,7 +63,7 @@ static bool find_dev(struct schib *schib, int dev_no) static void virtio_setup(uint64_t dev_info) { - struct schib schib; + Schib schib; int ssid; bool found = false; uint16_t dev_no; @@ -92,15 +91,11 @@ static void virtio_setup(uint64_t dev_info) } } - if (!found) { - virtio_panic("No virtio-blk device found!\n"); - } + IPL_assert(found, "No virtio device found"); - virtio_setup_block(blk_schid); + virtio_setup_device(blk_schid); - if (!virtio_ipl_disk_is_valid()) { - virtio_panic("No valid hard disk detected.\n"); - } + IPL_assert(virtio_ipl_disk_is_valid(), "No valid IPL device detected"); } int main(void) @@ -111,6 +106,6 @@ int main(void) zipl_load(); /* no return */ - virtio_panic("Failed to load OS from hard disk\n"); + panic("Failed to load OS from hard disk\n"); return 0; /* make compiler happy */ } diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h index 5484c2a45c..616d96738d 100644 --- a/pc-bios/s390-ccw/s390-ccw.h +++ b/pc-bios/s390-ccw/s390-ccw.h @@ -45,15 +45,22 @@ typedef unsigned long long __u64; #include "cio.h" +typedef struct irb Irb; +typedef struct ccw1 Ccw1; +typedef struct cmd_orb CmdOrb; +typedef struct schib Schib; +typedef struct chsc_area_sda ChscAreaSda; +typedef struct senseid SenseId; +typedef struct subchannel_id SubChannelId; + /* start.s */ void disabled_wait(void); void consume_sclp_int(void); /* main.c */ -void virtio_panic(const char *string); +void panic(const char *string); void write_subsystem_identification(void); extern char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); -extern char ring_area[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); extern uint64_t boot_value; /* sclp-ascii.c */ @@ -63,10 +70,11 @@ void sclp_setup(void); /* virtio.c */ unsigned long virtio_load_direct(ulong rec_list1, ulong rec_list2, ulong subchan_id, void *load_addr); -bool virtio_is_blk(struct subchannel_id schid); -void virtio_setup_block(struct subchannel_id schid); +bool virtio_is_supported(SubChannelId schid); +void virtio_setup_device(SubChannelId schid); int virtio_read(ulong sector, void *load_addr); int enable_mss_facility(void); +ulong get_second(void); /* bootmap.c */ void zipl_load(void); @@ -143,4 +151,42 @@ static inline void yield(void) #define MAX_SECTOR_SIZE 4096 +static inline void sleep(unsigned int seconds) +{ + ulong target = get_second() + seconds; + + while (get_second() < target) { + yield(); + } +} + +static inline void *memcpy(void *s1, const void *s2, size_t n) +{ + uint8_t *p1 = s1; + const uint8_t *p2 = s2; + + while (n--) { + p1[n] = p2[n]; + } + return s1; +} + +static inline void IPL_assert(bool term, const char *message) +{ + if (!term) { + sclp_print("\n! "); + sclp_print(message); + panic(" !\n"); /* no return */ + } +} + +static inline void IPL_check(bool term, const char *message) +{ + if (!term) { + sclp_print("\n! WARNING: "); + sclp_print(message); + sclp_print(" !\n"); + } +} + #endif /* S390_CCW_H */ diff --git a/pc-bios/s390-ccw/scsi.h b/pc-bios/s390-ccw/scsi.h new file mode 100644 index 0000000000..fc830f7e52 --- /dev/null +++ b/pc-bios/s390-ccw/scsi.h @@ -0,0 +1,184 @@ +/* + * SCSI definitions for s390 machine loader for qemu + * + * Copyright 2015 IBM Corp. + * Author: Eugene "jno" Dvurechenski <jno@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#ifndef SCSI_H +#define SCSI_H + +#include "s390-ccw.h" + +#define SCSI_DEFAULT_CDB_SIZE 32 +#define SCSI_DEFAULT_SENSE_SIZE 96 + +#define CDB_STATUS_GOOD 0 +#define CDB_STATUS_CHECK_CONDITION 0x02U +#define CDB_STATUS_VALID(status) (((status) & ~0x3eU) == 0) + +#define SCSI_SENSE_CODE_MASK 0x7fU +#define SCSI_SENSE_KEY_MASK 0x0fU +#define SCSI_SENSE_KEY_NO_SENSE 0 +#define SCSI_SENSE_KEY_UNIT_ATTENTION 6 + +union ScsiLun { + uint64_t v64; /* numeric shortcut */ + uint8_t v8[8]; /* generic 8 bytes representation */ + uint16_t v16[4]; /* 4-level big-endian LUN as specified by SAM-2 */ +}; +typedef union ScsiLun ScsiLun; + +struct ScsiSense70 { + uint8_t b0; /* b0 & 7f = resp code (0x70 or 0x71) */ + uint8_t b1, b2; /* b2 & 0f = sense key */ + uint8_t u1[1 * 4 + 1 + 1 * 4]; /* b7 = N - 7 */ + uint8_t additional_sense_code; /* b12 */ + uint8_t additional_sense_code_qualifier; /* b13 */ + uint8_t u2[1 + 3 + 0]; /* up to N (<=252) bytes */ +} __attribute__((packed)); +typedef struct ScsiSense70 ScsiSense70; + +/* don't confuse with virtio-scsi response/status fields! */ + +static inline uint8_t scsi_sense_response(const void *p) +{ + return ((const ScsiSense70 *)p)->b0 & SCSI_SENSE_CODE_MASK; +} + +static inline uint8_t scsi_sense_key(const void *p) +{ + return ((const ScsiSense70 *)p)->b2 & SCSI_SENSE_KEY_MASK; +} + +#define SCSI_INQ_RDT_CDROM 0x05 + +struct ScsiInquiryStd { + uint8_t peripheral_qdt; /* b0, use (b0 & 0x1f) to get SCSI_INQ_RDT */ + uint8_t b1; /* Removable Media Bit = b1 & 0x80 */ + uint8_t spc_version; /* b2 */ + uint8_t b3; /* b3 & 0x0f == resp_data_fmt == 2, must! */ + uint8_t u1[1 + 1 + 1 + 1 + 8]; /* b4..b15 unused, b4 = (N - 1) */ + char prod_id[16]; /* "QEMU CD-ROM" is here */ + uint8_t u2[4 /* b32..b35 unused, mandatory */ + + 8 + 12 + 1 + 1 + 8 * 2 + 22 /* b36..95 unused, optional*/ + + 0]; /* b96..bN unused, vendor specific */ + /* byte N */ +} __attribute__((packed)); +typedef struct ScsiInquiryStd ScsiInquiryStd; + +struct ScsiCdbInquiry { + uint8_t command; /* b0, == 0x12 */ + uint8_t b1; /* b1, |= 0x01 (evpd) */ + uint8_t b2; /* b2; if evpd==1 */ + uint16_t alloc_len; /* b3, b4 */ + uint8_t control; /* b5 */ +} __attribute__((packed)); +typedef struct ScsiCdbInquiry ScsiCdbInquiry; + +struct ScsiCdbRead10 { + uint8_t command; /* =0x28 */ + uint8_t b1; + uint32_t lba; + uint8_t b6; + uint16_t xfer_length; + uint8_t control; +} __attribute__((packed)); +typedef struct ScsiCdbRead10 ScsiCdbRead10; + +struct ScsiCdbTestUnitReady { + uint8_t command; /* =0x00 */ + uint8_t b1_b4[4]; + uint8_t control; +} __attribute__((packed)); +typedef struct ScsiCdbTestUnitReady ScsiCdbTestUnitReady; + +struct ScsiCdbReportLuns { + uint8_t command; /* =0xa0 */ + uint8_t b1; + uint8_t select_report; /* =0x02, "all" */ + uint8_t b3_b5[3]; + uint32_t alloc_len; + uint8_t b10; + uint8_t control; +} __attribute__((packed)); +typedef struct ScsiCdbReportLuns ScsiCdbReportLuns; + +struct ScsiLunReport { + uint32_t lun_list_len; + uint32_t b4_b7; + ScsiLun lun[1]; /* space for at least 1 lun must be allocated */ +} __attribute__((packed)); +typedef struct ScsiLunReport ScsiLunReport; + +struct ScsiCdbReadCapacity16 { + uint8_t command; /* =0x9e = "service action in 16" */ + uint8_t service_action; /* 5 bits, =0x10 = "read capacity 16" */ + uint64_t b2_b9; + uint32_t alloc_len; + uint8_t b14; + uint8_t control; +} __attribute__((packed)); +typedef struct ScsiCdbReadCapacity16 ScsiCdbReadCapacity16; + +struct ScsiReadCapacity16Data { + uint64_t ret_lba; /* get it, 0..7 */ + uint32_t lb_len; /* bytes, 8..11 */ + uint8_t u1[2 + 1 * 2 + 16]; /* b12..b31, unused */ +} __attribute__((packed)); +typedef struct ScsiReadCapacity16Data ScsiReadCapacity16Data; + +static inline ScsiLun make_lun(uint16_t channel, uint16_t target, uint32_t lun) +{ + ScsiLun r = { .v64 = 0 }; + + /* See QEMU code to choose the way to handle LUNs. + * + * So, a valid LUN must have (always channel #0): + * lun[0] == 1 + * lun[1] - target, any value + * lun[2] == 0 or (LUN, MSB, 0x40 set, 0x80 clear) + * lun[3] - LUN, LSB, any value + */ + r.v8[0] = 1; + r.v8[1] = target & 0xffU; + r.v8[2] = (lun >> 8) & 0x3fU; + if (r.v8[2]) { + r.v8[2] |= 0x40; + } + r.v8[3] = lun & 0xffU; + + return r; +} + +static inline const char *scsi_cdb_status_msg(uint8_t status) +{ + static char err_msg[] = "STATUS=XX"; + uint8_t v = status & 0x3eU; + + fill_hex_val(err_msg + 7, &v, 1); + return err_msg; +} + +static inline const char *scsi_cdb_asc_msg(const void *s) +{ + static char err_msg[] = "RSPN=XX KEY=XX CODE=XX QLFR=XX"; + const ScsiSense70 *p = s; + uint8_t sr = scsi_sense_response(s); + uint8_t sk = scsi_sense_key(s); + uint8_t ac = p->additional_sense_code; + uint8_t cq = p->additional_sense_code_qualifier; + + fill_hex_val(err_msg + 5, &sr, 1); + fill_hex_val(err_msg + 12, &sk, 1); + fill_hex_val(err_msg + 20, &ac, 1); + fill_hex_val(err_msg + 28, &cq, 1); + + return err_msg; +} + +#endif /* SCSI_H */ diff --git a/pc-bios/s390-ccw/virtio-scsi.c b/pc-bios/s390-ccw/virtio-scsi.c new file mode 100644 index 0000000000..3bb48e917e --- /dev/null +++ b/pc-bios/s390-ccw/virtio-scsi.c @@ -0,0 +1,342 @@ +/* + * Virtio-SCSI implementation for s390 machine loader for qemu + * + * Copyright 2015 IBM Corp. + * Author: Eugene "jno" Dvurechenski <jno@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#include "s390-ccw.h" +#include "virtio.h" +#include "scsi.h" +#include "virtio-scsi.h" + +static ScsiDevice default_scsi_device; +static VirtioScsiCmdReq req; +static VirtioScsiCmdResp resp; + +static uint8_t scsi_inquiry_std_response[256]; + +static inline void vs_assert(bool term, const char **msgs) +{ + if (!term) { + int i = 0; + + sclp_print("\n! "); + while (msgs[i]) { + sclp_print(msgs[i++]); + } + panic(" !\n"); + } +} + +static void virtio_scsi_verify_response(VirtioScsiCmdResp *resp, + const char *title) +{ + const char *mr[] = { + title, ": response ", virtio_scsi_response_msg(resp), 0 + }; + const char *ms[] = { + title, + CDB_STATUS_VALID(resp->status) ? ": " : ": invalid ", + scsi_cdb_status_msg(resp->status), + resp->status == CDB_STATUS_CHECK_CONDITION ? " " : 0, + resp->sense_len ? scsi_cdb_asc_msg(resp->sense) + : "no sense data", + scsi_sense_response(resp->sense) == 0x70 ? ", sure" : "?", + 0 + }; + + vs_assert(resp->response == VIRTIO_SCSI_S_OK, mr); + vs_assert(resp->status == CDB_STATUS_GOOD, ms); +} + +static void prepare_request(VDev *vdev, const void *cdb, int cdb_size, + void *data, uint32_t data_size) +{ + const ScsiDevice *sdev = vdev->scsi_device; + + memset(&req, 0, sizeof(req)); + req.lun = make_lun(sdev->channel, sdev->target, sdev->lun); + memcpy(&req.cdb, cdb, cdb_size); + + memset(&resp, 0, sizeof(resp)); + resp.status = 0xff; /* set invalid */ + resp.response = 0xff; /* */ + + if (data && data_size) { + memset(data, 0, data_size); + } +} + +static inline void vs_io_assert(bool term, const char *msg) +{ + if (!term) { + virtio_scsi_verify_response(&resp, msg); + } +} + +static void vs_run(const char *title, VirtioCmd *cmd, VDev *vdev, + const void *cdb, int cdb_size, + void *data, uint32_t data_size) +{ + prepare_request(vdev, cdb, cdb_size, data, data_size); + vs_io_assert(virtio_run(vdev, VR_REQUEST, cmd) == 0, title); +} + +/* SCSI protocol implementation routines */ + +static bool scsi_inquiry(VDev *vdev, void *data, uint32_t data_size) +{ + ScsiCdbInquiry cdb = { + .command = 0x12, + .alloc_len = data_size < 65535 ? data_size : 65535, + }; + VirtioCmd inquiry[] = { + { &req, sizeof(req), VRING_DESC_F_NEXT }, + { &resp, sizeof(resp), VRING_DESC_F_WRITE | VRING_DESC_F_NEXT }, + { data, data_size, VRING_DESC_F_WRITE }, + }; + + vs_run("inquiry", inquiry, vdev, &cdb, sizeof(cdb), data, data_size); + + return virtio_scsi_response_ok(&resp); +} + +static bool scsi_test_unit_ready(VDev *vdev) +{ + ScsiCdbTestUnitReady cdb = { + .command = 0x00, + }; + VirtioCmd test_unit_ready[] = { + { &req, sizeof(req), VRING_DESC_F_NEXT }, + { &resp, sizeof(resp), VRING_DESC_F_WRITE }, + }; + + prepare_request(vdev, &cdb, sizeof(cdb), 0, 0); + virtio_run(vdev, VR_REQUEST, test_unit_ready); /* ignore errors here */ + + return virtio_scsi_response_ok(&resp); +} + +static bool scsi_report_luns(VDev *vdev, void *data, uint32_t data_size) +{ + ScsiCdbReportLuns cdb = { + .command = 0xa0, + .select_report = 0x02, /* REPORT ALL */ + .alloc_len = data_size, + }; + VirtioCmd report_luns[] = { + { &req, sizeof(req), VRING_DESC_F_NEXT }, + { &resp, sizeof(resp), VRING_DESC_F_WRITE | VRING_DESC_F_NEXT }, + { data, data_size, VRING_DESC_F_WRITE }, + }; + + vs_run("report luns", report_luns, + vdev, &cdb, sizeof(cdb), data, data_size); + + return virtio_scsi_response_ok(&resp); +} + +static bool scsi_read_10(VDev *vdev, + ulong sector, int sectors, void *data) +{ + int f = vdev->blk_factor; + unsigned int data_size = sectors * virtio_get_block_size() * f; + ScsiCdbRead10 cdb = { + .command = 0x28, + .lba = sector * f, + .xfer_length = sectors * f, + }; + VirtioCmd read_10[] = { + { &req, sizeof(req), VRING_DESC_F_NEXT }, + { &resp, sizeof(resp), VRING_DESC_F_WRITE | VRING_DESC_F_NEXT }, + { data, data_size * f, VRING_DESC_F_WRITE }, + }; + + debug_print_int("read_10 sector", sector); + debug_print_int("read_10 sectors", sectors); + + vs_run("read(10)", read_10, vdev, &cdb, sizeof(cdb), data, data_size); + + return virtio_scsi_response_ok(&resp); +} + +static bool scsi_read_capacity(VDev *vdev, + void *data, uint32_t data_size) +{ + ScsiCdbReadCapacity16 cdb = { + .command = 0x9e, /* SERVICE_ACTION_IN_16 */ + .service_action = 0x10, /* SA_READ_CAPACITY */ + .alloc_len = data_size, + }; + VirtioCmd read_capacity_16[] = { + { &req, sizeof(req), VRING_DESC_F_NEXT }, + { &resp, sizeof(resp), VRING_DESC_F_WRITE | VRING_DESC_F_NEXT }, + { data, data_size, VRING_DESC_F_WRITE }, + }; + + vs_run("read capacity", read_capacity_16, + vdev, &cdb, sizeof(cdb), data, data_size); + + return virtio_scsi_response_ok(&resp); +} + +/* virtio-scsi routines */ + +static void virtio_scsi_locate_device(VDev *vdev) +{ + const uint16_t channel = 0; /* again, it's what QEMU does */ + uint16_t target; + static uint8_t data[16 + 8 * 63]; + ScsiLunReport *r = (void *) data; + ScsiDevice *sdev = vdev->scsi_device; + int i, luns; + + /* QEMU has hardcoded channel #0 in many places. + * If this hardcoded value is ever changed, we'll need to add code for + * vdev->config.scsi.max_channel != 0 here. + */ + debug_print_int("config.scsi.max_channel", vdev->config.scsi.max_channel); + debug_print_int("config.scsi.max_target ", vdev->config.scsi.max_target); + debug_print_int("config.scsi.max_lun ", vdev->config.scsi.max_lun); + + for (target = 0; target <= vdev->config.scsi.max_target; target++) { + sdev->channel = channel; + sdev->target = target; /* sdev->lun will be 0 here */ + if (!scsi_report_luns(vdev, data, sizeof(data))) { + if (resp.response == VIRTIO_SCSI_S_BAD_TARGET) { + continue; + } + print_int("target", target); + virtio_scsi_verify_response(&resp, "SCSI cannot report LUNs"); + } + if (r->lun_list_len == 0) { + print_int("no LUNs for target", target); + continue; + } + luns = r->lun_list_len / 8; + debug_print_int("LUNs reported", luns); + if (luns == 1) { + /* There is no ",lun=#" arg for -device or ",lun=0" given. + * Hence, the only LUN reported. + * Usually, it's 0. + */ + sdev->lun = r->lun[0].v16[0]; /* it's returned this way */ + debug_print_int("Have to use LUN", sdev->lun); + return; /* we have to use this device */ + } + for (i = 0; i < luns; i++) { + if (r->lun[i].v64) { + /* Look for non-zero LUN - we have where to choose from */ + sdev->lun = r->lun[i].v16[0]; + debug_print_int("Will use LUN", sdev->lun); + return; /* we have found a device */ + } + } + } + panic("\n! Cannot locate virtio-scsi device !\n"); +} + +int virtio_scsi_read_many(VDev *vdev, + ulong sector, void *load_addr, int sec_num) +{ + if (!scsi_read_10(vdev, sector, sec_num, load_addr)) { + virtio_scsi_verify_response(&resp, "virtio-scsi:read_many"); + } + + return 0; +} + +static bool virtio_scsi_inquiry_response_is_cdrom(void *data) +{ + const ScsiInquiryStd *response = data; + const int resp_data_fmt = response->b3 & 0x0f; + int i; + + IPL_check(resp_data_fmt == 2, "Wrong INQUIRY response format"); + if (resp_data_fmt != 2) { + return false; /* cannot decode */ + } + + if ((response->peripheral_qdt & 0x1f) == SCSI_INQ_RDT_CDROM) { + return true; + } + + for (i = 0; i < sizeof(response->prod_id); i++) { + if (response->prod_id[i] != QEMU_CDROM_SIGNATURE[i]) { + return false; + } + } + return true; +} + +static void scsi_parse_capacity_report(void *data, + uint64_t *last_lba, uint32_t *lb_len) +{ + ScsiReadCapacity16Data *p = data; + + if (last_lba) { + *last_lba = p->ret_lba; + } + + if (lb_len) { + *lb_len = p->lb_len; + } +} + +void virtio_scsi_setup(VDev *vdev) +{ + int retry_test_unit_ready = 3; + uint8_t data[256]; + uint32_t data_size = sizeof(data); + + vdev->scsi_device = &default_scsi_device; + virtio_scsi_locate_device(vdev); + + /* We have to "ping" the device before it becomes readable */ + while (!scsi_test_unit_ready(vdev)) { + + if (!virtio_scsi_response_ok(&resp)) { + uint8_t code = resp.sense[0] & SCSI_SENSE_CODE_MASK; + uint8_t sense_key = resp.sense[2] & SCSI_SENSE_KEY_MASK; + + IPL_assert(resp.sense_len != 0, "virtio-scsi:setup: no SENSE data"); + + IPL_assert(retry_test_unit_ready && code == 0x70 && + sense_key == SCSI_SENSE_KEY_UNIT_ATTENTION, + "virtio-scsi:setup: cannot retry"); + + /* retry on CHECK_CONDITION/UNIT_ATTENTION as it + * may not designate a real error, but it may be + * a result of device reset, etc. + */ + retry_test_unit_ready--; + sleep(1); + continue; + } + + virtio_scsi_verify_response(&resp, "virtio-scsi:setup"); + } + + /* read and cache SCSI INQUIRY response */ + if (!scsi_inquiry(vdev, scsi_inquiry_std_response, + sizeof(scsi_inquiry_std_response))) { + virtio_scsi_verify_response(&resp, "virtio-scsi:setup:inquiry"); + } + + if (virtio_scsi_inquiry_response_is_cdrom(scsi_inquiry_std_response)) { + sclp_print("SCSI CD-ROM detected.\n"); + vdev->is_cdrom = true; + vdev->scsi_block_size = VIRTIO_ISO_BLOCK_SIZE; + } + + if (!scsi_read_capacity(vdev, data, data_size)) { + virtio_scsi_verify_response(&resp, "virtio-scsi:setup:read_capacity"); + } + scsi_parse_capacity_report(data, &vdev->scsi_last_block, + (uint32_t *) &vdev->scsi_block_size); +} diff --git a/pc-bios/s390-ccw/virtio-scsi.h b/pc-bios/s390-ccw/virtio-scsi.h new file mode 100644 index 0000000000..f50b38b18b --- /dev/null +++ b/pc-bios/s390-ccw/virtio-scsi.h @@ -0,0 +1,72 @@ +/* + * Virtio-SCSI definitions for s390 machine loader for qemu + * + * Copyright 2015 IBM Corp. + * Author: Eugene "jno" Dvurechenski <jno@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#ifndef VIRTIO_SCSI_H +#define VIRTIO_SCSI_H + +#include "s390-ccw.h" +#include "virtio.h" +#include "scsi.h" + +#define VIRTIO_SCSI_CDB_SIZE SCSI_DEFAULT_CDB_SIZE +#define VIRTIO_SCSI_SENSE_SIZE SCSI_DEFAULT_SENSE_SIZE + +/* command-specific response values */ +#define VIRTIO_SCSI_S_OK 0x00 +#define VIRTIO_SCSI_S_BAD_TARGET 0x03 + +#define QEMU_CDROM_SIGNATURE "QEMU CD-ROM " + +enum virtio_scsi_vq_id { + VR_CONTROL = 0, + VR_EVENT = 1, + VR_REQUEST = 2, +}; + +struct VirtioScsiCmdReq { + ScsiLun lun; + uint64_t id; + uint8_t task_attr; /* = 0 = VIRTIO_SCSI_S_SIMPLE */ + uint8_t prio; + uint8_t crn; /* = 0 */ + uint8_t cdb[VIRTIO_SCSI_CDB_SIZE]; +} __attribute__((packed)); +typedef struct VirtioScsiCmdReq VirtioScsiCmdReq; + +struct VirtioScsiCmdResp { + uint32_t sense_len; + uint32_t residual; + uint16_t status_qualifier; + uint8_t status; /* first check for .response */ + uint8_t response; /* then for .status */ + uint8_t sense[VIRTIO_SCSI_SENSE_SIZE]; +} __attribute__((packed)); +typedef struct VirtioScsiCmdResp VirtioScsiCmdResp; + +static inline const char *virtio_scsi_response_msg(const VirtioScsiCmdResp *r) +{ + static char err_msg[] = "VS RESP=XX"; + uint8_t v = r->response; + + fill_hex_val(err_msg + 8, &v, 1); + return err_msg; +} + +static inline bool virtio_scsi_response_ok(const VirtioScsiCmdResp *r) +{ + return r->response == VIRTIO_SCSI_S_OK && r->status == CDB_STATUS_GOOD; +} + +void virtio_scsi_setup(VDev *vdev); +int virtio_scsi_read_many(VDev *vdev, + ulong sector, void *load_addr, int sec_num); + +#endif /* VIRTIO_SCSI_H */ diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c index 87aed38a95..1d34e8c1aa 100644 --- a/pc-bios/s390-ccw/virtio.c +++ b/pc-bios/s390-ccw/virtio.c @@ -10,39 +10,68 @@ #include "s390-ccw.h" #include "virtio.h" +#include "virtio-scsi.h" -static struct vring block; +#define VRING_WAIT_REPLY_TIMEOUT 3 + +static VRing block[VIRTIO_MAX_VQS]; +static char ring_area[VIRTIO_RING_SIZE * VIRTIO_MAX_VQS] + __attribute__((__aligned__(PAGE_SIZE))); static char chsc_page[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); +static VDev vdev = { + .nr_vqs = 1, + .vrings = block, + .cmd_vr_idx = 0, + .ring_area = ring_area, + .wait_reply_timeout = VRING_WAIT_REPLY_TIMEOUT, + .schid = { .one = 1 }, + .scsi_block_size = VIRTIO_SCSI_BLOCK_SIZE, + .blk_factor = 1, +}; + +VDev *virtio_get_device(void) +{ + return &vdev; +} + +VirtioDevType virtio_get_device_type(void) +{ + return vdev.senseid.cu_model; +} + +/* virtio spec v1.0 para 4.3.3.2 */ static long kvm_hypercall(unsigned long nr, unsigned long param1, - unsigned long param2) + unsigned long param2, unsigned long param3) { register ulong r_nr asm("1") = nr; register ulong r_param1 asm("2") = param1; register ulong r_param2 asm("3") = param2; + register ulong r_param3 asm("4") = param3; register long retval asm("2"); asm volatile ("diag 2,4,0x500" : "=d" (retval) - : "d" (r_nr), "0" (r_param1), "r"(r_param2) + : "d" (r_nr), "0" (r_param1), "r"(r_param2), "d"(r_param3) : "memory", "cc"); return retval; } -static void virtio_notify(struct subchannel_id schid) +static long virtio_notify(SubChannelId schid, int vq_idx, long cookie) { - kvm_hypercall(KVM_S390_VIRTIO_CCW_NOTIFY, *(u32 *)&schid, 0); + return kvm_hypercall(KVM_S390_VIRTIO_CCW_NOTIFY, *(u32 *)&schid, + vq_idx, cookie); } /*********************************************** * Virtio functions * ***********************************************/ -static int drain_irqs(struct subchannel_id schid) +static int drain_irqs(SubChannelId schid) { - struct irb irb = {}; + Irb irb = {}; int r = 0; while (1) { @@ -59,17 +88,17 @@ static int drain_irqs(struct subchannel_id schid) } } -static int run_ccw(struct subchannel_id schid, int cmd, void *ptr, int len) +static int run_ccw(VDev *vdev, int cmd, void *ptr, int len) { - struct ccw1 ccw = {}; - struct cmd_orb orb = {}; - struct schib schib; + Ccw1 ccw = {}; + CmdOrb orb = {}; + Schib schib; int r; /* start command processing */ - stsch_err(schid, &schib); + stsch_err(vdev->schid, &schib); schib.scsw.ctrl = SCSW_FCTL_START_FUNC; - msch(schid, &schib); + msch(vdev->schid, &schib); /* start subchannel command */ orb.fmt = 1; @@ -80,41 +109,29 @@ static int run_ccw(struct subchannel_id schid, int cmd, void *ptr, int len) ccw.cda = (long)ptr; ccw.count = len; - r = ssch(schid, &orb); + r = ssch(vdev->schid, &orb); /* * XXX Wait until device is done processing the CCW. For now we can * assume that a simple tsch will have finished the CCW processing, * but the architecture allows for asynchronous operation */ if (!r) { - r = drain_irqs(schid); + r = drain_irqs(vdev->schid); } return r; } -static void virtio_set_status(struct subchannel_id schid, - unsigned long dev_addr) +static void vring_init(VRing *vr, VqInfo *info) { - unsigned char status = dev_addr; - if (run_ccw(schid, CCW_CMD_WRITE_STATUS, &status, sizeof(status))) { - virtio_panic("Could not write status to host!\n"); - } -} + void *p = (void *) info->queue; -static void virtio_reset(struct subchannel_id schid) -{ - run_ccw(schid, CCW_CMD_VDEV_RESET, NULL, 0); -} - -static void vring_init(struct vring *vr, unsigned int num, void *p, - unsigned long align) -{ debug_print_addr("init p", p); - vr->num = num; + vr->id = info->index; + vr->num = info->num; vr->desc = p; - vr->avail = p + num*sizeof(struct vring_desc); - vr->used = (void *)(((unsigned long)&vr->avail->ring[num] + align-1) - & ~(align - 1)); + vr->avail = p + info->num * sizeof(VRingDesc); + vr->used = (void *)(((unsigned long)&vr->avail->ring[info->num] + + info->align - 1) & ~(info->align - 1)); /* Zero out all relevant field */ vr->avail->flags = 0; @@ -125,16 +142,18 @@ static void vring_init(struct vring *vr, unsigned int num, void *p, vr->used->idx = 0; vr->used_idx = 0; vr->next_idx = 0; + vr->cookie = 0; debug_print_addr("init vr", vr); } -static void vring_notify(struct subchannel_id schid) +static bool vring_notify(VRing *vr) { - virtio_notify(schid); + vr->cookie = virtio_notify(vr->schid, vr->id, vr->cookie); + return vr->cookie >= 0; } -static void vring_send_buf(struct vring *vr, void *p, int len, int flags) +static void vring_send_buf(VRing *vr, void *p, int len, int flags) { /* For follow-up chains we need to keep the first entry point */ if (!(flags & VRING_HIDDEN_IS_CHAIN)) { @@ -162,11 +181,26 @@ static u64 get_clock(void) return r; } -static ulong get_second(void) +ulong get_second(void) { return (get_clock() >> 12) / 1000000; } +static int vr_poll(VRing *vr) +{ + if (vr->used->idx == vr->used_idx) { + vring_notify(vr); + yield(); + return 0; + } + + vr->used_idx = vr->used->idx; + vr->next_idx = 0; + vr->desc[0].len = 0; + vr->desc[0].flags = 0; + return 1; /* vr has been updated */ +} + /* * Wait for the host to reply. * @@ -174,67 +208,92 @@ static ulong get_second(void) * * Returns 0 on success, 1 on timeout. */ -static int vring_wait_reply(struct vring *vr, int timeout) +static int vring_wait_reply(void) { - ulong target_second = get_second() + timeout; - struct subchannel_id schid = vr->schid; - int r = 0; + ulong target_second = get_second() + vdev.wait_reply_timeout; - /* Wait until the used index has moved. */ - while (vr->used->idx == vr->used_idx) { - vring_notify(schid); - if (timeout && (get_second() >= target_second)) { - r = 1; - break; + /* Wait for any queue to be updated by the host */ + do { + int i, r = 0; + + for (i = 0; i < vdev.nr_vqs; i++) { + r += vr_poll(&vdev.vrings[i]); } yield(); - } + if (r) { + return 0; + } + } while (!vdev.wait_reply_timeout || (get_second() < target_second)); - vr->used_idx = vr->used->idx; - vr->next_idx = 0; - vr->desc[0].len = 0; - vr->desc[0].flags = 0; + return 1; +} - return r; +int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd) +{ + VRing *vr = &vdev->vrings[vqid]; + int i = 0; + + do { + vring_send_buf(vr, cmd[i].data, cmd[i].size, + cmd[i].flags | (i ? VRING_HIDDEN_IS_CHAIN : 0)); + } while (cmd[i++].flags & VRING_DESC_F_NEXT); + + vring_wait_reply(); + if (drain_irqs(vr->schid)) { + return -1; + } + return 0; } /*********************************************** * Virtio block * ***********************************************/ -int virtio_read_many(ulong sector, void *load_addr, int sec_num) +static int virtio_blk_read_many(VDev *vdev, + ulong sector, void *load_addr, int sec_num) { - struct virtio_blk_outhdr out_hdr; + VirtioBlkOuthdr out_hdr; u8 status; - int r; + VRing *vr = &vdev->vrings[vdev->cmd_vr_idx]; /* Tell the host we want to read */ out_hdr.type = VIRTIO_BLK_T_IN; out_hdr.ioprio = 99; out_hdr.sector = virtio_sector_adjust(sector); - vring_send_buf(&block, &out_hdr, sizeof(out_hdr), VRING_DESC_F_NEXT); + vring_send_buf(vr, &out_hdr, sizeof(out_hdr), VRING_DESC_F_NEXT); /* This is where we want to receive data */ - vring_send_buf(&block, load_addr, virtio_get_block_size() * sec_num, + vring_send_buf(vr, load_addr, virtio_get_block_size() * sec_num, VRING_DESC_F_WRITE | VRING_HIDDEN_IS_CHAIN | VRING_DESC_F_NEXT); /* status field */ - vring_send_buf(&block, &status, sizeof(u8), VRING_DESC_F_WRITE | - VRING_HIDDEN_IS_CHAIN); + vring_send_buf(vr, &status, sizeof(u8), + VRING_DESC_F_WRITE | VRING_HIDDEN_IS_CHAIN); /* Now we can tell the host to read */ - vring_wait_reply(&block, 0); + vring_wait_reply(); - r = drain_irqs(block.schid); - if (r) { + if (drain_irqs(vr->schid)) { /* Well, whatever status is supposed to contain... */ status = 1; } return status; } +int virtio_read_many(ulong sector, void *load_addr, int sec_num) +{ + switch (vdev.senseid.cu_model) { + case VIRTIO_ID_BLOCK: + return virtio_blk_read_many(&vdev, sector, load_addr, sec_num); + case VIRTIO_ID_SCSI: + return virtio_scsi_read_many(&vdev, sector, load_addr, sec_num); + } + panic("\n! No readable IPL device !\n"); + return -1; +} + unsigned long virtio_load_direct(ulong rec_list1, ulong rec_list2, ulong subchan_id, void *load_addr) { @@ -251,7 +310,7 @@ unsigned long virtio_load_direct(ulong rec_list1, ulong rec_list2, sclp_print("."); status = virtio_read_many(sec, (void *)addr, sec_num); if (status) { - virtio_panic("I/O Error"); + panic("I/O Error"); } addr += sec_num * virtio_get_block_size(); @@ -263,78 +322,110 @@ int virtio_read(ulong sector, void *load_addr) return virtio_read_many(sector, load_addr, 1); } -static VirtioBlkConfig blk_cfg = {}; -static bool guessed_disk_nature; +/* + * Other supported value pairs, if any, would need to be added here. + * Note: head count is always 15. + */ +static inline u8 virtio_eckd_sectors_for_block_size(int size) +{ + switch (size) { + case 512: + return 49; + case 1024: + return 33; + case 2048: + return 21; + case 4096: + return 12; + } + return 0; +} -bool virtio_guessed_disk_nature(void) +VirtioGDN virtio_guessed_disk_nature(void) { - return guessed_disk_nature; + return vdev.guessed_disk_nature; } void virtio_assume_scsi(void) { - guessed_disk_nature = true; - blk_cfg.blk_size = 512; - blk_cfg.physical_block_exp = 0; + switch (vdev.senseid.cu_model) { + case VIRTIO_ID_BLOCK: + vdev.guessed_disk_nature = VIRTIO_GDN_SCSI; + vdev.config.blk.blk_size = VIRTIO_SCSI_BLOCK_SIZE; + vdev.config.blk.physical_block_exp = 0; + vdev.blk_factor = 1; + break; + case VIRTIO_ID_SCSI: + vdev.scsi_block_size = VIRTIO_SCSI_BLOCK_SIZE; + break; + } } void virtio_assume_iso9660(void) { - guessed_disk_nature = true; - blk_cfg.blk_size = 2048; - blk_cfg.physical_block_exp = 0; + switch (vdev.senseid.cu_model) { + case VIRTIO_ID_BLOCK: + vdev.guessed_disk_nature = VIRTIO_GDN_SCSI; + vdev.config.blk.blk_size = VIRTIO_ISO_BLOCK_SIZE; + vdev.config.blk.physical_block_exp = 0; + vdev.blk_factor = VIRTIO_ISO_BLOCK_SIZE / VIRTIO_SECTOR_SIZE; + break; + case VIRTIO_ID_SCSI: + vdev.scsi_block_size = VIRTIO_ISO_BLOCK_SIZE; + break; + } } void virtio_assume_eckd(void) { - guessed_disk_nature = true; - blk_cfg.blk_size = 4096; - blk_cfg.physical_block_exp = 0; - - /* this must be here to calculate code segment position */ - blk_cfg.geometry.heads = 15; - blk_cfg.geometry.sectors = 12; + vdev.guessed_disk_nature = VIRTIO_GDN_DASD; + vdev.blk_factor = 1; + vdev.config.blk.physical_block_exp = 0; + switch (vdev.senseid.cu_model) { + case VIRTIO_ID_BLOCK: + vdev.config.blk.blk_size = 4096; + break; + case VIRTIO_ID_SCSI: + vdev.config.blk.blk_size = vdev.scsi_block_size; + break; + } + vdev.config.blk.geometry.heads = 15; + vdev.config.blk.geometry.sectors = + virtio_eckd_sectors_for_block_size(vdev.config.blk.blk_size); } bool virtio_disk_is_scsi(void) { - if (guessed_disk_nature) { - return (virtio_get_block_size() == 512); + if (vdev.guessed_disk_nature == VIRTIO_GDN_SCSI) { + return true; } - return (blk_cfg.geometry.heads == 255) - && (blk_cfg.geometry.sectors == 63) - && (virtio_get_block_size() == 512); -} - -/* - * Other supported value pairs, if any, would need to be added here. - * Note: head count is always 15. - */ -static inline u8 virtio_eckd_sectors_for_block_size(int size) -{ - switch (size) { - case 512: - return 49; - case 1024: - return 33; - case 2048: - return 21; - case 4096: - return 12; + switch (vdev.senseid.cu_model) { + case VIRTIO_ID_BLOCK: + return (vdev.config.blk.geometry.heads == 255) + && (vdev.config.blk.geometry.sectors == 63) + && (virtio_get_block_size() == VIRTIO_SCSI_BLOCK_SIZE); + case VIRTIO_ID_SCSI: + return true; } - return 0; + return false; } bool virtio_disk_is_eckd(void) { const int block_size = virtio_get_block_size(); - if (guessed_disk_nature) { - return (block_size == 4096); + if (vdev.guessed_disk_nature == VIRTIO_GDN_DASD) { + return true; } - return (blk_cfg.geometry.heads == 15) - && (blk_cfg.geometry.sectors == - virtio_eckd_sectors_for_block_size(block_size)); + switch (vdev.senseid.cu_model) { + case VIRTIO_ID_BLOCK: + return (vdev.config.blk.geometry.heads == 15) + && (vdev.config.blk.geometry.sectors == + virtio_eckd_sectors_for_block_size(block_size)); + case VIRTIO_ID_SCSI: + return false; + } + return false; } bool virtio_ipl_disk_is_valid(void) @@ -344,34 +435,80 @@ bool virtio_ipl_disk_is_valid(void) int virtio_get_block_size(void) { - return blk_cfg.blk_size << blk_cfg.physical_block_exp; + switch (vdev.senseid.cu_model) { + case VIRTIO_ID_BLOCK: + return vdev.config.blk.blk_size << vdev.config.blk.physical_block_exp; + case VIRTIO_ID_SCSI: + return vdev.scsi_block_size; + } + return 0; } uint8_t virtio_get_heads(void) { - return blk_cfg.geometry.heads; + switch (vdev.senseid.cu_model) { + case VIRTIO_ID_BLOCK: + return vdev.config.blk.geometry.heads; + case VIRTIO_ID_SCSI: + return vdev.guessed_disk_nature == VIRTIO_GDN_DASD + ? vdev.config.blk.geometry.heads : 255; + } + return 0; } uint8_t virtio_get_sectors(void) { - return blk_cfg.geometry.sectors; + switch (vdev.senseid.cu_model) { + case VIRTIO_ID_BLOCK: + return vdev.config.blk.geometry.sectors; + case VIRTIO_ID_SCSI: + return vdev.guessed_disk_nature == VIRTIO_GDN_DASD + ? vdev.config.blk.geometry.sectors : 63; + } + return 0; } uint64_t virtio_get_blocks(void) { - return blk_cfg.capacity / - (virtio_get_block_size() / VIRTIO_SECTOR_SIZE); + const uint64_t factor = virtio_get_block_size() / VIRTIO_SECTOR_SIZE; + switch (vdev.senseid.cu_model) { + case VIRTIO_ID_BLOCK: + return vdev.config.blk.capacity / factor; + case VIRTIO_ID_SCSI: + return vdev.scsi_last_block / factor; + } + return 0; } -void virtio_setup_block(struct subchannel_id schid) +static void virtio_setup_ccw(VDev *vdev) { - struct vq_info_block info; - struct vq_config_block config = {}; - - blk_cfg.blk_size = 0; /* mark "illegal" - setup started... */ - guessed_disk_nature = false; - - virtio_reset(schid); + int i, cfg_size = 0; + unsigned char status = VIRTIO_CONFIG_S_DRIVER_OK; + + IPL_assert(virtio_is_supported(vdev->schid), "PE"); + /* device ID has been established now */ + + vdev->config.blk.blk_size = 0; /* mark "illegal" - setup started... */ + vdev->guessed_disk_nature = VIRTIO_GDN_NONE; + + run_ccw(vdev, CCW_CMD_VDEV_RESET, NULL, 0); + + switch (vdev->senseid.cu_model) { + case VIRTIO_ID_BLOCK: + vdev->nr_vqs = 1; + vdev->cmd_vr_idx = 0; + cfg_size = sizeof(vdev->config.blk); + break; + case VIRTIO_ID_SCSI: + vdev->nr_vqs = 3; + vdev->cmd_vr_idx = VR_REQUEST; + cfg_size = sizeof(vdev->config.scsi); + break; + default: + panic("Unsupported virtio device\n"); + } + IPL_assert(run_ccw(vdev, CCW_CMD_READ_CONF, &vdev->config, cfg_size) == 0, + "Could not get block device configuration"); /* * Skipping CCW_CMD_READ_FEAT. We're not doing anything fancy, and @@ -379,54 +516,84 @@ void virtio_setup_block(struct subchannel_id schid) * expect it. */ - config.index = 0; - if (run_ccw(schid, CCW_CMD_READ_VQ_CONF, &config, sizeof(config))) { - virtio_panic("Could not get block device VQ configuration\n"); - } - if (run_ccw(schid, CCW_CMD_READ_CONF, &blk_cfg, sizeof(blk_cfg))) { - virtio_panic("Could not get block device configuration\n"); - } - vring_init(&block, config.num, ring_area, - KVM_S390_VIRTIO_RING_ALIGN); - - info.queue = (unsigned long long) ring_area; - info.align = KVM_S390_VIRTIO_RING_ALIGN; - info.index = 0; - info.num = config.num; - block.schid = schid; - - if (!run_ccw(schid, CCW_CMD_SET_VQ, &info, sizeof(info))) { - virtio_set_status(schid, VIRTIO_CONFIG_S_DRIVER_OK); + for (i = 0; i < vdev->nr_vqs; i++) { + VqInfo info = { + .queue = (unsigned long long) ring_area + (i * VIRTIO_RING_SIZE), + .align = KVM_S390_VIRTIO_RING_ALIGN, + .index = i, + .num = 0, + }; + VqConfig config = { + .index = i, + .num = 0, + }; + + IPL_assert( + run_ccw(vdev, CCW_CMD_READ_VQ_CONF, &config, sizeof(config)) == 0, + "Could not get block device VQ configuration"); + info.num = config.num; + vring_init(&vdev->vrings[i], &info); + vdev->vrings[i].schid = vdev->schid; + IPL_assert(run_ccw(vdev, CCW_CMD_SET_VQ, &info, sizeof(info)) == 0, + "Cannot set VQ info"); } + IPL_assert( + run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status)) == 0, + "Could not write status to host"); +} - if (!virtio_ipl_disk_is_valid()) { - /* make sure all getters but blocksize return 0 for invalid IPL disk */ - memset(&blk_cfg, 0, sizeof(blk_cfg)); - virtio_assume_scsi(); +void virtio_setup_device(SubChannelId schid) +{ + vdev.schid = schid; + virtio_setup_ccw(&vdev); + + switch (vdev.senseid.cu_model) { + case VIRTIO_ID_BLOCK: + sclp_print("Using virtio-blk.\n"); + if (!virtio_ipl_disk_is_valid()) { + /* make sure all getters but blocksize return 0 for + * invalid IPL disk + */ + memset(&vdev.config.blk, 0, sizeof(vdev.config.blk)); + virtio_assume_scsi(); + } + break; + case VIRTIO_ID_SCSI: + IPL_assert(vdev.config.scsi.sense_size == VIRTIO_SCSI_SENSE_SIZE, + "Config: sense size mismatch"); + IPL_assert(vdev.config.scsi.cdb_size == VIRTIO_SCSI_CDB_SIZE, + "Config: CDB size mismatch"); + + sclp_print("Using virtio-scsi.\n"); + virtio_scsi_setup(&vdev); + break; + default: + panic("\n! No IPL device available !\n"); } } -bool virtio_is_blk(struct subchannel_id schid) +bool virtio_is_supported(SubChannelId schid) { - int r; - struct senseid senseid = {}; - + vdev.schid = schid; + memset(&vdev.senseid, 0, sizeof(vdev.senseid)); /* run sense id command */ - r = run_ccw(schid, CCW_CMD_SENSE_ID, &senseid, sizeof(senseid)); - if (r) { + if (run_ccw(&vdev, CCW_CMD_SENSE_ID, &vdev.senseid, sizeof(vdev.senseid))) { return false; } - if ((senseid.cu_type != 0x3832) || (senseid.cu_model != VIRTIO_ID_BLOCK)) { - return false; + if (vdev.senseid.cu_type == 0x3832) { + switch (vdev.senseid.cu_model) { + case VIRTIO_ID_BLOCK: + case VIRTIO_ID_SCSI: + return true; + } } - - return true; + return false; } int enable_mss_facility(void) { int ret; - struct chsc_area_sda *sda_area = (struct chsc_area_sda *) chsc_page; + ChscAreaSda *sda_area = (ChscAreaSda *) chsc_page; memset(sda_area, 0, PAGE_SIZE); sda_area->request.length = 0x0400; diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h index afa01a885b..3c6e91510e 100644 --- a/pc-bios/s390-ccw/virtio.h +++ b/pc-bios/s390-ccw/virtio.h @@ -23,49 +23,58 @@ /* We've given up on this device. */ #define VIRTIO_CONFIG_S_FAILED 0x80 -enum virtio_dev_type { +enum VirtioDevType { VIRTIO_ID_NET = 1, VIRTIO_ID_BLOCK = 2, VIRTIO_ID_CONSOLE = 3, VIRTIO_ID_BALLOON = 5, + VIRTIO_ID_SCSI = 8, }; - -struct virtio_dev_header { - enum virtio_dev_type type : 8; - u8 num_vq; - u8 feature_len; - u8 config_len; - u8 status; - u8 vqconfig[]; +typedef enum VirtioDevType VirtioDevType; + +struct VirtioDevHeader { + VirtioDevType type:8; + uint8_t num_vq; + uint8_t feature_len; + uint8_t config_len; + uint8_t status; + uint8_t vqconfig[]; } __attribute__((packed)); +typedef struct VirtioDevHeader VirtioDevHeader; -struct virtio_vqconfig { - u64 token; - u64 address; - u16 num; - u8 pad[6]; +struct VirtioVqConfig { + uint64_t token; + uint64_t address; + uint16_t num; + uint8_t pad[6]; } __attribute__((packed)); +typedef struct VirtioVqConfig VirtioVqConfig; -struct vq_info_block { - u64 queue; - u32 align; - u16 index; - u16 num; +struct VqInfo { + uint64_t queue; + uint32_t align; + uint16_t index; + uint16_t num; } __attribute__((packed)); +typedef struct VqInfo VqInfo; -struct vq_config_block { - u16 index; - u16 num; +struct VqConfig { + uint16_t index; + uint16_t num; } __attribute__((packed)); +typedef struct VqConfig VqConfig; -struct virtio_dev { - struct virtio_dev_header *header; - struct virtio_vqconfig *vqconfig; +struct VirtioDev { + VirtioDevHeader *header; + VirtioVqConfig *vqconfig; char *host_features; char *guest_features; char *config; }; +typedef struct VirtioDev VirtioDev; +#define VIRTIO_RING_SIZE (PAGE_SIZE * 8) +#define VIRTIO_MAX_VQS 3 #define KVM_S390_VIRTIO_RING_ALIGN 4096 #define VRING_USED_F_NO_NOTIFY 1 @@ -81,46 +90,53 @@ struct virtio_dev { #define VRING_HIDDEN_IS_CHAIN 256 /* Virtio ring descriptors: 16 bytes. These can chain together via "next". */ -struct vring_desc { +struct VRingDesc { /* Address (guest-physical). */ - u64 addr; + uint64_t addr; /* Length. */ - u32 len; + uint32_t len; /* The flags as indicated above. */ - u16 flags; + uint16_t flags; /* We chain unused descriptors via this, too */ - u16 next; + uint16_t next; } __attribute__((packed)); +typedef struct VRingDesc VRingDesc; -struct vring_avail { - u16 flags; - u16 idx; - u16 ring[]; +struct VRingAvail { + uint16_t flags; + uint16_t idx; + uint16_t ring[]; } __attribute__((packed)); +typedef struct VRingAvail VRingAvail; -/* u32 is used here for ids for padding reasons. */ -struct vring_used_elem { +/* uint32_t is used here for ids for padding reasons. */ +struct VRingUsedElem { /* Index of start of used descriptor chain. */ - u32 id; + uint32_t id; /* Total length of the descriptor chain which was used (written to) */ - u32 len; + uint32_t len; } __attribute__((packed)); +typedef struct VRingUsedElem VRingUsedElem; -struct vring_used { - u16 flags; - u16 idx; - struct vring_used_elem ring[]; +struct VRingUsed { + uint16_t flags; + uint16_t idx; + VRingUsedElem ring[]; } __attribute__((packed)); +typedef struct VRingUsed VRingUsed; -struct vring { +struct VRing { unsigned int num; int next_idx; int used_idx; - struct vring_desc *desc; - struct vring_avail *avail; - struct vring_used *used; - struct subchannel_id schid; + VRingDesc *desc; + VRingAvail *avail; + VRingUsed *used; + SubChannelId schid; + long cookie; + int id; }; +typedef struct VRing VRing; /*********************************************** @@ -152,39 +168,49 @@ struct vring { #define VIRTIO_BLK_T_BARRIER 0x80000000 /* This is the first element of the read scatter-gather list. */ -struct virtio_blk_outhdr { +struct VirtioBlkOuthdr { /* VIRTIO_BLK_T* */ - u32 type; + uint32_t type; /* io priority. */ - u32 ioprio; + uint32_t ioprio; /* Sector (ie. 512 byte offset) */ - u64 sector; + uint64_t sector; }; +typedef struct VirtioBlkOuthdr VirtioBlkOuthdr; -typedef struct VirtioBlkConfig { - u64 capacity; /* in 512-byte sectors */ - u32 size_max; /* max segment size (if VIRTIO_BLK_F_SIZE_MAX) */ - u32 seg_max; /* max number of segments (if VIRTIO_BLK_F_SEG_MAX) */ +struct VirtioBlkConfig { + uint64_t capacity; /* in 512-byte sectors */ + uint32_t size_max; /* max segment size (if VIRTIO_BLK_F_SIZE_MAX) */ + uint32_t seg_max; /* max number of segments (if VIRTIO_BLK_F_SEG_MAX) */ - struct virtio_blk_geometry { - u16 cylinders; - u8 heads; - u8 sectors; + struct VirtioBlkGeometry { + uint16_t cylinders; + uint8_t heads; + uint8_t sectors; } geometry; /* (if VIRTIO_BLK_F_GEOMETRY) */ - u32 blk_size; /* block size of device (if VIRTIO_BLK_F_BLK_SIZE) */ + uint32_t blk_size; /* block size of device (if VIRTIO_BLK_F_BLK_SIZE) */ /* the next 4 entries are guarded by VIRTIO_BLK_F_TOPOLOGY */ - u8 physical_block_exp; /* exponent for physical block per logical block */ - u8 alignment_offset; /* alignment offset in logical blocks */ - u16 min_io_size; /* min I/O size without performance penalty + uint8_t physical_block_exp; /* exponent for physical blk per logical blk */ + uint8_t alignment_offset; /* alignment offset in logical blocks */ + uint16_t min_io_size; /* min I/O size without performance penalty in logical blocks */ - u32 opt_io_size; /* optimal sustained I/O size in logical blocks */ + uint32_t opt_io_size; /* optimal sustained I/O size in logical blks */ + + uint8_t wce; /* writeback mode (if VIRTIO_BLK_F_CONFIG_WCE) */ +} __attribute__((packed)); +typedef struct VirtioBlkConfig VirtioBlkConfig; - u8 wce; /* writeback mode (if VIRTIO_BLK_F_CONFIG_WCE) */ -} __attribute__((packed)) VirtioBlkConfig; +enum guessed_disk_nature_type { + VIRTIO_GDN_NONE = 0, + VIRTIO_GDN_DASD = 1, + VIRTIO_GDN_CDROM = 2, + VIRTIO_GDN_SCSI = 3, +}; +typedef enum guessed_disk_nature_type VirtioGDN; -bool virtio_guessed_disk_nature(void); +VirtioGDN virtio_guessed_disk_nature(void); void virtio_assume_scsi(void); void virtio_assume_eckd(void); void virtio_assume_iso9660(void); @@ -199,10 +225,68 @@ extern uint64_t virtio_get_blocks(void); extern int virtio_read_many(ulong sector, void *load_addr, int sec_num); #define VIRTIO_SECTOR_SIZE 512 +#define VIRTIO_ISO_BLOCK_SIZE 2048 +#define VIRTIO_SCSI_BLOCK_SIZE 512 static inline ulong virtio_sector_adjust(ulong sector) { return sector * (virtio_get_block_size() / VIRTIO_SECTOR_SIZE); } +struct VirtioScsiConfig { + uint32_t num_queues; + uint32_t seg_max; + uint32_t max_sectors; + uint32_t cmd_per_lun; + uint32_t event_info_size; + uint32_t sense_size; + uint32_t cdb_size; + uint16_t max_channel; + uint16_t max_target; + uint32_t max_lun; +} __attribute__((packed)); +typedef struct VirtioScsiConfig VirtioScsiConfig; + +struct ScsiDevice { + uint16_t channel; /* Always 0 in QEMU */ + uint16_t target; /* will be scanned over */ + uint32_t lun; /* will be reported */ +}; +typedef struct ScsiDevice ScsiDevice; + +struct VDev { + int nr_vqs; + VRing *vrings; + int cmd_vr_idx; + void *ring_area; + long wait_reply_timeout; + VirtioGDN guessed_disk_nature; + SubChannelId schid; + SenseId senseid; + union { + VirtioBlkConfig blk; + VirtioScsiConfig scsi; + } config; + ScsiDevice *scsi_device; + bool is_cdrom; + int scsi_block_size; + int blk_factor; + uint64_t scsi_last_block; + uint32_t scsi_dev_cyls; + uint8_t scsi_dev_heads; +}; +typedef struct VDev VDev; + +VDev *virtio_get_device(void); +VirtioDevType virtio_get_device_type(void); + +struct VirtioCmd { + void *data; + int size; + int flags; +}; +typedef struct VirtioCmd VirtioCmd; + +int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd); + #endif /* VIRTIO_H */ diff --git a/qapi-schema.json b/qapi-schema.json index 88f9b81c12..7f8d799bde 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -3082,6 +3082,7 @@ # # 'unmapped' and 'pause' since 2.0 # 'ro' and 'kp_comma' since 2.4 +# 'kp_equals' and 'power' since 2.6 ## { 'enum': 'QKeyCode', 'data': [ 'unmapped', @@ -3100,7 +3101,7 @@ 'left', 'up', 'down', 'right', 'insert', 'delete', 'stop', 'again', 'props', 'undo', 'front', 'copy', 'open', 'paste', 'find', 'cut', 'lf', 'help', 'meta_l', 'meta_r', 'compose', 'pause', 'ro', - 'kp_comma' ] } + 'kp_comma', 'kp_equals', 'power' ] } ## # @KeyValue diff --git a/qemu-doc.texi b/qemu-doc.texi index bc9dd13cc9..79141d3582 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -1262,13 +1262,18 @@ basic example. @subsection Inter-VM Shared Memory device -With KVM enabled on a Linux host, a shared memory device is available. Guests -map a POSIX shared memory region into the guest as a PCI device that enables -zero-copy communication to the application level of the guests. The basic -syntax is: +On Linux hosts, a shared memory device is available. The basic syntax +is: @example -qemu-system-i386 -device ivshmem,size=@var{size},shm=@var{shm-name} +qemu-system-x86_64 -device ivshmem-plain,memdev=@var{hostmem} +@end example + +where @var{hostmem} names a host memory backend. For a POSIX shared +memory backend, use something like + +@example +-object memory-backend-file,size=1M,share,mem-path=/dev/shm/ivshmem,id=@var{hostmem} @end example If desired, interrupts can be sent between guest VMs accessing the same shared @@ -1282,28 +1287,24 @@ memory server is: ivshmem-server -p @var{pidfile} -S @var{path} -m @var{shm-name} -l @var{shm-size} -n @var{vectors} # Then start your qemu instances with matching arguments -qemu-system-i386 -device ivshmem,size=@var{shm-size},vectors=@var{vectors},chardev=@var{id} - [,msi=on][,ioeventfd=on][,role=peer|master] +qemu-system-x86_64 -device ivshmem-doorbell,vectors=@var{vectors},chardev=@var{id} -chardev socket,path=@var{path},id=@var{id} @end example When using the server, the guest will be assigned a VM ID (>=0) that allows guests using the same server to communicate via interrupts. Guests can read their -VM ID from a device register (see example code). Since receiving the shared -memory region from the server is asynchronous, there is a (small) chance the -guest may boot before the shared memory is attached. To allow an application -to ensure shared memory is attached, the VM ID register will return -1 (an -invalid VM ID) until the memory is attached. Once the shared memory is -attached, the VM ID will return the guest's valid VM ID. With these semantics, -the guest application can check to ensure the shared memory is attached to the -guest before proceeding. - -The @option{role} argument can be set to either master or peer and will affect -how the shared memory is migrated. With @option{role=master}, the guest will -copy the shared memory on migration to the destination host. With -@option{role=peer}, the guest will not be able to migrate with the device attached. -With the @option{peer} case, the device should be detached and then reattached -after migration using the PCI hotplug support. +VM ID from a device register (see ivshmem-spec.txt). + +@subsubsection Migration with ivshmem + +With device property @option{master=on}, the guest will copy the shared +memory on migration to the destination host. With @option{master=off}, +the guest will not be able to migrate with the device attached. In the +latter case, the device should be detached and then reattached after +migration using the PCI hotplug support. + +At most one of the devices sharing the same memory can be master. The +master must complete migration before you plug back the other devices. @subsubsection ivshmem and hugepages @@ -1311,8 +1312,8 @@ Instead of specifying the <shm size> using POSIX shm, you may specify a memory backend that has hugepage support: @example -qemu-system-i386 -object memory-backend-file,size=1G,mem-path=/mnt/hugepages/my-shmem-file,id=mb1 - -device ivshmem,x-memdev=mb1 +qemu-system-x86_64 -object memory-backend-file,size=1G,mem-path=/dev/hugepages/my-shmem-file,share,id=mb1 + -device ivshmem-plain,memdev=mb1 @end example ivshmem-server also supports hugepages mount points with the diff --git a/qemu-options.hx b/qemu-options.hx index c8bb70cf7c..68f9a2a750 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1226,15 +1226,6 @@ STEXI Set the initial graphical resolution and depth (PPC, SPARC only). ETEXI -DEF("input-linux", 1, QEMU_OPTION_input_linux, - "-input-linux <evdev>\n" - " Use input device.\n", QEMU_ARCH_ALL) -STEXI -@item -input-linux @var{dev} -@findex -input-linux -Use input device. -ETEXI - DEF("vnc", HAS_ARG, QEMU_OPTION_vnc , "-vnc display start a VNC server on display\n", QEMU_ARCH_ALL) STEXI diff --git a/qga/main.c b/qga/main.c index f1cbd2a74c..c552782101 100644 --- a/qga/main.c +++ b/qga/main.c @@ -618,13 +618,7 @@ static gboolean channel_event_cb(GIOCondition condition, gpointer data) GAState *s = data; gchar buf[QGA_READ_COUNT_DEFAULT+1]; gsize count; - GError *err = NULL; GIOStatus status = ga_channel_read(s->channel, buf, QGA_READ_COUNT_DEFAULT, &count); - if (err != NULL) { - g_warning("error reading channel: %s", err->message); - g_error_free(err); - return false; - } switch (status) { case G_IO_STATUS_ERROR: g_warning("error reading channel"); diff --git a/scripts/clean-includes b/scripts/clean-includes index 8052bbd3b4..72b47f17f9 100755 --- a/scripts/clean-includes +++ b/scripts/clean-includes @@ -140,7 +140,7 @@ for f in "$@"; do perl -n -i -e 'print if !/\s*#\s*include\s*(["<][^>"]*[">])/ || ! (grep { $_ eq $1 } qw ( "config-host.h" "config-target.h" "qemu/compiler.h" - <stdarg.h> <stddef.h> <stdbool.h> <stdint.h> <sys/types.h> + <setjmp.h> <stdarg.h> <stddef.h> <stdbool.h> <stdint.h> <sys/types.h> <stdlib.h> <stdio.h> <string.h> <strings.h> <inttypes.h> <limits.h> <unistd.h> <time.h> <ctype.h> <errno.h> <fcntl.h> <sys/stat.h> <sys/time.h> <assert.h> <signal.h> diff --git a/target-mips/cpu.h b/target-mips/cpu.h index 1e2b070cc3..4f3ebb9dbb 100644 --- a/target-mips/cpu.h +++ b/target-mips/cpu.h @@ -99,6 +99,7 @@ struct CPUMIPSFPUContext { uint32_t fcr0; #define FCR0_FREP 29 #define FCR0_UFRP 28 +#define FCR0_HAS2008 23 #define FCR0_F64 22 #define FCR0_L 21 #define FCR0_W 20 @@ -110,6 +111,8 @@ struct CPUMIPSFPUContext { #define FCR0_REV 0 /* fcsr */ uint32_t fcr31; +#define FCR31_ABS2008 19 +#define FCR31_NAN2008 18 #define SET_FP_COND(num,env) do { ((env).fcr31) |= ((num) ? (1 << ((num) + 24)) : (1 << 23)); } while(0) #define CLEAR_FP_COND(num,env) do { ((env).fcr31) &= ~((num) ? (1 << ((num) + 24)) : (1 << 23)); } while(0) #define GET_FP_COND(env) ((((env).fcr31 >> 24) & 0xfe) | (((env).fcr31 >> 23) & 0x1)) diff --git a/target-mips/translate.c b/target-mips/translate.c index 12ed8208d0..0f43bf4758 100644 --- a/target-mips/translate.c +++ b/target-mips/translate.c @@ -20012,6 +20012,7 @@ void cpu_state_reset(CPUMIPSState *env) env->CP0_PageGrain_rw_bitmask = env->cpu_model->CP0_PageGrain_rw_bitmask; env->CP0_PageGrain = env->cpu_model->CP0_PageGrain; env->active_fpu.fcr0 = env->cpu_model->CP1_fcr0; + env->active_fpu.fcr31 = env->cpu_model->CP1_fcr31; env->msair = env->cpu_model->MSAIR; env->insn_flags = env->cpu_model->insn_flags; diff --git a/target-mips/translate_init.c b/target-mips/translate_init.c index cdef59d952..3192db0960 100644 --- a/target-mips/translate_init.c +++ b/target-mips/translate_init.c @@ -84,6 +84,7 @@ struct mips_def_t { int32_t CP0_TCStatus_rw_bitmask; int32_t CP0_SRSCtl; int32_t CP1_fcr0; + int32_t CP1_fcr31; int32_t MSAIR; int32_t SEGBITS; int32_t PABITS; @@ -421,9 +422,10 @@ static const mips_def_t mips_defs[] = .CP0_Status_rw_bitmask = 0x3C68FF1F, .CP0_PageGrain_rw_bitmask = (1U << CP0PG_RIE) | (1 << CP0PG_XIE) | (1 << CP0PG_ELPA) | (1 << CP0PG_IEC), - .CP1_fcr0 = (1 << FCR0_FREP) | (1 << FCR0_UFRP) | (1 << FCR0_F64) | - (1 << FCR0_L) | (1 << FCR0_W) | (1 << FCR0_D) | - (1 << FCR0_S) | (0x03 << FCR0_PRID), + .CP1_fcr0 = (1 << FCR0_FREP) | (1 << FCR0_UFRP) | (1 << FCR0_HAS2008) | + (1 << FCR0_F64) | (1 << FCR0_L) | (1 << FCR0_W) | + (1 << FCR0_D) | (1 << FCR0_S) | (0x03 << FCR0_PRID), + .CP1_fcr31 = (1 << FCR31_ABS2008) | (1 << FCR31_NAN2008), .SEGBITS = 32, .PABITS = 40, .insn_flags = CPU_MIPS32R5 | ASE_MSA, @@ -458,9 +460,10 @@ static const mips_def_t mips_defs[] = .CP0_PageGrain = (1 << CP0PG_IEC) | (1 << CP0PG_XIE) | (1U << CP0PG_RIE), .CP0_PageGrain_rw_bitmask = 0, - .CP1_fcr0 = (1 << FCR0_FREP) | (1 << FCR0_F64) | (1 << FCR0_L) | - (1 << FCR0_W) | (1 << FCR0_D) | (1 << FCR0_S) | - (0x00 << FCR0_PRID) | (0x0 << FCR0_REV), + .CP1_fcr0 = (1 << FCR0_FREP) | (1 << FCR0_HAS2008) | (1 << FCR0_F64) | + (1 << FCR0_L) | (1 << FCR0_W) | (1 << FCR0_D) | + (1 << FCR0_S) | (0x00 << FCR0_PRID) | (0x0 << FCR0_REV), + .CP1_fcr31 = (1 << FCR31_ABS2008) | (1 << FCR31_NAN2008), .SEGBITS = 32, .PABITS = 32, .insn_flags = CPU_MIPS32R6 | ASE_MICROMIPS, @@ -677,9 +680,10 @@ static const mips_def_t mips_defs[] = .CP0_PageGrain = (1 << CP0PG_IEC) | (1 << CP0PG_XIE) | (1U << CP0PG_RIE), .CP0_PageGrain_rw_bitmask = (1 << CP0PG_ELPA), - .CP1_fcr0 = (1 << FCR0_FREP) | (1 << FCR0_F64) | (1 << FCR0_L) | - (1 << FCR0_W) | (1 << FCR0_D) | (1 << FCR0_S) | - (0x00 << FCR0_PRID) | (0x0 << FCR0_REV), + .CP1_fcr0 = (1 << FCR0_FREP) | (1 << FCR0_HAS2008) | (1 << FCR0_F64) | + (1 << FCR0_L) | (1 << FCR0_W) | (1 << FCR0_D) | + (1 << FCR0_S) | (0x00 << FCR0_PRID) | (0x0 << FCR0_REV), + .CP1_fcr31 = (1 << FCR31_ABS2008) | (1 << FCR31_NAN2008), .SEGBITS = 48, .PABITS = 48, .insn_flags = CPU_MIPS64R6 | ASE_MSA, diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 8d90d862de..676081e69d 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -474,9 +474,17 @@ struct ppc_slb_t { #define MSR_RI 1 /* Recoverable interrupt 1 */ #define MSR_LE 0 /* Little-endian mode 1 hflags */ -#define LPCR_ILE (1 << (63-38)) -#define LPCR_AIL_SHIFT (63-40) /* Alternate interrupt location */ -#define LPCR_AIL (3 << LPCR_AIL_SHIFT) +/* LPCR bits */ +#define LPCR_VPM0 (1ull << (63 - 0)) +#define LPCR_VPM1 (1ull << (63 - 1)) +#define LPCR_ISL (1ull << (63 - 2)) +#define LPCR_KBV (1ull << (63 - 3)) +#define LPCR_ILE (1ull << (63 - 38)) +#define LPCR_MER (1ull << (63 - 52)) +#define LPCR_LPES0 (1ull << (63 - 60)) +#define LPCR_LPES1 (1ull << (63 - 61)) +#define LPCR_AIL_SHIFT (63 - 40) /* Alternate interrupt location */ +#define LPCR_AIL (3ull << LPCR_AIL_SHIFT) #define msr_sf ((env->msr >> MSR_SF) & 1) #define msr_isf ((env->msr >> MSR_ISF) & 1) @@ -1260,6 +1268,7 @@ void store_booke_tcr (CPUPPCState *env, target_ulong val); void store_booke_tsr (CPUPPCState *env, target_ulong val); void ppc_tlb_invalidate_all (CPUPPCState *env); void ppc_tlb_invalidate_one (CPUPPCState *env, target_ulong addr); +void cpu_ppc_set_papr(PowerPCCPU *cpu); #endif #endif @@ -1346,11 +1355,14 @@ static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch) #define SPR_SRR1 (0x01B) #define SPR_CFAR (0x01C) #define SPR_AMR (0x01D) +#define SPR_ACOP (0x01F) #define SPR_BOOKE_PID (0x030) +#define SPR_BOOKS_PID (0x030) #define SPR_BOOKE_DECAR (0x036) #define SPR_BOOKE_CSRR0 (0x03A) #define SPR_BOOKE_CSRR1 (0x03B) #define SPR_BOOKE_DEAR (0x03D) +#define SPR_IAMR (0x03D) #define SPR_BOOKE_ESR (0x03E) #define SPR_BOOKE_IVPR (0x03F) #define SPR_MPC_EIE (0x050) @@ -1381,6 +1393,11 @@ static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch) #define SPR_MPC_ICTRL (0x09E) #define SPR_MPC_BAR (0x09F) #define SPR_PSPB (0x09F) +#define SPR_DAWR (0x0B4) +#define SPR_RPR (0x0BA) +#define SPR_CIABR (0x0BB) +#define SPR_DAWRX (0x0BC) +#define SPR_HFSCR (0x0BE) #define SPR_VRSAVE (0x100) #define SPR_USPRG0 (0x100) #define SPR_USPRG1 (0x101) @@ -1435,19 +1452,25 @@ static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch) #define SPR_HSRR1 (0x13B) #define SPR_BOOKE_IAC4 (0x13B) #define SPR_BOOKE_DAC1 (0x13C) -#define SPR_LPIDR (0x13D) +#define SPR_MMCRH (0x13C) #define SPR_DABR2 (0x13D) #define SPR_BOOKE_DAC2 (0x13D) +#define SPR_TFMR (0x13D) #define SPR_BOOKE_DVC1 (0x13E) #define SPR_LPCR (0x13E) #define SPR_BOOKE_DVC2 (0x13F) +#define SPR_LPIDR (0x13F) #define SPR_BOOKE_TSR (0x150) +#define SPR_HMER (0x150) +#define SPR_HMEER (0x151) #define SPR_PCR (0x152) +#define SPR_BOOKE_LPIDR (0x152) #define SPR_BOOKE_TCR (0x154) #define SPR_BOOKE_TLB0PS (0x158) #define SPR_BOOKE_TLB1PS (0x159) #define SPR_BOOKE_TLB2PS (0x15A) #define SPR_BOOKE_TLB3PS (0x15B) +#define SPR_AMOR (0x15D) #define SPR_BOOKE_MAS7_MAS3 (0x174) #define SPR_BOOKE_IVOR0 (0x190) #define SPR_BOOKE_IVOR1 (0x191) @@ -1666,7 +1689,9 @@ static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch) #define SPR_MPC_MD_DBRAM1 (0x32A) #define SPR_RCPU_L2U_RA3 (0x32B) #define SPR_TAR (0x32F) +#define SPR_IC (0x350) #define SPR_VTB (0x351) +#define SPR_MMCRC (0x353) #define SPR_440_INV0 (0x370) #define SPR_440_INV1 (0x371) #define SPR_440_INV2 (0x372) @@ -1683,6 +1708,7 @@ static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch) #define SPR_POWER_SPMC1 (0x37C) #define SPR_POWER_SPMC2 (0x37D) #define SPR_POWER_MMCRS (0x37E) +#define SPR_WORT (0x37F) #define SPR_PPR (0x380) #define SPR_750_GQR0 (0x390) #define SPR_440_DNV0 (0x390) @@ -1705,6 +1731,7 @@ static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch) #define SPR_440_DVLIM (0x398) #define SPR_750_WPAR (0x399) #define SPR_440_IVLIM (0x399) +#define SPR_TSCR (0x399) #define SPR_750_DMAU (0x39A) #define SPR_750_DMAL (0x39B) #define SPR_440_RSTCFG (0x39B) @@ -1879,9 +1906,10 @@ static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch) #define L1CSR1_ICE 0x00000001 /* Instruction Cache Enable */ /* HID0 bits */ -#define HID0_DEEPNAP (1 << 24) -#define HID0_DOZE (1 << 23) -#define HID0_NAP (1 << 22) +#define HID0_DEEPNAP (1 << 24) /* pre-2.06 */ +#define HID0_DOZE (1 << 23) /* pre-2.06 */ +#define HID0_NAP (1 << 22) /* pre-2.06 */ +#define HID0_HILE (1ull << (63 - 19)) /* POWER8 */ /*****************************************************************************/ /* PowerPC Instructions types definitions */ @@ -2230,6 +2258,25 @@ enum { PCR_TM_DIS = 1ull << (63-2), /* Trans. memory disable (POWER8) */ }; +/* HMER/HMEER */ +enum { + HMER_MALFUNCTION_ALERT = 1ull << (63 - 0), + HMER_PROC_RECV_DONE = 1ull << (63 - 2), + HMER_PROC_RECV_ERROR_MASKED = 1ull << (63 - 3), + HMER_TFAC_ERROR = 1ull << (63 - 4), + HMER_TFMR_PARITY_ERROR = 1ull << (63 - 5), + HMER_XSCOM_FAIL = 1ull << (63 - 8), + HMER_XSCOM_DONE = 1ull << (63 - 9), + HMER_PROC_RECV_AGAIN = 1ull << (63 - 11), + HMER_WARN_RISE = 1ull << (63 - 14), + HMER_WARN_FALL = 1ull << (63 - 15), + HMER_SCOM_FIR_HMI = 1ull << (63 - 16), + HMER_TRIG_FIR_HMI = 1ull << (63 - 17), + HMER_HYP_RESOURCE_ERR = 1ull << (63 - 20), + HMER_XSCOM_STATUS_MASK = 7ull << (63 - 23), + HMER_XSCOM_STATUS_LSH = (63 - 23), +}; + /*****************************************************************************/ static inline target_ulong cpu_read_xer(CPUPPCState *env) diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c index 159ec69790..c4c81467e4 100644 --- a/target-ppc/kvm.c +++ b/target-ppc/kvm.c @@ -334,6 +334,12 @@ static long gethugepagesize(const char *mem_path) return fs.f_bsize; } +/* + * FIXME TOCTTOU: this iterates over memory backends' mem-path, which + * may or may not name the same files / on the same filesystem now as + * when we actually open and map them. Iterate over the file + * descriptors instead, and use qemu_fd_getpagesize(). + */ static int find_max_supported_pagesize(Object *obj, void *opaque) { char *mem_path; @@ -2002,7 +2008,7 @@ int kvmppc_get_hypercall(CPUPPCState *env, uint8_t *buf, int buf_len) hc[2] = cpu_to_be32(0x48000008); hc[3] = cpu_to_be32(bswap32(0x3860ffff)); - return 0; + return 1; } static inline int kvmppc_enable_hcall(KVMState *s, target_ulong hcall) diff --git a/target-ppc/translate.c b/target-ppc/translate.c index e402ff9203..6f0e7b4fac 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -4282,14 +4282,17 @@ static inline void gen_op_mfspr(DisasContext *ctx) void (*read_cb)(DisasContext *ctx, int gprn, int sprn); uint32_t sprn = SPR(ctx->opcode); -#if !defined(CONFIG_USER_ONLY) - if (ctx->hv) +#if defined(CONFIG_USER_ONLY) + read_cb = ctx->spr_cb[sprn].uea_read; +#else + if (ctx->pr) { + read_cb = ctx->spr_cb[sprn].uea_read; + } else if (ctx->hv) { read_cb = ctx->spr_cb[sprn].hea_read; - else if (!ctx->pr) + } else { read_cb = ctx->spr_cb[sprn].oea_read; - else + } #endif - read_cb = ctx->spr_cb[sprn].uea_read; if (likely(read_cb != NULL)) { if (likely(read_cb != SPR_NOACCESS)) { (*read_cb)(ctx, rD(ctx->opcode), sprn); @@ -4437,14 +4440,17 @@ static void gen_mtspr(DisasContext *ctx) void (*write_cb)(DisasContext *ctx, int sprn, int gprn); uint32_t sprn = SPR(ctx->opcode); -#if !defined(CONFIG_USER_ONLY) - if (ctx->hv) +#if defined(CONFIG_USER_ONLY) + write_cb = ctx->spr_cb[sprn].uea_write; +#else + if (ctx->pr) { + write_cb = ctx->spr_cb[sprn].uea_write; + } else if (ctx->hv) { write_cb = ctx->spr_cb[sprn].hea_write; - else if (!ctx->pr) + } else { write_cb = ctx->spr_cb[sprn].oea_write; - else + } #endif - write_cb = ctx->spr_cb[sprn].uea_write; if (likely(write_cb != NULL)) { if (likely(write_cb != SPR_NOACCESS)) { (*write_cb)(ctx, sprn, rS(ctx->opcode)); diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index fb206aff29..0a33597f6b 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -579,17 +579,33 @@ static inline void vscr_init (CPUPPCState *env, uint32_t val) #define spr_register_kvm(env, num, name, uea_read, uea_write, \ oea_read, oea_write, one_reg_id, initial_value) \ _spr_register(env, num, name, uea_read, uea_write, initial_value) +#define spr_register_kvm_hv(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, hea_read, hea_write, \ + one_reg_id, initial_value) \ + _spr_register(env, num, name, uea_read, uea_write, initial_value) #else #if !defined(CONFIG_KVM) #define spr_register_kvm(env, num, name, uea_read, uea_write, \ - oea_read, oea_write, one_reg_id, initial_value) \ + oea_read, oea_write, one_reg_id, initial_value) \ + _spr_register(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, oea_read, oea_write, initial_value) +#define spr_register_kvm_hv(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, hea_read, hea_write, \ + one_reg_id, initial_value) \ _spr_register(env, num, name, uea_read, uea_write, \ - oea_read, oea_write, initial_value) + oea_read, oea_write, hea_read, hea_write, initial_value) #else #define spr_register_kvm(env, num, name, uea_read, uea_write, \ - oea_read, oea_write, one_reg_id, initial_value) \ + oea_read, oea_write, one_reg_id, initial_value) \ + _spr_register(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, oea_read, oea_write, \ + one_reg_id, initial_value) +#define spr_register_kvm_hv(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, hea_read, hea_write, \ + one_reg_id, initial_value) \ _spr_register(env, num, name, uea_read, uea_write, \ - oea_read, oea_write, one_reg_id, initial_value) + oea_read, oea_write, hea_read, hea_write, \ + one_reg_id, initial_value) #endif #endif @@ -598,6 +614,13 @@ static inline void vscr_init (CPUPPCState *env, uint32_t val) spr_register_kvm(env, num, name, uea_read, uea_write, \ oea_read, oea_write, 0, initial_value) +#define spr_register_hv(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, hea_read, hea_write, \ + initial_value) \ + spr_register_kvm_hv(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, hea_read, hea_write, \ + 0, initial_value) + static inline void _spr_register(CPUPPCState *env, int num, const char *name, void (*uea_read)(DisasContext *ctx, int gprn, int sprn), @@ -606,6 +629,8 @@ static inline void _spr_register(CPUPPCState *env, int num, void (*oea_read)(DisasContext *ctx, int gprn, int sprn), void (*oea_write)(DisasContext *ctx, int sprn, int gprn), + void (*hea_read)(DisasContext *opaque, int gprn, int sprn), + void (*hea_write)(DisasContext *opaque, int sprn, int gprn), #endif #if defined(CONFIG_KVM) uint64_t one_reg_id, @@ -633,6 +658,8 @@ static inline void _spr_register(CPUPPCState *env, int num, #if !defined(CONFIG_USER_ONLY) spr->oea_read = oea_read; spr->oea_write = oea_write; + spr->hea_read = hea_read; + spr->hea_write = hea_write; #endif #if defined(CONFIG_KVM) spr->one_reg_id = one_reg_id, @@ -1036,30 +1063,102 @@ static void gen_spr_7xx (CPUPPCState *env) #ifdef TARGET_PPC64 #ifndef CONFIG_USER_ONLY -static void spr_read_uamr (DisasContext *ctx, int gprn, int sprn) +static void spr_write_amr(DisasContext *ctx, int sprn, int gprn) { - gen_load_spr(cpu_gpr[gprn], SPR_AMR); - spr_load_dump_spr(SPR_AMR); -} + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); -static void spr_write_uamr (DisasContext *ctx, int sprn, int gprn) -{ - gen_store_spr(SPR_AMR, cpu_gpr[gprn]); + /* Note, the HV=1 PR=0 case is handled earlier by simply using + * spr_write_generic for HV mode in the SPR table + */ + + /* Build insertion mask into t1 based on context */ + if (ctx->pr) { + gen_load_spr(t1, SPR_UAMOR); + } else { + gen_load_spr(t1, SPR_AMOR); + } + + /* Mask new bits into t2 */ + tcg_gen_and_tl(t2, t1, cpu_gpr[gprn]); + + /* Load AMR and clear new bits in t0 */ + gen_load_spr(t0, SPR_AMR); + tcg_gen_andc_tl(t0, t0, t1); + + /* Or'in new bits and write it out */ + tcg_gen_or_tl(t0, t0, t2); + gen_store_spr(SPR_AMR, t0); spr_store_dump_spr(SPR_AMR); + + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(t2); } -static void spr_write_uamr_pr (DisasContext *ctx, int sprn, int gprn) +static void spr_write_uamor(DisasContext *ctx, int sprn, int gprn) { TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + + /* Note, the HV=1 case is handled earlier by simply using + * spr_write_generic for HV mode in the SPR table + */ + /* Build insertion mask into t1 based on context */ + gen_load_spr(t1, SPR_AMOR); + + /* Mask new bits into t2 */ + tcg_gen_and_tl(t2, t1, cpu_gpr[gprn]); + + /* Load AMR and clear new bits in t0 */ gen_load_spr(t0, SPR_UAMOR); - tcg_gen_and_tl(t0, t0, cpu_gpr[gprn]); - gen_store_spr(SPR_AMR, t0); - spr_store_dump_spr(SPR_AMR); + tcg_gen_andc_tl(t0, t0, t1); + + /* Or'in new bits and write it out */ + tcg_gen_or_tl(t0, t0, t2); + gen_store_spr(SPR_UAMOR, t0); + spr_store_dump_spr(SPR_UAMOR); + + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(t2); +} + +static void spr_write_iamr(DisasContext *ctx, int sprn, int gprn) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + + /* Note, the HV=1 case is handled earlier by simply using + * spr_write_generic for HV mode in the SPR table + */ + + /* Build insertion mask into t1 based on context */ + gen_load_spr(t1, SPR_AMOR); + + /* Mask new bits into t2 */ + tcg_gen_and_tl(t2, t1, cpu_gpr[gprn]); + + /* Load AMR and clear new bits in t0 */ + gen_load_spr(t0, SPR_IAMR); + tcg_gen_andc_tl(t0, t0, t1); + + /* Or'in new bits and write it out */ + tcg_gen_or_tl(t0, t0, t2); + gen_store_spr(SPR_IAMR, t0); + spr_store_dump_spr(SPR_IAMR); + + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(t2); } #endif /* CONFIG_USER_ONLY */ -static void gen_spr_amr (CPUPPCState *env) +static void gen_spr_amr(CPUPPCState *env, bool has_iamr) { #ifndef CONFIG_USER_ONLY /* Virtual Page Class Key protection */ @@ -1067,17 +1166,31 @@ static void gen_spr_amr (CPUPPCState *env) * userspace accessible, 29 is privileged. So we only need to set * the kvm ONE_REG id on one of them, we use 29 */ spr_register(env, SPR_UAMR, "UAMR", - &spr_read_uamr, &spr_write_uamr_pr, - &spr_read_uamr, &spr_write_uamr, + &spr_read_generic, &spr_write_amr, + &spr_read_generic, &spr_write_amr, 0); - spr_register_kvm(env, SPR_AMR, "AMR", + spr_register_kvm_hv(env, SPR_AMR, "AMR", SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_amr, &spr_read_generic, &spr_write_generic, KVM_REG_PPC_AMR, 0); - spr_register_kvm(env, SPR_UAMOR, "UAMOR", + spr_register_kvm_hv(env, SPR_UAMOR, "UAMOR", SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_uamor, &spr_read_generic, &spr_write_generic, KVM_REG_PPC_UAMOR, 0); + spr_register_hv(env, SPR_AMOR, "AMOR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0); + if (has_iamr) { + spr_register_kvm_hv(env, SPR_IAMR, "IAMR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_iamr, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_IAMR, 0); + } #endif /* !CONFIG_USER_ONLY */ } #endif /* TARGET_PPC64 */ @@ -7464,6 +7577,25 @@ static void gen_spr_book3s_dbg(CPUPPCState *env) KVM_REG_PPC_DABRX, 0x00000000); } +static void gen_spr_book3s_207_dbg(CPUPPCState *env) +{ + spr_register_kvm_hv(env, SPR_DAWR, "DAWR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_DAWR, 0x00000000); + spr_register_kvm_hv(env, SPR_DAWRX, "DAWRX", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_DAWRX, 0x00000000); + spr_register_kvm_hv(env, SPR_CIABR, "CIABR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_CIABR, 0x00000000); +} + static void gen_spr_970_dbg(CPUPPCState *env) { /* Breakpoints */ @@ -7878,6 +8010,36 @@ static void gen_spr_power8_pspb(CPUPPCState *env) KVM_REG_PPC_PSPB, 0); } +static void gen_spr_power8_ic(CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + spr_register_hv(env, SPR_IC, "IC", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0); +#endif +} + +static void gen_spr_power8_book4(CPUPPCState *env) +{ + /* Add a number of P8 book4 registers */ +#if !defined(CONFIG_USER_ONLY) + spr_register_kvm(env, SPR_ACOP, "ACOP", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_ACOP, 0); + spr_register_kvm(env, SPR_BOOKS_PID, "PID", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_PID, 0); + spr_register_kvm(env, SPR_WORT, "WORT", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_WORT, 0); +#endif +} + static void init_proc_book3s_64(CPUPPCState *env, int version) { gen_spr_ne_601(env); @@ -7899,7 +8061,7 @@ static void init_proc_book3s_64(CPUPPCState *env, int version) case BOOK3S_CPU_POWER7: case BOOK3S_CPU_POWER8: gen_spr_book3s_ids(env); - gen_spr_amr(env); + gen_spr_amr(env, version >= BOOK3S_CPU_POWER8); gen_spr_book3s_purr(env); env->ci_large_pages = true; break; @@ -7930,9 +8092,13 @@ static void init_proc_book3s_64(CPUPPCState *env, int version) gen_spr_power8_tm(env); gen_spr_power8_pspb(env); gen_spr_vtb(env); + gen_spr_power8_ic(env); + gen_spr_power8_book4(env); } if (version < BOOK3S_CPU_POWER8) { gen_spr_book3s_dbg(env); + } else { + gen_spr_book3s_207_dbg(env); } #if !defined(CONFIG_USER_ONLY) switch (version) { @@ -8332,8 +8498,33 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) pcc->l1_icache_size = 0x8000; pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr; } -#endif /* defined (TARGET_PPC64) */ +#if !defined(CONFIG_USER_ONLY) + +void cpu_ppc_set_papr(PowerPCCPU *cpu) +{ + CPUPPCState *env = &cpu->env; + ppc_spr_t *amor = &env->spr_cb[SPR_AMOR]; + + /* PAPR always has exception vectors in RAM not ROM. To ensure this, + * MSR[IP] should never be set. + * + * We also disallow setting of MSR_HV + */ + env->msr_mask &= ~((1ull << MSR_EP) | MSR_HVB); + + /* Set a full AMOR so guest can use the AMR as it sees fit */ + env->spr[SPR_AMOR] = amor->default_value = 0xffffffffffffffffull; + + /* Tell KVM that we're in PAPR mode */ + if (kvm_enabled()) { + kvmppc_set_papr(cpu); + } +} + +#endif /* !defined(CONFIG_USER_ONLY) */ + +#endif /* defined (TARGET_PPC64) */ /*****************************************************************************/ /* Generic CPU instantiation routine */ @@ -9703,7 +9894,7 @@ static void ppc_cpu_reset(CPUState *s) #if defined(TARGET_PPC64) if (env->mmu_model & POWERPC_MMU_64) { - env->msr |= (1ULL << MSR_SF); + msr |= (1ULL << MSR_SF); } #endif diff --git a/target-tricore/Makefile.objs b/target-tricore/Makefile.objs index 21e820d8f9..7a05670718 100644 --- a/target-tricore/Makefile.objs +++ b/target-tricore/Makefile.objs @@ -1 +1 @@ -obj-y += translate.o helper.o cpu.o op_helper.o +obj-y += translate.o helper.o cpu.o op_helper.o fpu_helper.o diff --git a/target-tricore/cpu.h b/target-tricore/cpu.h index 5fee376674..90045a93d2 100644 --- a/target-tricore/cpu.h +++ b/target-tricore/cpu.h @@ -183,8 +183,7 @@ struct CPUTriCoreState { uint32_t M2CNT; uint32_t M3CNT; /* Floating Point Registers */ - /* XXX: */ - + float_status fp_status; /* QEMU */ int error_code; uint32_t hflags; /* CPU State */ @@ -217,6 +216,7 @@ struct CPUTriCoreState { #define MASK_PSW_GW 0x00000100 #define MASK_PSW_CDE 0x00000080 #define MASK_PSW_CDC 0x0000007f +#define MASK_PSW_FPU_RM 0x3000000 #define MASK_SYSCON_PRO_TEN 0x2 #define MASK_SYSCON_FCD_SF 0x1 @@ -339,6 +339,8 @@ enum { uint32_t psw_read(CPUTriCoreState *env); void psw_write(CPUTriCoreState *env, uint32_t val); +void fpu_set_state(CPUTriCoreState *env); + #include "cpu-qom.h" #define MMU_USER_IDX 2 diff --git a/target-tricore/fpu_helper.c b/target-tricore/fpu_helper.c new file mode 100644 index 0000000000..98fe9472b1 --- /dev/null +++ b/target-tricore/fpu_helper.c @@ -0,0 +1,217 @@ +/* + * TriCore emulation for qemu: fpu helper. + * + * Copyright (c) 2016 Bastian Koppelmann University of Paderborn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/helper-proto.h" + +#define ADD_NAN 0x7cf00001 +#define DIV_NAN 0x7fc00008 +#define MUL_NAN 0x7fc00002 +#define FPU_FS PSW_USB_C +#define FPU_FI PSW_USB_V +#define FPU_FV PSW_USB_SV +#define FPU_FZ PSW_USB_AV +#define FPU_FU PSW_USB_SAV + +/* we don't care about input_denormal */ +static inline uint8_t f_get_excp_flags(CPUTriCoreState *env) +{ + return get_float_exception_flags(&env->fp_status) + & (float_flag_invalid + | float_flag_overflow + | float_flag_underflow + | float_flag_output_denormal + | float_flag_divbyzero + | float_flag_inexact); +} + +static inline bool f_is_denormal(float32 arg) +{ + return float32_is_zero_or_denormal(arg) && !float32_is_zero(arg); +} + +static void f_update_psw_flags(CPUTriCoreState *env, uint8_t flags) +{ + uint8_t some_excp = 0; + set_float_exception_flags(0, &env->fp_status); + + if (flags & float_flag_invalid) { + env->FPU_FI = 1 << 31; + some_excp = 1; + } + + if (flags & float_flag_overflow) { + env->FPU_FV = 1 << 31; + some_excp = 1; + } + + if (flags & float_flag_underflow || flags & float_flag_output_denormal) { + env->FPU_FU = 1 << 31; + some_excp = 1; + } + + if (flags & float_flag_divbyzero) { + env->FPU_FZ = 1 << 31; + some_excp = 1; + } + + if (flags & float_flag_inexact || flags & float_flag_output_denormal) { + env->PSW |= 1 << 26; + some_excp = 1; + } + + env->FPU_FS = some_excp; +} + +#define FADD_SUB(op) \ +uint32_t helper_f##op(CPUTriCoreState *env, uint32_t r1, uint32_t r2) \ +{ \ + float32 arg1 = make_float32(r1); \ + float32 arg2 = make_float32(r2); \ + uint32_t flags; \ + float32 f_result; \ + \ + f_result = float32_##op(arg2, arg1, &env->fp_status); \ + flags = f_get_excp_flags(env); \ + if (flags) { \ + /* If the output is a NaN, but the inputs aren't, \ + we return a unique value. */ \ + if ((flags & float_flag_invalid) \ + && !float32_is_any_nan(arg1) \ + && !float32_is_any_nan(arg2)) { \ + f_result = ADD_NAN; \ + } \ + f_update_psw_flags(env, flags); \ + } else { \ + env->FPU_FS = 0; \ + } \ + return (uint32_t)f_result; \ +} +FADD_SUB(add) +FADD_SUB(sub) + +uint32_t helper_fmul(CPUTriCoreState *env, uint32_t r1, uint32_t r2) +{ + uint32_t flags; + float32 arg1 = make_float32(r1); + float32 arg2 = make_float32(r2); + float32 f_result; + + f_result = float32_mul(arg1, arg2, &env->fp_status); + + flags = f_get_excp_flags(env); + if (flags) { + /* If the output is a NaN, but the inputs aren't, + we return a unique value. */ + if ((flags & float_flag_invalid) + && !float32_is_any_nan(arg1) + && !float32_is_any_nan(arg2)) { + f_result = MUL_NAN; + } + f_update_psw_flags(env, flags); + } else { + env->FPU_FS = 0; + } + return (uint32_t)f_result; + +} + +uint32_t helper_fdiv(CPUTriCoreState *env, uint32_t r1, uint32_t r2) +{ + uint32_t flags; + float32 arg1 = make_float32(r1); + float32 arg2 = make_float32(r2); + float32 f_result; + + f_result = float32_div(arg1, arg2 , &env->fp_status); + + flags = f_get_excp_flags(env); + if (flags) { + /* If the output is a NaN, but the inputs aren't, + we return a unique value. */ + if ((flags & float_flag_invalid) + && !float32_is_any_nan(arg1) + && !float32_is_any_nan(arg2)) { + f_result = DIV_NAN; + } + f_update_psw_flags(env, flags); + } else { + env->FPU_FS = 0; + } + + return (uint32_t)f_result; +} + +uint32_t helper_fcmp(CPUTriCoreState *env, uint32_t r1, uint32_t r2) +{ + uint32_t result, flags; + float32 arg1 = make_float32(r1); + float32 arg2 = make_float32(r2); + + set_flush_inputs_to_zero(0, &env->fp_status); + + result = 1 << (float32_compare_quiet(arg1, arg2, &env->fp_status) + 1); + result |= f_is_denormal(arg1) << 4; + result |= f_is_denormal(arg2) << 5; + + flags = f_get_excp_flags(env); + if (flags) { + f_update_psw_flags(env, flags); + } else { + env->FPU_FS = 0; + } + + set_flush_inputs_to_zero(1, &env->fp_status); + return result; +} + +uint32_t helper_ftoi(CPUTriCoreState *env, uint32_t arg) +{ + float32 f_arg = make_float32(arg); + int32_t result, flags; + + result = float32_to_int32(f_arg, &env->fp_status); + + flags = f_get_excp_flags(env); + if (flags) { + if (float32_is_any_nan(f_arg)) { + result = 0; + } + f_update_psw_flags(env, flags); + } else { + env->FPU_FS = 0; + } + return (uint32_t)result; +} + +uint32_t helper_itof(CPUTriCoreState *env, uint32_t arg) +{ + float32 f_result; + uint32_t flags; + f_result = int32_to_float32(arg, &env->fp_status); + + flags = f_get_excp_flags(env); + if (flags) { + f_update_psw_flags(env, flags); + } else { + env->FPU_FS = 0; + } + return (uint32_t)f_result; +} diff --git a/target-tricore/helper.c b/target-tricore/helper.c index 7d96daddb1..71b31cdb9b 100644 --- a/target-tricore/helper.c +++ b/target-tricore/helper.c @@ -110,10 +110,18 @@ void tricore_cpu_list(FILE *f, fprintf_function cpu_fprintf) g_slist_free(list); } +void fpu_set_state(CPUTriCoreState *env) +{ + set_float_rounding_mode(env->PSW & MASK_PSW_FPU_RM, &env->fp_status); + set_flush_inputs_to_zero(1, &env->fp_status); + set_flush_to_zero(1, &env->fp_status); + set_default_nan_mode(1, &env->fp_status); +} + uint32_t psw_read(CPUTriCoreState *env) { /* clear all USB bits */ - env->PSW &= 0xffffff; + env->PSW &= 0x6ffffff; /* now set them from the cache */ env->PSW |= ((env->PSW_USB_C != 0) << 31); env->PSW |= ((env->PSW_USB_V & (1 << 31)) >> 1); @@ -132,4 +140,6 @@ void psw_write(CPUTriCoreState *env, uint32_t val) env->PSW_USB_AV = (val & MASK_USB_AV) << 3; env->PSW_USB_SAV = (val & MASK_USB_SAV) << 4; env->PSW = val; + + fpu_set_state(env); } diff --git a/target-tricore/helper.h b/target-tricore/helper.h index 2c8ed78940..9333e161ab 100644 --- a/target-tricore/helper.h +++ b/target-tricore/helper.h @@ -105,6 +105,13 @@ DEF_HELPER_FLAGS_1(parity, TCG_CALL_NO_RWG_SE, i32, i32) /* float */ DEF_HELPER_FLAGS_4(pack, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32, i32) DEF_HELPER_1(unpack, i64, i32) +DEF_HELPER_3(fadd, i32, env, i32, i32) +DEF_HELPER_3(fsub, i32, env, i32, i32) +DEF_HELPER_3(fmul, i32, env, i32, i32) +DEF_HELPER_3(fdiv, i32, env, i32, i32) +DEF_HELPER_3(fcmp, i32, env, i32, i32) +DEF_HELPER_2(ftoi, i32, env, i32) +DEF_HELPER_2(itof, i32, env, i32) /* dvinit */ DEF_HELPER_3(dvinit_b_13, i64, env, i32, i32) DEF_HELPER_3(dvinit_b_131, i64, env, i32, i32) diff --git a/target-tricore/op_helper.c b/target-tricore/op_helper.c index 55f6724da8..40656c357c 100644 --- a/target-tricore/op_helper.c +++ b/target-tricore/op_helper.c @@ -1045,6 +1045,8 @@ uint64_t helper_msub64_q_ssov(CPUTriCoreState *env, uint64_t r1, uint32_t r2, } else { result = INT64_MIN; } + } else { + env->PSW_USB_V = 0; } } else { if (ovf < 0) { diff --git a/target-tricore/translate.c b/target-tricore/translate.c index d13e5c8c62..912bf226be 100644 --- a/target-tricore/translate.c +++ b/target-tricore/translate.c @@ -6672,6 +6672,21 @@ static void decode_rr_divide(CPUTriCoreState *env, DisasContext *ctx) generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } break; + case OPC2_32_RR_MUL_F: + gen_helper_fmul(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_DIV_F: + gen_helper_fdiv(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_CMP_F: + gen_helper_fcmp(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1], cpu_gpr_d[r2]); + break; + case OPC2_32_RR_FTOI: + gen_helper_ftoi(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1]); + break; + case OPC2_32_RR_ITOF: + gen_helper_itof(cpu_gpr_d[r3], cpu_env, cpu_gpr_d[r1]); + break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } @@ -7013,48 +7028,60 @@ static void decode_rrr_divide(CPUTriCoreState *env, DisasContext *ctx) r3 = MASK_OP_RRR_S3(ctx->opcode); r4 = MASK_OP_RRR_D(ctx->opcode); - CHECK_REG_PAIR(r3); - switch (op2) { case OPC2_32_RRR_DVADJ: + CHECK_REG_PAIR(r3); CHECK_REG_PAIR(r4); GEN_HELPER_RRR(dvadj, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; case OPC2_32_RRR_DVSTEP: + CHECK_REG_PAIR(r3); CHECK_REG_PAIR(r4); GEN_HELPER_RRR(dvstep, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; case OPC2_32_RRR_DVSTEP_U: + CHECK_REG_PAIR(r3); CHECK_REG_PAIR(r4); GEN_HELPER_RRR(dvstep_u, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; case OPC2_32_RRR_IXMAX: + CHECK_REG_PAIR(r3); CHECK_REG_PAIR(r4); GEN_HELPER_RRR(ixmax, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; case OPC2_32_RRR_IXMAX_U: + CHECK_REG_PAIR(r3); CHECK_REG_PAIR(r4); GEN_HELPER_RRR(ixmax_u, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; case OPC2_32_RRR_IXMIN: + CHECK_REG_PAIR(r3); CHECK_REG_PAIR(r4); GEN_HELPER_RRR(ixmin, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; case OPC2_32_RRR_IXMIN_U: + CHECK_REG_PAIR(r3); CHECK_REG_PAIR(r4); GEN_HELPER_RRR(ixmin_u, cpu_gpr_d[r4], cpu_gpr_d[r4+1], cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r2]); break; case OPC2_32_RRR_PACK: + CHECK_REG_PAIR(r3); gen_helper_pack(cpu_gpr_d[r4], cpu_PSW_C, cpu_gpr_d[r3], cpu_gpr_d[r3+1], cpu_gpr_d[r1]); break; + case OPC2_32_RRR_ADD_F: + gen_helper_fadd(cpu_gpr_d[r4], cpu_env, cpu_gpr_d[r1], cpu_gpr_d[r3]); + break; + case OPC2_32_RRR_SUB_F: + gen_helper_fsub(cpu_gpr_d[r4], cpu_env, cpu_gpr_d[r1], cpu_gpr_d[r3]); + break; default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } @@ -8632,6 +8659,7 @@ static void decode_32Bit_opc(CPUTriCoreState *env, DisasContext *ctx) break; case OPCM_32_RRR_DIVIDE: decode_rrr_divide(env, ctx); + break; /* RRR2 Format */ case OPCM_32_RRR2_MADD: decode_rrr2_madd(env, ctx); @@ -8661,6 +8689,7 @@ static void decode_32Bit_opc(CPUTriCoreState *env, DisasContext *ctx) /* RRRR format */ case OPCM_32_RRRR_EXTRACT_INSERT: decode_rrrr_extract_insert(env, ctx); + break; /* RRRW format */ case OPCM_32_RRRW_EXTRACT_INSERT: decode_rrrw_extract_insert(env, ctx); @@ -8771,6 +8800,7 @@ void cpu_state_reset(CPUTriCoreState *env) { /* Reset Regs to Default Value */ env->PSW = 0xb80; + fpu_set_state(env); } static void tricore_tcg_init_csfr(void) diff --git a/target-tricore/tricore-opcodes.h b/target-tricore/tricore-opcodes.h index 1bfed0ce48..df666b081f 100644 --- a/target-tricore/tricore-opcodes.h +++ b/target-tricore/tricore-opcodes.h @@ -1126,6 +1126,20 @@ enum { OPC2_32_RR_CRC32 = 0x03, OPC2_32_RR_DIV = 0x20, OPC2_32_RR_DIV_U = 0x21, + OPC2_32_RR_MUL_F = 0x04, + OPC2_32_RR_DIV_F = 0x05, + OPC2_32_RR_FTOI = 0x10, + OPC2_32_RR_ITOF = 0x14, + OPC2_32_RR_CMP_F = 0x00, + OPC2_32_RR_FTOIZ = 0x13, + OPC2_32_RR_FTOQ31 = 0x11, + OPC2_32_RR_FTOQ31Z = 0x18, + OPC2_32_RR_FTOU = 0x12, + OPC2_32_RR_FTOUZ = 0x17, + OPC2_32_RR_Q31TOF = 0x15, + OPC2_32_RR_QSEED_F = 0x19, + OPC2_32_RR_UPDFL = 0x0c, + OPC2_32_RR_UTOF = 0x16, }; /* OPCM_32_RR_IDIRECT */ enum { @@ -1209,6 +1223,10 @@ enum { OPC2_32_RRR_IXMIN = 0x08, OPC2_32_RRR_IXMIN_U = 0x09, OPC2_32_RRR_PACK = 0x00, + OPC2_32_RRR_ADD_F = 0x02, + OPC2_32_RRR_SUB_F = 0x03, + OPC2_32_RRR_MADD_F = 0x06, + OPC2_32_RRR_MSUB_F = 0x07, }; /* * RRR1 Format diff --git a/tests/Makefile b/tests/Makefile index d27e31d2b7..ab185d8647 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -168,7 +168,7 @@ gcov-files-pci-y += hw/display/virtio-gpu-pci.c gcov-files-pci-$(CONFIG_VIRTIO_VGA) += hw/display/virtio-vga.c check-qtest-pci-y += tests/intel-hda-test$(EXESUF) gcov-files-pci-y += hw/audio/intel-hda.c hw/audio/hda-codec.c -check-qtest-pci-$(CONFIG_POSIX) += tests/ivshmem-test$(EXESUF) +check-qtest-pci-$(CONFIG_EVENTFD) += tests/ivshmem-test$(EXESUF) gcov-files-pci-y += hw/misc/ivshmem.c check-qtest-i386-y = tests/endianness-test$(EXESUF) diff --git a/tests/ivshmem-test.c b/tests/ivshmem-test.c index e184c67a1d..c027ff1e09 100644 --- a/tests/ivshmem-test.c +++ b/tests/ivshmem-test.c @@ -110,25 +110,26 @@ static void setup_vm_cmd(IVState *s, const char *cmd, bool msix) s->pcibus = qpci_init_pc(); s->dev = get_device(s->pcibus); - /* FIXME: other bar order fails, mappings changes */ - s->mem_base = qpci_iomap(s->dev, 2, &barsize); - g_assert_nonnull(s->mem_base); - g_assert_cmpuint(barsize, ==, TMPSHMSIZE); + s->reg_base = qpci_iomap(s->dev, 0, &barsize); + g_assert_nonnull(s->reg_base); + g_assert_cmpuint(barsize, ==, 256); if (msix) { qpci_msix_enable(s->dev); } - s->reg_base = qpci_iomap(s->dev, 0, &barsize); - g_assert_nonnull(s->reg_base); - g_assert_cmpuint(barsize, ==, 256); + s->mem_base = qpci_iomap(s->dev, 2, &barsize); + g_assert_nonnull(s->mem_base); + g_assert_cmpuint(barsize, ==, TMPSHMSIZE); qpci_device_enable(s->dev); } static void setup_vm(IVState *s) { - char *cmd = g_strdup_printf("-device ivshmem,shm=%s,size=1M", tmpshm); + char *cmd = g_strdup_printf("-object memory-backend-file" + ",id=mb1,size=1M,share,mem-path=/dev/shm%s" + " -device ivshmem-plain,memdev=mb1", tmpshm); setup_vm_cmd(s, cmd, false); @@ -144,32 +145,41 @@ static void test_ivshmem_single(void) setup_vm(&state); s = &state; - /* valid io */ - out_reg(s, INTRMASK, 0); - in_reg(s, INTRSTATUS); - in_reg(s, IVPOSITION); + /* initial state of readable registers */ + g_assert_cmpuint(in_reg(s, INTRMASK), ==, 0); + g_assert_cmpuint(in_reg(s, INTRSTATUS), ==, 0); + g_assert_cmpuint(in_reg(s, IVPOSITION), ==, 0); + /* trigger interrupt via registers */ out_reg(s, INTRMASK, 0xffffffff); g_assert_cmpuint(in_reg(s, INTRMASK), ==, 0xffffffff); out_reg(s, INTRSTATUS, 1); - /* XXX: intercept IRQ, not seen in resp */ + /* check interrupt status */ g_assert_cmpuint(in_reg(s, INTRSTATUS), ==, 1); + /* reading clears */ + g_assert_cmpuint(in_reg(s, INTRSTATUS), ==, 0); + /* TODO intercept actual interrupt (needs qtest work) */ - /* invalid io */ + /* invalid register access */ out_reg(s, IVPOSITION, 1); + in_reg(s, DOORBELL); + + /* ring the (non-functional) doorbell */ out_reg(s, DOORBELL, 8 << 16); + /* write shared memory */ for (i = 0; i < G_N_ELEMENTS(data); i++) { data[i] = i; } qtest_memwrite(s->qtest, (uintptr_t)s->mem_base, data, sizeof(data)); + /* verify write */ for (i = 0; i < G_N_ELEMENTS(data); i++) { g_assert_cmpuint(((uint32_t *)tmpshmem)[i], ==, i); } + /* read it back and verify read */ memset(data, 0, sizeof(data)); - qtest_memread(s->qtest, (uintptr_t)s->mem_base, data, sizeof(data)); for (i = 0; i < G_N_ELEMENTS(data); i++) { g_assert_cmpuint(data[i], ==, i); @@ -276,8 +286,10 @@ static void *server_thread(void *data) static void setup_vm_with_server(IVState *s, int nvectors, bool msi) { char *cmd = g_strdup_printf("-chardev socket,id=chr0,path=%s,nowait " - "-device ivshmem,size=1M,chardev=chr0,vectors=%d,msi=%s", - tmpserver, nvectors, msi ? "true" : "false"); + "-device ivshmem%s,chardev=chr0,vectors=%d", + tmpserver, + msi ? "-doorbell" : ",size=1M,msi=off", + nvectors); setup_vm_cmd(s, cmd, msi); @@ -293,8 +305,7 @@ static void test_ivshmem_server(bool msi) int nvectors = 2; guint64 end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; - memset(tmpshmem, 0x42, TMPSHMSIZE); - ret = ivshmem_server_init(&server, tmpserver, tmpshm, + ret = ivshmem_server_init(&server, tmpserver, tmpshm, true, TMPSHMSIZE, nvectors, g_test_verbose()); g_assert_cmpint(ret, ==, 0); @@ -302,49 +313,39 @@ static void test_ivshmem_server(bool msi) ret = ivshmem_server_start(&server); g_assert_cmpint(ret, ==, 0); - setup_vm_with_server(&state1, nvectors, msi); - s1 = &state1; - setup_vm_with_server(&state2, nvectors, msi); - s2 = &state2; - - g_assert_cmpuint(in_reg(s1, IVPOSITION), ==, 0xffffffff); - g_assert_cmpuint(in_reg(s2, IVPOSITION), ==, 0xffffffff); - - g_assert_cmpuint(qtest_readb(s1->qtest, (uintptr_t)s1->mem_base), ==, 0x00); - thread.server = &server; ret = pipe(thread.pipe); g_assert_cmpint(ret, ==, 0); thread.thread = g_thread_new("ivshmem-server", server_thread, &thread); g_assert(thread.thread != NULL); - /* waiting until mapping is done */ - while (g_get_monotonic_time() < end_time) { - g_usleep(1000); - - if (qtest_readb(s1->qtest, (uintptr_t)s1->mem_base) == 0x42 && - qtest_readb(s2->qtest, (uintptr_t)s2->mem_base) == 0x42) { - break; - } - } + setup_vm_with_server(&state1, nvectors, msi); + s1 = &state1; + setup_vm_with_server(&state2, nvectors, msi); + s2 = &state2; /* check got different VM ids */ vm1 = in_reg(s1, IVPOSITION); vm2 = in_reg(s2, IVPOSITION); - g_assert_cmpuint(vm1, !=, vm2); + g_assert_cmpint(vm1, >=, 0); + g_assert_cmpint(vm2, >=, 0); + g_assert_cmpint(vm1, !=, vm2); + /* check number of MSI-X vectors */ global_qtest = s1->qtest; if (msi) { ret = qpci_msix_table_size(s1->dev); g_assert_cmpuint(ret, ==, nvectors); } - /* ping vm2 -> vm1 */ + /* TODO test behavior before MSI-X is enabled */ + + /* ping vm2 -> vm1 on vector 0 */ if (msi) { ret = qpci_msix_pending(s1->dev, 0); g_assert_cmpuint(ret, ==, 0); } else { - out_reg(s1, INTRSTATUS, 0); + g_assert_cmpuint(in_reg(s1, INTRSTATUS), ==, 0); } out_reg(s2, DOORBELL, vm1 << 16); do { @@ -353,18 +354,18 @@ static void test_ivshmem_server(bool msi) } while (ret == 0 && g_get_monotonic_time() < end_time); g_assert_cmpuint(ret, !=, 0); - /* ping vm1 -> vm2 */ + /* ping vm1 -> vm2 on vector 1 */ global_qtest = s2->qtest; if (msi) { - ret = qpci_msix_pending(s2->dev, 0); + ret = qpci_msix_pending(s2->dev, 1); g_assert_cmpuint(ret, ==, 0); } else { - out_reg(s2, INTRSTATUS, 0); + g_assert_cmpuint(in_reg(s2, INTRSTATUS), ==, 0); } - out_reg(s1, DOORBELL, vm2 << 16); + out_reg(s1, DOORBELL, vm2 << 16 | 1); do { g_usleep(10000); - ret = msi ? qpci_msix_pending(s2->dev, 0) : in_reg(s2, INTRSTATUS); + ret = msi ? qpci_msix_pending(s2->dev, 1) : in_reg(s2, INTRSTATUS); } while (ret == 0 && g_get_monotonic_time() < end_time); g_assert_cmpuint(ret, !=, 0); @@ -415,7 +416,7 @@ static void test_ivshmem_memdev(void) /* just for the sake of checking memory-backend property */ setup_vm_cmd(&state, "-object memory-backend-ram,size=1M,id=mb1" - " -device ivshmem,x-memdev=mb1", false); + " -device ivshmem-plain,memdev=mb1", false); cleanup_vm(&state); } diff --git a/tests/libqos/pci-pc.c b/tests/libqos/pci-pc.c index 08167c09fe..77f15e5a0e 100644 --- a/tests/libqos/pci-pc.c +++ b/tests/libqos/pci-pc.c @@ -184,7 +184,9 @@ static void *qpci_pc_iomap(QPCIBus *bus, QPCIDevice *dev, int barno, uint64_t *s if (io_type == PCI_BASE_ADDRESS_SPACE_IO) { uint16_t loc; - g_assert((s->pci_iohole_alloc + size) <= s->pci_iohole_size); + g_assert(QEMU_ALIGN_UP(s->pci_iohole_alloc, size) + size + <= s->pci_iohole_size); + s->pci_iohole_alloc = QEMU_ALIGN_UP(s->pci_iohole_alloc, size); loc = s->pci_iohole_start + s->pci_iohole_alloc; s->pci_iohole_alloc += size; @@ -194,7 +196,9 @@ static void *qpci_pc_iomap(QPCIBus *bus, QPCIDevice *dev, int barno, uint64_t *s } else { uint64_t loc; - g_assert((s->pci_hole_alloc + size) <= s->pci_hole_size); + g_assert(QEMU_ALIGN_UP(s->pci_hole_alloc, size) + size + <= s->pci_hole_size); + s->pci_hole_alloc = QEMU_ALIGN_UP(s->pci_hole_alloc, size); loc = s->pci_hole_start + s->pci_hole_alloc; s->pci_hole_alloc += size; diff --git a/ui/cocoa.m b/ui/cocoa.m index 7063a025c0..691471493f 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -33,6 +33,7 @@ #include "sysemu/sysemu.h" #include "qmp-commands.h" #include "sysemu/blockdev.h" +#include <Carbon/Carbon.h> #ifndef MAC_OS_X_VERSION_10_5 #define MAC_OS_X_VERSION_10_5 1050 @@ -72,178 +73,139 @@ bool stretch_video; NSTextField *pauseLabel; NSArray * supportedImageFileTypes; -// keymap conversion -int keymap[] = -{ -// SdlI macI macH SdlH 104xtH 104xtC sdl - 30, // 0 0x00 0x1e A QZ_a - 31, // 1 0x01 0x1f S QZ_s - 32, // 2 0x02 0x20 D QZ_d - 33, // 3 0x03 0x21 F QZ_f - 35, // 4 0x04 0x23 H QZ_h - 34, // 5 0x05 0x22 G QZ_g - 44, // 6 0x06 0x2c Z QZ_z - 45, // 7 0x07 0x2d X QZ_x - 46, // 8 0x08 0x2e C QZ_c - 47, // 9 0x09 0x2f V QZ_v - 0, // 10 0x0A Undefined - 48, // 11 0x0B 0x30 B QZ_b - 16, // 12 0x0C 0x10 Q QZ_q - 17, // 13 0x0D 0x11 W QZ_w - 18, // 14 0x0E 0x12 E QZ_e - 19, // 15 0x0F 0x13 R QZ_r - 21, // 16 0x10 0x15 Y QZ_y - 20, // 17 0x11 0x14 T QZ_t - 2, // 18 0x12 0x02 1 QZ_1 - 3, // 19 0x13 0x03 2 QZ_2 - 4, // 20 0x14 0x04 3 QZ_3 - 5, // 21 0x15 0x05 4 QZ_4 - 7, // 22 0x16 0x07 6 QZ_6 - 6, // 23 0x17 0x06 5 QZ_5 - 13, // 24 0x18 0x0d = QZ_EQUALS - 10, // 25 0x19 0x0a 9 QZ_9 - 8, // 26 0x1A 0x08 7 QZ_7 - 12, // 27 0x1B 0x0c - QZ_MINUS - 9, // 28 0x1C 0x09 8 QZ_8 - 11, // 29 0x1D 0x0b 0 QZ_0 - 27, // 30 0x1E 0x1b ] QZ_RIGHTBRACKET - 24, // 31 0x1F 0x18 O QZ_o - 22, // 32 0x20 0x16 U QZ_u - 26, // 33 0x21 0x1a [ QZ_LEFTBRACKET - 23, // 34 0x22 0x17 I QZ_i - 25, // 35 0x23 0x19 P QZ_p - 28, // 36 0x24 0x1c ENTER QZ_RETURN - 38, // 37 0x25 0x26 L QZ_l - 36, // 38 0x26 0x24 J QZ_j - 40, // 39 0x27 0x28 ' QZ_QUOTE - 37, // 40 0x28 0x25 K QZ_k - 39, // 41 0x29 0x27 ; QZ_SEMICOLON - 43, // 42 0x2A 0x2b \ QZ_BACKSLASH - 51, // 43 0x2B 0x33 , QZ_COMMA - 53, // 44 0x2C 0x35 / QZ_SLASH - 49, // 45 0x2D 0x31 N QZ_n - 50, // 46 0x2E 0x32 M QZ_m - 52, // 47 0x2F 0x34 . QZ_PERIOD - 15, // 48 0x30 0x0f TAB QZ_TAB - 57, // 49 0x31 0x39 SPACE QZ_SPACE - 41, // 50 0x32 0x29 ` QZ_BACKQUOTE - 14, // 51 0x33 0x0e BKSP QZ_BACKSPACE - 0, // 52 0x34 Undefined - 1, // 53 0x35 0x01 ESC QZ_ESCAPE - 220, // 54 0x36 0xdc E0,5C R GUI QZ_RMETA - 219, // 55 0x37 0xdb E0,5B L GUI QZ_LMETA - 42, // 56 0x38 0x2a L SHFT QZ_LSHIFT - 58, // 57 0x39 0x3a CAPS QZ_CAPSLOCK - 56, // 58 0x3A 0x38 L ALT QZ_LALT - 29, // 59 0x3B 0x1d L CTRL QZ_LCTRL - 54, // 60 0x3C 0x36 R SHFT QZ_RSHIFT - 184,// 61 0x3D 0xb8 E0,38 R ALT QZ_RALT - 157,// 62 0x3E 0x9d E0,1D R CTRL QZ_RCTRL - 0, // 63 0x3F Undefined - 0, // 64 0x40 Undefined - 0, // 65 0x41 Undefined - 0, // 66 0x42 Undefined - 55, // 67 0x43 0x37 KP * QZ_KP_MULTIPLY - 0, // 68 0x44 Undefined - 78, // 69 0x45 0x4e KP + QZ_KP_PLUS - 0, // 70 0x46 Undefined - 69, // 71 0x47 0x45 NUM QZ_NUMLOCK - 0, // 72 0x48 Undefined - 0, // 73 0x49 Undefined - 0, // 74 0x4A Undefined - 181,// 75 0x4B 0xb5 E0,35 KP / QZ_KP_DIVIDE - 152,// 76 0x4C 0x9c E0,1C KP EN QZ_KP_ENTER - 0, // 77 0x4D undefined - 74, // 78 0x4E 0x4a KP - QZ_KP_MINUS - 0, // 79 0x4F Undefined - 0, // 80 0x50 Undefined - 0, // 81 0x51 QZ_KP_EQUALS - 82, // 82 0x52 0x52 KP 0 QZ_KP0 - 79, // 83 0x53 0x4f KP 1 QZ_KP1 - 80, // 84 0x54 0x50 KP 2 QZ_KP2 - 81, // 85 0x55 0x51 KP 3 QZ_KP3 - 75, // 86 0x56 0x4b KP 4 QZ_KP4 - 76, // 87 0x57 0x4c KP 5 QZ_KP5 - 77, // 88 0x58 0x4d KP 6 QZ_KP6 - 71, // 89 0x59 0x47 KP 7 QZ_KP7 - 0, // 90 0x5A Undefined - 72, // 91 0x5B 0x48 KP 8 QZ_KP8 - 73, // 92 0x5C 0x49 KP 9 QZ_KP9 - 0, // 93 0x5D Undefined - 0, // 94 0x5E Undefined - 0, // 95 0x5F Undefined - 63, // 96 0x60 0x3f F5 QZ_F5 - 64, // 97 0x61 0x40 F6 QZ_F6 - 65, // 98 0x62 0x41 F7 QZ_F7 - 61, // 99 0x63 0x3d F3 QZ_F3 - 66, // 100 0x64 0x42 F8 QZ_F8 - 67, // 101 0x65 0x43 F9 QZ_F9 - 0, // 102 0x66 Undefined - 87, // 103 0x67 0x57 F11 QZ_F11 - 0, // 104 0x68 Undefined - 183,// 105 0x69 0xb7 QZ_PRINT - 0, // 106 0x6A Undefined - 70, // 107 0x6B 0x46 SCROLL QZ_SCROLLOCK - 0, // 108 0x6C Undefined - 68, // 109 0x6D 0x44 F10 QZ_F10 - 0, // 110 0x6E Undefined - 88, // 111 0x6F 0x58 F12 QZ_F12 - 0, // 112 0x70 Undefined - 110,// 113 0x71 0x0 QZ_PAUSE - 210,// 114 0x72 0xd2 E0,52 INSERT QZ_INSERT - 199,// 115 0x73 0xc7 E0,47 HOME QZ_HOME - 201,// 116 0x74 0xc9 E0,49 PG UP QZ_PAGEUP - 211,// 117 0x75 0xd3 E0,53 DELETE QZ_DELETE - 62, // 118 0x76 0x3e F4 QZ_F4 - 207,// 119 0x77 0xcf E0,4f END QZ_END - 60, // 120 0x78 0x3c F2 QZ_F2 - 209,// 121 0x79 0xd1 E0,51 PG DN QZ_PAGEDOWN - 59, // 122 0x7A 0x3b F1 QZ_F1 - 203,// 123 0x7B 0xcb e0,4B L ARROW QZ_LEFT - 205,// 124 0x7C 0xcd e0,4D R ARROW QZ_RIGHT - 208,// 125 0x7D 0xd0 E0,50 D ARROW QZ_DOWN - 200,// 126 0x7E 0xc8 E0,48 U ARROW QZ_UP -/* completed according to http://www.libsdl.org/cgi/cvsweb.cgi/SDL12/src/video/quartz/SDL_QuartzKeys.h?rev=1.6&content-type=text/x-cvsweb-markup */ - -/* Additional 104 Key XP-Keyboard Scancodes from http://www.computer-engineering.org/ps2keyboard/scancodes1.html */ -/* - 221 // 0xdd e0,5d APPS - // E0,2A,E0,37 PRNT SCRN - // E1,1D,45,E1,9D,C5 PAUSE - 83 // 0x53 0x53 KP . -// ACPI Scan Codes - 222 // 0xde E0, 5E Power - 223 // 0xdf E0, 5F Sleep - 227 // 0xe3 E0, 63 Wake -// Windows Multimedia Scan Codes - 153 // 0x99 E0, 19 Next Track - 144 // 0x90 E0, 10 Previous Track - 164 // 0xa4 E0, 24 Stop - 162 // 0xa2 E0, 22 Play/Pause - 160 // 0xa0 E0, 20 Mute - 176 // 0xb0 E0, 30 Volume Up - 174 // 0xae E0, 2E Volume Down - 237 // 0xed E0, 6D Media Select - 236 // 0xec E0, 6C E-Mail - 161 // 0xa1 E0, 21 Calculator - 235 // 0xeb E0, 6B My Computer - 229 // 0xe5 E0, 65 WWW Search - 178 // 0xb2 E0, 32 WWW Home - 234 // 0xea E0, 6A WWW Back - 233 // 0xe9 E0, 69 WWW Forward - 232 // 0xe8 E0, 68 WWW Stop - 231 // 0xe7 E0, 67 WWW Refresh - 230 // 0xe6 E0, 66 WWW Favorites -*/ +// Mac to QKeyCode conversion +const int mac_to_qkeycode_map[] = { + [kVK_ANSI_A] = Q_KEY_CODE_A, + [kVK_ANSI_B] = Q_KEY_CODE_B, + [kVK_ANSI_C] = Q_KEY_CODE_C, + [kVK_ANSI_D] = Q_KEY_CODE_D, + [kVK_ANSI_E] = Q_KEY_CODE_E, + [kVK_ANSI_F] = Q_KEY_CODE_F, + [kVK_ANSI_G] = Q_KEY_CODE_G, + [kVK_ANSI_H] = Q_KEY_CODE_H, + [kVK_ANSI_I] = Q_KEY_CODE_I, + [kVK_ANSI_J] = Q_KEY_CODE_J, + [kVK_ANSI_K] = Q_KEY_CODE_K, + [kVK_ANSI_L] = Q_KEY_CODE_L, + [kVK_ANSI_M] = Q_KEY_CODE_M, + [kVK_ANSI_N] = Q_KEY_CODE_N, + [kVK_ANSI_O] = Q_KEY_CODE_O, + [kVK_ANSI_P] = Q_KEY_CODE_P, + [kVK_ANSI_Q] = Q_KEY_CODE_Q, + [kVK_ANSI_R] = Q_KEY_CODE_R, + [kVK_ANSI_S] = Q_KEY_CODE_S, + [kVK_ANSI_T] = Q_KEY_CODE_T, + [kVK_ANSI_U] = Q_KEY_CODE_U, + [kVK_ANSI_V] = Q_KEY_CODE_V, + [kVK_ANSI_W] = Q_KEY_CODE_W, + [kVK_ANSI_X] = Q_KEY_CODE_X, + [kVK_ANSI_Y] = Q_KEY_CODE_Y, + [kVK_ANSI_Z] = Q_KEY_CODE_Z, + + [kVK_ANSI_0] = Q_KEY_CODE_0, + [kVK_ANSI_1] = Q_KEY_CODE_1, + [kVK_ANSI_2] = Q_KEY_CODE_2, + [kVK_ANSI_3] = Q_KEY_CODE_3, + [kVK_ANSI_4] = Q_KEY_CODE_4, + [kVK_ANSI_5] = Q_KEY_CODE_5, + [kVK_ANSI_6] = Q_KEY_CODE_6, + [kVK_ANSI_7] = Q_KEY_CODE_7, + [kVK_ANSI_8] = Q_KEY_CODE_8, + [kVK_ANSI_9] = Q_KEY_CODE_9, + + [kVK_ANSI_Grave] = Q_KEY_CODE_GRAVE_ACCENT, + [kVK_ANSI_Minus] = Q_KEY_CODE_MINUS, + [kVK_ANSI_Equal] = Q_KEY_CODE_EQUAL, + [kVK_Delete] = Q_KEY_CODE_BACKSPACE, + [kVK_CapsLock] = Q_KEY_CODE_CAPS_LOCK, + [kVK_Tab] = Q_KEY_CODE_TAB, + [kVK_Return] = Q_KEY_CODE_RET, + [kVK_ANSI_LeftBracket] = Q_KEY_CODE_BRACKET_LEFT, + [kVK_ANSI_RightBracket] = Q_KEY_CODE_BRACKET_RIGHT, + [kVK_ANSI_Backslash] = Q_KEY_CODE_BACKSLASH, + [kVK_ANSI_Semicolon] = Q_KEY_CODE_SEMICOLON, + [kVK_ANSI_Quote] = Q_KEY_CODE_APOSTROPHE, + [kVK_ANSI_Comma] = Q_KEY_CODE_COMMA, + [kVK_ANSI_Period] = Q_KEY_CODE_DOT, + [kVK_ANSI_Slash] = Q_KEY_CODE_SLASH, + [kVK_Shift] = Q_KEY_CODE_SHIFT, + [kVK_RightShift] = Q_KEY_CODE_SHIFT_R, + [kVK_Control] = Q_KEY_CODE_CTRL, + [kVK_RightControl] = Q_KEY_CODE_CTRL_R, + [kVK_Option] = Q_KEY_CODE_ALT, + [kVK_RightOption] = Q_KEY_CODE_ALT_R, + [kVK_Command] = Q_KEY_CODE_META_L, + [0x36] = Q_KEY_CODE_META_R, /* There is no kVK_RightCommand */ + [kVK_Space] = Q_KEY_CODE_SPC, + + [kVK_ANSI_Keypad0] = Q_KEY_CODE_KP_0, + [kVK_ANSI_Keypad1] = Q_KEY_CODE_KP_1, + [kVK_ANSI_Keypad2] = Q_KEY_CODE_KP_2, + [kVK_ANSI_Keypad3] = Q_KEY_CODE_KP_3, + [kVK_ANSI_Keypad4] = Q_KEY_CODE_KP_4, + [kVK_ANSI_Keypad5] = Q_KEY_CODE_KP_5, + [kVK_ANSI_Keypad6] = Q_KEY_CODE_KP_6, + [kVK_ANSI_Keypad7] = Q_KEY_CODE_KP_7, + [kVK_ANSI_Keypad8] = Q_KEY_CODE_KP_8, + [kVK_ANSI_Keypad9] = Q_KEY_CODE_KP_9, + [kVK_ANSI_KeypadDecimal] = Q_KEY_CODE_KP_DECIMAL, + [kVK_ANSI_KeypadEnter] = Q_KEY_CODE_KP_ENTER, + [kVK_ANSI_KeypadPlus] = Q_KEY_CODE_KP_ADD, + [kVK_ANSI_KeypadMinus] = Q_KEY_CODE_KP_SUBTRACT, + [kVK_ANSI_KeypadMultiply] = Q_KEY_CODE_KP_MULTIPLY, + [kVK_ANSI_KeypadDivide] = Q_KEY_CODE_KP_DIVIDE, + [kVK_ANSI_KeypadEquals] = Q_KEY_CODE_KP_EQUALS, + [kVK_ANSI_KeypadClear] = Q_KEY_CODE_NUM_LOCK, + + [kVK_UpArrow] = Q_KEY_CODE_UP, + [kVK_DownArrow] = Q_KEY_CODE_DOWN, + [kVK_LeftArrow] = Q_KEY_CODE_LEFT, + [kVK_RightArrow] = Q_KEY_CODE_RIGHT, + + [kVK_Help] = Q_KEY_CODE_INSERT, + [kVK_Home] = Q_KEY_CODE_HOME, + [kVK_PageUp] = Q_KEY_CODE_PGUP, + [kVK_PageDown] = Q_KEY_CODE_PGDN, + [kVK_End] = Q_KEY_CODE_END, + [kVK_ForwardDelete] = Q_KEY_CODE_DELETE, + + [kVK_Escape] = Q_KEY_CODE_ESC, + + /* The Power key can't be used directly because the operating system uses + * it. This key can be emulated by using it in place of another key such as + * F1. Don't forget to disable the real key binding. + */ + /* [kVK_F1] = Q_KEY_CODE_POWER, */ + + [kVK_F1] = Q_KEY_CODE_F1, + [kVK_F2] = Q_KEY_CODE_F2, + [kVK_F3] = Q_KEY_CODE_F3, + [kVK_F4] = Q_KEY_CODE_F4, + [kVK_F5] = Q_KEY_CODE_F5, + [kVK_F6] = Q_KEY_CODE_F6, + [kVK_F7] = Q_KEY_CODE_F7, + [kVK_F8] = Q_KEY_CODE_F8, + [kVK_F9] = Q_KEY_CODE_F9, + [kVK_F10] = Q_KEY_CODE_F10, + [kVK_F11] = Q_KEY_CODE_F11, + [kVK_F12] = Q_KEY_CODE_F12, + [kVK_F13] = Q_KEY_CODE_PRINT, + [kVK_F14] = Q_KEY_CODE_SCROLL_LOCK, + [kVK_F15] = Q_KEY_CODE_PAUSE, + + /* + * The eject and volume keys can't be used here because they are handled at + * a lower level than what an Application can see. + */ }; static int cocoa_keycode_to_qemu(int keycode) { - if (ARRAY_SIZE(keymap) <= keycode) { + if (ARRAY_SIZE(mac_to_qkeycode_map) <= keycode) { fprintf(stderr, "(cocoa) warning unknown keycode 0x%x\n", keycode); return 0; } - return keymap[keycode]; + return mac_to_qkeycode_map[keycode]; } /* Displays an alert dialog box with the specified message */ @@ -557,21 +519,24 @@ QemuCocoaView *cocoaView; case NSFlagsChanged: keycode = cocoa_keycode_to_qemu([event keyCode]); - if ((keycode == 219 || keycode == 220) && !isMouseGrabbed) { + if ((keycode == Q_KEY_CODE_META_L || keycode == Q_KEY_CODE_META_R) + && !isMouseGrabbed) { /* Don't pass command key changes to guest unless mouse is grabbed */ keycode = 0; } if (keycode) { - if (keycode == 58 || keycode == 69) { // emulate caps lock and num lock keydown and keyup - qemu_input_event_send_key_number(dcl->con, keycode, true); - qemu_input_event_send_key_number(dcl->con, keycode, false); + // emulate caps lock and num lock keydown and keyup + if (keycode == Q_KEY_CODE_CAPS_LOCK || + keycode == Q_KEY_CODE_NUM_LOCK) { + qemu_input_event_send_key_qcode(dcl->con, keycode, true); + qemu_input_event_send_key_qcode(dcl->con, keycode, false); } else if (qemu_console_is_graphic(NULL)) { if (modifiers_state[keycode] == 0) { // keydown - qemu_input_event_send_key_number(dcl->con, keycode, true); + qemu_input_event_send_key_qcode(dcl->con, keycode, true); modifiers_state[keycode] = 1; } else { // keyup - qemu_input_event_send_key_number(dcl->con, keycode, false); + qemu_input_event_send_key_qcode(dcl->con, keycode, false); modifiers_state[keycode] = 0; } } @@ -598,14 +563,14 @@ QemuCocoaView *cocoaView; switch (keycode) { // enable graphic console - case 0x02 ... 0x0a: // '1' to '9' keys - console_select(keycode - 0x02); + case Q_KEY_CODE_1 ... Q_KEY_CODE_9: // '1' to '9' keys + console_select(keycode - 11); break; } // handle keys for graphic console } else if (qemu_console_is_graphic(NULL)) { - qemu_input_event_send_key_number(dcl->con, keycode, true); + qemu_input_event_send_key_qcode(dcl->con, keycode, true); // handlekeys for Monitor } else { @@ -653,7 +618,7 @@ QemuCocoaView *cocoaView; } if (qemu_console_is_graphic(NULL)) { - qemu_input_event_send_key_number(dcl->con, keycode, false); + qemu_input_event_send_key_qcode(dcl->con, keycode, false); } break; case NSMouseMoved: @@ -823,7 +788,7 @@ QemuCocoaView *cocoaView; for (index = 0; index < max_index; index++) { if (modifiers_state[index]) { modifiers_state[index] = 0; - qemu_input_event_send_key_number(dcl->con, index, false); + qemu_input_event_send_key_qcode(dcl->con, index, false); } } } @@ -858,6 +823,7 @@ QemuCocoaView *cocoaView; - (void)ejectDeviceMedia:(id)sender; - (void)changeDeviceMedia:(id)sender; - (BOOL)verifyQuit; +- (void)openDocumentation:(NSString *)filename; @end @implementation QemuCocoaAppController @@ -994,20 +960,42 @@ QemuCocoaView *cocoaView; [cocoaView toggleFullScreen:sender]; } +/* Tries to find then open the specified filename */ +- (void) openDocumentation: (NSString *) filename +{ + /* Where to look for local files */ + NSString *path_array[] = {@"../share/doc/qemu/", @"../doc/qemu/", @"../"}; + NSString *full_file_path; + + /* iterate thru the possible paths until the file is found */ + int index; + for (index = 0; index < ARRAY_SIZE(path_array); index++) { + full_file_path = [[NSBundle mainBundle] executablePath]; + full_file_path = [full_file_path stringByDeletingLastPathComponent]; + full_file_path = [NSString stringWithFormat: @"%@/%@%@", full_file_path, + path_array[index], filename]; + if ([[NSWorkspace sharedWorkspace] openFile: full_file_path] == YES) { + return; + } + } + + /* If none of the paths opened a file */ + NSBeep(); + QEMU_Alert(@"Failed to open file"); +} + - (void)showQEMUDoc:(id)sender { COCOA_DEBUG("QemuCocoaAppController: showQEMUDoc\n"); - [[NSWorkspace sharedWorkspace] openFile:[NSString stringWithFormat:@"%@/../doc/qemu/qemu-doc.html", - [[NSBundle mainBundle] resourcePath]] withApplication:@"Help Viewer"]; + [self openDocumentation: @"qemu-doc.html"]; } - (void)showQEMUTec:(id)sender { COCOA_DEBUG("QemuCocoaAppController: showQEMUTec\n"); - [[NSWorkspace sharedWorkspace] openFile:[NSString stringWithFormat:@"%@/../doc/qemu/qemu-tech.html", - [[NSBundle mainBundle] resourcePath]] withApplication:@"Help Viewer"]; + [self openDocumentation: @"qemu-tech.html"]; } /* Stretches video to fit host monitor size */ diff --git a/ui/input-linux.c b/ui/input-linux.c index 84c52d3292..9c921cc0ad 100644 --- a/ui/input-linux.c +++ b/ui/input-linux.c @@ -11,6 +11,7 @@ #include "qemu/sockets.h" #include "sysemu/sysemu.h" #include "ui/input.h" +#include "qom/object_interfaces.h" #include <sys/ioctl.h> #include "standard-headers/linux/input.h" @@ -128,10 +129,21 @@ static int qemu_input_linux_to_qcode(unsigned int lnx) return linux_to_qcode[lnx]; } +#define TYPE_INPUT_LINUX "input-linux" +#define INPUT_LINUX(obj) \ + OBJECT_CHECK(InputLinux, (obj), TYPE_INPUT_LINUX) +#define INPUT_LINUX_GET_CLASS(obj) \ + OBJECT_GET_CLASS(InputLinuxClass, (obj), TYPE_INPUT_LINUX) +#define INPUT_LINUX_CLASS(klass) \ + OBJECT_CLASS_CHECK(InputLinuxClass, (klass), TYPE_INPUT_LINUX) + typedef struct InputLinux InputLinux; +typedef struct InputLinuxClass InputLinuxClass; struct InputLinux { - const char *evdev; + Object parent; + + char *evdev; int fd; bool repeat; bool grab_request; @@ -140,9 +152,14 @@ struct InputLinux { bool keydown[KEY_CNT]; int keycount; int wheel; + bool initialized; QTAILQ_ENTRY(InputLinux) next; }; +struct InputLinuxClass { + ObjectClass parent_class; +}; + static QTAILQ_HEAD(, InputLinux) inputs = QTAILQ_HEAD_INITIALIZER(inputs); static void input_linux_toggle_grab(InputLinux *il) @@ -197,6 +214,13 @@ static void input_linux_event_keyboard(void *opaque) */ continue; } + if (event.code >= KEY_CNT) { + /* + * Should not happen. But better safe than sorry, + * and we make Coverity happy too. + */ + continue; + } /* keep track of key state */ if (!il->keydown[event.code] && event.value) { il->keydown[event.code] = true; @@ -310,25 +334,21 @@ static void input_linux_event_mouse(void *opaque) } } -int input_linux_init(void *opaque, QemuOpts *opts, Error **errp) +static void input_linux_complete(UserCreatable *uc, Error **errp) { - InputLinux *il = g_new0(InputLinux, 1); + InputLinux *il = INPUT_LINUX(uc); uint32_t evtmap; int rc, ver; - il->evdev = qemu_opt_get(opts, "evdev"); - il->grab_all = qemu_opt_get_bool(opts, "grab-all", false); - il->repeat = qemu_opt_get_bool(opts, "repeat", false); - if (!il->evdev) { error_setg(errp, "no input device specified"); - goto err_free; + return; } il->fd = open(il->evdev, O_RDWR); if (il->fd < 0) { error_setg_file_open(errp, errno, il->evdev); - goto err_free; + return; } qemu_set_nonblock(il->fd); @@ -357,36 +377,111 @@ int input_linux_init(void *opaque, QemuOpts *opts, Error **errp) } input_linux_toggle_grab(il); QTAILQ_INSERT_TAIL(&inputs, il, next); - return 0; + il->initialized = true; + return; err_close: close(il->fd); -err_free: - g_free(il); - return -1; + return; +} + +static void input_linux_instance_finalize(Object *obj) +{ + InputLinux *il = INPUT_LINUX(obj); + + if (il->initialized) { + QTAILQ_REMOVE(&inputs, il, next); + close(il->fd); + } + g_free(il->evdev); +} + +static char *input_linux_get_evdev(Object *obj, Error **errp) +{ + InputLinux *il = INPUT_LINUX(obj); + + return g_strdup(il->evdev); +} + +static void input_linux_set_evdev(Object *obj, const char *value, + Error **errp) +{ + InputLinux *il = INPUT_LINUX(obj); + + if (il->evdev) { + error_setg(errp, "evdev property already set"); + return; + } + il->evdev = g_strdup(value); } -static QemuOptsList qemu_input_linux_opts = { - .name = "input-linux", - .head = QTAILQ_HEAD_INITIALIZER(qemu_input_linux_opts.head), - .implied_opt_name = "evdev", - .desc = { - { - .name = "evdev", - .type = QEMU_OPT_STRING, - },{ - .name = "grab-all", - .type = QEMU_OPT_BOOL, - },{ - .name = "repeat", - .type = QEMU_OPT_BOOL, - }, - { /* end of list */ } - }, +static bool input_linux_get_grab_all(Object *obj, Error **errp) +{ + InputLinux *il = INPUT_LINUX(obj); + + return il->grab_all; +} + +static void input_linux_set_grab_all(Object *obj, bool value, + Error **errp) +{ + InputLinux *il = INPUT_LINUX(obj); + + il->grab_all = value; +} + +static bool input_linux_get_repeat(Object *obj, Error **errp) +{ + InputLinux *il = INPUT_LINUX(obj); + + return il->repeat; +} + +static void input_linux_set_repeat(Object *obj, bool value, + Error **errp) +{ + InputLinux *il = INPUT_LINUX(obj); + + il->repeat = value; +} + +static void input_linux_instance_init(Object *obj) +{ + object_property_add_str(obj, "evdev", + input_linux_get_evdev, + input_linux_set_evdev, NULL); + object_property_add_bool(obj, "grab_all", + input_linux_get_grab_all, + input_linux_set_grab_all, NULL); + object_property_add_bool(obj, "repeat", + input_linux_get_repeat, + input_linux_set_repeat, NULL); +} + +static void input_linux_class_init(ObjectClass *oc, void *data) +{ + UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); + + ucc->complete = input_linux_complete; +} + +static const TypeInfo input_linux_info = { + .name = TYPE_INPUT_LINUX, + .parent = TYPE_OBJECT, + .class_size = sizeof(InputLinuxClass), + .class_init = input_linux_class_init, + .instance_size = sizeof(InputLinux), + .instance_init = input_linux_instance_init, + .instance_finalize = input_linux_instance_finalize, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } }; -static void input_linux_register_config(void) +static void register_types(void) { - qemu_add_opts(&qemu_input_linux_opts); + type_register_static(&input_linux_info); } -opts_init(input_linux_register_config); + +type_init(register_types); diff --git a/ui/spice-core.c b/ui/spice-core.c index e1179258d0..61db3c18b3 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -828,6 +828,11 @@ void qemu_spice_init(void) #ifdef HAVE_SPICE_GL if (qemu_opt_get_bool(opts, "gl", 0)) { + if ((port != 0) || (tls_port != 0)) { + error_report("SPICE GL support is local-only for now and " + "incompatible with -spice port/tls-port"); + exit(1); + } if (egl_rendernode_init() == 0) { display_opengl = 1; } diff --git a/util/coroutine-sigaltstack.c b/util/coroutine-sigaltstack.c index 6b8aee7a2b..a7c3366553 100644 --- a/util/coroutine-sigaltstack.c +++ b/util/coroutine-sigaltstack.c @@ -26,7 +26,6 @@ #undef _FORTIFY_SOURCE #endif #include "qemu/osdep.h" -#include <setjmp.h> #include <pthread.h> #include "qemu-common.h" #include "qemu/coroutine_int.h" diff --git a/util/coroutine-ucontext.c b/util/coroutine-ucontext.c index 4914f60199..2bb7e10d4b 100644 --- a/util/coroutine-ucontext.c +++ b/util/coroutine-ucontext.c @@ -23,7 +23,6 @@ #undef _FORTIFY_SOURCE #endif #include "qemu/osdep.h" -#include <setjmp.h> #include <ucontext.h> #include "qemu-common.h" #include "qemu/coroutine_int.h" diff --git a/util/event_notifier-posix.c b/util/event_notifier-posix.c index b00189a754..e150301c33 100644 --- a/util/event_notifier-posix.c +++ b/util/event_notifier-posix.c @@ -21,11 +21,17 @@ #include <sys/eventfd.h> #endif +#ifdef CONFIG_EVENTFD +/* + * Initialize @e with existing file descriptor @fd. + * @fd must be a genuine eventfd object, emulation with pipe won't do. + */ void event_notifier_init_fd(EventNotifier *e, int fd) { e->rfd = fd; e->wfd = fd; } +#endif int event_notifier_init(EventNotifier *e, int active) { diff --git a/util/oslib-posix.c b/util/oslib-posix.c index 09082564c4..20ca141dec 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -50,7 +50,6 @@ #include "qemu/sockets.h" #include <sys/mman.h> #include <libgen.h> -#include <setjmp.h> #include <sys/signal.h> #include "qemu/cutils.h" @@ -3733,12 +3733,6 @@ int main(int argc, char **argv, char **envp) #endif break; } - case QEMU_OPTION_input_linux: - if (!qemu_opts_parse_noisily(qemu_find_opts("input-linux"), - optarg, true)) { - exit(1); - } - break; case QEMU_OPTION_no_acpi: acpi_enabled = 0; break; @@ -4602,10 +4596,6 @@ int main(int argc, char **argv, char **envp) qemu_spice_display_init(); } #endif -#ifdef CONFIG_LINUX - qemu_opts_foreach(qemu_find_opts("input-linux"), - input_linux_init, NULL, &error_fatal); -#endif if (foreach_device_config(DEV_GDB, gdbserver_start) < 0) { exit(1); |