diff options
Diffstat (limited to 'hw')
344 files changed, 11420 insertions, 4583 deletions
diff --git a/hw/9pfs/9p-handle.c b/hw/9pfs/9p-handle.c index 1687661bc9..9875f1894c 100644 --- a/hw/9pfs/9p-handle.c +++ b/hw/9pfs/9p-handle.c @@ -378,7 +378,6 @@ static int handle_utimensat(FsContext *ctx, V9fsPath *fs_path, const struct timespec *buf) { int ret; -#ifdef CONFIG_UTIMENSAT int fd; struct handle_data *data = (struct handle_data *)ctx->private; @@ -388,10 +387,6 @@ static int handle_utimensat(FsContext *ctx, V9fsPath *fs_path, } ret = futimens(fd, buf); close(fd); -#else - ret = -1; - errno = ENOSYS; -#endif return ret; } diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c index a2486566af..6e478f4765 100644 --- a/hw/9pfs/9p-local.c +++ b/hw/9pfs/9p-local.c @@ -53,13 +53,37 @@ int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags, mode_t mode) { LocalData *data = fs_ctx->private; - - /* All paths are relative to the path data->mountfd points to */ - while (*path == '/') { - path++; + int fd = data->mountfd; + + while (*path && fd != -1) { + const char *c; + int next_fd; + char *head; + + /* Only relative paths without consecutive slashes */ + assert(*path != '/'); + + head = g_strdup(path); + c = strchrnul(path, '/'); + if (*c) { + /* Intermediate path element */ + head[c - path] = 0; + path = c + 1; + next_fd = openat_dir(fd, head); + } else { + /* Rightmost path element */ + next_fd = openat_file(fd, head, flags, mode); + path = c; + } + g_free(head); + if (fd != data->mountfd) { + close_preserve_errno(fd); + } + fd = next_fd; } - return relative_openat_nofollow(data->mountfd, path, flags, mode); + assert(fd != data->mountfd); + return fd; } int local_opendir_nofollow(FsContext *fs_ctx, const char *path) @@ -83,6 +107,7 @@ static void unlinkat_preserve_errno(int dirfd, const char *path, int flags) } #define VIRTFS_META_DIR ".virtfs_metadata" +#define VIRTFS_META_ROOT_FILE VIRTFS_META_DIR "_root" static FILE *local_fopenat(int dirfd, const char *name, const char *mode) { @@ -119,13 +144,17 @@ static void local_mapped_file_attr(int dirfd, const char *name, char buf[ATTR_MAX]; int map_dirfd; - map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR); - if (map_dirfd == -1) { - return; - } + if (strcmp(name, ".")) { + map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR); + if (map_dirfd == -1) { + return; + } - fp = local_fopenat(map_dirfd, name, "r"); - close_preserve_errno(map_dirfd); + fp = local_fopenat(map_dirfd, name, "r"); + close_preserve_errno(map_dirfd); + } else { + fp = local_fopenat(dirfd, VIRTFS_META_ROOT_FILE, "r"); + } if (!fp) { return; } @@ -203,26 +232,38 @@ static int local_set_mapped_file_attrat(int dirfd, const char *name, int ret; char buf[ATTR_MAX]; int uid = -1, gid = -1, mode = -1, rdev = -1; - int map_dirfd; - - ret = mkdirat(dirfd, VIRTFS_META_DIR, 0700); - if (ret < 0 && errno != EEXIST) { - return -1; - } - - map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR); - if (map_dirfd == -1) { - return -1; - } + int map_dirfd = -1, map_fd; + bool is_root = !strcmp(name, "."); + + if (is_root) { + fp = local_fopenat(dirfd, VIRTFS_META_ROOT_FILE, "r"); + if (!fp) { + if (errno == ENOENT) { + goto update_map_file; + } else { + return -1; + } + } + } else { + ret = mkdirat(dirfd, VIRTFS_META_DIR, 0700); + if (ret < 0 && errno != EEXIST) { + return -1; + } - fp = local_fopenat(map_dirfd, name, "r"); - if (!fp) { - if (errno == ENOENT) { - goto update_map_file; - } else { - close_preserve_errno(map_dirfd); + map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR); + if (map_dirfd == -1) { return -1; } + + fp = local_fopenat(map_dirfd, name, "r"); + if (!fp) { + if (errno == ENOENT) { + goto update_map_file; + } else { + close_preserve_errno(map_dirfd); + return -1; + } + } } memset(buf, 0, ATTR_MAX); while (fgets(buf, ATTR_MAX, fp)) { @@ -240,12 +281,26 @@ static int local_set_mapped_file_attrat(int dirfd, const char *name, fclose(fp); update_map_file: - fp = local_fopenat(map_dirfd, name, "w"); - close_preserve_errno(map_dirfd); + if (is_root) { + fp = local_fopenat(dirfd, VIRTFS_META_ROOT_FILE, "w"); + } else { + fp = local_fopenat(map_dirfd, name, "w"); + /* We can't go this far with map_dirfd not being a valid file descriptor + * but some versions of gcc aren't smart enough to see it. + */ + if (map_dirfd != -1) { + close_preserve_errno(map_dirfd); + } + } if (!fp) { return -1; } + map_fd = fileno(fp); + assert(map_fd != -1); + ret = fchmod(map_fd, 0600); + assert(ret == 0); + if (credp->fc_uid != -1) { uid = credp->fc_uid; } @@ -454,7 +509,8 @@ static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs) static bool local_is_mapped_file_metadata(FsContext *fs_ctx, const char *name) { - return !strcmp(name, VIRTFS_META_DIR); + return + !strcmp(name, VIRTFS_META_DIR) || !strcmp(name, VIRTFS_META_ROOT_FILE); } static struct dirent *local_readdir(FsContext *ctx, V9fsFidOpenState *fs) @@ -471,7 +527,7 @@ again: entry->d_type = DT_UNKNOWN; } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { if (local_is_mapped_file_metadata(ctx, entry->d_name)) { - /* skip the meta data directory */ + /* skip the meta data */ goto again; } entry->d_type = DT_UNKNOWN; @@ -577,7 +633,7 @@ static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path, if (fs_ctx->export_flags & V9FS_SM_MAPPED || fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { - err = mknodat(dirfd, name, SM_LOCAL_MODE_BITS | S_IFREG, 0); + err = mknodat(dirfd, name, fs_ctx->fmode | S_IFREG, 0); if (err == -1) { goto out; } @@ -629,7 +685,7 @@ static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, if (fs_ctx->export_flags & V9FS_SM_MAPPED || fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { - err = mkdirat(dirfd, name, SM_LOCAL_DIR_MODE_BITS); + err = mkdirat(dirfd, name, fs_ctx->dmode); if (err == -1) { goto out; } @@ -730,7 +786,7 @@ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, /* Determine the security model */ if (fs_ctx->export_flags & V9FS_SM_MAPPED || fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { - fd = openat_file(dirfd, name, flags, SM_LOCAL_MODE_BITS); + fd = openat_file(dirfd, name, flags, fs_ctx->fmode); if (fd == -1) { goto out; } @@ -793,7 +849,7 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath, ssize_t oldpath_size, write_size; fd = openat_file(dirfd, name, O_CREAT | O_EXCL | O_RDWR, - SM_LOCAL_MODE_BITS); + fs_ctx->fmode); if (fd == -1) { goto out; } @@ -992,6 +1048,14 @@ static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name, if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { int map_dirfd; + /* We need to remove the metadata as well: + * - the metadata directory if we're removing a directory + * - the metadata file in the parent's metadata directory + * + * If any of these are missing (ie, ENOENT) then we're probably + * trying to remove something that wasn't created in mapped-file + * mode. We just ignore the error. + */ if (flags == AT_REMOVEDIR) { int fd; @@ -999,32 +1063,20 @@ static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name, if (fd == -1) { goto err_out; } - /* - * If directory remove .virtfs_metadata contained in the - * directory - */ ret = unlinkat(fd, VIRTFS_META_DIR, AT_REMOVEDIR); close_preserve_errno(fd); if (ret < 0 && errno != ENOENT) { - /* - * We didn't had the .virtfs_metadata file. May be file created - * in non-mapped mode ?. Ignore ENOENT. - */ goto err_out; } } - /* - * Now remove the name from parent directory - * .virtfs_metadata directory. - */ map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR); - ret = unlinkat(map_dirfd, name, 0); - close_preserve_errno(map_dirfd); - if (ret < 0 && errno != ENOENT) { - /* - * We didn't had the .virtfs_metadata file. May be file created - * in non-mapped mode ?. Ignore ENOENT. - */ + if (map_dirfd != -1) { + ret = unlinkat(map_dirfd, name, 0); + close_preserve_errno(map_dirfd); + if (ret < 0 && errno != ENOENT) { + goto err_out; + } + } else if (errno != ENOENT) { goto err_out; } } @@ -1048,7 +1100,7 @@ static int local_remove(FsContext *ctx, const char *path) goto out; } - if (fstatat(dirfd, path, &stbuf, AT_SYMLINK_NOFOLLOW) < 0) { + if (fstatat(dirfd, name, &stbuf, AT_SYMLINK_NOFOLLOW) < 0) { goto err_out; } @@ -1138,14 +1190,32 @@ static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path, } if (dir_path) { - v9fs_path_sprintf(target, "%s/%s", dir_path->data, name); - } else if (strcmp(name, "/")) { - v9fs_path_sprintf(target, "%s", name); + if (!strcmp(name, ".")) { + /* "." relative to "foo/bar" is "foo/bar" */ + v9fs_path_copy(target, dir_path); + } else if (!strcmp(name, "..")) { + if (!strcmp(dir_path->data, ".")) { + /* ".." relative to the root is "." */ + v9fs_path_sprintf(target, "."); + } else { + char *tmp = g_path_get_dirname(dir_path->data); + /* Symbolic links are resolved by the client. We can assume + * that ".." relative to "foo/bar" is equivalent to "foo" + */ + v9fs_path_sprintf(target, "%s", tmp); + g_free(tmp); + } + } else { + assert(!strchr(name, '/')); + v9fs_path_sprintf(target, "%s/%s", dir_path->data, name); + } + } else if (!strcmp(name, "/") || !strcmp(name, ".") || + !strcmp(name, "..")) { + /* This is the root fid */ + v9fs_path_sprintf(target, "."); } else { - /* We want the path of the export root to be relative, otherwise - * "*at()" syscalls would treat it as "/" in the host. - */ - v9fs_path_sprintf(target, "%s", "."); + assert(!strchr(name, '/')); + v9fs_path_sprintf(target, "./%s", name); } return 0; } @@ -1397,6 +1467,23 @@ static int local_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse) return -1; } + if (fse->export_flags & V9FS_SM_MAPPED || + fse->export_flags & V9FS_SM_MAPPED_FILE) { + fse->fmode = + qemu_opt_get_number(opts, "fmode", SM_LOCAL_MODE_BITS) & 0777; + fse->dmode = + qemu_opt_get_number(opts, "dmode", SM_LOCAL_DIR_MODE_BITS) & 0777; + } else { + if (qemu_opt_find(opts, "fmode")) { + error_report("fmode is only valid for mapped 9p modes"); + return -1; + } + if (qemu_opt_find(opts, "dmode")) { + error_report("dmode is only valid for mapped 9p modes"); + return -1; + } + } + fse->path = g_strdup(path); return 0; diff --git a/hw/9pfs/9p-synth.c b/hw/9pfs/9p-synth.c index 4b6d4e6a3f..df0a8de08a 100644 --- a/hw/9pfs/9p-synth.c +++ b/hw/9pfs/9p-synth.c @@ -494,8 +494,7 @@ static int synth_name_to_path(FsContext *ctx, V9fsPath *dir_path, } out: /* Copy the node pointer to fid */ - target->data = g_malloc(sizeof(void *)); - memcpy(target->data, &node, sizeof(void *)); + target->data = g_memdup(&node, sizeof(void *)); target->size = sizeof(void *); return 0; } diff --git a/hw/9pfs/9p-util.c b/hw/9pfs/9p-util.c index fdb4d57376..f709c27a1f 100644 --- a/hw/9pfs/9p-util.c +++ b/hw/9pfs/9p-util.c @@ -14,49 +14,6 @@ #include "qemu/xattr.h" #include "9p-util.h" -int relative_openat_nofollow(int dirfd, const char *path, int flags, - mode_t mode) -{ - int fd; - - fd = dup(dirfd); - if (fd == -1) { - return -1; - } - - while (*path) { - const char *c; - int next_fd; - char *head; - - /* Only relative paths without consecutive slashes */ - assert(path[0] != '/'); - - head = g_strdup(path); - c = strchr(path, '/'); - if (c) { - head[c - path] = 0; - next_fd = openat_dir(fd, head); - } else { - next_fd = openat_file(fd, head, flags, mode); - } - g_free(head); - if (next_fd == -1) { - close_preserve_errno(fd); - return -1; - } - close(fd); - fd = next_fd; - - if (!c) { - break; - } - path = c + 1; - } - - return fd; -} - ssize_t fgetxattrat_nofollow(int dirfd, const char *filename, const char *name, void *value, size_t size) { diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h index 517027c520..91299a24b8 100644 --- a/hw/9pfs/9p-util.h +++ b/hw/9pfs/9p-util.h @@ -50,8 +50,6 @@ static inline int openat_file(int dirfd, const char *name, int flags, return fd; } -int relative_openat_nofollow(int dirfd, const char *path, int flags, - mode_t mode); ssize_t fgetxattrat_nofollow(int dirfd, const char *path, const char *name, void *value, size_t size); int fsetxattrat_nofollow(int dirfd, const char *path, const char *name, diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index ab3e22f231..333dbb6f8e 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -65,11 +65,6 @@ ssize_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...) return ret; } -static void pdu_push_and_notify(V9fsPDU *pdu) -{ - pdu->s->transport->push_and_notify(pdu); -} - static int omode_to_uflags(int8_t mode) { int ret = 0; @@ -629,15 +624,11 @@ void pdu_free(V9fsPDU *pdu) QLIST_INSERT_HEAD(&s->free_list, pdu, next); } -/* - * We don't do error checking for pdu_marshal/unmarshal here - * because we always expect to have enough space to encode - * error details - */ static void coroutine_fn pdu_complete(V9fsPDU *pdu, ssize_t len) { int8_t id = pdu->id + 1; /* Response */ V9fsState *s = pdu->s; + int ret; if (len < 0) { int err = -len; @@ -649,11 +640,19 @@ static void coroutine_fn pdu_complete(V9fsPDU *pdu, ssize_t len) str.data = strerror(err); str.size = strlen(str.data); - len += pdu_marshal(pdu, len, "s", &str); + ret = pdu_marshal(pdu, len, "s", &str); + if (ret < 0) { + goto out_notify; + } + len += ret; id = P9_RERROR; } - len += pdu_marshal(pdu, len, "d", err); + ret = pdu_marshal(pdu, len, "d", err); + if (ret < 0) { + goto out_notify; + } + len += ret; if (s->proto_version == V9FS_PROTO_2000L) { id = P9_RLERROR; @@ -662,13 +661,16 @@ static void coroutine_fn pdu_complete(V9fsPDU *pdu, ssize_t len) } /* fill out the header */ - pdu_marshal(pdu, 0, "dbw", (int32_t)len, id, pdu->tag); + if (pdu_marshal(pdu, 0, "dbw", (int32_t)len, id, pdu->tag) < 0) { + goto out_notify; + } /* keep these in sync */ pdu->size = len; pdu->id = id; - pdu_push_and_notify(pdu); +out_notify: + pdu->s->transport->push_and_notify(pdu); /* Now wakeup anybody waiting in flush for this request */ if (!qemu_co_queue_next(&pdu->complete)) { @@ -1669,7 +1671,7 @@ static void v9fs_init_qiov_from_pdu(QEMUIOVector *qiov, V9fsPDU *pdu, unsigned int niov; if (is_write) { - pdu->s->transport->init_out_iov_from_pdu(pdu, &iov, &niov); + pdu->s->transport->init_out_iov_from_pdu(pdu, &iov, &niov, size + skip); } else { pdu->s->transport->init_in_iov_from_pdu(pdu, &iov, &niov, size + skip); } @@ -2374,7 +2376,7 @@ static void coroutine_fn v9fs_flush(void *opaque) trace_v9fs_flush(pdu->tag, pdu->id, tag); if (pdu->tag == tag) { - error_report("Warning: the guest sent a self-referencing 9P flush request"); + warn_report("the guest sent a self-referencing 9P flush request"); } else { QLIST_FOREACH(cancel_pdu, &s->active_list, next) { if (cancel_pdu->tag == tag) { @@ -2576,7 +2578,10 @@ static int coroutine_fn v9fs_complete_rename(V9fsPDU *pdu, V9fsFidState *fidp, err = -EINVAL; goto out; } - v9fs_co_name_to_path(pdu, &dirfidp->path, name->data, &new_path); + err = v9fs_co_name_to_path(pdu, &dirfidp->path, name->data, &new_path); + if (err < 0) { + goto out; + } } else { old_name = fidp->path.data; end = strrchr(old_name, '/'); @@ -2588,8 +2593,11 @@ static int coroutine_fn v9fs_complete_rename(V9fsPDU *pdu, V9fsFidState *fidp, new_name = g_malloc0(end - old_name + name->size + 1); strncat(new_name, old_name, end - old_name); strncat(new_name + (end - old_name), name->data, name->size); - v9fs_co_name_to_path(pdu, NULL, new_name, &new_path); + err = v9fs_co_name_to_path(pdu, NULL, new_name, &new_path); g_free(new_name); + if (err < 0) { + goto out; + } } err = v9fs_co_rename(pdu, &fidp->path, &new_path); if (err < 0) { @@ -2669,20 +2677,26 @@ out_nofid: v9fs_string_free(&name); } -static void coroutine_fn v9fs_fix_fid_paths(V9fsPDU *pdu, V9fsPath *olddir, - V9fsString *old_name, - V9fsPath *newdir, - V9fsString *new_name) +static int coroutine_fn v9fs_fix_fid_paths(V9fsPDU *pdu, V9fsPath *olddir, + V9fsString *old_name, + V9fsPath *newdir, + V9fsString *new_name) { V9fsFidState *tfidp; V9fsPath oldpath, newpath; V9fsState *s = pdu->s; - + int err; v9fs_path_init(&oldpath); v9fs_path_init(&newpath); - v9fs_co_name_to_path(pdu, olddir, old_name->data, &oldpath); - v9fs_co_name_to_path(pdu, newdir, new_name->data, &newpath); + err = v9fs_co_name_to_path(pdu, olddir, old_name->data, &oldpath); + if (err < 0) { + goto out; + } + err = v9fs_co_name_to_path(pdu, newdir, new_name->data, &newpath); + if (err < 0) { + goto out; + } /* * Fixup fid's pointing to the old name to @@ -2694,8 +2708,10 @@ static void coroutine_fn v9fs_fix_fid_paths(V9fsPDU *pdu, V9fsPath *olddir, v9fs_fix_path(&tfidp->path, &newpath, strlen(oldpath.data)); } } +out: v9fs_path_free(&oldpath); v9fs_path_free(&newpath); + return err; } static int coroutine_fn v9fs_complete_renameat(V9fsPDU *pdu, int32_t olddirfid, @@ -2729,8 +2745,8 @@ static int coroutine_fn v9fs_complete_renameat(V9fsPDU *pdu, int32_t olddirfid, } if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) { /* Only for path based fid we need to do the below fixup */ - v9fs_fix_fid_paths(pdu, &olddirfidp->path, old_name, - &newdirfidp->path, new_name); + err = v9fs_fix_fid_paths(pdu, &olddirfidp->path, old_name, + &newdirfidp->path, new_name); } out: if (olddirfidp) { @@ -3446,12 +3462,16 @@ static inline bool is_read_only_op(V9fsPDU *pdu) } } -void pdu_submit(V9fsPDU *pdu) +void pdu_submit(V9fsPDU *pdu, P9MsgHeader *hdr) { Coroutine *co; CoroutineEntry *handler; V9fsState *s = pdu->s; + pdu->size = le32_to_cpu(hdr->size_le); + pdu->id = hdr->id; + pdu->tag = le16_to_cpu(hdr->tag_le); + if (pdu->id >= ARRAY_SIZE(pdu_co_handlers) || (pdu_co_handlers[pdu->id] == NULL)) { handler = v9fs_op_not_supp; @@ -3462,6 +3482,8 @@ void pdu_submit(V9fsPDU *pdu) if (is_ro_export(&s->ctx) && !is_read_only_op(pdu)) { handler = v9fs_fs_ro; } + + qemu_co_queue_init(&pdu->complete); co = qemu_coroutine_create(handler, pdu); qemu_coroutine_enter(co); } @@ -3518,6 +3540,9 @@ int v9fs_device_realize_common(V9fsState *s, Error **errp) s->ops = fse->ops; + s->ctx.fmode = fse->fmode; + s->ctx.dmode = fse->dmode; + s->fid_list = NULL; qemu_co_rwlock_init(&s->rename_lock); diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h index 5312d8a424..d1cfeaf10e 100644 --- a/hw/9pfs/9p.h +++ b/hw/9pfs/9p.h @@ -124,6 +124,11 @@ typedef struct { uint8_t id; uint16_t tag_le; } QEMU_PACKED P9MsgHeader; +/* According to the specification, 9p messages start with a 7-byte header. + * Since most of the code uses this header size in literal form, we must be + * sure this is indeed the case. + */ +QEMU_BUILD_BUG_ON(sizeof(P9MsgHeader) != 7); struct V9fsPDU { @@ -347,7 +352,7 @@ ssize_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...); ssize_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...); V9fsPDU *pdu_alloc(V9fsState *s); void pdu_free(V9fsPDU *pdu); -void pdu_submit(V9fsPDU *pdu); +void pdu_submit(V9fsPDU *pdu, P9MsgHeader *hdr); void v9fs_reset(V9fsState *s); struct V9fsTransport { @@ -358,7 +363,7 @@ struct V9fsTransport { void (*init_in_iov_from_pdu)(V9fsPDU *pdu, struct iovec **piov, unsigned int *pniov, size_t size); void (*init_out_iov_from_pdu)(V9fsPDU *pdu, struct iovec **piov, - unsigned int *pniov); + unsigned int *pniov, size_t size); void (*push_and_notify)(V9fsPDU *pdu); }; diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c index 3782f43702..62650b0a6b 100644 --- a/hw/9pfs/virtio-9p-device.c +++ b/hw/9pfs/virtio-9p-device.c @@ -53,30 +53,23 @@ static void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq) goto out_free_pdu; } - if (elem->in_num == 0) { + if (iov_size(elem->in_sg, elem->in_num) < 7) { virtio_error(vdev, "The guest sent a VirtFS request without space for " "the reply"); goto out_free_req; } - QEMU_BUILD_BUG_ON(sizeof(out) != 7); - v->elems[pdu->idx] = elem; - len = iov_to_buf(elem->out_sg, elem->out_num, 0, - &out, sizeof(out)); - if (len != sizeof(out)) { + len = iov_to_buf(elem->out_sg, elem->out_num, 0, &out, 7); + if (len != 7) { virtio_error(vdev, "The guest sent a malformed VirtFS request: " "header size is %zd, should be 7", len); goto out_free_req; } - pdu->size = le32_to_cpu(out.size_le); - - pdu->id = out.id; - pdu->tag = le16_to_cpu(out.tag_le); + v->elems[pdu->idx] = elem; - qemu_co_queue_init(&pdu->complete); - pdu_submit(pdu); + pdu_submit(pdu, &out); } return; @@ -153,8 +146,16 @@ static ssize_t virtio_pdu_vmarshal(V9fsPDU *pdu, size_t offset, V9fsState *s = pdu->s; V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); VirtQueueElement *elem = v->elems[pdu->idx]; + ssize_t ret; + + ret = v9fs_iov_vmarshal(elem->in_sg, elem->in_num, offset, 1, fmt, ap); + if (ret < 0) { + VirtIODevice *vdev = VIRTIO_DEVICE(v); - return v9fs_iov_vmarshal(elem->in_sg, elem->in_num, offset, 1, fmt, ap); + virtio_error(vdev, "Failed to encode VirtFS reply type %d", + pdu->id + 1); + } + return ret; } static ssize_t virtio_pdu_vunmarshal(V9fsPDU *pdu, size_t offset, @@ -163,28 +164,52 @@ static ssize_t virtio_pdu_vunmarshal(V9fsPDU *pdu, size_t offset, V9fsState *s = pdu->s; V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); VirtQueueElement *elem = v->elems[pdu->idx]; + ssize_t ret; + + ret = v9fs_iov_vunmarshal(elem->out_sg, elem->out_num, offset, 1, fmt, ap); + if (ret < 0) { + VirtIODevice *vdev = VIRTIO_DEVICE(v); - return v9fs_iov_vunmarshal(elem->out_sg, elem->out_num, offset, 1, fmt, ap); + virtio_error(vdev, "Failed to decode VirtFS request type %d", pdu->id); + } + return ret; } -/* The size parameter is used by other transports. Do not drop it. */ static void virtio_init_in_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov, unsigned int *pniov, size_t size) { V9fsState *s = pdu->s; V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); VirtQueueElement *elem = v->elems[pdu->idx]; + size_t buf_size = iov_size(elem->in_sg, elem->in_num); + + if (buf_size < size) { + VirtIODevice *vdev = VIRTIO_DEVICE(v); + + virtio_error(vdev, + "VirtFS reply type %d needs %zu bytes, buffer has %zu", + pdu->id + 1, size, buf_size); + } *piov = elem->in_sg; *pniov = elem->in_num; } static void virtio_init_out_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov, - unsigned int *pniov) + unsigned int *pniov, size_t size) { V9fsState *s = pdu->s; V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); VirtQueueElement *elem = v->elems[pdu->idx]; + size_t buf_size = iov_size(elem->out_sg, elem->out_num); + + if (buf_size < size) { + VirtIODevice *vdev = VIRTIO_DEVICE(v); + + virtio_error(vdev, + "VirtFS request type %d needs %zu bytes, buffer has %zu", + pdu->id, size, buf_size); + } *piov = elem->out_sg; *pniov = elem->out_num; diff --git a/hw/9pfs/xen-9p-backend.c b/hw/9pfs/xen-9p-backend.c index 5df97c90fa..ee87f08926 100644 --- a/hw/9pfs/xen-9p-backend.c +++ b/hw/9pfs/xen-9p-backend.c @@ -54,6 +54,8 @@ typedef struct Xen9pfsDev { Xen9pfsRing *rings; } Xen9pfsDev; +static void xen_9pfs_disconnect(struct XenDevice *xendev); + static void xen_9pfs_in_sg(Xen9pfsRing *ring, struct iovec *in_sg, int *num, @@ -125,10 +127,19 @@ static ssize_t xen_9pfs_pdu_vmarshal(V9fsPDU *pdu, Xen9pfsDev *xen_9pfs = container_of(pdu->s, Xen9pfsDev, state); struct iovec in_sg[2]; int num; + ssize_t ret; xen_9pfs_in_sg(&xen_9pfs->rings[pdu->tag % xen_9pfs->num_rings], in_sg, &num, pdu->idx, ROUND_UP(offset + 128, 512)); - return v9fs_iov_vmarshal(in_sg, num, offset, 0, fmt, ap); + + ret = v9fs_iov_vmarshal(in_sg, num, offset, 0, fmt, ap); + if (ret < 0) { + xen_pv_printf(&xen_9pfs->xendev, 0, + "Failed to encode VirtFS request type %d\n", pdu->id + 1); + xen_be_set_state(&xen_9pfs->xendev, XenbusStateClosing); + xen_9pfs_disconnect(&xen_9pfs->xendev); + } + return ret; } static ssize_t xen_9pfs_pdu_vunmarshal(V9fsPDU *pdu, @@ -139,15 +150,25 @@ static ssize_t xen_9pfs_pdu_vunmarshal(V9fsPDU *pdu, Xen9pfsDev *xen_9pfs = container_of(pdu->s, Xen9pfsDev, state); struct iovec out_sg[2]; int num; + ssize_t ret; xen_9pfs_out_sg(&xen_9pfs->rings[pdu->tag % xen_9pfs->num_rings], out_sg, &num, pdu->idx); - return v9fs_iov_vunmarshal(out_sg, num, offset, 0, fmt, ap); + + ret = v9fs_iov_vunmarshal(out_sg, num, offset, 0, fmt, ap); + if (ret < 0) { + xen_pv_printf(&xen_9pfs->xendev, 0, + "Failed to decode VirtFS request type %d\n", pdu->id); + xen_be_set_state(&xen_9pfs->xendev, XenbusStateClosing); + xen_9pfs_disconnect(&xen_9pfs->xendev); + } + return ret; } static void xen_9pfs_init_out_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov, - unsigned int *pniov) + unsigned int *pniov, + size_t size) { Xen9pfsDev *xen_9pfs = container_of(pdu->s, Xen9pfsDev, state); Xen9pfsRing *ring = &xen_9pfs->rings[pdu->tag % xen_9pfs->num_rings]; @@ -169,11 +190,22 @@ static void xen_9pfs_init_in_iov_from_pdu(V9fsPDU *pdu, Xen9pfsDev *xen_9pfs = container_of(pdu->s, Xen9pfsDev, state); Xen9pfsRing *ring = &xen_9pfs->rings[pdu->tag % xen_9pfs->num_rings]; int num; + size_t buf_size; g_free(ring->sg); ring->sg = g_malloc0(sizeof(*ring->sg) * 2); xen_9pfs_in_sg(ring, ring->sg, &num, pdu->idx, size); + + buf_size = iov_size(ring->sg, num); + if (buf_size < size) { + xen_pv_printf(&xen_9pfs->xendev, 0, "Xen 9pfs request type %d" + "needs %zu bytes, buffer has %zu\n", pdu->id, size, + buf_size); + xen_be_set_state(&xen_9pfs->xendev, XenbusStateClosing); + xen_9pfs_disconnect(&xen_9pfs->xendev); + } + *piov = ring->sg; *pniov = num; } @@ -217,7 +249,7 @@ static int xen_9pfs_init(struct XenDevice *xendev) static int xen_9pfs_receive(Xen9pfsRing *ring) { P9MsgHeader h; - RING_IDX cons, prod, masked_prod, masked_cons; + RING_IDX cons, prod, masked_prod, masked_cons, queued; V9fsPDU *pdu; if (ring->inprogress) { @@ -228,8 +260,8 @@ static int xen_9pfs_receive(Xen9pfsRing *ring) prod = ring->intf->out_prod; xen_rmb(); - if (xen_9pfs_queued(prod, cons, XEN_FLEX_RING_SIZE(ring->ring_order)) < - sizeof(h)) { + queued = xen_9pfs_queued(prod, cons, XEN_FLEX_RING_SIZE(ring->ring_order)); + if (queued < sizeof(h)) { return 0; } ring->inprogress = true; @@ -240,17 +272,16 @@ static int xen_9pfs_receive(Xen9pfsRing *ring) xen_9pfs_read_packet((uint8_t *) &h, ring->ring.out, sizeof(h), masked_prod, &masked_cons, XEN_FLEX_RING_SIZE(ring->ring_order)); + if (queued < le32_to_cpu(h.size_le)) { + return 0; + } /* cannot fail, because we only handle one request per ring at a time */ pdu = pdu_alloc(&ring->priv->state); - pdu->size = le32_to_cpu(h.size_le); - pdu->id = h.id; - pdu->tag = le32_to_cpu(h.tag_le); ring->out_size = le32_to_cpu(h.size_le); ring->out_cons = cons + le32_to_cpu(h.size_le); - qemu_co_queue_init(&pdu->complete); - pdu_submit(pdu); + pdu_submit(pdu, &h); return 0; } @@ -272,15 +303,30 @@ static void xen_9pfs_evtchn_event(void *opaque) qemu_bh_schedule(ring->bh); } -static int xen_9pfs_free(struct XenDevice *xendev) +static void xen_9pfs_disconnect(struct XenDevice *xendev) { + Xen9pfsDev *xen_9pdev = container_of(xendev, Xen9pfsDev, xendev); int i; + + for (i = 0; i < xen_9pdev->num_rings; i++) { + if (xen_9pdev->rings[i].evtchndev != NULL) { + qemu_set_fd_handler(xenevtchn_fd(xen_9pdev->rings[i].evtchndev), + NULL, NULL, NULL); + xenevtchn_unbind(xen_9pdev->rings[i].evtchndev, + xen_9pdev->rings[i].local_port); + xen_9pdev->rings[i].evtchndev = NULL; + } + } +} + +static int xen_9pfs_free(struct XenDevice *xendev) +{ Xen9pfsDev *xen_9pdev = container_of(xendev, Xen9pfsDev, xendev); + int i; - g_free(xen_9pdev->id); - g_free(xen_9pdev->tag); - g_free(xen_9pdev->path); - g_free(xen_9pdev->security_model); + if (xen_9pdev->rings[0].evtchndev != NULL) { + xen_9pfs_disconnect(xendev); + } for (i = 0; i < xen_9pdev->num_rings; i++) { if (xen_9pdev->rings[i].data != NULL) { @@ -293,16 +339,15 @@ static int xen_9pfs_free(struct XenDevice *xendev) xen_9pdev->rings[i].intf, 1); } - if (xen_9pdev->rings[i].evtchndev > 0) { - qemu_set_fd_handler(xenevtchn_fd(xen_9pdev->rings[i].evtchndev), - NULL, NULL, NULL); - xenevtchn_unbind(xen_9pdev->rings[i].evtchndev, - xen_9pdev->rings[i].local_port); - } if (xen_9pdev->rings[i].bh != NULL) { qemu_bh_delete(xen_9pdev->rings[i].bh); } } + + g_free(xen_9pdev->id); + g_free(xen_9pdev->tag); + g_free(xen_9pdev->path); + g_free(xen_9pdev->security_model); g_free(xen_9pdev->rings); return 0; } @@ -426,11 +471,6 @@ static void xen_9pfs_alloc(struct XenDevice *xendev) xenstore_write_be_int(xendev, "max-ring-page-order", MAX_RING_ORDER); } -static void xen_9pfs_disconnect(struct XenDevice *xendev) -{ - /* Dynamic hotplug of PV filesystems at runtime is not supported. */ -} - struct XenDevOps xen_9pfs_ops = { .size = sizeof(Xen9pfsDev), .flags = DEVOPS_FLAG_NEED_GNTDEV, diff --git a/hw/acpi/core.c b/hw/acpi/core.c index e890a5d675..95fcac95a2 100644 --- a/hw/acpi/core.c +++ b/hw/acpi/core.c @@ -561,7 +561,7 @@ static void acpi_pm1_cnt_write(ACPIREGS *ar, uint16_t val) uint16_t sus_typ = (val >> 10) & 7; switch(sus_typ) { case 0: /* soft power off */ - qemu_system_shutdown_request(); + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); break; case 1: qemu_system_suspend_request(); @@ -569,7 +569,7 @@ static void acpi_pm1_cnt_write(ACPIREGS *ar, uint16_t val) default: if (sus_typ == ar->pm1.cnt.s4_val) { /* S4 request */ qapi_event_send_suspend_disk(&error_abort); - qemu_system_shutdown_request(); + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); } break; } diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index 5c279bbaca..c5d8646abc 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -33,7 +33,6 @@ #include "sysemu/sysemu.h" #include "hw/acpi/acpi.h" #include "hw/acpi/tco.h" -#include "sysemu/kvm.h" #include "exec/address-spaces.h" #include "hw/i386/ich9.h" diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c index 210073d283..cda2c9dd06 100644 --- a/hw/acpi/memory_hotplug.c +++ b/hw/acpi/memory_hotplug.c @@ -83,23 +83,25 @@ static uint64_t acpi_memory_hotplug_read(void *opaque, hwaddr addr, o = OBJECT(mdev->dimm); switch (addr) { case 0x0: /* Lo part of phys address where DIMM is mapped */ - val = o ? object_property_get_int(o, PC_DIMM_ADDR_PROP, NULL) : 0; + val = o ? object_property_get_uint(o, PC_DIMM_ADDR_PROP, NULL) : 0; trace_mhp_acpi_read_addr_lo(mem_st->selector, val); break; case 0x4: /* Hi part of phys address where DIMM is mapped */ - val = o ? object_property_get_int(o, PC_DIMM_ADDR_PROP, NULL) >> 32 : 0; + val = + o ? object_property_get_uint(o, PC_DIMM_ADDR_PROP, NULL) >> 32 : 0; trace_mhp_acpi_read_addr_hi(mem_st->selector, val); break; case 0x8: /* Lo part of DIMM size */ - val = o ? object_property_get_int(o, PC_DIMM_SIZE_PROP, NULL) : 0; + val = o ? object_property_get_uint(o, PC_DIMM_SIZE_PROP, NULL) : 0; trace_mhp_acpi_read_size_lo(mem_st->selector, val); break; case 0xc: /* Hi part of DIMM size */ - val = o ? object_property_get_int(o, PC_DIMM_SIZE_PROP, NULL) >> 32 : 0; + val = + o ? object_property_get_uint(o, PC_DIMM_SIZE_PROP, NULL) >> 32 : 0; trace_mhp_acpi_read_size_hi(mem_st->selector, val); break; case 0x10: /* node proximity for _PXM method */ - val = o ? object_property_get_int(o, PC_DIMM_NODE_PROP, NULL) : 0; + val = o ? object_property_get_uint(o, PC_DIMM_NODE_PROP, NULL) : 0; trace_mhp_acpi_read_pxm(mem_st->selector, val); break; case 0x14: /* pack and return is_* fields */ diff --git a/hw/acpi/nvdimm.c b/hw/acpi/nvdimm.c index 8e7d6ec034..6ceea196e7 100644 --- a/hw/acpi/nvdimm.c +++ b/hw/acpi/nvdimm.c @@ -236,14 +236,14 @@ static void nvdimm_build_structure_spa(GArray *structures, DeviceState *dev) { NvdimmNfitSpa *nfit_spa; - uint64_t addr = object_property_get_int(OBJECT(dev), PC_DIMM_ADDR_PROP, - NULL); - uint64_t size = object_property_get_int(OBJECT(dev), PC_DIMM_SIZE_PROP, - NULL); - uint32_t node = object_property_get_int(OBJECT(dev), PC_DIMM_NODE_PROP, - NULL); + uint64_t addr = object_property_get_uint(OBJECT(dev), PC_DIMM_ADDR_PROP, + NULL); + uint64_t size = object_property_get_uint(OBJECT(dev), PC_DIMM_SIZE_PROP, + NULL); + uint32_t node = object_property_get_uint(OBJECT(dev), PC_DIMM_NODE_PROP, + NULL); int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, - NULL); + NULL); nfit_spa = acpi_data_push(structures, sizeof(*nfit_spa)); @@ -284,8 +284,8 @@ static void nvdimm_build_structure_memdev(GArray *structures, DeviceState *dev) { NvdimmNfitMemDev *nfit_memdev; - uint64_t size = object_property_get_int(OBJECT(dev), PC_DIMM_SIZE_PROP, - NULL); + uint64_t size = object_property_get_uint(OBJECT(dev), PC_DIMM_SIZE_PROP, + NULL); int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, NULL); uint32_t handle = nvdimm_slot_to_handle(slot); @@ -338,9 +338,10 @@ static void nvdimm_build_structure_dcr(GArray *structures, DeviceState *dev) nfit_dcr->revision_id = cpu_to_le16(1 /* Current Revision supported in ACPI 6.0 is 1. */); nfit_dcr->serial_number = cpu_to_le32(sn); - nfit_dcr->fic = cpu_to_le16(0x201 /* Format Interface Code. See Chapter - 2: NVDIMM Device Specific Method - (DSM) in DSM Spec Rev1.*/); + nfit_dcr->fic = cpu_to_le16(0x301 /* Format Interface Code: + Byte addressable, no energy backed. + See ACPI 6.2, sect 5.2.25.6 and + JEDEC Annex L Release 3. */); } static GArray *nvdimm_build_device_structure(void) diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index 2b0f3e1bfb..c420a388ea 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c @@ -37,7 +37,6 @@ #include "hw/pci/pci_bus.h" #include "qapi/error.h" #include "qom/qom-qobject.h" -#include "qapi/qmp/qint.h" //#define DEBUG @@ -63,10 +62,10 @@ typedef struct AcpiPciHpFind { static int acpi_pcihp_get_bsel(PCIBus *bus) { Error *local_err = NULL; - int64_t bsel = object_property_get_int(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, - &local_err); + uint64_t bsel = object_property_get_uint(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, + &local_err); - if (local_err || bsel < 0 || bsel >= ACPI_PCIHP_MAX_HOTPLUG_BUS) { + if (local_err || bsel >= ACPI_PCIHP_MAX_HOTPLUG_BUS) { if (local_err) { error_free(local_err); } diff --git a/hw/alpha/typhoon.c b/hw/alpha/typhoon.c index f50f5cf186..ae11e012c7 100644 --- a/hw/alpha/typhoon.c +++ b/hw/alpha/typhoon.c @@ -17,6 +17,7 @@ #define TYPE_TYPHOON_PCI_HOST_BRIDGE "typhoon-pcihost" +#define TYPE_TYPHOON_IOMMU_MEMORY_REGION "typhoon-iommu-memory-region" typedef struct TyphoonCchip { MemoryRegion region; @@ -41,7 +42,7 @@ typedef struct TyphoonPchip { MemoryRegion reg_conf; AddressSpace iommu_as; - MemoryRegion iommu; + IOMMUMemoryRegion iommu; uint64_t ctl; TyphoonWindow win[4]; @@ -663,8 +664,9 @@ static bool window_translate(TyphoonWindow *win, hwaddr addr, /* Handle PCI-to-system address translation. */ /* TODO: A translation failure here ought to set PCI error codes on the Pchip and generate a machine check interrupt. */ -static IOMMUTLBEntry typhoon_translate_iommu(MemoryRegion *iommu, hwaddr addr, - bool is_write) +static IOMMUTLBEntry typhoon_translate_iommu(IOMMUMemoryRegion *iommu, + hwaddr addr, + IOMMUAccessFlags flag) { TyphoonPchip *pchip = container_of(iommu, TyphoonPchip, iommu); IOMMUTLBEntry ret; @@ -724,10 +726,6 @@ static IOMMUTLBEntry typhoon_translate_iommu(MemoryRegion *iommu, hwaddr addr, return ret; } -static const MemoryRegionIOMMUOps typhoon_iommu_ops = { - .translate = typhoon_translate_iommu, -}; - static AddressSpace *typhoon_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) { TyphoonState *s = opaque; @@ -891,9 +889,11 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, qdev_init_nofail(dev); /* Host memory as seen from the PCI side, via the IOMMU. */ - memory_region_init_iommu(&s->pchip.iommu, OBJECT(s), &typhoon_iommu_ops, + memory_region_init_iommu(&s->pchip.iommu, sizeof(s->pchip.iommu), + TYPE_TYPHOON_IOMMU_MEMORY_REGION, OBJECT(s), "iommu-typhoon", UINT64_MAX); - address_space_init(&s->pchip.iommu_as, &s->pchip.iommu, "pchip0-pci"); + address_space_init(&s->pchip.iommu_as, MEMORY_REGION(&s->pchip.iommu), + "pchip0-pci"); pci_setup_iommu(b, typhoon_pci_dma_iommu, s); /* Pchip0 PCI special/interrupt acknowledge, 0x801.F800.0000, 64MB. */ @@ -951,9 +951,24 @@ static const TypeInfo typhoon_pcihost_info = { .class_init = typhoon_pcihost_class_init, }; +static void typhoon_iommu_memory_region_class_init(ObjectClass *klass, + void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = typhoon_translate_iommu; +} + +static const TypeInfo typhoon_iommu_memory_region_info = { + .parent = TYPE_IOMMU_MEMORY_REGION, + .name = TYPE_TYPHOON_IOMMU_MEMORY_REGION, + .class_init = typhoon_iommu_memory_region_class_init, +}; + static void typhoon_register_types(void) { type_register_static(&typhoon_pcihost_info); + type_register_static(&typhoon_iommu_memory_region_info); } type_init(typhoon_register_types) diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 4c5c4ee76c..a2e56ecaae 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -18,3 +18,4 @@ obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o obj-$(CONFIG_FSL_IMX6) += fsl-imx6.o sabrelite.o obj-$(CONFIG_ASPEED_SOC) += aspeed_soc.o aspeed.o +obj-$(CONFIG_MPS2) += mps2.o diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 283c038814..0c5635f300 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -39,6 +39,7 @@ typedef struct AspeedBoardConfig { const char *fmc_model; const char *spi_model; uint32_t num_cs; + void (*i2c_init)(AspeedBoardState *bmc); } AspeedBoardConfig; enum { @@ -82,6 +83,9 @@ enum { SCU_AST2500_HW_STRAP_ACPI_ENABLE | \ SCU_HW_STRAP_SPI_MODE(SCU_HW_STRAP_SPI_MASTER)) +static void palmetto_bmc_i2c_init(AspeedBoardState *bmc); +static void ast2500_evb_i2c_init(AspeedBoardState *bmc); + static const AspeedBoardConfig aspeed_boards[] = { [PALMETTO_BMC] = { .soc_name = "ast2400-a1", @@ -89,6 +93,7 @@ static const AspeedBoardConfig aspeed_boards[] = { .fmc_model = "n25q256a", .spi_model = "mx25l25635e", .num_cs = 1, + .i2c_init = palmetto_bmc_i2c_init, }, [AST2500_EVB] = { .soc_name = "ast2500-a1", @@ -96,6 +101,7 @@ static const AspeedBoardConfig aspeed_boards[] = { .fmc_model = "n25q256a", .spi_model = "mx25l25635e", .num_cs = 1, + .i2c_init = ast2500_evb_i2c_init, }, [ROMULUS_BMC] = { .soc_name = "ast2500-a1", @@ -174,8 +180,8 @@ static void aspeed_board_init(MachineState *machine, sc = ASPEED_SOC_GET_CLASS(&bmc->soc); - object_property_set_int(OBJECT(&bmc->soc), ram_size, "ram-size", - &error_abort); + object_property_set_uint(OBJECT(&bmc->soc), ram_size, "ram-size", + &error_abort); object_property_set_int(OBJECT(&bmc->soc), cfg->hw_strap1, "hw-strap1", &error_abort); object_property_set_int(OBJECT(&bmc->soc), cfg->num_cs, "num-cs", @@ -187,8 +193,8 @@ static void aspeed_board_init(MachineState *machine, * Allocate RAM after the memory controller has checked the size * was valid. If not, a default value is used. */ - ram_size = object_property_get_int(OBJECT(&bmc->soc), "ram-size", - &error_abort); + ram_size = object_property_get_uint(OBJECT(&bmc->soc), "ram-size", + &error_abort); memory_region_allocate_system_memory(&bmc->ram, NULL, "ram", ram_size); memory_region_add_subregion(get_system_memory(), sc->info->sdram_base, @@ -210,7 +216,7 @@ static void aspeed_board_init(MachineState *machine, * SoC and 128MB for the AST2500 SoC, which is twice as big as * needed by the flash modules of the Aspeed machines. */ - memory_region_init_rom(boot_rom, OBJECT(bmc), "aspeed.boot_rom", + memory_region_init_rom_nomigrate(boot_rom, OBJECT(bmc), "aspeed.boot_rom", fl->size, &error_abort); memory_region_add_subregion(get_system_memory(), FIRMWARE_ADDR, boot_rom); @@ -223,9 +229,31 @@ static void aspeed_board_init(MachineState *machine, aspeed_board_binfo.ram_size = ram_size; aspeed_board_binfo.loader_start = sc->info->sdram_base; + if (cfg->i2c_init) { + cfg->i2c_init(bmc); + } + arm_load_kernel(ARM_CPU(first_cpu), &aspeed_board_binfo); } +static void palmetto_bmc_i2c_init(AspeedBoardState *bmc) +{ + AspeedSoCState *soc = &bmc->soc; + DeviceState *dev; + + /* The palmetto platform expects a ds3231 RTC but a ds1338 is + * enough to provide basic RTC features. Alarms will be missing */ + i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 0), "ds1338", 0x68); + + /* add a TMP423 temperature sensor */ + dev = i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 2), + "tmp423", 0x4c); + object_property_set_int(OBJECT(dev), 31000, "temperature0", &error_abort); + object_property_set_int(OBJECT(dev), 28000, "temperature1", &error_abort); + object_property_set_int(OBJECT(dev), 20000, "temperature2", &error_abort); + object_property_set_int(OBJECT(dev), 110000, "temperature3", &error_abort); +} + static void palmetto_bmc_init(MachineState *machine) { aspeed_board_init(machine, &aspeed_boards[PALMETTO_BMC]); @@ -250,6 +278,14 @@ static const TypeInfo palmetto_bmc_type = { .class_init = palmetto_bmc_class_init, }; +static void ast2500_evb_i2c_init(AspeedBoardState *bmc) +{ + AspeedSoCState *soc = &bmc->soc; + + /* The AST2500 EVB expects a LM75 but a TMP105 is compatible */ + i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 7), "tmp105", 0x4d); +} + static void ast2500_evb_init(MachineState *machine) { aspeed_board_init(machine, &aspeed_boards[AST2500_EVB]); diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c index 4937e2bc83..5529024edf 100644 --- a/hw/arm/aspeed_soc.c +++ b/hw/arm/aspeed_soc.c @@ -62,6 +62,7 @@ static const AspeedSoCInfo aspeed_socs[] = { .spi_bases = aspeed_soc_ast2400_spi_bases, .fmc_typename = "aspeed.smc.fmc", .spi_typename = aspeed_soc_ast2400_typenames, + .wdts_num = 2, }, { .name = "ast2400-a1", .cpu_model = "arm926", @@ -72,6 +73,7 @@ static const AspeedSoCInfo aspeed_socs[] = { .spi_bases = aspeed_soc_ast2400_spi_bases, .fmc_typename = "aspeed.smc.fmc", .spi_typename = aspeed_soc_ast2400_typenames, + .wdts_num = 2, }, { .name = "ast2400", .cpu_model = "arm926", @@ -82,6 +84,7 @@ static const AspeedSoCInfo aspeed_socs[] = { .spi_bases = aspeed_soc_ast2400_spi_bases, .fmc_typename = "aspeed.smc.fmc", .spi_typename = aspeed_soc_ast2400_typenames, + .wdts_num = 2, }, { .name = "ast2500-a1", .cpu_model = "arm1176", @@ -92,6 +95,7 @@ static const AspeedSoCInfo aspeed_socs[] = { .spi_bases = aspeed_soc_ast2500_spi_bases, .fmc_typename = "aspeed.smc.ast2500-fmc", .spi_typename = aspeed_soc_ast2500_typenames, + .wdts_num = 3, }, }; @@ -175,9 +179,11 @@ static void aspeed_soc_init(Object *obj) object_property_add_alias(obj, "ram-size", OBJECT(&s->sdmc), "ram-size", &error_abort); - object_initialize(&s->wdt, sizeof(s->wdt), TYPE_ASPEED_WDT); - object_property_add_child(obj, "wdt", OBJECT(&s->wdt), NULL); - qdev_set_parent_bus(DEVICE(&s->wdt), sysbus_get_default()); + for (i = 0; i < sc->info->wdts_num; i++) { + object_initialize(&s->wdt[i], sizeof(s->wdt[i]), TYPE_ASPEED_WDT); + object_property_add_child(obj, "wdt[*]", OBJECT(&s->wdt[i]), NULL); + qdev_set_parent_bus(DEVICE(&s->wdt[i]), sysbus_get_default()); + } object_initialize(&s->ftgmac100, sizeof(s->ftgmac100), TYPE_FTGMAC100); object_property_add_child(obj, "ftgmac100", OBJECT(&s->ftgmac100), NULL); @@ -205,7 +211,7 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) } /* SRAM */ - memory_region_init_ram(&s->sram, OBJECT(dev), "aspeed.sram", + memory_region_init_ram_nomigrate(&s->sram, OBJECT(dev), "aspeed.sram", sc->info->sram_size, &err); if (err) { error_propagate(errp, err); @@ -300,12 +306,15 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdmc), 0, ASPEED_SOC_SDMC_BASE); /* Watch dog */ - object_property_set_bool(OBJECT(&s->wdt), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; + for (i = 0; i < sc->info->wdts_num; i++) { + object_property_set_bool(OBJECT(&s->wdt[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0, + ASPEED_SOC_WDT_BASE + i * 0x20); } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt), 0, ASPEED_SOC_WDT_BASE); /* Net */ qdev_set_nic_properties(DEVICE(&s->ftgmac100), &nd_table[0]); diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c index 369ef1e3bd..12e0dd11af 100644 --- a/hw/arm/bcm2835_peripherals.c +++ b/hw/arm/bcm2835_peripherals.c @@ -13,7 +13,6 @@ #include "hw/arm/bcm2835_peripherals.h" #include "hw/misc/bcm2835_mbox_defs.h" #include "hw/arm/raspi_platform.h" -#include "sysemu/char.h" #include "sysemu/sysemu.h" /* Peripheral base address on the VC (GPU) system bus */ @@ -126,7 +125,7 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) Object *obj; MemoryRegion *ram; Error *err = NULL; - uint32_t ram_size, vcram_size; + uint64_t ram_size, vcram_size; int n; obj = object_property_get_link(OBJECT(dev), "ram", &err); @@ -208,15 +207,14 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) INTERRUPT_ARM_MAILBOX)); /* Framebuffer */ - vcram_size = (uint32_t)object_property_get_int(OBJECT(s), "vcram-size", - &err); + vcram_size = object_property_get_uint(OBJECT(s), "vcram-size", &err); if (err) { error_propagate(errp, err); return; } - object_property_set_int(OBJECT(&s->fb), ram_size - vcram_size, - "vcram-base", &err); + object_property_set_uint(OBJECT(&s->fb), ram_size - vcram_size, + "vcram-base", &err); if (err) { error_propagate(errp, err); return; diff --git a/hw/arm/exynos4210.c b/hw/arm/exynos4210.c index 960f27e45a..f9e79f3ebb 100644 --- a/hw/arm/exynos4210.c +++ b/hw/arm/exynos4210.c @@ -26,6 +26,7 @@ #include "qemu-common.h" #include "qemu/log.h" #include "cpu.h" +#include "hw/cpu/a9mpcore.h" #include "hw/boards.h" #include "sysemu/sysemu.h" #include "hw/sysbus.h" @@ -86,6 +87,9 @@ /* Clock controller SFR base address */ #define EXYNOS4210_CLK_BASE_ADDR 0x10030000 +/* PRNG/HASH SFR base address */ +#define EXYNOS4210_RNG_BASE_ADDR 0x10830400 + /* Display controllers (FIMD) */ #define EXYNOS4210_FIMD0_BASE_ADDR 0x11C00000 @@ -160,16 +164,14 @@ static uint64_t exynos4210_calc_affinity(int cpu) return mp_affinity; } -Exynos4210State *exynos4210_init(MemoryRegion *system_mem, - unsigned long ram_size) +Exynos4210State *exynos4210_init(MemoryRegion *system_mem) { - int i, n; Exynos4210State *s = g_new(Exynos4210State, 1); qemu_irq gate_irq[EXYNOS4210_NCPUS][EXYNOS4210_IRQ_GATE_NINPUTS]; - unsigned long mem_size; - DeviceState *dev; SysBusDevice *busdev; ObjectClass *cpu_oc; + DeviceState *dev; + int i, n; cpu_oc = cpu_class_by_name(TYPE_ARM_CPU, "cortex-a9"); assert(cpu_oc); @@ -213,7 +215,7 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem, } /* Private memory region and Internal GIC */ - dev = qdev_create(NULL, "a9mpcore_priv"); + dev = qdev_create(NULL, TYPE_A9MPCORE_PRIV); qdev_prop_set_uint32(dev, "num-cpu", EXYNOS4210_NCPUS); qdev_init_nofail(dev); busdev = SYS_BUS_DEVICE(dev); @@ -279,7 +281,6 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem, /* Internal ROM */ memory_region_init_ram(&s->irom_mem, NULL, "exynos4210.irom", EXYNOS4210_IROM_SIZE, &error_fatal); - vmstate_register_ram_global(&s->irom_mem); memory_region_set_readonly(&s->irom_mem, true); memory_region_add_subregion(system_mem, EXYNOS4210_IROM_BASE_ADDR, &s->irom_mem); @@ -295,26 +296,9 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem, /* Internal RAM */ memory_region_init_ram(&s->iram_mem, NULL, "exynos4210.iram", EXYNOS4210_IRAM_SIZE, &error_fatal); - vmstate_register_ram_global(&s->iram_mem); memory_region_add_subregion(system_mem, EXYNOS4210_IRAM_BASE_ADDR, &s->iram_mem); - /* DRAM */ - mem_size = ram_size; - if (mem_size > EXYNOS4210_DRAM_MAX_SIZE) { - memory_region_init_ram(&s->dram1_mem, NULL, "exynos4210.dram1", - mem_size - EXYNOS4210_DRAM_MAX_SIZE, &error_fatal); - vmstate_register_ram_global(&s->dram1_mem); - memory_region_add_subregion(system_mem, EXYNOS4210_DRAM1_BASE_ADDR, - &s->dram1_mem); - mem_size = EXYNOS4210_DRAM_MAX_SIZE; - } - memory_region_init_ram(&s->dram0_mem, NULL, "exynos4210.dram0", mem_size, - &error_fatal); - vmstate_register_ram_global(&s->dram0_mem); - memory_region_add_subregion(system_mem, EXYNOS4210_DRAM0_BASE_ADDR, - &s->dram0_mem); - /* PMU. * The only reason of existence at the moment is that secondary CPU boot * loader uses PMU INFORM5 register as a holding pen. @@ -322,6 +306,7 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem, sysbus_create_simple("exynos4210.pmu", EXYNOS4210_PMU_BASE_ADDR, NULL); sysbus_create_simple("exynos4210.clk", EXYNOS4210_CLK_BASE_ADDR, NULL); + sysbus_create_simple("exynos4210.rng", EXYNOS4210_RNG_BASE_ADDR, NULL); /* PWM */ sysbus_create_varargs("exynos4210.pwm", EXYNOS4210_PWM_BASE_ADDR, diff --git a/hw/arm/exynos4_boards.c b/hw/arm/exynos4_boards.c index 4853c31802..7c03ed32b7 100644 --- a/hw/arm/exynos4_boards.c +++ b/hw/arm/exynos4_boards.c @@ -22,6 +22,7 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu/error-report.h" #include "qemu-common.h" #include "cpu.h" @@ -56,6 +57,12 @@ typedef enum Exynos4BoardType { EXYNOS4_NUM_OF_BOARDS } Exynos4BoardType; +typedef struct Exynos4BoardState { + Exynos4210State *soc; + MemoryRegion dram0_mem; + MemoryRegion dram1_mem; +} Exynos4BoardState; + static int exynos4_board_id[EXYNOS4_NUM_OF_BOARDS] = { [EXYNOS4_BOARD_NURI] = 0xD33, [EXYNOS4_BOARD_SMDKC210] = 0xB16, @@ -96,9 +103,32 @@ static void lan9215_init(uint32_t base, qemu_irq irq) } } -static Exynos4210State *exynos4_boards_init_common(MachineState *machine, - Exynos4BoardType board_type) +static void exynos4_boards_init_ram(Exynos4BoardState *s, + MemoryRegion *system_mem, + unsigned long ram_size) +{ + unsigned long mem_size = ram_size; + + if (mem_size > EXYNOS4210_DRAM_MAX_SIZE) { + memory_region_init_ram(&s->dram1_mem, NULL, "exynos4210.dram1", + mem_size - EXYNOS4210_DRAM_MAX_SIZE, + &error_fatal); + memory_region_add_subregion(system_mem, EXYNOS4210_DRAM1_BASE_ADDR, + &s->dram1_mem); + mem_size = EXYNOS4210_DRAM_MAX_SIZE; + } + + memory_region_init_ram(&s->dram0_mem, NULL, "exynos4210.dram0", mem_size, + &error_fatal); + memory_region_add_subregion(system_mem, EXYNOS4210_DRAM0_BASE_ADDR, + &s->dram0_mem); +} + +static Exynos4BoardState * +exynos4_boards_init_common(MachineState *machine, + Exynos4BoardType board_type) { + Exynos4BoardState *s = g_new(Exynos4BoardState, 1); MachineClass *mc = MACHINE_GET_CLASS(machine); if (smp_cpus != EXYNOS4210_NCPUS && !qtest_enabled()) { @@ -127,8 +157,12 @@ static Exynos4210State *exynos4_boards_init_common(MachineState *machine, machine->kernel_cmdline, machine->initrd_filename); - return exynos4210_init(get_system_memory(), - exynos4_board_ram_size[board_type]); + exynos4_boards_init_ram(s, get_system_memory(), + exynos4_board_ram_size[board_type]); + + s->soc = exynos4210_init(get_system_memory()); + + return s; } static void nuri_init(MachineState *machine) @@ -140,11 +174,11 @@ static void nuri_init(MachineState *machine) static void smdkc210_init(MachineState *machine) { - Exynos4210State *s = exynos4_boards_init_common(machine, - EXYNOS4_BOARD_SMDKC210); + Exynos4BoardState *s = exynos4_boards_init_common(machine, + EXYNOS4_BOARD_SMDKC210); lan9215_init(SMDK_LAN9118_BASE_ADDR, - qemu_irq_invert(s->irq_table[exynos4210_get_irq(37, 1)])); + qemu_irq_invert(s->soc->irq_table[exynos4210_get_irq(37, 1)])); arm_load_kernel(ARM_CPU(first_cpu), &exynos4_board_binfo); } diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c index 9056f27bf8..8cff3c1f7b 100644 --- a/hw/arm/fsl-imx25.c +++ b/hw/arm/fsl-imx25.c @@ -30,7 +30,7 @@ #include "sysemu/sysemu.h" #include "exec/address-spaces.h" #include "hw/boards.h" -#include "sysemu/char.h" +#include "chardev/char.h" static void fsl_imx25_init(Object *obj) { @@ -249,7 +249,7 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp) } /* initialize 2 x 16 KB ROM */ - memory_region_init_rom(&s->rom[0], NULL, + memory_region_init_rom_nomigrate(&s->rom[0], NULL, "imx25.rom0", FSL_IMX25_ROM0_SIZE, &err); if (err) { error_propagate(errp, err); @@ -257,7 +257,7 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp) } memory_region_add_subregion(get_system_memory(), FSL_IMX25_ROM0_ADDR, &s->rom[0]); - memory_region_init_rom(&s->rom[1], NULL, + memory_region_init_rom_nomigrate(&s->rom[1], NULL, "imx25.rom1", FSL_IMX25_ROM1_SIZE, &err); if (err) { error_propagate(errp, err); @@ -275,7 +275,6 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp) } memory_region_add_subregion(get_system_memory(), FSL_IMX25_IRAM_ADDR, &s->iram); - vmstate_register_ram_global(&s->iram); /* internal RAM (128 KB) is aliased over 128 MB - 128 KB */ memory_region_init_alias(&s->iram_alias, NULL, "imx25.iram_alias", diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c index d7e2d832b2..90278758f9 100644 --- a/hw/arm/fsl-imx31.c +++ b/hw/arm/fsl-imx31.c @@ -27,7 +27,7 @@ #include "sysemu/sysemu.h" #include "exec/address-spaces.h" #include "hw/boards.h" -#include "sysemu/char.h" +#include "chardev/char.h" static void fsl_imx31_init(Object *obj) { @@ -219,7 +219,7 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp) } /* On a real system, the first 16k is a `secure boot rom' */ - memory_region_init_rom(&s->secure_rom, NULL, "imx31.secure_rom", + memory_region_init_rom_nomigrate(&s->secure_rom, NULL, "imx31.secure_rom", FSL_IMX31_SECURE_ROM_SIZE, &err); if (err) { error_propagate(errp, err); @@ -229,7 +229,7 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp) &s->secure_rom); /* There is also a 16k ROM */ - memory_region_init_rom(&s->rom, NULL, "imx31.rom", + memory_region_init_rom_nomigrate(&s->rom, NULL, "imx31.rom", FSL_IMX31_ROM_SIZE, &err); if (err) { error_propagate(errp, err); @@ -247,7 +247,6 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp) } memory_region_add_subregion(get_system_memory(), FSL_IMX31_IRAM_ADDR, &s->iram); - vmstate_register_ram_global(&s->iram); /* internal RAM (16 KB) is aliased over 256 MB - 16 KB */ memory_region_init_alias(&s->iram_alias, NULL, "imx31.iram_alias", diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c index 6969e734ad..576c6631a1 100644 --- a/hw/arm/fsl-imx6.c +++ b/hw/arm/fsl-imx6.c @@ -24,7 +24,7 @@ #include "qemu-common.h" #include "hw/arm/fsl-imx6.h" #include "sysemu/sysemu.h" -#include "sysemu/char.h" +#include "chardev/char.h" #include "qemu/error-report.h" #define NAME_SIZE 20 @@ -399,7 +399,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) FSL_IMX6_ENET_MAC_1588_IRQ)); /* ROM memory */ - memory_region_init_rom(&s->rom, NULL, "imx6.rom", + memory_region_init_rom_nomigrate(&s->rom, NULL, "imx6.rom", FSL_IMX6_ROM_SIZE, &err); if (err) { error_propagate(errp, err); @@ -409,7 +409,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) &s->rom); /* CAAM memory */ - memory_region_init_rom(&s->caam, NULL, "imx6.caam", + memory_region_init_rom_nomigrate(&s->caam, NULL, "imx6.caam", FSL_IMX6_CAAM_MEM_SIZE, &err); if (err) { error_propagate(errp, err); @@ -427,7 +427,6 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) } memory_region_add_subregion(get_system_memory(), FSL_IMX6_OCRAM_ADDR, &s->ocram); - vmstate_register_ram_global(&s->ocram); /* internal OCRAM (256 KB) is aliased over 1 MB */ memory_region_init_alias(&s->ocram_alias, NULL, "imx6.ocram_alias", diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c index 0a4508cef3..20e60f15c4 100644 --- a/hw/arm/highbank.c +++ b/hw/arm/highbank.c @@ -108,9 +108,9 @@ static void hb_regs_write(void *opaque, hwaddr offset, if (offset == 0xf00) { if (value == 1 || value == 2) { - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); } else if (value == 3) { - qemu_system_shutdown_request(); + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); } } @@ -276,7 +276,7 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) memory_region_add_subregion(sysmem, 0, dram); sysram = g_new(MemoryRegion, 1); - memory_region_init_ram(sysram, NULL, "highbank.sysram", 0x8000, + memory_region_init_ram_nomigrate(sysram, NULL, "highbank.sysram", 0x8000, &error_fatal); memory_region_add_subregion(sysmem, 0xfff88000, sysram); if (bios_name != NULL) { @@ -383,9 +383,9 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) highbank_binfo.write_board_setup = hb_write_board_setup; highbank_binfo.secure_board_setup = true; } else { - error_report("WARNING: cannot load built-in Monitor support " - "if KVM is enabled. Some guests (such as Linux) " - "may not boot."); + warn_report("cannot load built-in Monitor support " + "if KVM is enabled. Some guests (such as Linux) " + "may not boot."); } arm_load_kernel(ARM_CPU(first_cpu), &highbank_binfo); diff --git a/hw/arm/imx25_pdk.c b/hw/arm/imx25_pdk.c index 44e741fde3..7d42c74001 100644 --- a/hw/arm/imx25_pdk.c +++ b/hw/arm/imx25_pdk.c @@ -80,9 +80,9 @@ static void imx25_pdk_init(MachineState *machine) /* We need to initialize our memory */ if (machine->ram_size > (FSL_IMX25_SDRAM0_SIZE + FSL_IMX25_SDRAM1_SIZE)) { - error_report("WARNING: RAM size " RAM_ADDR_FMT " above max supported, " - "reduced to %x", machine->ram_size, - FSL_IMX25_SDRAM0_SIZE + FSL_IMX25_SDRAM1_SIZE); + warn_report("RAM size " RAM_ADDR_FMT " above max supported, " + "reduced to %x", machine->ram_size, + FSL_IMX25_SDRAM0_SIZE + FSL_IMX25_SDRAM1_SIZE); machine->ram_size = FSL_IMX25_SDRAM0_SIZE + FSL_IMX25_SDRAM1_SIZE; } diff --git a/hw/arm/integratorcp.c b/hw/arm/integratorcp.c index 5610ffc9ce..d79221d166 100644 --- a/hw/arm/integratorcp.c +++ b/hw/arm/integratorcp.c @@ -158,7 +158,7 @@ static void integratorcm_do_remap(IntegratorCMState *s) static void integratorcm_set_ctrl(IntegratorCMState *s, uint32_t value) { if (value & 8) { - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); } if ((s->cm_ctrl ^ value) & 1) { /* (value & 1) != 0 means the green "MISC LED" is lit. @@ -276,7 +276,7 @@ static void integratorcm_init(Object *obj) s->cm_init = 0x00000112; s->cm_refcnt_offset = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 24, 1000); - memory_region_init_ram(&s->flash, obj, "integrator.flash", 0x100000, + memory_region_init_ram_nomigrate(&s->flash, obj, "integrator.flash", 0x100000, &error_fatal); vmstate_register_ram_global(&s->flash); diff --git a/hw/arm/kzm.c b/hw/arm/kzm.c index 2c96ee33b6..3ed6577a55 100644 --- a/hw/arm/kzm.c +++ b/hw/arm/kzm.c @@ -79,9 +79,9 @@ static void kzm_init(MachineState *machine) /* Check the amount of memory is compatible with the SOC */ if (machine->ram_size > (FSL_IMX31_SDRAM0_SIZE + FSL_IMX31_SDRAM1_SIZE)) { - error_report("WARNING: RAM size " RAM_ADDR_FMT " above max supported, " - "reduced to %x", machine->ram_size, - FSL_IMX31_SDRAM0_SIZE + FSL_IMX31_SDRAM1_SIZE); + warn_report("RAM size " RAM_ADDR_FMT " above max supported, " + "reduced to %x", machine->ram_size, + FSL_IMX31_SDRAM0_SIZE + FSL_IMX31_SDRAM1_SIZE); machine->ram_size = FSL_IMX31_SDRAM0_SIZE + FSL_IMX31_SDRAM1_SIZE; } diff --git a/hw/arm/mainstone.c b/hw/arm/mainstone.c index f962236cf4..fb268e691e 100644 --- a/hw/arm/mainstone.c +++ b/hw/arm/mainstone.c @@ -130,7 +130,6 @@ static void mainstone_common_init(MemoryRegion *address_space_mem, mpu = pxa270_init(address_space_mem, mainstone_binfo.ram_size, cpu_model); memory_region_init_ram(rom, NULL, "mainstone.rom", MAINSTONE_ROM, &error_fatal); - vmstate_register_ram_global(rom); memory_region_set_readonly(rom, true); memory_region_add_subregion(address_space_mem, 0, rom); diff --git a/hw/arm/mps2.c b/hw/arm/mps2.c new file mode 100644 index 0000000000..f727b4378b --- /dev/null +++ b/hw/arm/mps2.c @@ -0,0 +1,385 @@ +/* + * ARM V2M MPS2 board emulation. + * + * Copyright (c) 2017 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* The MPS2 and MPS2+ dev boards are FPGA based (the 2+ has a bigger + * FPGA but is otherwise the same as the 2). Since the CPU itself + * and most of the devices are in the FPGA, the details of the board + * as seen by the guest depend significantly on the FPGA image. + * We model the following FPGA images: + * "mps2-an385" -- Cortex-M3 as documented in ARM Application Note AN385 + * "mps2-an511" -- Cortex-M3 'DesignStart' as documented in AN511 + * + * Links to the TRM for the board itself and to the various Application + * Notes which document the FPGA images can be found here: + * https://developer.arm.com/products/system-design/development-boards/cortex-m-prototyping-system + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "hw/arm/arm.h" +#include "hw/arm/armv7m.h" +#include "hw/or-irq.h" +#include "hw/boards.h" +#include "exec/address-spaces.h" +#include "sysemu/sysemu.h" +#include "hw/misc/unimp.h" +#include "hw/char/cmsdk-apb-uart.h" +#include "hw/timer/cmsdk-apb-timer.h" +#include "hw/misc/mps2-scc.h" +#include "hw/devices.h" +#include "net/net.h" + +typedef enum MPS2FPGAType { + FPGA_AN385, + FPGA_AN511, +} MPS2FPGAType; + +typedef struct { + MachineClass parent; + MPS2FPGAType fpga_type; + const char *cpu_model; + uint32_t scc_id; +} MPS2MachineClass; + +typedef struct { + MachineState parent; + + ARMv7MState armv7m; + MemoryRegion psram; + MemoryRegion ssram1; + MemoryRegion ssram1_m; + MemoryRegion ssram23; + MemoryRegion ssram23_m; + MemoryRegion blockram; + MemoryRegion blockram_m1; + MemoryRegion blockram_m2; + MemoryRegion blockram_m3; + MemoryRegion sram; + MPS2SCC scc; +} MPS2MachineState; + +#define TYPE_MPS2_MACHINE "mps2" +#define TYPE_MPS2_AN385_MACHINE MACHINE_TYPE_NAME("mps2-an385") +#define TYPE_MPS2_AN511_MACHINE MACHINE_TYPE_NAME("mps2-an511") + +#define MPS2_MACHINE(obj) \ + OBJECT_CHECK(MPS2MachineState, obj, TYPE_MPS2_MACHINE) +#define MPS2_MACHINE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(MPS2MachineClass, obj, TYPE_MPS2_MACHINE) +#define MPS2_MACHINE_CLASS(klass) \ + OBJECT_CLASS_CHECK(MPS2MachineClass, klass, TYPE_MPS2_MACHINE) + +/* Main SYSCLK frequency in Hz */ +#define SYSCLK_FRQ 25000000 + +/* Initialize the auxiliary RAM region @mr and map it into + * the memory map at @base. + */ +static void make_ram(MemoryRegion *mr, const char *name, + hwaddr base, hwaddr size) +{ + memory_region_init_ram(mr, NULL, name, size, &error_fatal); + memory_region_add_subregion(get_system_memory(), base, mr); +} + +/* Create an alias of an entire original MemoryRegion @orig + * located at @base in the memory map. + */ +static void make_ram_alias(MemoryRegion *mr, const char *name, + MemoryRegion *orig, hwaddr base) +{ + memory_region_init_alias(mr, NULL, name, orig, 0, + memory_region_size(orig)); + memory_region_add_subregion(get_system_memory(), base, mr); +} + +static void mps2_common_init(MachineState *machine) +{ + MPS2MachineState *mms = MPS2_MACHINE(machine); + MPS2MachineClass *mmc = MPS2_MACHINE_GET_CLASS(machine); + MemoryRegion *system_memory = get_system_memory(); + DeviceState *armv7m, *sccdev; + + if (!machine->cpu_model) { + machine->cpu_model = mmc->cpu_model; + } + + if (strcmp(machine->cpu_model, mmc->cpu_model) != 0) { + error_report("This board can only be used with CPU %s", mmc->cpu_model); + exit(1); + } + + /* The FPGA images have an odd combination of different RAMs, + * because in hardware they are different implementations and + * connected to different buses, giving varying performance/size + * tradeoffs. For QEMU they're all just RAM, though. We arbitrarily + * call the 16MB our "system memory", as it's the largest lump. + * + * Common to both boards: + * 0x21000000..0x21ffffff : PSRAM (16MB) + * AN385 only: + * 0x00000000 .. 0x003fffff : ZBT SSRAM1 + * 0x00400000 .. 0x007fffff : mirror of ZBT SSRAM1 + * 0x20000000 .. 0x203fffff : ZBT SSRAM 2&3 + * 0x20400000 .. 0x207fffff : mirror of ZBT SSRAM 2&3 + * 0x01000000 .. 0x01003fff : block RAM (16K) + * 0x01004000 .. 0x01007fff : mirror of above + * 0x01008000 .. 0x0100bfff : mirror of above + * 0x0100c000 .. 0x0100ffff : mirror of above + * AN511 only: + * 0x00000000 .. 0x0003ffff : FPGA block RAM + * 0x00400000 .. 0x007fffff : ZBT SSRAM1 + * 0x20000000 .. 0x2001ffff : SRAM + * 0x20400000 .. 0x207fffff : ZBT SSRAM 2&3 + * + * The AN385 has a feature where the lowest 16K can be mapped + * either to the bottom of the ZBT SSRAM1 or to the block RAM. + * This is of no use for QEMU so we don't implement it (as if + * zbt_boot_ctrl is always zero). + */ + memory_region_allocate_system_memory(&mms->psram, + NULL, "mps.ram", 0x1000000); + memory_region_add_subregion(system_memory, 0x21000000, &mms->psram); + + switch (mmc->fpga_type) { + case FPGA_AN385: + make_ram(&mms->ssram1, "mps.ssram1", 0x0, 0x400000); + make_ram_alias(&mms->ssram1_m, "mps.ssram1_m", &mms->ssram1, 0x400000); + make_ram(&mms->ssram23, "mps.ssram23", 0x20000000, 0x400000); + make_ram_alias(&mms->ssram23_m, "mps.ssram23_m", + &mms->ssram23, 0x20400000); + make_ram(&mms->blockram, "mps.blockram", 0x01000000, 0x4000); + make_ram_alias(&mms->blockram_m1, "mps.blockram_m1", + &mms->blockram, 0x01004000); + make_ram_alias(&mms->blockram_m2, "mps.blockram_m2", + &mms->blockram, 0x01008000); + make_ram_alias(&mms->blockram_m3, "mps.blockram_m3", + &mms->blockram, 0x0100c000); + break; + case FPGA_AN511: + make_ram(&mms->blockram, "mps.blockram", 0x0, 0x40000); + make_ram(&mms->ssram1, "mps.ssram1", 0x00400000, 0x00800000); + make_ram(&mms->sram, "mps.sram", 0x20000000, 0x20000); + make_ram(&mms->ssram23, "mps.ssram23", 0x20400000, 0x400000); + break; + default: + g_assert_not_reached(); + } + + object_initialize(&mms->armv7m, sizeof(mms->armv7m), TYPE_ARMV7M); + armv7m = DEVICE(&mms->armv7m); + qdev_set_parent_bus(armv7m, sysbus_get_default()); + switch (mmc->fpga_type) { + case FPGA_AN385: + qdev_prop_set_uint32(armv7m, "num-irq", 32); + break; + case FPGA_AN511: + qdev_prop_set_uint32(armv7m, "num-irq", 64); + break; + default: + g_assert_not_reached(); + } + qdev_prop_set_string(armv7m, "cpu-model", machine->cpu_model); + object_property_set_link(OBJECT(&mms->armv7m), OBJECT(system_memory), + "memory", &error_abort); + object_property_set_bool(OBJECT(&mms->armv7m), true, "realized", + &error_fatal); + + create_unimplemented_device("zbtsmram mirror", 0x00400000, 0x00400000); + create_unimplemented_device("RESERVED 1", 0x00800000, 0x00800000); + create_unimplemented_device("Block RAM", 0x01000000, 0x00010000); + create_unimplemented_device("RESERVED 2", 0x01010000, 0x1EFF0000); + create_unimplemented_device("RESERVED 3", 0x20800000, 0x00800000); + create_unimplemented_device("PSRAM", 0x21000000, 0x01000000); + /* These three ranges all cover multiple devices; we may implement + * some of them below (in which case the real device takes precedence + * over the unimplemented-region mapping). + */ + create_unimplemented_device("CMSDK APB peripheral region @0x40000000", + 0x40000000, 0x00010000); + create_unimplemented_device("CMSDK peripheral region @0x40010000", + 0x40010000, 0x00010000); + create_unimplemented_device("Extra peripheral region @0x40020000", + 0x40020000, 0x00010000); + create_unimplemented_device("RESERVED 4", 0x40030000, 0x001D0000); + create_unimplemented_device("VGA", 0x41000000, 0x0200000); + + switch (mmc->fpga_type) { + case FPGA_AN385: + { + /* The overflow IRQs for UARTs 0, 1 and 2 are ORed together. + * Overflow for UARTs 4 and 5 doesn't trigger any interrupt. + */ + Object *orgate; + DeviceState *orgate_dev; + int i; + + orgate = object_new(TYPE_OR_IRQ); + object_property_set_int(orgate, 6, "num-lines", &error_fatal); + object_property_set_bool(orgate, true, "realized", &error_fatal); + orgate_dev = DEVICE(orgate); + qdev_connect_gpio_out(orgate_dev, 0, qdev_get_gpio_in(armv7m, 12)); + + for (i = 0; i < 5; i++) { + static const hwaddr uartbase[] = {0x40004000, 0x40005000, + 0x40006000, 0x40007000, + 0x40009000}; + Chardev *uartchr = i < MAX_SERIAL_PORTS ? serial_hds[i] : NULL; + /* RX irq number; TX irq is always one greater */ + static const int uartirq[] = {0, 2, 4, 18, 20}; + qemu_irq txovrint = NULL, rxovrint = NULL; + + if (i < 3) { + txovrint = qdev_get_gpio_in(orgate_dev, i * 2); + rxovrint = qdev_get_gpio_in(orgate_dev, i * 2 + 1); + } + + cmsdk_apb_uart_create(uartbase[i], + qdev_get_gpio_in(armv7m, uartirq[i] + 1), + qdev_get_gpio_in(armv7m, uartirq[i]), + txovrint, rxovrint, + NULL, + uartchr, SYSCLK_FRQ); + } + break; + } + case FPGA_AN511: + { + /* The overflow IRQs for all UARTs are ORed together. + * Tx and Rx IRQs for each UART are ORed together. + */ + Object *orgate; + DeviceState *orgate_dev; + int i; + + orgate = object_new(TYPE_OR_IRQ); + object_property_set_int(orgate, 10, "num-lines", &error_fatal); + object_property_set_bool(orgate, true, "realized", &error_fatal); + orgate_dev = DEVICE(orgate); + qdev_connect_gpio_out(orgate_dev, 0, qdev_get_gpio_in(armv7m, 12)); + + for (i = 0; i < 5; i++) { + /* system irq numbers for the combined tx/rx for each UART */ + static const int uart_txrx_irqno[] = {0, 2, 45, 46, 56}; + static const hwaddr uartbase[] = {0x40004000, 0x40005000, + 0x4002c000, 0x4002d000, + 0x4002e000}; + Chardev *uartchr = i < MAX_SERIAL_PORTS ? serial_hds[i] : NULL; + Object *txrx_orgate; + DeviceState *txrx_orgate_dev; + + txrx_orgate = object_new(TYPE_OR_IRQ); + object_property_set_int(txrx_orgate, 2, "num-lines", &error_fatal); + object_property_set_bool(txrx_orgate, true, "realized", + &error_fatal); + txrx_orgate_dev = DEVICE(txrx_orgate); + qdev_connect_gpio_out(txrx_orgate_dev, 0, + qdev_get_gpio_in(armv7m, uart_txrx_irqno[i])); + cmsdk_apb_uart_create(uartbase[i], + qdev_get_gpio_in(txrx_orgate_dev, 0), + qdev_get_gpio_in(txrx_orgate_dev, 1), + qdev_get_gpio_in(orgate_dev, 0), + qdev_get_gpio_in(orgate_dev, 1), + NULL, + uartchr, SYSCLK_FRQ); + } + break; + } + default: + g_assert_not_reached(); + } + + cmsdk_apb_timer_create(0x40000000, qdev_get_gpio_in(armv7m, 8), SYSCLK_FRQ); + cmsdk_apb_timer_create(0x40001000, qdev_get_gpio_in(armv7m, 9), SYSCLK_FRQ); + + object_initialize(&mms->scc, sizeof(mms->scc), TYPE_MPS2_SCC); + sccdev = DEVICE(&mms->scc); + qdev_set_parent_bus(armv7m, sysbus_get_default()); + qdev_prop_set_uint32(sccdev, "scc-cfg4", 0x2); + qdev_prop_set_uint32(sccdev, "scc-aid", 0x02000008); + qdev_prop_set_uint32(sccdev, "scc-id", mmc->scc_id); + object_property_set_bool(OBJECT(&mms->scc), true, "realized", + &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(sccdev), 0, 0x4002f000); + + /* In hardware this is a LAN9220; the LAN9118 is software compatible + * except that it doesn't support the checksum-offload feature. + */ + lan9118_init(&nd_table[0], 0x40200000, + qdev_get_gpio_in(armv7m, + mmc->fpga_type == FPGA_AN385 ? 13 : 47)); + + system_clock_scale = NANOSECONDS_PER_SECOND / SYSCLK_FRQ; + + armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, + 0x400000); +} + +static void mps2_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->init = mps2_common_init; + mc->max_cpus = 1; +} + +static void mps2_an385_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + MPS2MachineClass *mmc = MPS2_MACHINE_CLASS(oc); + + mc->desc = "ARM MPS2 with AN385 FPGA image for Cortex-M3"; + mmc->fpga_type = FPGA_AN385; + mmc->cpu_model = "cortex-m3"; + mmc->scc_id = 0x41040000 | (385 << 4); +} + +static void mps2_an511_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + MPS2MachineClass *mmc = MPS2_MACHINE_CLASS(oc); + + mc->desc = "ARM MPS2 with AN511 DesignStart FPGA image for Cortex-M3"; + mmc->fpga_type = FPGA_AN511; + mmc->cpu_model = "cortex-m3"; + mmc->scc_id = 0x4104000 | (511 << 4); +} + +static const TypeInfo mps2_info = { + .name = TYPE_MPS2_MACHINE, + .parent = TYPE_MACHINE, + .abstract = true, + .instance_size = sizeof(MPS2MachineState), + .class_size = sizeof(MPS2MachineClass), + .class_init = mps2_class_init, +}; + +static const TypeInfo mps2_an385_info = { + .name = TYPE_MPS2_AN385_MACHINE, + .parent = TYPE_MPS2_MACHINE, + .class_init = mps2_an385_class_init, +}; + +static const TypeInfo mps2_an511_info = { + .name = TYPE_MPS2_AN511_MACHINE, + .parent = TYPE_MPS2_MACHINE, + .class_init = mps2_an511_class_init, +}; + +static void mps2_machine_init(void) +{ + type_register_static(&mps2_info); + type_register_static(&mps2_an385_info); + type_register_static(&mps2_an511_info); +} + +type_init(mps2_machine_init); diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index cbbca4e17a..7e8ab3184c 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -898,7 +898,7 @@ static void mv88w8618_pit_write(void *opaque, hwaddr offset, case MP_BOARD_RESET: if (value == MP_BOARD_RESET_MAGIC) { - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); } break; } @@ -1606,7 +1606,6 @@ static void musicpal_init(MachineState *machine) memory_region_init_ram(sram, NULL, "musicpal.sram", MP_SRAM_SIZE, &error_fatal); - vmstate_register_ram_global(sram); memory_region_add_subregion(address_space_mem, MP_SRAM_BASE, sram); dev = sysbus_create_simple(TYPE_MV88W8618_PIC, MP_PIC_BASE, diff --git a/hw/arm/omap1.c b/hw/arm/omap1.c index b3cf0ec690..3d15ff6779 100644 --- a/hw/arm/omap1.c +++ b/hw/arm/omap1.c @@ -355,7 +355,7 @@ static void omap_wd_timer_write(void *opaque, hwaddr addr, /* XXX: on T|E hardware somehow this has no effect, * on Zire 71 it works as specified. */ s->reset = 1; - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); } } s->last_wr = value & 0xff; @@ -1545,8 +1545,10 @@ static inline void omap_clkm_idlect1_update(struct omap_mpu_state_s *s, if (value & (1 << 11)) { /* SETARM_IDLE */ cpu_interrupt(CPU(s->cpu), CPU_INTERRUPT_HALT); } - if (!(value & (1 << 10))) /* WKUP_MODE */ - qemu_system_shutdown_request(); /* XXX: disable wakeup from IRQ */ + if (!(value & (1 << 10))) { /* WKUP_MODE */ + /* XXX: disable wakeup from IRQ */ + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); + } #define SET_CANIDLE(clock, bit) \ if (diff & (1 << bit)) { \ @@ -1693,7 +1695,7 @@ static void omap_clkm_write(void *opaque, hwaddr addr, diff = s->clkm.arm_rstct1 ^ value; s->clkm.arm_rstct1 = value & 0x0007; if (value & 9) { - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); s->clkm.cold_start = 0xa; } if (diff & ~value & 4) { /* DSP_RST */ @@ -3880,7 +3882,6 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *system_memory, memory_region_add_subregion(system_memory, OMAP_EMIFF_BASE, &s->emiff_ram); memory_region_init_ram(&s->imif_ram, NULL, "omap1.sram", s->sram_size, &error_fatal); - vmstate_register_ram_global(&s->imif_ram); memory_region_add_subregion(system_memory, OMAP_IMIF_BASE, &s->imif_ram); omap_clkm_init(system_memory, 0xfffece00, 0xe1008000, s); diff --git a/hw/arm/omap2.c b/hw/arm/omap2.c index cf1b4ba58f..bbf0b7e188 100644 --- a/hw/arm/omap2.c +++ b/hw/arm/omap2.c @@ -30,7 +30,7 @@ #include "hw/arm/omap.h" #include "sysemu/sysemu.h" #include "qemu/timer.h" -#include "sysemu/char.h" +#include "chardev/char-fe.h" #include "hw/block/flash.h" #include "hw/arm/soc_dma.h" #include "hw/sysbus.h" @@ -1610,7 +1610,7 @@ static void omap_prcm_write(void *opaque, hwaddr addr, case 0x450: /* RM_RSTCTRL_WKUP */ /* TODO: reset */ if (value & 2) - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); break; case 0x454: /* RM_RSTTIME_WKUP */ s->rsttime_wkup = value & 0x1fff; @@ -2280,7 +2280,6 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem, memory_region_add_subregion(sysmem, OMAP2_Q2_BASE, &s->sdram); memory_region_init_ram(&s->sram, NULL, "omap2.sram", s->sram_size, &error_fatal); - vmstate_register_ram_global(&s->sram); memory_region_add_subregion(sysmem, OMAP2_SRAM_BASE, &s->sram); s->l4 = omap_l4_init(sysmem, OMAP2_L4_BASE, 54); diff --git a/hw/arm/omap_sx1.c b/hw/arm/omap_sx1.c index 5d74026cb2..9809106617 100644 --- a/hw/arm/omap_sx1.c +++ b/hw/arm/omap_sx1.c @@ -125,7 +125,6 @@ static void sx1_init(MachineState *machine, const int version) /* External Flash (EMIFS) */ memory_region_init_ram(flash, NULL, "omap_sx1.flash0-0", flash_size, &error_fatal); - vmstate_register_ram_global(flash); memory_region_set_readonly(flash, true); memory_region_add_subregion(address_space, OMAP_CS0_BASE, flash); @@ -167,9 +166,8 @@ static void sx1_init(MachineState *machine, const int version) if ((version == 1) && (dinfo = drive_get(IF_PFLASH, 0, fl_idx)) != NULL) { MemoryRegion *flash_1 = g_new(MemoryRegion, 1); - memory_region_init_ram(flash_1, NULL, "omap_sx1.flash1-0", flash1_size, - &error_fatal); - vmstate_register_ram_global(flash_1); + memory_region_init_ram(flash_1, NULL, "omap_sx1.flash1-0", + flash1_size, &error_fatal); memory_region_set_readonly(flash_1, true); memory_region_add_subregion(address_space, OMAP_CS1_BASE, flash_1); diff --git a/hw/arm/palm.c b/hw/arm/palm.c index 7f460732e3..64cf8ca921 100644 --- a/hw/arm/palm.c +++ b/hw/arm/palm.c @@ -216,7 +216,6 @@ static void palmte_init(MachineState *machine) /* External Flash (EMIFS) */ memory_region_init_ram(flash, NULL, "palmte.flash", flash_size, &error_fatal); - vmstate_register_ram_global(flash); memory_region_set_readonly(flash, true); memory_region_add_subregion(address_space_mem, OMAP_CS0_BASE, flash); diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index eea551dc16..194b0bc808 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -17,7 +17,7 @@ #include "hw/char/serial.h" #include "hw/i2c/i2c.h" #include "hw/ssi/ssi.h" -#include "sysemu/char.h" +#include "chardev/char-fe.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "qemu/cutils.h" @@ -1970,7 +1970,8 @@ static void pxa2xx_fir_realize(DeviceState *dev, Error **errp) PXA2xxFIrState *s = PXA2XX_FIR(dev); qemu_chr_fe_set_handlers(&s->chr, pxa2xx_fir_is_empty, - pxa2xx_fir_rx, pxa2xx_fir_event, s, NULL, true); + pxa2xx_fir_rx, pxa2xx_fir_event, NULL, s, NULL, + true); } static bool pxa2xx_fir_vmstate_validate(void *opaque, int version_id) @@ -2075,11 +2076,9 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space, /* SDRAM & Internal Memory Storage */ memory_region_init_ram(&s->sdram, NULL, "pxa270.sdram", sdram_size, &error_fatal); - vmstate_register_ram_global(&s->sdram); memory_region_add_subregion(address_space, PXA2XX_SDRAM_BASE, &s->sdram); memory_region_init_ram(&s->internal, NULL, "pxa270.internal", 0x40000, &error_fatal); - vmstate_register_ram_global(&s->internal); memory_region_add_subregion(address_space, PXA2XX_INTERNAL_BASE, &s->internal); @@ -2207,11 +2206,9 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size) /* SDRAM & Internal Memory Storage */ memory_region_init_ram(&s->sdram, NULL, "pxa255.sdram", sdram_size, &error_fatal); - vmstate_register_ram_global(&s->sdram); memory_region_add_subregion(address_space, PXA2XX_SDRAM_BASE, &s->sdram); memory_region_init_ram(&s->internal, NULL, "pxa255.internal", PXA2XX_INTERNAL_SIZE, &error_fatal); - vmstate_register_ram_global(&s->internal); memory_region_add_subregion(address_space, PXA2XX_INTERNAL_BASE, &s->internal); diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c index 2b295f14c4..32cdc98c6d 100644 --- a/hw/arm/raspi.c +++ b/hw/arm/raspi.c @@ -153,8 +153,8 @@ static void raspi2_init(MachineState *machine) qdev_prop_set_drive(carddev, "drive", blk, &error_fatal); object_property_set_bool(OBJECT(carddev), true, "realized", &error_fatal); - vcram_size = object_property_get_int(OBJECT(&s->soc), "vcram-size", - &error_abort); + vcram_size = object_property_get_uint(OBJECT(&s->soc), "vcram-size", + &error_abort); setup_boot(machine, 2, machine->ram_size - vcram_size); } diff --git a/hw/arm/realview.c b/hw/arm/realview.c index b7d4753400..76ff5579bc 100644 --- a/hw/arm/realview.c +++ b/hw/arm/realview.c @@ -145,13 +145,11 @@ static void realview_init(MachineState *machine, ram_size = 0x20000000; memory_region_init_ram(ram_lo, NULL, "realview.lowmem", low_ram_size, &error_fatal); - vmstate_register_ram_global(ram_lo); memory_region_add_subregion(sysmem, 0x20000000, ram_lo); } memory_region_init_ram(ram_hi, NULL, "realview.highmem", ram_size, &error_fatal); - vmstate_register_ram_global(ram_hi); low_ram_size = ram_size; if (low_ram_size > 0x10000000) low_ram_size = 0x10000000; @@ -347,7 +345,6 @@ static void realview_init(MachineState *machine, until after Linux boots the secondary CPUs. */ memory_region_init_ram(ram_hack, NULL, "realview.hack", 0x1000, &error_fatal); - vmstate_register_ram_global(ram_hack); memory_region_add_subregion(sysmem, SMP_BOOT_ADDR, ram_hack); realview_binfo.ram_size = ram_size; diff --git a/hw/arm/spitz.c b/hw/arm/spitz.c index 324626847c..7f588cea21 100644 --- a/hw/arm/spitz.c +++ b/hw/arm/spitz.c @@ -848,7 +848,7 @@ static void spitz_lcd_hsync_handler(void *opaque, int line, int level) static void spitz_reset(void *opaque, int line, int level) { if (level) { - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); } } @@ -920,7 +920,6 @@ static void spitz_common_init(MachineState *machine, sl_flash_register(mpu, (model == spitz) ? FLASH_128M : FLASH_1024M); memory_region_init_ram(rom, NULL, "spitz.rom", SPITZ_ROM, &error_fatal); - vmstate_register_ram_global(rom); memory_region_set_readonly(rom, true); memory_region_add_subregion(address_space_mem, 0, rom); diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index ea7a8094e1..408c1a14d3 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -1197,7 +1197,7 @@ static void do_sys_reset(void *opaque, int n, int level) { if (level) { - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); } } @@ -1290,13 +1290,11 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model, /* Flash programming is done via the SCU, so pretend it is ROM. */ memory_region_init_ram(flash, NULL, "stellaris.flash", flash_size, &error_fatal); - vmstate_register_ram_global(flash); memory_region_set_readonly(flash, true); memory_region_add_subregion(system_memory, 0, flash); memory_region_init_ram(sram, NULL, "stellaris.sram", sram_size, &error_fatal); - vmstate_register_ram_global(sram); memory_region_add_subregion(system_memory, 0x20000000, sram); nvic = armv7m_init(system_memory, flash_size, NUM_IRQ_LINES, diff --git a/hw/arm/stm32f205_soc.c b/hw/arm/stm32f205_soc.c index 6e1260d2ed..f61e735f0f 100644 --- a/hw/arm/stm32f205_soc.c +++ b/hw/arm/stm32f205_soc.c @@ -100,8 +100,6 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp) memory_region_init_alias(flash_alias, NULL, "STM32F205.flash.alias", flash, 0, FLASH_SIZE); - vmstate_register_ram_global(flash); - memory_region_set_readonly(flash, true); memory_region_set_readonly(flash_alias, true); @@ -110,7 +108,6 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp) memory_region_init_ram(sram, NULL, "STM32F205.sram", SRAM_SIZE, &error_fatal); - vmstate_register_ram_global(sram); memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, sram); armv7m = DEVICE(&s->armv7m); diff --git a/hw/arm/strongarm.c b/hw/arm/strongarm.c index 3311cc38a4..6a45dcc009 100644 --- a/hw/arm/strongarm.c +++ b/hw/arm/strongarm.c @@ -34,7 +34,8 @@ #include "strongarm.h" #include "qemu/error-report.h" #include "hw/arm/arm.h" -#include "sysemu/char.h" +#include "chardev/char-fe.h" +#include "chardev/char-serial.h" #include "sysemu/sysemu.h" #include "hw/ssi/ssi.h" #include "qemu/cutils.h" @@ -1105,7 +1106,7 @@ static void strongarm_uart_tx(void *opaque) if (s->utcr3 & UTCR3_LBM) /* loopback */ { strongarm_uart_receive(s, &s->tx_fifo[s->tx_start], 1); - } else if (qemu_chr_fe_get_driver(&s->chr)) { + } else if (qemu_chr_fe_backend_connected(&s->chr)) { /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ qemu_chr_fe_write_all(&s->chr, &s->tx_fifo[s->tx_start], 1); @@ -1246,7 +1247,7 @@ static void strongarm_uart_realize(DeviceState *dev, Error **errp) strongarm_uart_can_receive, strongarm_uart_receive, strongarm_uart_event, - s, NULL, true); + NULL, s, NULL, true); } static void strongarm_uart_reset(DeviceState *dev) diff --git a/hw/arm/tosa.c b/hw/arm/tosa.c index 9f58a23fb5..8b757ff6a3 100644 --- a/hw/arm/tosa.c +++ b/hw/arm/tosa.c @@ -90,7 +90,7 @@ static void tosa_out_switch(void *opaque, int line, int level) static void tosa_reset(void *opaque, int line, int level) { if (level) { - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); } } @@ -235,7 +235,6 @@ static void tosa_init(MachineState *machine) mpu = pxa255_init(address_space_mem, tosa_binfo.ram_size); memory_region_init_ram(rom, NULL, "tosa.rom", TOSA_ROM, &error_fatal); - vmstate_register_ram_global(rom); memory_region_set_readonly(rom, true); memory_region_add_subregion(address_space_mem, 0, rom); diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index c6b1e674b4..528c65ddb6 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -392,7 +392,6 @@ static void a15_daughterboard_init(const VexpressMachineState *vms, /* 0x2e000000: system SRAM */ memory_region_init_ram(sram, NULL, "vexpress.a15sram", 0x10000, &error_fatal); - vmstate_register_ram_global(sram); memory_region_add_subregion(sysmem, 0x2e000000, sram); /* 0x7ffb0000: DMA330 DMA controller: not modelled */ @@ -675,13 +674,11 @@ static void vexpress_common_init(MachineState *machine) sram_size = 0x2000000; memory_region_init_ram(sram, NULL, "vexpress.sram", sram_size, &error_fatal); - vmstate_register_ram_global(sram); memory_region_add_subregion(sysmem, map[VE_SRAM], sram); vram_size = 0x800000; memory_region_init_ram(vram, NULL, "vexpress.vram", vram_size, &error_fatal); - vmstate_register_ram_global(vram); memory_region_add_subregion(sysmem, map[VE_VIDEORAM], vram); /* 0x4e000000 LAN9118 Ethernet */ diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index e5852067f5..3d78ff68e6 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -496,12 +496,10 @@ build_srat(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) srat->reserved1 = cpu_to_le32(1); for (i = 0; i < cpu_list->len; ++i) { - int node_id = cpu_list->cpus[i].props.has_node_id ? - cpu_list->cpus[i].props.node_id : 0; core = acpi_data_push(table_data, sizeof(*core)); core->type = ACPI_SRAT_PROCESSOR_GICC; core->length = sizeof(*core); - core->proximity = cpu_to_le32(node_id); + core->proximity = cpu_to_le32(cpu_list->cpus[i].props.node_id); core->acpi_processor_uid = cpu_to_le32(i); core->flags = cpu_to_le32(1); } @@ -776,6 +774,10 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables) if (nb_numa_nodes > 0) { acpi_add_table(table_offsets, tables_blob); build_srat(tables_blob, tables->linker, vms); + if (have_numa_distance) { + acpi_add_table(table_offsets, tables_blob); + build_slit(tables_blob, tables->linker); + } } if (its_class_name() && !vmc->no_its) { diff --git a/hw/arm/virt.c b/hw/arm/virt.c index c7c8159dfd..31739d75a3 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -219,6 +219,27 @@ static void create_fdt(VirtMachineState *vms) "clk24mhz"); qemu_fdt_setprop_cell(fdt, "/apb-pclk", "phandle", vms->clock_phandle); + if (have_numa_distance) { + int size = nb_numa_nodes * nb_numa_nodes * 3 * sizeof(uint32_t); + uint32_t *matrix = g_malloc0(size); + int idx, i, j; + + for (i = 0; i < nb_numa_nodes; i++) { + for (j = 0; j < nb_numa_nodes; j++) { + idx = (i * nb_numa_nodes + j) * 3; + matrix[idx + 0] = cpu_to_be32(i); + matrix[idx + 1] = cpu_to_be32(j); + matrix[idx + 2] = cpu_to_be32(numa_info[i].distance[j]); + } + } + + qemu_fdt_add_subnode(fdt, "/distance-map"); + qemu_fdt_setprop_string(fdt, "/distance-map", "compatible", + "numa-distance-map-v1"); + qemu_fdt_setprop(fdt, "/distance-map", "distance-matrix", + matrix, size); + g_free(matrix); + } } static void fdt_add_psci_node(const VirtMachineState *vms) @@ -1134,8 +1155,8 @@ static void create_secure_ram(VirtMachineState *vms, hwaddr base = vms->memmap[VIRT_SECURE_MEM].base; hwaddr size = vms->memmap[VIRT_SECURE_MEM].size; - memory_region_init_ram(secram, NULL, "virt.secure-ram", size, &error_fatal); - vmstate_register_ram_global(secram); + memory_region_init_ram(secram, NULL, "virt.secure-ram", size, + &error_fatal); memory_region_add_subregion(secure_sysmem, base, secram); nodename = g_strdup_printf("/secram@%" PRIx64, base); @@ -1351,7 +1372,6 @@ static void machvirt_init(MachineState *machine) for (n = 0; n < possible_cpus->len; n++) { Object *cpuobj; CPUState *cs; - int node_id; if (n >= smp_cpus) { break; @@ -1364,19 +1384,8 @@ static void machvirt_init(MachineState *machine) cs = CPU(cpuobj); cs->cpu_index = n; - node_id = possible_cpus->cpus[cs->cpu_index].props.node_id; - if (!possible_cpus->cpus[cs->cpu_index].props.has_node_id) { - /* by default CPUState::numa_node was 0 if it's not set via CLI - * keep it this way for now but in future we probably should - * refuse to start up with incomplete numa mapping */ - node_id = 0; - } - if (cs->numa_node == CPU_UNSET_NUMA_NODE_ID) { - cs->numa_node = node_id; - } else { - /* CPU isn't device_add compatible yet, this shouldn't happen */ - error_setg(&error_abort, "user set node-id not implemented"); - } + numa_cpu_pre_plug(&possible_cpus->cpus[cs->cpu_index], DEVICE(cpuobj), + &error_fatal); if (!vms->secure) { object_property_set_bool(cpuobj, false, "has_el3", NULL); diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index 3985356fc2..6b11a75e67 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -206,7 +206,6 @@ static void zynq_init(MachineState *machine) /* 256K of on-chip memory */ memory_region_init_ram(ocm_ram, NULL, "zynq.ocm_ram", 256 << 10, &error_fatal); - vmstate_register_ram_global(ocm_ram); memory_region_add_subregion(address_space_mem, 0xFFFC0000, ocm_ram); DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0); diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index 64f52f80a5..9eceadbdc8 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -228,7 +228,6 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) memory_region_init_ram(&s->ocm_ram[i], NULL, ocm_name, XLNX_ZYNQMP_OCM_RAM_SIZE, &error_fatal); - vmstate_register_ram_global(&s->ocm_ram[i]); memory_region_add_subregion(get_system_memory(), XLNX_ZYNQMP_OCM_RAM_0_ADDRESS + i * XLNX_ZYNQMP_OCM_RAM_SIZE, diff --git a/hw/audio/adlib.c b/hw/audio/adlib.c index c6e0f10c16..97b876c7e0 100644 --- a/hw/audio/adlib.c +++ b/hw/audio/adlib.c @@ -74,8 +74,6 @@ typedef struct { PortioList port_list; } AdlibState; -static AdlibState *glob_adlib; - static void adlib_stop_opl_timer (AdlibState *s, size_t n) { OPLTimerOver (s->opl, n); @@ -130,9 +128,9 @@ static uint32_t adlib_read(void *opaque, uint32_t nport) return data; } -static void timer_handler (int c, double interval_Sec) +static void timer_handler (void *opaque, int c, double interval_Sec) { - AdlibState *s = glob_adlib; + AdlibState *s = opaque; unsigned n = c & 1; #ifdef DEBUG double interval; @@ -259,19 +257,13 @@ static void adlib_realizefn (DeviceState *dev, Error **errp) AdlibState *s = ADLIB(dev); struct audsettings as; - if (glob_adlib) { - error_setg (errp, "Cannot create more than 1 adlib device"); - return; - } - glob_adlib = s; - s->opl = OPLCreate (3579545, s->freq); if (!s->opl) { error_setg (errp, "OPLCreate %d failed", s->freq); return; } else { - OPLSetTimerHandler (s->opl, timer_handler, 0); + OPLSetTimerHandler(s->opl, timer_handler, s); s->enabled = 1; } diff --git a/hw/audio/fmopl.c b/hw/audio/fmopl.c index 202f752c5d..5cfb6a96dd 100644 --- a/hw/audio/fmopl.c +++ b/hw/audio/fmopl.c @@ -788,14 +788,18 @@ static void OPLWriteReg(FM_OPL *OPL, int r, int v) { double interval = st2 ? (double)OPL->T[1]*OPL->TimerBase : 0.0; OPL->st[1] = st2; - if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+1,interval); + if (OPL->TimerHandler) { + (OPL->TimerHandler)(OPL->TimerParam, 1, interval); + } } /* timer 1 */ if(OPL->st[0] != st1) { double interval = st1 ? (double)OPL->T[0]*OPL->TimerBase : 0.0; OPL->st[0] = st1; - if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+0,interval); + if (OPL->TimerHandler) { + (OPL->TimerHandler)(OPL->TimerParam, 0, interval); + } } } return; @@ -1128,10 +1132,11 @@ void OPLDestroy(FM_OPL *OPL) /* ---------- Option handlers ---------- */ -void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset) +void OPLSetTimerHandler(FM_OPL *OPL, OPL_TIMERHANDLER TimerHandler, + void *param) { OPL->TimerHandler = TimerHandler; - OPL->TimerParam = channelOffset; + OPL->TimerParam = param; } /* ---------- YM3812 I/O interface ---------- */ @@ -1197,6 +1202,9 @@ int OPLTimerOver(FM_OPL *OPL,int c) } } /* reload timer */ - if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+c,(double)OPL->T[c]*OPL->TimerBase); + if (OPL->TimerHandler) { + (OPL->TimerHandler)(OPL->TimerParam, c, + (double)OPL->T[c] * OPL->TimerBase); + } return OPL->status>>7; } diff --git a/hw/audio/fmopl.h b/hw/audio/fmopl.h index fc9f16b58a..f4065f425c 100644 --- a/hw/audio/fmopl.h +++ b/hw/audio/fmopl.h @@ -3,7 +3,7 @@ #include <stdint.h> -typedef void (*OPL_TIMERHANDLER)(int channel,double interval_Sec); +typedef void (*OPL_TIMERHANDLER)(void *param, int channel, double interval_Sec); /* !!!!! here is private section , do not access there member direct !!!!! */ @@ -87,13 +87,14 @@ typedef struct fm_opl_f { uint8_t wavesel; /* external event callback handler */ OPL_TIMERHANDLER TimerHandler; /* TIMER handler */ - int TimerParam; /* TIMER parameter */ + void *TimerParam; /* TIMER parameter */ } FM_OPL; /* ---------- Generic interface section ---------- */ FM_OPL *OPLCreate(int clock, int rate); void OPLDestroy(FM_OPL *OPL); -void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset); +void OPLSetTimerHandler(FM_OPL *OPL, OPL_TIMERHANDLER TimerHandler, + void *param); int OPLWrite(FM_OPL *OPL,int a,int v); unsigned char OPLRead(FM_OPL *OPL,int a); diff --git a/hw/block/fdc.c b/hw/block/fdc.c index 2e629b398b..401129073b 100644 --- a/hw/block/fdc.c +++ b/hw/block/fdc.c @@ -511,7 +511,7 @@ typedef struct FloppyDrive { static Property floppy_drive_properties[] = { DEFINE_PROP_UINT32("unit", FloppyDrive, unit, -1), DEFINE_BLOCK_PROPERTIES(FloppyDrive, conf), - DEFINE_PROP_DEFAULT("drive-type", FloppyDrive, type, + DEFINE_PROP_SIGNED("drive-type", FloppyDrive, type, FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, FloppyDriveType), DEFINE_PROP_END_OF_LIST(), @@ -1217,7 +1217,7 @@ static const VMStateDescription vmstate_fdc = { VMSTATE_UINT8(config, FDCtrl), VMSTATE_UINT8(lock, FDCtrl), VMSTATE_UINT8(pwrd, FDCtrl), - VMSTATE_UINT8_EQUAL(num_floppies, FDCtrl), + VMSTATE_UINT8_EQUAL(num_floppies, FDCtrl, NULL), VMSTATE_STRUCT_ARRAY(drives, FDCtrl, MAX_FD, 1, vmstate_fdrive, FDrive), VMSTATE_END_OF_LIST() @@ -2805,13 +2805,13 @@ static Property isa_fdc_properties[] = { DEFINE_PROP_DRIVE("driveB", FDCtrlISABus, state.qdev_for_drives[1].blk), DEFINE_PROP_BIT("check_media_rate", FDCtrlISABus, state.check_media_rate, 0, true), - DEFINE_PROP_DEFAULT("fdtypeA", FDCtrlISABus, state.qdev_for_drives[0].type, + DEFINE_PROP_SIGNED("fdtypeA", FDCtrlISABus, state.qdev_for_drives[0].type, FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, FloppyDriveType), - DEFINE_PROP_DEFAULT("fdtypeB", FDCtrlISABus, state.qdev_for_drives[1].type, + DEFINE_PROP_SIGNED("fdtypeB", FDCtrlISABus, state.qdev_for_drives[1].type, FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, FloppyDriveType), - DEFINE_PROP_DEFAULT("fallback", FDCtrlISABus, state.fallback, + DEFINE_PROP_SIGNED("fallback", FDCtrlISABus, state.fallback, FLOPPY_DRIVE_TYPE_288, qdev_prop_fdc_drive_type, FloppyDriveType), DEFINE_PROP_END_OF_LIST(), @@ -2862,13 +2862,13 @@ static const VMStateDescription vmstate_sysbus_fdc ={ static Property sysbus_fdc_properties[] = { DEFINE_PROP_DRIVE("driveA", FDCtrlSysBus, state.qdev_for_drives[0].blk), DEFINE_PROP_DRIVE("driveB", FDCtrlSysBus, state.qdev_for_drives[1].blk), - DEFINE_PROP_DEFAULT("fdtypeA", FDCtrlSysBus, state.qdev_for_drives[0].type, + DEFINE_PROP_SIGNED("fdtypeA", FDCtrlSysBus, state.qdev_for_drives[0].type, FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, FloppyDriveType), - DEFINE_PROP_DEFAULT("fdtypeB", FDCtrlSysBus, state.qdev_for_drives[1].type, + DEFINE_PROP_SIGNED("fdtypeB", FDCtrlSysBus, state.qdev_for_drives[1].type, FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, FloppyDriveType), - DEFINE_PROP_DEFAULT("fallback", FDCtrlISABus, state.fallback, + DEFINE_PROP_SIGNED("fallback", FDCtrlISABus, state.fallback, FLOPPY_DRIVE_TYPE_144, qdev_prop_fdc_drive_type, FloppyDriveType), DEFINE_PROP_END_OF_LIST(), @@ -2891,10 +2891,10 @@ static const TypeInfo sysbus_fdc_info = { static Property sun4m_fdc_properties[] = { DEFINE_PROP_DRIVE("drive", FDCtrlSysBus, state.qdev_for_drives[0].blk), - DEFINE_PROP_DEFAULT("fdtype", FDCtrlSysBus, state.qdev_for_drives[0].type, + DEFINE_PROP_SIGNED("fdtype", FDCtrlSysBus, state.qdev_for_drives[0].type, FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type, FloppyDriveType), - DEFINE_PROP_DEFAULT("fallback", FDCtrlISABus, state.fallback, + DEFINE_PROP_SIGNED("fallback", FDCtrlISABus, state.fallback, FLOPPY_DRIVE_TYPE_144, qdev_prop_fdc_drive_type, FloppyDriveType), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/block/nvme.c b/hw/block/nvme.c index 7428db9f0c..6071dc12d8 100644 --- a/hw/block/nvme.c +++ b/hw/block/nvme.c @@ -9,7 +9,7 @@ */ /** - * Reference Specs: http://www.nvmexpress.org, 1.1, 1.0e + * Reference Specs: http://www.nvmexpress.org, 1.2, 1.1, 1.0e * * http://www.nvmexpress.org/resources/ */ @@ -17,7 +17,11 @@ /** * Usage: add options: * -drive file=<file>,if=none,id=<drive_id> - * -device nvme,drive=<drive_id>,serial=<serial>,id=<id[optional]> + * -device nvme,drive=<drive_id>,serial=<serial>,id=<id[optional]>, \ + * cmb_size_mb=<cmb_size_mb[optional]> + * + * Note cmb_size_mb denotes size of CMB in MB. CMB is assumed to be at + * offset 0 in BAR2 and supports only WDS, RDS and SQS for now. */ #include "qemu/osdep.h" @@ -34,6 +38,16 @@ static void nvme_process_sq(void *opaque); +static void nvme_addr_read(NvmeCtrl *n, hwaddr addr, void *buf, int size) +{ + if (n->cmbsz && addr >= n->ctrl_mem.addr && + addr < (n->ctrl_mem.addr + int128_get64(n->ctrl_mem.size))) { + memcpy(buf, (void *)&n->cmbuf[addr - n->ctrl_mem.addr], size); + } else { + pci_dma_read(&n->parent_obj, addr, buf, size); + } +} + static int nvme_check_sqid(NvmeCtrl *n, uint16_t sqid) { return sqid < n->num_queues && n->sq[sqid] != NULL ? 0 : -1; @@ -79,8 +93,8 @@ static void nvme_isr_notify(NvmeCtrl *n, NvmeCQueue *cq) } } -static uint16_t nvme_map_prp(QEMUSGList *qsg, uint64_t prp1, uint64_t prp2, - uint32_t len, NvmeCtrl *n) +static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1, + uint64_t prp2, uint32_t len, NvmeCtrl *n) { hwaddr trans_len = n->page_size - (prp1 % n->page_size); trans_len = MIN(len, trans_len); @@ -88,10 +102,15 @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, uint64_t prp1, uint64_t prp2, if (!prp1) { return NVME_INVALID_FIELD | NVME_DNR; + } else if (n->cmbsz && prp1 >= n->ctrl_mem.addr && + prp1 < n->ctrl_mem.addr + int128_get64(n->ctrl_mem.size)) { + qsg->nsg = 0; + qemu_iovec_init(iov, num_prps); + qemu_iovec_add(iov, (void *)&n->cmbuf[prp1 - n->ctrl_mem.addr], trans_len); + } else { + pci_dma_sglist_init(qsg, &n->parent_obj, num_prps); + qemu_sglist_add(qsg, prp1, trans_len); } - - pci_dma_sglist_init(qsg, &n->parent_obj, num_prps); - qemu_sglist_add(qsg, prp1, trans_len); len -= trans_len; if (len) { if (!prp2) { @@ -104,7 +123,7 @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, uint64_t prp1, uint64_t prp2, nents = (len + n->page_size - 1) >> n->page_bits; prp_trans = MIN(n->max_prp_ents, nents) * sizeof(uint64_t); - pci_dma_read(&n->parent_obj, prp2, (void *)prp_list, prp_trans); + nvme_addr_read(n, prp2, (void *)prp_list, prp_trans); while (len != 0) { uint64_t prp_ent = le64_to_cpu(prp_list[i]); @@ -116,7 +135,7 @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, uint64_t prp1, uint64_t prp2, i = 0; nents = (len + n->page_size - 1) >> n->page_bits; prp_trans = MIN(n->max_prp_ents, nents) * sizeof(uint64_t); - pci_dma_read(&n->parent_obj, prp_ent, (void *)prp_list, + nvme_addr_read(n, prp_ent, (void *)prp_list, prp_trans); prp_ent = le64_to_cpu(prp_list[i]); } @@ -126,7 +145,11 @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, uint64_t prp1, uint64_t prp2, } trans_len = MIN(len, n->page_size); - qemu_sglist_add(qsg, prp_ent, trans_len); + if (qsg->nsg){ + qemu_sglist_add(qsg, prp_ent, trans_len); + } else { + qemu_iovec_add(iov, (void *)&n->cmbuf[prp_ent - n->ctrl_mem.addr], trans_len); + } len -= trans_len; i++; } @@ -134,7 +157,11 @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, uint64_t prp1, uint64_t prp2, if (prp2 & (n->page_size - 1)) { goto unmap; } - qemu_sglist_add(qsg, prp2, len); + if (qsg->nsg) { + qemu_sglist_add(qsg, prp2, len); + } else { + qemu_iovec_add(iov, (void *)&n->cmbuf[prp2 - n->ctrl_mem.addr], trans_len); + } } } return NVME_SUCCESS; @@ -148,16 +175,24 @@ static uint16_t nvme_dma_read_prp(NvmeCtrl *n, uint8_t *ptr, uint32_t len, uint64_t prp1, uint64_t prp2) { QEMUSGList qsg; + QEMUIOVector iov; + uint16_t status = NVME_SUCCESS; - if (nvme_map_prp(&qsg, prp1, prp2, len, n)) { + if (nvme_map_prp(&qsg, &iov, prp1, prp2, len, n)) { return NVME_INVALID_FIELD | NVME_DNR; } - if (dma_buf_read(ptr, len, &qsg)) { + if (qsg.nsg > 0) { + if (dma_buf_read(ptr, len, &qsg)) { + status = NVME_INVALID_FIELD | NVME_DNR; + } qemu_sglist_destroy(&qsg); - return NVME_INVALID_FIELD | NVME_DNR; + } else { + if (qemu_iovec_to_buf(&iov, 0, ptr, len) != len) { + status = NVME_INVALID_FIELD | NVME_DNR; + } + qemu_iovec_destroy(&iov); } - qemu_sglist_destroy(&qsg); - return NVME_SUCCESS; + return status; } static void nvme_post_cqes(void *opaque) @@ -271,20 +306,27 @@ static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd, return NVME_LBA_RANGE | NVME_DNR; } - if (nvme_map_prp(&req->qsg, prp1, prp2, data_size, n)) { + if (nvme_map_prp(&req->qsg, &req->iov, prp1, prp2, data_size, n)) { block_acct_invalid(blk_get_stats(n->conf.blk), acct); return NVME_INVALID_FIELD | NVME_DNR; } - assert((nlb << data_shift) == req->qsg.size); - - req->has_sg = true; dma_acct_start(n->conf.blk, &req->acct, &req->qsg, acct); - req->aiocb = is_write ? - dma_blk_write(n->conf.blk, &req->qsg, data_offset, BDRV_SECTOR_SIZE, - nvme_rw_cb, req) : - dma_blk_read(n->conf.blk, &req->qsg, data_offset, BDRV_SECTOR_SIZE, - nvme_rw_cb, req); + if (req->qsg.nsg > 0) { + req->has_sg = true; + req->aiocb = is_write ? + dma_blk_write(n->conf.blk, &req->qsg, data_offset, BDRV_SECTOR_SIZE, + nvme_rw_cb, req) : + dma_blk_read(n->conf.blk, &req->qsg, data_offset, BDRV_SECTOR_SIZE, + nvme_rw_cb, req); + } else { + req->has_sg = false; + req->aiocb = is_write ? + blk_aio_pwritev(n->conf.blk, data_offset, &req->iov, 0, nvme_rw_cb, + req) : + blk_aio_preadv(n->conf.blk, data_offset, &req->iov, 0, nvme_rw_cb, + req); + } return NVME_NO_COMPLETE; } @@ -637,7 +679,7 @@ static void nvme_process_sq(void *opaque) while (!(nvme_sq_empty(sq) || QTAILQ_EMPTY(&sq->req_list))) { addr = sq->dma_addr + sq->head * n->sqe_size; - pci_dma_read(&n->parent_obj, addr, (void *)&cmd, sizeof(cmd)); + nvme_addr_read(n, addr, (void *)&cmd, sizeof(cmd)); nvme_inc_sq_head(sq); req = QTAILQ_FIRST(&sq->req_list); @@ -852,6 +894,32 @@ static const MemoryRegionOps nvme_mmio_ops = { }, }; +static void nvme_cmb_write(void *opaque, hwaddr addr, uint64_t data, + unsigned size) +{ + NvmeCtrl *n = (NvmeCtrl *)opaque; + memcpy(&n->cmbuf[addr], &data, size); +} + +static uint64_t nvme_cmb_read(void *opaque, hwaddr addr, unsigned size) +{ + uint64_t val; + NvmeCtrl *n = (NvmeCtrl *)opaque; + + memcpy(&val, &n->cmbuf[addr], size); + return val; +} + +static const MemoryRegionOps nvme_cmb_ops = { + .read = nvme_cmb_read, + .write = nvme_cmb_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 2, + .max_access_size = 8, + }, +}; + static int nvme_init(PCIDevice *pci_dev) { NvmeCtrl *n = NVME(pci_dev); @@ -936,9 +1004,34 @@ static int nvme_init(PCIDevice *pci_dev) NVME_CAP_SET_CSS(n->bar.cap, 1); NVME_CAP_SET_MPSMAX(n->bar.cap, 4); - n->bar.vs = 0x00010100; + n->bar.vs = 0x00010200; n->bar.intmc = n->bar.intms = 0; + if (n->cmb_size_mb) { + + NVME_CMBLOC_SET_BIR(n->bar.cmbloc, 2); + NVME_CMBLOC_SET_OFST(n->bar.cmbloc, 0); + + NVME_CMBSZ_SET_SQS(n->bar.cmbsz, 1); + NVME_CMBSZ_SET_CQS(n->bar.cmbsz, 0); + NVME_CMBSZ_SET_LISTS(n->bar.cmbsz, 0); + NVME_CMBSZ_SET_RDS(n->bar.cmbsz, 1); + NVME_CMBSZ_SET_WDS(n->bar.cmbsz, 1); + NVME_CMBSZ_SET_SZU(n->bar.cmbsz, 2); /* MBs */ + NVME_CMBSZ_SET_SZ(n->bar.cmbsz, n->cmb_size_mb); + + n->cmbloc = n->bar.cmbloc; + n->cmbsz = n->bar.cmbsz; + + n->cmbuf = g_malloc0(NVME_CMBSZ_GETSIZE(n->bar.cmbsz)); + memory_region_init_io(&n->ctrl_mem, OBJECT(n), &nvme_cmb_ops, n, + "nvme-cmb", NVME_CMBSZ_GETSIZE(n->bar.cmbsz)); + pci_register_bar(&n->parent_obj, NVME_CMBLOC_BIR(n->bar.cmbloc), + PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64 | + PCI_BASE_ADDRESS_MEM_PREFETCH, &n->ctrl_mem); + + } + for (i = 0; i < n->num_namespaces; i++) { NvmeNamespace *ns = &n->namespaces[i]; NvmeIdNs *id_ns = &ns->id_ns; @@ -964,12 +1057,17 @@ static void nvme_exit(PCIDevice *pci_dev) g_free(n->namespaces); g_free(n->cq); g_free(n->sq); + if (n->cmbsz) { + memory_region_unref(&n->ctrl_mem); + } + msix_uninit_exclusive_bar(pci_dev); } static Property nvme_props[] = { DEFINE_BLOCK_PROPERTIES(NvmeCtrl, conf), DEFINE_PROP_STRING("serial", NvmeCtrl, serial), + DEFINE_PROP_UINT32("cmb_size_mb", NvmeCtrl, cmb_size_mb, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/block/nvme.h b/hw/block/nvme.h index a0d15649f9..6aab338ff5 100644 --- a/hw/block/nvme.h +++ b/hw/block/nvme.h @@ -14,6 +14,8 @@ typedef struct NvmeBar { uint32_t aqa; uint64_t asq; uint64_t acq; + uint32_t cmbloc; + uint32_t cmbsz; } NvmeBar; enum NvmeCapShift { @@ -138,6 +140,72 @@ enum NvmeAqaMask { #define NVME_AQA_ASQS(aqa) ((aqa >> AQA_ASQS_SHIFT) & AQA_ASQS_MASK) #define NVME_AQA_ACQS(aqa) ((aqa >> AQA_ACQS_SHIFT) & AQA_ACQS_MASK) +enum NvmeCmblocShift { + CMBLOC_BIR_SHIFT = 0, + CMBLOC_OFST_SHIFT = 12, +}; + +enum NvmeCmblocMask { + CMBLOC_BIR_MASK = 0x7, + CMBLOC_OFST_MASK = 0xfffff, +}; + +#define NVME_CMBLOC_BIR(cmbloc) ((cmbloc >> CMBLOC_BIR_SHIFT) & \ + CMBLOC_BIR_MASK) +#define NVME_CMBLOC_OFST(cmbloc)((cmbloc >> CMBLOC_OFST_SHIFT) & \ + CMBLOC_OFST_MASK) + +#define NVME_CMBLOC_SET_BIR(cmbloc, val) \ + (cmbloc |= (uint64_t)(val & CMBLOC_BIR_MASK) << CMBLOC_BIR_SHIFT) +#define NVME_CMBLOC_SET_OFST(cmbloc, val) \ + (cmbloc |= (uint64_t)(val & CMBLOC_OFST_MASK) << CMBLOC_OFST_SHIFT) + +enum NvmeCmbszShift { + CMBSZ_SQS_SHIFT = 0, + CMBSZ_CQS_SHIFT = 1, + CMBSZ_LISTS_SHIFT = 2, + CMBSZ_RDS_SHIFT = 3, + CMBSZ_WDS_SHIFT = 4, + CMBSZ_SZU_SHIFT = 8, + CMBSZ_SZ_SHIFT = 12, +}; + +enum NvmeCmbszMask { + CMBSZ_SQS_MASK = 0x1, + CMBSZ_CQS_MASK = 0x1, + CMBSZ_LISTS_MASK = 0x1, + CMBSZ_RDS_MASK = 0x1, + CMBSZ_WDS_MASK = 0x1, + CMBSZ_SZU_MASK = 0xf, + CMBSZ_SZ_MASK = 0xfffff, +}; + +#define NVME_CMBSZ_SQS(cmbsz) ((cmbsz >> CMBSZ_SQS_SHIFT) & CMBSZ_SQS_MASK) +#define NVME_CMBSZ_CQS(cmbsz) ((cmbsz >> CMBSZ_CQS_SHIFT) & CMBSZ_CQS_MASK) +#define NVME_CMBSZ_LISTS(cmbsz)((cmbsz >> CMBSZ_LISTS_SHIFT) & CMBSZ_LISTS_MASK) +#define NVME_CMBSZ_RDS(cmbsz) ((cmbsz >> CMBSZ_RDS_SHIFT) & CMBSZ_RDS_MASK) +#define NVME_CMBSZ_WDS(cmbsz) ((cmbsz >> CMBSZ_WDS_SHIFT) & CMBSZ_WDS_MASK) +#define NVME_CMBSZ_SZU(cmbsz) ((cmbsz >> CMBSZ_SZU_SHIFT) & CMBSZ_SZU_MASK) +#define NVME_CMBSZ_SZ(cmbsz) ((cmbsz >> CMBSZ_SZ_SHIFT) & CMBSZ_SZ_MASK) + +#define NVME_CMBSZ_SET_SQS(cmbsz, val) \ + (cmbsz |= (uint64_t)(val & CMBSZ_SQS_MASK) << CMBSZ_SQS_SHIFT) +#define NVME_CMBSZ_SET_CQS(cmbsz, val) \ + (cmbsz |= (uint64_t)(val & CMBSZ_CQS_MASK) << CMBSZ_CQS_SHIFT) +#define NVME_CMBSZ_SET_LISTS(cmbsz, val) \ + (cmbsz |= (uint64_t)(val & CMBSZ_LISTS_MASK) << CMBSZ_LISTS_SHIFT) +#define NVME_CMBSZ_SET_RDS(cmbsz, val) \ + (cmbsz |= (uint64_t)(val & CMBSZ_RDS_MASK) << CMBSZ_RDS_SHIFT) +#define NVME_CMBSZ_SET_WDS(cmbsz, val) \ + (cmbsz |= (uint64_t)(val & CMBSZ_WDS_MASK) << CMBSZ_WDS_SHIFT) +#define NVME_CMBSZ_SET_SZU(cmbsz, val) \ + (cmbsz |= (uint64_t)(val & CMBSZ_SZU_MASK) << CMBSZ_SZU_SHIFT) +#define NVME_CMBSZ_SET_SZ(cmbsz, val) \ + (cmbsz |= (uint64_t)(val & CMBSZ_SZ_MASK) << CMBSZ_SZ_SHIFT) + +#define NVME_CMBSZ_GETSIZE(cmbsz) \ + (NVME_CMBSZ_SZ(cmbsz) * (1 << (12 + 4 * NVME_CMBSZ_SZU(cmbsz)))) + typedef struct NvmeCmd { uint8_t opcode; uint8_t fuse; @@ -644,6 +712,7 @@ typedef struct NvmeRequest { NvmeCqe cqe; BlockAcctCookie acct; QEMUSGList qsg; + QEMUIOVector iov; QTAILQ_ENTRY(NvmeRequest)entry; } NvmeRequest; @@ -688,6 +757,7 @@ typedef struct NvmeNamespace { typedef struct NvmeCtrl { PCIDevice parent_obj; MemoryRegion iomem; + MemoryRegion ctrl_mem; NvmeBar bar; BlockConf conf; @@ -701,6 +771,10 @@ typedef struct NvmeCtrl { uint32_t num_queues; uint32_t max_q_ents; uint64_t ns_size; + uint32_t cmb_size_mb; + uint32_t cmbsz; + uint32_t cmbloc; + uint8_t *cmbuf; char *serial; NvmeNamespace *namespaces; diff --git a/hw/block/onenand.c b/hw/block/onenand.c index ddf5492426..b7423607d9 100644 --- a/hw/block/onenand.c +++ b/hw/block/onenand.c @@ -807,7 +807,7 @@ static int onenand_initfn(SysBusDevice *sbd) } s->otp = memset(g_malloc((64 + 2) << PAGE_SHIFT), 0xff, (64 + 2) << PAGE_SHIFT); - memory_region_init_ram(&s->ram, OBJECT(s), "onenand.ram", + memory_region_init_ram_nomigrate(&s->ram, OBJECT(s), "onenand.ram", 0xc000 << s->shift, &error_fatal); vmstate_register_ram_global(&s->ram); ram = memory_region_get_ram_ptr(&s->ram); diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index 594d4cf6fe..1113ab1ccf 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -753,7 +753,6 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp) return; } - vmstate_register_ram(&pfl->mem, DEVICE(pfl)); pfl->storage = memory_region_get_ram_ptr(&pfl->mem); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &pfl->mem); diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c index e6c5c6c25d..c81ddd3a99 100644 --- a/hw/block/pflash_cfi02.c +++ b/hw/block/pflash_cfi02.c @@ -629,7 +629,6 @@ static void pflash_cfi02_realize(DeviceState *dev, Error **errp) return; } - vmstate_register_ram(&pfl->orig_mem, DEVICE(pfl)); pfl->storage = memory_region_get_ram_ptr(&pfl->orig_mem); pfl->chip_len = chip_len; diff --git a/hw/block/trace-events b/hw/block/trace-events index 65e83dc258..c332c01ea8 100644 --- a/hw/block/trace-events +++ b/hw/block/trace-events @@ -1,11 +1,11 @@ # See docs/tracing.txt for syntax documentation. # hw/block/virtio-blk.c -virtio_blk_req_complete(void *req, int status) "req %p status %d" -virtio_blk_rw_complete(void *req, int ret) "req %p ret %d" -virtio_blk_handle_write(void *req, uint64_t sector, size_t nsectors) "req %p sector %"PRIu64" nsectors %zu" -virtio_blk_handle_read(void *req, uint64_t sector, size_t nsectors) "req %p sector %"PRIu64" nsectors %zu" -virtio_blk_submit_multireq(void *mrb, int start, int num_reqs, uint64_t offset, size_t size, bool is_write) "mrb %p start %d num_reqs %d offset %"PRIu64" size %zu is_write %d" +virtio_blk_req_complete(void *vdev, void *req, int status) "vdev %p req %p status %d" +virtio_blk_rw_complete(void *vdev, void *req, int ret) "vdev %p req %p ret %d" +virtio_blk_handle_write(void *vdev, void *req, uint64_t sector, size_t nsectors) "vdev %p req %p sector %"PRIu64" nsectors %zu" +virtio_blk_handle_read(void *vdev, void *req, uint64_t sector, size_t nsectors) "vdev %p req %p sector %"PRIu64" nsectors %zu" +virtio_blk_submit_multireq(void *vdev, void *mrb, int start, int num_reqs, uint64_t offset, size_t size, bool is_write) "vdev %p mrb %p start %d num_reqs %d offset %"PRIu64" size %zu is_write %d" # hw/block/hd-geometry.c hd_geometry_lchs_guess(void *blk, int cyls, int heads, int secs) "blk %p LCHS %d %d %d" diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 604d37dfc8..b750bd8b53 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -50,7 +50,7 @@ static void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status) VirtIOBlock *s = req->dev; VirtIODevice *vdev = VIRTIO_DEVICE(s); - trace_virtio_blk_req_complete(req, status); + trace_virtio_blk_req_complete(vdev, req, status); stb_p(&req->in->status, status); virtqueue_push(req->vq, &req->elem, req->in_len); @@ -88,12 +88,13 @@ static void virtio_blk_rw_complete(void *opaque, int ret) { VirtIOBlockReq *next = opaque; VirtIOBlock *s = next->dev; + VirtIODevice *vdev = VIRTIO_DEVICE(s); aio_context_acquire(blk_get_aio_context(s->conf.conf.blk)); while (next) { VirtIOBlockReq *req = next; next = req->mr_next; - trace_virtio_blk_rw_complete(req, ret); + trace_virtio_blk_rw_complete(vdev, req, ret); if (req->qiov.nalloc != -1) { /* If nalloc is != 1 req->qiov is a local copy of the original @@ -355,7 +356,8 @@ static inline void submit_requests(BlockBackend *blk, MultiReqBuffer *mrb, mrb->reqs[i - 1]->mr_next = mrb->reqs[i]; } - trace_virtio_blk_submit_multireq(mrb, start, num_reqs, + trace_virtio_blk_submit_multireq(VIRTIO_DEVICE(mrb->reqs[start]->dev), + mrb, start, num_reqs, sector_num << BDRV_SECTOR_BITS, qiov->size, is_write); block_acct_merge_done(blk_get_stats(blk), @@ -526,11 +528,11 @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) if (is_write) { qemu_iovec_init_external(&req->qiov, iov, out_num); - trace_virtio_blk_handle_write(req, req->sector_num, + trace_virtio_blk_handle_write(vdev, req, req->sector_num, req->qiov.size / BDRV_SECTOR_SIZE); } else { qemu_iovec_init_external(&req->qiov, in_iov, in_num); - trace_virtio_blk_handle_read(req, req->sector_num, + trace_virtio_blk_handle_read(vdev, req, req->sector_num, req->qiov.size / BDRV_SECTOR_SIZE); } @@ -981,10 +983,6 @@ static void virtio_blk_instance_init(Object *obj) { VirtIOBlock *s = VIRTIO_BLK(obj); - object_property_add_link(obj, "iothread", TYPE_IOTHREAD, - (Object **)&s->conf.iothread, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, NULL); device_add_bootindex_property(obj, &s->conf.conf.bootindex, "bootindex", "/disk@0,0", DEVICE(obj), NULL); @@ -1012,6 +1010,8 @@ static Property virtio_blk_properties[] = { DEFINE_PROP_BIT("request-merging", VirtIOBlock, conf.request_merging, 0, true), DEFINE_PROP_UINT16("num-queues", VirtIOBlock, conf.num_queues, 1), + DEFINE_PROP_LINK("iothread", VirtIOBlock, conf.iothread, TYPE_IOTHREAD, + IOThread *), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c index 3a22805fbc..d42ed7070d 100644 --- a/hw/block/xen_disk.c +++ b/hw/block/xen_disk.c @@ -36,8 +36,6 @@ static int batch_maps = 0; -static int max_requests = 32; - /* ------------------------------------------------------------- */ #define BLOCK_SIZE 512 @@ -84,6 +82,8 @@ struct ioreq { BlockAcctCookie acct; }; +#define MAX_RING_PAGE_ORDER 4 + struct XenBlkDev { struct XenDevice xendev; /* must be first */ char *params; @@ -94,7 +94,8 @@ struct XenBlkDev { bool directiosafe; const char *fileproto; const char *filename; - int ring_ref; + unsigned int ring_ref[1 << MAX_RING_PAGE_ORDER]; + unsigned int nr_ring_ref; void *sring; int64_t file_blk; int64_t file_size; @@ -110,6 +111,7 @@ struct XenBlkDev { int requests_total; int requests_inflight; int requests_finished; + unsigned int max_requests; /* Persistent grants extension */ gboolean feature_discard; @@ -199,7 +201,7 @@ static struct ioreq *ioreq_start(struct XenBlkDev *blkdev) struct ioreq *ioreq = NULL; if (QLIST_EMPTY(&blkdev->freelist)) { - if (blkdev->requests_total >= max_requests) { + if (blkdev->requests_total >= blkdev->max_requests) { goto out; } /* allocate new struct */ @@ -769,31 +771,30 @@ static int blk_send_response_one(struct ioreq *ioreq) struct XenBlkDev *blkdev = ioreq->blkdev; int send_notify = 0; int have_requests = 0; - blkif_response_t resp; - void *dst; - - resp.id = ioreq->req.id; - resp.operation = ioreq->req.operation; - resp.status = ioreq->status; + blkif_response_t *resp; /* Place on the response ring for the relevant domain. */ switch (blkdev->protocol) { case BLKIF_PROTOCOL_NATIVE: - dst = RING_GET_RESPONSE(&blkdev->rings.native, blkdev->rings.native.rsp_prod_pvt); + resp = (blkif_response_t *) RING_GET_RESPONSE(&blkdev->rings.native, + blkdev->rings.native.rsp_prod_pvt); break; case BLKIF_PROTOCOL_X86_32: - dst = RING_GET_RESPONSE(&blkdev->rings.x86_32_part, - blkdev->rings.x86_32_part.rsp_prod_pvt); + resp = (blkif_response_t *) RING_GET_RESPONSE(&blkdev->rings.x86_32_part, + blkdev->rings.x86_32_part.rsp_prod_pvt); break; case BLKIF_PROTOCOL_X86_64: - dst = RING_GET_RESPONSE(&blkdev->rings.x86_64_part, - blkdev->rings.x86_64_part.rsp_prod_pvt); + resp = (blkif_response_t *) RING_GET_RESPONSE(&blkdev->rings.x86_64_part, + blkdev->rings.x86_64_part.rsp_prod_pvt); break; default: - dst = NULL; return 0; } - memcpy(dst, &resp, sizeof(resp)); + + resp->id = ioreq->req.id; + resp->operation = ioreq->req.operation; + resp->status = ioreq->status; + blkdev->rings.common.rsp_prod_pvt++; RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&blkdev->rings.common, send_notify); @@ -905,7 +906,7 @@ static void blk_handle_requests(struct XenBlkDev *blkdev) ioreq_runio_qemu_aio(ioreq); } - if (blkdev->more_work && blkdev->requests_inflight < max_requests) { + if (blkdev->more_work && blkdev->requests_inflight < blkdev->max_requests) { qemu_bh_schedule(blkdev->bh); } } @@ -918,15 +919,6 @@ static void blk_bh(void *opaque) blk_handle_requests(blkdev); } -/* - * We need to account for the grant allocations requiring contiguous - * chunks; the worst case number would be - * max_req * max_seg + (max_req - 1) * (max_seg - 1) + 1, - * but in order to keep things simple just use - * 2 * max_req * max_seg. - */ -#define MAX_GRANTS(max_req, max_seg) (2 * (max_req) * (max_seg)) - static void blk_alloc(struct XenDevice *xendev) { struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); @@ -938,11 +930,6 @@ static void blk_alloc(struct XenDevice *xendev) if (xen_mode != XEN_EMULATE) { batch_maps = 1; } - if (xengnttab_set_max_grants(xendev->gnttabdev, - MAX_GRANTS(max_requests, BLKIF_MAX_SEGMENTS_PER_REQUEST)) < 0) { - xen_pv_printf(xendev, 0, "xengnttab_set_max_grants failed: %s\n", - strerror(errno)); - } } static void blk_parse_discard(struct XenBlkDev *blkdev) @@ -1023,13 +1010,23 @@ static int blk_init(struct XenDevice *xendev) blkdev->file_blk = BLOCK_SIZE; + blkdev->feature_grant_copy = + (xengnttab_grant_copy(blkdev->xendev.gnttabdev, 0, NULL) == 0); + + xen_pv_printf(&blkdev->xendev, 3, "grant copy operation %s\n", + blkdev->feature_grant_copy ? "enabled" : "disabled"); + /* fill info * blk_connect supplies sector-size and sectors */ xenstore_write_be_int(&blkdev->xendev, "feature-flush-cache", 1); - xenstore_write_be_int(&blkdev->xendev, "feature-persistent", 1); + xenstore_write_be_int(&blkdev->xendev, "feature-persistent", + !blkdev->feature_grant_copy); xenstore_write_be_int(&blkdev->xendev, "info", info); + xenstore_write_be_int(&blkdev->xendev, "max-ring-page-order", + MAX_RING_PAGE_ORDER); + blk_parse_discard(blkdev); g_free(directiosafe); @@ -1051,12 +1048,25 @@ out_error: return -1; } +/* + * We need to account for the grant allocations requiring contiguous + * chunks; the worst case number would be + * max_req * max_seg + (max_req - 1) * (max_seg - 1) + 1, + * but in order to keep things simple just use + * 2 * max_req * max_seg. + */ +#define MAX_GRANTS(max_req, max_seg) (2 * (max_req) * (max_seg)) + static int blk_connect(struct XenDevice *xendev) { struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); int pers, index, qflags; bool readonly = true; bool writethrough = true; + int order, ring_ref; + unsigned int ring_size, max_grants; + unsigned int i; + uint32_t *domids; /* read-only ? */ if (blkdev->directiosafe) { @@ -1131,9 +1141,42 @@ static int blk_connect(struct XenDevice *xendev) xenstore_write_be_int64(&blkdev->xendev, "sectors", blkdev->file_size / blkdev->file_blk); - if (xenstore_read_fe_int(&blkdev->xendev, "ring-ref", &blkdev->ring_ref) == -1) { + if (xenstore_read_fe_int(&blkdev->xendev, "ring-page-order", + &order) == -1) { + blkdev->nr_ring_ref = 1; + + if (xenstore_read_fe_int(&blkdev->xendev, "ring-ref", + &ring_ref) == -1) { + return -1; + } + blkdev->ring_ref[0] = ring_ref; + + } else if (order >= 0 && order <= MAX_RING_PAGE_ORDER) { + blkdev->nr_ring_ref = 1 << order; + + for (i = 0; i < blkdev->nr_ring_ref; i++) { + char *key; + + key = g_strdup_printf("ring-ref%u", i); + if (!key) { + return -1; + } + + if (xenstore_read_fe_int(&blkdev->xendev, key, + &ring_ref) == -1) { + g_free(key); + return -1; + } + blkdev->ring_ref[i] = ring_ref; + + g_free(key); + } + } else { + xen_pv_printf(xendev, 0, "invalid ring-page-order: %d\n", + order); return -1; } + if (xenstore_read_fe_int(&blkdev->xendev, "event-channel", &blkdev->xendev.remote_port) == -1) { return -1; @@ -1156,41 +1199,85 @@ static int blk_connect(struct XenDevice *xendev) blkdev->protocol = BLKIF_PROTOCOL_NATIVE; } - blkdev->sring = xengnttab_map_grant_ref(blkdev->xendev.gnttabdev, - blkdev->xendev.dom, - blkdev->ring_ref, - PROT_READ | PROT_WRITE); + ring_size = XC_PAGE_SIZE * blkdev->nr_ring_ref; + switch (blkdev->protocol) { + case BLKIF_PROTOCOL_NATIVE: + { + blkdev->max_requests = __CONST_RING_SIZE(blkif, ring_size); + break; + } + case BLKIF_PROTOCOL_X86_32: + { + blkdev->max_requests = __CONST_RING_SIZE(blkif_x86_32, ring_size); + break; + } + case BLKIF_PROTOCOL_X86_64: + { + blkdev->max_requests = __CONST_RING_SIZE(blkif_x86_64, ring_size); + break; + } + default: + return -1; + } + + /* Calculate the maximum number of grants needed by ioreqs */ + max_grants = MAX_GRANTS(blkdev->max_requests, + BLKIF_MAX_SEGMENTS_PER_REQUEST); + /* Add on the number needed for the ring pages */ + max_grants += blkdev->nr_ring_ref; + + if (xengnttab_set_max_grants(blkdev->xendev.gnttabdev, max_grants)) { + xen_pv_printf(xendev, 0, "xengnttab_set_max_grants failed: %s\n", + strerror(errno)); + return -1; + } + + domids = g_malloc0_n(blkdev->nr_ring_ref, sizeof(uint32_t)); + for (i = 0; i < blkdev->nr_ring_ref; i++) { + domids[i] = blkdev->xendev.dom; + } + + blkdev->sring = xengnttab_map_grant_refs(blkdev->xendev.gnttabdev, + blkdev->nr_ring_ref, + domids, + blkdev->ring_ref, + PROT_READ | PROT_WRITE); + + g_free(domids); + if (!blkdev->sring) { return -1; } + blkdev->cnt_map++; switch (blkdev->protocol) { case BLKIF_PROTOCOL_NATIVE: { blkif_sring_t *sring_native = blkdev->sring; - BACK_RING_INIT(&blkdev->rings.native, sring_native, XC_PAGE_SIZE); + BACK_RING_INIT(&blkdev->rings.native, sring_native, ring_size); break; } case BLKIF_PROTOCOL_X86_32: { blkif_x86_32_sring_t *sring_x86_32 = blkdev->sring; - BACK_RING_INIT(&blkdev->rings.x86_32_part, sring_x86_32, XC_PAGE_SIZE); + BACK_RING_INIT(&blkdev->rings.x86_32_part, sring_x86_32, ring_size); break; } case BLKIF_PROTOCOL_X86_64: { blkif_x86_64_sring_t *sring_x86_64 = blkdev->sring; - BACK_RING_INIT(&blkdev->rings.x86_64_part, sring_x86_64, XC_PAGE_SIZE); + BACK_RING_INIT(&blkdev->rings.x86_64_part, sring_x86_64, ring_size); break; } } if (blkdev->feature_persistent) { /* Init persistent grants */ - blkdev->max_grants = max_requests * BLKIF_MAX_SEGMENTS_PER_REQUEST; + blkdev->max_grants = blkdev->max_requests * + BLKIF_MAX_SEGMENTS_PER_REQUEST; blkdev->persistent_gnts = g_tree_new_full((GCompareDataFunc)int_cmp, NULL, NULL, batch_maps ? @@ -1202,15 +1289,9 @@ static int blk_connect(struct XenDevice *xendev) xen_be_bind_evtchn(&blkdev->xendev); - blkdev->feature_grant_copy = - (xengnttab_grant_copy(blkdev->xendev.gnttabdev, 0, NULL) == 0); - - xen_pv_printf(&blkdev->xendev, 3, "grant copy operation %s\n", - blkdev->feature_grant_copy ? "enabled" : "disabled"); - - xen_pv_printf(&blkdev->xendev, 1, "ok: proto %s, ring-ref %d, " + xen_pv_printf(&blkdev->xendev, 1, "ok: proto %s, nr-ring-ref %u, " "remote port %d, local port %d\n", - blkdev->xendev.protocol, blkdev->ring_ref, + blkdev->xendev.protocol, blkdev->nr_ring_ref, blkdev->xendev.remote_port, blkdev->xendev.local_port); return 0; } @@ -1227,7 +1308,8 @@ static void blk_disconnect(struct XenDevice *xendev) xen_pv_unbind_evtchn(&blkdev->xendev); if (blkdev->sring) { - xengnttab_unmap(blkdev->xendev.gnttabdev, blkdev->sring, 1); + xengnttab_unmap(blkdev->xendev.gnttabdev, blkdev->sring, + blkdev->nr_ring_ref); blkdev->cnt_map--; blkdev->sring = NULL; } diff --git a/hw/bt/hci-csr.c b/hw/bt/hci-csr.c index 3c193848fc..d13192b9b5 100644 --- a/hw/bt/hci-csr.c +++ b/hw/bt/hci-csr.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu-common.h" -#include "sysemu/char.h" +#include "chardev/char-serial.h" #include "qemu/timer.h" #include "qemu/bswap.h" #include "hw/irq.h" @@ -82,17 +82,14 @@ enum { static inline void csrhci_fifo_wake(struct csrhci_s *s) { - Chardev *chr = (Chardev *)s; - CharBackend *be = chr->be; + Chardev *chr = CHARDEV(s); if (!s->enable || !s->out_len) return; /* XXX: Should wait for s->modem_state & CHR_TIOCM_RTS? */ - if (be && be->chr_can_read && be->chr_can_read(be->opaque) && - be->chr_read) { - be->chr_read(be->opaque, - s->outfifo + s->out_start++, 1); + if (qemu_chr_be_can_write(chr)) { + qemu_chr_be_write(chr, s->outfifo + s->out_start++, 1); s->out_len--; if (s->out_start >= s->out_size) { s->out_start = 0; diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs index 55fcb68fd2..1bcd37e98d 100644 --- a/hw/char/Makefile.objs +++ b/hw/char/Makefile.objs @@ -19,6 +19,7 @@ obj-$(CONFIG_DIGIC) += digic-uart.o obj-$(CONFIG_STM32F2XX_USART) += stm32f2xx_usart.o obj-$(CONFIG_RASPI) += bcm2835_aux.o +common-obj-$(CONFIG_CMSDK_APB_UART) += cmsdk-apb-uart.o common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o common-obj-$(CONFIG_ISA_DEBUG) += debugcon.o common-obj-$(CONFIG_GRLIB) += grlib_apbuart.o diff --git a/hw/char/bcm2835_aux.c b/hw/char/bcm2835_aux.c index 4d46ad60ae..370dc7e296 100644 --- a/hw/char/bcm2835_aux.c +++ b/hw/char/bcm2835_aux.c @@ -279,7 +279,7 @@ static void bcm2835_aux_realize(DeviceState *dev, Error **errp) BCM2835AuxState *s = BCM2835_AUX(dev); qemu_chr_fe_set_handlers(&s->chr, bcm2835_aux_can_receive, - bcm2835_aux_receive, NULL, s, NULL, true); + bcm2835_aux_receive, NULL, NULL, s, NULL, true); } static Property bcm2835_aux_props[] = { diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c index 4dcee571c0..6143494060 100644 --- a/hw/char/cadence_uart.c +++ b/hw/char/cadence_uart.c @@ -23,7 +23,8 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" -#include "sysemu/char.h" +#include "chardev/char-fe.h" +#include "chardev/char-serial.h" #include "qemu/timer.h" #include "qemu/log.h" #include "hw/char/cadence_uart.h" @@ -278,7 +279,7 @@ static gboolean cadence_uart_xmit(GIOChannel *chan, GIOCondition cond, int ret; /* instant drain the fifo when there's no back-end */ - if (!qemu_chr_fe_get_driver(&s->chr)) { + if (!qemu_chr_fe_backend_connected(&s->chr)) { s->tx_count = 0; return FALSE; } @@ -484,7 +485,7 @@ static void cadence_uart_realize(DeviceState *dev, Error **errp) fifo_trigger_update, s); qemu_chr_fe_set_handlers(&s->chr, uart_can_receive, uart_receive, - uart_event, s, NULL, true); + uart_event, NULL, s, NULL, true); } static void cadence_uart_init(Object *obj) diff --git a/hw/char/cmsdk-apb-uart.c b/hw/char/cmsdk-apb-uart.c new file mode 100644 index 0000000000..1ad1e14295 --- /dev/null +++ b/hw/char/cmsdk-apb-uart.c @@ -0,0 +1,403 @@ +/* + * ARM CMSDK APB UART emulation + * + * Copyright (c) 2017 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* This is a model of the "APB UART" which is part of the Cortex-M + * System Design Kit (CMSDK) and documented in the Cortex-M System + * Design Kit Technical Reference Manual (ARM DDI0479C): + * https://developer.arm.com/products/system-design/system-design-kits/cortex-m-system-design-kit + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "trace.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "chardev/char-fe.h" +#include "chardev/char-serial.h" +#include "hw/char/cmsdk-apb-uart.h" + +REG32(DATA, 0) +REG32(STATE, 4) + FIELD(STATE, TXFULL, 0, 1) + FIELD(STATE, RXFULL, 1, 1) + FIELD(STATE, TXOVERRUN, 2, 1) + FIELD(STATE, RXOVERRUN, 3, 1) +REG32(CTRL, 8) + FIELD(CTRL, TX_EN, 0, 1) + FIELD(CTRL, RX_EN, 1, 1) + FIELD(CTRL, TX_INTEN, 2, 1) + FIELD(CTRL, RX_INTEN, 3, 1) + FIELD(CTRL, TXO_INTEN, 4, 1) + FIELD(CTRL, RXO_INTEN, 5, 1) + FIELD(CTRL, HSTEST, 6, 1) +REG32(INTSTATUS, 0xc) + FIELD(INTSTATUS, TX, 0, 1) + FIELD(INTSTATUS, RX, 1, 1) + FIELD(INTSTATUS, TXO, 2, 1) + FIELD(INTSTATUS, RXO, 3, 1) +REG32(BAUDDIV, 0x10) +REG32(PID4, 0xFD0) +REG32(PID5, 0xFD4) +REG32(PID6, 0xFD8) +REG32(PID7, 0xFDC) +REG32(PID0, 0xFE0) +REG32(PID1, 0xFE4) +REG32(PID2, 0xFE8) +REG32(PID3, 0xFEC) +REG32(CID0, 0xFF0) +REG32(CID1, 0xFF4) +REG32(CID2, 0xFF8) +REG32(CID3, 0xFFC) + +/* PID/CID values */ +static const int uart_id[] = { + 0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */ + 0x21, 0xb8, 0x1b, 0x00, /* PID0..PID3 */ + 0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */ +}; + +static bool uart_baudrate_ok(CMSDKAPBUART *s) +{ + /* The minimum permitted bauddiv setting is 16, so we just ignore + * settings below that (usually this means the device has just + * been reset and not yet programmed). + */ + return s->bauddiv >= 16 && s->bauddiv <= s->pclk_frq; +} + +static void uart_update_parameters(CMSDKAPBUART *s) +{ + QEMUSerialSetParams ssp; + + /* This UART is always 8N1 but the baud rate is programmable. */ + if (!uart_baudrate_ok(s)) { + return; + } + + ssp.data_bits = 8; + ssp.parity = 'N'; + ssp.stop_bits = 1; + ssp.speed = s->pclk_frq / s->bauddiv; + qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); + trace_cmsdk_apb_uart_set_params(ssp.speed); +} + +static void cmsdk_apb_uart_update(CMSDKAPBUART *s) +{ + /* update outbound irqs, including handling the way the rxo and txo + * interrupt status bits are just logical AND of the overrun bit in + * STATE and the overrun interrupt enable bit in CTRL. + */ + uint32_t omask = (R_INTSTATUS_RXO_MASK | R_INTSTATUS_TXO_MASK); + s->intstatus &= ~omask; + s->intstatus |= (s->state & (s->ctrl >> 2) & omask); + + qemu_set_irq(s->txint, !!(s->intstatus & R_INTSTATUS_TX_MASK)); + qemu_set_irq(s->rxint, !!(s->intstatus & R_INTSTATUS_RX_MASK)); + qemu_set_irq(s->txovrint, !!(s->intstatus & R_INTSTATUS_TXO_MASK)); + qemu_set_irq(s->rxovrint, !!(s->intstatus & R_INTSTATUS_RXO_MASK)); + qemu_set_irq(s->uartint, !!(s->intstatus)); +} + +static int uart_can_receive(void *opaque) +{ + CMSDKAPBUART *s = CMSDK_APB_UART(opaque); + + /* We can take a char if RX is enabled and the buffer is empty */ + if (s->ctrl & R_CTRL_RX_EN_MASK && !(s->state & R_STATE_RXFULL_MASK)) { + return 1; + } + return 0; +} + +static void uart_receive(void *opaque, const uint8_t *buf, int size) +{ + CMSDKAPBUART *s = CMSDK_APB_UART(opaque); + + trace_cmsdk_apb_uart_receive(*buf); + + /* In fact uart_can_receive() ensures that we can't be + * called unless RX is enabled and the buffer is empty, + * but we include this logic as documentation of what the + * hardware does if a character arrives in these circumstances. + */ + if (!(s->ctrl & R_CTRL_RX_EN_MASK)) { + /* Just drop the character on the floor */ + return; + } + + if (s->state & R_STATE_RXFULL_MASK) { + s->state |= R_STATE_RXOVERRUN_MASK; + } + + s->rxbuf = *buf; + s->state |= R_STATE_RXFULL_MASK; + if (s->ctrl & R_CTRL_RX_INTEN_MASK) { + s->intstatus |= R_INTSTATUS_RX_MASK; + } + cmsdk_apb_uart_update(s); +} + +static uint64_t uart_read(void *opaque, hwaddr offset, unsigned size) +{ + CMSDKAPBUART *s = CMSDK_APB_UART(opaque); + uint64_t r; + + switch (offset) { + case A_DATA: + r = s->rxbuf; + s->state &= ~R_STATE_RXFULL_MASK; + cmsdk_apb_uart_update(s); + break; + case A_STATE: + r = s->state; + break; + case A_CTRL: + r = s->ctrl; + break; + case A_INTSTATUS: + r = s->intstatus; + break; + case A_BAUDDIV: + r = s->bauddiv; + break; + case A_PID4 ... A_CID3: + r = uart_id[(offset - A_PID4) / 4]; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "CMSDK APB UART read: bad offset %x\n", (int) offset); + r = 0; + break; + } + trace_cmsdk_apb_uart_read(offset, r, size); + return r; +} + +/* Try to send tx data, and arrange to be called back later if + * we can't (ie the char backend is busy/blocking). + */ +static gboolean uart_transmit(GIOChannel *chan, GIOCondition cond, void *opaque) +{ + CMSDKAPBUART *s = CMSDK_APB_UART(opaque); + int ret; + + s->watch_tag = 0; + + if (!(s->ctrl & R_CTRL_TX_EN_MASK) || !(s->state & R_STATE_TXFULL_MASK)) { + return FALSE; + } + + ret = qemu_chr_fe_write(&s->chr, &s->txbuf, 1); + if (ret <= 0) { + s->watch_tag = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, + uart_transmit, s); + if (!s->watch_tag) { + /* Most common reason to be here is "no chardev backend": + * just insta-drain the buffer, so the serial output + * goes into a void, rather than blocking the guest. + */ + goto buffer_drained; + } + /* Transmit pending */ + trace_cmsdk_apb_uart_tx_pending(); + return FALSE; + } + +buffer_drained: + /* Character successfully sent */ + trace_cmsdk_apb_uart_tx(s->txbuf); + s->state &= ~R_STATE_TXFULL_MASK; + /* Going from TXFULL set to clear triggers the tx interrupt */ + if (s->ctrl & R_CTRL_TX_INTEN_MASK) { + s->intstatus |= R_INTSTATUS_TX_MASK; + } + cmsdk_apb_uart_update(s); + return FALSE; +} + +static void uart_cancel_transmit(CMSDKAPBUART *s) +{ + if (s->watch_tag) { + g_source_remove(s->watch_tag); + s->watch_tag = 0; + } +} + +static void uart_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + CMSDKAPBUART *s = CMSDK_APB_UART(opaque); + + trace_cmsdk_apb_uart_write(offset, value, size); + + switch (offset) { + case A_DATA: + s->txbuf = value; + if (s->state & R_STATE_TXFULL_MASK) { + /* Buffer already full -- note the overrun and let the + * existing pending transmit callback handle the new char. + */ + s->state |= R_STATE_TXOVERRUN_MASK; + cmsdk_apb_uart_update(s); + } else { + s->state |= R_STATE_TXFULL_MASK; + uart_transmit(NULL, G_IO_OUT, s); + } + break; + case A_STATE: + /* Bits 0 and 1 are read only; bits 2 and 3 are W1C */ + s->state &= ~(value & + (R_STATE_TXOVERRUN_MASK | R_STATE_RXOVERRUN_MASK)); + cmsdk_apb_uart_update(s); + break; + case A_CTRL: + s->ctrl = value & 0x7f; + if ((s->ctrl & R_CTRL_TX_EN_MASK) && !uart_baudrate_ok(s)) { + qemu_log_mask(LOG_GUEST_ERROR, + "CMSDK APB UART: Tx enabled with invalid baudrate\n"); + } + cmsdk_apb_uart_update(s); + break; + case A_INTSTATUS: + /* All bits are W1C. Clearing the overrun interrupt bits really + * clears the overrun status bits in the STATE register (which + * is then reflected into the intstatus value by the update function). + */ + s->state &= ~(value & (R_INTSTATUS_TXO_MASK | R_INTSTATUS_RXO_MASK)); + cmsdk_apb_uart_update(s); + break; + case A_BAUDDIV: + s->bauddiv = value & 0xFFFFF; + uart_update_parameters(s); + break; + case A_PID4 ... A_CID3: + qemu_log_mask(LOG_GUEST_ERROR, + "CMSDK APB UART write: write to RO offset 0x%x\n", + (int)offset); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "CMSDK APB UART write: bad offset 0x%x\n", (int) offset); + break; + } +} + +static const MemoryRegionOps uart_ops = { + .read = uart_read, + .write = uart_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void cmsdk_apb_uart_reset(DeviceState *dev) +{ + CMSDKAPBUART *s = CMSDK_APB_UART(dev); + + trace_cmsdk_apb_uart_reset(); + uart_cancel_transmit(s); + s->state = 0; + s->ctrl = 0; + s->intstatus = 0; + s->bauddiv = 0; + s->txbuf = 0; + s->rxbuf = 0; +} + +static void cmsdk_apb_uart_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + CMSDKAPBUART *s = CMSDK_APB_UART(obj); + + memory_region_init_io(&s->iomem, obj, &uart_ops, s, "uart", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->txint); + sysbus_init_irq(sbd, &s->rxint); + sysbus_init_irq(sbd, &s->txovrint); + sysbus_init_irq(sbd, &s->rxovrint); + sysbus_init_irq(sbd, &s->uartint); +} + +static void cmsdk_apb_uart_realize(DeviceState *dev, Error **errp) +{ + CMSDKAPBUART *s = CMSDK_APB_UART(dev); + + if (s->pclk_frq == 0) { + error_setg(errp, "CMSDK APB UART: pclk-frq property must be set"); + return; + } + + /* This UART has no flow control, so we do not need to register + * an event handler to deal with CHR_EVENT_BREAK. + */ + qemu_chr_fe_set_handlers(&s->chr, uart_can_receive, uart_receive, + NULL, NULL, s, NULL, true); +} + +static int cmsdk_apb_uart_post_load(void *opaque, int version_id) +{ + CMSDKAPBUART *s = CMSDK_APB_UART(opaque); + + /* If we have a pending character, arrange to resend it. */ + if (s->state & R_STATE_TXFULL_MASK) { + s->watch_tag = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, + uart_transmit, s); + } + uart_update_parameters(s); + return 0; +} + +static const VMStateDescription cmsdk_apb_uart_vmstate = { + .name = "cmsdk-apb-uart", + .version_id = 1, + .minimum_version_id = 1, + .post_load = cmsdk_apb_uart_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32(state, CMSDKAPBUART), + VMSTATE_UINT32(ctrl, CMSDKAPBUART), + VMSTATE_UINT32(intstatus, CMSDKAPBUART), + VMSTATE_UINT32(bauddiv, CMSDKAPBUART), + VMSTATE_UINT8(txbuf, CMSDKAPBUART), + VMSTATE_UINT8(rxbuf, CMSDKAPBUART), + VMSTATE_END_OF_LIST() + } +}; + +static Property cmsdk_apb_uart_properties[] = { + DEFINE_PROP_CHR("chardev", CMSDKAPBUART, chr), + DEFINE_PROP_UINT32("pclk-frq", CMSDKAPBUART, pclk_frq, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void cmsdk_apb_uart_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = cmsdk_apb_uart_realize; + dc->vmsd = &cmsdk_apb_uart_vmstate; + dc->reset = cmsdk_apb_uart_reset; + dc->props = cmsdk_apb_uart_properties; +} + +static const TypeInfo cmsdk_apb_uart_info = { + .name = TYPE_CMSDK_APB_UART, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(CMSDKAPBUART), + .instance_init = cmsdk_apb_uart_init, + .class_init = cmsdk_apb_uart_class_init, +}; + +static void cmsdk_apb_uart_register_types(void) +{ + type_register_static(&cmsdk_apb_uart_info); +} + +type_init(cmsdk_apb_uart_register_types); diff --git a/hw/char/debugcon.c b/hw/char/debugcon.c index 80dce07e7f..95ccec6f8b 100644 --- a/hw/char/debugcon.c +++ b/hw/char/debugcon.c @@ -27,7 +27,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/hw.h" -#include "sysemu/char.h" +#include "chardev/char-fe.h" #include "hw/isa/isa.h" #include "hw/i386/pc.h" @@ -87,12 +87,12 @@ static const MemoryRegionOps debugcon_ops = { static void debugcon_realize_core(DebugconState *s, Error **errp) { - if (!qemu_chr_fe_get_driver(&s->chr)) { + if (!qemu_chr_fe_backend_connected(&s->chr)) { error_setg(errp, "Can't create debugcon device, empty char device"); return; } - qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, NULL, s, NULL, true); + qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, NULL, NULL, s, NULL, true); } static void debugcon_isa_realizefn(DeviceState *dev, Error **errp) diff --git a/hw/char/digic-uart.c b/hw/char/digic-uart.c index 029f5bbf5e..6ebcb87a40 100644 --- a/hw/char/digic-uart.c +++ b/hw/char/digic-uart.c @@ -29,7 +29,7 @@ #include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sysbus.h" -#include "sysemu/char.h" +#include "chardev/char-fe.h" #include "qemu/log.h" #include "hw/char/digic-uart.h" @@ -146,7 +146,7 @@ static void digic_uart_realize(DeviceState *dev, Error **errp) DigicUartState *s = DIGIC_UART(dev); qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, - uart_event, s, NULL, true); + uart_event, NULL, s, NULL, true); } static void digic_uart_init(Object *obj) diff --git a/hw/char/escc.c b/hw/char/escc.c index 9228091cec..89ae9eb997 100644 --- a/hw/char/escc.c +++ b/hw/char/escc.c @@ -26,7 +26,8 @@ #include "hw/hw.h" #include "hw/sysbus.h" #include "hw/char/escc.h" -#include "sysemu/char.h" +#include "chardev/char-fe.h" +#include "chardev/char-serial.h" #include "ui/console.h" #include "ui/input.h" #include "trace.h" @@ -416,7 +417,7 @@ static void escc_update_parameters(ChannelState *s) int speed, parity, data_bits, stop_bits; QEMUSerialSetParams ssp; - if (!qemu_chr_fe_get_driver(&s->chr) || s->type != ser) + if (!qemu_chr_fe_backend_connected(&s->chr) || s->type != ser) return; if (s->wregs[W_TXCTRL1] & TXCTRL1_PAREN) { @@ -556,7 +557,7 @@ static void escc_mem_write(void *opaque, hwaddr addr, trace_escc_mem_writeb_data(CHN_C(s), val); s->tx = val; if (s->wregs[W_TXCTRL2] & TXCTRL2_TXEN) { // tx enabled - if (qemu_chr_fe_get_driver(&s->chr)) { + if (qemu_chr_fe_backend_connected(&s->chr)) { /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ qemu_chr_fe_write_all(&s->chr, &s->tx, 1); @@ -1012,10 +1013,10 @@ static void escc_realize(DeviceState *dev, Error **errp) ESCC_SIZE << s->it_shift); for (i = 0; i < 2; i++) { - if (qemu_chr_fe_get_driver(&s->chn[i].chr)) { + if (qemu_chr_fe_backend_connected(&s->chn[i].chr)) { s->chn[i].clock = s->frequency / 2; qemu_chr_fe_set_handlers(&s->chn[i].chr, serial_can_receive, - serial_receive1, serial_event, + serial_receive1, serial_event, NULL, &s->chn[i], NULL, true); } } diff --git a/hw/char/etraxfs_ser.c b/hw/char/etraxfs_ser.c index 54383878e0..a184026410 100644 --- a/hw/char/etraxfs_ser.c +++ b/hw/char/etraxfs_ser.c @@ -24,7 +24,7 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" -#include "sysemu/char.h" +#include "chardev/char-fe.h" #include "qemu/log.h" #define D(x) @@ -233,7 +233,7 @@ static void etraxfs_ser_realize(DeviceState *dev, Error **errp) qemu_chr_fe_set_handlers(&s->chr, serial_can_receive, serial_receive, - serial_event, s, NULL, true); + serial_event, NULL, s, NULL, true); } static void etraxfs_ser_class_init(ObjectClass *klass, void *data) diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c index bff706ab3a..3957e78abf 100644 --- a/hw/char/exynos4210_uart.c +++ b/hw/char/exynos4210_uart.c @@ -23,7 +23,8 @@ #include "hw/sysbus.h" #include "qemu/error-report.h" #include "sysemu/sysemu.h" -#include "sysemu/char.h" +#include "chardev/char-fe.h" +#include "chardev/char-serial.h" #include "hw/arm/exynos4210.h" @@ -379,7 +380,7 @@ static void exynos4210_uart_write(void *opaque, hwaddr offset, break; case UTXH: - if (qemu_chr_fe_get_driver(&s->chr)) { + if (qemu_chr_fe_backend_connected(&s->chr)) { s->reg[I_(UTRSTAT)] &= ~(UTRSTAT_TRANSMITTER_EMPTY | UTRSTAT_Tx_BUFFER_EMPTY); ch = (uint8_t)val; @@ -644,7 +645,7 @@ static void exynos4210_uart_realize(DeviceState *dev, Error **errp) qemu_chr_fe_set_handlers(&s->chr, exynos4210_uart_can_receive, exynos4210_uart_receive, exynos4210_uart_event, - s, NULL, true); + NULL, s, NULL, true); } static Property exynos4210_uart_properties[] = { diff --git a/hw/char/grlib_apbuart.c b/hw/char/grlib_apbuart.c index db686e6a6f..bac11bec58 100644 --- a/hw/char/grlib_apbuart.c +++ b/hw/char/grlib_apbuart.c @@ -24,7 +24,7 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" -#include "sysemu/char.h" +#include "chardev/char-fe.h" #include "trace.h" @@ -201,7 +201,7 @@ static void grlib_apbuart_write(void *opaque, hwaddr addr, case DATA_OFFSET: case DATA_OFFSET + 3: /* When only one byte write */ /* Transmit when character device available and transmitter enabled */ - if (qemu_chr_fe_get_driver(&uart->chr) && + if (qemu_chr_fe_backend_connected(&uart->chr) && (uart->control & UART_TRANSMIT_ENABLE)) { c = value & 0xFF; /* XXX this blocks entire thread. Rewrite to use @@ -247,7 +247,7 @@ static int grlib_apbuart_init(SysBusDevice *dev) grlib_apbuart_can_receive, grlib_apbuart_receive, grlib_apbuart_event, - uart, NULL, true); + NULL, uart, NULL, true); sysbus_init_irq(dev, &uart->irq); diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c index 52e67f8dc9..70405ccf8b 100644 --- a/hw/char/imx_serial.c +++ b/hw/char/imx_serial.c @@ -21,7 +21,6 @@ #include "qemu/osdep.h" #include "hw/char/imx_serial.h" #include "sysemu/sysemu.h" -#include "sysemu/char.h" #include "qemu/log.h" #ifndef DEBUG_IMX_UART @@ -316,7 +315,7 @@ static void imx_serial_realize(DeviceState *dev, Error **errp) DPRINTF("char dev for uart: %p\n", qemu_chr_fe_get_driver(&s->chr)); qemu_chr_fe_set_handlers(&s->chr, imx_can_receive, imx_receive, - imx_event, s, NULL, true); + imx_event, NULL, s, NULL, true); } static void imx_serial_init(Object *obj) diff --git a/hw/char/ipoctal232.c b/hw/char/ipoctal232.c index 93929c2880..5e09caf851 100644 --- a/hw/char/ipoctal232.c +++ b/hw/char/ipoctal232.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "hw/ipack/ipack.h" #include "qemu/bitops.h" -#include "sysemu/char.h" +#include "chardev/char-fe.h" /* #define DEBUG_IPOCTAL */ @@ -542,10 +542,10 @@ static void ipoctal_realize(DeviceState *dev, Error **errp) ch->ipoctal = s; /* Redirect IP-Octal channels to host character devices */ - if (qemu_chr_fe_get_driver(&ch->dev)) { + if (qemu_chr_fe_backend_connected(&ch->dev)) { qemu_chr_fe_set_handlers(&ch->dev, hostdev_can_receive, hostdev_receive, hostdev_event, - ch, NULL, true); + NULL, ch, NULL, true); DPRINTF("Redirecting channel %u to %s\n", i, ch->dev->label); } else { DPRINTF("Could not redirect channel %u, no chardev set\n", i); diff --git a/hw/char/lm32_juart.c b/hw/char/lm32_juart.c index f8c1e0d076..d75c835ad2 100644 --- a/hw/char/lm32_juart.c +++ b/hw/char/lm32_juart.c @@ -21,7 +21,7 @@ #include "hw/hw.h" #include "hw/sysbus.h" #include "trace.h" -#include "sysemu/char.h" +#include "chardev/char-fe.h" #include "hw/char/lm32_juart.h" @@ -119,7 +119,7 @@ static void lm32_juart_realize(DeviceState *dev, Error **errp) LM32JuartState *s = LM32_JUART(dev); qemu_chr_fe_set_handlers(&s->chr, juart_can_rx, juart_rx, - juart_event, s, NULL, true); + juart_event, NULL, s, NULL, true); } static const VMStateDescription vmstate_lm32_juart = { diff --git a/hw/char/lm32_uart.c b/hw/char/lm32_uart.c index 7f3597c4b0..c4a3b9b275 100644 --- a/hw/char/lm32_uart.c +++ b/hw/char/lm32_uart.c @@ -26,7 +26,7 @@ #include "hw/hw.h" #include "hw/sysbus.h" #include "trace.h" -#include "sysemu/char.h" +#include "chardev/char-fe.h" #include "qemu/error-report.h" enum { @@ -266,7 +266,7 @@ static void lm32_uart_realize(DeviceState *dev, Error **errp) LM32UartState *s = LM32_UART(dev); qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, - uart_event, s, NULL, true); + uart_event, NULL, s, NULL, true); } static const VMStateDescription vmstate_lm32_uart = { diff --git a/hw/char/mcf_uart.c b/hw/char/mcf_uart.c index e69672f4e9..56fa402b58 100644 --- a/hw/char/mcf_uart.c +++ b/hw/char/mcf_uart.c @@ -9,7 +9,7 @@ #include "hw/hw.h" #include "hw/sysbus.h" #include "hw/m68k/mcf.h" -#include "sysemu/char.h" +#include "chardev/char-fe.h" #include "exec/address-spaces.h" #include "qapi/error.h" @@ -305,7 +305,7 @@ static void mcf_uart_realize(DeviceState *dev, Error **errp) mcf_uart_state *s = MCF_UART(dev); qemu_chr_fe_set_handlers(&s->chr, mcf_uart_can_receive, mcf_uart_receive, - mcf_uart_event, s, NULL, true); + mcf_uart_event, NULL, s, NULL, true); } static Property mcf_uart_properties[] = { diff --git a/hw/char/milkymist-uart.c b/hw/char/milkymist-uart.c index ae8e2f3554..548ee27bca 100644 --- a/hw/char/milkymist-uart.c +++ b/hw/char/milkymist-uart.c @@ -25,7 +25,7 @@ #include "hw/hw.h" #include "hw/sysbus.h" #include "trace.h" -#include "sysemu/char.h" +#include "chardev/char-fe.h" #include "qemu/error-report.h" enum { @@ -199,7 +199,7 @@ static void milkymist_uart_realize(DeviceState *dev, Error **errp) MilkymistUartState *s = MILKYMIST_UART(dev); qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, - uart_event, s, NULL, true); + uart_event, NULL, s, NULL, true); } static void milkymist_uart_init(Object *obj) diff --git a/hw/char/omap_uart.c b/hw/char/omap_uart.c index 31ebb1592c..6fd1b9cf6b 100644 --- a/hw/char/omap_uart.c +++ b/hw/char/omap_uart.c @@ -18,7 +18,7 @@ * with this program; if not, see <http://www.gnu.org/licenses/>. */ #include "qemu/osdep.h" -#include "sysemu/char.h" +#include "chardev/char.h" #include "hw/hw.h" #include "hw/arm/omap.h" #include "hw/char/serial.h" diff --git a/hw/char/parallel.c b/hw/char/parallel.c index c71a4a0f4f..f79dc76543 100644 --- a/hw/char/parallel.c +++ b/hw/char/parallel.c @@ -25,7 +25,8 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/hw.h" -#include "sysemu/char.h" +#include "chardev/char-parallel.h" +#include "chardev/char-fe.h" #include "hw/isa/isa.h" #include "hw/i386/pc.h" #include "sysemu/sysemu.h" @@ -502,6 +503,10 @@ static const VMStateDescription vmstate_parallel_isa = { } }; +static int parallel_can_receive(void *opaque) +{ + return 1; +} static void parallel_isa_realizefn(DeviceState *dev, Error **errp) { @@ -512,7 +517,7 @@ static void parallel_isa_realizefn(DeviceState *dev, Error **errp) int base; uint8_t dummy; - if (!qemu_chr_fe_get_driver(&s->chr)) { + if (!qemu_chr_fe_backend_connected(&s->chr)) { error_setg(errp, "Can't create parallel device, empty char device"); return; } @@ -534,6 +539,8 @@ static void parallel_isa_realizefn(DeviceState *dev, Error **errp) isa_init_irq(isadev, &s->irq, isa->isairq); qemu_register_reset(parallel_reset, s); + qemu_chr_fe_set_handlers(&s->chr, parallel_can_receive, NULL, + NULL, NULL, s, NULL, true); if (qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_PP_READ_STATUS, &dummy) == 0) { s->hw_driver = 1; s->status = dummy; diff --git a/hw/char/pl011.c b/hw/char/pl011.c index 24ea9738b6..2aa277fc4f 100644 --- a/hw/char/pl011.c +++ b/hw/char/pl011.c @@ -9,7 +9,7 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" -#include "sysemu/char.h" +#include "chardev/char-fe.h" #include "qemu/log.h" #include "trace.h" @@ -329,7 +329,7 @@ static void pl011_realize(DeviceState *dev, Error **errp) PL011State *s = PL011(dev); qemu_chr_fe_set_handlers(&s->chr, pl011_can_receive, pl011_receive, - pl011_event, s, NULL, true); + pl011_event, NULL, s, NULL, true); } static void pl011_class_init(ObjectClass *oc, void *data) diff --git a/hw/char/sclpconsole-lm.c b/hw/char/sclpconsole-lm.c index 07d6ebd112..c500bdaf29 100644 --- a/hw/char/sclpconsole-lm.c +++ b/hw/char/sclpconsole-lm.c @@ -17,7 +17,7 @@ #include "hw/qdev.h" #include "qemu/thread.h" #include "qemu/error-report.h" -#include "sysemu/char.h" +#include "chardev/char-fe.h" #include "hw/s390x/sclp.h" #include "hw/s390x/event-facility.h" @@ -195,7 +195,7 @@ static int write_console_data(SCLPEvent *event, const uint8_t *buf, int len) { SCLPConsoleLM *scon = SCLPLM_CONSOLE(event); - if (!qemu_chr_fe_get_driver(&scon->chr)) { + if (!qemu_chr_fe_backend_connected(&scon->chr)) { /* If there's no backend, we can just say we consumed all data. */ return len; } @@ -313,7 +313,7 @@ static int console_init(SCLPEvent *event) console_available = true; qemu_chr_fe_set_handlers(&scon->chr, chr_can_read, - chr_read, NULL, scon, NULL, true); + chr_read, NULL, NULL, scon, NULL, true); return 0; } diff --git a/hw/char/sclpconsole.c b/hw/char/sclpconsole.c index b78f240a73..d0265dfa7a 100644 --- a/hw/char/sclpconsole.c +++ b/hw/char/sclpconsole.c @@ -19,7 +19,7 @@ #include "hw/s390x/sclp.h" #include "hw/s390x/event-facility.h" -#include "sysemu/char.h" +#include "chardev/char-fe.h" typedef struct ASCIIConsoleData { EventBufferHeader ebh; @@ -163,7 +163,7 @@ static ssize_t write_console_data(SCLPEvent *event, const uint8_t *buf, { SCLPConsole *scon = SCLP_CONSOLE(event); - if (!qemu_chr_fe_get_driver(&scon->chr)) { + if (!qemu_chr_fe_backend_connected(&scon->chr)) { /* If there's no backend, we can just say we consumed all data. */ return len; } @@ -228,7 +228,7 @@ static int console_init(SCLPEvent *event) } console_available = true; qemu_chr_fe_set_handlers(&scon->chr, chr_can_read, - chr_read, NULL, scon, NULL, true); + chr_read, NULL, NULL, scon, NULL, true); return 0; } diff --git a/hw/char/serial.c b/hw/char/serial.c index 03d890ca24..9aec6c60d8 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -25,7 +25,7 @@ #include "qemu/osdep.h" #include "hw/char/serial.h" -#include "sysemu/char.h" +#include "chardev/char-serial.h" #include "qapi/error.h" #include "qemu/timer.h" #include "exec/address-spaces.h" @@ -312,6 +312,24 @@ static void serial_write_fcr(SerialState *s, uint8_t val) } } +static void serial_update_tiocm(SerialState *s) +{ + int flags; + + qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_GET_TIOCM, &flags); + + flags &= ~(CHR_TIOCM_RTS | CHR_TIOCM_DTR); + + if (s->mcr & UART_MCR_RTS) { + flags |= CHR_TIOCM_RTS; + } + if (s->mcr & UART_MCR_DTR) { + flags |= CHR_TIOCM_DTR; + } + + qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_TIOCM, &flags); +} + static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { @@ -426,24 +444,13 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val, break; case 4: { - int flags; int old_mcr = s->mcr; s->mcr = val & 0x1f; if (val & UART_MCR_LOOP) break; if (s->poll_msl >= 0 && old_mcr != s->mcr) { - - qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_GET_TIOCM, &flags); - - flags &= ~(CHR_TIOCM_RTS | CHR_TIOCM_DTR); - - if (val & UART_MCR_RTS) - flags |= CHR_TIOCM_RTS; - if (val & UART_MCR_DTR) - flags |= CHR_TIOCM_DTR; - - qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_TIOCM, &flags); + serial_update_tiocm(s); /* Update the modem status after a one-character-send wait-time, since there may be a response from the device/computer at the other end of the serial line */ timer_mod(s->modem_status_poll, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->char_transmit_time); @@ -884,9 +891,37 @@ static void serial_reset(void *opaque) s->msr &= ~UART_MSR_ANY_DELTA; } +static int serial_be_change(void *opaque) +{ + SerialState *s = opaque; + + qemu_chr_fe_set_handlers(&s->chr, serial_can_receive1, serial_receive1, + serial_event, serial_be_change, s, NULL, true); + + serial_update_parameters(s); + + qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_BREAK, + &s->last_break_enable); + + s->poll_msl = (s->ier & UART_IER_MSI) ? 1 : 0; + serial_update_msl(s); + + if (s->poll_msl >= 0 && !(s->mcr & UART_MCR_LOOP)) { + serial_update_tiocm(s); + } + + if (s->watch_tag > 0) { + g_source_remove(s->watch_tag); + s->watch_tag = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, + serial_watch_cb, s); + } + + return 0; +} + void serial_realize_core(SerialState *s, Error **errp) { - if (!qemu_chr_fe_get_driver(&s->chr)) { + if (!qemu_chr_fe_backend_connected(&s->chr)) { error_setg(errp, "Can't create serial device, empty char device"); return; } @@ -897,7 +932,7 @@ void serial_realize_core(SerialState *s, Error **errp) qemu_register_reset(serial_reset, s); qemu_chr_fe_set_handlers(&s->chr, serial_can_receive1, serial_receive1, - serial_event, s, NULL, true); + serial_event, serial_be_change, s, NULL, true); fifo8_create(&s->recv_fifo, UART_FIFO_LENGTH); fifo8_create(&s->xmit_fifo, UART_FIFO_LENGTH); serial_reset(s); @@ -905,7 +940,7 @@ void serial_realize_core(SerialState *s, Error **errp) void serial_exit_core(SerialState *s) { - qemu_chr_fe_deinit(&s->chr); + qemu_chr_fe_deinit(&s->chr, false); timer_del(s->modem_status_poll); timer_free(s->modem_status_poll); diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c index 303eb0a678..835b5378a0 100644 --- a/hw/char/sh_serial.c +++ b/hw/char/sh_serial.c @@ -27,7 +27,7 @@ #include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sh4/sh.h" -#include "sysemu/char.h" +#include "chardev/char-fe.h" #include "exec/address-spaces.h" #include "qapi/error.h" @@ -110,7 +110,7 @@ static void sh_serial_write(void *opaque, hwaddr offs, } return; case 0x0c: /* FTDR / TDR */ - if (qemu_chr_fe_get_driver(&s->chr)) { + if (qemu_chr_fe_backend_connected(&s->chr)) { ch = val; /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ @@ -400,7 +400,7 @@ void sh_serial_init(MemoryRegion *sysmem, qemu_chr_fe_init(&s->chr, chr, &error_abort); qemu_chr_fe_set_handlers(&s->chr, sh_serial_can_receive1, sh_serial_receive1, - sh_serial_event, s, NULL, true); + sh_serial_event, NULL, s, NULL, true); } s->eri = eri_source; diff --git a/hw/char/spapr_vty.c b/hw/char/spapr_vty.c index e30c8da57c..0fa416ca6b 100644 --- a/hw/char/spapr_vty.c +++ b/hw/char/spapr_vty.c @@ -4,7 +4,7 @@ #include "qemu-common.h" #include "cpu.h" #include "hw/qdev.h" -#include "sysemu/char.h" +#include "chardev/char-fe.h" #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_vio.h" @@ -78,13 +78,13 @@ static void spapr_vty_realize(VIOsPAPRDevice *sdev, Error **errp) { VIOsPAPRVTYDevice *dev = VIO_SPAPR_VTY_DEVICE(sdev); - if (!qemu_chr_fe_get_driver(&dev->chardev)) { + if (!qemu_chr_fe_backend_connected(&dev->chardev)) { error_setg(errp, "chardev property not set"); return; } qemu_chr_fe_set_handlers(&dev->chardev, vty_can_receive, - vty_receive, NULL, dev, NULL, true); + vty_receive, NULL, NULL, dev, NULL, true); } /* Forward declaration */ diff --git a/hw/char/stm32f2xx_usart.c b/hw/char/stm32f2xx_usart.c index 59872e6d3b..268e435338 100644 --- a/hw/char/stm32f2xx_usart.c +++ b/hw/char/stm32f2xx_usart.c @@ -207,7 +207,8 @@ static void stm32f2xx_usart_realize(DeviceState *dev, Error **errp) STM32F2XXUsartState *s = STM32F2XX_USART(dev); qemu_chr_fe_set_handlers(&s->chr, stm32f2xx_usart_can_receive, - stm32f2xx_usart_receive, NULL, s, NULL, true); + stm32f2xx_usart_receive, NULL, NULL, + s, NULL, true); } static void stm32f2xx_usart_class_init(ObjectClass *klass, void *data) diff --git a/hw/char/terminal3270.c b/hw/char/terminal3270.c index b2dda01baa..28f599111d 100644 --- a/hw/char/terminal3270.c +++ b/hw/char/terminal3270.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "sysemu/char.h" +#include "chardev/char-fe.h" #include "hw/s390x/3270-ccw.h" /* Enough spaces for different window sizes. */ @@ -179,7 +179,7 @@ static void terminal_init(EmulatedCcw3270Device *dev, Error **errp) } terminal_available = true; qemu_chr_fe_set_handlers(&t->chr, terminal_can_read, - terminal_read, chr_event, t, NULL, true); + terminal_read, chr_event, NULL, t, NULL, true); } static int read_payload_3270(EmulatedCcw3270Device *dev, uint32_t cda, @@ -239,7 +239,7 @@ static int write_payload_3270(EmulatedCcw3270Device *dev, uint8_t cmd, return 0; } } - if (!qemu_chr_fe_get_driver(&t->chr)) { + if (!qemu_chr_fe_backend_connected(&t->chr)) { /* We just say we consumed all data if there's no backend. */ return count; } diff --git a/hw/char/trace-events b/hw/char/trace-events index 7fd48bb80d..daf4ee470a 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -56,3 +56,12 @@ pl011_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" pl011_can_receive(uint32_t lcr, int read_count, int r) "LCR %08x read_count %d returning %d" pl011_put_fifo(uint32_t c, int read_count) "new char 0x%x read_count now %d" pl011_put_fifo_full(void) "FIFO now full, RXFF set" + +# hw/char/cmsdk_apb_uart.c +cmsdk_apb_uart_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB UART read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +cmsdk_apb_uart_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB UART write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +cmsdk_apb_uart_reset(void) "CMSDK APB UART: reset" +cmsdk_apb_uart_receive(uint8_t c) "CMSDK APB UART: got character 0x%x from backend" +cmsdk_apb_uart_tx_pending(void) "CMSDK APB UART: character send to backend pending" +cmsdk_apb_uart_tx(uint8_t c) "CMSDK APB UART: character 0x%x sent to backend" +cmsdk_apb_uart_set_params(int speed) "CMSDK APB UART: params set to %d 8N1" diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c index 798d9b69fd..198b2a89c0 100644 --- a/hw/char/virtio-console.c +++ b/hw/char/virtio-console.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/char.h" +#include "chardev/char-fe.h" #include "qemu/error-report.h" #include "trace.h" #include "hw/virtio/virtio-serial.h" @@ -49,7 +49,7 @@ static ssize_t flush_buf(VirtIOSerialPort *port, VirtConsole *vcon = VIRTIO_CONSOLE(port); ssize_t ret; - if (!qemu_chr_fe_get_driver(&vcon->chr)) { + if (!qemu_chr_fe_backend_connected(&vcon->chr)) { /* If there's no backend, we can just say we consumed all data. */ return len; } @@ -163,12 +163,35 @@ static void chr_event(void *opaque, int event) } } +static int chr_be_change(void *opaque) +{ + VirtConsole *vcon = opaque; + VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(vcon); + VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port); + + if (k->is_console) { + qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read, + NULL, chr_be_change, vcon, NULL, true); + } else { + qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read, + chr_event, chr_be_change, vcon, NULL, false); + } + + if (vcon->watch) { + g_source_remove(vcon->watch); + vcon->watch = qemu_chr_fe_add_watch(&vcon->chr, + G_IO_OUT | G_IO_HUP, + chr_write_unblocked, vcon); + } + + return 0; +} + static void virtconsole_realize(DeviceState *dev, Error **errp) { VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(dev); VirtConsole *vcon = VIRTIO_CONSOLE(dev); VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(dev); - Chardev *chr = qemu_chr_fe_get_driver(&vcon->chr); if (port->id == 0 && !k->is_console) { error_setg(errp, "Port number 0 on virtio-serial devices reserved " @@ -176,7 +199,7 @@ static void virtconsole_realize(DeviceState *dev, Error **errp) return; } - if (chr) { + if (qemu_chr_fe_backend_connected(&vcon->chr)) { /* * For consoles we don't block guest data transfer just * because nothing is connected - we'll just let it go @@ -188,11 +211,13 @@ static void virtconsole_realize(DeviceState *dev, Error **errp) */ if (k->is_console) { qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read, - NULL, vcon, NULL, true); + NULL, chr_be_change, + vcon, NULL, true); virtio_serial_open(port); } else { qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read, - chr_event, vcon, NULL, false); + chr_event, chr_be_change, + vcon, NULL, false); } } } diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index d797a6796e..f5bc173844 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -186,6 +186,9 @@ static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq, port->elem->out_sg[i].iov_base + port->iov_offset, buf_size); + if (!port->elem) { /* bail if we got disconnected */ + return; + } if (port->throttled) { port->iov_idx = i; if (ret > 0) { @@ -1121,6 +1124,9 @@ static void virtio_serial_device_unrealize(DeviceState *dev, Error **errp) timer_free(vser->post_load->timer); g_free(vser->post_load); } + + qbus_set_hotplug_handler(BUS(&vser->bus), NULL, errp); + virtio_cleanup(vdev); } diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c index c01f41090e..3643dfe067 100644 --- a/hw/char/xen_console.c +++ b/hw/char/xen_console.c @@ -25,7 +25,7 @@ #include "qapi/error.h" #include "hw/hw.h" -#include "sysemu/char.h" +#include "chardev/char-fe.h" #include "hw/xen/xen_backend.h" #include "qapi/error.h" @@ -150,7 +150,7 @@ static void xencons_send(struct XenConsole *con) ssize_t len, size; size = con->buffer.size - con->buffer.consumed; - if (qemu_chr_fe_get_driver(&con->chr)) { + if (qemu_chr_fe_backend_connected(&con->chr)) { len = qemu_chr_fe_write(&con->chr, con->buffer.data + con->buffer.consumed, size); @@ -246,7 +246,7 @@ static int con_initialise(struct XenDevice *xendev) xen_be_bind_evtchn(&con->xendev); qemu_chr_fe_set_handlers(&con->chr, xencons_can_receive, - xencons_receive, NULL, con, NULL, true); + xencons_receive, NULL, NULL, con, NULL, true); xen_pv_printf(xendev, 1, "ring mfn %d, remote port %d, local port %d, limit %zd\n", @@ -261,7 +261,7 @@ static void con_disconnect(struct XenDevice *xendev) { struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); - qemu_chr_fe_deinit(&con->chr); + qemu_chr_fe_deinit(&con->chr, false); xen_pv_unbind_evtchn(&con->xendev); if (con->sring) { diff --git a/hw/char/xilinx_uartlite.c b/hw/char/xilinx_uartlite.c index 37d313b429..2a8bc1e497 100644 --- a/hw/char/xilinx_uartlite.c +++ b/hw/char/xilinx_uartlite.c @@ -24,7 +24,7 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" -#include "sysemu/char.h" +#include "chardev/char-fe.h" #define DUART(x) @@ -212,7 +212,7 @@ static void xilinx_uartlite_realize(DeviceState *dev, Error **errp) XilinxUARTLite *s = XILINX_UARTLITE(dev); qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, - uart_event, s, NULL, true); + uart_event, NULL, s, NULL, true); } static void xilinx_uartlite_init(Object *obj) diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs index 91450b2eab..f8d7a4aaed 100644 --- a/hw/core/Makefile.objs +++ b/hw/core/Makefile.objs @@ -5,7 +5,7 @@ common-obj-y += fw-path-provider.o # irq.o needed for qdev GPIO handling: common-obj-y += irq.o common-obj-y += hotplug.o -obj-y += nmi.o +common-obj-y += nmi.o common-obj-$(CONFIG_EMPTY_SLOT) += empty_slot.o common-obj-$(CONFIG_XILINX_AXI) += stream.o diff --git a/hw/core/loader.c b/hw/core/loader.c index bf17b42cbe..c17ace0a2e 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -150,7 +150,9 @@ int load_image_targphys_as(const char *filename, return -1; } if (size > 0) { - rom_add_file_fixed_as(filename, addr, -1, as); + if (rom_add_file_fixed_as(filename, addr, -1, as) < 0) { + return -1; + } } return size; } @@ -478,6 +480,7 @@ int load_elf_ram(const char *filename, } if (target_data_order != e_ident[EI_DATA]) { + fprintf(stderr, "%s: wrong endianness\n", filename); ret = ELF_LOAD_WRONG_ENDIAN; goto fail; } @@ -611,8 +614,9 @@ static int load_uboot_image(const char *filename, hwaddr *ep, hwaddr *loadaddr, return -1; size = read(fd, hdr, sizeof(uboot_image_header_t)); - if (size < 0) + if (size < sizeof(uboot_image_header_t)) { goto out; + } bswap_uboot_header(hdr); diff --git a/hw/core/machine.c b/hw/core/machine.c index fd6a436064..41b53a17ad 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -21,6 +21,7 @@ #include "qemu/error-report.h" #include "qemu/cutils.h" #include "sysemu/numa.h" +#include "sysemu/qtest.h" static char *machine_get_accel(Object *obj, Error **errp) { @@ -700,34 +701,51 @@ static char *cpu_slot_to_string(const CPUArchId *cpu) return g_string_free(s, false); } -static void machine_numa_validate(MachineState *machine) +static void machine_numa_finish_init(MachineState *machine) { int i; + bool default_mapping; GString *s = g_string_new(NULL); MachineClass *mc = MACHINE_GET_CLASS(machine); const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(machine); assert(nb_numa_nodes); for (i = 0; i < possible_cpus->len; i++) { + if (possible_cpus->cpus[i].props.has_node_id) { + break; + } + } + default_mapping = (i == possible_cpus->len); + + for (i = 0; i < possible_cpus->len; i++) { const CPUArchId *cpu_slot = &possible_cpus->cpus[i]; - /* at this point numa mappings are initilized by CLI options - * or with default mappings so it's sufficient to list - * all not yet mapped CPUs here */ - /* TODO: make it hard error in future */ if (!cpu_slot->props.has_node_id) { - char *cpu_str = cpu_slot_to_string(cpu_slot); - g_string_append_printf(s, "%sCPU %d [%s]", s->len ? ", " : "", i, - cpu_str); - g_free(cpu_str); + /* fetch default mapping from board and enable it */ + CpuInstanceProperties props = cpu_slot->props; + + if (!default_mapping) { + /* record slots with not set mapping, + * TODO: make it hard error in future */ + char *cpu_str = cpu_slot_to_string(cpu_slot); + g_string_append_printf(s, "%sCPU %d [%s]", + s->len ? ", " : "", i, cpu_str); + g_free(cpu_str); + + /* non mapped cpus used to fallback to node 0 */ + props.node_id = 0; + } + + props.has_node_id = true; + machine_set_cpu_numa_node(machine, &props, &error_fatal); } } - if (s->len) { - error_report("warning: CPU(s) not present in any NUMA nodes: %s", - s->str); - error_report("warning: All CPU(s) up to maxcpus should be described " - "in NUMA config, ability to start up with partial NUMA " - "mappings is obsoleted and will be removed in future"); + if (s->len && !qtest_enabled()) { + warn_report("CPU(s) not present in any NUMA nodes: %s", + s->str); + warn_report("All CPU(s) up to maxcpus should be described " + "in NUMA config, ability to start up with partial NUMA " + "mappings is obsoleted and will be removed in future"); } g_string_free(s, true); } @@ -737,7 +755,7 @@ void machine_run_board_init(MachineState *machine) MachineClass *machine_class = MACHINE_GET_CLASS(machine); if (nb_numa_nodes) { - machine_numa_validate(machine); + machine_numa_finish_init(machine); } machine_class->init(machine); } @@ -752,31 +770,11 @@ static void machine_class_finalize(ObjectClass *klass, void *data) g_free(mc->name); } -static void register_compat_prop(const char *driver, - const char *property, - const char *value) -{ - GlobalProperty *p = g_new0(GlobalProperty, 1); - /* Machine compat_props must never cause errors: */ - p->errp = &error_abort; - p->driver = driver; - p->property = property; - p->value = value; - qdev_prop_register_global(p); -} - -static void machine_register_compat_for_subclass(ObjectClass *oc, void *opaque) -{ - GlobalProperty *p = opaque; - register_compat_prop(object_class_get_name(oc), p->property, p->value); -} - void machine_register_compat_props(MachineState *machine) { MachineClass *mc = MACHINE_GET_CLASS(machine); int i; GlobalProperty *p; - ObjectClass *oc; if (!mc->compat_props) { return; @@ -784,22 +782,9 @@ void machine_register_compat_props(MachineState *machine) for (i = 0; i < mc->compat_props->len; i++) { p = g_array_index(mc->compat_props, GlobalProperty *, i); - oc = object_class_by_name(p->driver); - if (oc && object_class_is_abstract(oc)) { - /* temporary hack to make sure we do not override - * globals set explicitly on -global: if an abstract class - * is on compat_props, register globals for all its - * non-abstract subtypes instead. - * - * This doesn't solve the problem for cases where - * a non-abstract typename mentioned on compat_props - * has subclasses, like spapr-pci-host-bridge. - */ - object_class_foreach(machine_register_compat_for_subclass, - p->driver, false, p); - } else { - register_compat_prop(p->driver, p->property, p->value); - } + /* Machine compat_props must never cause errors: */ + p->errp = &error_abort; + qdev_prop_register_global(p); } } diff --git a/hw/core/platform-bus.c b/hw/core/platform-bus.c index 329ac670c0..33d32fbf22 100644 --- a/hw/core/platform-bus.c +++ b/hw/core/platform-bus.c @@ -71,7 +71,7 @@ hwaddr platform_bus_get_mmio_addr(PlatformBusDevice *pbus, SysBusDevice *sbdev, return -1; } - return object_property_get_int(OBJECT(sbdev_mr), "addr", NULL); + return object_property_get_uint(OBJECT(sbdev_mr), "addr", NULL); } static void platform_bus_count_irqs(SysBusDevice *sbdev, void *opaque) diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index 79c2014135..ec10da7424 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -20,7 +20,7 @@ #include "hw/block/block.h" #include "net/hub.h" #include "qapi/visitor.h" -#include "sysemu/char.h" +#include "chardev/char-fe.h" #include "sysemu/iothread.h" static void get_pointer(Object *obj, Visitor *v, Property *prop, @@ -159,7 +159,7 @@ static void set_drive(Object *obj, Visitor *v, const char *name, void *opaque, set_pointer(obj, v, opaque, parse_drive, name, errp); } -PropertyInfo qdev_prop_drive = { +const PropertyInfo qdev_prop_drive = { .name = "str", .description = "Node name or ID of a block device to use as a backend", .get = get_drive, @@ -225,10 +225,10 @@ static void release_chr(Object *obj, const char *name, void *opaque) Property *prop = opaque; CharBackend *be = qdev_get_prop_ptr(dev, prop); - qemu_chr_fe_deinit(be); + qemu_chr_fe_deinit(be, false); } -PropertyInfo qdev_prop_chr = { +const PropertyInfo qdev_prop_chr = { .name = "str", .description = "ID of a chardev to use as a backend", .get = get_chr, @@ -313,7 +313,7 @@ out: g_free(str); } -PropertyInfo qdev_prop_netdev = { +const PropertyInfo qdev_prop_netdev = { .name = "str", .description = "ID of a netdev to use as a backend", .get = get_netdev, @@ -393,7 +393,7 @@ static void set_vlan(Object *obj, Visitor *v, const char *name, void *opaque, *ptr = hubport; } -PropertyInfo qdev_prop_vlan = { +const PropertyInfo qdev_prop_vlan = { .name = "int32", .description = "Integer VLAN id to connect to", .print = print_vlan, diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index fa3617db2d..58a8f92d92 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -9,7 +9,7 @@ #include "hw/block/block.h" #include "net/hub.h" #include "qapi/visitor.h" -#include "sysemu/char.h" +#include "chardev/char.h" void qdev_prop_set_after_realize(DeviceState *dev, const char *name, Error **errp) @@ -25,7 +25,8 @@ void qdev_prop_set_after_realize(DeviceState *dev, const char *name, } } -void qdev_prop_allow_set_link_before_realize(Object *obj, const char *name, +void qdev_prop_allow_set_link_before_realize(const Object *obj, + const char *name, Object *val, Error **errp) { DeviceState *dev = DEVICE(obj); @@ -69,6 +70,12 @@ static void set_enum(Object *obj, Visitor *v, const char *name, void *opaque, visit_type_enum(v, prop->name, ptr, prop->info->enum_table, errp); } +static void set_default_value_enum(Object *obj, const Property *prop) +{ + object_property_set_str(obj, prop->info->enum_table[prop->defval.i], + prop->name, &error_abort); +} + /* Bit */ static uint32_t qdev_get_prop_mask(Property *prop) @@ -120,11 +127,17 @@ static void prop_set_bit(Object *obj, Visitor *v, const char *name, bit_prop_set(dev, prop, value); } -PropertyInfo qdev_prop_bit = { +static void set_default_value_bool(Object *obj, const Property *prop) +{ + object_property_set_bool(obj, prop->defval.u, prop->name, &error_abort); +} + +const PropertyInfo qdev_prop_bit = { .name = "bool", .description = "on/off", .get = prop_get_bit, .set = prop_set_bit, + .set_default_value = set_default_value_bool, }; /* Bit64 */ @@ -178,11 +191,12 @@ static void prop_set_bit64(Object *obj, Visitor *v, const char *name, bit64_prop_set(dev, prop, value); } -PropertyInfo qdev_prop_bit64 = { +const PropertyInfo qdev_prop_bit64 = { .name = "bool", .description = "on/off", .get = prop_get_bit64, .set = prop_set_bit64, + .set_default_value = set_default_value_bool, }; /* --- bool --- */ @@ -212,10 +226,11 @@ static void set_bool(Object *obj, Visitor *v, const char *name, void *opaque, visit_type_bool(v, name, ptr, errp); } -PropertyInfo qdev_prop_bool = { +const PropertyInfo qdev_prop_bool = { .name = "bool", .get = get_bool, .set = set_bool, + .set_default_value = set_default_value_bool, }; /* --- 8bit integer --- */ @@ -245,10 +260,21 @@ static void set_uint8(Object *obj, Visitor *v, const char *name, void *opaque, visit_type_uint8(v, name, ptr, errp); } -PropertyInfo qdev_prop_uint8 = { +static void set_default_value_int(Object *obj, const Property *prop) +{ + object_property_set_int(obj, prop->defval.i, prop->name, &error_abort); +} + +static void set_default_value_uint(Object *obj, const Property *prop) +{ + object_property_set_uint(obj, prop->defval.u, prop->name, &error_abort); +} + +const PropertyInfo qdev_prop_uint8 = { .name = "uint8", .get = get_uint8, .set = set_uint8, + .set_default_value = set_default_value_uint, }; /* --- 16bit integer --- */ @@ -278,10 +304,11 @@ static void set_uint16(Object *obj, Visitor *v, const char *name, visit_type_uint16(v, name, ptr, errp); } -PropertyInfo qdev_prop_uint16 = { +const PropertyInfo qdev_prop_uint16 = { .name = "uint16", .get = get_uint16, .set = set_uint16, + .set_default_value = set_default_value_uint, }; /* --- 32bit integer --- */ @@ -336,16 +363,18 @@ static void set_int32(Object *obj, Visitor *v, const char *name, void *opaque, visit_type_int32(v, name, ptr, errp); } -PropertyInfo qdev_prop_uint32 = { +const PropertyInfo qdev_prop_uint32 = { .name = "uint32", .get = get_uint32, .set = set_uint32, + .set_default_value = set_default_value_uint, }; -PropertyInfo qdev_prop_int32 = { +const PropertyInfo qdev_prop_int32 = { .name = "int32", .get = get_int32, .set = set_int32, + .set_default_value = set_default_value_int, }; /* --- 64bit integer --- */ @@ -375,10 +404,11 @@ static void set_uint64(Object *obj, Visitor *v, const char *name, visit_type_uint64(v, name, ptr, errp); } -PropertyInfo qdev_prop_uint64 = { +const PropertyInfo qdev_prop_uint64 = { .name = "uint64", .get = get_uint64, .set = set_uint64, + .set_default_value = set_default_value_uint, }; /* --- string --- */ @@ -427,7 +457,7 @@ static void set_string(Object *obj, Visitor *v, const char *name, *ptr = str; } -PropertyInfo qdev_prop_string = { +const PropertyInfo qdev_prop_string = { .name = "str", .release = release_string, .get = get_string, @@ -437,7 +467,7 @@ PropertyInfo qdev_prop_string = { /* --- pointer --- */ /* Not a proper property, just for dirty hacks. TODO Remove it! */ -PropertyInfo qdev_prop_ptr = { +const PropertyInfo qdev_prop_ptr = { .name = "ptr", }; @@ -511,7 +541,7 @@ inval: g_free(str); } -PropertyInfo qdev_prop_macaddr = { +const PropertyInfo qdev_prop_macaddr = { .name = "str", .description = "Ethernet 6-byte MAC Address, example: 52:54:00:12:34:56", .get = get_mac, @@ -520,60 +550,65 @@ PropertyInfo qdev_prop_macaddr = { /* --- on/off/auto --- */ -PropertyInfo qdev_prop_on_off_auto = { +const PropertyInfo qdev_prop_on_off_auto = { .name = "OnOffAuto", .description = "on/off/auto", .enum_table = OnOffAuto_lookup, .get = get_enum, .set = set_enum, + .set_default_value = set_default_value_enum, }; /* --- lost tick policy --- */ QEMU_BUILD_BUG_ON(sizeof(LostTickPolicy) != sizeof(int)); -PropertyInfo qdev_prop_losttickpolicy = { +const PropertyInfo qdev_prop_losttickpolicy = { .name = "LostTickPolicy", .enum_table = LostTickPolicy_lookup, .get = get_enum, .set = set_enum, + .set_default_value = set_default_value_enum, }; /* --- Block device error handling policy --- */ QEMU_BUILD_BUG_ON(sizeof(BlockdevOnError) != sizeof(int)); -PropertyInfo qdev_prop_blockdev_on_error = { +const PropertyInfo qdev_prop_blockdev_on_error = { .name = "BlockdevOnError", .description = "Error handling policy, " "report/ignore/enospc/stop/auto", .enum_table = BlockdevOnError_lookup, .get = get_enum, .set = set_enum, + .set_default_value = set_default_value_enum, }; /* --- BIOS CHS translation */ QEMU_BUILD_BUG_ON(sizeof(BiosAtaTranslation) != sizeof(int)); -PropertyInfo qdev_prop_bios_chs_trans = { +const PropertyInfo qdev_prop_bios_chs_trans = { .name = "BiosAtaTranslation", .description = "Logical CHS translation algorithm, " "auto/none/lba/large/rechs", .enum_table = BiosAtaTranslation_lookup, .get = get_enum, .set = set_enum, + .set_default_value = set_default_value_enum, }; /* --- FDC default drive types */ -PropertyInfo qdev_prop_fdc_drive_type = { +const PropertyInfo qdev_prop_fdc_drive_type = { .name = "FdcDriveType", .description = "FDC drive type, " "144/288/120/none/auto", .enum_table = FloppyDriveType_lookup, .get = get_enum, - .set = set_enum + .set = set_enum, + .set_default_value = set_default_value_enum, }; /* --- pci address --- */ @@ -642,12 +677,13 @@ static int print_pci_devfn(DeviceState *dev, Property *prop, char *dest, } } -PropertyInfo qdev_prop_pci_devfn = { +const PropertyInfo qdev_prop_pci_devfn = { .name = "int32", .description = "Slot and optional function number, example: 06.0 or 06", .print = print_pci_devfn, .get = get_int32, .set = set_pci_devfn, + .set_default_value = set_default_value_int, }; /* --- blocksize --- */ @@ -690,11 +726,12 @@ static void set_blocksize(Object *obj, Visitor *v, const char *name, *ptr = value; } -PropertyInfo qdev_prop_blocksize = { +const PropertyInfo qdev_prop_blocksize = { .name = "uint16", .description = "A power of two between 512 and 32768", .get = get_uint16, .set = set_blocksize, + .set_default_value = set_default_value_uint, }; /* --- pci host address --- */ @@ -804,7 +841,7 @@ inval: g_free(str); } -PropertyInfo qdev_prop_pci_host_devaddr = { +const PropertyInfo qdev_prop_pci_host_devaddr = { .name = "str", .description = "Address (bus/device/function) of " "the host device, example: 04:10.0", @@ -913,10 +950,11 @@ static void set_prop_arraylen(Object *obj, Visitor *v, const char *name, } } -PropertyInfo qdev_prop_arraylen = { +const PropertyInfo qdev_prop_arraylen = { .name = "uint32", .get = get_uint32, .set = set_prop_arraylen, + .set_default_value = set_default_value_uint, }; /* --- public helpers --- */ @@ -1047,6 +1085,27 @@ void qdev_prop_register_global(GlobalProperty *prop) global_props = g_list_append(global_props, prop); } +void register_compat_prop(const char *driver, + const char *property, + const char *value) +{ + GlobalProperty *p = g_new0(GlobalProperty, 1); + + /* Any compat_props must never cause error */ + p->errp = &error_abort; + p->driver = driver; + p->property = property; + p->value = value; + qdev_prop_register_global(p); +} + +void register_compat_props_array(GlobalProperty *prop) +{ + for (; prop && prop->driver; prop++) { + register_compat_prop(prop->driver, prop->property, prop->value); + } +} + void qdev_prop_register_global_list(GlobalProperty *props) { int i; @@ -1074,15 +1133,15 @@ int qdev_prop_check_globals(void) oc = object_class_by_name(prop->driver); oc = object_class_dynamic_cast(oc, TYPE_DEVICE); if (!oc) { - error_report("Warning: global %s.%s has invalid class name", - prop->driver, prop->property); + warn_report("global %s.%s has invalid class name", + prop->driver, prop->property); ret = 1; continue; } dc = DEVICE_CLASS(oc); if (!dc->hotpluggable && !prop->used) { - error_report("Warning: global %s.%s=%s not used", - prop->driver, prop->property, prop->value); + warn_report("global %s.%s=%s not used", + prop->driver, prop->property, prop->value); ret = 1; continue; } @@ -1090,8 +1149,7 @@ int qdev_prop_check_globals(void) return ret; } -static void qdev_prop_set_globals_for_type(DeviceState *dev, - const char *typename) +void qdev_prop_set_globals(DeviceState *dev) { GList *l; @@ -1099,7 +1157,7 @@ static void qdev_prop_set_globals_for_type(DeviceState *dev, GlobalProperty *prop = l->data; Error *err = NULL; - if (strcmp(typename, prop->driver) != 0) { + if (object_dynamic_cast(OBJECT(dev), prop->driver) == NULL) { continue; } prop->used = true; @@ -1111,22 +1169,12 @@ static void qdev_prop_set_globals_for_type(DeviceState *dev, error_propagate(prop->errp, err); } else { assert(prop->user_provided); - error_reportf_err(err, "Warning: "); + warn_report_err(err); } } } } -void qdev_prop_set_globals(DeviceState *dev) -{ - ObjectClass *class = object_get_class(OBJECT(dev)); - - do { - qdev_prop_set_globals_for_type(dev, object_class_get_name(class)); - class = object_class_get_parent(class); - } while (class); -} - /* --- 64bit unsigned int 'size' type --- */ static void get_size(Object *obj, Visitor *v, const char *name, void *opaque, @@ -1149,8 +1197,27 @@ static void set_size(Object *obj, Visitor *v, const char *name, void *opaque, visit_type_size(v, name, ptr, errp); } -PropertyInfo qdev_prop_size = { +const PropertyInfo qdev_prop_size = { .name = "size", .get = get_size, .set = set_size, + .set_default_value = set_default_value_uint, +}; + +/* --- object link property --- */ + +static void create_link_property(Object *obj, Property *prop, Error **errp) +{ + Object **child = qdev_get_prop_ptr(DEVICE(obj), prop); + + object_property_add_link(obj, prop->name, prop->link_type, + child, + qdev_prop_allow_set_link_before_realize, + OBJ_PROP_LINK_UNREF_ON_RELEASE, + errp); +} + +const PropertyInfo qdev_prop_link = { + .name = "link", + .create = create_link_property, }; diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 71ff95fd71..606ab53c42 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -37,7 +37,6 @@ #include "hw/boards.h" #include "hw/sysbus.h" #include "qapi-event.h" -#include "migration/vmstate.h" bool qdev_hotplug = false; static bool qdev_hot_added = false; @@ -745,6 +744,10 @@ static void qdev_property_add_legacy(DeviceState *dev, Property *prop, return; } + if (prop->info->create) { + return; + } + name = g_strdup_printf("legacy-%s", prop->name); object_property_add(OBJECT(dev), name, "str", prop->info->print ? qdev_get_legacy_property : prop->info->get, @@ -771,20 +774,23 @@ void qdev_property_add_static(DeviceState *dev, Property *prop, Error *local_err = NULL; Object *obj = OBJECT(dev); - /* - * TODO qdev_prop_ptr does not have getters or setters. It must - * go now that it can be replaced with links. The test should be - * removed along with it: all static properties are read/write. - */ - if (!prop->info->get && !prop->info->set) { - return; + if (prop->info->create) { + prop->info->create(obj, prop, &local_err); + } else { + /* + * TODO qdev_prop_ptr does not have getters or setters. It must + * go now that it can be replaced with links. The test should be + * removed along with it: all static properties are read/write. + */ + if (!prop->info->get && !prop->info->set) { + return; + } + object_property_add(obj, prop->name, prop->info->name, + prop->info->get, prop->info->set, + prop->info->release, + prop, &local_err); } - object_property_add(obj, prop->name, prop->info->name, - prop->info->get, prop->info->set, - prop->info->release, - prop, &local_err); - if (local_err) { error_propagate(errp, local_err); return; @@ -794,17 +800,8 @@ void qdev_property_add_static(DeviceState *dev, Property *prop, prop->info->description, &error_abort); - if (prop->qtype == QTYPE_NONE) { - return; - } - - if (prop->qtype == QTYPE_QBOOL) { - object_property_set_bool(obj, prop->defval, prop->name, &error_abort); - } else if (prop->info->enum_table) { - object_property_set_str(obj, prop->info->enum_table[prop->defval], - prop->name, &error_abort); - } else if (prop->qtype == QTYPE_QINT) { - object_property_set_int(obj, prop->defval, prop->name, &error_abort); + if (prop->set_default) { + prop->info->set_default_value(obj, prop); } } diff --git a/hw/core/register.c b/hw/core/register.c index da38ef3a54..900294b9c4 100644 --- a/hw/core/register.c +++ b/hw/core/register.c @@ -195,8 +195,8 @@ void register_write_memory(void *opaque, hwaddr addr, } if (!reg) { - qemu_log_mask(LOG_GUEST_ERROR, "Write to unimplemented register at " \ - "address: %#" PRIx64 "\n", addr); + qemu_log_mask(LOG_GUEST_ERROR, "%s: write to unimplemented register " \ + "at address: %#" PRIx64 "\n", reg_array->prefix, addr); return; } @@ -224,8 +224,8 @@ uint64_t register_read_memory(void *opaque, hwaddr addr, } if (!reg) { - qemu_log_mask(LOG_GUEST_ERROR, "Read to unimplemented register at " \ - "address: %#" PRIx64 "\n", addr); + qemu_log_mask(LOG_GUEST_ERROR, "%s: read to unimplemented register " \ + "at address: %#" PRIx64 "\n", reg_array->prefix, addr); return 0; } diff --git a/hw/cpu/Makefile.objs b/hw/cpu/Makefile.objs index 942a4bb82e..cd52d20b65 100644 --- a/hw/cpu/Makefile.objs +++ b/hw/cpu/Makefile.objs @@ -2,5 +2,4 @@ obj-$(CONFIG_ARM11MPCORE) += arm11mpcore.o obj-$(CONFIG_REALVIEW) += realview_mpcore.o obj-$(CONFIG_A9MPCORE) += a9mpcore.o obj-$(CONFIG_A15MPCORE) += a15mpcore.o -obj-y += core.o - +common-obj-y += core.o diff --git a/hw/cris/axis_dev88.c b/hw/cris/axis_dev88.c index 60df8877c1..80674f6bbb 100644 --- a/hw/cris/axis_dev88.c +++ b/hw/cris/axis_dev88.c @@ -281,9 +281,8 @@ void axisdev88_init(MachineState *machine) /* The ETRAX-FS has 128Kb on chip ram, the docs refer to it as the internal memory. */ - memory_region_init_ram(phys_intmem, NULL, "axisdev88.chipram", INTMEM_SIZE, - &error_fatal); - vmstate_register_ram_global(phys_intmem); + memory_region_init_ram(phys_intmem, NULL, "axisdev88.chipram", + INTMEM_SIZE, &error_fatal); memory_region_add_subregion(address_space_mem, 0x38000000, phys_intmem); /* Attach a NAND flash to CS1. */ diff --git a/hw/display/cg3.c b/hw/display/cg3.c index 1de15a1d34..e069c4484c 100644 --- a/hw/display/cg3.c +++ b/hw/display/cg3.c @@ -283,7 +283,7 @@ static void cg3_initfn(Object *obj) SysBusDevice *sbd = SYS_BUS_DEVICE(obj); CG3State *s = CG3(obj); - memory_region_init_ram(&s->rom, obj, "cg3.prom", FCODE_MAX_ROM_SIZE, + memory_region_init_ram_nomigrate(&s->rom, obj, "cg3.prom", FCODE_MAX_ROM_SIZE, &error_fatal); memory_region_set_readonly(&s->rom, true); sysbus_init_mmio(sbd, &s->rom); @@ -314,7 +314,6 @@ static void cg3_realizefn(DeviceState *dev, Error **errp) memory_region_init_ram(&s->vram_mem, NULL, "cg3.vram", s->vram_size, &error_fatal); memory_region_set_log(&s->vram_mem, true, DIRTY_MEMORY_VGA); - vmstate_register_ram_global(&s->vram_mem); sysbus_init_mmio(sbd, &s->vram_mem); sysbus_init_irq(sbd, &s->irq); diff --git a/hw/display/qxl.c b/hw/display/qxl.c index ad09bb98f9..7f8c73b56d 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -2091,14 +2091,12 @@ static void qxl_realize_common(PCIQXLDevice *qxl, Error **errp) qxl->rom_size = qxl_rom_size(); memory_region_init_ram(&qxl->rom_bar, OBJECT(qxl), "qxl.vrom", qxl->rom_size, &error_fatal); - vmstate_register_ram(&qxl->rom_bar, &qxl->pci.qdev); init_qxl_rom(qxl); init_qxl_ram(qxl); qxl->guest_surfaces.cmds = g_new0(QXLPHYSICAL, qxl->ssd.num_surfaces); memory_region_init_ram(&qxl->vram_bar, OBJECT(qxl), "qxl.vram", qxl->vram_size, &error_fatal); - vmstate_register_ram(&qxl->vram_bar, &qxl->pci.qdev); memory_region_init_alias(&qxl->vram32_bar, OBJECT(qxl), "qxl.vram32", &qxl->vram_bar, 0, qxl->vram32_size); @@ -2200,7 +2198,6 @@ static void qxl_realize_secondary(PCIDevice *dev, Error **errp) qxl_init_ramsize(qxl); memory_region_init_ram(&qxl->vga.vram, OBJECT(dev), "qxl.vgavram", qxl->vga.vram_size, &error_fatal); - vmstate_register_ram(&qxl->vga.vram, &qxl->pci.qdev); qxl->vga.vram_ptr = memory_region_get_ram_ptr(&qxl->vga.vram); qxl->vga.con = graphic_console_init(DEVICE(dev), 0, &qxl_ops, qxl); @@ -2373,12 +2370,12 @@ static VMStateDescription qxl_vmstate = { VMSTATE_UINT32(last_release_offset, PCIQXLDevice), VMSTATE_UINT32(mode, PCIQXLDevice), VMSTATE_UINT32(ssd.unique, PCIQXLDevice), - VMSTATE_INT32_EQUAL(num_memslots, PCIQXLDevice), + VMSTATE_INT32_EQUAL(num_memslots, PCIQXLDevice, NULL), VMSTATE_STRUCT_ARRAY(guest_slots, PCIQXLDevice, NUM_MEMSLOTS, 0, qxl_memslot, struct guest_slots), VMSTATE_STRUCT(guest_primary.surface, PCIQXLDevice, 0, qxl_surface, QXLSurfaceCreate), - VMSTATE_INT32_EQUAL(ssd.num_surfaces, PCIQXLDevice), + VMSTATE_INT32_EQUAL(ssd.num_surfaces, PCIQXLDevice, NULL), VMSTATE_VARRAY_INT32(guest_surfaces.cmds, PCIQXLDevice, ssd.num_surfaces, 0, vmstate_info_uint64, uint64_t), diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 9d254ef2e1..af792c533b 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -1578,7 +1578,7 @@ static void sm501_init(SM501State *s, DeviceState *dev, s->local_mem_size_index); /* local memory */ - memory_region_init_ram(&s->local_mem_region, OBJECT(dev), "sm501.local", + memory_region_init_ram_nomigrate(&s->local_mem_region, OBJECT(dev), "sm501.local", get_local_mem_size(s), &error_fatal); vmstate_register_ram_global(&s->local_mem_region); memory_region_set_log(&s->local_mem_region, true, DIRTY_MEMORY_VGA); diff --git a/hw/display/tc6393xb.c b/hw/display/tc6393xb.c index 92f7120acc..74d10af3d4 100644 --- a/hw/display/tc6393xb.c +++ b/hw/display/tc6393xb.c @@ -588,7 +588,6 @@ TC6393xbState *tc6393xb_init(MemoryRegion *sysmem, uint32_t base, qemu_irq irq) memory_region_init_ram(&s->vram, NULL, "tc6393xb.vram", 0x100000, &error_fatal); - vmstate_register_ram_global(&s->vram); s->vram_ptr = memory_region_get_ram_ptr(&s->vram); memory_region_add_subregion(sysmem, base + 0x100000, &s->vram); s->scr_width = 480; diff --git a/hw/display/tcx.c b/hw/display/tcx.c index 6593c1d6af..daa93e0929 100644 --- a/hw/display/tcx.c +++ b/hw/display/tcx.c @@ -752,7 +752,7 @@ static void tcx_initfn(Object *obj) SysBusDevice *sbd = SYS_BUS_DEVICE(obj); TCXState *s = TCX(obj); - memory_region_init_ram(&s->rom, obj, "tcx.prom", FCODE_MAX_ROM_SIZE, + memory_region_init_ram_nomigrate(&s->rom, obj, "tcx.prom", FCODE_MAX_ROM_SIZE, &error_fatal); memory_region_set_readonly(&s->rom, true); sysbus_init_mmio(sbd, &s->rom); @@ -812,7 +812,7 @@ static void tcx_realizefn(DeviceState *dev, Error **errp) uint8_t *vram_base; char *fcode_filename; - memory_region_init_ram(&s->vram_mem, OBJECT(s), "tcx.vram", + memory_region_init_ram_nomigrate(&s->vram_mem, OBJECT(s), "tcx.vram", s->vram_size * (1 + 4 + 4), &error_fatal); vmstate_register_ram_global(&s->vram_mem); memory_region_set_log(&s->vram_mem, true, DIRTY_MEMORY_VGA); diff --git a/hw/display/vga.c b/hw/display/vga.c index dcc95f88e2..63421f9ee8 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -2099,7 +2099,7 @@ const VMStateDescription vmstate_vga_common = { VMSTATE_BUFFER(palette, VGACommonState), VMSTATE_INT32(bank_offset, VGACommonState), - VMSTATE_UINT8_EQUAL(is_vbe_vmstate, VGACommonState), + VMSTATE_UINT8_EQUAL(is_vbe_vmstate, VGACommonState, NULL), VMSTATE_UINT16(vbe_index, VGACommonState), VMSTATE_UINT16_ARRAY(vbe_regs, VGACommonState, VBE_DISPI_INDEX_NB), VMSTATE_UINT32(vbe_start_addr, VGACommonState), @@ -2166,7 +2166,7 @@ void vga_common_init(VGACommonState *s, Object *obj, bool global_vmstate) } s->is_vbe_vmstate = 1; - memory_region_init_ram(&s->vram, obj, "vga.vram", s->vram_size, + memory_region_init_ram_nomigrate(&s->vram, obj, "vga.vram", s->vram_size, &error_fatal); vmstate_register_ram(&s->vram, global_vmstate ? NULL : DEVICE(obj)); xen_register_framebuffer(&s->vram); diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 58dc0b2737..6aae147324 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -962,7 +962,7 @@ static const VMStateDescription vmstate_virtio_gpu_scanouts = { .version_id = 1, .fields = (VMStateField[]) { VMSTATE_INT32(enable, struct VirtIOGPU), - VMSTATE_UINT32_EQUAL(conf.max_outputs, struct VirtIOGPU), + VMSTATE_UINT32_EQUAL(conf.max_outputs, struct VirtIOGPU, NULL), VMSTATE_STRUCT_VARRAY_UINT32(scanout, struct VirtIOGPU, conf.max_outputs, 1, vmstate_virtio_gpu_scanout, @@ -1092,7 +1092,9 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, dpy_gfx_replace_surface(scanout->con, scanout->ds); dpy_gfx_update(scanout->con, 0, 0, scanout->width, scanout->height); - update_cursor(g, &scanout->cursor); + if (scanout->cursor.resource_id) { + update_cursor(g, &scanout->cursor); + } res->scanout_bitmask |= (1 << i); } diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index ec5f27d67e..4a64b41259 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -1192,7 +1192,7 @@ static const VMStateDescription vmstate_vmware_vga_internal = { .minimum_version_id = 0, .post_load = vmsvga_post_load, .fields = (VMStateField[]) { - VMSTATE_INT32_EQUAL(new_depth, struct vmsvga_state_s), + VMSTATE_INT32_EQUAL(new_depth, struct vmsvga_state_s, NULL), VMSTATE_INT32(enable, struct vmsvga_state_s), VMSTATE_INT32(config, struct vmsvga_state_s), VMSTATE_INT32(cursor.id, struct vmsvga_state_s), @@ -1241,7 +1241,6 @@ static void vmsvga_init(DeviceState *dev, struct vmsvga_state_s *s, s->fifo_size = SVGA_FIFO_SIZE; memory_region_init_ram(&s->fifo_ram, NULL, "vmsvga.fifo", s->fifo_size, &error_fatal); - vmstate_register_ram_global(&s->fifo_ram); s->fifo_ptr = memory_region_get_ram_ptr(&s->fifo_ram); vga_common_init(&s->vga, OBJECT(dev), true); diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c index 7a8727aa21..df8b78f6f4 100644 --- a/hw/display/xenfb.c +++ b/hw/display/xenfb.c @@ -28,7 +28,6 @@ #include "hw/hw.h" #include "ui/console.h" -#include "sysemu/char.h" #include "hw/xen/xen_backend.h" #include <xen/event_channel.h> @@ -47,7 +46,6 @@ struct common { struct XenDevice xendev; /* must be first */ void *page; - QemuConsole *con; }; struct XenInput { @@ -62,6 +60,7 @@ struct XenInput { struct XenFB { struct common c; + QemuConsole *con; size_t fb_len; int row_stride; int depth; @@ -72,7 +71,6 @@ struct XenFB { int fbpages; int feature_update; int bug_trigger; - int have_console; int do_resize; struct { @@ -81,6 +79,7 @@ struct XenFB { int up_count; int up_fullscreen; }; +static const GraphicHwOps xenfb_ops; /* -------------------------------------------------------------------- */ @@ -307,10 +306,18 @@ static void xenfb_mouse_event(void *opaque, int dx, int dy, int dz, int button_state) { struct XenInput *xenfb = opaque; - DisplaySurface *surface = qemu_console_surface(xenfb->c.con); - int dw = surface_width(surface); - int dh = surface_height(surface); - int i; + QemuConsole *con = qemu_console_lookup_by_index(0); + DisplaySurface *surface; + int dw, dh, i; + + if (!con) { + xen_pv_printf(&xenfb->c.xendev, 0, "No QEMU console available"); + return; + } + + surface = qemu_console_surface(con); + dw = surface_width(surface); + dh = surface_height(surface); trace_xenfb_mouse_event(opaque, dx, dy, dz, button_state, xenfb->abs_pointer_wanted); @@ -345,11 +352,6 @@ static int input_initialise(struct XenDevice *xendev) struct XenInput *in = container_of(xendev, struct XenInput, c.xendev); int rc; - if (!in->c.con) { - xen_pv_printf(xendev, 1, "ds not set (yet)\n"); - return -1; - } - rc = common_bind(&in->c); if (rc != 0) return rc; @@ -609,7 +611,7 @@ static int xenfb_configure_fb(struct XenFB *xenfb, size_t fb_len_lim, */ static void xenfb_guest_copy(struct XenFB *xenfb, int x, int y, int w, int h) { - DisplaySurface *surface = qemu_console_surface(xenfb->c.con); + DisplaySurface *surface = qemu_console_surface(xenfb->con); int line, oops = 0; int bpp = surface_bits_per_pixel(surface); int linesize = surface_stride(surface); @@ -643,7 +645,7 @@ static void xenfb_guest_copy(struct XenFB *xenfb, int x, int y, int w, int h) xen_pv_printf(&xenfb->c.xendev, 0, "%s: oops: convert %d -> %d bpp?\n", __FUNCTION__, xenfb->depth, bpp); - dpy_gfx_update(xenfb->c.con, x, y, w, h); + dpy_gfx_update(xenfb->con, x, y, w, h); } #ifdef XENFB_TYPE_REFRESH_PERIOD @@ -729,7 +731,7 @@ static void xenfb_update(void *opaque) surface = qemu_create_displaysurface(xenfb->width, xenfb->height); break; } - dpy_gfx_replace_surface(xenfb->c.con, surface); + dpy_gfx_replace_surface(xenfb->con, surface); xen_pv_printf(&xenfb->c.xendev, 1, "update: resizing: %dx%d @ %d bpp%s\n", xenfb->width, xenfb->height, xenfb->depth, @@ -878,16 +880,7 @@ static int fb_initialise(struct XenDevice *xendev) if (rc != 0) return rc; -#if 0 /* handled in xen_init_display() for now */ - if (!fb->have_console) { - fb->c.ds = graphic_console_init(xenfb_update, - xenfb_invalidate, - NULL, - NULL, - fb); - fb->have_console = 1; - } -#endif + fb->con = graphic_console_init(NULL, 0, &xenfb_ops, fb); if (xenstore_read_fe_int(xendev, "feature-update", &fb->feature_update) == -1) fb->feature_update = 0; @@ -973,42 +966,3 @@ static const GraphicHwOps xenfb_ops = { .gfx_update = xenfb_update, .update_interval = xenfb_update_interval, }; - -/* - * FIXME/TODO: Kill this. - * Temporary needed while DisplayState reorganization is in flight. - */ -void xen_init_display(int domid) -{ - struct XenDevice *xfb, *xin; - struct XenFB *fb; - struct XenInput *in; - int i = 0; - -wait_more: - i++; - main_loop_wait(true); - xfb = xen_pv_find_xendev("vfb", domid, 0); - xin = xen_pv_find_xendev("vkbd", domid, 0); - if (!xfb || !xin) { - if (i < 256) { - usleep(10000); - goto wait_more; - } - xen_pv_printf(NULL, 1, "displaystate setup failed\n"); - return; - } - - /* vfb */ - fb = container_of(xfb, struct XenFB, c.xendev); - fb->c.con = graphic_console_init(NULL, 0, &xenfb_ops, fb); - fb->have_console = 1; - - /* vkbd */ - in = container_of(xin, struct XenInput, c.xendev); - in->c.con = fb->c.con; - - /* retry ->init() */ - xen_be_check_state(xin); - xen_be_check_state(xfb); -} diff --git a/hw/display/xlnx_dp.c b/hw/display/xlnx_dp.c index f7b7b80c68..561f828e7a 100644 --- a/hw/display/xlnx_dp.c +++ b/hw/display/xlnx_dp.c @@ -515,7 +515,7 @@ static void xlnx_dp_aux_set_command(XlnxDPState *s, uint32_t value) s->core_registers[DP_INTERRUPT_SIGNAL_STATE] |= 0x04; } -static void xlnx_dp_set_dpdma(Object *obj, const char *name, Object *val, +static void xlnx_dp_set_dpdma(const Object *obj, const char *name, Object *val, Error **errp) { XlnxDPState *s = XLNX_DP(obj); @@ -624,6 +624,9 @@ static void xlnx_dp_change_graphic_fmt(XlnxDPState *s) case 0: s->v_plane.format = PIXMAN_x8b8g8r8; break; + case DP_NL_VID_Y0_CB_Y1_CR: + s->v_plane.format = PIXMAN_yuy2; + break; case DP_NL_VID_RGBA8880: s->v_plane.format = PIXMAN_x8b8g8r8; break; diff --git a/hw/dma/rc4030.c b/hw/dma/rc4030.c index 0080141905..5d4833eeca 100644 --- a/hw/dma/rc4030.c +++ b/hw/dma/rc4030.c @@ -54,6 +54,8 @@ typedef struct dma_pagetable_entry { #define RC4030(obj) \ OBJECT_CHECK(rc4030State, (obj), TYPE_RC4030) +#define TYPE_RC4030_IOMMU_MEMORY_REGION "rc4030-iommu-memory-region" + typedef struct rc4030State { SysBusDevice parent; @@ -90,7 +92,7 @@ typedef struct rc4030State qemu_irq jazz_bus_irq; /* whole DMA memory region, root of DMA address space */ - MemoryRegion dma_mr; + IOMMUMemoryRegion dma_mr; AddressSpace dma_as; MemoryRegion iomem_chipset; @@ -488,8 +490,8 @@ static const MemoryRegionOps jazzio_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static IOMMUTLBEntry rc4030_dma_translate(MemoryRegion *iommu, hwaddr addr, - bool is_write) +static IOMMUTLBEntry rc4030_dma_translate(IOMMUMemoryRegion *iommu, hwaddr addr, + IOMMUAccessFlags flag) { rc4030State *s = container_of(iommu, rc4030State, dma_mr); IOMMUTLBEntry ret = { @@ -516,10 +518,6 @@ static IOMMUTLBEntry rc4030_dma_translate(MemoryRegion *iommu, hwaddr addr, return ret; } -static const MemoryRegionIOMMUOps rc4030_dma_ops = { - .translate = rc4030_dma_translate, -}; - static void rc4030_reset(DeviceState *dev) { rc4030State *s = RC4030(dev); @@ -677,9 +675,10 @@ static void rc4030_realize(DeviceState *dev, Error **errp) memory_region_init_io(&s->iomem_jazzio, NULL, &jazzio_ops, s, "rc4030.jazzio", 0x00001000); - memory_region_init_iommu(&s->dma_mr, o, &rc4030_dma_ops, - "rc4030.dma", UINT32_MAX); - address_space_init(&s->dma_as, &s->dma_mr, "rc4030-dma"); + memory_region_init_iommu(&s->dma_mr, sizeof(s->dma_mr), + TYPE_RC4030_IOMMU_MEMORY_REGION, + o, "rc4030.dma", UINT32_MAX); + address_space_init(&s->dma_as, MEMORY_REGION(&s->dma_mr), "rc4030-dma"); } static void rc4030_unrealize(DeviceState *dev, Error **errp) @@ -710,14 +709,29 @@ static const TypeInfo rc4030_info = { .class_init = rc4030_class_init, }; +static void rc4030_iommu_memory_region_class_init(ObjectClass *klass, + void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = rc4030_dma_translate; +} + +static const TypeInfo rc4030_iommu_memory_region_info = { + .parent = TYPE_IOMMU_MEMORY_REGION, + .name = TYPE_RC4030_IOMMU_MEMORY_REGION, + .class_init = rc4030_iommu_memory_region_class_init, +}; + static void rc4030_register_types(void) { type_register_static(&rc4030_info); + type_register_static(&rc4030_iommu_memory_region_info); } type_init(rc4030_register_types) -DeviceState *rc4030_init(rc4030_dma **dmas, MemoryRegion **dma_mr) +DeviceState *rc4030_init(rc4030_dma **dmas, IOMMUMemoryRegion **dma_mr) { DeviceState *dev; diff --git a/hw/dma/sparc32_dma.c b/hw/dma/sparc32_dma.c index 9c6bdc6295..eb491b50ca 100644 --- a/hw/dma/sparc32_dma.c +++ b/hw/dma/sparc32_dma.c @@ -270,23 +270,28 @@ static const VMStateDescription vmstate_dma = { } }; -static int sparc32_dma_init1(SysBusDevice *sbd) +static void sparc32_dma_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - DMAState *s = SPARC32_DMA(dev); - int reg_size; + DeviceState *dev = DEVICE(obj); + DMAState *s = SPARC32_DMA(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); sysbus_init_irq(sbd, &s->irq); - reg_size = s->is_ledma ? DMA_ETH_SIZE : DMA_SIZE; - memory_region_init_io(&s->iomem, OBJECT(s), &dma_mem_ops, s, - "dma", reg_size); sysbus_init_mmio(sbd, &s->iomem); qdev_init_gpio_in(dev, dma_set_irq, 1); qdev_init_gpio_out(dev, s->gpio, 2); +} - return 0; +static void sparc32_dma_realize(DeviceState *dev, Error **errp) +{ + DMAState *s = SPARC32_DMA(dev); + int reg_size; + + reg_size = s->is_ledma ? DMA_ETH_SIZE : DMA_SIZE; + memory_region_init_io(&s->iomem, OBJECT(dev), &dma_mem_ops, s, + "dma", reg_size); } static Property sparc32_dma_properties[] = { @@ -298,12 +303,11 @@ static Property sparc32_dma_properties[] = { static void sparc32_dma_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = sparc32_dma_init1; dc->reset = dma_reset; dc->vmsd = &vmstate_dma; dc->props = sparc32_dma_properties; + dc->realize = sparc32_dma_realize; /* Reason: pointer property "iommu_opaque" */ dc->user_creatable = false; } @@ -312,6 +316,7 @@ static const TypeInfo sparc32_dma_info = { .name = TYPE_SPARC32_DMA, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(DMAState), + .instance_init = sparc32_dma_init, .class_init = sparc32_dma_class_init, }; diff --git a/hw/dma/sun4m_iommu.c b/hw/dma/sun4m_iommu.c index b3cbc54c23..335ef63cbc 100644 --- a/hw/dma/sun4m_iommu.c +++ b/hw/dma/sun4m_iommu.c @@ -349,17 +349,16 @@ static void iommu_reset(DeviceState *d) s->regs[IOMMU_MASK_ID] = IOMMU_TS_MASK; } -static int iommu_init1(SysBusDevice *dev) +static void iommu_init(Object *obj) { - IOMMUState *s = SUN4M_IOMMU(dev); + IOMMUState *s = SUN4M_IOMMU(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); sysbus_init_irq(dev, &s->irq); - memory_region_init_io(&s->iomem, OBJECT(s), &iommu_mem_ops, s, "iommu", + memory_region_init_io(&s->iomem, obj, &iommu_mem_ops, s, "iommu", IOMMU_NREGS * sizeof(uint32_t)); sysbus_init_mmio(dev, &s->iomem); - - return 0; } static Property iommu_properties[] = { @@ -370,9 +369,7 @@ static Property iommu_properties[] = { static void iommu_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = iommu_init1; dc->reset = iommu_reset; dc->vmsd = &vmstate_iommu; dc->props = iommu_properties; @@ -382,6 +379,7 @@ static const TypeInfo iommu_info = { .name = TYPE_SUN4M_IOMMU, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(IOMMUState), + .instance_init = iommu_init, .class_init = iommu_class_init, }; diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c index 6065689ad1..3987b5ff96 100644 --- a/hw/dma/xilinx_axidma.c +++ b/hw/dma/xilinx_axidma.c @@ -554,9 +554,7 @@ static void xilinx_axidma_realize(DeviceState *dev, Error **errp) return; xilinx_axidma_realize_fail: - if (!*errp) { - *errp = local_err; - } + error_propagate(errp, local_err); } static void xilinx_axidma_init(Object *obj) diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index ce5b1f0fa4..c762c7366a 100644 --- a/hw/i2c/aspeed_i2c.c +++ b/hw/i2c/aspeed_i2c.c @@ -169,12 +169,33 @@ static uint64_t aspeed_i2c_bus_read(void *opaque, hwaddr offset, } } +static void aspeed_i2c_set_state(AspeedI2CBus *bus, uint8_t state) +{ + bus->cmd &= ~(I2CD_TX_STATE_MASK << I2CD_TX_STATE_SHIFT); + bus->cmd |= (state & I2CD_TX_STATE_MASK) << I2CD_TX_STATE_SHIFT; +} + +static uint8_t aspeed_i2c_get_state(AspeedI2CBus *bus) +{ + return (bus->cmd >> I2CD_TX_STATE_SHIFT) & I2CD_TX_STATE_MASK; +} + +/* + * The state machine needs some refinement. It is only used to track + * invalid STOP commands for the moment. + */ static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) { + bus->cmd &= ~0xFFFF; bus->cmd |= value & 0xFFFF; bus->intr_status = 0; if (bus->cmd & I2CD_M_START_CMD) { + uint8_t state = aspeed_i2c_get_state(bus) & I2CD_MACTIVE ? + I2CD_MSTARTR : I2CD_MSTART; + + aspeed_i2c_set_state(bus, state); + if (i2c_start_transfer(bus->bus, extract32(bus->buf, 1, 7), extract32(bus->buf, 0, 1))) { bus->intr_status |= I2CD_INTR_TX_NAK; @@ -182,16 +203,34 @@ static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) bus->intr_status |= I2CD_INTR_TX_ACK; } - } else if (bus->cmd & I2CD_M_TX_CMD) { + /* START command is also a TX command, as the slave address is + * sent on the bus */ + bus->cmd &= ~(I2CD_M_START_CMD | I2CD_M_TX_CMD); + + /* No slave found */ + if (!i2c_bus_busy(bus->bus)) { + return; + } + aspeed_i2c_set_state(bus, I2CD_MACTIVE); + } + + if (bus->cmd & I2CD_M_TX_CMD) { + aspeed_i2c_set_state(bus, I2CD_MTXD); if (i2c_send(bus->bus, bus->buf)) { - bus->intr_status |= (I2CD_INTR_TX_NAK | I2CD_INTR_ABNORMAL); + bus->intr_status |= (I2CD_INTR_TX_NAK); i2c_end_transfer(bus->bus); } else { bus->intr_status |= I2CD_INTR_TX_ACK; } + bus->cmd &= ~I2CD_M_TX_CMD; + aspeed_i2c_set_state(bus, I2CD_MACTIVE); + } - } else if (bus->cmd & I2CD_M_RX_CMD) { - int ret = i2c_recv(bus->bus); + if (bus->cmd & (I2CD_M_RX_CMD | I2CD_M_S_RX_CMD_LAST)) { + int ret; + + aspeed_i2c_set_state(bus, I2CD_MRXD); + ret = i2c_recv(bus->bus); if (ret < 0) { qemu_log_mask(LOG_GUEST_ERROR, "%s: read failed\n", __func__); ret = 0xff; @@ -199,20 +238,25 @@ static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) bus->intr_status |= I2CD_INTR_RX_DONE; } bus->buf = (ret & I2CD_BYTE_BUF_RX_MASK) << I2CD_BYTE_BUF_RX_SHIFT; + if (bus->cmd & I2CD_M_S_RX_CMD_LAST) { + i2c_nack(bus->bus); + } + bus->cmd &= ~(I2CD_M_RX_CMD | I2CD_M_S_RX_CMD_LAST); + aspeed_i2c_set_state(bus, I2CD_MACTIVE); } - if (bus->cmd & (I2CD_M_STOP_CMD | I2CD_M_S_RX_CMD_LAST)) { - if (!i2c_bus_busy(bus->bus)) { + if (bus->cmd & I2CD_M_STOP_CMD) { + if (!(aspeed_i2c_get_state(bus) & I2CD_MACTIVE)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: abnormal stop\n", __func__); bus->intr_status |= I2CD_INTR_ABNORMAL; } else { + aspeed_i2c_set_state(bus, I2CD_MSTOP); i2c_end_transfer(bus->bus); bus->intr_status |= I2CD_INTR_NORMAL_STOP; } + bus->cmd &= ~I2CD_M_STOP_CMD; + aspeed_i2c_set_state(bus, I2CD_IDLE); } - - /* command is handled, reset it and check for interrupts */ - bus->cmd &= ~0xFFFF; - aspeed_i2c_bus_raise_interrupt(bus); } static void aspeed_i2c_bus_write(void *opaque, hwaddr offset, @@ -262,6 +306,7 @@ static void aspeed_i2c_bus_write(void *opaque, hwaddr offset, } aspeed_i2c_bus_handle_cmd(bus, value); + aspeed_i2c_bus_raise_interrupt(bus); break; default: diff --git a/hw/i2c/i2c-ddc.c b/hw/i2c/i2c-ddc.c index 66899d7233..6b92e95c73 100644 --- a/hw/i2c/i2c-ddc.c +++ b/hw/i2c/i2c-ddc.c @@ -17,6 +17,7 @@ */ #include "qemu/osdep.h" +#include "qemu-common.h" #include "qemu/log.h" #include "hw/i2c/i2c.h" #include "hw/i2c/i2c-ddc.h" diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index afcadacd2e..6b7bade183 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -57,7 +57,6 @@ #include "hw/acpi/aml-build.h" -#include "qapi/qmp/qint.h" #include "qom/qom-qobject.h" #include "hw/i386/amd_iommu.h" #include "hw/i386/intel_iommu.h" @@ -137,9 +136,9 @@ static void acpi_get_pm_info(AcpiPmInfo *pm) obj = piix; pm->cpu_hp_io_base = PIIX4_CPU_HOTPLUG_IO_BASE; pm->pcihp_io_base = - object_property_get_int(obj, ACPI_PCIHP_IO_BASE_PROP, NULL); + object_property_get_uint(obj, ACPI_PCIHP_IO_BASE_PROP, NULL); pm->pcihp_io_len = - object_property_get_int(obj, ACPI_PCIHP_IO_LEN_PROP, NULL); + object_property_get_uint(obj, ACPI_PCIHP_IO_LEN_PROP, NULL); } if (lpc) { obj = lpc; @@ -150,41 +149,42 @@ static void acpi_get_pm_info(AcpiPmInfo *pm) /* Fill in optional s3/s4 related properties */ o = object_property_get_qobject(obj, ACPI_PM_PROP_S3_DISABLED, NULL); if (o) { - pm->s3_disabled = qint_get_int(qobject_to_qint(o)); + pm->s3_disabled = qnum_get_uint(qobject_to_qnum(o)); } else { pm->s3_disabled = false; } qobject_decref(o); o = object_property_get_qobject(obj, ACPI_PM_PROP_S4_DISABLED, NULL); if (o) { - pm->s4_disabled = qint_get_int(qobject_to_qint(o)); + pm->s4_disabled = qnum_get_uint(qobject_to_qnum(o)); } else { pm->s4_disabled = false; } qobject_decref(o); o = object_property_get_qobject(obj, ACPI_PM_PROP_S4_VAL, NULL); if (o) { - pm->s4_val = qint_get_int(qobject_to_qint(o)); + pm->s4_val = qnum_get_uint(qobject_to_qnum(o)); } else { pm->s4_val = false; } qobject_decref(o); /* Fill in mandatory properties */ - pm->sci_int = object_property_get_int(obj, ACPI_PM_PROP_SCI_INT, NULL); - - pm->acpi_enable_cmd = object_property_get_int(obj, - ACPI_PM_PROP_ACPI_ENABLE_CMD, - NULL); - pm->acpi_disable_cmd = object_property_get_int(obj, - ACPI_PM_PROP_ACPI_DISABLE_CMD, - NULL); - pm->io_base = object_property_get_int(obj, ACPI_PM_PROP_PM_IO_BASE, - NULL); - pm->gpe0_blk = object_property_get_int(obj, ACPI_PM_PROP_GPE0_BLK, + pm->sci_int = object_property_get_uint(obj, ACPI_PM_PROP_SCI_INT, NULL); + + pm->acpi_enable_cmd = object_property_get_uint(obj, + ACPI_PM_PROP_ACPI_ENABLE_CMD, + NULL); + pm->acpi_disable_cmd = + object_property_get_uint(obj, + ACPI_PM_PROP_ACPI_DISABLE_CMD, + NULL); + pm->io_base = object_property_get_uint(obj, ACPI_PM_PROP_PM_IO_BASE, NULL); - pm->gpe0_blk_len = object_property_get_int(obj, ACPI_PM_PROP_GPE0_BLK_LEN, - NULL); + pm->gpe0_blk = object_property_get_uint(obj, ACPI_PM_PROP_GPE0_BLK, + NULL); + pm->gpe0_blk_len = object_property_get_uint(obj, ACPI_PM_PROP_GPE0_BLK_LEN, + NULL); pm->pcihp_bridge_en = object_property_get_bool(obj, "acpi-pci-hotplug-with-bridge-support", NULL); @@ -237,19 +237,19 @@ static void acpi_get_pci_holes(Range *hole, Range *hole64) g_assert(pci_host); range_set_bounds1(hole, - object_property_get_int(pci_host, - PCI_HOST_PROP_PCI_HOLE_START, - NULL), - object_property_get_int(pci_host, - PCI_HOST_PROP_PCI_HOLE_END, - NULL)); + object_property_get_uint(pci_host, + PCI_HOST_PROP_PCI_HOLE_START, + NULL), + object_property_get_uint(pci_host, + PCI_HOST_PROP_PCI_HOLE_END, + NULL)); range_set_bounds1(hole64, - object_property_get_int(pci_host, - PCI_HOST_PROP_PCI_HOLE64_START, - NULL), - object_property_get_int(pci_host, - PCI_HOST_PROP_PCI_HOLE64_END, - NULL)); + object_property_get_uint(pci_host, + PCI_HOST_PROP_PCI_HOLE64_START, + NULL), + object_property_get_uint(pci_host, + PCI_HOST_PROP_PCI_HOLE64_END, + NULL)); } #define ACPI_PORT_SMI_CMD 0x00b2 /* TODO: this is APM_CNT_IOPORT */ @@ -529,7 +529,7 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus, bsel = object_property_get_qobject(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, NULL); if (bsel) { - int64_t bsel_val = qint_get_int(qobject_to_qint(bsel)); + uint64_t bsel_val = qnum_get_uint(qobject_to_qnum(bsel)); aml_append(parent_scope, aml_name_decl("BSEL", aml_int(bsel_val))); notify_method = aml_method("DVNT", 2, AML_NOTSERIALIZED); @@ -639,7 +639,8 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus, /* If bus supports hotplug select it and notify about local events */ if (bsel) { - int64_t bsel_val = qint_get_int(qobject_to_qint(bsel)); + uint64_t bsel_val = qnum_get_uint(qobject_to_qnum(bsel)); + aml_append(method, aml_store(aml_int(bsel_val), aml_name("BNUM"))); aml_append(method, aml_call2("DVNT", aml_name("PCIU"), aml_int(1) /* Device Check */) @@ -1912,16 +1913,6 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, build_piix4_pci0_int(dsdt); } else { sb_scope = aml_scope("_SB"); - aml_append(sb_scope, - aml_operation_region("PCST", AML_SYSTEM_IO, aml_int(0xae00), 0x0c)); - aml_append(sb_scope, - aml_operation_region("PCSB", AML_SYSTEM_IO, aml_int(0xae0c), 0x01)); - field = aml_field("PCSB", AML_ANY_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS); - aml_append(field, aml_named_field("PCIB", 8)); - aml_append(sb_scope, field); - aml_append(dsdt, sb_scope); - - sb_scope = aml_scope("_SB"); dev = aml_device("PCI0"); aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A08"))); aml_append(dev, aml_name_decl("_CID", aml_eisaid("PNP0A03"))); @@ -2335,8 +2326,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) srat->reserved1 = cpu_to_le32(1); for (i = 0; i < apic_ids->len; i++) { - int node_id = apic_ids->cpus[i].props.has_node_id ? - apic_ids->cpus[i].props.node_id : 0; + int node_id = apic_ids->cpus[i].props.node_id; uint32_t apic_id = apic_ids->cpus[i].arch_id; if (apic_id < 255) { @@ -2404,14 +2394,17 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) } /* - * Entry is required for Windows to enable memory hotplug in OS. + * Entry is required for Windows to enable memory hotplug in OS + * and for Linux to enable SWIOTLB when booted with less than + * 4G of RAM. Windows works better if the entry sets proximity + * to the highest NUMA node in the machine. * Memory devices may override proximity set by this entry, * providing _PXM method if necessary. */ if (hotplugabble_address_space_size) { numamem = acpi_data_push(table_data, sizeof *numamem); build_srat_memory(numamem, pcms->hotplug_memory.base, - hotplugabble_address_space_size, 0, + hotplugabble_address_space_size, pcms->numa_nodes - 1, MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED); } @@ -2612,12 +2605,12 @@ static bool acpi_get_mcfg(AcpiMcfgInfo *mcfg) if (!o) { return false; } - mcfg->mcfg_base = qint_get_int(qobject_to_qint(o)); + mcfg->mcfg_base = qnum_get_uint(qobject_to_qnum(o)); qobject_decref(o); o = object_property_get_qobject(pci_host, PCIE_HOST_MCFG_SIZE, NULL); assert(o); - mcfg->mcfg_size = qint_get_int(qobject_to_qint(o)); + mcfg->mcfg_size = qnum_get_uint(qobject_to_qnum(o)); qobject_decref(o); return true; } @@ -2773,17 +2766,17 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) ACPI_BUILD_ALIGN_SIZE); if (tables_blob->len > legacy_table_size) { /* Should happen only with PCI bridges and -M pc-i440fx-2.0. */ - error_report("Warning: migration may not work."); + warn_report("migration may not work."); } g_array_set_size(tables_blob, legacy_table_size); } else { /* Make sure we have a buffer in case we need to resize the tables. */ if (tables_blob->len > ACPI_BUILD_TABLE_SIZE / 2) { /* As of QEMU 2.1, this fires with 160 VCPUs and 255 memory slots. */ - error_report("Warning: ACPI tables are larger than 64k."); - error_report("Warning: migration may not work."); - error_report("Warning: please remove CPUs, NUMA nodes, " - "memory slots or PCI bridges."); + warn_report("ACPI tables are larger than 64k."); + warn_report("migration may not work."); + warn_report("please remove CPUs, NUMA nodes, " + "memory slots or PCI bridges."); } acpi_align_size(tables_blob, ACPI_BUILD_TABLE_SIZE); } diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 329058dac8..334938a280 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -52,7 +52,7 @@ struct AMDVIAddressSpace { uint8_t bus_num; /* bus number */ uint8_t devfn; /* device function */ AMDVIState *iommu_state; /* AMDVI - one per machine */ - MemoryRegion iommu; /* Device's address translation region */ + IOMMUMemoryRegion iommu; /* Device's address translation region */ MemoryRegion iommu_ir; /* Device's interrupt remapping region */ AddressSpace as; /* device's corresponding address space */ }; @@ -987,8 +987,8 @@ static inline bool amdvi_is_interrupt_addr(hwaddr addr) return addr >= AMDVI_INT_ADDR_FIRST && addr <= AMDVI_INT_ADDR_LAST; } -static IOMMUTLBEntry amdvi_translate(MemoryRegion *iommu, hwaddr addr, - bool is_write) +static IOMMUTLBEntry amdvi_translate(IOMMUMemoryRegion *iommu, hwaddr addr, + IOMMUAccessFlags flag) { AMDVIAddressSpace *as = container_of(iommu, AMDVIAddressSpace, iommu); AMDVIState *s = as->iommu_state; @@ -1017,7 +1017,7 @@ static IOMMUTLBEntry amdvi_translate(MemoryRegion *iommu, hwaddr addr, return ret; } - amdvi_do_translate(as, addr, is_write, &ret); + amdvi_do_translate(as, addr, flag & IOMMU_WO, &ret); trace_amdvi_translation_result(as->bus_num, PCI_SLOT(as->devfn), PCI_FUNC(as->devfn), addr, ret.translated_addr); return ret; @@ -1044,9 +1044,13 @@ static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) iommu_as[devfn]->devfn = (uint8_t)devfn; iommu_as[devfn]->iommu_state = s; - memory_region_init_iommu(&iommu_as[devfn]->iommu, OBJECT(s), - &s->iommu_ops, "amd-iommu", UINT64_MAX); - address_space_init(&iommu_as[devfn]->as, &iommu_as[devfn]->iommu, + memory_region_init_iommu(&iommu_as[devfn]->iommu, + sizeof(iommu_as[devfn]->iommu), + TYPE_AMD_IOMMU_MEMORY_REGION, + OBJECT(s), + "amd-iommu", UINT64_MAX); + address_space_init(&iommu_as[devfn]->as, + MEMORY_REGION(&iommu_as[devfn]->iommu), "amd-iommu"); } return &iommu_as[devfn]->as; @@ -1067,7 +1071,7 @@ static const MemoryRegionOps mmio_mem_ops = { } }; -static void amdvi_iommu_notify_flag_changed(MemoryRegion *iommu, +static void amdvi_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu, IOMMUNotifierFlag old, IOMMUNotifierFlag new) { @@ -1085,8 +1089,6 @@ static void amdvi_init(AMDVIState *s) { amdvi_iotlb_reset(s); - s->iommu_ops.translate = amdvi_translate; - s->iommu_ops.notify_flag_changed = amdvi_iommu_notify_flag_changed; s->devtab_len = 0; s->cmdbuf_len = 0; s->cmdbuf_head = 0; @@ -1158,13 +1160,23 @@ static void amdvi_realize(DeviceState *dev, Error **err) x86_iommu->type = TYPE_AMD; qdev_set_parent_bus(DEVICE(&s->pci), &bus->qbus); object_property_set_bool(OBJECT(&s->pci), true, "realized", err); - s->capab_offset = pci_add_capability(&s->pci.dev, AMDVI_CAPAB_ID_SEC, 0, - AMDVI_CAPAB_SIZE); - assert(s->capab_offset > 0); - ret = pci_add_capability(&s->pci.dev, PCI_CAP_ID_MSI, 0, AMDVI_CAPAB_REG_SIZE); - assert(ret > 0); - ret = pci_add_capability(&s->pci.dev, PCI_CAP_ID_HT, 0, AMDVI_CAPAB_REG_SIZE); - assert(ret > 0); + ret = pci_add_capability(&s->pci.dev, AMDVI_CAPAB_ID_SEC, 0, + AMDVI_CAPAB_SIZE, err); + if (ret < 0) { + return; + } + s->capab_offset = ret; + + ret = pci_add_capability(&s->pci.dev, PCI_CAP_ID_MSI, 0, + AMDVI_CAPAB_REG_SIZE, err); + if (ret < 0) { + return; + } + ret = pci_add_capability(&s->pci.dev, PCI_CAP_ID_HT, 0, + AMDVI_CAPAB_REG_SIZE, err); + if (ret < 0) { + return; + } /* set up MMIO */ memory_region_init_io(&s->mmio, OBJECT(s), &mmio_mem_ops, s, "amdvi-mmio", @@ -1217,10 +1229,25 @@ static const TypeInfo amdviPCI = { .instance_size = sizeof(AMDVIPCIState), }; +static void amdvi_iommu_memory_region_class_init(ObjectClass *klass, void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = amdvi_translate; + imrc->notify_flag_changed = amdvi_iommu_notify_flag_changed; +} + +static const TypeInfo amdvi_iommu_memory_region_info = { + .parent = TYPE_IOMMU_MEMORY_REGION, + .name = TYPE_AMD_IOMMU_MEMORY_REGION, + .class_init = amdvi_iommu_memory_region_class_init, +}; + static void amdviPCI_register_types(void) { type_register_static(&amdviPCI); type_register_static(&amdvi); + type_register_static(&amdvi_iommu_memory_region_info); } type_init(amdviPCI_register_types); diff --git a/hw/i386/amd_iommu.h b/hw/i386/amd_iommu.h index 0d3dc6a9f2..d370ae3549 100644 --- a/hw/i386/amd_iommu.h +++ b/hw/i386/amd_iommu.h @@ -220,6 +220,8 @@ #define TYPE_AMD_IOMMU_PCI "AMDVI-PCI" +#define TYPE_AMD_IOMMU_MEMORY_REGION "amd-iommu-iommu-memory-region" + typedef struct AMDVIAddressSpace AMDVIAddressSpace; /* functions to steal PCI config space */ @@ -276,9 +278,6 @@ typedef struct AMDVIState { uint8_t romask[AMDVI_MMIO_SIZE]; /* MMIO read/only mask */ bool mmio_enabled; - /* IOMMU function */ - MemoryRegionIOMMUOps iommu_ops; - /* for each served device */ AMDVIAddressSpace **address_spaces[PCI_BUS_MAX]; diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 9ba2162cd9..e398746b4b 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -37,24 +37,6 @@ #include "kvm_i386.h" #include "trace.h" -/*#define DEBUG_INTEL_IOMMU*/ -#ifdef DEBUG_INTEL_IOMMU -enum { - DEBUG_GENERAL, DEBUG_CSR, DEBUG_INV, DEBUG_MMU, DEBUG_FLOG, - DEBUG_CACHE, DEBUG_IR, -}; -#define VTD_DBGBIT(x) (1 << DEBUG_##x) -static int vtd_dbgflags = VTD_DBGBIT(GENERAL) | VTD_DBGBIT(CSR); - -#define VTD_DPRINTF(what, fmt, ...) do { \ - if (vtd_dbgflags & VTD_DBGBIT(what)) { \ - fprintf(stderr, "(vtd)%s: " fmt "\n", __func__, \ - ## __VA_ARGS__); } \ - } while (0) -#else -#define VTD_DPRINTF(what, fmt, ...) do {} while (0) -#endif - static void vtd_define_quad(IntelIOMMUState *s, hwaddr addr, uint64_t val, uint64_t wmask, uint64_t w1cmask) { @@ -199,9 +181,10 @@ static void vtd_reset_context_cache(IntelIOMMUState *s) GHashTableIter bus_it; uint32_t devfn_it; + trace_vtd_context_cache_reset(); + g_hash_table_iter_init(&bus_it, s->vtd_as_by_busptr); - VTD_DPRINTF(CACHE, "global context_cache_gen=1"); while (g_hash_table_iter_next (&bus_it, NULL, (void**)&vtd_bus)) { for (devfn_it = 0; devfn_it < X86_IOMMU_PCI_DEVFN_MAX; ++devfn_it) { vtd_as = vtd_bus->dev_as[devfn_it]; @@ -291,8 +274,8 @@ static void vtd_generate_interrupt(IntelIOMMUState *s, hwaddr mesg_addr_reg, msi.address = vtd_get_long_raw(s, mesg_addr_reg); msi.data = vtd_get_long_raw(s, mesg_data_reg); - VTD_DPRINTF(FLOG, "msi: addr 0x%"PRIx64 " data 0x%"PRIx32, - msi.address, msi.data); + trace_vtd_irq_generate(msi.address, msi.data); + apic_get_class()->send_msi(&msi); } @@ -304,14 +287,14 @@ static void vtd_generate_fault_event(IntelIOMMUState *s, uint32_t pre_fsts) { if (pre_fsts & VTD_FSTS_PPF || pre_fsts & VTD_FSTS_PFO || pre_fsts & VTD_FSTS_IQE) { - VTD_DPRINTF(FLOG, "there are previous interrupt conditions " - "to be serviced by software, fault event is not generated " - "(FSTS_REG 0x%"PRIx32 ")", pre_fsts); + trace_vtd_err("There are previous interrupt conditions " + "to be serviced by software, fault event " + "is not generated."); return; } vtd_set_clear_mask_long(s, DMAR_FECTL_REG, 0, VTD_FECTL_IP); if (vtd_get_long_raw(s, DMAR_FECTL_REG) & VTD_FECTL_IM) { - VTD_DPRINTF(FLOG, "Interrupt Mask set, fault event is not generated"); + trace_vtd_err("Interrupt Mask set, irq is not generated."); } else { vtd_generate_interrupt(s, DMAR_FEADDR_REG, DMAR_FEDATA_REG); vtd_set_clear_mask_long(s, DMAR_FECTL_REG, VTD_FECTL_IP, 0); @@ -348,7 +331,7 @@ static void vtd_update_fsts_ppf(IntelIOMMUState *s) } } vtd_set_clear_mask_long(s, DMAR_FSTS_REG, VTD_FSTS_PPF, ppf_mask); - VTD_DPRINTF(FLOG, "set PPF of FSTS_REG to %d", ppf_mask ? 1 : 0); + trace_vtd_fsts_ppf(!!ppf_mask); } static void vtd_set_frcd_and_update_ppf(IntelIOMMUState *s, uint16_t index) @@ -380,8 +363,8 @@ static void vtd_record_frcd(IntelIOMMUState *s, uint16_t index, } vtd_set_quad_raw(s, frcd_reg_addr, lo); vtd_set_quad_raw(s, frcd_reg_addr + 8, hi); - VTD_DPRINTF(FLOG, "record to FRCD_REG #%"PRIu16 ": hi 0x%"PRIx64 - ", lo 0x%"PRIx64, index, hi, lo); + + trace_vtd_frr_new(index, hi, lo); } /* Try to collapse multiple pending faults from the same requester */ @@ -393,7 +376,6 @@ static bool vtd_try_collapse_fault(IntelIOMMUState *s, uint16_t source_id) for (i = 0; i < DMAR_FRCD_REG_NR; i++) { frcd_reg = vtd_get_quad_raw(s, addr); - VTD_DPRINTF(FLOG, "frcd_reg #%d 0x%"PRIx64, i, frcd_reg); if ((frcd_reg & VTD_FRCD_F) && ((frcd_reg & VTD_FRCD_SID_MASK) == source_id)) { return true; @@ -416,21 +398,24 @@ static void vtd_report_dmar_fault(IntelIOMMUState *s, uint16_t source_id, /* This is not a normal fault reason case. Drop it. */ return; } - VTD_DPRINTF(FLOG, "sid 0x%"PRIx16 ", fault %d, addr 0x%"PRIx64 - ", is_write %d", source_id, fault, addr, is_write); + + trace_vtd_dmar_fault(source_id, fault, addr, is_write); + if (fsts_reg & VTD_FSTS_PFO) { - VTD_DPRINTF(FLOG, "new fault is not recorded due to " - "Primary Fault Overflow"); + trace_vtd_err("New fault is not recorded due to " + "Primary Fault Overflow."); return; } + if (vtd_try_collapse_fault(s, source_id)) { - VTD_DPRINTF(FLOG, "new fault is not recorded due to " - "compression of faults"); + trace_vtd_err("New fault is not recorded due to " + "compression of faults."); return; } + if (vtd_is_frcd_set(s, s->next_frcd_reg)) { - VTD_DPRINTF(FLOG, "Primary Fault Overflow and " - "new fault is not recorded, set PFO field"); + trace_vtd_err("Next Fault Recording Reg is used, " + "new fault is not recorded, set PFO field."); vtd_set_clear_mask_long(s, DMAR_FSTS_REG, 0, VTD_FSTS_PFO); return; } @@ -438,8 +423,8 @@ static void vtd_report_dmar_fault(IntelIOMMUState *s, uint16_t source_id, vtd_record_frcd(s, s->next_frcd_reg, source_id, addr, fault, is_write); if (fsts_reg & VTD_FSTS_PPF) { - VTD_DPRINTF(FLOG, "there are pending faults already, " - "fault event is not generated"); + trace_vtd_err("There are pending faults already, " + "fault event is not generated."); vtd_set_frcd_and_update_ppf(s, s->next_frcd_reg); s->next_frcd_reg++; if (s->next_frcd_reg == DMAR_FRCD_REG_NR) { @@ -512,7 +497,7 @@ static int vtd_get_root_entry(IntelIOMMUState *s, uint8_t index, return 0; } -static inline bool vtd_context_entry_present(VTDContextEntry *context) +static inline bool vtd_ce_present(VTDContextEntry *context) { return context->lo & VTD_CONTEXT_ENTRY_P; } @@ -533,7 +518,7 @@ static int vtd_get_context_entry_from_root(VTDRootEntry *root, uint8_t index, return 0; } -static inline dma_addr_t vtd_get_slpt_base_from_context(VTDContextEntry *ce) +static inline dma_addr_t vtd_ce_get_slpt_base(VTDContextEntry *ce) { return ce->lo & VTD_CONTEXT_ENTRY_SLPTPTR; } @@ -585,19 +570,49 @@ static inline bool vtd_is_level_supported(IntelIOMMUState *s, uint32_t level) /* Get the page-table level that hardware should use for the second-level * page-table walk from the Address Width field of context-entry. */ -static inline uint32_t vtd_get_level_from_context_entry(VTDContextEntry *ce) +static inline uint32_t vtd_ce_get_level(VTDContextEntry *ce) { return 2 + (ce->hi & VTD_CONTEXT_ENTRY_AW); } -static inline uint32_t vtd_get_agaw_from_context_entry(VTDContextEntry *ce) +static inline uint32_t vtd_ce_get_agaw(VTDContextEntry *ce) { return 30 + (ce->hi & VTD_CONTEXT_ENTRY_AW) * 9; } +static inline uint32_t vtd_ce_get_type(VTDContextEntry *ce) +{ + return ce->lo & VTD_CONTEXT_ENTRY_TT; +} + +/* Return true if check passed, otherwise false */ +static inline bool vtd_ce_type_check(X86IOMMUState *x86_iommu, + VTDContextEntry *ce) +{ + switch (vtd_ce_get_type(ce)) { + case VTD_CONTEXT_TT_MULTI_LEVEL: + /* Always supported */ + break; + case VTD_CONTEXT_TT_DEV_IOTLB: + if (!x86_iommu->dt_supported) { + return false; + } + break; + case VTD_CONTEXT_TT_PASS_THROUGH: + if (!x86_iommu->pt_supported) { + return false; + } + break; + default: + /* Unknwon type */ + return false; + } + return true; +} + static inline uint64_t vtd_iova_limit(VTDContextEntry *ce) { - uint32_t ce_agaw = vtd_get_agaw_from_context_entry(ce); + uint32_t ce_agaw = vtd_ce_get_agaw(ce); return 1ULL << MIN(ce_agaw, VTD_MGAW); } @@ -635,6 +650,29 @@ static bool vtd_slpte_nonzero_rsvd(uint64_t slpte, uint32_t level) } } +/* Find the VTD address space associated with a given bus number */ +static VTDBus *vtd_find_as_from_bus_num(IntelIOMMUState *s, uint8_t bus_num) +{ + VTDBus *vtd_bus = s->vtd_as_by_bus_num[bus_num]; + if (!vtd_bus) { + /* + * Iterate over the registered buses to find the one which + * currently hold this bus number, and update the bus_num + * lookup table: + */ + GHashTableIter iter; + + g_hash_table_iter_init(&iter, s->vtd_as_by_busptr); + while (g_hash_table_iter_next(&iter, NULL, (void **)&vtd_bus)) { + if (pci_bus_num(vtd_bus->bus) == bus_num) { + s->vtd_as_by_bus_num[bus_num] = vtd_bus; + return vtd_bus; + } + } + } + return vtd_bus; +} + /* Given the @iova, get relevant @slptep. @slpte_level will be the last level * of the translation, can be used for deciding the size of large page. */ @@ -642,14 +680,14 @@ static int vtd_iova_to_slpte(VTDContextEntry *ce, uint64_t iova, bool is_write, uint64_t *slptep, uint32_t *slpte_level, bool *reads, bool *writes) { - dma_addr_t addr = vtd_get_slpt_base_from_context(ce); - uint32_t level = vtd_get_level_from_context_entry(ce); + dma_addr_t addr = vtd_ce_get_slpt_base(ce); + uint32_t level = vtd_ce_get_level(ce); uint32_t offset; uint64_t slpte; uint64_t access_right_check; if (!vtd_iova_range_check(iova, ce)) { - VTD_DPRINTF(GENERAL, "error: iova 0x%"PRIx64 " exceeds limits", iova); + trace_vtd_err_dmar_iova_overflow(iova); return -VTD_FR_ADDR_BEYOND_MGAW; } @@ -661,10 +699,8 @@ static int vtd_iova_to_slpte(VTDContextEntry *ce, uint64_t iova, bool is_write, slpte = vtd_get_slpte(addr, offset); if (slpte == (uint64_t)-1) { - VTD_DPRINTF(GENERAL, "error: fail to access second-level paging " - "entry at level %"PRIu32 " for iova 0x%"PRIx64, - level, iova); - if (level == vtd_get_level_from_context_entry(ce)) { + trace_vtd_err_dmar_slpte_read_error(iova, level); + if (level == vtd_ce_get_level(ce)) { /* Invalid programming of context-entry */ return -VTD_FR_CONTEXT_ENTRY_INV; } else { @@ -674,15 +710,11 @@ static int vtd_iova_to_slpte(VTDContextEntry *ce, uint64_t iova, bool is_write, *reads = (*reads) && (slpte & VTD_SL_R); *writes = (*writes) && (slpte & VTD_SL_W); if (!(slpte & access_right_check)) { - VTD_DPRINTF(GENERAL, "error: lack of %s permission for " - "iova 0x%"PRIx64 " slpte 0x%"PRIx64, - (is_write ? "write" : "read"), iova, slpte); + trace_vtd_err_dmar_slpte_perm_error(iova, level, slpte, is_write); return is_write ? -VTD_FR_WRITE : -VTD_FR_READ; } if (vtd_slpte_nonzero_rsvd(slpte, level)) { - VTD_DPRINTF(GENERAL, "error: non-zero reserved field in second " - "level paging entry level %"PRIu32 " slpte 0x%"PRIx64, - level, slpte); + trace_vtd_err_dmar_slpte_resv_error(iova, level, slpte); return -VTD_FR_PAGING_ENTRY_RSVD; } @@ -809,8 +841,8 @@ static int vtd_page_walk(VTDContextEntry *ce, uint64_t start, uint64_t end, vtd_page_walk_hook hook_fn, void *private, bool notify_unmap) { - dma_addr_t addr = vtd_get_slpt_base_from_context(ce); - uint32_t level = vtd_get_level_from_context_entry(ce); + dma_addr_t addr = vtd_ce_get_slpt_base(ce); + uint32_t level = vtd_ce_get_level(ce); if (!vtd_iova_range_check(start, ce)) { return -VTD_FR_ADDR_BEYOND_MGAW; @@ -831,6 +863,7 @@ static int vtd_dev_to_context_entry(IntelIOMMUState *s, uint8_t bus_num, { VTDRootEntry re; int ret_fr; + X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s); ret_fr = vtd_get_root_entry(s, bus_num, &re); if (ret_fr) { @@ -841,7 +874,9 @@ static int vtd_dev_to_context_entry(IntelIOMMUState *s, uint8_t bus_num, /* Not error - it's okay we don't have root entry. */ trace_vtd_re_not_present(bus_num); return -VTD_FR_ROOT_ENTRY_P; - } else if (re.rsvd || (re.val & VTD_ROOT_ENTRY_RSVD)) { + } + + if (re.rsvd || (re.val & VTD_ROOT_ENTRY_RSVD)) { trace_vtd_re_invalid(re.rsvd, re.val); return -VTD_FR_ROOT_ENTRY_RSVD; } @@ -851,31 +886,116 @@ static int vtd_dev_to_context_entry(IntelIOMMUState *s, uint8_t bus_num, return ret_fr; } - if (!vtd_context_entry_present(ce)) { + if (!vtd_ce_present(ce)) { /* Not error - it's okay we don't have context entry. */ trace_vtd_ce_not_present(bus_num, devfn); return -VTD_FR_CONTEXT_ENTRY_P; - } else if ((ce->hi & VTD_CONTEXT_ENTRY_RSVD_HI) || - (ce->lo & VTD_CONTEXT_ENTRY_RSVD_LO)) { + } + + if ((ce->hi & VTD_CONTEXT_ENTRY_RSVD_HI) || + (ce->lo & VTD_CONTEXT_ENTRY_RSVD_LO)) { trace_vtd_ce_invalid(ce->hi, ce->lo); return -VTD_FR_CONTEXT_ENTRY_RSVD; } + /* Check if the programming of context-entry is valid */ - if (!vtd_is_level_supported(s, vtd_get_level_from_context_entry(ce))) { + if (!vtd_is_level_supported(s, vtd_ce_get_level(ce))) { + trace_vtd_ce_invalid(ce->hi, ce->lo); + return -VTD_FR_CONTEXT_ENTRY_INV; + } + + /* Do translation type check */ + if (!vtd_ce_type_check(x86_iommu, ce)) { trace_vtd_ce_invalid(ce->hi, ce->lo); return -VTD_FR_CONTEXT_ENTRY_INV; + } + + return 0; +} + +/* + * Fetch translation type for specific device. Returns <0 if error + * happens, otherwise return the shifted type to check against + * VTD_CONTEXT_TT_*. + */ +static int vtd_dev_get_trans_type(VTDAddressSpace *as) +{ + IntelIOMMUState *s; + VTDContextEntry ce; + int ret; + + s = as->iommu_state; + + ret = vtd_dev_to_context_entry(s, pci_bus_num(as->bus), + as->devfn, &ce); + if (ret) { + return ret; + } + + return vtd_ce_get_type(&ce); +} + +static bool vtd_dev_pt_enabled(VTDAddressSpace *as) +{ + int ret; + + assert(as); + + ret = vtd_dev_get_trans_type(as); + if (ret < 0) { + /* + * Possibly failed to parse the context entry for some reason + * (e.g., during init, or any guest configuration errors on + * context entries). We should assume PT not enabled for + * safety. + */ + return false; + } + + return ret == VTD_CONTEXT_TT_PASS_THROUGH; +} + +/* Return whether the device is using IOMMU translation. */ +static bool vtd_switch_address_space(VTDAddressSpace *as) +{ + bool use_iommu; + + assert(as); + + use_iommu = as->iommu_state->dmar_enabled & !vtd_dev_pt_enabled(as); + + trace_vtd_switch_address_space(pci_bus_num(as->bus), + VTD_PCI_SLOT(as->devfn), + VTD_PCI_FUNC(as->devfn), + use_iommu); + + /* Turn off first then on the other */ + if (use_iommu) { + memory_region_set_enabled(&as->sys_alias, false); + memory_region_set_enabled(MEMORY_REGION(&as->iommu), true); } else { - switch (ce->lo & VTD_CONTEXT_ENTRY_TT) { - case VTD_CONTEXT_TT_MULTI_LEVEL: - /* fall through */ - case VTD_CONTEXT_TT_DEV_IOTLB: - break; - default: - trace_vtd_ce_invalid(ce->hi, ce->lo); - return -VTD_FR_CONTEXT_ENTRY_INV; + memory_region_set_enabled(MEMORY_REGION(&as->iommu), false); + memory_region_set_enabled(&as->sys_alias, true); + } + + return use_iommu; +} + +static void vtd_switch_address_space_all(IntelIOMMUState *s) +{ + GHashTableIter iter; + VTDBus *vtd_bus; + int i; + + g_hash_table_iter_init(&iter, s->vtd_as_by_busptr); + while (g_hash_table_iter_next(&iter, NULL, (void **)&vtd_bus)) { + for (i = 0; i < X86_IOMMU_PCI_DEVFN_MAX; i++) { + if (!vtd_bus->dev_as[i]) { + continue; + } + vtd_switch_address_space(vtd_bus->dev_as[i]); } } - return 0; } static inline uint16_t vtd_make_source_id(uint8_t bus_num, uint8_t devfn) @@ -915,6 +1035,31 @@ static inline bool vtd_is_interrupt_addr(hwaddr addr) return VTD_INTERRUPT_ADDR_FIRST <= addr && addr <= VTD_INTERRUPT_ADDR_LAST; } +static void vtd_pt_enable_fast_path(IntelIOMMUState *s, uint16_t source_id) +{ + VTDBus *vtd_bus; + VTDAddressSpace *vtd_as; + bool success = false; + + vtd_bus = vtd_find_as_from_bus_num(s, VTD_SID_TO_BUS(source_id)); + if (!vtd_bus) { + goto out; + } + + vtd_as = vtd_bus->dev_as[VTD_SID_TO_DEVFN(source_id)]; + if (!vtd_as) { + goto out; + } + + if (vtd_switch_address_space(vtd_as) == false) { + /* We switched off IOMMU region successfully. */ + success = true; + } + +out: + trace_vtd_pt_enable_fast_path(source_id, success); +} + /* Map dev to context-entry then do a paging-structures walk to do a iommu * translation. * @@ -924,8 +1069,10 @@ static inline bool vtd_is_interrupt_addr(hwaddr addr) * @devfn: The devfn, which is the combined of device and function number * @is_write: The access is a write operation * @entry: IOMMUTLBEntry that contain the addr to be translated and result + * + * Returns true if translation is successful, otherwise false. */ -static void vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, +static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, uint8_t devfn, hwaddr addr, bool is_write, IOMMUTLBEntry *entry) { @@ -959,6 +1106,7 @@ static void vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, page_mask = iotlb_entry->mask; goto out; } + /* Try to fetch context-entry from cache first */ if (cc_entry->context_cache_gen == s->context_cache_gen) { trace_vtd_iotlb_cc_hit(bus_num, devfn, cc_entry->context_entry.hi, @@ -976,7 +1124,7 @@ static void vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, } else { vtd_report_dmar_fault(s, source_id, addr, ret_fr, is_write); } - return; + goto error; } /* Update context-cache */ trace_vtd_iotlb_cc_update(bus_num, devfn, ce.hi, ce.lo, @@ -986,6 +1134,31 @@ static void vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, cc_entry->context_cache_gen = s->context_cache_gen; } + /* + * We don't need to translate for pass-through context entries. + * Also, let's ignore IOTLB caching as well for PT devices. + */ + if (vtd_ce_get_type(&ce) == VTD_CONTEXT_TT_PASS_THROUGH) { + entry->iova = addr & VTD_PAGE_MASK; + entry->translated_addr = entry->iova; + entry->addr_mask = VTD_PAGE_MASK; + entry->perm = IOMMU_RW; + trace_vtd_translate_pt(source_id, entry->iova); + + /* + * When this happens, it means firstly caching-mode is not + * enabled, and this is the first passthrough translation for + * the device. Let's enable the fast path for passthrough. + * + * When passthrough is disabled again for the device, we can + * capture it via the context entry invalidation, then the + * IOMMU region can be swapped back. + */ + vtd_pt_enable_fast_path(s, source_id); + + return true; + } + ret_fr = vtd_iova_to_slpte(&ce, addr, is_write, &slpte, &level, &reads, &writes); if (ret_fr) { @@ -995,7 +1168,7 @@ static void vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, } else { vtd_report_dmar_fault(s, source_id, addr, ret_fr, is_write); } - return; + goto error; } page_mask = vtd_slpt_level_page_mask(level); @@ -1005,7 +1178,15 @@ out: entry->iova = addr & page_mask; entry->translated_addr = vtd_get_slpte_addr(slpte) & page_mask; entry->addr_mask = ~page_mask; - entry->perm = (writes ? 2 : 0) + (reads ? 1 : 0); + entry->perm = IOMMU_ACCESS_FLAG(reads, writes); + return true; + +error: + entry->iova = 0; + entry->translated_addr = 0; + entry->addr_mask = 0; + entry->perm = IOMMU_NONE; + return false; } static void vtd_root_table_setup(IntelIOMMUState *s) @@ -1014,8 +1195,7 @@ static void vtd_root_table_setup(IntelIOMMUState *s) s->root_extended = s->root & VTD_RTADDR_RTT; s->root &= VTD_RTADDR_ADDR_MASK; - VTD_DPRINTF(CSR, "root_table addr 0x%"PRIx64 " %s", s->root, - (s->root_extended ? "(extended)" : "")); + trace_vtd_reg_dmar_root(s->root, s->root_extended); } static void vtd_iec_notify_all(IntelIOMMUState *s, bool global, @@ -1035,8 +1215,7 @@ static void vtd_interrupt_remap_table_setup(IntelIOMMUState *s) /* Notify global invalidation */ vtd_iec_notify_all(s, true, 0, 0); - VTD_DPRINTF(CSR, "int remap table addr 0x%"PRIx64 " size %"PRIu32, - s->intr_root, s->intr_size); + trace_vtd_reg_ir_root(s->intr_root, s->intr_size); } static void vtd_iommu_replay_all(IntelIOMMUState *s) @@ -1055,6 +1234,7 @@ static void vtd_context_global_invalidate(IntelIOMMUState *s) if (s->context_cache_gen == VTD_CONTEXT_CACHE_GEN_MAX) { vtd_reset_context_cache(s); } + vtd_switch_address_space_all(s); /* * From VT-d spec 6.5.2.1, a global context entry invalidation * should be followed by a IOTLB global invalidation, so we should @@ -1065,29 +1245,6 @@ static void vtd_context_global_invalidate(IntelIOMMUState *s) vtd_iommu_replay_all(s); } - -/* Find the VTD address space currently associated with a given bus number, - */ -static VTDBus *vtd_find_as_from_bus_num(IntelIOMMUState *s, uint8_t bus_num) -{ - VTDBus *vtd_bus = s->vtd_as_by_bus_num[bus_num]; - if (!vtd_bus) { - /* Iterate over the registered buses to find the one - * which currently hold this bus number, and update the bus_num lookup table: - */ - GHashTableIter iter; - - g_hash_table_iter_init(&iter, s->vtd_as_by_busptr); - while (g_hash_table_iter_next (&iter, NULL, (void**)&vtd_bus)) { - if (pci_bus_num(vtd_bus->bus) == bus_num) { - s->vtd_as_by_bus_num[bus_num] = vtd_bus; - return vtd_bus; - } - } - } - return vtd_bus; -} - /* Do a context-cache device-selective invalidation. * @func_mask: FM field after shifting */ @@ -1130,6 +1287,11 @@ static void vtd_context_device_invalidate(IntelIOMMUState *s, VTD_PCI_FUNC(devfn_it)); vtd_as->context_cache_entry.context_cache_gen = 0; /* + * Do switch address space when needed, in case if the + * device passthrough bit is switched. + */ + vtd_switch_address_space(vtd_as); + /* * So a device is moving out of (or moving into) a * domain, a replay() suites here to notify all the * IOMMU_NOTIFIER_MAP registers about this change. @@ -1155,11 +1317,8 @@ static uint64_t vtd_context_cache_invalidate(IntelIOMMUState *s, uint64_t val) switch (type) { case VTD_CCMD_DOMAIN_INVL: - VTD_DPRINTF(INV, "domain-selective invalidation domain 0x%"PRIx16, - (uint16_t)VTD_CCMD_DID(val)); /* Fall through */ case VTD_CCMD_GLOBAL_INVL: - VTD_DPRINTF(INV, "global invalidation"); caig = VTD_CCMD_GLOBAL_INVL_A; vtd_context_global_invalidate(s); break; @@ -1170,7 +1329,7 @@ static uint64_t vtd_context_cache_invalidate(IntelIOMMUState *s, uint64_t val) break; default: - VTD_DPRINTF(GENERAL, "error: invalid granularity"); + trace_vtd_err("Context cache invalidate type error."); caig = 0; } return caig; @@ -1178,7 +1337,7 @@ static uint64_t vtd_context_cache_invalidate(IntelIOMMUState *s, uint64_t val) static void vtd_iotlb_global_invalidate(IntelIOMMUState *s) { - trace_vtd_iotlb_reset("global invalidation recved"); + trace_vtd_inv_desc_iotlb_global(); vtd_reset_iotlb(s); vtd_iommu_replay_all(s); } @@ -1189,6 +1348,8 @@ static void vtd_iotlb_domain_invalidate(IntelIOMMUState *s, uint16_t domain_id) VTDContextEntry ce; VTDAddressSpace *vtd_as; + trace_vtd_inv_desc_iotlb_domain(domain_id); + g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_domain, &domain_id); @@ -1205,7 +1366,7 @@ static void vtd_iotlb_domain_invalidate(IntelIOMMUState *s, uint16_t domain_id) static int vtd_page_invalidate_notify_hook(IOMMUTLBEntry *entry, void *private) { - memory_region_notify_iommu((MemoryRegion *)private, *entry); + memory_region_notify_iommu((IOMMUMemoryRegion *)private, *entry); return 0; } @@ -1234,6 +1395,8 @@ static void vtd_iotlb_page_invalidate(IntelIOMMUState *s, uint16_t domain_id, { VTDIOTLBPageInvInfo info; + trace_vtd_inv_desc_iotlb_pages(domain_id, addr, am); + assert(am <= VTD_MAMV); info.domain_id = domain_id; info.addr = addr; @@ -1256,15 +1419,12 @@ static uint64_t vtd_iotlb_flush(IntelIOMMUState *s, uint64_t val) switch (type) { case VTD_TLB_GLOBAL_FLUSH: - VTD_DPRINTF(INV, "global invalidation"); iaig = VTD_TLB_GLOBAL_FLUSH_A; vtd_iotlb_global_invalidate(s); break; case VTD_TLB_DSI_FLUSH: domain_id = VTD_TLB_DID(val); - VTD_DPRINTF(INV, "domain-selective invalidation domain 0x%"PRIx16, - domain_id); iaig = VTD_TLB_DSI_FLUSH_A; vtd_iotlb_domain_invalidate(s, domain_id); break; @@ -1274,11 +1434,8 @@ static uint64_t vtd_iotlb_flush(IntelIOMMUState *s, uint64_t val) addr = vtd_get_quad_raw(s, DMAR_IVA_REG); am = VTD_IVA_AM(addr); addr = VTD_IVA_ADDR(addr); - VTD_DPRINTF(INV, "page-selective invalidation domain 0x%"PRIx16 - " addr 0x%"PRIx64 " mask %"PRIu8, domain_id, addr, am); if (am > VTD_MAMV) { - VTD_DPRINTF(GENERAL, "error: supported max address mask value is " - "%"PRIu8, (uint8_t)VTD_MAMV); + trace_vtd_err("IOTLB PSI flush: address mask overflow."); iaig = 0; break; } @@ -1287,16 +1444,13 @@ static uint64_t vtd_iotlb_flush(IntelIOMMUState *s, uint64_t val) break; default: - VTD_DPRINTF(GENERAL, "error: invalid granularity"); + trace_vtd_err("IOTLB flush: invalid granularity."); iaig = 0; } return iaig; } -static inline bool vtd_queued_inv_enable_check(IntelIOMMUState *s) -{ - return s->iq_tail == 0; -} +static void vtd_fetch_inv_desc(IntelIOMMUState *s); static inline bool vtd_queued_inv_disable_check(IntelIOMMUState *s) { @@ -1308,21 +1462,27 @@ static void vtd_handle_gcmd_qie(IntelIOMMUState *s, bool en) { uint64_t iqa_val = vtd_get_quad_raw(s, DMAR_IQA_REG); - VTD_DPRINTF(INV, "Queued Invalidation Enable %s", (en ? "on" : "off")); + trace_vtd_inv_qi_enable(en); + if (en) { - if (vtd_queued_inv_enable_check(s)) { - s->iq = iqa_val & VTD_IQA_IQA_MASK; - /* 2^(x+8) entries */ - s->iq_size = 1UL << ((iqa_val & VTD_IQA_QS) + 8); - s->qi_enabled = true; - VTD_DPRINTF(INV, "DMAR_IQA_REG 0x%"PRIx64, iqa_val); - VTD_DPRINTF(INV, "Invalidation Queue addr 0x%"PRIx64 " size %d", - s->iq, s->iq_size); - /* Ok - report back to driver */ - vtd_set_clear_mask_long(s, DMAR_GSTS_REG, 0, VTD_GSTS_QIES); - } else { - VTD_DPRINTF(GENERAL, "error: can't enable Queued Invalidation: " - "tail %"PRIu16, s->iq_tail); + s->iq = iqa_val & VTD_IQA_IQA_MASK; + /* 2^(x+8) entries */ + s->iq_size = 1UL << ((iqa_val & VTD_IQA_QS) + 8); + s->qi_enabled = true; + trace_vtd_inv_qi_setup(s->iq, s->iq_size); + /* Ok - report back to driver */ + vtd_set_clear_mask_long(s, DMAR_GSTS_REG, 0, VTD_GSTS_QIES); + + if (s->iq_tail != 0) { + /* + * This is a spec violation but Windows guests are known to set up + * Queued Invalidation this way so we allow the write and process + * Invalidation Descriptors right away. + */ + trace_vtd_warn_invalid_qi_tail(s->iq_tail); + if (!(vtd_get_long_raw(s, DMAR_FSTS_REG) & VTD_FSTS_IQE)) { + vtd_fetch_inv_desc(s); + } } } else { if (vtd_queued_inv_disable_check(s)) { @@ -1333,10 +1493,7 @@ static void vtd_handle_gcmd_qie(IntelIOMMUState *s, bool en) /* Ok - report back to driver */ vtd_set_clear_mask_long(s, DMAR_GSTS_REG, VTD_GSTS_QIES, 0); } else { - VTD_DPRINTF(GENERAL, "error: can't disable Queued Invalidation: " - "head %"PRIu16 ", tail %"PRIu16 - ", last_descriptor %"PRIu8, - s->iq_head, s->iq_tail, s->iq_last_desc_type); + trace_vtd_err_qi_disable(s->iq_head, s->iq_tail, s->iq_last_desc_type); } } } @@ -1344,8 +1501,6 @@ static void vtd_handle_gcmd_qie(IntelIOMMUState *s, bool en) /* Set Root Table Pointer */ static void vtd_handle_gcmd_srtp(IntelIOMMUState *s) { - VTD_DPRINTF(CSR, "set Root Table Pointer"); - vtd_root_table_setup(s); /* Ok - report back to driver */ vtd_set_clear_mask_long(s, DMAR_GSTS_REG, 0, VTD_GSTS_RTPS); @@ -1354,49 +1509,11 @@ static void vtd_handle_gcmd_srtp(IntelIOMMUState *s) /* Set Interrupt Remap Table Pointer */ static void vtd_handle_gcmd_sirtp(IntelIOMMUState *s) { - VTD_DPRINTF(CSR, "set Interrupt Remap Table Pointer"); - vtd_interrupt_remap_table_setup(s); /* Ok - report back to driver */ vtd_set_clear_mask_long(s, DMAR_GSTS_REG, 0, VTD_GSTS_IRTPS); } -static void vtd_switch_address_space(VTDAddressSpace *as) -{ - assert(as); - - trace_vtd_switch_address_space(pci_bus_num(as->bus), - VTD_PCI_SLOT(as->devfn), - VTD_PCI_FUNC(as->devfn), - as->iommu_state->dmar_enabled); - - /* Turn off first then on the other */ - if (as->iommu_state->dmar_enabled) { - memory_region_set_enabled(&as->sys_alias, false); - memory_region_set_enabled(&as->iommu, true); - } else { - memory_region_set_enabled(&as->iommu, false); - memory_region_set_enabled(&as->sys_alias, true); - } -} - -static void vtd_switch_address_space_all(IntelIOMMUState *s) -{ - GHashTableIter iter; - VTDBus *vtd_bus; - int i; - - g_hash_table_iter_init(&iter, s->vtd_as_by_busptr); - while (g_hash_table_iter_next(&iter, NULL, (void **)&vtd_bus)) { - for (i = 0; i < X86_IOMMU_PCI_DEVFN_MAX; i++) { - if (!vtd_bus->dev_as[i]) { - continue; - } - vtd_switch_address_space(vtd_bus->dev_as[i]); - } - } -} - /* Handle Translation Enable/Disable */ static void vtd_handle_gcmd_te(IntelIOMMUState *s, bool en) { @@ -1404,7 +1521,7 @@ static void vtd_handle_gcmd_te(IntelIOMMUState *s, bool en) return; } - VTD_DPRINTF(CSR, "Translation Enable %s", (en ? "on" : "off")); + trace_vtd_dmar_enable(en); if (en) { s->dmar_enabled = true; @@ -1425,7 +1542,7 @@ static void vtd_handle_gcmd_te(IntelIOMMUState *s, bool en) /* Handle Interrupt Remap Enable/Disable */ static void vtd_handle_gcmd_ire(IntelIOMMUState *s, bool en) { - VTD_DPRINTF(CSR, "Interrupt Remap Enable %s", (en ? "on" : "off")); + trace_vtd_ir_enable(en); if (en) { s->intr_enabled = true; @@ -1445,7 +1562,7 @@ static void vtd_handle_gcmd_write(IntelIOMMUState *s) uint32_t val = vtd_get_long_raw(s, DMAR_GCMD_REG); uint32_t changed = status ^ val; - VTD_DPRINTF(CSR, "value 0x%"PRIx32 " status 0x%"PRIx32, val, status); + trace_vtd_reg_write_gcmd(status, val); if (changed & VTD_GCMD_TE) { /* Translation enable/disable */ vtd_handle_gcmd_te(s, val & VTD_GCMD_TE); @@ -1477,8 +1594,8 @@ static void vtd_handle_ccmd_write(IntelIOMMUState *s) /* Context-cache invalidation request */ if (val & VTD_CCMD_ICC) { if (s->qi_enabled) { - VTD_DPRINTF(GENERAL, "error: Queued Invalidation enabled, " - "should not use register-based invalidation"); + trace_vtd_err("Queued Invalidation enabled, " + "should not use register-based invalidation"); return; } ret = vtd_context_cache_invalidate(s, val); @@ -1486,7 +1603,6 @@ static void vtd_handle_ccmd_write(IntelIOMMUState *s) vtd_set_clear_mask_quad(s, DMAR_CCMD_REG, VTD_CCMD_ICC, 0ULL); ret = vtd_set_clear_mask_quad(s, DMAR_CCMD_REG, VTD_CCMD_CAIG_MASK, ret); - VTD_DPRINTF(INV, "CCMD_REG write-back val: 0x%"PRIx64, ret); } } @@ -1499,8 +1615,8 @@ static void vtd_handle_iotlb_write(IntelIOMMUState *s) /* IOTLB invalidation request */ if (val & VTD_TLB_IVT) { if (s->qi_enabled) { - VTD_DPRINTF(GENERAL, "error: Queued Invalidation enabled, " - "should not use register-based invalidation"); + trace_vtd_err("Queued Invalidation enabled, " + "should not use register-based invalidation."); return; } ret = vtd_iotlb_flush(s, val); @@ -1508,7 +1624,6 @@ static void vtd_handle_iotlb_write(IntelIOMMUState *s) vtd_set_clear_mask_quad(s, DMAR_IOTLB_REG, VTD_TLB_IVT, 0ULL); ret = vtd_set_clear_mask_quad(s, DMAR_IOTLB_REG, VTD_TLB_FLUSH_GRANU_MASK_A, ret); - VTD_DPRINTF(INV, "IOTLB_REG write-back val: 0x%"PRIx64, ret); } } @@ -1519,11 +1634,9 @@ static bool vtd_get_inv_desc(dma_addr_t base_addr, uint32_t offset, dma_addr_t addr = base_addr + offset * sizeof(*inv_desc); if (dma_memory_read(&address_space_memory, addr, inv_desc, sizeof(*inv_desc))) { - VTD_DPRINTF(GENERAL, "error: fail to fetch Invalidation Descriptor " - "base_addr 0x%"PRIx64 " offset %"PRIu32, base_addr, offset); + trace_vtd_err("Read INV DESC failed."); inv_desc->lo = 0; inv_desc->hi = 0; - return false; } inv_desc->lo = le64_to_cpu(inv_desc->lo); @@ -1609,13 +1722,11 @@ static bool vtd_process_iotlb_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) switch (inv_desc->lo & VTD_INV_DESC_IOTLB_G) { case VTD_INV_DESC_IOTLB_GLOBAL: - trace_vtd_inv_desc_iotlb_global(); vtd_iotlb_global_invalidate(s); break; case VTD_INV_DESC_IOTLB_DOMAIN: domain_id = VTD_INV_DESC_IOTLB_DID(inv_desc->lo); - trace_vtd_inv_desc_iotlb_domain(domain_id); vtd_iotlb_domain_invalidate(s, domain_id); break; @@ -1623,7 +1734,6 @@ static bool vtd_process_iotlb_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) domain_id = VTD_INV_DESC_IOTLB_DID(inv_desc->lo); addr = VTD_INV_DESC_IOTLB_ADDR(inv_desc->hi); am = VTD_INV_DESC_IOTLB_AM(inv_desc->hi); - trace_vtd_inv_desc_iotlb_pages(domain_id, addr, am); if (am > VTD_MAMV) { trace_vtd_inv_desc_iotlb_invalid(inv_desc->hi, inv_desc->lo); return false; @@ -1641,10 +1751,9 @@ static bool vtd_process_iotlb_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) static bool vtd_process_inv_iec_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) { - VTD_DPRINTF(INV, "inv ir glob %d index %d mask %d", - inv_desc->iec.granularity, - inv_desc->iec.index, - inv_desc->iec.index_mask); + trace_vtd_inv_desc_iec(inv_desc->iec.granularity, + inv_desc->iec.index, + inv_desc->iec.index_mask); vtd_iec_notify_all(s, !inv_desc->iec.granularity, inv_desc->iec.index, @@ -1673,9 +1782,7 @@ static bool vtd_process_device_iotlb_desc(IntelIOMMUState *s, if ((inv_desc->lo & VTD_INV_DESC_DEVICE_IOTLB_RSVD_LO) || (inv_desc->hi & VTD_INV_DESC_DEVICE_IOTLB_RSVD_HI)) { - VTD_DPRINTF(GENERAL, "error: non-zero reserved field in Device " - "IOTLB Invalidate Descriptor hi 0x%"PRIx64 " lo 0x%"PRIx64, - inv_desc->hi, inv_desc->lo); + trace_vtd_inv_desc_iotlb_invalid(inv_desc->hi, inv_desc->lo); return false; } @@ -1720,7 +1827,7 @@ static bool vtd_process_inv_desc(IntelIOMMUState *s) VTDInvDesc inv_desc; uint8_t desc_type; - VTD_DPRINTF(INV, "iq head %"PRIu16, s->iq_head); + trace_vtd_inv_qi_head(s->iq_head); if (!vtd_get_inv_desc(s->iq, s->iq_head, &inv_desc)) { s->iq_last_desc_type = VTD_INV_DESC_NONE; return false; @@ -1759,8 +1866,7 @@ static bool vtd_process_inv_desc(IntelIOMMUState *s) break; case VTD_INV_DESC_DEVICE: - VTD_DPRINTF(INV, "Device IOTLB Invalidation Descriptor hi 0x%"PRIx64 - " lo 0x%"PRIx64, inv_desc.hi, inv_desc.lo); + trace_vtd_inv_desc("device", inv_desc.hi, inv_desc.lo); if (!vtd_process_device_iotlb_desc(s, &inv_desc)) { return false; } @@ -1780,11 +1886,11 @@ static bool vtd_process_inv_desc(IntelIOMMUState *s) /* Try to fetch and process more Invalidation Descriptors */ static void vtd_fetch_inv_desc(IntelIOMMUState *s) { - VTD_DPRINTF(INV, "fetch Invalidation Descriptors"); + trace_vtd_inv_qi_fetch(); + if (s->iq_tail >= s->iq_size) { /* Detects an invalid Tail pointer */ - VTD_DPRINTF(GENERAL, "error: iq_tail is %"PRIu16 - " while iq_size is %"PRIu16, s->iq_tail, s->iq_size); + trace_vtd_err_qi_tail(s->iq_tail, s->iq_size); vtd_handle_inv_queue_error(s); return; } @@ -1807,7 +1913,8 @@ static void vtd_handle_iqt_write(IntelIOMMUState *s) uint64_t val = vtd_get_quad_raw(s, DMAR_IQT_REG); s->iq_tail = VTD_IQT_QT(val); - VTD_DPRINTF(INV, "set iq tail %"PRIu16, s->iq_tail); + trace_vtd_inv_qi_tail(s->iq_tail); + if (s->qi_enabled && !(vtd_get_long_raw(s, DMAR_FSTS_REG) & VTD_FSTS_IQE)) { /* Process Invalidation Queue here */ vtd_fetch_inv_desc(s); @@ -1822,8 +1929,7 @@ static void vtd_handle_fsts_write(IntelIOMMUState *s) if ((fectl_reg & VTD_FECTL_IP) && !(fsts_reg & status_fields)) { vtd_set_clear_mask_long(s, DMAR_FECTL_REG, VTD_FECTL_IP, 0); - VTD_DPRINTF(FLOG, "all pending interrupt conditions serviced, clear " - "IP field of FECTL_REG"); + trace_vtd_fsts_clear_ip(); } /* FIXME: when IQE is Clear, should we try to fetch some Invalidation * Descriptors if there are any when Queued Invalidation is enabled? @@ -1838,11 +1944,12 @@ static void vtd_handle_fectl_write(IntelIOMMUState *s) * software clears the IM field? Or just check if the IM field is zero? */ fectl_reg = vtd_get_long_raw(s, DMAR_FECTL_REG); + + trace_vtd_reg_write_fectl(fectl_reg); + if ((fectl_reg & VTD_FECTL_IP) && !(fectl_reg & VTD_FECTL_IM)) { vtd_generate_interrupt(s, DMAR_FEADDR_REG, DMAR_FEDATA_REG); vtd_set_clear_mask_long(s, DMAR_FECTL_REG, VTD_FECTL_IP, 0); - VTD_DPRINTF(FLOG, "IM field is cleared, generate " - "fault event interrupt"); } } @@ -1852,9 +1959,8 @@ static void vtd_handle_ics_write(IntelIOMMUState *s) uint32_t iectl_reg = vtd_get_long_raw(s, DMAR_IECTL_REG); if ((iectl_reg & VTD_IECTL_IP) && !(ics_reg & VTD_ICS_IWC)) { + trace_vtd_reg_ics_clear_ip(); vtd_set_clear_mask_long(s, DMAR_IECTL_REG, VTD_IECTL_IP, 0); - VTD_DPRINTF(INV, "pending completion interrupt condition serviced, " - "clear IP field of IECTL_REG"); } } @@ -1866,11 +1972,12 @@ static void vtd_handle_iectl_write(IntelIOMMUState *s) * software clears the IM field? Or just check if the IM field is zero? */ iectl_reg = vtd_get_long_raw(s, DMAR_IECTL_REG); + + trace_vtd_reg_write_iectl(iectl_reg); + if ((iectl_reg & VTD_IECTL_IP) && !(iectl_reg & VTD_IECTL_IM)) { vtd_generate_interrupt(s, DMAR_IEADDR_REG, DMAR_IEDATA_REG); vtd_set_clear_mask_long(s, DMAR_IECTL_REG, VTD_IECTL_IP, 0); - VTD_DPRINTF(INV, "IM field is cleared, generate " - "invalidation event interrupt"); } } @@ -1879,10 +1986,10 @@ static uint64_t vtd_mem_read(void *opaque, hwaddr addr, unsigned size) IntelIOMMUState *s = opaque; uint64_t val; + trace_vtd_reg_read(addr, size); + if (addr + size > DMAR_REG_SIZE) { - VTD_DPRINTF(GENERAL, "error: addr outside region: max 0x%"PRIx64 - ", got 0x%"PRIx64 " %d", - (uint64_t)DMAR_REG_SIZE, addr, size); + trace_vtd_err("Read MMIO over range."); return (uint64_t)-1; } @@ -1921,8 +2028,7 @@ static uint64_t vtd_mem_read(void *opaque, hwaddr addr, unsigned size) val = vtd_get_quad(s, addr); } } - VTD_DPRINTF(CSR, "addr 0x%"PRIx64 " size %d val 0x%"PRIx64, - addr, size, val); + return val; } @@ -1931,26 +2037,22 @@ static void vtd_mem_write(void *opaque, hwaddr addr, { IntelIOMMUState *s = opaque; + trace_vtd_reg_write(addr, size, val); + if (addr + size > DMAR_REG_SIZE) { - VTD_DPRINTF(GENERAL, "error: addr outside region: max 0x%"PRIx64 - ", got 0x%"PRIx64 " %d", - (uint64_t)DMAR_REG_SIZE, addr, size); + trace_vtd_err("Write MMIO over range."); return; } switch (addr) { /* Global Command Register, 32-bit */ case DMAR_GCMD_REG: - VTD_DPRINTF(CSR, "DMAR_GCMD_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); vtd_set_long(s, addr, val); vtd_handle_gcmd_write(s); break; /* Context Command Register, 64-bit */ case DMAR_CCMD_REG: - VTD_DPRINTF(CSR, "DMAR_CCMD_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); if (size == 4) { vtd_set_long(s, addr, val); } else { @@ -1960,8 +2062,6 @@ static void vtd_mem_write(void *opaque, hwaddr addr, break; case DMAR_CCMD_REG_HI: - VTD_DPRINTF(CSR, "DMAR_CCMD_REG_HI write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); assert(size == 4); vtd_set_long(s, addr, val); vtd_handle_ccmd_write(s); @@ -1969,8 +2069,6 @@ static void vtd_mem_write(void *opaque, hwaddr addr, /* IOTLB Invalidation Register, 64-bit */ case DMAR_IOTLB_REG: - VTD_DPRINTF(INV, "DMAR_IOTLB_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); if (size == 4) { vtd_set_long(s, addr, val); } else { @@ -1980,8 +2078,6 @@ static void vtd_mem_write(void *opaque, hwaddr addr, break; case DMAR_IOTLB_REG_HI: - VTD_DPRINTF(INV, "DMAR_IOTLB_REG_HI write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); assert(size == 4); vtd_set_long(s, addr, val); vtd_handle_iotlb_write(s); @@ -1989,8 +2085,6 @@ static void vtd_mem_write(void *opaque, hwaddr addr, /* Invalidate Address Register, 64-bit */ case DMAR_IVA_REG: - VTD_DPRINTF(INV, "DMAR_IVA_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); if (size == 4) { vtd_set_long(s, addr, val); } else { @@ -1999,16 +2093,12 @@ static void vtd_mem_write(void *opaque, hwaddr addr, break; case DMAR_IVA_REG_HI: - VTD_DPRINTF(INV, "DMAR_IVA_REG_HI write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); assert(size == 4); vtd_set_long(s, addr, val); break; /* Fault Status Register, 32-bit */ case DMAR_FSTS_REG: - VTD_DPRINTF(FLOG, "DMAR_FSTS_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); assert(size == 4); vtd_set_long(s, addr, val); vtd_handle_fsts_write(s); @@ -2016,8 +2106,6 @@ static void vtd_mem_write(void *opaque, hwaddr addr, /* Fault Event Control Register, 32-bit */ case DMAR_FECTL_REG: - VTD_DPRINTF(FLOG, "DMAR_FECTL_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); assert(size == 4); vtd_set_long(s, addr, val); vtd_handle_fectl_write(s); @@ -2025,40 +2113,30 @@ static void vtd_mem_write(void *opaque, hwaddr addr, /* Fault Event Data Register, 32-bit */ case DMAR_FEDATA_REG: - VTD_DPRINTF(FLOG, "DMAR_FEDATA_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); assert(size == 4); vtd_set_long(s, addr, val); break; /* Fault Event Address Register, 32-bit */ case DMAR_FEADDR_REG: - VTD_DPRINTF(FLOG, "DMAR_FEADDR_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); assert(size == 4); vtd_set_long(s, addr, val); break; /* Fault Event Upper Address Register, 32-bit */ case DMAR_FEUADDR_REG: - VTD_DPRINTF(FLOG, "DMAR_FEUADDR_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); assert(size == 4); vtd_set_long(s, addr, val); break; /* Protected Memory Enable Register, 32-bit */ case DMAR_PMEN_REG: - VTD_DPRINTF(CSR, "DMAR_PMEN_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); assert(size == 4); vtd_set_long(s, addr, val); break; /* Root Table Address Register, 64-bit */ case DMAR_RTADDR_REG: - VTD_DPRINTF(CSR, "DMAR_RTADDR_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); if (size == 4) { vtd_set_long(s, addr, val); } else { @@ -2067,16 +2145,12 @@ static void vtd_mem_write(void *opaque, hwaddr addr, break; case DMAR_RTADDR_REG_HI: - VTD_DPRINTF(CSR, "DMAR_RTADDR_REG_HI write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); assert(size == 4); vtd_set_long(s, addr, val); break; /* Invalidation Queue Tail Register, 64-bit */ case DMAR_IQT_REG: - VTD_DPRINTF(INV, "DMAR_IQT_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); if (size == 4) { vtd_set_long(s, addr, val); } else { @@ -2086,8 +2160,6 @@ static void vtd_mem_write(void *opaque, hwaddr addr, break; case DMAR_IQT_REG_HI: - VTD_DPRINTF(INV, "DMAR_IQT_REG_HI write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); assert(size == 4); vtd_set_long(s, addr, val); /* 19:63 of IQT_REG is RsvdZ, do nothing here */ @@ -2095,8 +2167,6 @@ static void vtd_mem_write(void *opaque, hwaddr addr, /* Invalidation Queue Address Register, 64-bit */ case DMAR_IQA_REG: - VTD_DPRINTF(INV, "DMAR_IQA_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); if (size == 4) { vtd_set_long(s, addr, val); } else { @@ -2105,16 +2175,12 @@ static void vtd_mem_write(void *opaque, hwaddr addr, break; case DMAR_IQA_REG_HI: - VTD_DPRINTF(INV, "DMAR_IQA_REG_HI write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); assert(size == 4); vtd_set_long(s, addr, val); break; /* Invalidation Completion Status Register, 32-bit */ case DMAR_ICS_REG: - VTD_DPRINTF(INV, "DMAR_ICS_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); assert(size == 4); vtd_set_long(s, addr, val); vtd_handle_ics_write(s); @@ -2122,8 +2188,6 @@ static void vtd_mem_write(void *opaque, hwaddr addr, /* Invalidation Event Control Register, 32-bit */ case DMAR_IECTL_REG: - VTD_DPRINTF(INV, "DMAR_IECTL_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); assert(size == 4); vtd_set_long(s, addr, val); vtd_handle_iectl_write(s); @@ -2131,32 +2195,24 @@ static void vtd_mem_write(void *opaque, hwaddr addr, /* Invalidation Event Data Register, 32-bit */ case DMAR_IEDATA_REG: - VTD_DPRINTF(INV, "DMAR_IEDATA_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); assert(size == 4); vtd_set_long(s, addr, val); break; /* Invalidation Event Address Register, 32-bit */ case DMAR_IEADDR_REG: - VTD_DPRINTF(INV, "DMAR_IEADDR_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); assert(size == 4); vtd_set_long(s, addr, val); break; /* Invalidation Event Upper Address Register, 32-bit */ case DMAR_IEUADDR_REG: - VTD_DPRINTF(INV, "DMAR_IEUADDR_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); assert(size == 4); vtd_set_long(s, addr, val); break; /* Fault Recording Registers, 128-bit */ case DMAR_FRCD_REG_0_0: - VTD_DPRINTF(FLOG, "DMAR_FRCD_REG_0_0 write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); if (size == 4) { vtd_set_long(s, addr, val); } else { @@ -2165,15 +2221,11 @@ static void vtd_mem_write(void *opaque, hwaddr addr, break; case DMAR_FRCD_REG_0_1: - VTD_DPRINTF(FLOG, "DMAR_FRCD_REG_0_1 write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); assert(size == 4); vtd_set_long(s, addr, val); break; case DMAR_FRCD_REG_0_2: - VTD_DPRINTF(FLOG, "DMAR_FRCD_REG_0_2 write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); if (size == 4) { vtd_set_long(s, addr, val); } else { @@ -2184,8 +2236,6 @@ static void vtd_mem_write(void *opaque, hwaddr addr, break; case DMAR_FRCD_REG_0_3: - VTD_DPRINTF(FLOG, "DMAR_FRCD_REG_0_3 write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); assert(size == 4); vtd_set_long(s, addr, val); /* May clear bit 127 (Fault), update PPF */ @@ -2193,8 +2243,6 @@ static void vtd_mem_write(void *opaque, hwaddr addr, break; case DMAR_IRTA_REG: - VTD_DPRINTF(IR, "DMAR_IRTA_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); if (size == 4) { vtd_set_long(s, addr, val); } else { @@ -2203,15 +2251,11 @@ static void vtd_mem_write(void *opaque, hwaddr addr, break; case DMAR_IRTA_REG_HI: - VTD_DPRINTF(IR, "DMAR_IRTA_REG_HI write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); assert(size == 4); vtd_set_long(s, addr, val); break; default: - VTD_DPRINTF(GENERAL, "error: unhandled reg write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); if (size == 4) { vtd_set_long(s, addr, val); } else { @@ -2220,39 +2264,46 @@ static void vtd_mem_write(void *opaque, hwaddr addr, } } -static IOMMUTLBEntry vtd_iommu_translate(MemoryRegion *iommu, hwaddr addr, - bool is_write) +static IOMMUTLBEntry vtd_iommu_translate(IOMMUMemoryRegion *iommu, hwaddr addr, + IOMMUAccessFlags flag) { VTDAddressSpace *vtd_as = container_of(iommu, VTDAddressSpace, iommu); IntelIOMMUState *s = vtd_as->iommu_state; - IOMMUTLBEntry ret = { + IOMMUTLBEntry iotlb = { + /* We'll fill in the rest later. */ .target_as = &address_space_memory, - .iova = addr, - .translated_addr = 0, - .addr_mask = ~(hwaddr)0, - .perm = IOMMU_NONE, }; + bool success; - if (!s->dmar_enabled) { + if (likely(s->dmar_enabled)) { + success = vtd_do_iommu_translate(vtd_as, vtd_as->bus, vtd_as->devfn, + addr, flag & IOMMU_WO, &iotlb); + } else { /* DMAR disabled, passthrough, use 4k-page*/ - ret.iova = addr & VTD_PAGE_MASK_4K; - ret.translated_addr = addr & VTD_PAGE_MASK_4K; - ret.addr_mask = ~VTD_PAGE_MASK_4K; - ret.perm = IOMMU_RW; - return ret; + iotlb.iova = addr & VTD_PAGE_MASK_4K; + iotlb.translated_addr = addr & VTD_PAGE_MASK_4K; + iotlb.addr_mask = ~VTD_PAGE_MASK_4K; + iotlb.perm = IOMMU_RW; + success = true; + } + + if (likely(success)) { + trace_vtd_dmar_translate(pci_bus_num(vtd_as->bus), + VTD_PCI_SLOT(vtd_as->devfn), + VTD_PCI_FUNC(vtd_as->devfn), + iotlb.iova, iotlb.translated_addr, + iotlb.addr_mask); + } else { + trace_vtd_err_dmar_translate(pci_bus_num(vtd_as->bus), + VTD_PCI_SLOT(vtd_as->devfn), + VTD_PCI_FUNC(vtd_as->devfn), + iotlb.iova); } - vtd_do_iommu_translate(vtd_as, vtd_as->bus, vtd_as->devfn, addr, - is_write, &ret); - VTD_DPRINTF(MMU, - "bus %"PRIu8 " slot %"PRIu8 " func %"PRIu8 " devfn %"PRIu8 - " iova 0x%"PRIx64 " hpa 0x%"PRIx64, pci_bus_num(vtd_as->bus), - VTD_PCI_SLOT(vtd_as->devfn), VTD_PCI_FUNC(vtd_as->devfn), - vtd_as->devfn, addr, ret.translated_addr); - return ret; + return iotlb; } -static void vtd_iommu_notify_flag_changed(MemoryRegion *iommu, +static void vtd_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu, IOMMUNotifierFlag old, IOMMUNotifierFlag new) { @@ -2286,11 +2337,26 @@ static void vtd_iommu_notify_flag_changed(MemoryRegion *iommu, } } +static int vtd_post_load(void *opaque, int version_id) +{ + IntelIOMMUState *iommu = opaque; + + /* + * Memory regions are dynamically turned on/off depending on + * context entry configurations from the guest. After migration, + * we need to make sure the memory regions are still correct. + */ + vtd_switch_address_space_all(iommu); + + return 0; +} + static const VMStateDescription vtd_vmstate = { .name = "iommu-intel", .version_id = 1, .minimum_version_id = 1, .priority = MIG_PRI_IOMMU, + .post_load = vtd_post_load, .fields = (VMStateField[]) { VMSTATE_UINT64(root, IntelIOMMUState), VMSTATE_UINT64(intr_root, IntelIOMMUState), @@ -2347,25 +2413,23 @@ static int vtd_irte_get(IntelIOMMUState *iommu, uint16_t index, addr = iommu->intr_root + index * sizeof(*entry); if (dma_memory_read(&address_space_memory, addr, entry, sizeof(*entry))) { - VTD_DPRINTF(GENERAL, "error: fail to access IR root at 0x%"PRIx64 - " + %"PRIu16, iommu->intr_root, index); + trace_vtd_err("Memory read failed for IRTE."); return -VTD_FR_IR_ROOT_INVAL; } + trace_vtd_ir_irte_get(index, le64_to_cpu(entry->data[1]), + le64_to_cpu(entry->data[0])); + if (!entry->irte.present) { - VTD_DPRINTF(GENERAL, "error: present flag not set in IRTE" - " entry index %u value 0x%"PRIx64 " 0x%"PRIx64, - index, le64_to_cpu(entry->data[1]), - le64_to_cpu(entry->data[0])); + trace_vtd_err_irte(index, le64_to_cpu(entry->data[1]), + le64_to_cpu(entry->data[0])); return -VTD_FR_IR_ENTRY_P; } if (entry->irte.__reserved_0 || entry->irte.__reserved_1 || entry->irte.__reserved_2) { - VTD_DPRINTF(GENERAL, "error: IRTE entry index %"PRIu16 - " reserved fields non-zero: 0x%"PRIx64 " 0x%"PRIx64, - index, le64_to_cpu(entry->data[1]), - le64_to_cpu(entry->data[0])); + trace_vtd_err_irte(index, le64_to_cpu(entry->data[1]), + le64_to_cpu(entry->data[0])); return -VTD_FR_IR_IRTE_RSVD; } @@ -2374,15 +2438,12 @@ static int vtd_irte_get(IntelIOMMUState *iommu, uint16_t index, source_id = le32_to_cpu(entry->irte.source_id); switch (entry->irte.sid_vtype) { case VTD_SVT_NONE: - VTD_DPRINTF(IR, "No SID validation for IRTE index %d", index); break; case VTD_SVT_ALL: mask = vtd_svt_mask[entry->irte.sid_q]; if ((source_id & mask) != (sid & mask)) { - VTD_DPRINTF(GENERAL, "SID validation for IRTE index " - "%d failed (reqid 0x%04x sid 0x%04x)", index, - sid, source_id); + trace_vtd_err_irte_sid(index, sid, source_id); return -VTD_FR_IR_SID_ERR; } break; @@ -2392,16 +2453,13 @@ static int vtd_irte_get(IntelIOMMUState *iommu, uint16_t index, bus_min = source_id & 0xff; bus = sid >> 8; if (bus > bus_max || bus < bus_min) { - VTD_DPRINTF(GENERAL, "SID validation for IRTE index %d " - "failed (bus %d outside %d-%d)", index, bus, - bus_min, bus_max); + trace_vtd_err_irte_sid_bus(index, bus, bus_min, bus_max); return -VTD_FR_IR_SID_ERR; } break; default: - VTD_DPRINTF(GENERAL, "Invalid SVT bits (0x%x) in IRTE index " - "%d", entry->irte.sid_vtype, index); + trace_vtd_err_irte_svt(index, entry->irte.sid_vtype); /* Take this as verification failure. */ return -VTD_FR_IR_SID_ERR; break; @@ -2436,10 +2494,8 @@ static int vtd_remap_irq_get(IntelIOMMUState *iommu, uint16_t index, irq->dest_mode = irte.irte.dest_mode; irq->redir_hint = irte.irte.redir_hint; - VTD_DPRINTF(IR, "remapping interrupt index %d: trig:%u,vec:%u," - "deliver:%u,dest:%u,dest_mode:%u", index, - irq->trigger_mode, irq->vector, irq->delivery_mode, - irq->dest, irq->dest_mode); + trace_vtd_ir_remap(index, irq->trigger_mode, irq->vector, + irq->delivery_mode, irq->dest, irq->dest_mode); return 0; } @@ -2481,28 +2537,29 @@ static int vtd_interrupt_remap_msi(IntelIOMMUState *iommu, assert(origin && translated); + trace_vtd_ir_remap_msi_req(origin->address, origin->data); + if (!iommu || !iommu->intr_enabled) { - goto do_not_translate; + memcpy(translated, origin, sizeof(*origin)); + goto out; } if (origin->address & VTD_MSI_ADDR_HI_MASK) { - VTD_DPRINTF(GENERAL, "error: MSI addr high 32 bits nonzero" - " during interrupt remapping: 0x%"PRIx32, - (uint32_t)((origin->address & VTD_MSI_ADDR_HI_MASK) >> \ - VTD_MSI_ADDR_HI_SHIFT)); + trace_vtd_err("MSI address high 32 bits non-zero when " + "Interrupt Remapping enabled."); return -VTD_FR_IR_REQ_RSVD; } addr.data = origin->address & VTD_MSI_ADDR_LO_MASK; if (addr.addr.__head != 0xfee) { - VTD_DPRINTF(GENERAL, "error: MSI addr low 32 bits invalid: " - "0x%"PRIx32, addr.data); + trace_vtd_err("MSI addr low 32 bit invalid."); return -VTD_FR_IR_REQ_RSVD; } /* This is compatible mode. */ if (addr.addr.int_mode != VTD_IR_INT_FORMAT_REMAP) { - goto do_not_translate; + memcpy(translated, origin, sizeof(*origin)); + goto out; } index = addr.addr.index_h << 15 | le16_to_cpu(addr.addr.index_l); @@ -2521,34 +2578,28 @@ static int vtd_interrupt_remap_msi(IntelIOMMUState *iommu, } if (addr.addr.sub_valid) { - VTD_DPRINTF(IR, "received MSI interrupt"); + trace_vtd_ir_remap_type("MSI"); if (origin->data & VTD_IR_MSI_DATA_RESERVED) { - VTD_DPRINTF(GENERAL, "error: MSI data bits non-zero for " - "interrupt remappable entry: 0x%"PRIx32, - origin->data); + trace_vtd_err_ir_msi_invalid(sid, origin->address, origin->data); return -VTD_FR_IR_REQ_RSVD; } } else { uint8_t vector = origin->data & 0xff; uint8_t trigger_mode = (origin->data >> MSI_DATA_TRIGGER_SHIFT) & 0x1; - VTD_DPRINTF(IR, "received IOAPIC interrupt"); + trace_vtd_ir_remap_type("IOAPIC"); /* IOAPIC entry vector should be aligned with IRTE vector * (see vt-d spec 5.1.5.1). */ if (vector != irq.vector) { - VTD_DPRINTF(GENERAL, "IOAPIC vector inconsistent: " - "entry: %d, IRTE: %d, index: %d", - vector, irq.vector, index); + trace_vtd_warn_ir_vector(sid, index, vector, irq.vector); } /* The Trigger Mode field must match the Trigger Mode in the IRTE. * (see vt-d spec 5.1.5.1). */ if (trigger_mode != irq.trigger_mode) { - VTD_DPRINTF(GENERAL, "IOAPIC trigger mode inconsistent: " - "entry: %u, IRTE: %u, index: %d", - trigger_mode, irq.trigger_mode, index); + trace_vtd_warn_ir_trigger(sid, index, trigger_mode, + irq.trigger_mode); } - } /* @@ -2560,13 +2611,9 @@ static int vtd_interrupt_remap_msi(IntelIOMMUState *iommu, /* Translate VTDIrq to MSI message */ vtd_generate_msi_message(&irq, translated); - VTD_DPRINTF(IR, "mapping MSI 0x%"PRIx64":0x%"PRIx32 " -> " - "0x%"PRIx64":0x%"PRIx32, origin->address, origin->data, - translated->address, translated->data); - return 0; - -do_not_translate: - memcpy(translated, origin, sizeof(*origin)); +out: + trace_vtd_ir_remap_msi(origin->address, origin->data, + translated->address, translated->data); return 0; } @@ -2603,16 +2650,10 @@ static MemTxResult vtd_mem_ir_write(void *opaque, hwaddr addr, ret = vtd_interrupt_remap_msi(opaque, &from, &to, sid); if (ret) { /* TODO: report error */ - VTD_DPRINTF(GENERAL, "int remap fail for addr 0x%"PRIx64 - " data 0x%"PRIx32, from.address, from.data); /* Drop this interrupt */ return MEMTX_ERROR; } - VTD_DPRINTF(IR, "delivering MSI 0x%"PRIx64":0x%"PRIx32 - " for device sid 0x%04x", - to.address, to.data, sid); - apic_get_class()->send_msi(&to); return MEMTX_OK; @@ -2677,8 +2718,9 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn) * vtd_sys_alias and intel_iommu regions. IR region is always * enabled. */ - memory_region_init_iommu(&vtd_dev_as->iommu, OBJECT(s), - &s->iommu_ops, "intel_iommu_dmar", + memory_region_init_iommu(&vtd_dev_as->iommu, sizeof(vtd_dev_as->iommu), + TYPE_INTEL_IOMMU_MEMORY_REGION, OBJECT(s), + "intel_iommu_dmar", UINT64_MAX); memory_region_init_alias(&vtd_dev_as->sys_alias, OBJECT(s), "vtd_sys_alias", get_system_memory(), @@ -2695,7 +2737,8 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn) memory_region_add_subregion_overlap(&vtd_dev_as->root, 0, &vtd_dev_as->sys_alias, 1); memory_region_add_subregion_overlap(&vtd_dev_as->root, 0, - &vtd_dev_as->iommu, 1); + MEMORY_REGION(&vtd_dev_as->iommu), + 1); vtd_switch_address_space(vtd_dev_as); } return vtd_dev_as; @@ -2775,9 +2818,9 @@ static int vtd_replay_hook(IOMMUTLBEntry *entry, void *private) return 0; } -static void vtd_iommu_replay(MemoryRegion *mr, IOMMUNotifier *n) +static void vtd_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) { - VTDAddressSpace *vtd_as = container_of(mr, VTDAddressSpace, iommu); + VTDAddressSpace *vtd_as = container_of(iommu_mr, VTDAddressSpace, iommu); IntelIOMMUState *s = vtd_as->iommu_state; uint8_t bus_n = pci_bus_num(vtd_as->bus); VTDContextEntry ce; @@ -2815,9 +2858,6 @@ static void vtd_init(IntelIOMMUState *s) memset(s->w1cmask, 0, DMAR_REG_SIZE); memset(s->womask, 0, DMAR_REG_SIZE); - s->iommu_ops.translate = vtd_iommu_translate; - s->iommu_ops.notify_flag_changed = vtd_iommu_notify_flag_changed; - s->iommu_ops.replay = vtd_iommu_replay; s->root = 0; s->root_extended = false; s->dmar_enabled = false; @@ -2844,6 +2884,10 @@ static void vtd_init(IntelIOMMUState *s) s->ecap |= VTD_ECAP_DT; } + if (x86_iommu->pt_supported) { + s->ecap |= VTD_ECAP_PT; + } + if (s->caching_mode) { s->cap |= VTD_CAP_CM; } @@ -2911,7 +2955,6 @@ static void vtd_reset(DeviceState *dev) { IntelIOMMUState *s = INTEL_IOMMU_DEVICE(dev); - VTD_DPRINTF(GENERAL, ""); vtd_init(s); /* @@ -2984,7 +3027,6 @@ static void vtd_realize(DeviceState *dev, Error **errp) } bus = pcms->bus; - VTD_DPRINTF(GENERAL, ""); x86_iommu->type = TYPE_INTEL; if (!vtd_decide_config(s, errp)) { @@ -3030,10 +3072,26 @@ static const TypeInfo vtd_info = { .class_init = vtd_class_init, }; +static void vtd_iommu_memory_region_class_init(ObjectClass *klass, + void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = vtd_iommu_translate; + imrc->notify_flag_changed = vtd_iommu_notify_flag_changed; + imrc->replay = vtd_iommu_replay; +} + +static const TypeInfo vtd_iommu_memory_region_info = { + .parent = TYPE_IOMMU_MEMORY_REGION, + .name = TYPE_INTEL_IOMMU_MEMORY_REGION, + .class_init = vtd_iommu_memory_region_class_init, +}; + static void vtd_register_types(void) { - VTD_DPRINTF(GENERAL, ""); type_register_static(&vtd_info); + type_register_static(&vtd_iommu_memory_region_info); } type_init(vtd_register_types) diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h index 29d67075f4..f50ecd8b73 100644 --- a/hw/i386/intel_iommu_internal.h +++ b/hw/i386/intel_iommu_internal.h @@ -187,6 +187,7 @@ /* Interrupt Remapping support */ #define VTD_ECAP_IR (1ULL << 3) #define VTD_ECAP_EIM (1ULL << 4) +#define VTD_ECAP_PT (1ULL << 6) #define VTD_ECAP_MHMV (15ULL << 20) /* CAP_REG */ @@ -383,6 +384,7 @@ typedef struct VTDIOTLBPageInvInfo VTDIOTLBPageInvInfo; /* Pagesize of VTD paging structures, including root and context tables */ #define VTD_PAGE_SHIFT 12 #define VTD_PAGE_SIZE (1ULL << VTD_PAGE_SHIFT) +#define VTD_PAGE_MASK (VTD_PAGE_SIZE - 1) #define VTD_PAGE_SHIFT_4K 12 #define VTD_PAGE_MASK_4K (~((1ULL << VTD_PAGE_SHIFT_4K) - 1)) diff --git a/hw/i386/kvm/clock.c b/hw/i386/kvm/clock.c index 13eca374cd..363d1b5743 100644 --- a/hw/i386/kvm/clock.c +++ b/hw/i386/kvm/clock.c @@ -19,6 +19,7 @@ #include "qemu/host-utils.h" #include "sysemu/sysemu.h" #include "sysemu/kvm.h" +#include "sysemu/hw_accel.h" #include "kvm_i386.h" #include "hw/sysbus.h" #include "hw/kvm/clock.h" @@ -69,6 +70,8 @@ static uint64_t kvmclock_current_nsec(KVMClockState *s) uint64_t nsec_hi; uint64_t nsec; + cpu_synchronize_state(cpu); + if (!(env->system_time_msr & 1ULL)) { /* KVM clock not active */ return 0; diff --git a/hw/i386/kvm/pci-assign.c b/hw/i386/kvm/pci-assign.c index 87dcbdd51a..33e20cb3e8 100644 --- a/hw/i386/kvm/pci-assign.c +++ b/hw/i386/kvm/pci-assign.c @@ -824,12 +824,13 @@ static void assign_device(AssignedDevice *dev, Error **errp) } } -static void verify_irqchip_in_kernel(Error **errp) +static int verify_irqchip_in_kernel(Error **errp) { if (kvm_irqchip_in_kernel()) { - return; + return -1; } error_setg(errp, "pci-assign requires KVM with in-kernel irqchip enabled"); + return 0; } static int assign_intx(AssignedDevice *dev, Error **errp) @@ -838,7 +839,6 @@ static int assign_intx(AssignedDevice *dev, Error **errp) PCIINTxRoute intx_route; bool intx_host_msi; int r; - Error *local_err = NULL; /* Interrupt PIN 0 means don't use INTx */ if (assigned_dev_pci_read_byte(&dev->dev, PCI_INTERRUPT_PIN) == 0) { @@ -846,9 +846,7 @@ static int assign_intx(AssignedDevice *dev, Error **errp) return 0; } - verify_irqchip_in_kernel(&local_err); - if (local_err) { - error_propagate(errp, local_err); + if (verify_irqchip_in_kernel(errp) < 0) { return -ENOTSUP; } @@ -1234,7 +1232,6 @@ static int assigned_device_pci_cap_init(PCIDevice *pci_dev, Error **errp) AssignedDevice *dev = PCI_ASSIGN(pci_dev); PCIRegion *pci_region = dev->real_device.regions; int ret, pos; - Error *local_err = NULL; /* Clear initial capabilities pointer and status copied from hw */ pci_set_byte(pci_dev->config + PCI_CAPABILITY_LIST, 0); @@ -1246,18 +1243,15 @@ static int assigned_device_pci_cap_init(PCIDevice *pci_dev, Error **errp) * MSI capability is the 1st capability in capability config */ pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_MSI, 0); if (pos != 0 && kvm_check_extension(kvm_state, KVM_CAP_ASSIGN_DEV_IRQ)) { - verify_irqchip_in_kernel(&local_err); - if (local_err) { - error_propagate(errp, local_err); + if (verify_irqchip_in_kernel(errp) < 0) { return -ENOTSUP; } dev->dev.cap_present |= QEMU_PCI_CAP_MSI; dev->cap.available |= ASSIGNED_DEVICE_CAP_MSI; /* Only 32-bit/no-mask currently supported */ - ret = pci_add_capability2(pci_dev, PCI_CAP_ID_MSI, pos, 10, - &local_err); + ret = pci_add_capability(pci_dev, PCI_CAP_ID_MSI, pos, 10, + errp); if (ret < 0) { - error_propagate(errp, local_err); return ret; } pci_dev->msi_cap = pos; @@ -1281,17 +1275,14 @@ static int assigned_device_pci_cap_init(PCIDevice *pci_dev, Error **errp) uint32_t msix_table_entry; uint16_t msix_max; - verify_irqchip_in_kernel(&local_err); - if (local_err) { - error_propagate(errp, local_err); + if (verify_irqchip_in_kernel(errp) < 0) { return -ENOTSUP; } dev->dev.cap_present |= QEMU_PCI_CAP_MSIX; dev->cap.available |= ASSIGNED_DEVICE_CAP_MSIX; - ret = pci_add_capability2(pci_dev, PCI_CAP_ID_MSIX, pos, 12, - &local_err); + ret = pci_add_capability(pci_dev, PCI_CAP_ID_MSIX, pos, 12, + errp); if (ret < 0) { - error_propagate(errp, local_err); return ret; } pci_dev->msix_cap = pos; @@ -1318,10 +1309,9 @@ static int assigned_device_pci_cap_init(PCIDevice *pci_dev, Error **errp) if (pos) { uint16_t pmc; - ret = pci_add_capability2(pci_dev, PCI_CAP_ID_PM, pos, PCI_PM_SIZEOF, - &local_err); + ret = pci_add_capability(pci_dev, PCI_CAP_ID_PM, pos, PCI_PM_SIZEOF, + errp); if (ret < 0) { - error_propagate(errp, local_err); return ret; } @@ -1363,9 +1353,9 @@ static int assigned_device_pci_cap_init(PCIDevice *pci_dev, Error **errp) PCI_CAP_ID_EXP); return -EINVAL; } else if (size != 0x3c) { - error_report("WARNING, %s: PCIe cap-id 0x%x has " - "non-standard size 0x%x; std size should be 0x3c", - __func__, PCI_CAP_ID_EXP, size); + warn_report("%s: PCIe cap-id 0x%x has " + "non-standard size 0x%x; std size should be 0x3c", + __func__, PCI_CAP_ID_EXP, size); } } else if (version == 0) { uint16_t vid, did; @@ -1386,10 +1376,9 @@ static int assigned_device_pci_cap_init(PCIDevice *pci_dev, Error **errp) return -EINVAL; } - ret = pci_add_capability2(pci_dev, PCI_CAP_ID_EXP, pos, size, - &local_err); + ret = pci_add_capability(pci_dev, PCI_CAP_ID_EXP, pos, size, + errp); if (ret < 0) { - error_propagate(errp, local_err); return ret; } @@ -1462,10 +1451,9 @@ static int assigned_device_pci_cap_init(PCIDevice *pci_dev, Error **errp) uint32_t status; /* Only expose the minimum, 8 byte capability */ - ret = pci_add_capability2(pci_dev, PCI_CAP_ID_PCIX, pos, 8, - &local_err); + ret = pci_add_capability(pci_dev, PCI_CAP_ID_PCIX, pos, 8, + errp); if (ret < 0) { - error_propagate(errp, local_err); return ret; } @@ -1490,10 +1478,9 @@ static int assigned_device_pci_cap_init(PCIDevice *pci_dev, Error **errp) pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_VPD, 0); if (pos) { /* Direct R/W passthrough */ - ret = pci_add_capability2(pci_dev, PCI_CAP_ID_VPD, pos, 8, - &local_err); + ret = pci_add_capability(pci_dev, PCI_CAP_ID_VPD, pos, 8, + errp); if (ret < 0) { - error_propagate(errp, local_err); return ret; } @@ -1508,10 +1495,9 @@ static int assigned_device_pci_cap_init(PCIDevice *pci_dev, Error **errp) pos += PCI_CAP_LIST_NEXT) { uint8_t len = pci_get_byte(pci_dev->config + pos + PCI_CAP_FLAGS); /* Direct R/W passthrough */ - ret = pci_add_capability2(pci_dev, PCI_CAP_ID_VNDR, pos, len, - &local_err); + ret = pci_add_capability(pci_dev, PCI_CAP_ID_VNDR, pos, len, + errp); if (ret < 0) { - error_propagate(errp, local_err); return ret; } diff --git a/hw/i386/kvmvapic.c b/hw/i386/kvmvapic.c index 82a49556af..fc962c5fbc 100644 --- a/hw/i386/kvmvapic.c +++ b/hw/i386/kvmvapic.c @@ -383,8 +383,7 @@ static void patch_byte(X86CPU *cpu, target_ulong addr, uint8_t byte) cpu_memory_rw_debug(CPU(cpu), addr, &byte, 1, 1); } -static void patch_call(VAPICROMState *s, X86CPU *cpu, target_ulong ip, - uint32_t target) +static void patch_call(X86CPU *cpu, target_ulong ip, uint32_t target) { uint32_t offset; @@ -393,76 +392,71 @@ static void patch_call(VAPICROMState *s, X86CPU *cpu, target_ulong ip, cpu_memory_rw_debug(CPU(cpu), ip + 1, (void *)&offset, sizeof(offset), 1); } -static void patch_instruction(VAPICROMState *s, X86CPU *cpu, target_ulong ip) +typedef struct PatchInfo { + VAPICHandlers *handler; + target_ulong ip; +} PatchInfo; + +static void do_patch_instruction(CPUState *cs, run_on_cpu_data data) { - CPUState *cs = CPU(cpu); - CPUX86State *env = &cpu->env; - VAPICHandlers *handlers; + X86CPU *x86_cpu = X86_CPU(cs); + PatchInfo *info = (PatchInfo *) data.host_ptr; + VAPICHandlers *handlers = info->handler; + target_ulong ip = info->ip; uint8_t opcode[2]; uint32_t imm32 = 0; - target_ulong current_pc = 0; - target_ulong current_cs_base = 0; - uint32_t current_flags = 0; - - if (smp_cpus == 1) { - handlers = &s->rom_state.up; - } else { - handlers = &s->rom_state.mp; - } - - if (!kvm_enabled()) { - cpu_get_tb_cpu_state(env, ¤t_pc, ¤t_cs_base, - ¤t_flags); - /* Account this instruction, because we will exit the tb. - This is the first instruction in the block. Therefore - there is no need in restoring CPU state. */ - if (use_icount) { - --cs->icount_decr.u16.low; - } - } - - pause_all_vcpus(); cpu_memory_rw_debug(cs, ip, opcode, sizeof(opcode), 0); switch (opcode[0]) { case 0x89: /* mov r32 to r/m32 */ - patch_byte(cpu, ip, 0x50 + modrm_reg(opcode[1])); /* push reg */ - patch_call(s, cpu, ip + 1, handlers->set_tpr); + patch_byte(x86_cpu, ip, 0x50 + modrm_reg(opcode[1])); /* push reg */ + patch_call(x86_cpu, ip + 1, handlers->set_tpr); break; case 0x8b: /* mov r/m32 to r32 */ - patch_byte(cpu, ip, 0x90); - patch_call(s, cpu, ip + 1, handlers->get_tpr[modrm_reg(opcode[1])]); + patch_byte(x86_cpu, ip, 0x90); + patch_call(x86_cpu, ip + 1, handlers->get_tpr[modrm_reg(opcode[1])]); break; case 0xa1: /* mov abs to eax */ - patch_call(s, cpu, ip, handlers->get_tpr[0]); + patch_call(x86_cpu, ip, handlers->get_tpr[0]); break; case 0xa3: /* mov eax to abs */ - patch_call(s, cpu, ip, handlers->set_tpr_eax); + patch_call(x86_cpu, ip, handlers->set_tpr_eax); break; case 0xc7: /* mov imm32, r/m32 (c7/0) */ - patch_byte(cpu, ip, 0x68); /* push imm32 */ + patch_byte(x86_cpu, ip, 0x68); /* push imm32 */ cpu_memory_rw_debug(cs, ip + 6, (void *)&imm32, sizeof(imm32), 0); cpu_memory_rw_debug(cs, ip + 1, (void *)&imm32, sizeof(imm32), 1); - patch_call(s, cpu, ip + 5, handlers->set_tpr); + patch_call(x86_cpu, ip + 5, handlers->set_tpr); break; case 0xff: /* push r/m32 */ - patch_byte(cpu, ip, 0x50); /* push eax */ - patch_call(s, cpu, ip + 1, handlers->get_tpr_stack); + patch_byte(x86_cpu, ip, 0x50); /* push eax */ + patch_call(x86_cpu, ip + 1, handlers->get_tpr_stack); break; default: abort(); } - resume_all_vcpus(); + g_free(info); +} + +static void patch_instruction(VAPICROMState *s, X86CPU *cpu, target_ulong ip) +{ + CPUState *cs = CPU(cpu); + VAPICHandlers *handlers; + PatchInfo *info; - if (!kvm_enabled()) { - /* Both tb_lock and iothread_mutex will be reset when - * longjmps back into the cpu_exec loop. */ - tb_lock(); - tb_gen_code(cs, current_pc, current_cs_base, current_flags, 1); - cpu_loop_exit_noexc(cs); + if (smp_cpus == 1) { + handlers = &s->rom_state.up; + } else { + handlers = &s->rom_state.mp; } + + info = g_new(PatchInfo, 1); + info->handler = handlers; + info->ip = ip; + + async_safe_run_on_cpu(cs, do_patch_instruction, RUN_ON_CPU_HOST_PTR(info)); } void vapic_report_tpr_access(DeviceState *dev, CPUState *cs, target_ulong ip, diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 816bfa872c..22e16031b0 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -347,7 +347,7 @@ static int check_fdc(Object *obj, void *opaque) return 0; } - iobase = object_property_get_int(obj, "iobase", &local_err); + iobase = object_property_get_uint(obj, "iobase", &local_err); if (local_err || iobase != 0x3f0) { error_free(local_err); return 0; @@ -381,8 +381,8 @@ ISADevice *pc_find_fdc0(void) } if (state.multiple) { - error_report("warning: multiple floppy disk controllers with " - "iobase=0x3f0 have been found"); + warn_report("multiple floppy disk controllers with " + "iobase=0x3f0 have been found"); error_printf("the one being picked for CMOS setup might not reflect " "your intent\n"); } @@ -519,7 +519,7 @@ static void port92_write(void *opaque, hwaddr addr, uint64_t val, s->outport = val; qemu_set_irq(s->a20_out, (val >> 1) & 1); if ((val & 1) && !(oldval & 1)) { - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); } } @@ -788,9 +788,7 @@ static FWCfgState *bochs_bios_init(AddressSpace *as, PCMachineState *pcms) for (i = 0; i < cpus->len; i++) { unsigned int apic_id = cpus->cpus[i].arch_id; assert(apic_id < pcms->apic_id_limit); - if (cpus->cpus[i].props.has_node_id) { - numa_fw_cfg[apic_id + 1] = cpu_to_le64(cpus->cpus[i].props.node_id); - } + numa_fw_cfg[apic_id + 1] = cpu_to_le64(cpus->cpus[i].props.node_id); } for (i = 0; i < nb_numa_nodes; i++) { numa_fw_cfg[pcms->apic_id_limit + 1 + i] = @@ -1100,7 +1098,7 @@ static void pc_new_cpu(const char *typename, int64_t apic_id, Error **errp) cpu = object_new(typename); - object_property_set_int(cpu, apic_id, "apic-id", &local_err); + object_property_set_uint(cpu, apic_id, "apic-id", &local_err); object_property_set_bool(cpu, true, "realized", &local_err); object_unref(cpu); @@ -1322,8 +1320,7 @@ void pc_acpi_init(const char *default_dsdt) acpi_table_add_builtin(opts, &err); if (err) { - error_reportf_err(err, "WARNING: failed to load %s: ", - filename); + warn_reportf_err(err, "failed to load %s: ", filename); } g_free(filename); } @@ -1446,7 +1443,6 @@ void pc_memory_init(PCMachineState *pcms, option_rom_mr = g_malloc(sizeof(*option_rom_mr)); memory_region_init_ram(option_rom_mr, NULL, "pc.rom", PC_ROM_SIZE, &error_fatal); - vmstate_register_ram_global(option_rom_mr); memory_region_add_subregion_overlap(rom_memory, PC_ROM_MIN_VGA, option_rom_mr, @@ -1560,7 +1556,7 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, * and earlier, use IRQ2 for compat. Otherwise, use IRQ16~23, * IRQ8 and IRQ2. */ - uint8_t compat = object_property_get_int(OBJECT(hpet), + uint8_t compat = object_property_get_uint(OBJECT(hpet), HPET_INTCAP, NULL); if (!compat) { qdev_prop_set_uint32(hpet, HPET_INTCAP, hpet_irqs); @@ -1694,6 +1690,7 @@ static void pc_dimm_plug(HotplugHandler *hotplug_dev, PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); MemoryRegion *mr = ddc->get_memory_region(dimm); uint64_t align = TARGET_PAGE_SIZE; + bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM); if (memory_region_get_alignment(mr) && pcmc->enforce_aligned_dimm) { align = memory_region_get_alignment(mr); @@ -1705,17 +1702,18 @@ static void pc_dimm_plug(HotplugHandler *hotplug_dev, goto out; } + if (is_nvdimm && !pcms->acpi_nvdimm_state.is_enabled) { + error_setg(&local_err, + "nvdimm is not enabled: missing 'nvdimm' in '-M'"); + goto out; + } + pc_dimm_memory_plug(dev, &pcms->hotplug_memory, mr, align, &local_err); if (local_err) { goto out; } - if (object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)) { - if (!pcms->acpi_nvdimm_state.is_enabled) { - error_setg(&local_err, - "nvdimm is not enabled: missing 'nvdimm' in '-M'"); - goto out; - } + if (is_nvdimm) { nvdimm_plug(&pcms->acpi_nvdimm_state); } @@ -1893,7 +1891,6 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { int idx; - int node_id; CPUState *cs; CPUArchId *cpu_slot; X86CPUTopoInfo topo; @@ -1984,21 +1981,7 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev, cs = CPU(cpu); cs->cpu_index = idx; - node_id = cpu_slot->props.node_id; - if (!cpu_slot->props.has_node_id) { - /* by default CPUState::numa_node was 0 if it's not set via CLI - * keep it this way for now but in future we probably should - * refuse to start up with incomplete numa mapping */ - node_id = 0; - } - if (cs->numa_node == CPU_UNSET_NUMA_NODE_ID) { - cs->numa_node = node_id; - } else if (cs->numa_node != node_id) { - error_setg(errp, "node-id %d must match numa node specified" - "with -numa option for cpu-index %d", - cs->numa_node, cs->cpu_index); - return; - } + numa_cpu_pre_plug(cpu_slot, dev, errp); } static void pc_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, @@ -2102,9 +2085,9 @@ static void pc_machine_set_max_ram_below_4g(Object *obj, Visitor *v, } if (value < (1ULL << 20)) { - error_report("Warning: small max_ram_below_4g(%"PRIu64 - ") less than 1M. BIOS may not work..", - value); + warn_report("small max_ram_below_4g(%"PRIu64 + ") less than 1M. BIOS may not work..", + value); } pcms->max_ram_below_4g = value; diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 2234bd0461..11b4336a42 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -52,7 +52,8 @@ #include <xen/hvm/hvm_info_table.h> #include "hw/xen/xen_pt.h" #endif -#include "migration/migration.h" +#include "migration/global_state.h" +#include "migration/misc.h" #include "kvm_i386.h" #include "sysemu/numa.h" @@ -130,10 +131,10 @@ static void pc_init1(MachineState *machine, lowmem = 0xc0000000; } if (lowmem & ((1ULL << 30) - 1)) { - error_report("Warning: Large machine and max_ram_below_4g " - "(%" PRIu64 ") not a multiple of 1G; " - "possible bad performance.", - pcms->max_ram_below_4g); + warn_report("Large machine and max_ram_below_4g " + "(%" PRIu64 ") not a multiple of 1G; " + "possible bad performance.", + pcms->max_ram_below_4g); } } } @@ -313,12 +314,9 @@ static void pc_init1(MachineState *machine, static void pc_compat_2_3(MachineState *machine) { PCMachineState *pcms = PC_MACHINE(machine); - savevm_skip_section_footers(); if (kvm_enabled()) { pcms->smm = ON_OFF_AUTO_OFF; } - global_state_set_optional(); - savevm_skip_configuration(); } static void pc_compat_2_2(MachineState *machine) diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 1523ef39e1..1653a47f0a 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -36,6 +36,7 @@ #include "hw/timer/mc146818rtc.h" #include "hw/xen/xen.h" #include "sysemu/kvm.h" +#include "kvm_i386.h" #include "hw/kvm/clock.h" #include "hw/pci-host/q35.h" #include "exec/address-spaces.h" @@ -100,9 +101,9 @@ static void pc_q35_init(MachineState *machine) lowmem = pcms->max_ram_below_4g; if (machine->ram_size - lowmem > lowmem && lowmem & ((1ULL << 30) - 1)) { - error_report("Warning: Large machine and max_ram_below_4g(%"PRIu64 - ") not a multiple of 1G; possible bad performance.", - pcms->max_ram_below_4g); + warn_report("Large machine and max_ram_below_4g(%"PRIu64 + ") not a multiple of 1G; possible bad performance.", + pcms->max_ram_below_4g); } } diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index f915ad0a36..6b183747fc 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -59,7 +59,6 @@ static void pc_isa_bios_init(MemoryRegion *rom_memory, isa_bios = g_malloc(sizeof(*isa_bios)); memory_region_init_ram(isa_bios, NULL, "isa-bios", isa_bios_size, &error_fatal); - vmstate_register_ram_global(isa_bios); memory_region_add_subregion_overlap(rom_memory, 0x100000 - isa_bios_size, isa_bios, @@ -196,7 +195,6 @@ static void old_pc_system_rom_init(MemoryRegion *rom_memory, bool isapc_ram_fw) } bios = g_malloc(sizeof(*bios)); memory_region_init_ram(bios, NULL, "pc.bios", bios_size, &error_fatal); - vmstate_register_ram_global(bios); if (!isapc_ram_fw) { memory_region_set_readonly(bios, true); } diff --git a/hw/i386/pci-assign-load-rom.c b/hw/i386/pci-assign-load-rom.c index fd59076e7a..43429b66be 100644 --- a/hw/i386/pci-assign-load-rom.c +++ b/hw/i386/pci-assign-load-rom.c @@ -59,7 +59,7 @@ void *pci_assign_dev_load_option_rom(PCIDevice *dev, struct Object *owner, fseek(fp, 0, SEEK_SET); snprintf(name, sizeof(name), "%s.rom", object_get_typename(owner)); - memory_region_init_ram(&dev->rom, owner, name, st.st_size, &error_abort); + memory_region_init_ram_nomigrate(&dev->rom, owner, name, st.st_size, &error_abort); vmstate_register_ram(&dev->rom, &dev->qdev); ptr = memory_region_get_ram_ptr(&dev->rom); memset(ptr, 0xff, st.st_size); diff --git a/hw/i386/trace-events b/hw/i386/trace-events index 04a6980800..42d8a7e27a 100644 --- a/hw/i386/trace-events +++ b/hw/i386/trace-events @@ -19,6 +19,13 @@ vtd_inv_desc_wait_sw(uint64_t addr, uint32_t data) "wait invalidate status write vtd_inv_desc_wait_irq(const char *msg) "%s" vtd_inv_desc_wait_invalid(uint64_t hi, uint64_t lo) "invalid wait desc hi 0x%"PRIx64" lo 0x%"PRIx64 vtd_inv_desc_wait_write_fail(uint64_t hi, uint64_t lo) "write fail for wait desc hi 0x%"PRIx64" lo 0x%"PRIx64 +vtd_inv_desc_iec(uint32_t granularity, uint32_t index, uint32_t mask) "granularity 0x%"PRIx32" index 0x%"PRIx32" mask 0x%"PRIx32 +vtd_inv_qi_enable(bool enable) "enabled %d" +vtd_inv_qi_setup(uint64_t addr, int size) "addr 0x%"PRIx64" size %d" +vtd_inv_qi_head(uint16_t head) "read head %d" +vtd_inv_qi_tail(uint16_t head) "write tail %d" +vtd_inv_qi_fetch(void) "" +vtd_context_cache_reset(void) "" vtd_re_not_present(uint8_t bus) "Root entry bus %"PRIu8" not present" vtd_re_invalid(uint64_t hi, uint64_t lo) "invalid root entry hi 0x%"PRIx64" lo 0x%"PRIx64 vtd_ce_not_present(uint8_t bus, uint8_t devfn) "Context entry bus %"PRIu8" devfn %"PRIu8" not present" @@ -38,6 +45,45 @@ vtd_page_walk_skip_perm(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"P vtd_page_walk_skip_reserve(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to rsrv set" vtd_switch_address_space(uint8_t bus, uint8_t slot, uint8_t fn, bool on) "Device %02x:%02x.%x switching address space (iommu enabled=%d)" vtd_as_unmap_whole(uint8_t bus, uint8_t slot, uint8_t fn, uint64_t iova, uint64_t size) "Device %02x:%02x.%x start 0x%"PRIx64" size 0x%"PRIx64 +vtd_translate_pt(uint16_t sid, uint64_t addr) "source id 0x%"PRIu16", iova 0x%"PRIx64 +vtd_pt_enable_fast_path(uint16_t sid, bool success) "sid 0x%"PRIu16" %d" +vtd_irq_generate(uint64_t addr, uint64_t data) "addr 0x%"PRIx64" data 0x%"PRIx64 +vtd_reg_read(uint64_t addr, uint64_t size) "addr 0x%"PRIx64" size 0x%"PRIx64 +vtd_reg_write(uint64_t addr, uint64_t size, uint64_t val) "addr 0x%"PRIx64" size 0x%"PRIx64" value 0x%"PRIx64 +vtd_reg_dmar_root(uint64_t addr, bool extended) "addr 0x%"PRIx64" extended %d" +vtd_reg_ir_root(uint64_t addr, uint32_t size) "addr 0x%"PRIx64" size 0x%"PRIx32 +vtd_reg_write_gcmd(uint32_t status, uint32_t val) "status 0x%"PRIx32" value 0x%"PRIx32 +vtd_reg_write_fectl(uint32_t value) "value 0x%"PRIx32 +vtd_reg_write_iectl(uint32_t value) "value 0x%"PRIx32 +vtd_reg_ics_clear_ip(void) "" +vtd_dmar_translate(uint8_t bus, uint8_t slot, uint8_t func, uint64_t iova, uint64_t gpa, uint64_t mask) "dev %02x:%02x.%02x iova 0x%"PRIx64" -> gpa 0x%"PRIx64" mask 0x%"PRIx64 +vtd_dmar_enable(bool en) "enable %d" +vtd_dmar_fault(uint16_t sid, int fault, uint64_t addr, bool is_write) "sid 0x%"PRIx16" fault %d addr 0x%"PRIx64" write %d" +vtd_ir_enable(bool en) "enable %d" +vtd_ir_irte_get(int index, uint64_t lo, uint64_t hi) "index %d low 0x%"PRIx64" high 0x%"PRIx64 +vtd_ir_remap(int index, int tri, int vec, int deliver, uint32_t dest, int dest_mode) "index %d trigger %d vector %d deliver %d dest 0x%"PRIx32" mode %d" +vtd_ir_remap_type(const char *type) "%s" +vtd_ir_remap_msi(uint64_t addr, uint64_t data, uint64_t addr2, uint64_t data2) "(addr 0x%"PRIx64", data 0x%"PRIx64") -> (addr 0x%"PRIx64", data 0x%"PRIx64")" +vtd_ir_remap_msi_req(uint64_t addr, uint64_t data) "addr 0x%"PRIx64" data 0x%"PRIx64 +vtd_fsts_ppf(bool set) "FSTS PPF bit set to %d" +vtd_fsts_clear_ip(void) "" +vtd_frr_new(int index, uint64_t hi, uint64_t lo) "index %d high 0x%"PRIx64" low 0x%"PRIx64 +vtd_err(const char *str) "%s" +vtd_err_dmar_iova_overflow(uint64_t iova) "iova 0x%"PRIx64 +vtd_err_dmar_slpte_read_error(uint64_t iova, int level) "iova 0x%"PRIx64" level %d" +vtd_err_dmar_slpte_perm_error(uint64_t iova, int level, uint64_t slpte, bool is_write) "iova 0x%"PRIx64" level %d slpte 0x%"PRIx64" write %d" +vtd_err_dmar_slpte_resv_error(uint64_t iova, int level, uint64_t slpte) "iova 0x%"PRIx64" level %d slpte 0x%"PRIx64 +vtd_err_dmar_translate(uint8_t bus, uint8_t slot, uint8_t func, uint64_t iova) "dev %02x:%02x.%02x iova 0x%"PRIx64 +vtd_warn_invalid_qi_tail(uint16_t tail) "tail 0x%"PRIx16 +vtd_err_qi_disable(uint16_t head, uint16_t tail, int type) "head 0x%"PRIx16" tail 0x%"PRIx16" last_desc_type %d" +vtd_err_qi_tail(uint16_t tail, uint16_t size) "tail 0x%"PRIx16" size 0x%"PRIx16 +vtd_err_irte(int index, uint64_t lo, uint64_t hi) "index %d low 0x%"PRIx64" high 0x%"PRIx64 +vtd_err_irte_sid(int index, uint16_t req, uint16_t target) "index %d SVT_ALL sid 0x%"PRIx16" (should be: 0x%"PRIx16")" +vtd_err_irte_sid_bus(int index, uint8_t bus, uint8_t min, uint8_t max) "index %d SVT_BUS bus 0x%"PRIx8" (should be: 0x%"PRIx8"-0x%"PRIx8")" +vtd_err_irte_svt(int index, int type) "index %d SVT type %d" +vtd_err_ir_msi_invalid(uint16_t sid, uint64_t addr, uint64_t data) "sid 0x%"PRIx16" addr 0x%"PRIx64" data 0x%"PRIx64 +vtd_warn_ir_vector(uint16_t sid, int index, int vec, int target) "sid 0x%"PRIx16" index %d vec %d (should be: %d)" +vtd_warn_ir_trigger(uint16_t sid, int index, int trig, int target) "sid 0x%"PRIx16" index %d trigger %d (should be: %d)" # hw/i386/amd_iommu.c amdvi_evntlog_fail(uint64_t addr, uint32_t head) "error: fail to write at addr 0x%"PRIx64" + offset 0x%"PRIx32 diff --git a/hw/i386/x86-iommu.c b/hw/i386/x86-iommu.c index 23dcd3f039..293caf83ef 100644 --- a/hw/i386/x86-iommu.c +++ b/hw/i386/x86-iommu.c @@ -88,55 +88,23 @@ static void x86_iommu_realize(DeviceState *dev, Error **errp) x86_iommu_set_default(X86_IOMMU_DEVICE(dev)); } +static Property x86_iommu_properties[] = { + DEFINE_PROP_BOOL("intremap", X86IOMMUState, intr_supported, false), + DEFINE_PROP_BOOL("device-iotlb", X86IOMMUState, dt_supported, false), + DEFINE_PROP_BOOL("pt", X86IOMMUState, pt_supported, true), + DEFINE_PROP_END_OF_LIST(), +}; + static void x86_iommu_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = x86_iommu_realize; -} - -static bool x86_iommu_intremap_prop_get(Object *o, Error **errp) -{ - X86IOMMUState *s = X86_IOMMU_DEVICE(o); - return s->intr_supported; -} - -static void x86_iommu_intremap_prop_set(Object *o, bool value, Error **errp) -{ - X86IOMMUState *s = X86_IOMMU_DEVICE(o); - s->intr_supported = value; -} - -static bool x86_iommu_device_iotlb_prop_get(Object *o, Error **errp) -{ - X86IOMMUState *s = X86_IOMMU_DEVICE(o); - return s->dt_supported; -} - -static void x86_iommu_device_iotlb_prop_set(Object *o, bool value, Error **errp) -{ - X86IOMMUState *s = X86_IOMMU_DEVICE(o); - s->dt_supported = value; -} - -static void x86_iommu_instance_init(Object *o) -{ - X86IOMMUState *s = X86_IOMMU_DEVICE(o); - - /* By default, do not support IR */ - s->intr_supported = false; - object_property_add_bool(o, "intremap", x86_iommu_intremap_prop_get, - x86_iommu_intremap_prop_set, NULL); - s->dt_supported = false; - object_property_add_bool(o, "device-iotlb", - x86_iommu_device_iotlb_prop_get, - x86_iommu_device_iotlb_prop_set, - NULL); + dc->props = x86_iommu_properties; } static const TypeInfo x86_iommu_info = { .name = TYPE_X86_IOMMU_DEVICE, .parent = TYPE_SYS_BUS_DEVICE, - .instance_init = x86_iommu_instance_init, .instance_size = sizeof(X86IOMMUState), .class_init = x86_iommu_class_init, .class_size = sizeof(X86IOMMUClass), diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index b1c05ffb86..3d951a3794 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -18,7 +18,6 @@ #include "hw/xen/xen_backend.h" #include "qmp-commands.h" -#include "sysemu/char.h" #include "qemu/error-report.h" #include "qemu/range.h" #include "sysemu/xen-mapcache.h" @@ -183,9 +182,9 @@ static void xen_ram_init(PCMachineState *pcms, { MemoryRegion *sysmem = get_system_memory(); ram_addr_t block_len; - uint64_t user_lowmem = object_property_get_int(qdev_get_machine(), - PC_MACHINE_MAX_RAM_BELOW_4G, - &error_abort); + uint64_t user_lowmem = object_property_get_uint(qdev_get_machine(), + PC_MACHINE_MAX_RAM_BELOW_4G, + &error_abort); /* Handle the machine opt max-ram-below-4g. It is basically doing * min(xen limit, user limit). @@ -216,7 +215,6 @@ static void xen_ram_init(PCMachineState *pcms, memory_region_init_ram(&ram_memory, NULL, "xen.ram", block_len, &error_fatal); *ram_memory_p = &ram_memory; - vmstate_register_ram_global(&ram_memory); memory_region_init_alias(&ram_640k, NULL, "xen.ram.640k", &ram_memory, 0, 0xa0000); @@ -1089,11 +1087,14 @@ static void cpu_handle_ioreq(void *opaque) * causes Xen to powerdown the domain. */ if (runstate_is_running()) { + ShutdownCause request; + if (qemu_shutdown_requested_get()) { destroy_hvm_domain(false); } - if (qemu_reset_requested_get()) { - qemu_system_reset(VMRESET_REPORT); + request = qemu_reset_requested_get(); + if (request) { + qemu_system_reset(request); destroy_hvm_domain(true); } } @@ -1395,7 +1396,7 @@ void xen_shutdown_fatal_error(const char *fmt, ...) va_end(ap); fprintf(stderr, "Will destroy the domain.\n"); /* destroy the domain */ - qemu_system_shutdown_request(); + qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_ERROR); } void xen_hvm_modified_memory(ram_addr_t start, ram_addr_t length) diff --git a/hw/i386/xen/xen_platform.c b/hw/i386/xen/xen_platform.c index 1419fc96d2..f23155832b 100644 --- a/hw/i386/xen/xen_platform.c +++ b/hw/i386/xen/xen_platform.c @@ -102,8 +102,19 @@ static void unplug_nic(PCIBus *b, PCIDevice *d, void *o) } } +/* Remove the peer of the NIC device. Normally, this would be a tap device. */ +static void del_nic_peer(NICState *nic, void *opaque) +{ + NetClientState *nc; + + nc = qemu_get_queue(nic); + if (nc->peer) + qemu_del_net_client(nc->peer); +} + static void pci_unplug_nics(PCIBus *bus) { + qemu_foreach_nic(del_nic_peer, NULL); pci_for_each_device(bus, 0, unplug_nic, NULL); } diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index f60826d6e0..874d3fe280 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -1669,7 +1669,7 @@ const VMStateDescription vmstate_ahci = { VMSTATE_UINT32(control_regs.impl, AHCIState), VMSTATE_UINT32(control_regs.version, AHCIState), VMSTATE_UINT32(idp_index, AHCIState), - VMSTATE_INT32_EQUAL(ports, AHCIState), + VMSTATE_INT32_EQUAL(ports, AHCIState, NULL), VMSTATE_END_OF_LIST() }, }; diff --git a/hw/ide/ich.c b/hw/ide/ich.c index 459916977e..989fca5e9f 100644 --- a/hw/ide/ich.c +++ b/hw/ide/ich.c @@ -130,7 +130,7 @@ static void pci_ich9_ahci_realize(PCIDevice *dev, Error **errp) pci_register_bar(dev, ICH9_MEM_BAR, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->ahci.mem); - sata_cap_offset = pci_add_capability2(dev, PCI_CAP_ID_SATA, + sata_cap_offset = pci_add_capability(dev, PCI_CAP_ID_SATA, ICH9_SATA_CAP_OFFSET, SATA_CAP_SIZE, errp); if (sata_cap_offset < 0) { diff --git a/hw/input/hid.c b/hw/input/hid.c index 93887ecc43..0d049ff61c 100644 --- a/hw/input/hid.c +++ b/hw/input/hid.c @@ -487,6 +487,7 @@ void hid_reset(HIDState *hs) memset(hs->kbd.keycodes, 0, sizeof(hs->kbd.keycodes)); memset(hs->kbd.key, 0, sizeof(hs->kbd.key)); hs->kbd.keys = 0; + hs->kbd.modifiers = 0; break; case HID_MOUSE: case HID_TABLET: diff --git a/hw/input/milkymist-softusb.c b/hw/input/milkymist-softusb.c index 40dfca157f..ef8f47cd83 100644 --- a/hw/input/milkymist-softusb.c +++ b/hw/input/milkymist-softusb.c @@ -256,12 +256,12 @@ static int milkymist_softusb_init(SysBusDevice *dev) sysbus_init_mmio(dev, &s->regs_region); /* register pmem and dmem */ - memory_region_init_ram(&s->pmem, OBJECT(s), "milkymist-softusb.pmem", + memory_region_init_ram_nomigrate(&s->pmem, OBJECT(s), "milkymist-softusb.pmem", s->pmem_size, &error_fatal); vmstate_register_ram_global(&s->pmem); s->pmem_ptr = memory_region_get_ram_ptr(&s->pmem); sysbus_init_mmio(dev, &s->pmem); - memory_region_init_ram(&s->dmem, OBJECT(s), "milkymist-softusb.dmem", + memory_region_init_ram_nomigrate(&s->dmem, OBJECT(s), "milkymist-softusb.dmem", s->dmem_size, &error_fatal); vmstate_register_ram_global(&s->dmem); s->dmem_ptr = memory_region_get_ram_ptr(&s->dmem); diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c index d414288839..c479f827b6 100644 --- a/hw/input/pckbd.c +++ b/hw/input/pckbd.c @@ -226,7 +226,7 @@ static void outport_write(KBDState *s, uint32_t val) s->outport = val; qemu_set_irq(s->a20_out, (val >> 1) & 1); if (!(val & 1)) { - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); } } @@ -301,7 +301,7 @@ static void kbd_write_command(void *opaque, hwaddr addr, s->outport &= ~KBD_OUT_A20; break; case KBD_CCMD_RESET: - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); break; case KBD_CCMD_NO_OP: /* ignore that */ diff --git a/hw/input/ps2.c b/hw/input/ps2.c index 1d3a440bbd..3ba05efd06 100644 --- a/hw/input/ps2.c +++ b/hw/input/ps2.c @@ -85,12 +85,12 @@ typedef struct { int rptr, wptr, count; } PS2Queue; -typedef struct { +struct PS2State { PS2Queue queue; int32_t write_cmd; void (*update_irq)(void *, int); void *update_arg; -} PS2State; +}; typedef struct { PS2State common; @@ -551,9 +551,17 @@ static uint8_t translate_table[256] = { 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, }; -void ps2_queue(void *opaque, int b) +static void ps2_reset_queue(PS2State *s) +{ + PS2Queue *q = &s->queue; + + q->rptr = 0; + q->wptr = 0; + q->count = 0; +} + +void ps2_queue(PS2State *s, int b) { - PS2State *s = (PS2State *)opaque; PS2Queue *q = &s->queue; if (q->count >= PS2_QUEUE_SIZE - 1) @@ -692,13 +700,12 @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, } } -uint32_t ps2_read_data(void *opaque) +uint32_t ps2_read_data(PS2State *s) { - PS2State *s = (PS2State *)opaque; PS2Queue *q; int val, index; - trace_ps2_read_data(opaque); + trace_ps2_read_data(s); q = &s->queue; if (q->count == 0) { /* NOTE: if no data left, we return the last keyboard one @@ -733,6 +740,7 @@ static void ps2_reset_keyboard(PS2KbdState *s) trace_ps2_reset_keyboard(s); s->scan_enabled = 1; s->scancode_set = 2; + ps2_reset_queue(&s->common); ps2_set_ledstate(s, 0); } @@ -1081,12 +1089,8 @@ void ps2_write_mouse(void *opaque, int val) static void ps2_common_reset(PS2State *s) { - PS2Queue *q; s->write_cmd = -1; - q = &s->queue; - q->rptr = 0; - q->wptr = 0; - q->count = 0; + ps2_reset_queue(s); s->update_irq(s->update_arg, 0); } diff --git a/hw/input/vmmouse.c b/hw/input/vmmouse.c index 4747da9a8d..b6d22086f4 100644 --- a/hw/input/vmmouse.c +++ b/hw/input/vmmouse.c @@ -243,7 +243,7 @@ static const VMStateDescription vmstate_vmmouse = { .minimum_version_id = 0, .post_load = vmmouse_post_load, .fields = (VMStateField[]) { - VMSTATE_INT32_EQUAL(queue_size, VMMouseState), + VMSTATE_INT32_EQUAL(queue_size, VMMouseState, NULL), VMSTATE_UINT32_ARRAY(queue, VMMouseState, VMMOUSE_QUEUE_SIZE), VMSTATE_UINT16(nb_queue, VMMouseState), VMSTATE_UINT16(status, VMMouseState), diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c index 1ef56f8d10..e1ac33042f 100644 --- a/hw/intc/apic_common.c +++ b/hw/intc/apic_common.c @@ -450,10 +450,10 @@ static void apic_common_get_id(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { APICCommonState *s = APIC_COMMON(obj); - int64_t value; + uint32_t value; value = s->apicbase & MSR_IA32_APICBASE_EXTD ? s->initial_apic_id : s->id; - visit_type_int(v, name, &value, errp); + visit_type_uint32(v, name, &value, errp); } static void apic_common_set_id(Object *obj, Visitor *v, const char *name, @@ -462,14 +462,14 @@ static void apic_common_set_id(Object *obj, Visitor *v, const char *name, APICCommonState *s = APIC_COMMON(obj); DeviceState *dev = DEVICE(obj); Error *local_err = NULL; - int64_t value; + uint32_t value; if (dev->realized) { qdev_prop_set_after_realize(dev, name, errp); return; } - visit_type_int(v, name, &value, &local_err); + visit_type_uint32(v, name, &value, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -484,7 +484,7 @@ static void apic_common_initfn(Object *obj) APICCommonState *s = APIC_COMMON(obj); s->id = s->initial_apic_id = -1; - object_property_add(obj, "id", "int", + object_property_add(obj, "id", "uint32", apic_common_get_id, apic_common_set_id, NULL, NULL, NULL); } diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index b305d9032a..5a0e2a3c1a 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -25,6 +25,7 @@ #include "qom/cpu.h" #include "qemu/log.h" #include "trace.h" +#include "sysemu/kvm.h" /* #define DEBUG_GIC */ @@ -1412,6 +1413,12 @@ static void arm_gic_realize(DeviceState *dev, Error **errp) return; } + if (kvm_enabled() && !kvm_arm_supports_user_irq()) { + error_setg(errp, "KVM with user space irqchip only works when the " + "host kernel supports KVM_CAP_ARM_USER_IRQ"); + return; + } + /* This creates distributor and main CPU interface (s->cpuiomem[0]) */ gic_init_irqs_and_mmio(s, gic_set_irq, gic_ops); diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c index af5cd367e9..ae095d08a3 100644 --- a/hw/intc/arm_gic_kvm.c +++ b/hw/intc/arm_gic_kvm.c @@ -100,14 +100,14 @@ static void kvm_gicd_access(GICState *s, int offset, int cpu, uint32_t *val, bool write) { kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS, - KVM_VGIC_ATTR(offset, cpu), val, write); + KVM_VGIC_ATTR(offset, cpu), val, write, &error_abort); } static void kvm_gicc_access(GICState *s, int offset, int cpu, uint32_t *val, bool write) { kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CPU_REGS, - KVM_VGIC_ATTR(offset, cpu), val, write); + KVM_VGIC_ATTR(offset, cpu), val, write, &error_abort); } #define for_each_irq_reg(_ctr, _max_irq, _field_width) \ @@ -538,13 +538,14 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp) if (kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0)) { uint32_t numirqs = s->num_irq; kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0, - &numirqs, true); + &numirqs, true, &error_abort); } /* Tell the kernel to complete VGIC initialization now */ if (kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, KVM_DEV_ARM_VGIC_CTRL_INIT)) { kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, - KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true); + KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true, + &error_abort); } } else if (ret != -ENODEV && ret != -ENOTSUP) { error_setg_errno(errp, -ret, "error creating in-kernel VGIC"); diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index c6493d6c07..410915a2ac 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -145,6 +145,7 @@ static const VMStateDescription vmstate_gicv3 = { .minimum_version_id = 1, .pre_save = gicv3_pre_save, .post_load = gicv3_post_load, + .priority = MIG_PRI_GICV3, .fields = (VMStateField[]) { VMSTATE_UINT32(gicd_ctlr, GICv3State), VMSTATE_UINT32_ARRAY(gicd_statusr, GICv3State, 2), @@ -267,7 +268,7 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp) * VLPIS == 0 (virtual LPIs not supported) * PLPIS == 0 (physical LPIs not supported) */ - cpu_affid = object_property_get_int(OBJECT(cpu), "mp-affinity", NULL); + cpu_affid = object_property_get_uint(OBJECT(cpu), "mp-affinity", NULL); last = (i == s->num_cpu - 1); /* The CPU mp-affinity property is in MPIDR register format; squash diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index 0b208560bd..5cbafaf497 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -216,18 +216,35 @@ static uint32_t icv_gprio_mask(GICv3CPUState *cs, int group) { /* Return a mask word which clears the subpriority bits from * a priority value for a virtual interrupt in the specified group. - * This depends on the VBPR value: + * This depends on the VBPR value. + * If using VBPR0 then: * a BPR of 0 means the group priority bits are [7:1]; * a BPR of 1 means they are [7:2], and so on down to * a BPR of 7 meaning no group priority bits at all. + * If using VBPR1 then: + * a BPR of 0 is impossible (the minimum value is 1) + * a BPR of 1 means the group priority bits are [7:1]; + * a BPR of 2 means they are [7:2], and so on down to + * a BPR of 7 meaning the group priority is [7]. + * * Which BPR to use depends on the group of the interrupt and * the current ICH_VMCR_EL2.VCBPR settings. + * + * This corresponds to the VGroupBits() pseudocode. */ + int bpr; + if (group == GICV3_G1NS && cs->ich_vmcr_el2 & ICH_VMCR_EL2_VCBPR) { group = GICV3_G0; } - return ~0U << (read_vbpr(cs, group) + 1); + bpr = read_vbpr(cs, group); + if (group == GICV3_G1NS) { + assert(bpr > 0); + bpr--; + } + + return ~0U << (bpr + 1); } static bool icv_hppi_can_preempt(GICv3CPUState *cs, uint64_t lr) @@ -674,20 +691,37 @@ static uint32_t icc_gprio_mask(GICv3CPUState *cs, int group) { /* Return a mask word which clears the subpriority bits from * a priority value for an interrupt in the specified group. - * This depends on the BPR value: + * This depends on the BPR value. For CBPR0 (S or NS): * a BPR of 0 means the group priority bits are [7:1]; * a BPR of 1 means they are [7:2], and so on down to * a BPR of 7 meaning no group priority bits at all. + * For CBPR1 NS: + * a BPR of 0 is impossible (the minimum value is 1) + * a BPR of 1 means the group priority bits are [7:1]; + * a BPR of 2 means they are [7:2], and so on down to + * a BPR of 7 meaning the group priority is [7]. + * * Which BPR to use depends on the group of the interrupt and * the current ICC_CTLR.CBPR settings. + * + * This corresponds to the GroupBits() pseudocode. */ + int bpr; + if ((group == GICV3_G1 && cs->icc_ctlr_el1[GICV3_S] & ICC_CTLR_EL1_CBPR) || (group == GICV3_G1NS && cs->icc_ctlr_el1[GICV3_NS] & ICC_CTLR_EL1_CBPR)) { group = GICV3_G0; } - return ~0U << ((cs->icc_bpr[group] & 7) + 1); + bpr = cs->icc_bpr[group] & 7; + + if (group == GICV3_G1NS) { + assert(bpr > 0); + bpr--; + } + + return ~0U << (bpr + 1); } static bool icc_no_enabled_hppi(GICv3CPUState *cs) @@ -1388,6 +1422,7 @@ static void icc_bpr_write(CPUARMState *env, const ARMCPRegInfo *ri, { GICv3CPUState *cs = icc_cs_from_env(env); int grp = (ri->crm == 8) ? GICV3_G0 : GICV3_G1; + uint64_t minval; if (icv_access(env, grp == GICV3_G0 ? HCR_FMO : HCR_IMO)) { icv_bpr_write(env, ri, value); @@ -1415,6 +1450,11 @@ static void icc_bpr_write(CPUARMState *env, const ARMCPRegInfo *ri, return; } + minval = (grp == GICV3_G1NS) ? GIC_MIN_BPR_NS : GIC_MIN_BPR; + if (value < minval) { + value = minval; + } + cs->icc_bpr[grp] = value & 7; gicv3_cpuif_update(cs); } @@ -1999,11 +2039,7 @@ static void icc_reset(CPUARMState *env, const ARMCPRegInfo *ri) cs->icc_pmr_el1 = 0; cs->icc_bpr[GICV3_G0] = GIC_MIN_BPR; cs->icc_bpr[GICV3_G1] = GIC_MIN_BPR; - if (arm_feature(env, ARM_FEATURE_EL3)) { - cs->icc_bpr[GICV3_G1NS] = GIC_MIN_BPR_NS; - } else { - cs->icc_bpr[GICV3_G1NS] = GIC_MIN_BPR; - } + cs->icc_bpr[GICV3_G1NS] = GIC_MIN_BPR_NS; memset(cs->icc_apr, 0, sizeof(cs->icc_apr)); memset(cs->icc_igrpen, 0, sizeof(cs->icc_igrpen)); cs->icc_ctlr_el3 = ICC_CTLR_EL3_NDS | ICC_CTLR_EL3_A3V | @@ -2014,7 +2050,7 @@ static void icc_reset(CPUARMState *env, const ARMCPRegInfo *ri) cs->ich_hcr_el2 = 0; memset(cs->ich_lr_el2, 0, sizeof(cs->ich_lr_el2)); cs->ich_vmcr_el2 = ICH_VMCR_EL2_VFIQEN | - (icv_min_vbpr(cs) << ICH_VMCR_EL2_VBPR1_SHIFT) | + ((icv_min_vbpr(cs) + 1) << ICH_VMCR_EL2_VBPR1_SHIFT) | (icv_min_vbpr(cs) << ICH_VMCR_EL2_VBPR0_SHIFT); } diff --git a/hw/intc/arm_gicv3_its_common.c b/hw/intc/arm_gicv3_its_common.c index 9d67c5c1ee..68b20fccd1 100644 --- a/hw/intc/arm_gicv3_its_common.c +++ b/hw/intc/arm_gicv3_its_common.c @@ -48,7 +48,16 @@ static const VMStateDescription vmstate_its = { .name = "arm_gicv3_its", .pre_save = gicv3_its_pre_save, .post_load = gicv3_its_post_load, - .unmigratable = true, + .priority = MIG_PRI_GICV3_ITS, + .fields = (VMStateField[]) { + VMSTATE_UINT32(ctlr, GICv3ITSState), + VMSTATE_UINT32(iidr, GICv3ITSState), + VMSTATE_UINT64(cbaser, GICv3ITSState), + VMSTATE_UINT64(cwriter, GICv3ITSState), + VMSTATE_UINT64(creadr, GICv3ITSState), + VMSTATE_UINT64_ARRAY(baser, GICv3ITSState, 8), + VMSTATE_END_OF_LIST() + }, }; static MemTxResult gicv3_its_trans_read(void *opaque, hwaddr offset, @@ -118,6 +127,7 @@ static void gicv3_its_common_reset(DeviceState *dev) s->cbaser = 0; s->cwriter = 0; s->creadr = 0; + s->iidr = 0; memset(&s->baser, 0, sizeof(s->baser)); gicv3_its_post_load(s, 0); diff --git a/hw/intc/arm_gicv3_its_kvm.c b/hw/intc/arm_gicv3_its_kvm.c index a0441d6bd1..1f8991b8a6 100644 --- a/hw/intc/arm_gicv3_its_kvm.c +++ b/hw/intc/arm_gicv3_its_kvm.c @@ -53,23 +53,38 @@ static int kvm_its_send_msi(GICv3ITSState *s, uint32_t value, uint16_t devid) return kvm_vm_ioctl(kvm_state, KVM_SIGNAL_MSI, &msi); } -static void kvm_arm_its_realize(DeviceState *dev, Error **errp) +/** + * vm_change_state_handler - VM change state callback aiming at flushing + * ITS tables into guest RAM + * + * The tables get flushed to guest RAM whenever the VM gets stopped. + */ +static void vm_change_state_handler(void *opaque, int running, + RunState state) { - GICv3ITSState *s = ARM_GICV3_ITS_COMMON(dev); - Error *local_err = NULL; + GICv3ITSState *s = (GICv3ITSState *)opaque; + Error *err = NULL; + int ret; - /* - * Block migration of a KVM GICv3 ITS device: the API for saving and - * restoring the state in the kernel is not yet available - */ - error_setg(&s->migration_blocker, "vITS migration is not implemented"); - migrate_add_blocker(s->migration_blocker, &local_err); - if (local_err) { - error_propagate(errp, local_err); - error_free(s->migration_blocker); + if (running) { return; } + ret = kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, + KVM_DEV_ARM_ITS_SAVE_TABLES, NULL, true, &err); + if (err) { + error_report_err(err); + } + if (ret < 0 && ret != -EFAULT) { + abort(); + } +} + +static void kvm_arm_its_realize(DeviceState *dev, Error **errp) +{ + GICv3ITSState *s = ARM_GICV3_ITS_COMMON(dev); + Error *local_err = NULL; + s->dev_fd = kvm_create_device(kvm_state, KVM_DEV_TYPE_ARM_VGIC_ITS, false); if (s->dev_fd < 0) { error_setg_errno(errp, -s->dev_fd, "error creating in-kernel ITS"); @@ -78,7 +93,7 @@ static void kvm_arm_its_realize(DeviceState *dev, Error **errp) /* explicit init of the ITS */ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, - KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true); + KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true, &error_abort); /* register the base address */ kvm_arm_register_device(&s->iomem_its_cntrl, -1, KVM_DEV_ARM_VGIC_GRP_ADDR, @@ -86,9 +101,23 @@ static void kvm_arm_its_realize(DeviceState *dev, Error **errp) gicv3_its_init_mmio(s, NULL); + if (!kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS, + GITS_CTLR)) { + error_setg(&s->migration_blocker, "This operating system kernel " + "does not support vITS migration"); + migrate_add_blocker(s->migration_blocker, &local_err); + if (local_err) { + error_propagate(errp, local_err); + error_free(s->migration_blocker); + return; + } + } + kvm_msi_use_devid = true; kvm_gsi_direct_mapping = false; kvm_msi_via_irqfd_allowed = kvm_irqfds_enabled(); + + qemu_add_vm_change_state_handler(vm_change_state_handler, s); } static void kvm_arm_its_init(Object *obj) @@ -102,6 +131,80 @@ static void kvm_arm_its_init(Object *obj) &error_abort); } +/** + * kvm_arm_its_pre_save - handles the saving of ITS registers. + * ITS tables are flushed into guest RAM separately and earlier, + * through the VM change state handler, since at the moment pre_save() + * is called, the guest RAM has already been saved. + */ +static void kvm_arm_its_pre_save(GICv3ITSState *s) +{ + int i; + + for (i = 0; i < 8; i++) { + kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS, + GITS_BASER + i * 8, &s->baser[i], false, + &error_abort); + } + + kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS, + GITS_CTLR, &s->ctlr, false, &error_abort); + + kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS, + GITS_CBASER, &s->cbaser, false, &error_abort); + + kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS, + GITS_CREADR, &s->creadr, false, &error_abort); + + kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS, + GITS_CWRITER, &s->cwriter, false, &error_abort); + + kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS, + GITS_IIDR, &s->iidr, false, &error_abort); +} + +/** + * kvm_arm_its_post_load - Restore both the ITS registers and tables + */ +static void kvm_arm_its_post_load(GICv3ITSState *s) +{ + int i; + + if (!s->iidr) { + return; + } + + kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS, + GITS_IIDR, &s->iidr, true, &error_abort); + + /* + * must be written before GITS_CREADR since GITS_CBASER write + * access resets GITS_CREADR. + */ + kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS, + GITS_CBASER, &s->cbaser, true, &error_abort); + + kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS, + GITS_CREADR, &s->creadr, true, &error_abort); + + kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS, + GITS_CWRITER, &s->cwriter, true, &error_abort); + + + for (i = 0; i < 8; i++) { + kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS, + GITS_BASER + i * 8, &s->baser[i], true, + &error_abort); + } + + kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, + KVM_DEV_ARM_ITS_RESTORE_TABLES, NULL, true, + &error_abort); + + kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS, + GITS_CTLR, &s->ctlr, true, &error_abort); +} + static void kvm_arm_its_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -109,6 +212,8 @@ static void kvm_arm_its_class_init(ObjectClass *klass, void *data) dc->realize = kvm_arm_its_realize; icc->send_msi = kvm_its_send_msi; + icc->pre_save = kvm_arm_its_pre_save; + icc->post_load = kvm_arm_its_post_load; } static const TypeInfo kvm_arm_its_info = { diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index 4ee2baa691..6051c77705 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -25,6 +25,7 @@ #include "hw/sysbus.h" #include "qemu/error-report.h" #include "sysemu/kvm.h" +#include "sysemu/sysemu.h" #include "kvm_arm.h" #include "gicv3_internal.h" #include "vgic_common.h" @@ -93,7 +94,7 @@ static inline void kvm_gicd_access(GICv3State *s, int offset, { kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS, KVM_VGIC_ATTR(offset, 0), - val, write); + val, write, &error_abort); } static inline void kvm_gicr_access(GICv3State *s, int offset, int cpu, @@ -101,7 +102,7 @@ static inline void kvm_gicr_access(GICv3State *s, int offset, int cpu, { kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_REDIST_REGS, KVM_VGIC_ATTR(offset, s->cpu[cpu].gicr_typer), - val, write); + val, write, &error_abort); } static inline void kvm_gicc_access(GICv3State *s, uint64_t reg, int cpu, @@ -109,7 +110,7 @@ static inline void kvm_gicc_access(GICv3State *s, uint64_t reg, int cpu, { kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS, KVM_VGIC_ATTR(reg, s->cpu[cpu].gicr_typer), - val, write); + val, write, &error_abort); } static inline void kvm_gic_line_level_access(GICv3State *s, int irq, int cpu, @@ -119,7 +120,7 @@ static inline void kvm_gic_line_level_access(GICv3State *s, int irq, int cpu, KVM_VGIC_ATTR(irq, s->cpu[cpu].gicr_typer) | (VGIC_LEVEL_INFO_LINE_LEVEL << KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_SHIFT), - val, write); + val, write, &error_abort); } /* Loop through each distributor IRQ related register; since bits @@ -630,7 +631,7 @@ static void arm_gicv3_icc_reset(CPUARMState *env, const ARMCPRegInfo *ri) /* Initialize to actual HW supported configuration */ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS, KVM_VGIC_ATTR(ICC_CTLR_EL1, cpu->mp_affinity), - &c->icc_ctlr_el1[GICV3_NS], false); + &c->icc_ctlr_el1[GICV3_NS], false, &error_abort); c->icc_ctlr_el1[GICV3_S] = c->icc_ctlr_el1[GICV3_NS]; } @@ -680,6 +681,35 @@ static const ARMCPRegInfo gicv3_cpuif_reginfo[] = { REGINFO_SENTINEL }; +/** + * vm_change_state_handler - VM change state callback aiming at flushing + * RDIST pending tables into guest RAM + * + * The tables get flushed to guest RAM whenever the VM gets stopped. + */ +static void vm_change_state_handler(void *opaque, int running, + RunState state) +{ + GICv3State *s = (GICv3State *)opaque; + Error *err = NULL; + int ret; + + if (running) { + return; + } + + ret = kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, + KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES, + NULL, true, &err); + if (err) { + error_report_err(err); + } + if (ret < 0 && ret != -EFAULT) { + abort(); + } +} + + static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) { GICv3State *s = KVM_ARM_GICV3(dev); @@ -717,11 +747,11 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) } kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, - 0, &s->num_irq, true); + 0, &s->num_irq, true, &error_abort); /* Tell the kernel to complete VGIC initialization now */ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, - KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true); + KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true, &error_abort); kvm_arm_register_device(&s->iomem_dist, -1, KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V3_ADDR_TYPE_DIST, s->dev_fd); @@ -751,6 +781,10 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) return; } } + if (kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, + KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES)) { + qemu_add_vm_change_state_handler(vm_change_state_handler, s); + } } static void kvm_arm_gicv3_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 32ffa0bf35..26a4b2dcb5 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -19,6 +19,7 @@ #include "hw/arm/arm.h" #include "hw/arm/armv7m_nvic.h" #include "target/arm/cpu.h" +#include "exec/exec-all.h" #include "qemu/log.h" #include "trace.h" @@ -528,6 +529,39 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset) case 0xd70: /* ISAR4. */ return 0x01310102; /* TODO: Implement debug registers. */ + case 0xd90: /* MPU_TYPE */ + /* Unified MPU; if the MPU is not present this value is zero */ + return cpu->pmsav7_dregion << 8; + break; + case 0xd94: /* MPU_CTRL */ + return cpu->env.v7m.mpu_ctrl; + case 0xd98: /* MPU_RNR */ + return cpu->env.cp15.c6_rgnr; + case 0xd9c: /* MPU_RBAR */ + case 0xda4: /* MPU_RBAR_A1 */ + case 0xdac: /* MPU_RBAR_A2 */ + case 0xdb4: /* MPU_RBAR_A3 */ + { + int region = cpu->env.cp15.c6_rgnr; + + if (region >= cpu->pmsav7_dregion) { + return 0; + } + return (cpu->env.pmsav7.drbar[region] & 0x1f) | (region & 0xf); + } + case 0xda0: /* MPU_RASR */ + case 0xda8: /* MPU_RASR_A1 */ + case 0xdb0: /* MPU_RASR_A2 */ + case 0xdb8: /* MPU_RASR_A3 */ + { + int region = cpu->env.cp15.c6_rgnr; + + if (region >= cpu->pmsav7_dregion) { + return 0; + } + return ((cpu->env.pmsav7.dracr[region] & 0xffff) << 16) | + (cpu->env.pmsav7.drsr[region] & 0xffff); + } default: qemu_log_mask(LOG_GUEST_ERROR, "NVIC: Bad read offset 0x%x\n", offset); return 0; @@ -627,6 +661,76 @@ static void nvic_writel(NVICState *s, uint32_t offset, uint32_t value) qemu_log_mask(LOG_UNIMP, "NVIC: Aux fault status registers unimplemented\n"); break; + case 0xd90: /* MPU_TYPE */ + return; /* RO */ + case 0xd94: /* MPU_CTRL */ + if ((value & + (R_V7M_MPU_CTRL_HFNMIENA_MASK | R_V7M_MPU_CTRL_ENABLE_MASK)) + == R_V7M_MPU_CTRL_HFNMIENA_MASK) { + qemu_log_mask(LOG_GUEST_ERROR, "MPU_CTRL: HFNMIENA and !ENABLE is " + "UNPREDICTABLE\n"); + } + cpu->env.v7m.mpu_ctrl = value & (R_V7M_MPU_CTRL_ENABLE_MASK | + R_V7M_MPU_CTRL_HFNMIENA_MASK | + R_V7M_MPU_CTRL_PRIVDEFENA_MASK); + tlb_flush(CPU(cpu)); + break; + case 0xd98: /* MPU_RNR */ + if (value >= cpu->pmsav7_dregion) { + qemu_log_mask(LOG_GUEST_ERROR, "MPU region out of range %" + PRIu32 "/%" PRIu32 "\n", + value, cpu->pmsav7_dregion); + } else { + cpu->env.cp15.c6_rgnr = value; + } + break; + case 0xd9c: /* MPU_RBAR */ + case 0xda4: /* MPU_RBAR_A1 */ + case 0xdac: /* MPU_RBAR_A2 */ + case 0xdb4: /* MPU_RBAR_A3 */ + { + int region; + + if (value & (1 << 4)) { + /* VALID bit means use the region number specified in this + * value and also update MPU_RNR.REGION with that value. + */ + region = extract32(value, 0, 4); + if (region >= cpu->pmsav7_dregion) { + qemu_log_mask(LOG_GUEST_ERROR, + "MPU region out of range %u/%" PRIu32 "\n", + region, cpu->pmsav7_dregion); + return; + } + cpu->env.cp15.c6_rgnr = region; + } else { + region = cpu->env.cp15.c6_rgnr; + } + + if (region >= cpu->pmsav7_dregion) { + return; + } + + cpu->env.pmsav7.drbar[region] = value & ~0x1f; + tlb_flush(CPU(cpu)); + break; + } + case 0xda0: /* MPU_RASR */ + case 0xda8: /* MPU_RASR_A1 */ + case 0xdb0: /* MPU_RASR_A2 */ + case 0xdb8: /* MPU_RASR_A3 */ + { + int region = cpu->env.cp15.c6_rgnr; + + if (region >= cpu->pmsav7_dregion) { + return; + } + + cpu->env.pmsav7.drsr[region] = value & 0xff3f; + cpu->env.pmsav7.dracr[region] = (value >> 16) & 0x173f; + tlb_flush(CPU(cpu)); + break; + } case 0xf00: /* Software Triggered Interrupt Register */ { /* user mode can only write to STIR if CCR.USERSETMPEND permits it */ diff --git a/hw/intc/exynos4210_gic.c b/hw/intc/exynos4210_gic.c index 2a55817b76..b6b00a4f58 100644 --- a/hw/intc/exynos4210_gic.c +++ b/hw/intc/exynos4210_gic.c @@ -116,7 +116,7 @@ enum ExtInt { * which is INTG16 in Internal Interrupt Combiner. */ -static uint32_t +static const uint32_t combiner_grp_to_gic_id[64-EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ][8] = { /* int combiner groups 16-19 */ { }, { }, { }, { }, @@ -286,21 +286,21 @@ static void exynos4210_gic_init(Object *obj) DeviceState *dev = DEVICE(obj); Exynos4210GicState *s = EXYNOS4210_GIC(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - uint32_t i; const char cpu_prefix[] = "exynos4210-gic-alias_cpu"; const char dist_prefix[] = "exynos4210-gic-alias_dist"; char cpu_alias_name[sizeof(cpu_prefix) + 3]; char dist_alias_name[sizeof(cpu_prefix) + 3]; - SysBusDevice *busdev; + SysBusDevice *gicbusdev; + uint32_t i; s->gic = qdev_create(NULL, "arm_gic"); qdev_prop_set_uint32(s->gic, "num-cpu", s->num_cpu); qdev_prop_set_uint32(s->gic, "num-irq", EXYNOS4210_GIC_NIRQ); qdev_init_nofail(s->gic); - busdev = SYS_BUS_DEVICE(s->gic); + gicbusdev = SYS_BUS_DEVICE(s->gic); /* Pass through outbound IRQ lines from the GIC */ - sysbus_pass_irq(sbd, busdev); + sysbus_pass_irq(sbd, gicbusdev); /* Pass through inbound GPIO lines to the GIC */ qdev_init_gpio_in(dev, exynos4210_gic_set_irq, @@ -316,7 +316,7 @@ static void exynos4210_gic_init(Object *obj) sprintf(cpu_alias_name, "%s%x", cpu_prefix, i); memory_region_init_alias(&s->cpu_alias[i], obj, cpu_alias_name, - sysbus_mmio_get_region(busdev, 1), + sysbus_mmio_get_region(gicbusdev, 1), 0, EXYNOS4210_GIC_CPU_REGION_SIZE); memory_region_add_subregion(&s->cpu_container, @@ -326,7 +326,7 @@ static void exynos4210_gic_init(Object *obj) sprintf(dist_alias_name, "%s%x", dist_prefix, i); memory_region_init_alias(&s->dist_alias[i], obj, dist_alias_name, - sysbus_mmio_get_region(busdev, 0), + sysbus_mmio_get_region(gicbusdev, 0), 0, EXYNOS4210_GIC_DIST_REGION_SIZE); memory_region_add_subregion(&s->dist_container, diff --git a/hw/intc/openpic.c b/hw/intc/openpic.c index 4349e45e04..9dd285b923 100644 --- a/hw/intc/openpic.c +++ b/hw/intc/openpic.c @@ -45,6 +45,7 @@ #include "qemu/bitops.h" #include "qapi/qmp/qerror.h" #include "qemu/log.h" +#include "qemu/timer.h" //#define DEBUG_OPENPIC @@ -54,8 +55,10 @@ static const int debug_openpic = 1; static const int debug_openpic = 0; #endif +static int get_current_cpu(void); #define DPRINTF(fmt, ...) do { \ if (debug_openpic) { \ + printf("Core%d: ", get_current_cpu()); \ printf(fmt , ## __VA_ARGS__); \ } \ } while (0) @@ -246,9 +249,31 @@ typedef struct IRQSource { #define IDR_EP 0x80000000 /* external pin */ #define IDR_CI 0x40000000 /* critical interrupt */ +/* Convert between openpic clock ticks and nanosecs. In the hardware the clock + frequency is driven by board inputs to the PIC which the PIC would then + divide by 4 or 8. For now hard code to 25MZ. +*/ +#define OPENPIC_TIMER_FREQ_MHZ 25 +#define OPENPIC_TIMER_NS_PER_TICK (1000 / OPENPIC_TIMER_FREQ_MHZ) +static inline uint64_t ns_to_ticks(uint64_t ns) +{ + return ns / OPENPIC_TIMER_NS_PER_TICK; +} +static inline uint64_t ticks_to_ns(uint64_t ticks) +{ + return ticks * OPENPIC_TIMER_NS_PER_TICK; +} + typedef struct OpenPICTimer { uint32_t tccr; /* Global timer current count register */ uint32_t tbcr; /* Global timer base count register */ + int n_IRQ; + bool qemu_timer_active; /* Is the qemu_timer is running? */ + struct QEMUTimer *qemu_timer; + struct OpenPICState *opp; /* Device timer is part of. */ + /* The QEMU_CLOCK_VIRTUAL time (in ns) corresponding to the last + current_count written or read, only defined if qemu_timer_active. */ + uint64_t origin_time; } OpenPICTimer; typedef struct OpenPICMSI { @@ -795,37 +820,98 @@ static uint64_t openpic_gbl_read(void *opaque, hwaddr addr, unsigned len) return retval; } +static void openpic_tmr_set_tmr(OpenPICTimer *tmr, uint32_t val, bool enabled); + +static void qemu_timer_cb(void *opaque) +{ + OpenPICTimer *tmr = opaque; + OpenPICState *opp = tmr->opp; + uint32_t n_IRQ = tmr->n_IRQ; + uint32_t val = tmr->tbcr & ~TBCR_CI; + uint32_t tog = ((tmr->tccr & TCCR_TOG) ^ TCCR_TOG); /* invert toggle. */ + + DPRINTF("%s n_IRQ=%d\n", __func__, n_IRQ); + /* Reload current count from base count and setup timer. */ + tmr->tccr = val | tog; + openpic_tmr_set_tmr(tmr, val, /*enabled=*/true); + /* Raise the interrupt. */ + opp->src[n_IRQ].destmask = read_IRQreg_idr(opp, n_IRQ); + openpic_set_irq(opp, n_IRQ, 1); + openpic_set_irq(opp, n_IRQ, 0); +} + +/* If enabled is true, arranges for an interrupt to be raised val clocks into + the future, if enabled is false cancels the timer. */ +static void openpic_tmr_set_tmr(OpenPICTimer *tmr, uint32_t val, bool enabled) +{ + uint64_t ns = ticks_to_ns(val & ~TCCR_TOG); + /* A count of zero causes a timer to be set to expire immediately. This + effectively stops the simulation since the timer is constantly expiring + which prevents guest code execution, so we don't honor that + configuration. On real hardware, this situation would generate an + interrupt on every clock cycle if the interrupt was unmasked. */ + if ((ns == 0) || !enabled) { + tmr->qemu_timer_active = false; + tmr->tccr = tmr->tccr & TCCR_TOG; + timer_del(tmr->qemu_timer); /* set timer to never expire. */ + } else { + tmr->qemu_timer_active = true; + uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + tmr->origin_time = now; + timer_mod(tmr->qemu_timer, now + ns); /* set timer expiration. */ + } +} + +/* Returns the currrent tccr value, i.e., timer value (in clocks) with + appropriate TOG. */ +static uint64_t openpic_tmr_get_timer(OpenPICTimer *tmr) +{ + uint64_t retval; + if (!tmr->qemu_timer_active) { + retval = tmr->tccr; + } else { + uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + uint64_t used = now - tmr->origin_time; /* nsecs */ + uint32_t used_ticks = (uint32_t)ns_to_ticks(used); + uint32_t count = (tmr->tccr & ~TCCR_TOG) - used_ticks; + retval = (uint32_t)((tmr->tccr & TCCR_TOG) | (count & ~TCCR_TOG)); + } + return retval; +} + static void openpic_tmr_write(void *opaque, hwaddr addr, uint64_t val, - unsigned len) + unsigned len) { OpenPICState *opp = opaque; int idx; - addr += 0x10f0; - DPRINTF("%s: addr %#" HWADDR_PRIx " <= %08" PRIx64 "\n", - __func__, addr, val); + __func__, (addr + 0x10f0), val); if (addr & 0xF) { return; } - if (addr == 0x10f0) { + if (addr == 0) { /* TFRR */ opp->tfrr = val; return; } - + addr -= 0x10; /* correct for TFRR */ idx = (addr >> 6) & 0x3; - addr = addr & 0x30; switch (addr & 0x30) { case 0x00: /* TCCR */ break; case 0x10: /* TBCR */ - if ((opp->timers[idx].tccr & TCCR_TOG) != 0 && - (val & TBCR_CI) == 0 && - (opp->timers[idx].tbcr & TBCR_CI) != 0) { - opp->timers[idx].tccr &= ~TCCR_TOG; + /* Did the enable status change? */ + if ((opp->timers[idx].tbcr & TBCR_CI) != (val & TBCR_CI)) { + /* Did "Count Inhibit" transition from 1 to 0? */ + if ((val & TBCR_CI) == 0) { + opp->timers[idx].tccr = val & ~TCCR_TOG; + } + openpic_tmr_set_tmr(&opp->timers[idx], + (val & ~TBCR_CI), + /*enabled=*/((val & TBCR_CI) == 0)); } opp->timers[idx].tbcr = val; break; @@ -844,27 +930,28 @@ static uint64_t openpic_tmr_read(void *opaque, hwaddr addr, unsigned len) uint32_t retval = -1; int idx; - DPRINTF("%s: addr %#" HWADDR_PRIx "\n", __func__, addr); + DPRINTF("%s: addr %#" HWADDR_PRIx "\n", __func__, addr + 0x10f0); if (addr & 0xF) { goto out; } - idx = (addr >> 6) & 0x3; - if (addr == 0x0) { + if (addr == 0) { /* TFRR */ retval = opp->tfrr; goto out; } + addr -= 0x10; /* correct for TFRR */ + idx = (addr >> 6) & 0x3; switch (addr & 0x30) { case 0x00: /* TCCR */ - retval = opp->timers[idx].tccr; + retval = openpic_tmr_get_timer(&opp->timers[idx]); break; case 0x10: /* TBCR */ retval = opp->timers[idx].tbcr; break; - case 0x20: /* TIPV */ + case 0x20: /* TVPR */ retval = read_IRQreg_ivpr(opp, opp->irq_tim0 + idx); break; - case 0x30: /* TIDE (TIDR) */ + case 0x30: /* TDR */ retval = read_IRQreg_idr(opp, opp->irq_tim0 + idx); break; } @@ -1138,7 +1225,10 @@ static uint32_t openpic_iack(OpenPICState *opp, IRQDest *dst, int cpu) IRQ_resetbit(&dst->raised, irq); } - if ((irq >= opp->irq_ipi0) && (irq < (opp->irq_ipi0 + OPENPIC_MAX_IPI))) { + /* Timers and IPIs support multicast. */ + if (((irq >= opp->irq_ipi0) && (irq < (opp->irq_ipi0 + OPENPIC_MAX_IPI))) || + ((irq >= opp->irq_tim0) && (irq < (opp->irq_tim0 + OPENPIC_MAX_TMR)))) { + DPRINTF("irq is IPI or TMR\n"); src->destmask &= ~(1 << cpu); if (src->destmask && !src->level) { /* trigger on CPUs that didn't know about it yet */ @@ -1343,6 +1433,10 @@ static void openpic_reset(DeviceState *d) for (i = 0; i < OPENPIC_MAX_TMR; i++) { opp->timers[i].tccr = 0; opp->timers[i].tbcr = TBCR_CI; + if (opp->timers[i].qemu_timer_active) { + timer_del(opp->timers[i].qemu_timer); /* Inhibit timer */ + opp->timers[i].qemu_timer_active = false; + } } /* Go out of RESET state */ opp->gcr = 0; @@ -1393,6 +1487,15 @@ static void fsl_common_init(OpenPICState *opp) opp->src[i].type = IRQ_TYPE_FSLSPECIAL; opp->src[i].level = false; } + + for (i = 0; i < OPENPIC_MAX_TMR; i++) { + opp->timers[i].n_IRQ = opp->irq_tim0 + i; + opp->timers[i].qemu_timer_active = false; + opp->timers[i].qemu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + &qemu_timer_cb, + &opp->timers[i]); + opp->timers[i].opp = opp; + } } static void map_list(OpenPICState *opp, const MemReg *list, int *count) @@ -1501,7 +1604,7 @@ static const VMStateDescription vmstate_openpic = { VMSTATE_UINT32(max_irq, OpenPICState), VMSTATE_STRUCT_VARRAY_UINT32(src, OpenPICState, max_irq, 0, vmstate_openpic_irqsource, IRQSource), - VMSTATE_UINT32_EQUAL(nb_cpus, OpenPICState), + VMSTATE_UINT32_EQUAL(nb_cpus, OpenPICState, NULL), VMSTATE_STRUCT_VARRAY_UINT32(dst, OpenPICState, nb_cpus, 0, vmstate_openpic_irqdest, IRQDest), VMSTATE_STRUCT_ARRAY(timers, OpenPICState, OPENPIC_MAX_TMR, 0, diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c index 711c11454f..6eaf178d79 100644 --- a/hw/intc/s390_flic.c +++ b/hw/intc/s390_flic.c @@ -13,11 +13,14 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "hw/sysbus.h" -#include "migration/qemu-file.h" +#include "hw/s390x/ioinst.h" #include "hw/s390x/s390_flic.h" +#include "hw/s390x/css.h" #include "trace.h" +#include "cpu.h" #include "hw/qdev.h" #include "qapi/error.h" +#include "hw/s390x/s390-virtio-ccw.h" S390FLICState *s390_get_flic(void) { @@ -48,7 +51,7 @@ void s390_flic_init(void) static int qemu_s390_register_io_adapter(S390FLICState *fs, uint32_t id, uint8_t isc, bool swap, - bool is_maskable) + bool is_maskable, uint8_t flags) { /* nothing to do */ return 0; @@ -79,15 +82,91 @@ static int qemu_s390_clear_io_flic(S390FLICState *fs, uint16_t subchannel_id, return -ENOSYS; } +static int qemu_s390_modify_ais_mode(S390FLICState *fs, uint8_t isc, + uint16_t mode) +{ + QEMUS390FLICState *flic = QEMU_S390_FLIC(fs); + + switch (mode) { + case SIC_IRQ_MODE_ALL: + flic->simm &= ~AIS_MODE_MASK(isc); + flic->nimm &= ~AIS_MODE_MASK(isc); + break; + case SIC_IRQ_MODE_SINGLE: + flic->simm |= AIS_MODE_MASK(isc); + flic->nimm &= ~AIS_MODE_MASK(isc); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int qemu_s390_inject_airq(S390FLICState *fs, uint8_t type, + uint8_t isc, uint8_t flags) +{ + QEMUS390FLICState *flic = QEMU_S390_FLIC(fs); + bool flag = flags & S390_ADAPTER_SUPPRESSIBLE; + uint32_t io_int_word = (isc << 27) | IO_INT_WORD_AI; + + if (flag && (flic->nimm & AIS_MODE_MASK(isc))) { + trace_qemu_s390_airq_suppressed(type, isc); + return 0; + } + + s390_io_interrupt(0, 0, 0, io_int_word); + + if (flag && (flic->simm & AIS_MODE_MASK(isc))) { + flic->nimm |= AIS_MODE_MASK(isc); + trace_qemu_s390_suppress_airq(isc, "Single-Interruption Mode", + "NO-Interruptions Mode"); + } + + return 0; +} + +static void qemu_s390_flic_reset(DeviceState *dev) +{ + QEMUS390FLICState *flic = QEMU_S390_FLIC(dev); + + flic->simm = 0; + flic->nimm = 0; +} + +bool ais_needed(void *opaque) +{ + S390FLICState *s = opaque; + + return s->ais_supported; +} + +static const VMStateDescription qemu_s390_flic_vmstate = { + .name = "qemu-s390-flic", + .version_id = 1, + .minimum_version_id = 1, + .needed = ais_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT8(simm, QEMUS390FLICState), + VMSTATE_UINT8(nimm, QEMUS390FLICState), + VMSTATE_END_OF_LIST() + } +}; + static void qemu_s390_flic_class_init(ObjectClass *oc, void *data) { + DeviceClass *dc = DEVICE_CLASS(oc); S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc); + dc->reset = qemu_s390_flic_reset; + dc->vmsd = &qemu_s390_flic_vmstate; fsc->register_io_adapter = qemu_s390_register_io_adapter; fsc->io_adapter_map = qemu_s390_io_adapter_map; fsc->add_adapter_routes = qemu_s390_add_adapter_routes; fsc->release_adapter_routes = qemu_s390_release_adapter_routes; fsc->clear_io_irq = qemu_s390_clear_io_flic; + fsc->modify_ais_mode = qemu_s390_modify_ais_mode; + fsc->inject_airq = qemu_s390_inject_airq; } static Property s390_flic_common_properties[] = { @@ -98,12 +177,16 @@ static Property s390_flic_common_properties[] = { static void s390_flic_common_realize(DeviceState *dev, Error **errp) { - uint32_t max_batch = S390_FLIC_COMMON(dev)->adapter_routes_max_batch; + S390FLICState *fs = S390_FLIC_COMMON(dev); + uint32_t max_batch = fs->adapter_routes_max_batch; if (max_batch > ADAPTER_ROUTES_MAX_GSI) { - error_setg(errp, "flic adapter_routes_max_batch too big" - "%d (%d allowed)", max_batch, ADAPTER_ROUTES_MAX_GSI); + error_setg(errp, "flic property adapter_routes_max_batch too big" + " (%d > %d)", max_batch, ADAPTER_ROUTES_MAX_GSI); + return; } + + fs->ais_supported = s390_has_feat(S390_FEAT_ADAPTER_INT_SUPPRESSION); } static void s390_flic_class_init(ObjectClass *oc, void *data) @@ -137,3 +220,50 @@ static void qemu_s390_flic_register_types(void) } type_init(qemu_s390_flic_register_types) + +static bool adapter_info_so_needed(void *opaque) +{ + return css_migration_enabled(); +} + +const VMStateDescription vmstate_adapter_info_so = { + .name = "s390_adapter_info/summary_offset", + .version_id = 1, + .minimum_version_id = 1, + .needed = adapter_info_so_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT32(summary_offset, AdapterInfo), + VMSTATE_END_OF_LIST() + } +}; + +const VMStateDescription vmstate_adapter_info = { + .name = "s390_adapter_info", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT64(ind_offset, AdapterInfo), + /* + * We do not have to migrate neither the id nor the addresses. + * The id is set by css_register_io_adapter and the addresses + * are set based on the IndAddr objects after those get mapped. + */ + VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * []) { + &vmstate_adapter_info_so, + NULL + } +}; + +const VMStateDescription vmstate_adapter_routes = { + + .name = "s390_adapter_routes", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(adapter, AdapterRoutes, 1, vmstate_adapter_info, + AdapterInfo), + VMSTATE_END_OF_LIST() + } +}; diff --git a/hw/intc/s390_flic_kvm.c b/hw/intc/s390_flic_kvm.c index cc44bc4e1e..be3fd00a57 100644 --- a/hw/intc/s390_flic_kvm.c +++ b/hw/intc/s390_flic_kvm.c @@ -15,11 +15,12 @@ #include "cpu.h" #include <sys/ioctl.h> #include "qemu/error-report.h" +#include "qapi/error.h" #include "hw/sysbus.h" #include "sysemu/kvm.h" -#include "migration/qemu-file.h" #include "hw/s390x/s390_flic.h" #include "hw/s390x/adapter.h" +#include "hw/s390x/css.h" #include "trace.h" #define FLIC_SAVE_INITIAL_SIZE getpagesize() @@ -149,6 +150,43 @@ static int kvm_s390_clear_io_flic(S390FLICState *fs, uint16_t subchannel_id, return rc ? -errno : 0; } +static int kvm_s390_modify_ais_mode(S390FLICState *fs, uint8_t isc, + uint16_t mode) +{ + KVMS390FLICState *flic = KVM_S390_FLIC(fs); + struct kvm_s390_ais_req req = { + .isc = isc, + .mode = mode, + }; + struct kvm_device_attr attr = { + .group = KVM_DEV_FLIC_AISM, + .addr = (uint64_t)&req, + }; + + if (!fs->ais_supported) { + return -ENOSYS; + } + + return ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr) ? -errno : 0; +} + +static int kvm_s390_inject_airq(S390FLICState *fs, uint8_t type, + uint8_t isc, uint8_t flags) +{ + KVMS390FLICState *flic = KVM_S390_FLIC(fs); + uint32_t id = css_get_adapter_id(type, isc); + struct kvm_device_attr attr = { + .group = KVM_DEV_FLIC_AIRQ_INJECT, + .attr = id, + }; + + if (!fs->ais_supported) { + return -ENOSYS; + } + + return ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr) ? -errno : 0; +} + /** * __get_all_irqs - store all pending irqs in buffer * @flic: pointer to flic device state @@ -186,13 +224,14 @@ static int __get_all_irqs(KVMS390FLICState *flic, static int kvm_s390_register_io_adapter(S390FLICState *fs, uint32_t id, uint8_t isc, bool swap, - bool is_maskable) + bool is_maskable, uint8_t flags) { struct kvm_s390_io_adapter adapter = { .id = id, .isc = isc, .maskable = is_maskable, .swap = swap, + .flags = flags, }; KVMS390FLICState *flic = KVM_S390_FLIC(fs); int r; @@ -374,7 +413,84 @@ out: return r; } +typedef struct KVMS390FLICStateMigTmp { + KVMS390FLICState *parent; + uint8_t simm; + uint8_t nimm; +} KVMS390FLICStateMigTmp; + +static void kvm_flic_ais_pre_save(void *opaque) +{ + KVMS390FLICStateMigTmp *tmp = opaque; + KVMS390FLICState *flic = tmp->parent; + struct kvm_s390_ais_all ais; + struct kvm_device_attr attr = { + .group = KVM_DEV_FLIC_AISM_ALL, + .addr = (uint64_t)&ais, + .attr = sizeof(ais), + }; + + if (ioctl(flic->fd, KVM_GET_DEVICE_ATTR, &attr)) { + error_report("Failed to retrieve kvm flic ais states"); + return; + } + + tmp->simm = ais.simm; + tmp->nimm = ais.nimm; +} + +static int kvm_flic_ais_post_load(void *opaque, int version_id) +{ + KVMS390FLICStateMigTmp *tmp = opaque; + KVMS390FLICState *flic = tmp->parent; + struct kvm_s390_ais_all ais = { + .simm = tmp->simm, + .nimm = tmp->nimm, + }; + struct kvm_device_attr attr = { + .group = KVM_DEV_FLIC_AISM_ALL, + .addr = (uint64_t)&ais, + }; + + /* This can happen when the user mis-configures its guests in an + * incompatible fashion or without a CPU model. For example using + * qemu with -cpu host (which is not migration safe) and do a + * migration from a host that has AIS to a host that has no AIS. + * In that case the target system will reject the migration here. + */ + if (!ais_needed(flic)) { + return -ENOSYS; + } + + return ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr) ? -errno : 0; +} + +static const VMStateDescription kvm_s390_flic_ais_tmp = { + .name = "s390-flic-ais-tmp", + .pre_save = kvm_flic_ais_pre_save, + .post_load = kvm_flic_ais_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT8(simm, KVMS390FLICStateMigTmp), + VMSTATE_UINT8(nimm, KVMS390FLICStateMigTmp), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription kvm_s390_flic_vmstate_ais = { + .name = "s390-flic/ais", + .version_id = 1, + .minimum_version_id = 1, + .needed = ais_needed, + .fields = (VMStateField[]) { + VMSTATE_WITH_TMP(KVMS390FLICState, KVMS390FLICStateMigTmp, + kvm_s390_flic_ais_tmp), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription kvm_s390_flic_vmstate = { + /* should have been like kvm-s390-flic, + * can't change without breaking compat */ .name = "s390-flic", .version_id = FLIC_SAVEVM_VERSION, .minimum_version_id = FLIC_SAVEVM_VERSION, @@ -389,27 +505,50 @@ static const VMStateDescription kvm_s390_flic_vmstate = { .flags = VMS_SINGLE, }, VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * []) { + &kvm_s390_flic_vmstate_ais, + NULL } }; +typedef struct KVMS390FLICStateClass { + S390FLICStateClass parent_class; + DeviceRealize parent_realize; +} KVMS390FLICStateClass; + +#define KVM_S390_FLIC_GET_CLASS(obj) \ + OBJECT_GET_CLASS(KVMS390FLICStateClass, (obj), TYPE_KVM_S390_FLIC) + +#define KVM_S390_FLIC_CLASS(klass) \ + OBJECT_CLASS_CHECK(KVMS390FLICStateClass, (klass), TYPE_KVM_S390_FLIC) + static void kvm_s390_flic_realize(DeviceState *dev, Error **errp) { KVMS390FLICState *flic_state = KVM_S390_FLIC(dev); struct kvm_create_device cd = {0}; struct kvm_device_attr test_attr = {0}; int ret; + Error *errp_local = NULL; + KVM_S390_FLIC_GET_CLASS(dev)->parent_realize(dev, &errp_local); + if (errp_local) { + goto fail; + } flic_state->fd = -1; if (!kvm_check_extension(kvm_state, KVM_CAP_DEVICE_CTRL)) { + error_setg_errno(&errp_local, errno, "KVM is missing capability" + " KVM_CAP_DEVICE_CTRL"); trace_flic_no_device_api(errno); - return; + goto fail; } cd.type = KVM_DEV_TYPE_FLIC; ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd); if (ret < 0) { + error_setg_errno(&errp_local, errno, "Creating the KVM device failed"); trace_flic_create_device(errno); - return; + goto fail; } flic_state->fd = cd.fd; @@ -417,16 +556,20 @@ static void kvm_s390_flic_realize(DeviceState *dev, Error **errp) test_attr.group = KVM_DEV_FLIC_CLEAR_IO_IRQ; flic_state->clear_io_supported = !ioctl(flic_state->fd, KVM_HAS_DEVICE_ATTR, test_attr); - + return; +fail: + error_propagate(errp, errp_local); } static void kvm_s390_flic_reset(DeviceState *dev) { KVMS390FLICState *flic = KVM_S390_FLIC(dev); + S390FLICState *fs = S390_FLIC_COMMON(dev); struct kvm_device_attr attr = { .group = KVM_DEV_FLIC_CLEAR_IRQS, }; int rc = 0; + uint8_t isc; if (flic->fd == -1) { return; @@ -434,6 +577,16 @@ static void kvm_s390_flic_reset(DeviceState *dev) flic_disable_wait_pfault(flic); + if (fs->ais_supported) { + for (isc = 0; isc <= MAX_ISC; isc++) { + rc = kvm_s390_modify_ais_mode(fs, isc, SIC_IRQ_MODE_ALL); + if (rc) { + error_report("Failed to reset ais mode for isc %d: %s", + isc, strerror(-rc)); + } + } + } + rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); if (rc) { trace_flic_reset_failed(errno); @@ -447,6 +600,7 @@ static void kvm_s390_flic_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc); + KVM_S390_FLIC_CLASS(oc)->parent_realize = dc->realize; dc->realize = kvm_s390_flic_realize; dc->vmsd = &kvm_s390_flic_vmstate; dc->reset = kvm_s390_flic_reset; @@ -455,12 +609,15 @@ static void kvm_s390_flic_class_init(ObjectClass *oc, void *data) fsc->add_adapter_routes = kvm_s390_add_adapter_routes; fsc->release_adapter_routes = kvm_s390_release_adapter_routes; fsc->clear_io_irq = kvm_s390_clear_io_flic; + fsc->modify_ais_mode = kvm_s390_modify_ais_mode; + fsc->inject_airq = kvm_s390_inject_airq; } static const TypeInfo kvm_s390_flic_info = { .name = TYPE_KVM_S390_FLIC, .parent = TYPE_S390_FLIC_COMMON, .instance_size = sizeof(KVMS390FLICState), + .class_size = sizeof(KVMS390FLICStateClass), .class_init = kvm_s390_flic_class_init, }; diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 729c1288f1..c586714d89 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -73,6 +73,10 @@ flic_create_device(int err) "flic: create device failed %d" flic_no_device_api(int err) "flic: no Device Contral API support %d" flic_reset_failed(int err) "flic: reset failed %d" +# hw/intc/s390_flic.c +qemu_s390_airq_suppressed(uint8_t type, uint8_t isc) "flic: adapter I/O interrupt suppressed (type %x isc %x)" +qemu_s390_suppress_airq(uint8_t isc, const char *from, const char *to) "flic: for isc %x, suppress airq by modifying ais mode from %s to %s" + # hw/intc/aspeed_vic.c aspeed_vic_set_irq(int irq, int level) "Enabling IRQ %d: %d" aspeed_vic_update_fiq(int flags) "Raising FIQ: %d" diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 292fffecd3..a84ba51ad8 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -38,50 +38,6 @@ #include "monitor/monitor.h" #include "hw/intc/intc.h" -void xics_cpu_destroy(XICSFabric *xi, PowerPCCPU *cpu) -{ - CPUState *cs = CPU(cpu); - ICPState *icp = ICP(cpu->intc); - - assert(icp); - assert(cs == icp->cs); - - icp->output = NULL; - icp->cs = NULL; -} - -void xics_cpu_setup(XICSFabric *xi, PowerPCCPU *cpu, ICPState *icp) -{ - CPUState *cs = CPU(cpu); - CPUPPCState *env = &cpu->env; - ICPStateClass *icpc; - - assert(icp); - - cpu->intc = OBJECT(icp); - icp->cs = cs; - - icpc = ICP_GET_CLASS(icp); - if (icpc->cpu_setup) { - icpc->cpu_setup(icp, cpu); - } - - switch (PPC_INPUT(env)) { - case PPC_FLAGS_INPUT_POWER7: - icp->output = env->irq_inputs[POWER7_INPUT_INT]; - break; - - case PPC_FLAGS_INPUT_970: - icp->output = env->irq_inputs[PPC970_INPUT_INT]; - break; - - default: - error_report("XICS interrupt controller does not support this CPU " - "bus model"); - abort(); - } -} - void icp_pic_print_info(ICPState *icp, Monitor *mon) { int cpu_index = icp->cs ? icp->cs->cpu_index : -1; @@ -325,6 +281,7 @@ static const VMStateDescription vmstate_icp_server = { static void icp_reset(void *dev) { ICPState *icp = ICP(dev); + ICPStateClass *icpc = ICP_GET_CLASS(icp); icp->xirr = 0; icp->pending_priority = 0xff; @@ -332,38 +289,78 @@ static void icp_reset(void *dev) /* Make all outputs are deasserted */ qemu_set_irq(icp->output, 0); + + if (icpc->reset) { + icpc->reset(icp); + } } static void icp_realize(DeviceState *dev, Error **errp) { ICPState *icp = ICP(dev); ICPStateClass *icpc = ICP_GET_CLASS(dev); + PowerPCCPU *cpu; + CPUPPCState *env; Object *obj; Error *err = NULL; - obj = object_property_get_link(OBJECT(dev), "xics", &err); + obj = object_property_get_link(OBJECT(dev), ICP_PROP_XICS, &err); if (!obj) { - error_setg(errp, "%s: required link 'xics' not found: %s", + error_setg(errp, "%s: required link '" ICP_PROP_XICS "' not found: %s", __func__, error_get_pretty(err)); return; } icp->xics = XICS_FABRIC(obj); + obj = object_property_get_link(OBJECT(dev), ICP_PROP_CPU, &err); + if (!obj) { + error_setg(errp, "%s: required link '" ICP_PROP_CPU "' not found: %s", + __func__, error_get_pretty(err)); + return; + } + + cpu = POWERPC_CPU(obj); + cpu->intc = OBJECT(icp); + icp->cs = CPU(obj); + + env = &cpu->env; + switch (PPC_INPUT(env)) { + case PPC_FLAGS_INPUT_POWER7: + icp->output = env->irq_inputs[POWER7_INPUT_INT]; + break; + + case PPC_FLAGS_INPUT_970: + icp->output = env->irq_inputs[PPC970_INPUT_INT]; + break; + + default: + error_setg(errp, "XICS interrupt controller does not support this CPU bus model"); + return; + } + if (icpc->realize) { - icpc->realize(dev, errp); + icpc->realize(icp, errp); } qemu_register_reset(icp_reset, dev); + vmstate_register(NULL, icp->cs->cpu_index, &vmstate_icp_server, icp); } +static void icp_unrealize(DeviceState *dev, Error **errp) +{ + ICPState *icp = ICP(dev); + + vmstate_unregister(NULL, &vmstate_icp_server, icp); + qemu_unregister_reset(icp_reset, dev); +} static void icp_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->vmsd = &vmstate_icp_server; dc->realize = icp_realize; + dc->unrealize = icp_unrealize; } static const TypeInfo icp_info = { @@ -580,7 +577,7 @@ static const VMStateDescription vmstate_ics_simple = { .post_load = ics_simple_dispatch_post_load, .fields = (VMStateField[]) { /* Sanity check */ - VMSTATE_UINT32_EQUAL(nr_irqs, ICSState), + VMSTATE_UINT32_EQUAL(nr_irqs, ICSState, NULL), VMSTATE_STRUCT_VARRAY_POINTER_UINT32(irqs, ICSState, nr_irqs, vmstate_ics_simple_irq, @@ -596,10 +593,8 @@ static void ics_simple_initfn(Object *obj) ics->offset = XICS_IRQ_BASE; } -static void ics_simple_realize(DeviceState *dev, Error **errp) +static void ics_simple_realize(ICSState *ics, Error **errp) { - ICSState *ics = ICS_SIMPLE(dev); - if (!ics->nr_irqs) { error_setg(errp, "Number of interrupts needs to be greater 0"); return; @@ -607,7 +602,7 @@ static void ics_simple_realize(DeviceState *dev, Error **errp) ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState)); ics->qirqs = qemu_allocate_irqs(ics_simple_set_irq, ics, ics->nr_irqs); - qemu_register_reset(ics_simple_reset, dev); + qemu_register_reset(ics_simple_reset, ics); } static Property ics_simple_properties[] = { @@ -644,9 +639,9 @@ static void ics_base_realize(DeviceState *dev, Error **errp) Object *obj; Error *err = NULL; - obj = object_property_get_link(OBJECT(dev), "xics", &err); + obj = object_property_get_link(OBJECT(dev), ICS_PROP_XICS, &err); if (!obj) { - error_setg(errp, "%s: required link 'xics' not found: %s", + error_setg(errp, "%s: required link '" ICS_PROP_XICS "' not found: %s", __func__, error_get_pretty(err)); return; } @@ -654,7 +649,7 @@ static void ics_base_realize(DeviceState *dev, Error **errp) if (icsc->realize) { - icsc->realize(dev, errp); + icsc->realize(ics, errp); } } diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c index dd93531ae3..3091ad3ac2 100644 --- a/hw/intc/xics_kvm.c +++ b/hw/intc/xics_kvm.c @@ -42,6 +42,14 @@ static int kernel_xics_fd = -1; +typedef struct KVMEnabledICP { + unsigned long vcpu_id; + QLIST_ENTRY(KVMEnabledICP) node; +} KVMEnabledICP; + +static QLIST_HEAD(, KVMEnabledICP) + kvm_enabled_icps = QLIST_HEAD_INITIALIZER(&kvm_enabled_icps); + /* * ICP-KVM */ @@ -102,25 +110,16 @@ static int icp_set_kvm_state(ICPState *icp, int version_id) return 0; } -static void icp_kvm_reset(void *dev) +static void icp_kvm_reset(ICPState *icp) { - ICPState *icp = ICP(dev); - - icp->xirr = 0; - icp->pending_priority = 0xff; - icp->mfrr = 0xff; - - /* Make all outputs as deasserted only if the CPU thread is in use */ - if (icp->output) { - qemu_set_irq(icp->output, 0); - } - icp_set_kvm_state(icp, 1); } -static void icp_kvm_cpu_setup(ICPState *icp, PowerPCCPU *cpu) +static void icp_kvm_realize(ICPState *icp, Error **errp) { - CPUState *cs = CPU(cpu); + CPUState *cs = icp->cs; + KVMEnabledICP *enabled_icp; + unsigned long vcpu_id = kvm_arch_vcpu_id(cs); int ret; if (kernel_xics_fd == -1) { @@ -132,34 +131,31 @@ static void icp_kvm_cpu_setup(ICPState *icp, PowerPCCPU *cpu) * which was hot-removed earlier we don't have to renable * KVM_CAP_IRQ_XICS capability again. */ - if (icp->cap_irq_xics_enabled) { - return; + QLIST_FOREACH(enabled_icp, &kvm_enabled_icps, node) { + if (enabled_icp->vcpu_id == vcpu_id) { + return; + } } - ret = kvm_vcpu_enable_cap(cs, KVM_CAP_IRQ_XICS, 0, kernel_xics_fd, - kvm_arch_vcpu_id(cs)); + ret = kvm_vcpu_enable_cap(cs, KVM_CAP_IRQ_XICS, 0, kernel_xics_fd, vcpu_id); if (ret < 0) { - error_report("Unable to connect CPU%ld to kernel XICS: %s", - kvm_arch_vcpu_id(cs), strerror(errno)); - exit(1); + error_setg(errp, "Unable to connect CPU%ld to kernel XICS: %s", vcpu_id, + strerror(errno)); + return; } - icp->cap_irq_xics_enabled = true; -} - -static void icp_kvm_realize(DeviceState *dev, Error **errp) -{ - qemu_register_reset(icp_kvm_reset, dev); + enabled_icp = g_malloc(sizeof(*enabled_icp)); + enabled_icp->vcpu_id = vcpu_id; + QLIST_INSERT_HEAD(&kvm_enabled_icps, enabled_icp, node); } static void icp_kvm_class_init(ObjectClass *klass, void *data) { - DeviceClass *dc = DEVICE_CLASS(klass); ICPStateClass *icpc = ICP_CLASS(klass); - dc->realize = icp_kvm_realize; icpc->pre_save = icp_get_kvm_state; icpc->post_load = icp_set_kvm_state; - icpc->cpu_setup = icp_kvm_cpu_setup; + icpc->realize = icp_kvm_realize; + icpc->reset = icp_kvm_reset; } static const TypeInfo icp_kvm_info = { @@ -332,10 +328,8 @@ static void ics_kvm_reset(void *dev) ics_set_kvm_state(ics, 1); } -static void ics_kvm_realize(DeviceState *dev, Error **errp) +static void ics_kvm_realize(ICSState *ics, Error **errp) { - ICSState *ics = ICS_SIMPLE(dev); - if (!ics->nr_irqs) { error_setg(errp, "Number of interrupts needs to be greater 0"); return; @@ -343,7 +337,7 @@ static void ics_kvm_realize(DeviceState *dev, Error **errp) ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState)); ics->qirqs = qemu_allocate_irqs(ics_kvm_set_irq, ics, ics->nr_irqs); - qemu_register_reset(ics_kvm_reset, dev); + qemu_register_reset(ics_kvm_reset, ics); } static void ics_kvm_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/xics_pnv.c b/hw/intc/xics_pnv.c index 12ae605f10..2a955a8946 100644 --- a/hw/intc/xics_pnv.c +++ b/hw/intc/xics_pnv.c @@ -159,11 +159,11 @@ static const MemoryRegionOps pnv_icp_ops = { }, }; -static void pnv_icp_realize(DeviceState *dev, Error **errp) +static void pnv_icp_realize(ICPState *icp, Error **errp) { - PnvICPState *icp = PNV_ICP(dev); + PnvICPState *pnv_icp = PNV_ICP(icp); - memory_region_init_io(&icp->mmio, OBJECT(dev), &pnv_icp_ops, + memory_region_init_io(&pnv_icp->mmio, OBJECT(icp), &pnv_icp_ops, icp, "icp-thread", 0x1000); } diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c index f05308b897..d98ea8b130 100644 --- a/hw/intc/xics_spapr.c +++ b/hw/intc/xics_spapr.c @@ -229,7 +229,7 @@ static void rtas_int_on(PowerPCCPU *cpu, sPAPRMachineState *spapr, rtas_st(rets, 0, RTAS_OUT_SUCCESS); } -int xics_spapr_init(sPAPRMachineState *spapr, Error **errp) +void xics_spapr_init(sPAPRMachineState *spapr) { /* Registration of global state belongs into realize */ spapr_rtas_register(RTAS_IBM_SET_XIVE, "ibm,set-xive", rtas_set_xive); @@ -243,7 +243,6 @@ int xics_spapr_init(sPAPRMachineState *spapr, Error **errp) spapr_register_hypercall(H_XIRR_X, h_xirr_x); spapr_register_hypercall(H_EOI, h_eoi); spapr_register_hypercall(H_IPOLL, h_ipoll); - return 0; } #define ICS_IRQ_FREE(ics, srcno) \ diff --git a/hw/ipmi/ipmi.c b/hw/ipmi/ipmi.c index 5cf1caa88a..b27babd504 100644 --- a/hw/ipmi/ipmi.c +++ b/hw/ipmi/ipmi.c @@ -44,14 +44,14 @@ static int ipmi_do_hw_op(IPMIInterface *s, enum ipmi_op op, int checkonly) if (checkonly) { return 0; } - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); return 0; case IPMI_POWEROFF_CHASSIS: if (checkonly) { return 0; } - qemu_system_shutdown_request(); + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); return 0; case IPMI_SEND_NMI: @@ -90,7 +90,7 @@ static TypeInfo ipmi_interface_type_info = { .class_init = ipmi_interface_class_init, }; -static void isa_ipmi_bmc_check(Object *obj, const char *name, +static void isa_ipmi_bmc_check(const Object *obj, const char *name, Object *val, Error **errp) { IPMIBmc *bmc = IPMI_BMC(val); diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c index e8e3d250b6..abab3bba4f 100644 --- a/hw/ipmi/ipmi_bmc_extern.c +++ b/hw/ipmi/ipmi_bmc_extern.c @@ -30,7 +30,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/timer.h" -#include "sysemu/char.h" +#include "chardev/char-fe.h" #include "sysemu/sysemu.h" #include "hw/ipmi/ipmi.h" @@ -447,13 +447,13 @@ static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp) { IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(dev); - if (!qemu_chr_fe_get_driver(&ibe->chr)) { + if (!qemu_chr_fe_backend_connected(&ibe->chr)) { error_setg(errp, "IPMI external bmc requires chardev attribute"); return; } qemu_chr_fe_set_handlers(&ibe->chr, can_receive, receive, - chr_event, ibe, NULL, true); + chr_event, NULL, ibe, NULL, true); } static int ipmi_bmc_extern_post_migrate(void *opaque, int version_id) diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index e2215dcf4d..ac8416d42b 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -606,7 +606,7 @@ static void ich9_rst_cnt_write(void *opaque, hwaddr addr, uint64_t val, ICH9LPCState *lpc = opaque; if (val & 4) { - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); return; } lpc->rst_cnt = val & 0xA; /* keep FULL_RST (bit 3) and SYS_RST (bit 1) */ diff --git a/hw/isa/pc87312.c b/hw/isa/pc87312.c index c707d24db4..5ce9f0a062 100644 --- a/hw/isa/pc87312.c +++ b/hw/isa/pc87312.c @@ -30,7 +30,7 @@ #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "sysemu/sysemu.h" -#include "sysemu/char.h" +#include "chardev/char.h" #include "trace.h" diff --git a/hw/m68k/an5206.c b/hw/m68k/an5206.c index 142bab98c9..c76244176f 100644 --- a/hw/m68k/an5206.c +++ b/hw/m68k/an5206.c @@ -61,7 +61,6 @@ static void an5206_init(MachineState *machine) /* Internal SRAM. */ memory_region_init_ram(sram, NULL, "an5206.sram", 512, &error_fatal); - vmstate_register_ram_global(sram); memory_region_add_subregion(address_space_mem, AN5206_RAMBAR_ADDR, sram); mcf5206_init(address_space_mem, AN5206_MBAR_ADDR, cpu); diff --git a/hw/m68k/mcf5208.c b/hw/m68k/mcf5208.c index 656351834e..f4b1387c0d 100644 --- a/hw/m68k/mcf5208.c +++ b/hw/m68k/mcf5208.c @@ -249,7 +249,6 @@ static void mcf5208evb_init(MachineState *machine) /* Internal SRAM. */ memory_region_init_ram(sram, NULL, "mcf5208.sram", 16384, &error_fatal); - vmstate_register_ram_global(sram); memory_region_add_subregion(address_space_mem, 0x80000000, sram); /* Internal peripherals. */ diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 9e8dab0e89..ea67b461c2 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -46,7 +46,8 @@ void pc_dimm_memory_plug(DeviceState *dev, MemoryHotplugState *hpms, uint64_t existing_dimms_capacity = 0; uint64_t addr; - addr = object_property_get_int(OBJECT(dimm), PC_DIMM_ADDR_PROP, &local_err); + addr = object_property_get_uint(OBJECT(dimm), + PC_DIMM_ADDR_PROP, &local_err); if (local_err) { goto out; } @@ -73,7 +74,7 @@ void pc_dimm_memory_plug(DeviceState *dev, MemoryHotplugState *hpms, goto out; } - object_property_set_int(OBJECT(dev), addr, PC_DIMM_ADDR_PROP, &local_err); + object_property_set_uint(OBJECT(dev), addr, PC_DIMM_ADDR_PROP, &local_err); if (local_err) { goto out; } @@ -135,7 +136,7 @@ static int pc_existing_dimms_capacity_internal(Object *obj, void *opaque) DeviceState *dev = DEVICE(obj); if (dev->realized) { - (*size) += object_property_get_int(obj, PC_DIMM_SIZE_PROP, + (*size) += object_property_get_uint(obj, PC_DIMM_SIZE_PROP, cap->errp); } @@ -181,8 +182,8 @@ int qmp_pc_dimm_device_list(Object *obj, void *opaque) di->addr = dimm->addr; di->slot = dimm->slot; di->node = dimm->node; - di->size = object_property_get_int(OBJECT(dimm), PC_DIMM_SIZE_PROP, - NULL); + di->size = object_property_get_uint(OBJECT(dimm), PC_DIMM_SIZE_PROP, + NULL); di->memdev = object_get_canonical_path(OBJECT(dimm->hostmem)); info->u.dimm.data = di; @@ -313,9 +314,9 @@ uint64_t pc_dimm_get_free_addr(uint64_t address_space_start, /* find address range that will fit new DIMM */ for (item = list; item; item = g_slist_next(item)) { PCDIMMDevice *dimm = item->data; - uint64_t dimm_size = object_property_get_int(OBJECT(dimm), - PC_DIMM_SIZE_PROP, - errp); + uint64_t dimm_size = object_property_get_uint(OBJECT(dimm), + PC_DIMM_SIZE_PROP, + errp); if (errp && *errp) { goto out; } @@ -349,13 +350,15 @@ static Property pc_dimm_properties[] = { DEFINE_PROP_UINT32(PC_DIMM_NODE_PROP, PCDIMMDevice, node, 0), DEFINE_PROP_INT32(PC_DIMM_SLOT_PROP, PCDIMMDevice, slot, PC_DIMM_UNASSIGNED_SLOT), + DEFINE_PROP_LINK(PC_DIMM_MEMDEV_PROP, PCDIMMDevice, hostmem, + TYPE_MEMORY_BACKEND, HostMemoryBackend *), DEFINE_PROP_END_OF_LIST(), }; static void pc_dimm_get_size(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - int64_t value; + uint64_t value; MemoryRegion *mr; PCDIMMDevice *dimm = PC_DIMM(obj); PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(obj); @@ -363,36 +366,13 @@ static void pc_dimm_get_size(Object *obj, Visitor *v, const char *name, mr = ddc->get_memory_region(dimm); value = memory_region_size(mr); - visit_type_int(v, name, &value, errp); -} - -static void pc_dimm_check_memdev_is_busy(Object *obj, const char *name, - Object *val, Error **errp) -{ - Error *local_err = NULL; - - if (host_memory_backend_is_mapped(MEMORY_BACKEND(val))) { - char *path = object_get_canonical_path_component(val); - error_setg(&local_err, "can't use already busy memdev: %s", path); - g_free(path); - } else { - qdev_prop_allow_set_link_before_realize(obj, name, val, &local_err); - } - - error_propagate(errp, local_err); + visit_type_uint64(v, name, &value, errp); } static void pc_dimm_init(Object *obj) { - PCDIMMDevice *dimm = PC_DIMM(obj); - - object_property_add(obj, PC_DIMM_SIZE_PROP, "int", pc_dimm_get_size, + object_property_add(obj, PC_DIMM_SIZE_PROP, "uint64", pc_dimm_get_size, NULL, NULL, NULL, &error_abort); - object_property_add_link(obj, PC_DIMM_MEMDEV_PROP, TYPE_MEMORY_BACKEND, - (Object **)&dimm->hostmem, - pc_dimm_check_memdev_is_busy, - OBJ_PROP_LINK_UNREF_ON_RELEASE, - &error_abort); } static void pc_dimm_realize(DeviceState *dev, Error **errp) @@ -403,6 +383,11 @@ static void pc_dimm_realize(DeviceState *dev, Error **errp) if (!dimm->hostmem) { error_setg(errp, "'" PC_DIMM_MEMDEV_PROP "' property is not set"); return; + } else if (host_memory_backend_is_mapped(dimm->hostmem)) { + char *path = object_get_canonical_path_component(OBJECT(dimm->hostmem)); + error_setg(errp, "can't use already busy memdev: %s", path); + g_free(path); + return; } if (((nb_numa_nodes > 0) && (dimm->node >= nb_numa_nodes)) || (!nb_numa_nodes && dimm->node)) { diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c index 4968bdbb28..b664dc0f9c 100644 --- a/hw/microblaze/petalogix_ml605_mmu.c +++ b/hw/microblaze/petalogix_ml605_mmu.c @@ -98,12 +98,10 @@ petalogix_ml605_init(MachineState *machine) /* Attach emulated BRAM through the LMB. */ memory_region_init_ram(phys_lmb_bram, NULL, "petalogix_ml605.lmb_bram", LMB_BRAM_SIZE, &error_fatal); - vmstate_register_ram_global(phys_lmb_bram); memory_region_add_subregion(address_space_mem, 0x00000000, phys_lmb_bram); memory_region_init_ram(phys_ram, NULL, "petalogix_ml605.ram", ram_size, &error_fatal); - vmstate_register_ram_global(phys_ram); memory_region_add_subregion(address_space_mem, MEMORY_BASEADDR, phys_ram); dinfo = drive_get(IF_PFLASH, 0, 0); diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c index 423bcd7f6c..5cb4deb69e 100644 --- a/hw/microblaze/petalogix_s3adsp1800_mmu.c +++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c @@ -78,12 +78,10 @@ petalogix_s3adsp1800_init(MachineState *machine) memory_region_init_ram(phys_lmb_bram, NULL, "petalogix_s3adsp1800.lmb_bram", LMB_BRAM_SIZE, &error_fatal); - vmstate_register_ram_global(phys_lmb_bram); memory_region_add_subregion(sysmem, 0x00000000, phys_lmb_bram); memory_region_init_ram(phys_ram, NULL, "petalogix_s3adsp1800.ram", ram_size, &error_fatal); - vmstate_register_ram_global(phys_ram); memory_region_add_subregion(sysmem, ddr_base, phys_ram); dinfo = drive_get(IF_PFLASH, 0, 0); diff --git a/hw/mips/boston.c b/hw/mips/boston.c index 83f7b82386..7985c60dde 100644 --- a/hw/mips/boston.c +++ b/hw/mips/boston.c @@ -35,7 +35,7 @@ #include "qemu/cutils.h" #include "qemu/error-report.h" #include "qemu/log.h" -#include "sysemu/char.h" +#include "chardev/char.h" #include "sysemu/device_tree.h" #include "sysemu/sysemu.h" #include "sysemu/qtest.h" @@ -232,7 +232,7 @@ static void boston_platreg_write(void *opaque, hwaddr addr, break; case PLAT_SOFTRST_CTL: if (val & PLAT_SOFTRST_CTL_SYSRESET) { - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); } break; default: @@ -484,7 +484,7 @@ static void boston_mach_init(MachineState *machine) sysbus_mmio_map_overlap(SYS_BUS_DEVICE(s->cps), 0, 0, 1); flash = g_new(MemoryRegion, 1); - memory_region_init_rom_device(flash, NULL, &boston_flash_ops, s, + memory_region_init_rom_device_nomigrate(flash, NULL, &boston_flash_ops, s, "boston.flash", 128 * M_BYTE, &err); memory_region_add_subregion_overlap(sys_mem, 0x18000000, flash, 0); @@ -533,7 +533,7 @@ static void boston_mach_init(MachineState *machine) chr = qemu_chr_new("lcd", "vc:320x240"); qemu_chr_fe_init(&s->lcd_display, chr, NULL); qemu_chr_fe_set_handlers(&s->lcd_display, NULL, NULL, - boston_lcd_event, s, NULL, true); + boston_lcd_event, NULL, s, NULL, true); ahci = pci_create_simple_multifunction(&PCI_BRIDGE(&pcie2->root)->sec_bus, PCI_DEVFN(0, 0), diff --git a/hw/mips/mips_fulong2e.c b/hw/mips/mips_fulong2e.c index e636c3abaa..3f3cb32651 100644 --- a/hw/mips/mips_fulong2e.c +++ b/hw/mips/mips_fulong2e.c @@ -32,7 +32,6 @@ #include "hw/mips/mips.h" #include "hw/mips/cpudevs.h" #include "hw/pci/pci.h" -#include "sysemu/char.h" #include "sysemu/sysemu.h" #include "audio/audio.h" #include "qemu/log.h" @@ -297,7 +296,6 @@ static void mips_fulong2e_init(MachineState *machine) memory_region_allocate_system_memory(ram, NULL, "fulong2e.ram", ram_size); memory_region_init_ram(bios, NULL, "fulong2e.bios", bios_size, &error_fatal); - vmstate_register_ram_global(bios); memory_region_set_readonly(bios, true); memory_region_add_subregion(address_space_mem, 0, ram); diff --git a/hw/mips/mips_jazz.c b/hw/mips/mips_jazz.c index 1cef581878..df2262a2a8 100644 --- a/hw/mips/mips_jazz.c +++ b/hw/mips/mips_jazz.c @@ -130,7 +130,7 @@ static void mips_jazz_init(MachineState *machine, CPUMIPSState *env; qemu_irq *i8259; rc4030_dma *dmas; - MemoryRegion *rc4030_dma_mr; + IOMMUMemoryRegion *rc4030_dma_mr; MemoryRegion *isa_mem = g_new(MemoryRegion, 1); MemoryRegion *isa_io = g_new(MemoryRegion, 1); MemoryRegion *rtc = g_new(MemoryRegion, 1); @@ -177,7 +177,6 @@ static void mips_jazz_init(MachineState *machine, memory_region_init_ram(bios, NULL, "mips_jazz.bios", MAGNUM_BIOS_SIZE, &error_fatal); - vmstate_register_ram_global(bios); memory_region_set_readonly(bios, true); memory_region_init_alias(bios2, NULL, "mips_jazz.bios", bios, 0, MAGNUM_BIOS_SIZE); @@ -244,7 +243,6 @@ static void mips_jazz_init(MachineState *machine, MemoryRegion *rom_mr = g_new(MemoryRegion, 1); memory_region_init_ram(rom_mr, NULL, "g364fb.rom", 0x80000, &error_fatal); - vmstate_register_ram_global(rom_mr); memory_region_set_readonly(rom_mr, true); uint8_t *rom = memory_region_get_ram_ptr(rom_mr); memory_region_add_subregion(address_space, 0x60000000, rom_mr); diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index 5dd177e961..3487d16f61 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -37,7 +37,6 @@ #include "hw/mips/mips.h" #include "hw/mips/cpudevs.h" #include "hw/pci/pci.h" -#include "sysemu/char.h" #include "sysemu/sysemu.h" #include "sysemu/arch_init.h" #include "qemu/log.h" @@ -470,7 +469,7 @@ static void malta_fpga_write(void *opaque, hwaddr addr, /* SOFTRES Register */ case 0x00500: if (val == 0x42) - qemu_system_reset_request (); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); break; /* BRKRES Register */ @@ -571,7 +570,7 @@ static MaltaFPGAState *malta_fpga_init(MemoryRegion *address_space, chr = qemu_chr_new("fpga", "vc:320x200"); qemu_chr_fe_init(&s->display, chr, NULL); qemu_chr_fe_set_handlers(&s->display, NULL, NULL, - malta_fgpa_display_event, s, NULL, true); + malta_fgpa_display_event, NULL, s, NULL, true); s->uart = serial_mm_init(address_space, base + 0x900, 3, uart_irq, 230400, uart_chr, DEVICE_NATIVE_ENDIAN); @@ -842,8 +841,9 @@ static int64_t load_kernel (void) if (loaderparams.initrd_filename) { initrd_size = get_image_size (loaderparams.initrd_filename); if (initrd_size > 0) { - initrd_offset = (kernel_high + ~INITRD_PAGE_MASK) & INITRD_PAGE_MASK; - if (initrd_offset + initrd_size > ram_size) { + initrd_offset = (loaderparams.ram_low_size - initrd_size + - ~INITRD_PAGE_MASK) & INITRD_PAGE_MASK; + if (kernel_high >= initrd_offset) { fprintf(stderr, "qemu: memory too small for initial ram disk '%s'\n", loaderparams.initrd_filename); @@ -1178,7 +1178,7 @@ void mips_malta_init(MachineState *machine) * handled by an overlapping region as the resulting ROM code subpage * regions are not executable. */ - memory_region_init_ram(bios_copy, NULL, "bios.1fc", BIOS_SIZE, + memory_region_init_ram_nomigrate(bios_copy, NULL, "bios.1fc", BIOS_SIZE, &error_fatal); if (!rom_copy(memory_region_get_ram_ptr(bios_copy), FLASH_ADDRESS, BIOS_SIZE)) { diff --git a/hw/mips/mips_mipssim.c b/hw/mips/mips_mipssim.c index 1b91195006..6990b1b0dd 100644 --- a/hw/mips/mips_mipssim.c +++ b/hw/mips/mips_mipssim.c @@ -179,7 +179,6 @@ mips_mipssim_init(MachineState *machine) ram_size); memory_region_init_ram(bios, NULL, "mips_mipssim.bios", BIOS_SIZE, &error_fatal); - vmstate_register_ram_global(bios); memory_region_set_readonly(bios, true); memory_region_add_subregion(address_space_mem, 0, ram); diff --git a/hw/mips/mips_r4k.c b/hw/mips/mips_r4k.c index 748586ed77..690874be2b 100644 --- a/hw/mips/mips_r4k.c +++ b/hw/mips/mips_r4k.c @@ -53,9 +53,9 @@ static void mips_qemu_write (void *opaque, hwaddr addr, uint64_t val, unsigned size) { if ((addr & 0xffff) == 0 && val == 42) - qemu_system_reset_request (); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); else if ((addr & 0xffff) == 4 && val == 42) - qemu_system_shutdown_request (); + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); } static uint64_t mips_qemu_read (void *opaque, hwaddr addr, @@ -238,7 +238,6 @@ void mips_r4k_init(MachineState *machine) bios = g_new(MemoryRegion, 1); memory_region_init_ram(bios, NULL, "mips_r4k.bios", BIOS_SIZE, &error_fatal); - vmstate_register_ram_global(bios); memory_region_set_readonly(bios, true); memory_region_add_subregion(get_system_memory(), 0x1fc00000, bios); diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index c8b489390f..3b91d32071 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -1,10 +1,12 @@ common-obj-$(CONFIG_APPLESMC) += applesmc.o common-obj-$(CONFIG_MAX111X) += max111x.o common-obj-$(CONFIG_TMP105) += tmp105.o +common-obj-$(CONFIG_TMP421) += tmp421.o common-obj-$(CONFIG_ISA_DEBUG) += debugexit.o common-obj-$(CONFIG_SGA) += sga.o common-obj-$(CONFIG_ISA_TESTDEV) += pc-testdev.o common-obj-$(CONFIG_PCI_TESTDEV) += pci-testdev.o +common-obj-$(CONFIG_EDU) += edu.o common-obj-y += unimp.o @@ -26,7 +28,7 @@ obj-$(CONFIG_IVSHMEM) += ivshmem.o obj-$(CONFIG_REALVIEW) += arm_sysctl.o obj-$(CONFIG_NSERIES) += cbus.o obj-$(CONFIG_ECCMEMCTL) += eccmemctl.o -obj-$(CONFIG_EXYNOS4) += exynos4210_pmu.o exynos4210_clk.o +obj-$(CONFIG_EXYNOS4) += exynos4210_pmu.o exynos4210_clk.o exynos4210_rng.o obj-$(CONFIG_IMX) += imx_ccm.o obj-$(CONFIG_IMX) += imx31_ccm.o obj-$(CONFIG_IMX) += imx25_ccm.o @@ -50,9 +52,10 @@ obj-$(CONFIG_STM32F2XX_SYSCFG) += stm32f2xx_syscfg.o obj-$(CONFIG_MIPS_CPS) += mips_cmgcr.o obj-$(CONFIG_MIPS_CPS) += mips_cpc.o obj-$(CONFIG_MIPS_ITU) += mips_itu.o +obj-$(CONFIG_MPS2_SCC) += mps2-scc.o obj-$(CONFIG_PVPANIC) += pvpanic.o -obj-$(CONFIG_EDU) += edu.o obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o obj-$(CONFIG_AUX) += auxbus.o obj-$(CONFIG_ASPEED_SOC) += aspeed_scu.o aspeed_sdmc.o +obj-y += mmio_interface.o diff --git a/hw/misc/applesmc.c b/hw/misc/applesmc.c index 77fab5b9d2..7896812304 100644 --- a/hw/misc/applesmc.c +++ b/hw/misc/applesmc.c @@ -39,21 +39,43 @@ /* #define DEBUG_SMC */ #define APPLESMC_DEFAULT_IOBASE 0x300 -/* data port used by Apple SMC */ -#define APPLESMC_DATA_PORT 0x0 -/* command/status port used by Apple SMC */ -#define APPLESMC_CMD_PORT 0x4 -#define APPLESMC_NR_PORTS 32 -#define APPLESMC_READ_CMD 0x10 -#define APPLESMC_WRITE_CMD 0x11 -#define APPLESMC_GET_KEY_BY_INDEX_CMD 0x12 -#define APPLESMC_GET_KEY_TYPE_CMD 0x13 +enum { + APPLESMC_DATA_PORT = 0x00, + APPLESMC_CMD_PORT = 0x04, + APPLESMC_ERR_PORT = 0x1e, + APPLESMC_NUM_PORTS = 0x20, +}; + +enum { + APPLESMC_READ_CMD = 0x10, + APPLESMC_WRITE_CMD = 0x11, + APPLESMC_GET_KEY_BY_INDEX_CMD = 0x12, + APPLESMC_GET_KEY_TYPE_CMD = 0x13, +}; + +enum { + APPLESMC_ST_CMD_DONE = 0x00, + APPLESMC_ST_DATA_READY = 0x01, + APPLESMC_ST_BUSY = 0x02, + APPLESMC_ST_ACK = 0x04, + APPLESMC_ST_NEW_CMD = 0x08, +}; + +enum { + APPLESMC_ST_1E_CMD_INTRUPTED = 0x80, + APPLESMC_ST_1E_STILL_BAD_CMD = 0x81, + APPLESMC_ST_1E_BAD_CMD = 0x82, + APPLESMC_ST_1E_NOEXIST = 0x84, + APPLESMC_ST_1E_WRITEONLY = 0x85, + APPLESMC_ST_1E_READONLY = 0x86, + APPLESMC_ST_1E_BAD_INDEX = 0xb8, +}; #ifdef DEBUG_SMC #define smc_debug(...) fprintf(stderr, "AppleSMC: " __VA_ARGS__) #else -#define smc_debug(...) do { } while(0) +#define smc_debug(...) do { } while (0) #endif static char default_osk[64] = "This is a dummy key. Enter the real key " @@ -74,15 +96,17 @@ struct AppleSMCState { MemoryRegion io_data; MemoryRegion io_cmd; + MemoryRegion io_err; uint32_t iobase; uint8_t cmd; uint8_t status; - uint8_t key[4]; + uint8_t status_1e; + uint8_t last_ret; + char key[4]; uint8_t read_pos; uint8_t data_len; uint8_t data_pos; uint8_t data[255]; - uint8_t charactic[4]; char *osk; QLIST_HEAD(, AppleSMCData) data_def; }; @@ -91,89 +115,138 @@ static void applesmc_io_cmd_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { AppleSMCState *s = opaque; - - smc_debug("CMD Write B: %#x = %#x\n", addr, val); - switch(val) { - case APPLESMC_READ_CMD: - s->status = 0x0c; - break; + uint8_t status = s->status & 0x0f; + + smc_debug("CMD received: 0x%02x\n", (uint8_t)val); + switch (val) { + case APPLESMC_READ_CMD: + /* did last command run through OK? */ + if (status == APPLESMC_ST_CMD_DONE || status == APPLESMC_ST_NEW_CMD) { + s->cmd = val; + s->status = APPLESMC_ST_NEW_CMD | APPLESMC_ST_ACK; + } else { + smc_debug("ERROR: previous command interrupted!\n"); + s->status = APPLESMC_ST_NEW_CMD; + s->status_1e = APPLESMC_ST_1E_CMD_INTRUPTED; + } + break; + default: + smc_debug("UNEXPECTED CMD 0x%02x\n", (uint8_t)val); + s->status = APPLESMC_ST_NEW_CMD; + s->status_1e = APPLESMC_ST_1E_BAD_CMD; } - s->cmd = val; s->read_pos = 0; s->data_pos = 0; } -static void applesmc_fill_data(AppleSMCState *s) +static struct AppleSMCData *applesmc_find_key(AppleSMCState *s) { struct AppleSMCData *d; QLIST_FOREACH(d, &s->data_def, node) { if (!memcmp(d->key, s->key, 4)) { - smc_debug("Key matched (%s Len=%d Data=%s)\n", d->key, - d->len, d->data); - memcpy(s->data, d->data, d->len); - return; + return d; } } + return NULL; } static void applesmc_io_data_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { AppleSMCState *s = opaque; + struct AppleSMCData *d; - smc_debug("DATA Write B: %#x = %#x\n", addr, val); - switch(s->cmd) { - case APPLESMC_READ_CMD: - if(s->read_pos < 4) { - s->key[s->read_pos] = val; - s->status = 0x04; - } else if(s->read_pos == 4) { - s->data_len = val; - s->status = 0x05; + smc_debug("DATA received: 0x%02x\n", (uint8_t)val); + switch (s->cmd) { + case APPLESMC_READ_CMD: + if ((s->status & 0x0f) == APPLESMC_ST_CMD_DONE) { + break; + } + if (s->read_pos < 4) { + s->key[s->read_pos] = val; + s->status = APPLESMC_ST_ACK; + } else if (s->read_pos == 4) { + d = applesmc_find_key(s); + if (d != NULL) { + memcpy(s->data, d->data, d->len); + s->data_len = d->len; s->data_pos = 0; - smc_debug("Key = %c%c%c%c Len = %d\n", s->key[0], - s->key[1], s->key[2], s->key[3], val); - applesmc_fill_data(s); + s->status = APPLESMC_ST_ACK | APPLESMC_ST_DATA_READY; + s->status_1e = APPLESMC_ST_CMD_DONE; /* clear on valid key */ + } else { + smc_debug("READ_CMD: key '%c%c%c%c' not found!\n", + s->key[0], s->key[1], s->key[2], s->key[3]); + s->status = APPLESMC_ST_CMD_DONE; + s->status_1e = APPLESMC_ST_1E_NOEXIST; } - s->read_pos++; - break; + } + s->read_pos++; + break; + default: + s->status = APPLESMC_ST_CMD_DONE; + s->status_1e = APPLESMC_ST_1E_STILL_BAD_CMD; } } -static uint64_t applesmc_io_data_read(void *opaque, hwaddr addr1, - unsigned size) +static void applesmc_io_err_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + smc_debug("ERR_CODE received: 0x%02x, ignoring!\n", (uint8_t)val); + /* NOTE: writing to the error port not supported! */ +} + +static uint64_t applesmc_io_data_read(void *opaque, hwaddr addr, unsigned size) { AppleSMCState *s = opaque; - uint8_t retval = 0; - - switch(s->cmd) { - case APPLESMC_READ_CMD: - if(s->data_pos < s->data_len) { - retval = s->data[s->data_pos]; - smc_debug("READ_DATA[%d] = %#hhx\n", s->data_pos, - retval); - s->data_pos++; - if(s->data_pos == s->data_len) { - s->status = 0x00; - smc_debug("EOF\n"); - } else - s->status = 0x05; + + switch (s->cmd) { + case APPLESMC_READ_CMD: + if (!(s->status & APPLESMC_ST_DATA_READY)) { + break; + } + if (s->data_pos < s->data_len) { + s->last_ret = s->data[s->data_pos]; + smc_debug("READ '%c%c%c%c'[%d] = %02x\n", + s->key[0], s->key[1], s->key[2], s->key[3], + s->data_pos, s->last_ret); + s->data_pos++; + if (s->data_pos == s->data_len) { + s->status = APPLESMC_ST_CMD_DONE; + smc_debug("READ '%c%c%c%c' Len=%d complete!\n", + s->key[0], s->key[1], s->key[2], s->key[3], + s->data_len); + } else { + s->status = APPLESMC_ST_ACK | APPLESMC_ST_DATA_READY; } + } + break; + default: + s->status = APPLESMC_ST_CMD_DONE; + s->status_1e = APPLESMC_ST_1E_STILL_BAD_CMD; } - smc_debug("DATA Read b: %#x = %#x\n", addr1, retval); + smc_debug("DATA sent: 0x%02x\n", s->last_ret); - return retval; + return s->last_ret; } -static uint64_t applesmc_io_cmd_read(void *opaque, hwaddr addr1, unsigned size) +static uint64_t applesmc_io_cmd_read(void *opaque, hwaddr addr, unsigned size) { AppleSMCState *s = opaque; - smc_debug("CMD Read B: %#x\n", addr1); + smc_debug("CMD sent: 0x%02x\n", s->status); return s->status; } +static uint64_t applesmc_io_err_read(void *opaque, hwaddr addr, unsigned size) +{ + AppleSMCState *s = opaque; + + /* NOTE: read does not clear the 1e status */ + smc_debug("ERR_CODE sent: 0x%02x\n", s->status_1e); + return s->status_1e; +} + static void applesmc_add_key(AppleSMCState *s, const char *key, int len, const char *data) { @@ -196,6 +269,9 @@ static void qdev_applesmc_isa_reset(DeviceState *dev) QLIST_FOREACH_SAFE(d, &s->data_def, node, next) { QLIST_REMOVE(d, node); } + s->status = 0x00; + s->status_1e = 0x00; + s->last_ret = 0x00; applesmc_add_key(s, "REV ", 6, "\x01\x13\x0f\x00\x00\x03"); applesmc_add_key(s, "OSK0", 32, s->osk); @@ -225,20 +301,35 @@ static const MemoryRegionOps applesmc_cmd_io_ops = { }, }; +static const MemoryRegionOps applesmc_err_io_ops = { + .write = applesmc_io_err_write, + .read = applesmc_io_err_read, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, +}; + static void applesmc_isa_realize(DeviceState *dev, Error **errp) { AppleSMCState *s = APPLE_SMC(dev); memory_region_init_io(&s->io_data, OBJECT(s), &applesmc_data_io_ops, s, - "applesmc-data", 4); + "applesmc-data", 1); isa_register_ioport(&s->parent_obj, &s->io_data, s->iobase + APPLESMC_DATA_PORT); memory_region_init_io(&s->io_cmd, OBJECT(s), &applesmc_cmd_io_ops, s, - "applesmc-cmd", 4); + "applesmc-cmd", 1); isa_register_ioport(&s->parent_obj, &s->io_cmd, s->iobase + APPLESMC_CMD_PORT); + memory_region_init_io(&s->io_err, OBJECT(s), &applesmc_err_io_ops, s, + "applesmc-err", 1); + isa_register_ioport(&s->parent_obj, &s->io_err, + s->iobase + APPLESMC_ERR_PORT); + if (!s->osk || (strlen(s->osk) != 64)) { fprintf(stderr, "WARNING: Using AppleSMC with invalid key\n"); s->osk = default_osk; diff --git a/hw/misc/arm_sysctl.c b/hw/misc/arm_sysctl.c index 8524008708..b20b44ea20 100644 --- a/hw/misc/arm_sysctl.c +++ b/hw/misc/arm_sysctl.c @@ -351,13 +351,13 @@ static bool vexpress_cfgctrl_write(arm_sysctl_state *s, unsigned int dcc, break; case SYS_CFG_SHUTDOWN: if (site == SYS_CFG_SITE_MB && device == 0) { - qemu_system_shutdown_request(); + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); return true; } break; case SYS_CFG_REBOOT: if (site == SYS_CFG_SITE_MB && device == 0) { - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); return true; } break; @@ -429,7 +429,7 @@ static void arm_sysctl_write(void *opaque, hwaddr offset, if (s->lockval == LOCK_VALUE) { s->resetlevel = val; if (val & 0x100) { - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); } } break; @@ -438,7 +438,7 @@ static void arm_sysctl_write(void *opaque, hwaddr offset, if (s->lockval == LOCK_VALUE) { s->resetlevel = val; if (val & 0x04) { - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); } } break; diff --git a/hw/misc/aspeed_sdmc.c b/hw/misc/aspeed_sdmc.c index 5f3ac0b6f6..f0b3053fae 100644 --- a/hw/misc/aspeed_sdmc.c +++ b/hw/misc/aspeed_sdmc.c @@ -157,8 +157,8 @@ static int ast2400_rambits(AspeedSDMCState *s) } /* use a common default */ - error_report("warning: Invalid RAM size 0x%" PRIx64 - ". Using default 256M", s->ram_size); + warn_report("Invalid RAM size 0x%" PRIx64 ". Using default 256M", + s->ram_size); s->ram_size = 256 << 20; return ASPEED_SDMC_DRAM_256MB; } @@ -179,8 +179,8 @@ static int ast2500_rambits(AspeedSDMCState *s) } /* use a common default */ - error_report("warning: Invalid RAM size 0x%" PRIx64 - ". Using default 512M", s->ram_size); + warn_report("Invalid RAM size 0x%" PRIx64 ". Using default 512M", + s->ram_size); s->ram_size = 512 << 20; return ASPEED_SDMC_AST2500_512MB; } diff --git a/hw/misc/auxbus.c b/hw/misc/auxbus.c index e4a7ba41de..8a90ddda84 100644 --- a/hw/misc/auxbus.c +++ b/hw/misc/auxbus.c @@ -244,7 +244,7 @@ static void aux_slave_dev_print(Monitor *mon, DeviceState *dev, int indent) monitor_printf(mon, "%*smemory " TARGET_FMT_plx "/" TARGET_FMT_plx "\n", indent, "", - object_property_get_int(OBJECT(s->mmio), "addr", NULL), + object_property_get_uint(OBJECT(s->mmio), "addr", NULL), memory_region_size(s->mmio)); } diff --git a/hw/misc/cbus.c b/hw/misc/cbus.c index 0c207e3104..677274ce3e 100644 --- a/hw/misc/cbus.c +++ b/hw/misc/cbus.c @@ -356,7 +356,7 @@ static inline void retu_write(CBusRetu *s, int reg, uint16_t val) case RETU_REG_WATCHDOG: if (val == 0 && (s->cc[0] & 2)) - qemu_system_shutdown_request(); + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); break; case RETU_REG_TXCR: diff --git a/hw/misc/eccmemctl.c b/hw/misc/eccmemctl.c index a0071f3eae..bb7cc52b5e 100644 --- a/hw/misc/eccmemctl.c +++ b/hw/misc/eccmemctl.c @@ -295,22 +295,29 @@ static void ecc_reset(DeviceState *d) s->regs[ECC_ECR1] = 0; } -static int ecc_init1(SysBusDevice *dev) +static void ecc_init(Object *obj) { - ECCState *s = ECC_MEMCTL(dev); + ECCState *s = ECC_MEMCTL(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); sysbus_init_irq(dev, &s->irq); - s->regs[0] = s->version; - memory_region_init_io(&s->iomem, OBJECT(dev), &ecc_mem_ops, s, "ecc", ECC_SIZE); + + memory_region_init_io(&s->iomem, obj, &ecc_mem_ops, s, "ecc", ECC_SIZE); sysbus_init_mmio(dev, &s->iomem); +} + +static void ecc_realize(DeviceState *dev, Error **errp) +{ + ECCState *s = ECC_MEMCTL(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + s->regs[0] = s->version; if (s->version == ECC_MCC) { // SS-600MP only memory_region_init_io(&s->iomem_diag, OBJECT(dev), &ecc_diag_mem_ops, s, "ecc.diag", ECC_DIAG_SIZE); - sysbus_init_mmio(dev, &s->iomem_diag); + sysbus_init_mmio(sbd, &s->iomem_diag); } - - return 0; } static Property ecc_properties[] = { @@ -321,9 +328,8 @@ static Property ecc_properties[] = { static void ecc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = ecc_init1; + dc->realize = ecc_realize; dc->reset = ecc_reset; dc->vmsd = &vmstate_ecc; dc->props = ecc_properties; @@ -333,6 +339,7 @@ static const TypeInfo ecc_info = { .name = TYPE_ECC_MEMCTL, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(ECCState), + .instance_init = ecc_init, .class_init = ecc_class_init, }; diff --git a/hw/misc/edu.c b/hw/misc/edu.c index 401039c100..01acacf142 100644 --- a/hw/misc/edu.c +++ b/hw/misc/edu.c @@ -343,6 +343,12 @@ static void pci_edu_realize(PCIDevice *pdev, Error **errp) EduState *edu = DO_UPCAST(EduState, pdev, pdev); uint8_t *pci_conf = pdev->config; + pci_config_set_interrupt_pin(pci_conf, 1); + + if (msi_init(pdev, 0, 1, true, false, errp)) { + return; + } + timer_init_ms(&edu->dma_timer, QEMU_CLOCK_VIRTUAL, edu_dma_timer, edu); qemu_mutex_init(&edu->thr_mutex); @@ -350,12 +356,6 @@ static void pci_edu_realize(PCIDevice *pdev, Error **errp) qemu_thread_create(&edu->thread, "edu", edu_fact_thread, edu, QEMU_THREAD_JOINABLE); - pci_config_set_interrupt_pin(pci_conf, 1); - - if (msi_init(pdev, 0, 1, true, false, errp)) { - return; - } - memory_region_init_io(&edu->mmio, OBJECT(edu), &edu_mmio_ops, edu, "edu-mmio", 1 << 20); pci_register_bar(pdev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &edu->mmio); diff --git a/hw/misc/exynos4210_pmu.c b/hw/misc/exynos4210_pmu.c index 63a8ccd355..0d7b64c5b3 100644 --- a/hw/misc/exynos4210_pmu.c +++ b/hw/misc/exynos4210_pmu.c @@ -26,6 +26,7 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" +#include "sysemu/sysemu.h" #ifndef DEBUG_PMU #define DEBUG_PMU 0 @@ -350,7 +351,11 @@ static const Exynos4210PmuReg exynos4210_pmu_regs[] = { {"PAD_RETENTION_MMCB_OPTION", PAD_RETENTION_MMCB_OPTION, 0x00000000}, {"PAD_RETENTION_EBIA_OPTION", PAD_RETENTION_EBIA_OPTION, 0x00000000}, {"PAD_RETENTION_EBIB_OPTION", PAD_RETENTION_EBIB_OPTION, 0x00000000}, - {"PS_HOLD_CONTROL", PS_HOLD_CONTROL, 0x00005200}, + /* + * PS_HOLD_CONTROL: reset value and manually toggle high the DATA bit. + * DATA bit high, set usually by bootloader, keeps system on. + */ + {"PS_HOLD_CONTROL", PS_HOLD_CONTROL, 0x00005200 | BIT(8)}, {"XUSBXTI_CONFIGURATION", XUSBXTI_CONFIGURATION, 0x00000001}, {"XUSBXTI_STATUS", XUSBXTI_STATUS, 0x00000001}, {"XUSBXTI_DURATION", XUSBXTI_DURATION, 0xFFF00000}, @@ -397,6 +402,12 @@ typedef struct Exynos4210PmuState { uint32_t reg[PMU_NUM_OF_REGISTERS]; } Exynos4210PmuState; +static void exynos4210_pmu_poweroff(void) +{ + PRINT_DEBUG("QEMU PMU: PS_HOLD bit down, powering off\n"); + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); +} + static uint64_t exynos4210_pmu_read(void *opaque, hwaddr offset, unsigned size) { @@ -428,6 +439,13 @@ static void exynos4210_pmu_write(void *opaque, hwaddr offset, PRINT_DEBUG_EXTEND("%s <0x%04x> <- 0x%04x\n", reg_p->name, (uint32_t)offset, (uint32_t)val); s->reg[i] = val; + if ((offset == PS_HOLD_CONTROL) && ((val & BIT(8)) == 0)) { + /* + * We are interested only in setting data bit + * of PS_HOLD_CONTROL register to indicate power off request. + */ + exynos4210_pmu_poweroff(); + } return; } reg_p++; diff --git a/hw/misc/exynos4210_rng.c b/hw/misc/exynos4210_rng.c new file mode 100644 index 0000000000..31ebe38e26 --- /dev/null +++ b/hw/misc/exynos4210_rng.c @@ -0,0 +1,277 @@ +/* + * Exynos4210 Pseudo Random Nubmer Generator Emulation + * + * Copyright (c) 2017 Krzysztof Kozlowski <krzk@kernel.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "crypto/random.h" +#include "hw/sysbus.h" +#include "qemu/log.h" + +#define DEBUG_EXYNOS_RNG 0 + +#define DPRINTF(fmt, ...) \ + do { \ + if (DEBUG_EXYNOS_RNG) { \ + printf("exynos4210_rng: " fmt, ## __VA_ARGS__); \ + } \ + } while (0) + +#define TYPE_EXYNOS4210_RNG "exynos4210.rng" +#define EXYNOS4210_RNG(obj) \ + OBJECT_CHECK(Exynos4210RngState, (obj), TYPE_EXYNOS4210_RNG) + +/* + * Exynos4220, PRNG, only polling mode is supported. + */ + +/* RNG_CONTROL_1 register bitfields, reset value: 0x0 */ +#define EXYNOS4210_RNG_CONTROL_1_PRNG 0x8 +#define EXYNOS4210_RNG_CONTROL_1_START_INIT BIT(4) +/* RNG_STATUS register bitfields, reset value: 0x1 */ +#define EXYNOS4210_RNG_STATUS_PRNG_ERROR BIT(7) +#define EXYNOS4210_RNG_STATUS_PRNG_DONE BIT(5) +#define EXYNOS4210_RNG_STATUS_MSG_DONE BIT(4) +#define EXYNOS4210_RNG_STATUS_PARTIAL_DONE BIT(3) +#define EXYNOS4210_RNG_STATUS_PRNG_BUSY BIT(2) +#define EXYNOS4210_RNG_STATUS_SEED_SETTING_DONE BIT(1) +#define EXYNOS4210_RNG_STATUS_BUFFER_READY BIT(0) +#define EXYNOS4210_RNG_STATUS_WRITE_MASK (EXYNOS4210_RNG_STATUS_PRNG_DONE \ + | EXYNOS4210_RNG_STATUS_MSG_DONE \ + | EXYNOS4210_RNG_STATUS_PARTIAL_DONE) + +#define EXYNOS4210_RNG_CONTROL_1 0x0 +#define EXYNOS4210_RNG_STATUS 0x10 +#define EXYNOS4210_RNG_SEED_IN 0x140 +#define EXYNOS4210_RNG_SEED_IN_OFFSET(n) (EXYNOS4210_RNG_SEED_IN + (n * 0x4)) +#define EXYNOS4210_RNG_PRNG 0x160 +#define EXYNOS4210_RNG_PRNG_OFFSET(n) (EXYNOS4210_RNG_PRNG + (n * 0x4)) + +#define EXYNOS4210_RNG_PRNG_NUM 5 + +#define EXYNOS4210_RNG_REGS_MEM_SIZE 0x200 + +typedef struct Exynos4210RngState { + SysBusDevice parent_obj; + MemoryRegion iomem; + + int32_t randr_value[EXYNOS4210_RNG_PRNG_NUM]; + /* bits from 0 to EXYNOS4210_RNG_PRNG_NUM if given seed register was set */ + uint32_t seed_set; + + /* Register values */ + uint32_t reg_control; + uint32_t reg_status; +} Exynos4210RngState; + +static bool exynos4210_rng_seed_ready(const Exynos4210RngState *s) +{ + uint32_t mask = MAKE_64BIT_MASK(0, EXYNOS4210_RNG_PRNG_NUM); + + /* Return true if all the seed-set bits are set. */ + return (s->seed_set & mask) == mask; +} + +static void exynos4210_rng_set_seed(Exynos4210RngState *s, unsigned int i, + uint64_t val) +{ + /* + * We actually ignore the seed and always generate true random numbers. + * Theoretically this should not match the device as Exynos has + * a Pseudo Random Number Generator but testing shown that it always + * generates random numbers regardless of the seed value. + */ + s->seed_set |= BIT(i); + + /* If all seeds were written, update the status to reflect it */ + if (exynos4210_rng_seed_ready(s)) { + s->reg_status |= EXYNOS4210_RNG_STATUS_SEED_SETTING_DONE; + } else { + s->reg_status &= ~EXYNOS4210_RNG_STATUS_SEED_SETTING_DONE; + } +} + +static void exynos4210_rng_run_engine(Exynos4210RngState *s) +{ + Error *err = NULL; + int ret; + + /* Seed set? */ + if ((s->reg_status & EXYNOS4210_RNG_STATUS_SEED_SETTING_DONE) == 0) { + goto out; + } + + /* PRNG engine chosen? */ + if ((s->reg_control & EXYNOS4210_RNG_CONTROL_1_PRNG) == 0) { + goto out; + } + + /* PRNG engine started? */ + if ((s->reg_control & EXYNOS4210_RNG_CONTROL_1_START_INIT) == 0) { + goto out; + } + + /* Get randoms */ + ret = qcrypto_random_bytes((uint8_t *)s->randr_value, + sizeof(s->randr_value), &err); + if (!ret) { + /* Notify that PRNG is ready */ + s->reg_status |= EXYNOS4210_RNG_STATUS_PRNG_DONE; + } else { + error_report_err(err); + } + +out: + /* Always clear start engine bit */ + s->reg_control &= ~EXYNOS4210_RNG_CONTROL_1_START_INIT; +} + +static uint64_t exynos4210_rng_read(void *opaque, hwaddr offset, + unsigned size) +{ + Exynos4210RngState *s = (Exynos4210RngState *)opaque; + uint32_t val = 0; + + assert(size == 4); + + switch (offset) { + case EXYNOS4210_RNG_CONTROL_1: + val = s->reg_control; + break; + + case EXYNOS4210_RNG_STATUS: + val = s->reg_status; + break; + + case EXYNOS4210_RNG_PRNG_OFFSET(0): + case EXYNOS4210_RNG_PRNG_OFFSET(1): + case EXYNOS4210_RNG_PRNG_OFFSET(2): + case EXYNOS4210_RNG_PRNG_OFFSET(3): + case EXYNOS4210_RNG_PRNG_OFFSET(4): + val = s->randr_value[(offset - EXYNOS4210_RNG_PRNG_OFFSET(0)) / 4]; + DPRINTF("returning random @0x%" HWADDR_PRIx ": 0x%" PRIx32 "\n", + offset, val); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad read offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + } + + return val; +} + +static void exynos4210_rng_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + Exynos4210RngState *s = (Exynos4210RngState *)opaque; + + assert(size == 4); + + switch (offset) { + case EXYNOS4210_RNG_CONTROL_1: + DPRINTF("RNG_CONTROL_1 = 0x%" PRIx64 "\n", val); + s->reg_control = val; + exynos4210_rng_run_engine(s); + break; + + case EXYNOS4210_RNG_STATUS: + /* For clearing status fields */ + s->reg_status &= ~EXYNOS4210_RNG_STATUS_WRITE_MASK; + s->reg_status |= val & EXYNOS4210_RNG_STATUS_WRITE_MASK; + break; + + case EXYNOS4210_RNG_SEED_IN_OFFSET(0): + case EXYNOS4210_RNG_SEED_IN_OFFSET(1): + case EXYNOS4210_RNG_SEED_IN_OFFSET(2): + case EXYNOS4210_RNG_SEED_IN_OFFSET(3): + case EXYNOS4210_RNG_SEED_IN_OFFSET(4): + exynos4210_rng_set_seed(s, + (offset - EXYNOS4210_RNG_SEED_IN_OFFSET(0)) / 4, + val); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad write offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + } +} + +static const MemoryRegionOps exynos4210_rng_ops = { + .read = exynos4210_rng_read, + .write = exynos4210_rng_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void exynos4210_rng_reset(DeviceState *dev) +{ + Exynos4210RngState *s = EXYNOS4210_RNG(dev); + + s->reg_control = 0; + s->reg_status = EXYNOS4210_RNG_STATUS_BUFFER_READY; + memset(s->randr_value, 0, sizeof(s->randr_value)); + s->seed_set = 0; +} + +static void exynos4210_rng_init(Object *obj) +{ + Exynos4210RngState *s = EXYNOS4210_RNG(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->iomem, obj, &exynos4210_rng_ops, s, + TYPE_EXYNOS4210_RNG, EXYNOS4210_RNG_REGS_MEM_SIZE); + sysbus_init_mmio(dev, &s->iomem); +} + +static const VMStateDescription exynos4210_rng_vmstate = { + .name = TYPE_EXYNOS4210_RNG, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32_ARRAY(randr_value, Exynos4210RngState, + EXYNOS4210_RNG_PRNG_NUM), + VMSTATE_UINT32(seed_set, Exynos4210RngState), + VMSTATE_UINT32(reg_status, Exynos4210RngState), + VMSTATE_UINT32(reg_control, Exynos4210RngState), + VMSTATE_END_OF_LIST() + } +}; + +static void exynos4210_rng_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = exynos4210_rng_reset; + dc->vmsd = &exynos4210_rng_vmstate; +} + +static const TypeInfo exynos4210_rng_info = { + .name = TYPE_EXYNOS4210_RNG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Exynos4210RngState), + .instance_init = exynos4210_rng_init, + .class_init = exynos4210_rng_class_init, +}; + +static void exynos4210_rng_register(void) +{ + type_register_static(&exynos4210_rng_info); +} + +type_init(exynos4210_rng_register) diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index 475e36a4c7..a58f9ee579 100644 --- a/hw/misc/ivshmem.c +++ b/hw/misc/ivshmem.c @@ -29,7 +29,7 @@ #include "qemu/error-report.h" #include "qemu/event_notifier.h" #include "qom/object_interfaces.h" -#include "sysemu/char.h" +#include "chardev/char-fe.h" #include "sysemu/hostmem.h" #include "sysemu/qtest.h" #include "qapi/visitor.h" @@ -491,9 +491,9 @@ static void setup_interrupt(IVShmemState *s, int vector, Error **errp) static void process_msg_shmem(IVShmemState *s, int fd, Error **errp) { + Error *local_err = NULL; struct stat buf; size_t size; - void *ptr; if (s->ivshmem_bar2) { error_setg(errp, "server sent unexpected shared memory message"); @@ -522,15 +522,13 @@ static void process_msg_shmem(IVShmemState *s, int fd, Error **errp) } /* 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); + memory_region_init_ram_from_fd(&s->server_bar2, OBJECT(s), + "ivshmem.bar2", size, true, fd, &local_err); + if (local_err) { + error_propagate(errp, local_err); return; } - memory_region_init_ram_ptr(&s->server_bar2, OBJECT(s), - "ivshmem.bar2", size, ptr); - memory_region_set_fd(&s->server_bar2, fd); + s->ivshmem_bar2 = &s->server_bar2; } @@ -896,7 +894,7 @@ static void ivshmem_common_realize(PCIDevice *dev, Error **errp) } qemu_chr_fe_set_handlers(&s->server_chr, ivshmem_can_receive, - ivshmem_read, NULL, s, NULL, true); + ivshmem_read, NULL, NULL, s, NULL, true); if (ivshmem_setup_interrupts(s, errp) < 0) { error_prepend(errp, "Failed to initialize interrupts: "); @@ -1011,18 +1009,6 @@ static const TypeInfo ivshmem_common_info = { .class_init = ivshmem_common_class_init, }; -static void ivshmem_check_memdev_is_busy(Object *obj, const char *name, - Object *val, Error **errp) -{ - if (host_memory_backend_is_mapped(MEMORY_BACKEND(val))) { - 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, @@ -1039,6 +1025,8 @@ static const VMStateDescription ivshmem_plain_vmsd = { static Property ivshmem_plain_properties[] = { DEFINE_PROP_ON_OFF_AUTO("master", IVShmemState, master, ON_OFF_AUTO_OFF), + DEFINE_PROP_LINK("memdev", IVShmemState, hostmem, TYPE_MEMORY_BACKEND, + HostMemoryBackend *), DEFINE_PROP_END_OF_LIST(), }; @@ -1046,11 +1034,6 @@ 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); s->not_legacy_32bit = 1; } @@ -1061,6 +1044,11 @@ static void ivshmem_plain_realize(PCIDevice *dev, Error **errp) if (!s->hostmem) { error_setg(errp, "You must specify a 'memdev'"); return; + } else if (host_memory_backend_is_mapped(s->hostmem)) { + char *path = object_get_canonical_path_component(OBJECT(s->hostmem)); + error_setg(errp, "can't use already busy memdev: %s", path); + g_free(path); + return; } ivshmem_common_realize(dev, errp); @@ -1130,7 +1118,7 @@ static void ivshmem_doorbell_realize(PCIDevice *dev, Error **errp) { IVShmemState *s = IVSHMEM_COMMON(dev); - if (!qemu_chr_fe_get_driver(&s->server_chr)) { + if (!qemu_chr_fe_backend_connected(&s->server_chr)) { error_setg(errp, "You must specify a 'chardev'"); return; } @@ -1259,7 +1247,7 @@ static void ivshmem_realize(PCIDevice *dev, Error **errp) " or ivshmem-doorbell instead"); } - if (!!qemu_chr_fe_get_driver(&s->server_chr) + !!s->shmobj != 1) { + if (qemu_chr_fe_backend_connected(&s->server_chr) + !!s->shmobj != 1) { error_setg(errp, "You must specify either 'shm' or 'chardev'"); return; } diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c index 05c02fb3a4..008d8bd4d5 100644 --- a/hw/misc/macio/cuda.c +++ b/hw/misc/macio/cuda.c @@ -612,7 +612,7 @@ static bool cuda_cmd_powerdown(CUDAState *s, return false; } - qemu_system_shutdown_request(); + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); return true; } @@ -624,7 +624,7 @@ static bool cuda_cmd_reset_system(CUDAState *s, return false; } - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); return true; } diff --git a/hw/misc/max111x.c b/hw/misc/max111x.c index 2a277bdb86..6dbdc03677 100644 --- a/hw/misc/max111x.c +++ b/hw/misc/max111x.c @@ -116,7 +116,7 @@ static const VMStateDescription vmstate_max111x = { VMSTATE_UINT8(tb1, MAX111xState), VMSTATE_UINT8(rb2, MAX111xState), VMSTATE_UINT8(rb3, MAX111xState), - VMSTATE_INT32_EQUAL(inputs, MAX111xState), + VMSTATE_INT32_EQUAL(inputs, MAX111xState, NULL), VMSTATE_INT32(com, MAX111xState), VMSTATE_ARRAY_INT32_UNSAFE(input, MAX111xState, inputs, vmstate_info_uint8, uint8_t), diff --git a/hw/misc/mips_cmgcr.c b/hw/misc/mips_cmgcr.c index a1edb53f95..211f6097fd 100644 --- a/hw/misc/mips_cmgcr.c +++ b/hw/misc/mips_cmgcr.c @@ -181,18 +181,6 @@ static void mips_gcr_init(Object *obj) SysBusDevice *sbd = SYS_BUS_DEVICE(obj); MIPSGCRState *s = MIPS_GCR(obj); - object_property_add_link(obj, "gic", TYPE_MEMORY_REGION, - (Object **)&s->gic_mr, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, - &error_abort); - - object_property_add_link(obj, "cpc", TYPE_MEMORY_REGION, - (Object **)&s->cpc_mr, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, - &error_abort); - memory_region_init_io(&s->iomem, OBJECT(s), &gcr_ops, s, "mips-gcr", GCR_ADDRSPACE_SZ); sysbus_init_mmio(sbd, &s->iomem); @@ -227,6 +215,10 @@ static Property mips_gcr_properties[] = { DEFINE_PROP_INT32("num-vp", MIPSGCRState, num_vps, 1), DEFINE_PROP_INT32("gcr-rev", MIPSGCRState, gcr_rev, 0x800), DEFINE_PROP_UINT64("gcr-base", MIPSGCRState, gcr_base, GCR_BASE_ADDR), + DEFINE_PROP_LINK("gic", MIPSGCRState, gic_mr, TYPE_MEMORY_REGION, + MemoryRegion *), + DEFINE_PROP_LINK("cpc", MIPSGCRState, cpc_mr, TYPE_MEMORY_REGION, + MemoryRegion *), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/misc/mmio_interface.c b/hw/misc/mmio_interface.c new file mode 100644 index 0000000000..6f004d2bab --- /dev/null +++ b/hw/misc/mmio_interface.c @@ -0,0 +1,128 @@ +/* + * mmio_interface.c + * + * Copyright (C) 2017 : GreenSocs + * http://www.greensocs.com/ , email: info@greensocs.com + * + * Developed by : + * Frederic Konrad <fred.konrad@greensocs.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option)any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "trace.h" +#include "hw/qdev-properties.h" +#include "hw/misc/mmio_interface.h" +#include "qapi/error.h" + +#ifndef DEBUG_MMIO_INTERFACE +#define DEBUG_MMIO_INTERFACE 0 +#endif + +static uint64_t mmio_interface_counter; + +#define DPRINTF(fmt, ...) do { \ + if (DEBUG_MMIO_INTERFACE) { \ + qemu_log("mmio_interface: 0x%" PRIX64 ": " fmt, s->id, ## __VA_ARGS__);\ + } \ +} while (0); + +static void mmio_interface_init(Object *obj) +{ + MMIOInterface *s = MMIO_INTERFACE(obj); + + if (DEBUG_MMIO_INTERFACE) { + s->id = mmio_interface_counter++; + } + + DPRINTF("interface created\n"); + s->host_ptr = 0; + s->subregion = 0; +} + +static void mmio_interface_realize(DeviceState *dev, Error **errp) +{ + MMIOInterface *s = MMIO_INTERFACE(dev); + + DPRINTF("realize from 0x%" PRIX64 " to 0x%" PRIX64 " map host pointer" + " %p\n", s->start, s->end, s->host_ptr); + + if (!s->host_ptr) { + error_setg(errp, "host_ptr property must be set"); + } + + if (!s->subregion) { + error_setg(errp, "subregion property must be set"); + } + + memory_region_init_ram_ptr(&s->ram_mem, OBJECT(s), "ram", + s->end - s->start + 1, s->host_ptr); + memory_region_set_readonly(&s->ram_mem, s->ro); + memory_region_add_subregion(s->subregion, s->start, &s->ram_mem); +} + +static void mmio_interface_unrealize(DeviceState *dev, Error **errp) +{ + MMIOInterface *s = MMIO_INTERFACE(dev); + + DPRINTF("unrealize from 0x%" PRIX64 " to 0x%" PRIX64 " map host pointer" + " %p\n", s->start, s->end, s->host_ptr); + memory_region_del_subregion(s->subregion, &s->ram_mem); +} + +static void mmio_interface_finalize(Object *obj) +{ + MMIOInterface *s = MMIO_INTERFACE(obj); + + DPRINTF("finalize from 0x%" PRIX64 " to 0x%" PRIX64 " map host pointer" + " %p\n", s->start, s->end, s->host_ptr); + object_unparent(OBJECT(&s->ram_mem)); +} + +static Property mmio_interface_properties[] = { + DEFINE_PROP_UINT64("start", MMIOInterface, start, 0), + DEFINE_PROP_UINT64("end", MMIOInterface, end, 0), + DEFINE_PROP_PTR("host_ptr", MMIOInterface, host_ptr), + DEFINE_PROP_BOOL("ro", MMIOInterface, ro, false), + DEFINE_PROP_MEMORY_REGION("subregion", MMIOInterface, subregion), + DEFINE_PROP_END_OF_LIST(), +}; + +static void mmio_interface_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = mmio_interface_realize; + dc->unrealize = mmio_interface_unrealize; + dc->props = mmio_interface_properties; +} + +static const TypeInfo mmio_interface_info = { + .name = TYPE_MMIO_INTERFACE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(MMIOInterface), + .instance_init = mmio_interface_init, + .instance_finalize = mmio_interface_finalize, + .class_init = mmio_interface_class_init, +}; + +static void mmio_interface_register_types(void) +{ + type_register_static(&mmio_interface_info); +} + +type_init(mmio_interface_register_types) diff --git a/hw/misc/mps2-scc.c b/hw/misc/mps2-scc.c new file mode 100644 index 0000000000..cc58d26f29 --- /dev/null +++ b/hw/misc/mps2-scc.c @@ -0,0 +1,310 @@ +/* + * ARM MPS2 SCC emulation + * + * Copyright (c) 2017 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* This is a model of the SCC (Serial Communication Controller) + * found in the FPGA images of MPS2 development boards. + * + * Documentation of it can be found in the MPS2 TRM: + * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.100112_0100_03_en/index.html + * and also in the Application Notes documenting individual FPGA images. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "trace.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/misc/mps2-scc.h" + +REG32(CFG0, 0) +REG32(CFG1, 4) +REG32(CFG3, 0xc) +REG32(CFG4, 0x10) +REG32(CFGDATA_RTN, 0xa0) +REG32(CFGDATA_OUT, 0xa4) +REG32(CFGCTRL, 0xa8) + FIELD(CFGCTRL, DEVICE, 0, 12) + FIELD(CFGCTRL, RES1, 12, 8) + FIELD(CFGCTRL, FUNCTION, 20, 6) + FIELD(CFGCTRL, RES2, 26, 4) + FIELD(CFGCTRL, WRITE, 30, 1) + FIELD(CFGCTRL, START, 31, 1) +REG32(CFGSTAT, 0xac) + FIELD(CFGSTAT, DONE, 0, 1) + FIELD(CFGSTAT, ERROR, 1, 1) +REG32(DLL, 0x100) +REG32(AID, 0xFF8) +REG32(ID, 0xFFC) + +/* Handle a write via the SYS_CFG channel to the specified function/device. + * Return false on error (reported to guest via SYS_CFGCTRL ERROR bit). + */ +static bool scc_cfg_write(MPS2SCC *s, unsigned function, + unsigned device, uint32_t value) +{ + trace_mps2_scc_cfg_write(function, device, value); + + if (function != 1 || device >= NUM_OSCCLK) { + qemu_log_mask(LOG_GUEST_ERROR, + "MPS2 SCC config write: bad function %d device %d\n", + function, device); + return false; + } + + s->oscclk[device] = value; + return true; +} + +/* Handle a read via the SYS_CFG channel to the specified function/device. + * Return false on error (reported to guest via SYS_CFGCTRL ERROR bit), + * or set *value on success. + */ +static bool scc_cfg_read(MPS2SCC *s, unsigned function, + unsigned device, uint32_t *value) +{ + if (function != 1 || device >= NUM_OSCCLK) { + qemu_log_mask(LOG_GUEST_ERROR, + "MPS2 SCC config read: bad function %d device %d\n", + function, device); + return false; + } + + *value = s->oscclk[device]; + + trace_mps2_scc_cfg_read(function, device, *value); + return true; +} + +static uint64_t mps2_scc_read(void *opaque, hwaddr offset, unsigned size) +{ + MPS2SCC *s = MPS2_SCC(opaque); + uint64_t r; + + switch (offset) { + case A_CFG0: + r = s->cfg0; + break; + case A_CFG1: + r = s->cfg1; + break; + case A_CFG3: + /* These are user-settable DIP switches on the board. We don't + * model that, so just return zeroes. + */ + r = 0; + break; + case A_CFG4: + r = s->cfg4; + break; + case A_CFGDATA_RTN: + r = s->cfgdata_rtn; + break; + case A_CFGDATA_OUT: + r = s->cfgdata_out; + break; + case A_CFGCTRL: + r = s->cfgctrl; + break; + case A_CFGSTAT: + r = s->cfgstat; + break; + case A_DLL: + r = s->dll; + break; + case A_AID: + r = s->aid; + break; + case A_ID: + r = s->id; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "MPS2 SCC read: bad offset %x\n", (int) offset); + r = 0; + break; + } + + trace_mps2_scc_read(offset, r, size); + return r; +} + +static void mps2_scc_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + MPS2SCC *s = MPS2_SCC(opaque); + + trace_mps2_scc_write(offset, value, size); + + switch (offset) { + case A_CFG0: + /* TODO on some boards bit 0 controls RAM remapping */ + s->cfg0 = value; + break; + case A_CFG1: + /* CFG1 bits [7:0] control the board LEDs. We don't currently have + * a mechanism for displaying this graphically, so use a trace event. + */ + trace_mps2_scc_leds(value & 0x80 ? '*' : '.', + value & 0x40 ? '*' : '.', + value & 0x20 ? '*' : '.', + value & 0x10 ? '*' : '.', + value & 0x08 ? '*' : '.', + value & 0x04 ? '*' : '.', + value & 0x02 ? '*' : '.', + value & 0x01 ? '*' : '.'); + s->cfg1 = value; + break; + case A_CFGDATA_OUT: + s->cfgdata_out = value; + break; + case A_CFGCTRL: + /* Writing to CFGCTRL clears SYS_CFGSTAT */ + s->cfgstat = 0; + s->cfgctrl = value & ~(R_CFGCTRL_RES1_MASK | + R_CFGCTRL_RES2_MASK | + R_CFGCTRL_START_MASK); + + if (value & R_CFGCTRL_START_MASK) { + /* Start bit set -- do a read or write (instantaneously) */ + int device = extract32(s->cfgctrl, R_CFGCTRL_DEVICE_SHIFT, + R_CFGCTRL_DEVICE_LENGTH); + int function = extract32(s->cfgctrl, R_CFGCTRL_FUNCTION_SHIFT, + R_CFGCTRL_FUNCTION_LENGTH); + + s->cfgstat = R_CFGSTAT_DONE_MASK; + if (s->cfgctrl & R_CFGCTRL_WRITE_MASK) { + if (!scc_cfg_write(s, function, device, s->cfgdata_out)) { + s->cfgstat |= R_CFGSTAT_ERROR_MASK; + } + } else { + uint32_t result; + if (!scc_cfg_read(s, function, device, &result)) { + s->cfgstat |= R_CFGSTAT_ERROR_MASK; + } else { + s->cfgdata_rtn = result; + } + } + } + break; + case A_DLL: + /* DLL stands for Digital Locked Loop. + * Bits [31:24] (DLL_LOCK_MASK) are writable, and indicate a + * mask of which of the DLL_LOCKED bits [16:23] should be ORed + * together to determine the ALL_UNMASKED_DLLS_LOCKED bit [0]. + * For QEMU, our DLLs are always locked, so we can leave bit 0 + * as 1 always and don't need to recalculate it. + */ + s->dll = deposit32(s->dll, 24, 8, extract32(value, 24, 8)); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "MPS2 SCC write: bad offset 0x%x\n", (int) offset); + break; + } +} + +static const MemoryRegionOps mps2_scc_ops = { + .read = mps2_scc_read, + .write = mps2_scc_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void mps2_scc_reset(DeviceState *dev) +{ + MPS2SCC *s = MPS2_SCC(dev); + int i; + + trace_mps2_scc_reset(); + s->cfg0 = 0; + s->cfg1 = 0; + s->cfgdata_rtn = 0; + s->cfgdata_out = 0; + s->cfgctrl = 0x100000; + s->cfgstat = 0; + s->dll = 0xffff0001; + for (i = 0; i < NUM_OSCCLK; i++) { + s->oscclk[i] = s->oscclk_reset[i]; + } +} + +static void mps2_scc_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + MPS2SCC *s = MPS2_SCC(obj); + + memory_region_init_io(&s->iomem, obj, &mps2_scc_ops, s, "mps2-scc", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); +} + +static void mps2_scc_realize(DeviceState *dev, Error **errp) +{ +} + +static const VMStateDescription mps2_scc_vmstate = { + .name = "mps2-scc", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(cfg0, MPS2SCC), + VMSTATE_UINT32(cfg1, MPS2SCC), + VMSTATE_UINT32(cfgdata_rtn, MPS2SCC), + VMSTATE_UINT32(cfgdata_out, MPS2SCC), + VMSTATE_UINT32(cfgctrl, MPS2SCC), + VMSTATE_UINT32(cfgstat, MPS2SCC), + VMSTATE_UINT32(dll, MPS2SCC), + VMSTATE_UINT32_ARRAY(oscclk, MPS2SCC, NUM_OSCCLK), + VMSTATE_END_OF_LIST() + } +}; + +static Property mps2_scc_properties[] = { + /* Values for various read-only ID registers (which are specific + * to the board model or FPGA image) + */ + DEFINE_PROP_UINT32("scc-cfg4", MPS2SCC, aid, 0), + DEFINE_PROP_UINT32("scc-aid", MPS2SCC, aid, 0), + DEFINE_PROP_UINT32("scc-id", MPS2SCC, aid, 0), + /* These are the initial settings for the source clocks on the board. + * In hardware they can be configured via a config file read by the + * motherboard configuration controller to suit the FPGA image. + * These default values are used by most of the standard FPGA images. + */ + DEFINE_PROP_UINT32("oscclk0", MPS2SCC, oscclk_reset[0], 50000000), + DEFINE_PROP_UINT32("oscclk1", MPS2SCC, oscclk_reset[1], 24576000), + DEFINE_PROP_UINT32("oscclk2", MPS2SCC, oscclk_reset[2], 25000000), + DEFINE_PROP_END_OF_LIST(), +}; + +static void mps2_scc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = mps2_scc_realize; + dc->vmsd = &mps2_scc_vmstate; + dc->reset = mps2_scc_reset; + dc->props = mps2_scc_properties; +} + +static const TypeInfo mps2_scc_info = { + .name = TYPE_MPS2_SCC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MPS2SCC), + .instance_init = mps2_scc_init, + .class_init = mps2_scc_class_init, +}; + +static void mps2_scc_register_types(void) +{ + type_register_static(&mps2_scc_info); +} + +type_init(mps2_scc_register_types); diff --git a/hw/misc/pvpanic.c b/hw/misc/pvpanic.c index 57da7f2199..2b1e9a6450 100644 --- a/hw/misc/pvpanic.c +++ b/hw/misc/pvpanic.c @@ -111,7 +111,7 @@ uint16_t pvpanic_port(void) if (!o) { return 0; } - return object_property_get_int(o, PVPANIC_IOPORT_PROP, NULL); + return object_property_get_uint(o, PVPANIC_IOPORT_PROP, NULL); } static Property pvpanic_isa_properties[] = { diff --git a/hw/misc/slavio_misc.c b/hw/misc/slavio_misc.c index edd5de0702..0b33cdcb61 100644 --- a/hw/misc/slavio_misc.c +++ b/hw/misc/slavio_misc.c @@ -258,7 +258,7 @@ static void slavio_aux2_mem_writeb(void *opaque, hwaddr addr, val &= AUX2_PWROFF; s->aux2 = val; if (val & AUX2_PWROFF) - qemu_system_shutdown_request(); + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); slavio_misc_update_irq(s); } @@ -338,7 +338,7 @@ static void slavio_sysctrl_mem_writel(void *opaque, hwaddr addr, case 0: if (val & SYS_RESET) { s->sysctrl = SYS_RESETSTAT; - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); } break; default: @@ -414,76 +414,73 @@ static const VMStateDescription vmstate_misc = { } }; -static int apc_init1(SysBusDevice *dev) +static void apc_init(Object *obj) { - APCState *s = APC(dev); + APCState *s = APC(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); sysbus_init_irq(dev, &s->cpu_halt); /* Power management (APC) XXX: not a Slavio device */ - memory_region_init_io(&s->iomem, OBJECT(s), &apc_mem_ops, s, + memory_region_init_io(&s->iomem, obj, &apc_mem_ops, s, "apc", MISC_SIZE); sysbus_init_mmio(dev, &s->iomem); - return 0; } -static int slavio_misc_init1(SysBusDevice *sbd) +static void slavio_misc_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - MiscState *s = SLAVIO_MISC(dev); + DeviceState *dev = DEVICE(obj); + MiscState *s = SLAVIO_MISC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); sysbus_init_irq(sbd, &s->irq); sysbus_init_irq(sbd, &s->fdc_tc); /* 8 bit registers */ /* Slavio control */ - memory_region_init_io(&s->cfg_iomem, OBJECT(s), &slavio_cfg_mem_ops, s, + memory_region_init_io(&s->cfg_iomem, obj, &slavio_cfg_mem_ops, s, "configuration", MISC_SIZE); sysbus_init_mmio(sbd, &s->cfg_iomem); /* Diagnostics */ - memory_region_init_io(&s->diag_iomem, OBJECT(s), &slavio_diag_mem_ops, s, + memory_region_init_io(&s->diag_iomem, obj, &slavio_diag_mem_ops, s, "diagnostic", MISC_SIZE); sysbus_init_mmio(sbd, &s->diag_iomem); /* Modem control */ - memory_region_init_io(&s->mdm_iomem, OBJECT(s), &slavio_mdm_mem_ops, s, + memory_region_init_io(&s->mdm_iomem, obj, &slavio_mdm_mem_ops, s, "modem", MISC_SIZE); sysbus_init_mmio(sbd, &s->mdm_iomem); /* 16 bit registers */ /* ss600mp diag LEDs */ - memory_region_init_io(&s->led_iomem, OBJECT(s), &slavio_led_mem_ops, s, + memory_region_init_io(&s->led_iomem, obj, &slavio_led_mem_ops, s, "leds", LED_SIZE); sysbus_init_mmio(sbd, &s->led_iomem); /* 32 bit registers */ /* System control */ - memory_region_init_io(&s->sysctrl_iomem, OBJECT(s), &slavio_sysctrl_mem_ops, s, + memory_region_init_io(&s->sysctrl_iomem, obj, &slavio_sysctrl_mem_ops, s, "system-control", SYSCTRL_SIZE); sysbus_init_mmio(sbd, &s->sysctrl_iomem); /* AUX 1 (Misc System Functions) */ - memory_region_init_io(&s->aux1_iomem, OBJECT(s), &slavio_aux1_mem_ops, s, + memory_region_init_io(&s->aux1_iomem, obj, &slavio_aux1_mem_ops, s, "misc-system-functions", MISC_SIZE); sysbus_init_mmio(sbd, &s->aux1_iomem); /* AUX 2 (Software Powerdown Control) */ - memory_region_init_io(&s->aux2_iomem, OBJECT(s), &slavio_aux2_mem_ops, s, + memory_region_init_io(&s->aux2_iomem, obj, &slavio_aux2_mem_ops, s, "software-powerdown-control", MISC_SIZE); sysbus_init_mmio(sbd, &s->aux2_iomem); qdev_init_gpio_in(dev, slavio_set_power_fail, 1); - - return 0; } static void slavio_misc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = slavio_misc_init1; dc->reset = slavio_misc_reset; dc->vmsd = &vmstate_misc; } @@ -492,21 +489,15 @@ static const TypeInfo slavio_misc_info = { .name = TYPE_SLAVIO_MISC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(MiscState), + .instance_init = slavio_misc_init, .class_init = slavio_misc_class_init, }; -static void apc_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = apc_init1; -} - static const TypeInfo apc_info = { .name = TYPE_APC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(MiscState), - .class_init = apc_class_init, + .instance_init = apc_init, }; static void slavio_misc_register_types(void) diff --git a/hw/misc/tmp421.c b/hw/misc/tmp421.c new file mode 100644 index 0000000000..4a505abbce --- /dev/null +++ b/hw/misc/tmp421.c @@ -0,0 +1,402 @@ +/* + * Texas Instruments TMP421 temperature sensor. + * + * Copyright (c) 2016 IBM Corporation. + * + * Largely inspired by : + * + * Texas Instruments TMP105 temperature sensor. + * + * Copyright (C) 2008 Nokia Corporation + * Written by Andrzej Zaborowski <andrew@openedhand.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "hw/i2c/i2c.h" +#include "qapi/error.h" +#include "qapi/visitor.h" + +/* Manufacturer / Device ID's */ +#define TMP421_MANUFACTURER_ID 0x55 +#define TMP421_DEVICE_ID 0x21 +#define TMP422_DEVICE_ID 0x22 +#define TMP423_DEVICE_ID 0x23 + +typedef struct DeviceInfo { + int model; + const char *name; +} DeviceInfo; + +static const DeviceInfo devices[] = { + { TMP421_DEVICE_ID, "tmp421" }, + { TMP422_DEVICE_ID, "tmp422" }, + { TMP423_DEVICE_ID, "tmp423" }, +}; + +typedef struct TMP421State { + /*< private >*/ + I2CSlave i2c; + /*< public >*/ + + int16_t temperature[4]; + + uint8_t status; + uint8_t config[2]; + uint8_t rate; + + uint8_t len; + uint8_t buf[2]; + uint8_t pointer; + +} TMP421State; + +typedef struct TMP421Class { + I2CSlaveClass parent_class; + DeviceInfo *dev; +} TMP421Class; + +#define TYPE_TMP421 "tmp421-generic" +#define TMP421(obj) OBJECT_CHECK(TMP421State, (obj), TYPE_TMP421) + +#define TMP421_CLASS(klass) \ + OBJECT_CLASS_CHECK(TMP421Class, (klass), TYPE_TMP421) +#define TMP421_GET_CLASS(obj) \ + OBJECT_GET_CLASS(TMP421Class, (obj), TYPE_TMP421) + +/* the TMP421 registers */ +#define TMP421_STATUS_REG 0x08 +#define TMP421_STATUS_BUSY (1 << 7) +#define TMP421_CONFIG_REG_1 0x09 +#define TMP421_CONFIG_RANGE (1 << 2) +#define TMP421_CONFIG_SHUTDOWN (1 << 6) +#define TMP421_CONFIG_REG_2 0x0A +#define TMP421_CONFIG_RC (1 << 2) +#define TMP421_CONFIG_LEN (1 << 3) +#define TMP421_CONFIG_REN (1 << 4) +#define TMP421_CONFIG_REN2 (1 << 5) +#define TMP421_CONFIG_REN3 (1 << 6) + +#define TMP421_CONVERSION_RATE_REG 0x0B +#define TMP421_ONE_SHOT 0x0F + +#define TMP421_RESET 0xFC +#define TMP421_MANUFACTURER_ID_REG 0xFE +#define TMP421_DEVICE_ID_REG 0xFF + +#define TMP421_TEMP_MSB0 0x00 +#define TMP421_TEMP_MSB1 0x01 +#define TMP421_TEMP_MSB2 0x02 +#define TMP421_TEMP_MSB3 0x03 +#define TMP421_TEMP_LSB0 0x10 +#define TMP421_TEMP_LSB1 0x11 +#define TMP421_TEMP_LSB2 0x12 +#define TMP421_TEMP_LSB3 0x13 + +static const int32_t mins[2] = { -40000, -55000 }; +static const int32_t maxs[2] = { 127000, 150000 }; + +static void tmp421_get_temperature(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + TMP421State *s = TMP421(obj); + bool ext_range = (s->config[0] & TMP421_CONFIG_RANGE); + int offset = ext_range * 64 * 256; + int64_t value; + int tempid; + + if (sscanf(name, "temperature%d", &tempid) != 1) { + error_setg(errp, "error reading %s: %m", name); + return; + } + + if (tempid >= 4 || tempid < 0) { + error_setg(errp, "error reading %s", name); + return; + } + + value = ((s->temperature[tempid] - offset) * 1000 + 128) / 256; + + visit_type_int(v, name, &value, errp); +} + +/* Units are 0.001 centigrades relative to 0 C. s->temperature is 8.8 + * fixed point, so units are 1/256 centigrades. A simple ratio will do. + */ +static void tmp421_set_temperature(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + TMP421State *s = TMP421(obj); + Error *local_err = NULL; + int64_t temp; + bool ext_range = (s->config[0] & TMP421_CONFIG_RANGE); + int offset = ext_range * 64 * 256; + int tempid; + + visit_type_int(v, name, &temp, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + if (temp >= maxs[ext_range] || temp < mins[ext_range]) { + error_setg(errp, "value %" PRId64 ".%03" PRIu64 " °C is out of range", + temp / 1000, temp % 1000); + return; + } + + if (sscanf(name, "temperature%d", &tempid) != 1) { + error_setg(errp, "error reading %s: %m", name); + return; + } + + if (tempid >= 4 || tempid < 0) { + error_setg(errp, "error reading %s", name); + return; + } + + s->temperature[tempid] = (int16_t) ((temp * 256 - 128) / 1000) + offset; +} + +static void tmp421_read(TMP421State *s) +{ + TMP421Class *sc = TMP421_GET_CLASS(s); + + s->len = 0; + + switch (s->pointer) { + case TMP421_MANUFACTURER_ID_REG: + s->buf[s->len++] = TMP421_MANUFACTURER_ID; + break; + case TMP421_DEVICE_ID_REG: + s->buf[s->len++] = sc->dev->model; + break; + case TMP421_CONFIG_REG_1: + s->buf[s->len++] = s->config[0]; + break; + case TMP421_CONFIG_REG_2: + s->buf[s->len++] = s->config[1]; + break; + case TMP421_CONVERSION_RATE_REG: + s->buf[s->len++] = s->rate; + break; + case TMP421_STATUS_REG: + s->buf[s->len++] = s->status; + break; + + /* FIXME: check for channel enablement in config registers */ + case TMP421_TEMP_MSB0: + s->buf[s->len++] = (((uint16_t) s->temperature[0]) >> 8); + s->buf[s->len++] = (((uint16_t) s->temperature[0]) >> 0) & 0xf0; + break; + case TMP421_TEMP_MSB1: + s->buf[s->len++] = (((uint16_t) s->temperature[1]) >> 8); + s->buf[s->len++] = (((uint16_t) s->temperature[1]) >> 0) & 0xf0; + break; + case TMP421_TEMP_MSB2: + s->buf[s->len++] = (((uint16_t) s->temperature[2]) >> 8); + s->buf[s->len++] = (((uint16_t) s->temperature[2]) >> 0) & 0xf0; + break; + case TMP421_TEMP_MSB3: + s->buf[s->len++] = (((uint16_t) s->temperature[3]) >> 8); + s->buf[s->len++] = (((uint16_t) s->temperature[3]) >> 0) & 0xf0; + break; + case TMP421_TEMP_LSB0: + s->buf[s->len++] = (((uint16_t) s->temperature[0]) >> 0) & 0xf0; + break; + case TMP421_TEMP_LSB1: + s->buf[s->len++] = (((uint16_t) s->temperature[1]) >> 0) & 0xf0; + break; + case TMP421_TEMP_LSB2: + s->buf[s->len++] = (((uint16_t) s->temperature[2]) >> 0) & 0xf0; + break; + case TMP421_TEMP_LSB3: + s->buf[s->len++] = (((uint16_t) s->temperature[3]) >> 0) & 0xf0; + break; + } +} + +static void tmp421_reset(I2CSlave *i2c); + +static void tmp421_write(TMP421State *s) +{ + switch (s->pointer) { + case TMP421_CONVERSION_RATE_REG: + s->rate = s->buf[0]; + break; + case TMP421_CONFIG_REG_1: + s->config[0] = s->buf[0]; + break; + case TMP421_CONFIG_REG_2: + s->config[1] = s->buf[0]; + break; + case TMP421_RESET: + tmp421_reset(I2C_SLAVE(s)); + break; + } +} + +static int tmp421_rx(I2CSlave *i2c) +{ + TMP421State *s = TMP421(i2c); + + if (s->len < 2) { + return s->buf[s->len++]; + } else { + return 0xff; + } +} + +static int tmp421_tx(I2CSlave *i2c, uint8_t data) +{ + TMP421State *s = TMP421(i2c); + + if (s->len == 0) { + /* first byte is the register pointer for a read or write + * operation */ + s->pointer = data; + s->len++; + } else if (s->len == 1) { + /* second byte is the data to write. The device only supports + * one byte writes */ + s->buf[0] = data; + tmp421_write(s); + } + + return 0; +} + +static int tmp421_event(I2CSlave *i2c, enum i2c_event event) +{ + TMP421State *s = TMP421(i2c); + + if (event == I2C_START_RECV) { + tmp421_read(s); + } + + s->len = 0; + return 0; +} + +static const VMStateDescription vmstate_tmp421 = { + .name = "TMP421", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT8(len, TMP421State), + VMSTATE_UINT8_ARRAY(buf, TMP421State, 2), + VMSTATE_UINT8(pointer, TMP421State), + VMSTATE_UINT8_ARRAY(config, TMP421State, 2), + VMSTATE_UINT8(status, TMP421State), + VMSTATE_UINT8(rate, TMP421State), + VMSTATE_INT16_ARRAY(temperature, TMP421State, 4), + VMSTATE_I2C_SLAVE(i2c, TMP421State), + VMSTATE_END_OF_LIST() + } +}; + +static void tmp421_reset(I2CSlave *i2c) +{ + TMP421State *s = TMP421(i2c); + TMP421Class *sc = TMP421_GET_CLASS(s); + + memset(s->temperature, 0, sizeof(s->temperature)); + s->pointer = 0; + + s->config[0] = 0; /* TMP421_CONFIG_RANGE */ + + /* resistance correction and channel enablement */ + switch (sc->dev->model) { + case TMP421_DEVICE_ID: + s->config[1] = 0x1c; + break; + case TMP422_DEVICE_ID: + s->config[1] = 0x3c; + break; + case TMP423_DEVICE_ID: + s->config[1] = 0x7c; + break; + } + + s->rate = 0x7; /* 8Hz */ + s->status = 0; +} + +static int tmp421_init(I2CSlave *i2c) +{ + TMP421State *s = TMP421(i2c); + + tmp421_reset(&s->i2c); + + return 0; +} + +static void tmp421_initfn(Object *obj) +{ + object_property_add(obj, "temperature0", "int", + tmp421_get_temperature, + tmp421_set_temperature, NULL, NULL, NULL); + object_property_add(obj, "temperature1", "int", + tmp421_get_temperature, + tmp421_set_temperature, NULL, NULL, NULL); + object_property_add(obj, "temperature2", "int", + tmp421_get_temperature, + tmp421_set_temperature, NULL, NULL, NULL); + object_property_add(obj, "temperature3", "int", + tmp421_get_temperature, + tmp421_set_temperature, NULL, NULL, NULL); +} + +static void tmp421_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); + TMP421Class *sc = TMP421_CLASS(klass); + + k->init = tmp421_init; + k->event = tmp421_event; + k->recv = tmp421_rx; + k->send = tmp421_tx; + dc->vmsd = &vmstate_tmp421; + sc->dev = (DeviceInfo *) data; +} + +static const TypeInfo tmp421_info = { + .name = TYPE_TMP421, + .parent = TYPE_I2C_SLAVE, + .instance_size = sizeof(TMP421State), + .class_size = sizeof(TMP421Class), + .instance_init = tmp421_initfn, + .abstract = true, +}; + +static void tmp421_register_types(void) +{ + int i; + + type_register_static(&tmp421_info); + for (i = 0; i < ARRAY_SIZE(devices); ++i) { + TypeInfo ti = { + .name = devices[i].name, + .parent = TYPE_TMP421, + .class_init = tmp421_class_init, + .class_data = (void *) &devices[i], + }; + type_register(&ti); + } +} + +type_init(tmp421_register_types) diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 0cc556ca9f..28b8cd1c2e 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -53,3 +53,11 @@ milkymist_pfpu_pulse_irq(void) "Pulse IRQ" # hw/misc/aspeed_scu.c aspeed_scu_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 + +# hw/misc/mps2_scc.c +mps2_scc_read(uint64_t offset, uint64_t data, unsigned size) "MPS2 SCC read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +mps2_scc_write(uint64_t offset, uint64_t data, unsigned size) "MPS2 SCC write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +mps2_scc_reset(void) "MPS2 SCC: reset" +mps2_scc_leds(char led7, char led6, char led5, char led4, char led3, char led2, char led1, char led0) "MPS2 SCC LEDs: %c%c%c%c%c%c%c%c" +mps2_scc_cfg_write(unsigned function, unsigned device, uint32_t value) "MPS2 SCC config write: function %d device %d data 0x%" PRIx32 +mps2_scc_cfg_read(unsigned function, unsigned device, uint32_t value) "MPS2 SCC config read: function %d device %d data 0x%" PRIx32 diff --git a/hw/misc/zynq_slcr.c b/hw/misc/zynq_slcr.c index 7891219001..44304d48be 100644 --- a/hw/misc/zynq_slcr.c +++ b/hw/misc/zynq_slcr.c @@ -405,7 +405,7 @@ static void zynq_slcr_write(void *opaque, hwaddr offset, switch (offset) { case PSS_RST_CTRL: if (val & R_PSS_RST_CTRL_SOFT_RST) { - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); } break; } diff --git a/hw/moxie/moxiesim.c b/hw/moxie/moxiesim.c index 3069834cf4..df3f1249ae 100644 --- a/hw/moxie/moxiesim.c +++ b/hw/moxie/moxiesim.c @@ -129,11 +129,9 @@ static void moxiesim_init(MachineState *machine) /* Allocate RAM. */ memory_region_init_ram(ram, NULL, "moxiesim.ram", ram_size, &error_fatal); - vmstate_register_ram_global(ram); memory_region_add_subregion(address_space_mem, ram_base, ram); - memory_region_init_ram(rom, NULL, "moxie.rom", 128*0x1000, &error_fatal); - vmstate_register_ram_global(rom); + memory_region_init_ram(rom, NULL, "moxie.rom", 128 * 0x1000, &error_fatal); memory_region_add_subregion(get_system_memory(), 0x1000, rom); if (kernel_filename) { diff --git a/hw/net/dp8393x.c b/hw/net/dp8393x.c index b53fcaa8bc..f2d2ce344c 100644 --- a/hw/net/dp8393x.c +++ b/hw/net/dp8393x.c @@ -887,7 +887,7 @@ static void dp8393x_realize(DeviceState *dev, Error **errp) s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s); s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */ - memory_region_init_ram(&s->prom, OBJECT(dev), + memory_region_init_ram_nomigrate(&s->prom, OBJECT(dev), "dp8393x-prom", SONIC_PROM_SIZE, &local_err); if (local_err) { error_propagate(errp, local_err); diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c index 6e234938db..6c42b4478c 100644 --- a/hw/net/e1000e.c +++ b/hw/net/e1000e.c @@ -47,6 +47,7 @@ #include "e1000e_core.h" #include "trace.h" +#include "qapi/error.h" #define TYPE_E1000E "e1000e" #define E1000E(obj) OBJECT_CHECK(E1000EState, (obj), TYPE_E1000E) @@ -372,21 +373,26 @@ e1000e_gen_dsn(uint8_t *mac) static int e1000e_add_pm_capability(PCIDevice *pdev, uint8_t offset, uint16_t pmc) { - int ret = pci_add_capability(pdev, PCI_CAP_ID_PM, offset, PCI_PM_SIZEOF); + Error *local_err = NULL; + int ret = pci_add_capability(pdev, PCI_CAP_ID_PM, offset, + PCI_PM_SIZEOF, &local_err); - if (ret >= 0) { - pci_set_word(pdev->config + offset + PCI_PM_PMC, - PCI_PM_CAP_VER_1_1 | - pmc); + if (local_err) { + error_report_err(local_err); + return ret; + } - pci_set_word(pdev->wmask + offset + PCI_PM_CTRL, - PCI_PM_CTRL_STATE_MASK | - PCI_PM_CTRL_PME_ENABLE | - PCI_PM_CTRL_DATA_SEL_MASK); + pci_set_word(pdev->config + offset + PCI_PM_PMC, + PCI_PM_CAP_VER_1_1 | + pmc); - pci_set_word(pdev->w1cmask + offset + PCI_PM_CTRL, - PCI_PM_CTRL_PME_STATUS); - } + pci_set_word(pdev->wmask + offset + PCI_PM_CTRL, + PCI_PM_CTRL_STATE_MASK | + PCI_PM_CTRL_PME_ENABLE | + PCI_PM_CTRL_DATA_SEL_MASK); + + pci_set_word(pdev->w1cmask + offset + PCI_PM_CTRL, + PCI_PM_CTRL_PME_STATUS); return ret; } @@ -645,12 +651,12 @@ static PropertyInfo e1000e_prop_disable_vnet, static Property e1000e_properties[] = { DEFINE_NIC_PROPERTIES(E1000EState, conf), - DEFINE_PROP_DEFAULT("disable_vnet_hdr", E1000EState, disable_vnet, false, + DEFINE_PROP_SIGNED("disable_vnet_hdr", E1000EState, disable_vnet, false, e1000e_prop_disable_vnet, bool), - DEFINE_PROP_DEFAULT("subsys_ven", E1000EState, subsys_ven, + DEFINE_PROP_SIGNED("subsys_ven", E1000EState, subsys_ven, PCI_VENDOR_ID_INTEL, e1000e_prop_subsys_ven, uint16_t), - DEFINE_PROP_DEFAULT("subsys", E1000EState, subsys, 0, + DEFINE_PROP_SIGNED("subsys", E1000EState, subsys, 0, e1000e_prop_subsys, uint16_t), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c index 4bf71f2d85..5a4774aab4 100644 --- a/hw/net/eepro100.c +++ b/hw/net/eepro100.c @@ -48,6 +48,7 @@ #include "sysemu/sysemu.h" #include "sysemu/dma.h" #include "qemu/bitops.h" +#include "qapi/error.h" /* QEMU sends frames smaller than 60 bytes to ethernet nics. * Such frames are rejected by real nics and their emulations. @@ -494,7 +495,7 @@ static void eepro100_fcp_interrupt(EEPRO100State * s) } #endif -static void e100_pci_reset(EEPRO100State * s) +static void e100_pci_reset(EEPRO100State *s, Error **errp) { E100PCIDeviceInfo *info = eepro100_get_class(s); uint32_t device = s->device; @@ -570,8 +571,12 @@ static void e100_pci_reset(EEPRO100State * s) /* Power Management Capabilities */ int cfg_offset = 0xdc; int r = pci_add_capability(&s->dev, PCI_CAP_ID_PM, - cfg_offset, PCI_PM_SIZEOF); - assert(r >= 0); + cfg_offset, PCI_PM_SIZEOF, + errp); + if (r < 0) { + return; + } + pci_set_word(pci_conf + cfg_offset + PCI_PM_PMC, 0x7e21); #if 0 /* TODO: replace dummy code for power management emulation. */ /* TODO: Power Management Control / Status. */ @@ -1858,12 +1863,17 @@ static void e100_nic_realize(PCIDevice *pci_dev, Error **errp) { EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev); E100PCIDeviceInfo *info = eepro100_get_class(s); + Error *local_err = NULL; TRACE(OTHER, logout("\n")); s->device = info->device; - e100_pci_reset(s); + e100_pci_reset(s, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } /* Add 64 * 2 EEPROM. i82557 and i82558 support a 64 word EEPROM, * i82559 and later support 64 or 256 word EEPROM. */ diff --git a/hw/net/milkymist-minimac2.c b/hw/net/milkymist-minimac2.c index c3a12e1197..3eaa19dfde 100644 --- a/hw/net/milkymist-minimac2.c +++ b/hw/net/milkymist-minimac2.c @@ -466,7 +466,7 @@ static int milkymist_minimac2_init(SysBusDevice *sbd) sysbus_init_mmio(sbd, &s->regs_region); /* register buffers memory */ - memory_region_init_ram(&s->buffers, OBJECT(dev), "milkymist-minimac2.buffers", + memory_region_init_ram_nomigrate(&s->buffers, OBJECT(dev), "milkymist-minimac2.buffers", buffers_size, &error_fatal); vmstate_register_ram_global(&s->buffers); s->rx0_buf = memory_region_get_ram_ptr(&s->buffers); diff --git a/hw/net/rocker/rocker.c b/hw/net/rocker/rocker.c index 6e70fddee3..4f0f6d71e5 100644 --- a/hw/net/rocker/rocker.c +++ b/hw/net/rocker/rocker.c @@ -244,11 +244,9 @@ static int tx_consume(Rocker *r, DescInfo *info) goto err_no_mem; } - if (pci_dma_read(dev, frag_addr, iov[iovcnt].iov_base, - iov[iovcnt].iov_len)) { - err = -ROCKER_ENXIO; - goto err_bad_io; - } + pci_dma_read(dev, frag_addr, iov[iovcnt].iov_base, + iov[iovcnt].iov_len); + iovcnt++; } @@ -261,7 +259,6 @@ static int tx_consume(Rocker *r, DescInfo *info) err = fp_port_eg(r->fp_port[port], iov, iovcnt); err_too_many_frags: -err_bad_io: err_no_mem: err_bad_attr: for (i = 0; i < ROCKER_TX_FRAGS_MAX; i++) { diff --git a/hw/net/rocker/rocker_desc.c b/hw/net/rocker/rocker_desc.c index ac02797b7e..6184c40f72 100644 --- a/hw/net/rocker/rocker_desc.c +++ b/hw/net/rocker/rocker_desc.c @@ -69,9 +69,7 @@ char *desc_get_buf(DescInfo *info, bool read_only) return NULL; } - if (pci_dma_read(dev, le64_to_cpu(info->desc.buf_addr), info->buf, size)) { - return NULL; - } + pci_dma_read(dev, le64_to_cpu(info->desc.buf_addr), info->buf, size); return info->buf; } diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 22874a9777..e037db63a3 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -77,6 +77,7 @@ static const int user_feature_bits[] = { VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_MRG_RXBUF, VIRTIO_NET_F_MTU, + VIRTIO_F_IOMMU_PLATFORM, /* This bit implies RARP isn't sent by QEMU out of band */ VIRTIO_NET_F_GUEST_ANNOUNCE, diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 98bd683f31..148071a396 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -25,6 +25,7 @@ #include "qapi/qmp/qjson.h" #include "qapi-event.h" #include "hw/virtio/virtio-access.h" +#include "migration/misc.h" #define VIRTIO_NET_VM_VERSION 11 @@ -33,8 +34,11 @@ /* previously fixed value */ #define VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE 256 +#define VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE 256 + /* for now, only allow larger queues; with virtio-1, guest can downsize */ #define VIRTIO_NET_RX_QUEUE_MIN_SIZE VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE +#define VIRTIO_NET_TX_QUEUE_MIN_SIZE VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE /* * Calculate the number of bytes up to and including the given 'field' of @@ -494,6 +498,24 @@ static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs, } } +static int virtio_net_max_tx_queue_size(VirtIONet *n) +{ + NetClientState *peer = n->nic_conf.peers.ncs[0]; + + /* + * Backends other than vhost-user don't support max queue size. + */ + if (!peer) { + return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE; + } + + if (peer->info->type != NET_CLIENT_DRIVER_VHOST_USER) { + return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE; + } + + return VIRTQUEUE_MAX_SIZE; +} + static int peer_attach(VirtIONet *n, int index) { NetClientState *nc = qemu_get_subqueue(n->nic, index); @@ -589,7 +611,15 @@ static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features, if (!get_vhost_net(nc->peer)) { return features; } - return vhost_net_get_features(get_vhost_net(nc->peer), features); + features = vhost_net_get_features(get_vhost_net(nc->peer), features); + vdev->backend_features = features; + + if (n->mtu_bypass_backend && + (n->host_features & 1ULL << VIRTIO_NET_F_MTU)) { + features |= (1ULL << VIRTIO_NET_F_MTU); + } + + return features; } static uint64_t virtio_net_bad_features(VirtIODevice *vdev) @@ -640,6 +670,11 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features) VirtIONet *n = VIRTIO_NET(vdev); int i; + if (n->mtu_bypass_backend && + !virtio_has_feature(vdev->backend_features, VIRTIO_NET_F_MTU)) { + features &= ~(1ULL << VIRTIO_NET_F_MTU); + } + virtio_net_set_multiqueue(n, virtio_has_feature(features, VIRTIO_NET_F_MQ)); @@ -723,6 +758,8 @@ static int virtio_net_handle_offloads(VirtIONet *n, uint8_t cmd, if (cmd == VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET) { uint64_t supported_offloads; + offloads = virtio_ldq_p(vdev, &offloads); + if (!n->has_vnet_hdr) { return VIRTIO_NET_ERR; } @@ -1494,15 +1531,18 @@ static void virtio_net_add_queue(VirtIONet *n, int index) n->vqs[index].rx_vq = virtio_add_queue(vdev, n->net_conf.rx_queue_size, virtio_net_handle_rx); + if (n->net_conf.tx && !strcmp(n->net_conf.tx, "timer")) { n->vqs[index].tx_vq = - virtio_add_queue(vdev, 256, virtio_net_handle_tx_timer); + virtio_add_queue(vdev, n->net_conf.tx_queue_size, + virtio_net_handle_tx_timer); n->vqs[index].tx_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, virtio_net_tx_timer, &n->vqs[index]); } else { n->vqs[index].tx_vq = - virtio_add_queue(vdev, 256, virtio_net_handle_tx_bh); + virtio_add_queue(vdev, n->net_conf.tx_queue_size, + virtio_net_handle_tx_bh); n->vqs[index].tx_bh = qemu_bh_new(virtio_net_tx_bh, &n->vqs[index]); } @@ -1904,7 +1944,7 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) */ if (n->net_conf.rx_queue_size < VIRTIO_NET_RX_QUEUE_MIN_SIZE || n->net_conf.rx_queue_size > VIRTQUEUE_MAX_SIZE || - (n->net_conf.rx_queue_size & (n->net_conf.rx_queue_size - 1))) { + !is_power_of_2(n->net_conf.rx_queue_size)) { error_setg(errp, "Invalid rx_queue_size (= %" PRIu16 "), " "must be a power of 2 between %d and %d.", n->net_conf.rx_queue_size, VIRTIO_NET_RX_QUEUE_MIN_SIZE, @@ -1913,6 +1953,17 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) return; } + if (n->net_conf.tx_queue_size < VIRTIO_NET_TX_QUEUE_MIN_SIZE || + n->net_conf.tx_queue_size > VIRTQUEUE_MAX_SIZE || + !is_power_of_2(n->net_conf.tx_queue_size)) { + error_setg(errp, "Invalid tx_queue_size (= %" PRIu16 "), " + "must be a power of 2 between %d and %d", + n->net_conf.tx_queue_size, VIRTIO_NET_TX_QUEUE_MIN_SIZE, + VIRTQUEUE_MAX_SIZE); + virtio_cleanup(vdev); + return; + } + n->max_queues = MAX(n->nic_conf.peers.queues, 1); if (n->max_queues * 2 + 1 > VIRTIO_QUEUE_MAX) { error_setg(errp, "Invalid number of queues (= %" PRIu32 "), " @@ -1933,6 +1984,9 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) error_report("Defaulting to \"bh\""); } + n->net_conf.tx_queue_size = MIN(virtio_net_max_tx_queue_size(n), + n->net_conf.tx_queue_size); + for (i = 0; i < n->max_queues; i++) { virtio_net_add_queue(n, i); } @@ -2092,7 +2146,11 @@ static Property virtio_net_properties[] = { DEFINE_PROP_STRING("tx", VirtIONet, net_conf.tx), DEFINE_PROP_UINT16("rx_queue_size", VirtIONet, net_conf.rx_queue_size, VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE), + DEFINE_PROP_UINT16("tx_queue_size", VirtIONet, net_conf.tx_queue_size, + VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE), DEFINE_PROP_UINT16("host_mtu", VirtIONet, net_conf.mtu, 0), + DEFINE_PROP_BOOL("x-mtu-bypass-backend", VirtIONet, mtu_bypass_backend, + true), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index 8b1fab24fd..a19a7a31dd 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -26,6 +26,7 @@ #include "qemu/bswap.h" #include "hw/pci/msix.h" #include "hw/pci/msi.h" +#include "migration/register.h" #include "vmxnet3.h" #include "vmxnet_debug.h" @@ -2262,6 +2263,11 @@ static const MemoryRegionOps b1_ops = { }, }; +static SaveVMHandlers savevm_vmxnet3_msix = { + .save_state = vmxnet3_msix_save, + .load_state = vmxnet3_msix_load, +}; + static uint64_t vmxnet3_device_serial_num(VMXNET3State *s) { uint64_t dsn_payload; @@ -2331,8 +2337,7 @@ static void vmxnet3_pci_realize(PCIDevice *pci_dev, Error **errp) vmxnet3_device_serial_num(s)); } - register_savevm(dev, "vmxnet3-msix", -1, 1, - vmxnet3_msix_save, vmxnet3_msix_load, s); + register_savevm_live(dev, "vmxnet3-msix", -1, 1, &savevm_vmxnet3_msix, s); } static void vmxnet3_instance_init(Object *obj) diff --git a/hw/net/xgmac.c b/hw/net/xgmac.c index 46b1aa17fa..0843bf185c 100644 --- a/hw/net/xgmac.c +++ b/hw/net/xgmac.c @@ -26,7 +26,6 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" -#include "sysemu/char.h" #include "qemu/log.h" #include "net/net.h" #include "net/checksum.h" diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c index b6701844d3..5ffa739f68 100644 --- a/hw/net/xilinx_axienet.c +++ b/hw/net/xilinx_axienet.c @@ -981,9 +981,7 @@ static void xilinx_enet_realize(DeviceState *dev, Error **errp) return; xilinx_enet_realize_fail: - if (!*errp) { - *errp = local_err; - } + error_propagate(errp, local_err); } static void xilinx_enet_init(Object *obj) diff --git a/hw/nios2/10m50_devboard.c b/hw/nios2/10m50_devboard.c index 051be73e9a..b6868b8233 100644 --- a/hw/nios2/10m50_devboard.c +++ b/hw/nios2/10m50_devboard.c @@ -57,19 +57,19 @@ static void nios2_10m50_ghrd_init(MachineState *machine) int i; /* Physical TCM (tb_ram_1k) with alias at 0xc0000000 */ - memory_region_init_ram(phys_tcm, NULL, "nios2.tcm", tcm_size, &error_abort); + memory_region_init_ram(phys_tcm, NULL, "nios2.tcm", tcm_size, + &error_abort); memory_region_init_alias(phys_tcm_alias, NULL, "nios2.tcm.alias", phys_tcm, 0, tcm_size); - vmstate_register_ram_global(phys_tcm); memory_region_add_subregion(address_space_mem, tcm_base, phys_tcm); memory_region_add_subregion(address_space_mem, 0xc0000000 + tcm_base, phys_tcm_alias); /* Physical DRAM with alias at 0xc0000000 */ - memory_region_init_ram(phys_ram, NULL, "nios2.ram", ram_size, &error_abort); + memory_region_init_ram(phys_ram, NULL, "nios2.ram", ram_size, + &error_abort); memory_region_init_alias(phys_ram_alias, NULL, "nios2.ram.alias", phys_ram, 0, ram_size); - vmstate_register_ram_global(phys_ram); memory_region_add_subregion(address_space_mem, ram_base, phys_ram); memory_region_add_subregion(address_space_mem, 0xc0000000 + ram_base, phys_ram_alias); diff --git a/hw/nvram/eeprom93xx.c b/hw/nvram/eeprom93xx.c index 848692abc0..2fd0e3c29f 100644 --- a/hw/nvram/eeprom93xx.c +++ b/hw/nvram/eeprom93xx.c @@ -143,7 +143,7 @@ static const VMStateDescription vmstate_eeprom = { VMSTATE_UINT8(addrbits, eeprom_t), VMSTATE_UINT16_HACK_TEST(size, eeprom_t, is_old_eeprom_version), VMSTATE_UNUSED_TEST(is_old_eeprom_version, 1), - VMSTATE_UINT16_EQUAL_V(size, eeprom_t, EEPROM_VERSION), + VMSTATE_UINT16_EQUAL_V(size, eeprom_t, EEPROM_VERSION, NULL), VMSTATE_UINT16(data, eeprom_t), VMSTATE_VARRAY_UINT16_UNSAFE(contents, eeprom_t, size, 0, vmstate_info_uint16, uint16_t), diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index 316fca9bc1..5bd904487f 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -37,17 +37,6 @@ #define FW_CFG_FILE_SLOTS_DFLT 0x20 -#define FW_CFG_NAME "fw_cfg" -#define FW_CFG_PATH "/machine/" FW_CFG_NAME - -#define TYPE_FW_CFG "fw_cfg" -#define TYPE_FW_CFG_IO "fw_cfg_io" -#define TYPE_FW_CFG_MEM "fw_cfg_mem" - -#define FW_CFG(obj) OBJECT_CHECK(FWCfgState, (obj), TYPE_FW_CFG) -#define FW_CFG_IO(obj) OBJECT_CHECK(FWCfgIoState, (obj), TYPE_FW_CFG_IO) -#define FW_CFG_MEM(obj) OBJECT_CHECK(FWCfgMemState, (obj), TYPE_FW_CFG_MEM) - /* FW_CFG_VERSION bits */ #define FW_CFG_VERSION 0x01 #define FW_CFG_VERSION_DMA 0x02 @@ -61,52 +50,12 @@ #define FW_CFG_DMA_SIGNATURE 0x51454d5520434647ULL /* "QEMU CFG" */ -typedef struct FWCfgEntry { +struct FWCfgEntry { uint32_t len; bool allow_write; uint8_t *data; void *callback_opaque; FWCfgReadCallback read_callback; -} FWCfgEntry; - -struct FWCfgState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - uint16_t file_slots; - FWCfgEntry *entries[2]; - int *entry_order; - FWCfgFiles *files; - uint16_t cur_entry; - uint32_t cur_offset; - Notifier machine_ready; - - int fw_cfg_order_override; - - bool dma_enabled; - dma_addr_t dma_addr; - AddressSpace *dma_as; - MemoryRegion dma_iomem; -}; - -struct FWCfgIoState { - /*< private >*/ - FWCfgState parent_obj; - /*< public >*/ - - MemoryRegion comb_iomem; - uint32_t iobase, dma_iobase; -}; - -struct FWCfgMemState { - /*< private >*/ - FWCfgState parent_obj; - /*< public >*/ - - MemoryRegion ctl_iomem, data_iomem; - uint32_t data_width; - MemoryRegionOps wide_data_ops; }; #define JPG_FILE 0 @@ -782,7 +731,7 @@ static int get_fw_cfg_order(FWCfgState *s, const char *name) } /* Stick unknown stuff at the end. */ - error_report("warning: Unknown firmware file in legacy mode: %s", name); + warn_report("Unknown firmware file in legacy mode: %s", name); return FW_CFG_ORDER_OVERRIDE_LAST; } @@ -910,16 +859,16 @@ static void fw_cfg_machine_ready(struct Notifier *n, void *data) -static void fw_cfg_init1(DeviceState *dev) +static void fw_cfg_common_realize(DeviceState *dev, Error **errp) { FWCfgState *s = FW_CFG(dev); MachineState *machine = MACHINE(qdev_get_machine()); + uint32_t version = FW_CFG_VERSION; - assert(!object_resolve_path(FW_CFG_PATH, NULL)); - - object_property_add_child(OBJECT(machine), FW_CFG_NAME, OBJECT(s), NULL); - - qdev_init_nofail(dev); + if (!fw_cfg_find()) { + error_setg(errp, "at most one %s device is permitted", TYPE_FW_CFG); + return; + } fw_cfg_add_bytes(s, FW_CFG_SIGNATURE, (char *)"QEMU", 4); fw_cfg_add_bytes(s, FW_CFG_UUID, &qemu_uuid, 16); @@ -928,6 +877,12 @@ static void fw_cfg_init1(DeviceState *dev) fw_cfg_bootsplash(s); fw_cfg_reboot(s); + if (s->dma_enabled) { + version |= FW_CFG_VERSION_DMA; + } + + fw_cfg_add_i32(s, FW_CFG_ID, version); + s->machine_ready.notify = fw_cfg_machine_ready; qemu_add_machine_init_done_notifier(&s->machine_ready); } @@ -936,30 +891,33 @@ FWCfgState *fw_cfg_init_io_dma(uint32_t iobase, uint32_t dma_iobase, AddressSpace *dma_as) { DeviceState *dev; + SysBusDevice *sbd; + FWCfgIoState *ios; FWCfgState *s; - uint32_t version = FW_CFG_VERSION; bool dma_requested = dma_iobase && dma_as; dev = qdev_create(NULL, TYPE_FW_CFG_IO); - qdev_prop_set_uint32(dev, "iobase", iobase); - qdev_prop_set_uint32(dev, "dma_iobase", dma_iobase); if (!dma_requested) { qdev_prop_set_bit(dev, "dma_enabled", false); } - fw_cfg_init1(dev); + object_property_add_child(OBJECT(qdev_get_machine()), TYPE_FW_CFG, + OBJECT(dev), NULL); + qdev_init_nofail(dev); + + sbd = SYS_BUS_DEVICE(dev); + ios = FW_CFG_IO(dev); + sysbus_add_io(sbd, iobase, &ios->comb_iomem); + s = FW_CFG(dev); if (s->dma_enabled) { /* 64 bits for the address field */ s->dma_as = dma_as; s->dma_addr = 0; - - version |= FW_CFG_VERSION_DMA; + sysbus_add_io(sbd, dma_iobase, &s->dma_iomem); } - fw_cfg_add_i32(s, FW_CFG_ID, version); - return s; } @@ -975,7 +933,6 @@ FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr, DeviceState *dev; SysBusDevice *sbd; FWCfgState *s; - uint32_t version = FW_CFG_VERSION; bool dma_requested = dma_addr && dma_as; dev = qdev_create(NULL, TYPE_FW_CFG_MEM); @@ -984,7 +941,9 @@ FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr, qdev_prop_set_bit(dev, "dma_enabled", false); } - fw_cfg_init1(dev); + object_property_add_child(OBJECT(qdev_get_machine()), TYPE_FW_CFG, + OBJECT(dev), NULL); + qdev_init_nofail(dev); sbd = SYS_BUS_DEVICE(dev); sysbus_mmio_map(sbd, 0, ctl_addr); @@ -996,11 +955,8 @@ FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr, s->dma_as = dma_as; s->dma_addr = 0; sysbus_mmio_map(sbd, 2, dma_addr); - version |= FW_CFG_VERSION_DMA; } - fw_cfg_add_i32(s, FW_CFG_ID, version); - return s; } @@ -1014,9 +970,11 @@ FWCfgState *fw_cfg_init_mem(hwaddr ctl_addr, hwaddr data_addr) FWCfgState *fw_cfg_find(void) { - return FW_CFG(object_resolve_path(FW_CFG_PATH, NULL)); + /* Returns NULL unless there is exactly one fw_cfg device */ + return FW_CFG(object_resolve_path_type("", TYPE_FW_CFG, NULL)); } + static void fw_cfg_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1059,8 +1017,6 @@ static void fw_cfg_file_slots_allocate(FWCfgState *s, Error **errp) } static Property fw_cfg_io_properties[] = { - DEFINE_PROP_UINT32("iobase", FWCfgIoState, iobase, -1), - DEFINE_PROP_UINT32("dma_iobase", FWCfgIoState, dma_iobase, -1), DEFINE_PROP_BOOL("dma_enabled", FWCfgIoState, parent_obj.dma_enabled, true), DEFINE_PROP_UINT16("x-file-slots", FWCfgIoState, parent_obj.file_slots, @@ -1071,7 +1027,6 @@ static Property fw_cfg_io_properties[] = { static void fw_cfg_io_realize(DeviceState *dev, Error **errp) { FWCfgIoState *s = FW_CFG_IO(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); Error *local_err = NULL; fw_cfg_file_slots_allocate(FW_CFG(s), &local_err); @@ -1085,14 +1040,14 @@ static void fw_cfg_io_realize(DeviceState *dev, Error **errp) * of the i/o region used is FW_CFG_CTL_SIZE */ memory_region_init_io(&s->comb_iomem, OBJECT(s), &fw_cfg_comb_mem_ops, FW_CFG(s), "fwcfg", FW_CFG_CTL_SIZE); - sysbus_add_io(sbd, s->iobase, &s->comb_iomem); if (FW_CFG(s)->dma_enabled) { memory_region_init_io(&FW_CFG(s)->dma_iomem, OBJECT(s), &fw_cfg_dma_mem_ops, FW_CFG(s), "fwcfg.dma", sizeof(dma_addr_t)); - sysbus_add_io(sbd, s->dma_iobase, &FW_CFG(s)->dma_iomem); } + + fw_cfg_common_realize(dev, errp); } static void fw_cfg_io_class_init(ObjectClass *klass, void *data) @@ -1159,6 +1114,8 @@ static void fw_cfg_mem_realize(DeviceState *dev, Error **errp) sizeof(dma_addr_t)); sysbus_init_mmio(sbd, &FW_CFG(s)->dma_iomem); } + + fw_cfg_common_realize(dev, errp); } static void fw_cfg_mem_class_init(ObjectClass *klass, void *data) diff --git a/hw/nvram/spapr_nvram.c b/hw/nvram/spapr_nvram.c index aa5d2c1f5f..bc355a4348 100644 --- a/hw/nvram/spapr_nvram.c +++ b/hw/nvram/spapr_nvram.c @@ -144,7 +144,15 @@ static void spapr_nvram_realize(VIOsPAPRDevice *dev, Error **errp) int ret; if (nvram->blk) { - nvram->size = blk_getlength(nvram->blk); + int64_t len = blk_getlength(nvram->blk); + + if (len < 0) { + error_setg_errno(errp, -len, + "could not get length of backing image"); + return; + } + + nvram->size = len; ret = blk_set_perm(nvram->blk, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE, diff --git a/hw/openrisc/openrisc_sim.c b/hw/openrisc/openrisc_sim.c index fc0d0967b7..e1eeffc490 100644 --- a/hw/openrisc/openrisc_sim.c +++ b/hw/openrisc/openrisc_sim.c @@ -120,7 +120,6 @@ static void openrisc_sim_init(MachineState *machine) ram = g_malloc(sizeof(*ram)); memory_region_init_ram(ram, NULL, "openrisc.ram", ram_size, &error_fatal); - vmstate_register_ram_global(ram); memory_region_add_subregion(get_system_memory(), 0, ram); cpu_openrisc_pic_init(cpu); diff --git a/hw/pci-bridge/dec.c b/hw/pci-bridge/dec.c index cca93620ac..eb275e1a25 100644 --- a/hw/pci-bridge/dec.c +++ b/hw/pci-bridge/dec.c @@ -62,6 +62,7 @@ static void dec_21154_pci_bridge_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); k->realize = dec_pci_bridge_realize; k->exit = pci_bridge_exitfn; k->vendor_id = PCI_VENDOR_ID_DEC; @@ -118,6 +119,7 @@ static void dec_21154_pci_host_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); k->realize = dec_21154_pci_host_realize; k->vendor_id = PCI_VENDOR_ID_DEC; k->device_id = PCI_DEVICE_ID_DEC_21154; diff --git a/hw/pci-bridge/gen_pcie_root_port.c b/hw/pci-bridge/gen_pcie_root_port.c index 8ebffa8bb0..cb694d6da5 100644 --- a/hw/pci-bridge/gen_pcie_root_port.c +++ b/hw/pci-bridge/gen_pcie_root_port.c @@ -20,6 +20,14 @@ #define GEN_PCIE_ROOT_PORT_AER_OFFSET 0x100 #define GEN_PCIE_ROOT_PORT_MSIX_NR_VECTOR 1 +typedef struct GenPCIERootPort { + /*< private >*/ + PCIESlot parent_obj; + /*< public >*/ + + bool migrate_msix; +} GenPCIERootPort; + static uint8_t gen_rp_aer_vector(const PCIDevice *d) { return 0; @@ -45,6 +53,13 @@ static void gen_rp_interrupts_uninit(PCIDevice *d) msix_uninit_exclusive_bar(d); } +static bool gen_rp_test_migrate_msix(void *opaque, int version_id) +{ + GenPCIERootPort *rp = opaque; + + return rp->migrate_msix; +} + static const VMStateDescription vmstate_rp_dev = { .name = "pcie-root-port", .version_id = 1, @@ -54,10 +69,18 @@ static const VMStateDescription vmstate_rp_dev = { VMSTATE_PCI_DEVICE(parent_obj.parent_obj.parent_obj, PCIESlot), VMSTATE_STRUCT(parent_obj.parent_obj.parent_obj.exp.aer_log, PCIESlot, 0, vmstate_pcie_aer_log, PCIEAERLog), + VMSTATE_MSIX_TEST(parent_obj.parent_obj.parent_obj.parent_obj, + GenPCIERootPort, + gen_rp_test_migrate_msix), VMSTATE_END_OF_LIST() } }; +static Property gen_rp_props[] = { + DEFINE_PROP_BOOL("x-migrate-msix", GenPCIERootPort, migrate_msix, true), + DEFINE_PROP_END_OF_LIST() +}; + static void gen_rp_dev_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -68,6 +91,7 @@ static void gen_rp_dev_class_init(ObjectClass *klass, void *data) k->device_id = PCI_DEVICE_ID_REDHAT_PCIE_RP; dc->desc = "PCI Express Root Port"; dc->vmsd = &vmstate_rp_dev; + dc->props = gen_rp_props; rpc->aer_vector = gen_rp_aer_vector; rpc->interrupts_init = gen_rp_interrupts_init; rpc->interrupts_uninit = gen_rp_interrupts_uninit; @@ -77,6 +101,7 @@ static void gen_rp_dev_class_init(ObjectClass *klass, void *data) static const TypeInfo gen_rp_dev_info = { .name = TYPE_GEN_PCIE_ROOT_PORT, .parent = TYPE_PCIE_ROOT_PORT, + .instance_size = sizeof(GenPCIERootPort), .class_init = gen_rp_dev_class_init, }; diff --git a/hw/pci-bridge/i82801b11.c b/hw/pci-bridge/i82801b11.c index 2404e7ebae..2c1b747b4b 100644 --- a/hw/pci-bridge/i82801b11.c +++ b/hw/pci-bridge/i82801b11.c @@ -44,6 +44,7 @@ #include "qemu/osdep.h" #include "hw/pci/pci.h" #include "hw/i386/ich9.h" +#include "qapi/error.h" /*****************************************************************************/ @@ -58,24 +59,23 @@ typedef struct I82801b11Bridge { /*< public >*/ } I82801b11Bridge; -static int i82801b11_bridge_initfn(PCIDevice *d) +static void i82801b11_bridge_realize(PCIDevice *d, Error **errp) { int rc; pci_bridge_initfn(d, TYPE_PCI_BUS); rc = pci_bridge_ssvid_init(d, I82801ba_SSVID_OFFSET, - I82801ba_SSVID_SVID, I82801ba_SSVID_SSID); + I82801ba_SSVID_SVID, I82801ba_SSVID_SSID, + errp); if (rc < 0) { goto err_bridge; } pci_config_set_prog_interface(d->config, PCI_CLASS_BRIDGE_PCI_INF_SUB); - return 0; + return; err_bridge: pci_bridge_exitfn(d); - - return rc; } static const VMStateDescription i82801b11_bridge_dev_vmstate = { @@ -95,7 +95,7 @@ static void i82801b11_bridge_class_init(ObjectClass *klass, void *data) k->vendor_id = PCI_VENDOR_ID_INTEL; k->device_id = PCI_DEVICE_ID_INTEL_82801BA_11; k->revision = ICH9_D2P_A2_REVISION; - k->init = i82801b11_bridge_initfn; + k->realize = i82801b11_bridge_realize; k->config_write = pci_bridge_write_config; dc->vmsd = &i82801b11_bridge_dev_vmstate; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c index 5dbd933cc1..4373f1d3e2 100644 --- a/hw/pci-bridge/pci_bridge_dev.c +++ b/hw/pci-bridge/pci_bridge_dev.c @@ -49,7 +49,7 @@ struct PCIBridgeDev { }; typedef struct PCIBridgeDev PCIBridgeDev; -static int pci_bridge_dev_initfn(PCIDevice *dev) +static void pci_bridge_dev_realize(PCIDevice *dev, Error **errp) { PCIBridge *br = PCI_BRIDGE(dev); PCIBridgeDev *bridge_dev = PCI_BRIDGE_DEV(dev); @@ -62,7 +62,7 @@ static int pci_bridge_dev_initfn(PCIDevice *dev) dev->config[PCI_INTERRUPT_PIN] = 0x1; memory_region_init(&bridge_dev->bar, OBJECT(dev), "shpc-bar", shpc_bar_size(dev)); - err = shpc_init(dev, &br->sec_bus, &bridge_dev->bar, 0); + err = shpc_init(dev, &br->sec_bus, &bridge_dev->bar, 0, errp); if (err) { goto shpc_error; } @@ -71,7 +71,7 @@ static int pci_bridge_dev_initfn(PCIDevice *dev) bridge_dev->msi = ON_OFF_AUTO_OFF; } - err = slotid_cap_init(dev, 0, bridge_dev->chassis_nr, 0); + err = slotid_cap_init(dev, 0, bridge_dev->chassis_nr, 0, errp); if (err) { goto slotid_error; } @@ -87,7 +87,7 @@ static int pci_bridge_dev_initfn(PCIDevice *dev) /* Can't satisfy user's explicit msi=on request, fail */ error_append_hint(&local_err, "You have to use msi=auto (default) " "or msi=off with this machine type.\n"); - error_report_err(local_err); + error_propagate(errp, local_err); goto msi_error; } assert(!local_err || bridge_dev->msi == ON_OFF_AUTO_AUTO); @@ -101,7 +101,7 @@ static int pci_bridge_dev_initfn(PCIDevice *dev) pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64, &bridge_dev->bar); } - return 0; + return; msi_error: slotid_cap_cleanup(dev); @@ -111,8 +111,6 @@ slotid_error: } shpc_error: pci_bridge_exitfn(dev); - - return err; } static void pci_bridge_dev_exitfn(PCIDevice *dev) @@ -216,7 +214,7 @@ static void pci_bridge_dev_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); - k->init = pci_bridge_dev_initfn; + k->realize = pci_bridge_dev_realize; k->exit = pci_bridge_dev_exitfn; k->config_write = pci_bridge_dev_write_config; k->vendor_id = PCI_VENDOR_ID_REDHAT; diff --git a/hw/pci-bridge/pcie_root_port.c b/hw/pci-bridge/pcie_root_port.c index cf3631806f..4d588cb22e 100644 --- a/hw/pci-bridge/pcie_root_port.c +++ b/hw/pci-bridge/pcie_root_port.c @@ -59,29 +59,30 @@ static void rp_realize(PCIDevice *d, Error **errp) PCIDeviceClass *dc = PCI_DEVICE_GET_CLASS(d); PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(d); int rc; - Error *local_err = NULL; pci_config_set_interrupt_pin(d->config, 1); pci_bridge_initfn(d, TYPE_PCIE_BUS); pcie_port_init_reg(d); - rc = pci_bridge_ssvid_init(d, rpc->ssvid_offset, dc->vendor_id, rpc->ssid); + rc = pci_bridge_ssvid_init(d, rpc->ssvid_offset, dc->vendor_id, + rpc->ssid, errp); if (rc < 0) { - error_setg(errp, "Can't init SSV ID, error %d", rc); + error_append_hint(errp, "Can't init SSV ID, error %d\n", rc); goto err_bridge; } if (rpc->interrupts_init) { - rc = rpc->interrupts_init(d, &local_err); + rc = rpc->interrupts_init(d, errp); if (rc < 0) { - error_propagate(errp, local_err); goto err_bridge; } } - rc = pcie_cap_init(d, rpc->exp_offset, PCI_EXP_TYPE_ROOT_PORT, p->port); + rc = pcie_cap_init(d, rpc->exp_offset, PCI_EXP_TYPE_ROOT_PORT, + p->port, errp); if (rc < 0) { - error_setg(errp, "Can't add Root Port capability, error %d", rc); + error_append_hint(errp, "Can't add Root Port capability, " + "error %d\n", rc); goto err_int; } @@ -98,9 +99,8 @@ static void rp_realize(PCIDevice *d, Error **errp) } rc = pcie_aer_init(d, PCI_ERR_VER, rpc->aer_offset, - PCI_ERR_SIZEOF, &local_err); + PCI_ERR_SIZEOF, errp); if (rc < 0) { - error_propagate(errp, local_err); goto err; } pcie_aer_root_init(d); diff --git a/hw/pci-bridge/xio3130_downstream.c b/hw/pci-bridge/xio3130_downstream.c index cfe8a3657f..e706f36cb7 100644 --- a/hw/pci-bridge/xio3130_downstream.c +++ b/hw/pci-bridge/xio3130_downstream.c @@ -56,33 +56,33 @@ static void xio3130_downstream_reset(DeviceState *qdev) pci_bridge_reset(qdev); } -static int xio3130_downstream_initfn(PCIDevice *d) +static void xio3130_downstream_realize(PCIDevice *d, Error **errp) { PCIEPort *p = PCIE_PORT(d); PCIESlot *s = PCIE_SLOT(d); int rc; - Error *err = NULL; pci_bridge_initfn(d, TYPE_PCIE_BUS); pcie_port_init_reg(d); rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR, XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT, - XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT, &err); + XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT, + errp); if (rc < 0) { assert(rc == -ENOTSUP); - error_report_err(err); goto err_bridge; } rc = pci_bridge_ssvid_init(d, XIO3130_SSVID_OFFSET, - XIO3130_SSVID_SVID, XIO3130_SSVID_SSID); + XIO3130_SSVID_SVID, XIO3130_SSVID_SSID, + errp); if (rc < 0) { goto err_bridge; } rc = pcie_cap_init(d, XIO3130_EXP_OFFSET, PCI_EXP_TYPE_DOWNSTREAM, - p->port); + p->port, errp); if (rc < 0) { goto err_msi; } @@ -98,13 +98,12 @@ static int xio3130_downstream_initfn(PCIDevice *d) } rc = pcie_aer_init(d, PCI_ERR_VER, XIO3130_AER_OFFSET, - PCI_ERR_SIZEOF, &err); + PCI_ERR_SIZEOF, errp); if (rc < 0) { - error_report_err(err); goto err; } - return 0; + return; err: pcie_chassis_del_slot(s); @@ -114,7 +113,6 @@ err_msi: msi_uninit(d); err_bridge: pci_bridge_exitfn(d); - return rc; } static void xio3130_downstream_exitfn(PCIDevice *d) @@ -181,7 +179,7 @@ static void xio3130_downstream_class_init(ObjectClass *klass, void *data) k->is_express = 1; k->is_bridge = 1; k->config_write = xio3130_downstream_write_config; - k->init = xio3130_downstream_initfn; + k->realize = xio3130_downstream_realize; k->exit = xio3130_downstream_exitfn; k->vendor_id = PCI_VENDOR_ID_TI; k->device_id = PCI_DEVICE_ID_TI_XIO3130D; diff --git a/hw/pci-bridge/xio3130_upstream.c b/hw/pci-bridge/xio3130_upstream.c index 401c78452b..a052224bbf 100644 --- a/hw/pci-bridge/xio3130_upstream.c +++ b/hw/pci-bridge/xio3130_upstream.c @@ -53,32 +53,32 @@ static void xio3130_upstream_reset(DeviceState *qdev) pcie_cap_deverr_reset(d); } -static int xio3130_upstream_initfn(PCIDevice *d) +static void xio3130_upstream_realize(PCIDevice *d, Error **errp) { PCIEPort *p = PCIE_PORT(d); int rc; - Error *err = NULL; pci_bridge_initfn(d, TYPE_PCIE_BUS); pcie_port_init_reg(d); rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR, XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT, - XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT, &err); + XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT, + errp); if (rc < 0) { assert(rc == -ENOTSUP); - error_report_err(err); goto err_bridge; } rc = pci_bridge_ssvid_init(d, XIO3130_SSVID_OFFSET, - XIO3130_SSVID_SVID, XIO3130_SSVID_SSID); + XIO3130_SSVID_SVID, XIO3130_SSVID_SSID, + errp); if (rc < 0) { goto err_bridge; } rc = pcie_cap_init(d, XIO3130_EXP_OFFSET, PCI_EXP_TYPE_UPSTREAM, - p->port); + p->port, errp); if (rc < 0) { goto err_msi; } @@ -86,13 +86,12 @@ static int xio3130_upstream_initfn(PCIDevice *d) pcie_cap_deverr_init(d); rc = pcie_aer_init(d, PCI_ERR_VER, XIO3130_AER_OFFSET, - PCI_ERR_SIZEOF, &err); + PCI_ERR_SIZEOF, errp); if (rc < 0) { - error_report_err(err); goto err; } - return 0; + return; err: pcie_cap_exit(d); @@ -100,7 +99,6 @@ err_msi: msi_uninit(d); err_bridge: pci_bridge_exitfn(d); - return rc; } static void xio3130_upstream_exitfn(PCIDevice *d) @@ -153,7 +151,7 @@ static void xio3130_upstream_class_init(ObjectClass *klass, void *data) k->is_express = 1; k->is_bridge = 1; k->config_write = xio3130_upstream_write_config; - k->init = xio3130_upstream_initfn; + k->realize = xio3130_upstream_realize; k->exit = xio3130_upstream_exitfn; k->vendor_id = PCI_VENDOR_ID_TI; k->device_id = PCI_DEVICE_ID_TI_XIO3130U; diff --git a/hw/pci-host/apb.c b/hw/pci-host/apb.c index edc88f4c65..96e5d0b60d 100644 --- a/hw/pci-host/apb.c +++ b/hw/pci-host/apb.c @@ -123,7 +123,7 @@ do { printf("IOMMU: " fmt , ## __VA_ARGS__); } while (0) typedef struct IOMMUState { AddressSpace iommu_as; - MemoryRegion iommu; + IOMMUMemoryRegion iommu; uint64_t regs[IOMMU_NREGS]; } IOMMUState; @@ -133,6 +133,8 @@ typedef struct IOMMUState { #define APB_DEVICE(obj) \ OBJECT_CHECK(APBState, (obj), TYPE_APB) +#define TYPE_APB_IOMMU_MEMORY_REGION "pbm-iommu-memory-region" + typedef struct APBState { PCIHostState parent_obj; @@ -208,8 +210,8 @@ static AddressSpace *pbm_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) } /* Called from RCU critical section */ -static IOMMUTLBEntry pbm_translate_iommu(MemoryRegion *iommu, hwaddr addr, - bool is_write) +static IOMMUTLBEntry pbm_translate_iommu(IOMMUMemoryRegion *iommu, hwaddr addr, + IOMMUAccessFlags flag) { IOMMUState *is = container_of(iommu, IOMMUState, iommu); hwaddr baseaddr, offset; @@ -322,10 +324,6 @@ static IOMMUTLBEntry pbm_translate_iommu(MemoryRegion *iommu, hwaddr addr, return ret; } -static MemoryRegionIOMMUOps pbm_iommu_ops = { - .translate = pbm_translate_iommu, -}; - static void iommu_config_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { @@ -482,9 +480,9 @@ static void apb_config_writel (void *opaque, hwaddr addr, s->reset_control |= val & RESET_WMASK; if (val & SOFT_POR) { s->nr_resets = 0; - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); } else if (val & SOFT_XIR) { - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); } } break; @@ -697,9 +695,10 @@ PCIBus *pci_apb_init(hwaddr special_base, is = &d->iommu; memset(is, 0, sizeof(IOMMUState)); - memory_region_init_iommu(&is->iommu, OBJECT(dev), &pbm_iommu_ops, + memory_region_init_iommu(&is->iommu, sizeof(is->iommu), + TYPE_APB_IOMMU_MEMORY_REGION, OBJECT(dev), "iommu-apb", UINT64_MAX); - address_space_init(&is->iommu_as, &is->iommu, "pbm-as"); + address_space_init(&is->iommu_as, MEMORY_REGION(&is->iommu), "pbm-as"); pci_setup_iommu(phb->bus, pbm_pci_dma_iommu, is); /* APB secondary busses */ @@ -860,11 +859,25 @@ static const TypeInfo pbm_pci_bridge_info = { .class_init = pbm_pci_bridge_class_init, }; +static void pbm_iommu_memory_region_class_init(ObjectClass *klass, void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = pbm_translate_iommu; +} + +static const TypeInfo pbm_iommu_memory_region_info = { + .parent = TYPE_IOMMU_MEMORY_REGION, + .name = TYPE_APB_IOMMU_MEMORY_REGION, + .class_init = pbm_iommu_memory_region_class_init, +}; + static void pbm_register_types(void) { type_register_static(&pbm_host_info); type_register_static(&pbm_pci_host_info); type_register_static(&pbm_pci_bridge_info); + type_register_static(&pbm_iommu_memory_region_info); } type_init(pbm_register_types) diff --git a/hw/pci-host/bonito.c b/hw/pci-host/bonito.c index 85a3bb0dd2..89133a9dd3 100644 --- a/hw/pci-host/bonito.c +++ b/hw/pci-host/bonito.c @@ -269,7 +269,7 @@ static void bonito_writel(void *opaque, hwaddr addr, } s->regs[saddr] = val; if (reset) { - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); } break; case BONITO_INTENSET: diff --git a/hw/pci-host/gpex.c b/hw/pci-host/gpex.c index e2629ce70d..83084b9aab 100644 --- a/hw/pci-host/gpex.c +++ b/hw/pci-host/gpex.c @@ -94,7 +94,7 @@ static void gpex_host_initfn(Object *obj) object_initialize(root, sizeof(*root), TYPE_GPEX_ROOT_DEVICE); object_property_add_child(obj, "gpex_root", OBJECT(root), NULL); - qdev_prop_set_uint32(DEVICE(root), "addr", PCI_DEVFN(0, 0)); + qdev_prop_set_int32(DEVICE(root), "addr", PCI_DEVFN(0, 0)); qdev_prop_set_bit(DEVICE(root), "multifunction", false); } diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c index 2d02de12d9..072a04e318 100644 --- a/hw/pci-host/piix.c +++ b/hw/pci-host/piix.c @@ -273,19 +273,19 @@ static void i440fx_pcihost_initfn(Object *obj) memory_region_init_io(&s->data_mem, obj, &pci_host_data_le_ops, s, "pci-conf-data", 4); - object_property_add(obj, PCI_HOST_PROP_PCI_HOLE_START, "int", + object_property_add(obj, PCI_HOST_PROP_PCI_HOLE_START, "uint32", i440fx_pcihost_get_pci_hole_start, NULL, NULL, NULL, NULL); - object_property_add(obj, PCI_HOST_PROP_PCI_HOLE_END, "int", + object_property_add(obj, PCI_HOST_PROP_PCI_HOLE_END, "uint32", i440fx_pcihost_get_pci_hole_end, NULL, NULL, NULL, NULL); - object_property_add(obj, PCI_HOST_PROP_PCI_HOLE64_START, "int", + object_property_add(obj, PCI_HOST_PROP_PCI_HOLE64_START, "uint64", i440fx_pcihost_get_pci_hole64_start, NULL, NULL, NULL, NULL); - object_property_add(obj, PCI_HOST_PROP_PCI_HOLE64_END, "int", + object_property_add(obj, PCI_HOST_PROP_PCI_HOLE64_END, "uint64", i440fx_pcihost_get_pci_hole64_end, NULL, NULL, NULL, NULL); } @@ -307,7 +307,7 @@ static void i440fx_realize(PCIDevice *dev, Error **errp) dev->config[I440FX_SMRAM] = 0x02; if (object_property_get_bool(qdev_get_machine(), "iommu", NULL)) { - error_report("warning: i440fx doesn't support emulated iommu"); + warn_report("i440fx doesn't support emulated iommu"); } } @@ -632,7 +632,7 @@ static void rcr_write(void *opaque, hwaddr addr, uint64_t val, unsigned len) PIIX3State *d = opaque; if (val & 4) { - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); return; } d->rcr = val & 2; /* keep System Reset type only */ diff --git a/hw/pci-host/prep.c b/hw/pci-host/prep.c index 900a6edfcf..8b293ba0f1 100644 --- a/hw/pci-host/prep.c +++ b/hw/pci-host/prep.c @@ -304,7 +304,7 @@ static void raven_realize(PCIDevice *d, Error **errp) d->config[0x0D] = 0x10; // latency_timer d->config[0x34] = 0x00; // capabilities_pointer - memory_region_init_ram(&s->bios, OBJECT(s), "bios", BIOS_SIZE, + memory_region_init_ram_nomigrate(&s->bios, OBJECT(s), "bios", BIOS_SIZE, &error_fatal); memory_region_set_readonly(&s->bios, true); memory_region_add_subregion(get_system_memory(), (uint32_t)(-BIOS_SIZE), diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index cd5c49616e..0e472f2ed4 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -129,12 +129,11 @@ static void q35_host_get_mmcfg_size(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { PCIExpressHost *e = PCIE_HOST_BRIDGE(obj); - uint32_t value = e->size; - visit_type_uint32(v, name, &value, errp); + visit_type_uint64(v, name, &e->size, errp); } -static Property mch_props[] = { +static Property q35_host_props[] = { DEFINE_PROP_UINT64(PCIE_HOST_MCFG_BASE, Q35PCIHost, parent_obj.base_addr, MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT), DEFINE_PROP_SIZE(PCI_HOST_PROP_PCI_HOLE64_SIZE, Q35PCIHost, @@ -154,7 +153,7 @@ static void q35_host_class_init(ObjectClass *klass, void *data) hc->root_bus_path = q35_host_root_bus_path; dc->realize = q35_host_realize; - dc->props = mch_props; + dc->props = q35_host_props; /* Reason: needs to be wired up by pc_q35_init */ dc->user_creatable = false; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); @@ -173,26 +172,26 @@ static void q35_host_initfn(Object *obj) object_initialize(&s->mch, sizeof(s->mch), TYPE_MCH_PCI_DEVICE); object_property_add_child(OBJECT(s), "mch", OBJECT(&s->mch), NULL); - qdev_prop_set_uint32(DEVICE(&s->mch), "addr", PCI_DEVFN(0, 0)); + qdev_prop_set_int32(DEVICE(&s->mch), "addr", PCI_DEVFN(0, 0)); qdev_prop_set_bit(DEVICE(&s->mch), "multifunction", false); - object_property_add(obj, PCI_HOST_PROP_PCI_HOLE_START, "int", + object_property_add(obj, PCI_HOST_PROP_PCI_HOLE_START, "uint32", q35_host_get_pci_hole_start, NULL, NULL, NULL, NULL); - object_property_add(obj, PCI_HOST_PROP_PCI_HOLE_END, "int", + object_property_add(obj, PCI_HOST_PROP_PCI_HOLE_END, "uint32", q35_host_get_pci_hole_end, NULL, NULL, NULL, NULL); - object_property_add(obj, PCI_HOST_PROP_PCI_HOLE64_START, "int", + object_property_add(obj, PCI_HOST_PROP_PCI_HOLE64_START, "uint64", q35_host_get_pci_hole64_start, NULL, NULL, NULL, NULL); - object_property_add(obj, PCI_HOST_PROP_PCI_HOLE64_END, "int", + object_property_add(obj, PCI_HOST_PROP_PCI_HOLE64_END, "uint64", q35_host_get_pci_hole64_end, NULL, NULL, NULL, NULL); - object_property_add(obj, PCIE_HOST_MCFG_SIZE, "int", + object_property_add(obj, PCIE_HOST_MCFG_SIZE, "uint64", q35_host_get_mmcfg_size, NULL, NULL, NULL, NULL); @@ -369,7 +368,7 @@ static void mch_update_smram(MCHPCIState *mch) tseg_size = 1024 * 1024 * 8; break; default: - tseg_size = 0; + tseg_size = 1024 * 1024 * (uint32_t)mch->ext_tseg_mbytes; break; } } else { @@ -392,6 +391,17 @@ static void mch_update_smram(MCHPCIState *mch) memory_region_transaction_commit(); } +static void mch_update_ext_tseg_mbytes(MCHPCIState *mch) +{ + PCIDevice *pd = PCI_DEVICE(mch); + uint8_t *reg = pd->config + MCH_HOST_BRIDGE_EXT_TSEG_MBYTES; + + if (mch->ext_tseg_mbytes > 0 && + pci_get_word(reg) == MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_QUERY) { + pci_set_word(reg, mch->ext_tseg_mbytes); + } +} + static void mch_write_config(PCIDevice *d, uint32_t address, uint32_t val, int len) { @@ -413,6 +423,11 @@ static void mch_write_config(PCIDevice *d, MCH_HOST_BRIDGE_SMRAM_SIZE)) { mch_update_smram(mch); } + + if (ranges_overlap(address, len, MCH_HOST_BRIDGE_EXT_TSEG_MBYTES, + MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_SIZE)) { + mch_update_ext_tseg_mbytes(mch); + } } static void mch_update(MCHPCIState *mch) @@ -420,6 +435,7 @@ static void mch_update(MCHPCIState *mch) mch_update_pciexbar(mch); mch_update_pam(mch); mch_update_smram(mch); + mch_update_ext_tseg_mbytes(mch); } static int mch_post_load(void *opaque, int version_id) @@ -457,6 +473,11 @@ static void mch_reset(DeviceState *qdev) d->wmask[MCH_HOST_BRIDGE_SMRAM] = MCH_HOST_BRIDGE_SMRAM_WMASK; d->wmask[MCH_HOST_BRIDGE_ESMRAMC] = MCH_HOST_BRIDGE_ESMRAMC_WMASK; + if (mch->ext_tseg_mbytes > 0) { + pci_set_word(d->config + MCH_HOST_BRIDGE_EXT_TSEG_MBYTES, + MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_QUERY); + } + mch_update(mch); } @@ -465,6 +486,12 @@ static void mch_realize(PCIDevice *d, Error **errp) int i; MCHPCIState *mch = MCH_PCI_DEVICE(d); + if (mch->ext_tseg_mbytes > MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_MAX) { + error_setg(errp, "invalid extended-tseg-mbytes value: %" PRIu16, + mch->ext_tseg_mbytes); + return; + } + /* setup pci memory mapping */ pc_pci_as_mapping_init(OBJECT(mch), mch->system_memory, mch->pci_address_space); @@ -530,6 +557,12 @@ uint64_t mch_mcfg_base(void) return MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT; } +static Property mch_props[] = { + DEFINE_PROP_UINT16("extended-tseg-mbytes", MCHPCIState, ext_tseg_mbytes, + 16), + DEFINE_PROP_END_OF_LIST(), +}; + static void mch_class_init(ObjectClass *klass, void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -538,6 +571,7 @@ static void mch_class_init(ObjectClass *klass, void *data) k->realize = mch_realize; k->config_write = mch_write_config; dc->reset = mch_reset; + dc->props = mch_props; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->desc = "Host bridge"; dc->vmsd = &vmstate_mch; diff --git a/hw/pci-host/xilinx-pcie.c b/hw/pci-host/xilinx-pcie.c index a968cea2af..4613dda1d2 100644 --- a/hw/pci-host/xilinx-pcie.c +++ b/hw/pci-host/xilinx-pcie.c @@ -120,7 +120,7 @@ static void xilinx_pcie_host_realize(DeviceState *dev, Error **errp) memory_region_set_enabled(&s->mmio, false); /* dummy I/O region */ - memory_region_init_ram(&s->io, OBJECT(s), "io", 16, NULL); + memory_region_init_ram_nomigrate(&s->io, OBJECT(s), "io", 16, NULL); memory_region_set_enabled(&s->io, false); /* interrupt out */ @@ -150,7 +150,7 @@ static void xilinx_pcie_host_init(Object *obj) object_initialize(root, sizeof(*root), TYPE_XILINX_PCIE_ROOT); object_property_add_child(obj, "root", OBJECT(root), NULL); - qdev_prop_set_uint32(DEVICE(root), "addr", PCI_DEVFN(0, 0)); + qdev_prop_set_int32(DEVICE(root), "addr", PCI_DEVFN(0, 0)); qdev_prop_set_bit(DEVICE(root), "multifunction", false); } diff --git a/hw/pci/msi.c b/hw/pci/msi.c index a87b2278a3..5e05ce5ec2 100644 --- a/hw/pci/msi.c +++ b/hw/pci/msi.c @@ -216,7 +216,7 @@ int msi_init(struct PCIDevice *dev, uint8_t offset, } cap_size = msi_cap_sizeof(flags); - config_offset = pci_add_capability2(dev, PCI_CAP_ID_MSI, offset, + config_offset = pci_add_capability(dev, PCI_CAP_ID_MSI, offset, cap_size, errp); if (config_offset < 0) { return config_offset; diff --git a/hw/pci/msix.c b/hw/pci/msix.c index bb54e8b0ac..5078d3dd19 100644 --- a/hw/pci/msix.c +++ b/hw/pci/msix.c @@ -22,6 +22,7 @@ #include "hw/xen/xen.h" #include "qemu/range.h" #include "qapi/error.h" +#include "trace.h" #define MSIX_CAP_LENGTH 12 @@ -130,10 +131,14 @@ static void msix_handle_mask_update(PCIDevice *dev, int vector, bool was_masked) } } +static bool msix_masked(PCIDevice *dev) +{ + return dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] & MSIX_MASKALL_MASK; +} + static void msix_update_function_masked(PCIDevice *dev) { - dev->msix_function_masked = !msix_enabled(dev) || - (dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] & MSIX_MASKALL_MASK); + dev->msix_function_masked = !msix_enabled(dev) || msix_masked(dev); } /* Handle MSI-X capability config write. */ @@ -148,6 +153,8 @@ void msix_write_config(PCIDevice *dev, uint32_t addr, return; } + trace_msix_write_config(dev->name, msix_enabled(dev), msix_masked(dev)); + was_masked = dev->msix_function_masked; msix_update_function_masked(dev); @@ -294,7 +301,7 @@ int msix_init(struct PCIDevice *dev, unsigned short nentries, return -EINVAL; } - cap = pci_add_capability2(dev, PCI_CAP_ID_MSIX, + cap = pci_add_capability(dev, PCI_CAP_ID_MSIX, cap_pos, MSIX_CAP_LENGTH, errp); if (cap < 0) { return cap; diff --git a/hw/pci/pci-stub.c b/hw/pci/pci-stub.c index 36d2c430c5..ecad664946 100644 --- a/hw/pci/pci-stub.c +++ b/hw/pci/pci-stub.c @@ -24,6 +24,9 @@ #include "qapi/qmp/qerror.h" #include "hw/pci/pci.h" #include "qmp-commands.h" +#include "hw/pci/msi.h" + +bool msi_nonbroken; PciInfoList *qmp_query_pci(Error **errp) { diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 98ccc27533..258fbe51e2 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -74,7 +74,7 @@ static const VMStateDescription vmstate_pcibus = { .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { - VMSTATE_INT32_EQUAL(nirq, PCIBus), + VMSTATE_INT32_EQUAL(nirq, PCIBus, NULL), VMSTATE_VARRAY_INT32(irq_count, PCIBus, nirq, 0, vmstate_info_int32, int32_t), @@ -2236,7 +2236,6 @@ static void pci_add_option_rom(PCIDevice *pdev, bool is_default_rom, } pdev->has_rom = true; memory_region_init_rom(&pdev->rom, OBJECT(pdev), name, size, &error_fatal); - vmstate_register_ram(&pdev->rom, &pdev->qdev); ptr = memory_region_get_ram_ptr(&pdev->rom); load_image(path, ptr); g_free(path); @@ -2259,28 +2258,12 @@ static void pci_del_option_rom(PCIDevice *pdev) } /* - * if offset = 0, - * Find and reserve space and add capability to the linked list - * in pci config space + * On success, pci_add_capability() returns a positive value + * that the offset of the pci capability. + * On failure, it sets an error and returns a negative error + * code. */ int pci_add_capability(PCIDevice *pdev, uint8_t cap_id, - uint8_t offset, uint8_t size) -{ - int ret; - Error *local_err = NULL; - - ret = pci_add_capability2(pdev, cap_id, offset, size, &local_err); - if (local_err) { - assert(ret < 0); - error_report_err(local_err); - } else { - /* success implies a positive offset in config space */ - assert(ret > 0); - } - return ret; -} - -int pci_add_capability2(PCIDevice *pdev, uint8_t cap_id, uint8_t offset, uint8_t size, Error **errp) { diff --git a/hw/pci/pci_bridge.c b/hw/pci/pci_bridge.c index 5118ef404f..720119b21a 100644 --- a/hw/pci/pci_bridge.c +++ b/hw/pci/pci_bridge.c @@ -33,6 +33,7 @@ #include "hw/pci/pci_bridge.h" #include "hw/pci/pci_bus.h" #include "qemu/range.h" +#include "qapi/error.h" /* PCI bridge subsystem vendor ID helper functions */ #define PCI_SSVID_SIZEOF 8 @@ -40,10 +41,13 @@ #define PCI_SSVID_SSID 6 int pci_bridge_ssvid_init(PCIDevice *dev, uint8_t offset, - uint16_t svid, uint16_t ssid) + uint16_t svid, uint16_t ssid, + Error **errp) { int pos; - pos = pci_add_capability(dev, PCI_CAP_ID_SSVID, offset, PCI_SSVID_SIZEOF); + + pos = pci_add_capability(dev, PCI_CAP_ID_SSVID, offset, + PCI_SSVID_SIZEOF, errp); if (pos < 0) { return pos; } diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index 18e634f577..32191f2a55 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -86,7 +86,9 @@ pcie_cap_v1_fill(PCIDevice *dev, uint8_t port, uint8_t type, uint8_t version) pci_set_word(cmask + PCI_EXP_LNKSTA, 0); } -int pcie_cap_init(PCIDevice *dev, uint8_t offset, uint8_t type, uint8_t port) +int pcie_cap_init(PCIDevice *dev, uint8_t offset, + uint8_t type, uint8_t port, + Error **errp) { /* PCIe cap v2 init */ int pos; @@ -94,7 +96,8 @@ int pcie_cap_init(PCIDevice *dev, uint8_t offset, uint8_t type, uint8_t port) assert(pci_is_express(dev)); - pos = pci_add_capability(dev, PCI_CAP_ID_EXP, offset, PCI_EXP_VER2_SIZEOF); + pos = pci_add_capability(dev, PCI_CAP_ID_EXP, offset, + PCI_EXP_VER2_SIZEOF, errp); if (pos < 0) { return pos; } @@ -123,11 +126,14 @@ int pcie_cap_v1_init(PCIDevice *dev, uint8_t offset, uint8_t type, { /* PCIe cap v1 init */ int pos; + Error *local_err = NULL; assert(pci_is_express(dev)); - pos = pci_add_capability(dev, PCI_CAP_ID_EXP, offset, PCI_EXP_VER1_SIZEOF); + pos = pci_add_capability(dev, PCI_CAP_ID_EXP, offset, + PCI_EXP_VER1_SIZEOF, &local_err); if (pos < 0) { + error_report_err(local_err); return pos; } dev->exp.exp_cap = pos; @@ -141,6 +147,8 @@ static int pcie_endpoint_cap_common_init(PCIDevice *dev, uint8_t offset, uint8_t cap_size) { uint8_t type = PCI_EXP_TYPE_ENDPOINT; + Error *local_err = NULL; + int ret; /* * Windows guests will report Code 10, device cannot start, if @@ -151,9 +159,17 @@ pcie_endpoint_cap_common_init(PCIDevice *dev, uint8_t offset, uint8_t cap_size) type = PCI_EXP_TYPE_RC_END; } - return (cap_size == PCI_EXP_VER1_SIZEOF) - ? pcie_cap_v1_init(dev, offset, type, 0) - : pcie_cap_init(dev, offset, type, 0); + if (cap_size == PCI_EXP_VER1_SIZEOF) { + return pcie_cap_v1_init(dev, offset, type, 0); + } else { + ret = pcie_cap_init(dev, offset, type, 0, &local_err); + + if (ret < 0) { + error_report_err(local_err); + } + + return ret; + } } int pcie_endpoint_cap_init(PCIDevice *dev, uint8_t offset) diff --git a/hw/pci/pcie_aer.c b/hw/pci/pcie_aer.c index 828052b0c0..97200742b4 100644 --- a/hw/pci/pcie_aer.c +++ b/hw/pci/pcie_aer.c @@ -813,7 +813,7 @@ const VMStateDescription vmstate_pcie_aer_log = { .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_UINT16(log_num, PCIEAERLog), - VMSTATE_UINT16_EQUAL(log_max, PCIEAERLog), + VMSTATE_UINT16_EQUAL(log_max, PCIEAERLog, NULL), VMSTATE_VALIDATE("log_num <= log_max", pcie_aer_state_log_num_valid), VMSTATE_STRUCT_VARRAY_POINTER_UINT16(log, PCIEAERLog, log_num, vmstate_pcie_aer_err, PCIEAERErr), diff --git a/hw/pci/shpc.c b/hw/pci/shpc.c index 42fafac91b..69fc14b218 100644 --- a/hw/pci/shpc.c +++ b/hw/pci/shpc.c @@ -446,12 +446,13 @@ static void shpc_cap_update_dword(PCIDevice *d) } /* Add SHPC capability to the config space for the device. */ -static int shpc_cap_add_config(PCIDevice *d) +static int shpc_cap_add_config(PCIDevice *d, Error **errp) { uint8_t *config; int config_offset; config_offset = pci_add_capability(d, PCI_CAP_ID_SHPC, - 0, SHPC_CAP_LENGTH); + 0, SHPC_CAP_LENGTH, + errp); if (config_offset < 0) { return config_offset; } @@ -581,13 +582,14 @@ void shpc_device_hot_unplug_request_cb(HotplugHandler *hotplug_dev, } /* Initialize the SHPC structure in bridge's BAR. */ -int shpc_init(PCIDevice *d, PCIBus *sec_bus, MemoryRegion *bar, unsigned offset) +int shpc_init(PCIDevice *d, PCIBus *sec_bus, MemoryRegion *bar, + unsigned offset, Error **errp) { int i, ret; int nslots = SHPC_MAX_SLOTS; /* TODO: qdev property? */ SHPCDevice *shpc = d->shpc = g_malloc0(sizeof(*d->shpc)); shpc->sec_bus = sec_bus; - ret = shpc_cap_add_config(d); + ret = shpc_cap_add_config(d, errp); if (ret) { g_free(d->shpc); return ret; diff --git a/hw/pci/slotid_cap.c b/hw/pci/slotid_cap.c index aec1e9166d..36d021b4a6 100644 --- a/hw/pci/slotid_cap.c +++ b/hw/pci/slotid_cap.c @@ -2,18 +2,21 @@ #include "hw/pci/slotid_cap.h" #include "hw/pci/pci.h" #include "qemu/error-report.h" +#include "qapi/error.h" #define SLOTID_CAP_LENGTH 4 #define SLOTID_NSLOTS_SHIFT ctz32(PCI_SID_ESR_NSLOTS) int slotid_cap_init(PCIDevice *d, int nslots, uint8_t chassis, - unsigned offset) + unsigned offset, + Error **errp) { int cap; + if (!chassis) { - error_report("Bridge chassis not specified. Each bridge is required " - "to be assigned a unique chassis id > 0."); + error_setg(errp, "Bridge chassis not specified. Each bridge is required" + " to be assigned a unique chassis id > 0."); return -EINVAL; } if (nslots < 0 || nslots > (PCI_SID_ESR_NSLOTS >> SLOTID_NSLOTS_SHIFT)) { @@ -21,7 +24,8 @@ int slotid_cap_init(PCIDevice *d, int nslots, return -EINVAL; } - cap = pci_add_capability(d, PCI_CAP_ID_SLOTID, offset, SLOTID_CAP_LENGTH); + cap = pci_add_capability(d, PCI_CAP_ID_SLOTID, offset, + SLOTID_CAP_LENGTH, errp); if (cap < 0) { return cap; } diff --git a/hw/pci/trace-events b/hw/pci/trace-events index 2b9cf24405..83c8f5ace7 100644 --- a/hw/pci/trace-events +++ b/hw/pci/trace-events @@ -7,3 +7,6 @@ pci_update_mappings_add(void *d, uint32_t bus, uint32_t slot, uint32_t func, int # hw/pci/pci_host.c pci_cfg_read(const char *dev, unsigned devid, unsigned fnid, unsigned offs, unsigned val) "%s %02u:%u @0x%x -> 0x%x" pci_cfg_write(const char *dev, unsigned devid, unsigned fnid, unsigned offs, unsigned val) "%s %02u:%u @0x%x <- 0x%x" + +# hw/pci/msix.c +msix_write_config(char *name, bool enabled, bool masked) "dev %s enabled %d masked %d" diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index f7df2388c1..62f1857206 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -774,7 +774,7 @@ static qemu_irq *ppce500_init_mpic(MachineState *machine, PPCE500Params *params, static void ppce500_power_off(void *opaque, int line, int on) { if (on) { - qemu_system_shutdown_request(); + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); } } diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index bae1c0ac99..3056d5f075 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -206,7 +206,6 @@ static void ppc_core99_init(MachineState *machine) /* allocate and load BIOS */ memory_region_init_ram(bios, NULL, "ppc_core99.bios", BIOS_SIZE, &error_fatal); - vmstate_register_ram_global(bios); if (bios_name == NULL) bios_name = PROM_FILENAME; diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index 97bb8541d7..f2ae60a360 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -143,7 +143,6 @@ static void ppc_heathrow_init(MachineState *machine) /* allocate and load BIOS */ memory_region_init_ram(bios, NULL, "ppc_heathrow.bios", BIOS_SIZE, &error_fatal); - vmstate_register_ram_global(bios); if (bios_name == NULL) bios_name = PROM_FILENAME; diff --git a/hw/ppc/mpc8544_guts.c b/hw/ppc/mpc8544_guts.c index ba69178d69..ce1254b5d4 100644 --- a/hw/ppc/mpc8544_guts.c +++ b/hw/ppc/mpc8544_guts.c @@ -98,7 +98,7 @@ static void mpc8544_guts_write(void *opaque, hwaddr addr, switch (addr) { case MPC8544_GUTS_ADDR_RSTCR: if (value & MPC8544_GUTS_RSTCR_RESET) { - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); } break; default: diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 231ed9735b..47221158d4 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -160,13 +160,13 @@ static void powernv_create_core_node(PnvChip *chip, PnvCore *pc, void *fdt) _FDT((fdt_setprop_cell(fdt, offset, "d-cache-size", pcc->l1_dcache_size))); } else { - error_report("Warning: Unknown L1 dcache size for cpu"); + warn_report("Unknown L1 dcache size for cpu"); } if (pcc->l1_icache_size) { _FDT((fdt_setprop_cell(fdt, offset, "i-cache-size", pcc->l1_icache_size))); } else { - error_report("Warning: Unknown L1 icache size for cpu"); + warn_report("Unknown L1 icache size for cpu"); } _FDT((fdt_setprop_cell(fdt, offset, "timebase-frequency", tbfreq))); @@ -378,8 +378,9 @@ static void powernv_populate_ipmi_bt(ISADevice *d, void *fdt, int lpc_off) _FDT(node); g_free(name); - fdt_setprop(fdt, node, "reg", io_regs, sizeof(io_regs)); - fdt_setprop(fdt, node, "compatible", compatible, sizeof(compatible)); + _FDT((fdt_setprop(fdt, node, "reg", io_regs, sizeof(io_regs)))); + _FDT((fdt_setprop(fdt, node, "compatible", compatible, + sizeof(compatible)))); /* Mark it as reserved to avoid Linux trying to claim it */ _FDT((fdt_setprop_string(fdt, node, "status", "reserved"))); @@ -555,7 +556,7 @@ static void ppc_powernv_init(MachineState *machine) /* allocate RAM */ if (machine->ram_size < (1 * G_BYTE)) { - error_report("Warning: skiboot may not work with < 1GB of RAM"); + warn_report("skiboot may not work with < 1GB of RAM"); } ram = g_new(MemoryRegion, 1); @@ -1110,7 +1111,7 @@ static void powernv_machine_initfn(Object *obj) static void powernv_machine_class_props_init(ObjectClass *oc) { - object_class_property_add(oc, "num-chips", "uint32_t", + object_class_property_add(oc, "num-chips", "uint32", pnv_get_num_chips, pnv_set_num_chips, NULL, NULL, NULL); object_class_property_set_description(oc, "num-chips", diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index 1b7ec70f03..67264830db 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -51,7 +51,7 @@ static void powernv_cpu_init(PowerPCCPU *cpu, Error **errp) int thread_index = 0; /* TODO: TCG supports only one thread */ ppc_spr_t *pir = &env->spr_cb[SPR_PIR]; - core_pir = object_property_get_int(OBJECT(cpu), "core-pir", &error_abort); + core_pir = object_property_get_uint(OBJECT(cpu), "core-pir", &error_abort); /* * The PIR of a thread is the core PIR + the thread index. We will @@ -118,18 +118,20 @@ static void pnv_core_realize_child(Object *child, XICSFabric *xi, Error **errp) PowerPCCPU *cpu = POWERPC_CPU(cs); Object *obj; - obj = object_new(TYPE_PNV_ICP); - object_property_add_child(OBJECT(cpu), "icp", obj, NULL); - object_property_add_const_link(obj, "xics", OBJECT(xi), &error_abort); - object_property_set_bool(obj, true, "realized", &local_err); + object_property_set_bool(child, true, "realized", &local_err); if (local_err) { error_propagate(errp, local_err); return; } - object_property_set_bool(child, true, "realized", &local_err); + obj = object_new(TYPE_PNV_ICP); + object_property_add_child(child, "icp", obj, NULL); + object_unref(obj); + object_property_add_const_link(obj, ICP_PROP_XICS, OBJECT(xi), + &error_abort); + object_property_add_const_link(obj, ICP_PROP_CPU, child, &error_abort); + object_property_set_bool(obj, true, "realized", &local_err); if (local_err) { - object_unparent(obj); error_propagate(errp, local_err); return; } @@ -140,8 +142,6 @@ static void pnv_core_realize_child(Object *child, XICSFabric *xi, Error **errp) error_propagate(errp, local_err); return; } - - xics_cpu_setup(xi, cpu, ICP(obj)); } static void pnv_core_realize(DeviceState *dev, Error **errp) diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c index 2bf5bfe3fd..9876c26622 100644 --- a/hw/ppc/pnv_psi.c +++ b/hw/ppc/pnv_psi.c @@ -474,7 +474,8 @@ static void pnv_psi_realize(DeviceState *dev, Error **errp) } /* Create PSI interrupt control source */ - object_property_add_const_link(OBJECT(ics), "xics", obj, &error_abort); + object_property_add_const_link(OBJECT(ics), ICS_PROP_XICS, obj, + &error_abort); object_property_set_int(OBJECT(ics), PSI_NUM_INTERRUPTS, "nr-irqs", &err); if (err) { error_propagate(errp, err); diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index 5f93083d4a..224184d66d 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -412,7 +412,7 @@ static void ppce500_set_irq(void *opaque, int pin, int level) if (level) { LOG_IRQ("%s: reset the PowerPC system\n", __func__); - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); } break; case PPCE500_INPUT_RESET_CORE: diff --git a/hw/ppc/ppc405_boards.c b/hw/ppc/ppc405_boards.c index d01798f245..e92db2c66a 100644 --- a/hw/ppc/ppc405_boards.c +++ b/hw/ppc/ppc405_boards.c @@ -220,7 +220,6 @@ static void ref405ep_init(MachineState *machine) sram_size = 512 * 1024; memory_region_init_ram(sram, NULL, "ef405ep.sram", sram_size, &error_fatal); - vmstate_register_ram_global(sram); memory_region_add_subregion(sysmem, 0xFFF00000, sram); /* allocate and load BIOS */ #ifdef DEBUG_BOARD_INIT @@ -255,7 +254,6 @@ static void ref405ep_init(MachineState *machine) bios = g_new(MemoryRegion, 1); memory_region_init_ram(bios, NULL, "ef405ep.bios", BIOS_SIZE, &error_fatal); - vmstate_register_ram_global(bios); if (bios_name == NULL) bios_name = BIOS_FILENAME; @@ -556,7 +554,6 @@ static void taihu_405ep_init(MachineState *machine) bios = g_new(MemoryRegion, 1); memory_region_init_ram(bios, NULL, "taihu_405ep.bios", BIOS_SIZE, &error_fatal); - vmstate_register_ram_global(bios); filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); if (filename) { bios_size = load_image(filename, memory_region_get_ram_ptr(bios)); diff --git a/hw/ppc/ppc405_uc.c b/hw/ppc/ppc405_uc.c index d5df94aa6e..f6fe3e6f5e 100644 --- a/hw/ppc/ppc405_uc.c +++ b/hw/ppc/ppc405_uc.c @@ -980,7 +980,6 @@ static void ppc405_ocm_init(CPUPPCState *env) /* XXX: Size is 4096 or 0x04000000 */ memory_region_init_ram(&ocm->isarc_ram, NULL, "ppc405.ocm", 4096, &error_fatal); - vmstate_register_ram_global(&ocm->isarc_ram); memory_region_init_alias(&ocm->dsarc_ram, NULL, "ppc405.dsarc", &ocm->isarc_ram, 0, 4096); qemu_register_reset(&ocm_reset, ocm); @@ -1807,7 +1806,7 @@ void ppc40x_chip_reset(PowerPCCPU *cpu) void ppc40x_system_reset(PowerPCCPU *cpu) { printf("Reset PowerPC system\n"); - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); } void store_40x_dbcr0 (CPUPPCState *env, uint32_t val) diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index d16646c95d..36d3dcd89a 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -36,7 +36,6 @@ #include "hw/pci/pci_host.h" #include "hw/ppc/ppc.h" #include "hw/boards.h" -#include "hw/audio/soundhw.h" #include "qemu/error-report.h" #include "qemu/log.h" #include "hw/ide.h" @@ -782,9 +781,6 @@ static void ibm_40p_init(MachineState *machine) qbus_walk_children(BUS(isa_bus), prep_set_cmos_checksum, NULL, NULL, NULL, &cmos_checksum); - /* initialize audio subsystem */ - soundhw_init(); - /* add some more devices */ if (defaults_enabled()) { isa_create_simple(isa_bus, "i8042"); diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 0980d733cd..970093e6b5 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -38,7 +38,9 @@ #include "sysemu/cpus.h" #include "sysemu/hw_accel.h" #include "kvm_ppc.h" -#include "migration/migration.h" +#include "migration/misc.h" +#include "migration/global_state.h" +#include "migration/register.h" #include "mmu-hash64.h" #include "mmu-book3s-v3.h" #include "qom/cpu.h" @@ -57,6 +59,7 @@ #include "hw/pci/pci.h" #include "hw/scsi/scsi.h" #include "hw/virtio/virtio-scsi.h" +#include "hw/virtio/vhost-scsi-common.h" #include "exec/address-spaces.h" #include "hw/usb.h" @@ -95,53 +98,107 @@ #define PHANDLE_XICP 0x00001111 -#define HTAB_SIZE(spapr) (1ULL << ((spapr)->htab_shift)) - static ICSState *spapr_ics_create(sPAPRMachineState *spapr, const char *type_ics, int nr_irqs, Error **errp) { - Error *err = NULL, *local_err = NULL; + Error *local_err = NULL; Object *obj; obj = object_new(type_ics); - object_property_add_child(OBJECT(spapr), "ics", obj, NULL); - object_property_add_const_link(obj, "xics", OBJECT(spapr), &error_abort); - object_property_set_int(obj, nr_irqs, "nr-irqs", &err); + object_property_add_child(OBJECT(spapr), "ics", obj, &error_abort); + object_property_add_const_link(obj, ICS_PROP_XICS, OBJECT(spapr), + &error_abort); + object_property_set_int(obj, nr_irqs, "nr-irqs", &local_err); + if (local_err) { + goto error; + } object_property_set_bool(obj, true, "realized", &local_err); - error_propagate(&err, local_err); - if (err) { - error_propagate(errp, err); - return NULL; + if (local_err) { + goto error; } return ICS_SIMPLE(obj); + +error: + error_propagate(errp, local_err); + return NULL; +} + +static bool pre_2_10_vmstate_dummy_icp_needed(void *opaque) +{ + /* Dummy entries correspond to unused ICPState objects in older QEMUs, + * and newer QEMUs don't even have them. In both cases, we don't want + * to send anything on the wire. + */ + return false; +} + +static const VMStateDescription pre_2_10_vmstate_dummy_icp = { + .name = "icp/server", + .version_id = 1, + .minimum_version_id = 1, + .needed = pre_2_10_vmstate_dummy_icp_needed, + .fields = (VMStateField[]) { + VMSTATE_UNUSED(4), /* uint32_t xirr */ + VMSTATE_UNUSED(1), /* uint8_t pending_priority */ + VMSTATE_UNUSED(1), /* uint8_t mfrr */ + VMSTATE_END_OF_LIST() + }, +}; + +static void pre_2_10_vmstate_register_dummy_icp(int i) +{ + vmstate_register(NULL, i, &pre_2_10_vmstate_dummy_icp, + (void *)(uintptr_t) i); +} + +static void pre_2_10_vmstate_unregister_dummy_icp(int i) +{ + vmstate_unregister(NULL, &pre_2_10_vmstate_dummy_icp, + (void *)(uintptr_t) i); +} + +static inline int xics_max_server_number(void) +{ + return DIV_ROUND_UP(max_cpus * kvmppc_smt_threads(), smp_threads); } static void xics_system_init(MachineState *machine, int nr_irqs, Error **errp) { sPAPRMachineState *spapr = SPAPR_MACHINE(machine); + sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); if (kvm_enabled()) { - Error *err = NULL; - if (machine_kernel_irqchip_allowed(machine) && !xics_kvm_init(spapr, errp)) { spapr->icp_type = TYPE_KVM_ICP; - spapr->ics = spapr_ics_create(spapr, TYPE_ICS_KVM, nr_irqs, &err); + spapr->ics = spapr_ics_create(spapr, TYPE_ICS_KVM, nr_irqs, errp); } if (machine_kernel_irqchip_required(machine) && !spapr->ics) { - error_reportf_err(err, - "kernel_irqchip requested but unavailable: "); - } else { - error_free(err); + error_prepend(errp, "kernel_irqchip requested but unavailable: "); + return; } } if (!spapr->ics) { - xics_spapr_init(spapr, errp); + xics_spapr_init(spapr); spapr->icp_type = TYPE_ICP; spapr->ics = spapr_ics_create(spapr, TYPE_ICS_SIMPLE, nr_irqs, errp); + if (!spapr->ics) { + return; + } + } + + if (smc->pre_2_10_has_unused_icps) { + int i; + + for (i = 0; i < xics_max_server_number(); i++) { + /* Dummy entries get deregistered when real ICPState objects + * are registered during CPU core hotplug. + */ + pre_2_10_vmstate_register_dummy_icp(i); + } } } @@ -178,25 +235,19 @@ static int spapr_fixup_cpu_smt_dt(void *fdt, int offset, PowerPCCPU *cpu, return ret; } -static int spapr_fixup_cpu_numa_dt(void *fdt, int offset, CPUState *cs) +static int spapr_fixup_cpu_numa_dt(void *fdt, int offset, PowerPCCPU *cpu) { - int ret = 0; - PowerPCCPU *cpu = POWERPC_CPU(cs); int index = ppc_get_vcpu_dt_id(cpu); uint32_t associativity[] = {cpu_to_be32(0x5), cpu_to_be32(0x0), cpu_to_be32(0x0), cpu_to_be32(0x0), - cpu_to_be32(cs->numa_node), + cpu_to_be32(cpu->node_id), cpu_to_be32(index)}; /* Advertise NUMA via ibm,associativity */ - if (nb_numa_nodes > 1) { - ret = fdt_setprop(fdt, offset, "ibm,associativity", associativity, + return fdt_setprop(fdt, offset, "ibm,associativity", associativity, sizeof(associativity)); - } - - return ret; } /* Populate the "ibm,pa-features" property */ @@ -321,9 +372,11 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPRMachineState *spapr) return ret; } - ret = spapr_fixup_cpu_numa_dt(fdt, offset, cs); - if (ret < 0) { - return ret; + if (nb_numa_nodes > 1) { + ret = spapr_fixup_cpu_numa_dt(fdt, offset, cpu); + if (ret < 0) { + return ret; + } } ret = spapr_fixup_cpu_smt_dt(fdt, offset, cpu, compat_smt); @@ -452,15 +505,13 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, uint32_t pft_size_prop[] = {0, cpu_to_be32(spapr->htab_shift)}; int compat_smt = MIN(smp_threads, ppc_compat_max_threads(cpu)); sPAPRDRConnector *drc; - sPAPRDRConnectorClass *drck; int drc_index; uint32_t radix_AP_encodings[PPC_PAGE_SIZES_MAX_SZ]; int i; - drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_CPU, index); + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_CPU, index); if (drc) { - drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - drc_index = drck->get_index(drc); + drc_index = spapr_drc_index(drc); _FDT((fdt_setprop_cell(fdt, offset, "ibm,my-drc-index", drc_index))); } @@ -481,13 +532,13 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, _FDT((fdt_setprop_cell(fdt, offset, "d-cache-size", pcc->l1_dcache_size))); } else { - error_report("Warning: Unknown L1 dcache size for cpu"); + warn_report("Unknown L1 dcache size for cpu"); } if (pcc->l1_icache_size) { _FDT((fdt_setprop_cell(fdt, offset, "i-cache-size", pcc->l1_icache_size))); } else { - error_report("Warning: Unknown L1 icache size for cpu"); + warn_report("Unknown L1 icache size for cpu"); } _FDT((fdt_setprop_cell(fdt, offset, "timebase-frequency", tbfreq))); @@ -538,7 +589,9 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, _FDT((fdt_setprop(fdt, offset, "ibm,pft-size", pft_size_prop, sizeof(pft_size_prop)))); - _FDT(spapr_fixup_cpu_numa_dt(fdt, offset, cs)); + if (nb_numa_nodes > 1) { + _FDT(spapr_fixup_cpu_numa_dt(fdt, offset, cpu)); + } _FDT(spapr_fixup_cpu_smt_dt(fdt, offset, cpu, compat_smt)); @@ -650,15 +703,13 @@ static int spapr_populate_drconf_memory(sPAPRMachineState *spapr, void *fdt) if (i >= hotplug_lmb_start) { sPAPRDRConnector *drc; - sPAPRDRConnectorClass *drck; - drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_LMB, i); + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, i); g_assert(drc); - drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); dynamic_memory[0] = cpu_to_be32(addr >> 32); dynamic_memory[1] = cpu_to_be32(addr & 0xffffffff); - dynamic_memory[2] = cpu_to_be32(drck->get_index(drc)); + dynamic_memory[2] = cpu_to_be32(spapr_drc_index(drc)); dynamic_memory[3] = cpu_to_be32(0); /* reserved */ dynamic_memory[4] = cpu_to_be32(numa_get_node(addr, NULL)); if (memory_region_present(get_system_memory(), addr)) { @@ -725,6 +776,11 @@ static int spapr_dt_cas_updates(sPAPRMachineState *spapr, void *fdt, } } + /* /interrupt controller */ + if (!spapr_ovec_test(ov5_updates, OV5_XIVE_EXPLOIT)) { + spapr_dt_xics(xics_max_server_number(), fdt, PHANDLE_XICP); + } + offset = fdt_path_offset(fdt, "/chosen"); if (offset < 0) { offset = fdt_add_subnode(fdt, 0, "chosen"); @@ -748,7 +804,7 @@ int spapr_h_cas_compose_response(sPAPRMachineState *spapr, size -= sizeof(hdr); - /* Create sceleton */ + /* Create skeleton */ fdt_skel = g_malloc0(size); _FDT((fdt_create(fdt_skel, size))); _FDT((fdt_begin_node(fdt_skel, ""))); @@ -816,6 +872,11 @@ static void spapr_dt_rtas(sPAPRMachineState *spapr, void *fdt) if (!kvm_enabled() || kvmppc_spapr_use_multitce()) { add_str(hypertas, "hcall-multi-tce"); } + + if (spapr->resize_hpt != SPAPR_RESIZE_HPT_DISABLED) { + add_str(hypertas, "hcall-hpt-resize"); + } + _FDT(fdt_setprop(fdt, rtas, "ibm,hypertas-functions", hypertas->str, hypertas->len)); g_string_free(hypertas, TRUE); @@ -857,7 +918,8 @@ static void spapr_dt_ov5_platform_support(void *fdt, int chosen) { PowerPCCPU *first_ppc_cpu = POWERPC_CPU(first_cpu); - char val[2 * 3] = { + char val[2 * 4] = { + 23, 0x00, /* Xive mode: 0 = legacy (as in ISA 2.7), 1 = Exploitation */ 24, 0x00, /* Hash/Radix, filled in below. */ 25, 0x00, /* Hash options: Segment Tables == no, GTSE == no. */ 26, 0x40, /* Radix options: GTSE == yes. */ @@ -865,19 +927,19 @@ static void spapr_dt_ov5_platform_support(void *fdt, int chosen) if (kvm_enabled()) { if (kvmppc_has_cap_mmu_radix() && kvmppc_has_cap_mmu_hash_v3()) { - val[1] = 0x80; /* OV5_MMU_BOTH */ + val[3] = 0x80; /* OV5_MMU_BOTH */ } else if (kvmppc_has_cap_mmu_radix()) { - val[1] = 0x40; /* OV5_MMU_RADIX_300 */ + val[3] = 0x40; /* OV5_MMU_RADIX_300 */ } else { - val[1] = 0x00; /* Hash */ + val[3] = 0x00; /* Hash */ } } else { if (first_ppc_cpu->env.mmu_model & POWERPC_MMU_V3) { /* V3 MMU supports both hash and radix (with dynamic switching) */ - val[1] = 0xC0; + val[3] = 0xC0; } else { /* Otherwise we can only do hash */ - val[1] = 0x00; + val[3] = 0x00; } } _FDT(fdt_setprop(fdt, chosen, "ibm,arch-vec-5-platform-support", @@ -977,7 +1039,6 @@ static void *spapr_build_fdt(sPAPRMachineState *spapr, void *fdt; sPAPRPHBState *phb; char *buf; - int smt = kvmppc_smt_threads(); fdt = g_malloc0(FDT_MAX_SIZE); _FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE))); @@ -1016,9 +1077,6 @@ static void *spapr_build_fdt(sPAPRMachineState *spapr, _FDT(fdt_setprop_cell(fdt, 0, "#address-cells", 2)); _FDT(fdt_setprop_cell(fdt, 0, "#size-cells", 2)); - /* /interrupt controller */ - spapr_dt_xics(DIV_ROUND_UP(max_cpus * smt, smp_threads), fdt, PHANDLE_XICP); - ret = spapr_populate_memory(spapr, fdt); if (ret < 0) { error_report("couldn't setup memory nodes in fdt"); @@ -1209,7 +1267,7 @@ static void spapr_store_hpte(PPCVirtualHypervisor *vhyp, hwaddr ptex, } } -static int spapr_hpt_shift_for_ramsize(uint64_t ramsize) +int spapr_hpt_shift_for_ramsize(uint64_t ramsize) { int shift; @@ -1222,16 +1280,21 @@ static int spapr_hpt_shift_for_ramsize(uint64_t ramsize) return shift; } -static void spapr_reallocate_hpt(sPAPRMachineState *spapr, int shift, - Error **errp) +void spapr_free_hpt(sPAPRMachineState *spapr) { - long rc; - - /* Clean up any HPT info from a previous boot */ g_free(spapr->htab); spapr->htab = NULL; spapr->htab_shift = 0; close_htab_fd(spapr); +} + +void spapr_reallocate_hpt(sPAPRMachineState *spapr, int shift, + Error **errp) +{ + long rc; + + /* Clean up any HPT info from a previous boot */ + spapr_free_hpt(spapr); rc = kvmppc_reset_htab(shift); if (rc < 0) { @@ -1274,9 +1337,17 @@ static void spapr_reallocate_hpt(sPAPRMachineState *spapr, int shift, void spapr_setup_hpt_and_vrma(sPAPRMachineState *spapr) { - spapr_reallocate_hpt(spapr, - spapr_hpt_shift_for_ramsize(MACHINE(spapr)->maxram_size), - &error_fatal); + int hpt_shift; + + if ((spapr->resize_hpt == SPAPR_RESIZE_HPT_DISABLED) + || (spapr->cas_reboot + && !spapr_ovec_test(spapr->ov5_cas, OV5_HPT_RESIZE))) { + hpt_shift = spapr_hpt_shift_for_ramsize(MACHINE(spapr)->maxram_size); + } else { + hpt_shift = spapr_hpt_shift_for_ramsize(MACHINE(spapr)->ram_size); + } + spapr_reallocate_hpt(spapr, hpt_shift, &error_fatal); + if (spapr->vrma_adjust) { spapr->rma_size = kvmppc_rma_size(spapr_node0_size(), spapr->htab_shift); @@ -1319,7 +1390,6 @@ static void ppc_spapr_reset(void) * Set the GR bit in PATB so that we know there is no HPT. */ spapr->patb_entry = PATBE1_GR; } else { - spapr->patb_entry = 0; spapr_setup_hpt_and_vrma(spapr); } @@ -1339,6 +1409,8 @@ static void ppc_spapr_reset(void) if (!spapr->cas_reboot) { spapr_ovec_cleanup(spapr->ov5_cas); spapr->ov5_cas = spapr_ovec_new(); + + ppc_set_compat_all(spapr->max_compat_pvr, &error_fatal); } fdt = spapr_build_fdt(spapr, rtas_addr, spapr->rtas_size); @@ -1436,6 +1508,18 @@ static int spapr_post_load(void *opaque, int version_id) err = spapr_rtc_import_offset(&spapr->rtc, spapr->rtc_offset); } + if (spapr->patb_entry) { + PowerPCCPU *cpu = POWERPC_CPU(first_cpu); + bool radix = !!(spapr->patb_entry & PATBE1_GR); + bool gtse = !!(cpu->env.spr[SPR_LPCR] & LPCR_GTSE); + + err = kvmppc_configure_v3_mmu(cpu, radix, gtse, spapr->patb_entry); + if (err) { + error_report("Process table config unsupported by the host"); + return -EINVAL; + } + } + return err; } @@ -1444,6 +1528,37 @@ static bool version_before_3(void *opaque, int version_id) return version_id < 3; } +static bool spapr_pending_events_needed(void *opaque) +{ + sPAPRMachineState *spapr = (sPAPRMachineState *)opaque; + return !QTAILQ_EMPTY(&spapr->pending_events); +} + +static const VMStateDescription vmstate_spapr_event_entry = { + .name = "spapr_event_log_entry", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(summary, sPAPREventLogEntry), + VMSTATE_UINT32(extended_length, sPAPREventLogEntry), + VMSTATE_VBUFFER_ALLOC_UINT32(extended_log, sPAPREventLogEntry, 0, + NULL, extended_length), + VMSTATE_END_OF_LIST() + }, +}; + +static const VMStateDescription vmstate_spapr_pending_events = { + .name = "spapr_pending_events", + .version_id = 1, + .minimum_version_id = 1, + .needed = spapr_pending_events_needed, + .fields = (VMStateField[]) { + VMSTATE_QTAILQ_V(pending_events, sPAPRMachineState, 1, + vmstate_spapr_event_entry, sPAPREventLogEntry, next), + VMSTATE_END_OF_LIST() + }, +}; + static bool spapr_ov5_cas_needed(void *opaque) { sPAPRMachineState *spapr = opaque; @@ -1542,6 +1657,7 @@ static const VMStateDescription vmstate_spapr = { .subsections = (const VMStateDescription*[]) { &vmstate_spapr_ov5_cas, &vmstate_spapr_patb_entry, + &vmstate_spapr_pending_events, NULL } }; @@ -1551,13 +1667,19 @@ static int htab_save_setup(QEMUFile *f, void *opaque) sPAPRMachineState *spapr = opaque; /* "Iteration" header */ - qemu_put_be32(f, spapr->htab_shift); + if (!spapr->htab_shift) { + qemu_put_be32(f, -1); + } else { + qemu_put_be32(f, spapr->htab_shift); + } if (spapr->htab) { spapr->htab_save_index = 0; spapr->htab_first_pass = true; } else { - assert(kvm_enabled()); + if (spapr->htab_shift) { + assert(kvm_enabled()); + } } @@ -1703,7 +1825,12 @@ static int htab_save_iterate(QEMUFile *f, void *opaque) int rc = 0; /* Iteration header */ - qemu_put_be32(f, 0); + if (!spapr->htab_shift) { + qemu_put_be32(f, -1); + return 0; + } else { + qemu_put_be32(f, 0); + } if (!spapr->htab) { assert(kvm_enabled()); @@ -1737,7 +1864,12 @@ static int htab_save_complete(QEMUFile *f, void *opaque) int fd; /* Iteration header */ - qemu_put_be32(f, 0); + if (!spapr->htab_shift) { + qemu_put_be32(f, -1); + return 0; + } else { + qemu_put_be32(f, 0); + } if (!spapr->htab) { int rc; @@ -1781,6 +1913,11 @@ static int htab_load(QEMUFile *f, void *opaque, int version_id) section_hdr = qemu_get_be32(f); + if (section_hdr == -1) { + spapr_free_hpt(spapr); + return 0; + } + if (section_hdr) { Error *local_err = NULL; @@ -1854,7 +1991,7 @@ static int htab_load(QEMUFile *f, void *opaque, int version_id) return 0; } -static void htab_cleanup(void *opaque) +static void htab_save_cleanup(void *opaque) { sPAPRMachineState *spapr = opaque; @@ -1862,10 +1999,10 @@ static void htab_cleanup(void *opaque) } static SaveVMHandlers savevm_htab_handlers = { - .save_live_setup = htab_save_setup, + .save_setup = htab_save_setup, .save_live_iterate = htab_save_iterate, .save_live_complete_precopy = htab_save_complete, - .cleanup = htab_cleanup, + .save_cleanup = htab_save_cleanup, .load_state = htab_load, }; @@ -1876,24 +2013,6 @@ static void spapr_boot_set(void *opaque, const char *boot_device, machine->boot_order = g_strdup(boot_device); } -/* - * Reset routine for LMB DR devices. - * - * Unlike PCI DR devices, LMB DR devices explicitly register this reset - * routine. Reset for PCI DR devices will be handled by PHB reset routine - * when it walks all its children devices. LMB devices reset occurs - * as part of spapr_ppc_reset(). - */ -static void spapr_drc_reset(void *opaque) -{ - sPAPRDRConnector *drc = opaque; - DeviceState *d = DEVICE(drc); - - if (d) { - device_reset(d); - } -} - static void spapr_create_lmb_dr_connectors(sPAPRMachineState *spapr) { MachineState *machine = MACHINE(spapr); @@ -1902,13 +2021,11 @@ static void spapr_create_lmb_dr_connectors(sPAPRMachineState *spapr) int i; for (i = 0; i < nr_lmbs; i++) { - sPAPRDRConnector *drc; uint64_t addr; addr = i * lmb_size + spapr->hotplug_memory.base; - drc = spapr_dr_connector_new(OBJECT(spapr), SPAPR_DR_CONNECTOR_TYPE_LMB, - addr/lmb_size); - qemu_register_reset(spapr_drc_reset, drc); + spapr_dr_connector_new(OBJECT(spapr), TYPE_SPAPR_DRC_LMB, + addr / lmb_size); } } @@ -2002,12 +2119,8 @@ static void spapr_init_cpus(sPAPRMachineState *spapr) int core_id = i * smp_threads; if (mc->has_hotpluggable_cpus) { - sPAPRDRConnector *drc = - spapr_dr_connector_new(OBJECT(spapr), - SPAPR_DR_CONNECTOR_TYPE_CPU, - (core_id / smp_threads) * smt); - - qemu_register_reset(spapr_drc_reset, drc); + spapr_dr_connector_new(OBJECT(spapr), TYPE_SPAPR_DRC_CPU, + (core_id / smp_threads) * smt); } if (i < boot_cores_nr) { @@ -2046,10 +2159,40 @@ static void ppc_spapr_init(MachineState *machine) hwaddr node0_size = spapr_node0_size(); long load_limit, fw_size; char *filename; + Error *resize_hpt_err = NULL; msi_nonbroken = true; QLIST_INIT(&spapr->phbs); + QTAILQ_INIT(&spapr->pending_dimm_unplugs); + + /* Check HPT resizing availability */ + kvmppc_check_papr_resize_hpt(&resize_hpt_err); + if (spapr->resize_hpt == SPAPR_RESIZE_HPT_DEFAULT) { + /* + * If the user explicitly requested a mode we should either + * supply it, or fail completely (which we do below). But if + * it's not set explicitly, we reset our mode to something + * that works + */ + if (resize_hpt_err) { + spapr->resize_hpt = SPAPR_RESIZE_HPT_DISABLED; + error_free(resize_hpt_err); + resize_hpt_err = NULL; + } else { + spapr->resize_hpt = smc->resize_hpt_default; + } + } + + assert(spapr->resize_hpt != SPAPR_RESIZE_HPT_DEFAULT); + + if ((spapr->resize_hpt != SPAPR_RESIZE_HPT_DISABLED) && resize_hpt_err) { + /* + * User requested HPT resize, but this host can't supply it. Bail out + */ + error_report_err(resize_hpt_err); + exit(1); + } /* Allocate RMA if necessary */ rma_alloc_size = kvmppc_alloc_rma(&rma); @@ -2119,12 +2262,17 @@ static void ppc_spapr_init(MachineState *machine) spapr_ovec_set(spapr->ov5, OV5_HP_EVT); } + /* advertise support for HPT resizing */ + if (spapr->resize_hpt != SPAPR_RESIZE_HPT_DISABLED) { + spapr_ovec_set(spapr->ov5, OV5_HPT_RESIZE); + } + /* init CPUs */ if (machine->cpu_model == NULL) { machine->cpu_model = kvm_enabled() ? "host" : smc->tcg_default_cpu; } - ppc_cpu_parse_features(machine->cpu_model); + spapr_cpu_parse_features(spapr); spapr_init_cpus(spapr); @@ -2334,10 +2482,6 @@ static void ppc_spapr_init(MachineState *machine) register_savevm_live(NULL, "spapr/htab", -1, 1, &savevm_htab_handlers, spapr); - /* used by RTAS */ - QTAILQ_INIT(&spapr->ccs_list); - qemu_register_reset(spapr_ccs_reset_hook, spapr); - qemu_register_boot_set(spapr_boot_set, spapr); if (kvm_enabled()) { @@ -2378,6 +2522,7 @@ static char *spapr_get_fw_dev_path(FWPathProvider *p, BusState *bus, ((type *)object_dynamic_cast(OBJECT(obj), (name))) SCSIDevice *d = CAST(SCSIDevice, dev, TYPE_SCSI_DEVICE); sPAPRPHBState *phb = CAST(sPAPRPHBState, dev, TYPE_SPAPR_PCI_HOST_BRIDGE); + VHostSCSICommon *vsc = CAST(VHostSCSICommon, dev, TYPE_VHOST_SCSI_COMMON); if (d) { void *spapr = CAST(void, bus->parent, "spapr-vscsi"); @@ -2434,6 +2579,18 @@ static char *spapr_get_fw_dev_path(FWPathProvider *p, BusState *bus, return g_strdup_printf("pci@%"PRIX64, phb->buid); } + if (vsc) { + /* Same logic as virtio above */ + unsigned id = 0x1000000 | (vsc->target << 16) | vsc->lun; + return g_strdup_printf("disk@%"PRIX64, (uint64_t)id << 32); + } + + if (g_str_equal("pci-bridge", qdev_fw_name(dev))) { + /* SLOF uses "pci" instead of "pci-bridge" for PCI bridges */ + PCIDevice *pcidev = CAST(PCIDevice, dev, TYPE_PCI_DEVICE); + return g_strdup_printf("pci@%x", PCI_SLOT(pcidev->devfn)); + } + return NULL; } @@ -2467,6 +2624,40 @@ static void spapr_set_modern_hotplug_events(Object *obj, bool value, spapr->use_hotplug_event_source = value; } +static char *spapr_get_resize_hpt(Object *obj, Error **errp) +{ + sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + + switch (spapr->resize_hpt) { + case SPAPR_RESIZE_HPT_DEFAULT: + return g_strdup("default"); + case SPAPR_RESIZE_HPT_DISABLED: + return g_strdup("disabled"); + case SPAPR_RESIZE_HPT_ENABLED: + return g_strdup("enabled"); + case SPAPR_RESIZE_HPT_REQUIRED: + return g_strdup("required"); + } + g_assert_not_reached(); +} + +static void spapr_set_resize_hpt(Object *obj, const char *value, Error **errp) +{ + sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + + if (strcmp(value, "default") == 0) { + spapr->resize_hpt = SPAPR_RESIZE_HPT_DEFAULT; + } else if (strcmp(value, "disabled") == 0) { + spapr->resize_hpt = SPAPR_RESIZE_HPT_DISABLED; + } else if (strcmp(value, "enabled") == 0) { + spapr->resize_hpt = SPAPR_RESIZE_HPT_ENABLED; + } else if (strcmp(value, "required") == 0) { + spapr->resize_hpt = SPAPR_RESIZE_HPT_REQUIRED; + } else { + error_setg(errp, "Bad value for \"resize-hpt\" property"); + } +} + static void spapr_machine_initfn(Object *obj) { sPAPRMachineState *spapr = SPAPR_MACHINE(obj); @@ -2487,6 +2678,16 @@ static void spapr_machine_initfn(Object *obj) " place of standard EPOW events when possible" " (required for memory hot-unplug support)", NULL); + + ppc_compat_add_property(obj, "max-cpu-compat", &spapr->max_compat_pvr, + "Maximum permitted CPU compatibility mode", + &error_fatal); + + object_property_add_str(obj, "resize-hpt", + spapr_get_resize_hpt, spapr_set_resize_hpt, NULL); + object_property_set_description(obj, "resize-hpt", + "Resizing of the Hash Page Table (enabled, disabled, required)", + NULL); } static void spapr_machine_finalizefn(Object *obj) @@ -2516,41 +2717,49 @@ static void spapr_add_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size, Error **errp) { sPAPRDRConnector *drc; - sPAPRDRConnectorClass *drck; uint32_t nr_lmbs = size/SPAPR_MEMORY_BLOCK_SIZE; int i, fdt_offset, fdt_size; void *fdt; uint64_t addr = addr_start; + bool hotplugged = spapr_drc_hotplugged(dev); + Error *local_err = NULL; for (i = 0; i < nr_lmbs; i++) { - drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_LMB, - addr/SPAPR_MEMORY_BLOCK_SIZE); + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, + addr / SPAPR_MEMORY_BLOCK_SIZE); g_assert(drc); fdt = create_device_tree(&fdt_size); fdt_offset = spapr_populate_memory_node(fdt, node, addr, SPAPR_MEMORY_BLOCK_SIZE); - drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - drck->attach(drc, dev, fdt, fdt_offset, !dev->hotplugged, errp); - addr += SPAPR_MEMORY_BLOCK_SIZE; - if (!dev->hotplugged) { - /* guests expect coldplugged LMBs to be pre-allocated */ - drck->set_allocation_state(drc, SPAPR_DR_ALLOCATION_STATE_USABLE); - drck->set_isolation_state(drc, SPAPR_DR_ISOLATION_STATE_UNISOLATED); + spapr_drc_attach(drc, dev, fdt, fdt_offset, &local_err); + if (local_err) { + while (addr > addr_start) { + addr -= SPAPR_MEMORY_BLOCK_SIZE; + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, + addr / SPAPR_MEMORY_BLOCK_SIZE); + spapr_drc_detach(drc); + } + g_free(fdt); + error_propagate(errp, local_err); + return; } + if (!hotplugged) { + spapr_drc_reset(drc); + } + addr += SPAPR_MEMORY_BLOCK_SIZE; } /* send hotplug notification to the * guest only in case of hotplugged memory */ - if (dev->hotplugged) { + if (hotplugged) { if (dedicated_hp_event_source) { - drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_LMB, - addr_start / SPAPR_MEMORY_BLOCK_SIZE); - drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, + addr_start / SPAPR_MEMORY_BLOCK_SIZE); spapr_hotplug_req_add_by_count_indexed(SPAPR_DR_CONNECTOR_TYPE_LMB, nr_lmbs, - drck->get_index(drc)); + spapr_drc_index(drc)); } else { spapr_hotplug_req_add_by_count(SPAPR_DR_CONNECTOR_TYPE_LMB, nr_lmbs); @@ -2569,126 +2778,203 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, uint64_t align = memory_region_get_alignment(mr); uint64_t size = memory_region_size(mr); uint64_t addr; - char *mem_dev; - - if (size % SPAPR_MEMORY_BLOCK_SIZE) { - error_setg(&local_err, "Hotplugged memory size must be a multiple of " - "%lld MB", SPAPR_MEMORY_BLOCK_SIZE/M_BYTE); - goto out; - } - - mem_dev = object_property_get_str(OBJECT(dimm), PC_DIMM_MEMDEV_PROP, NULL); - if (mem_dev && !kvmppc_is_mem_backend_page_size_ok(mem_dev)) { - error_setg(&local_err, "Memory backend has bad page size. " - "Use 'memory-backend-file' with correct mem-path."); - goto out; - } pc_dimm_memory_plug(dev, &ms->hotplug_memory, mr, align, &local_err); if (local_err) { goto out; } - addr = object_property_get_int(OBJECT(dimm), PC_DIMM_ADDR_PROP, &local_err); + addr = object_property_get_uint(OBJECT(dimm), + PC_DIMM_ADDR_PROP, &local_err); if (local_err) { - pc_dimm_memory_unplug(dev, &ms->hotplug_memory, mr); - goto out; + goto out_unplug; } spapr_add_lmbs(dev, addr, size, node, spapr_ovec_test(ms->ov5_cas, OV5_HP_EVT), - &error_abort); + &local_err); + if (local_err) { + goto out_unplug; + } + + return; +out_unplug: + pc_dimm_memory_unplug(dev, &ms->hotplug_memory, mr); out: error_propagate(errp, local_err); } -typedef struct sPAPRDIMMState { +static void spapr_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) +{ + PCDIMMDevice *dimm = PC_DIMM(dev); + PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); + MemoryRegion *mr = ddc->get_memory_region(dimm); + uint64_t size = memory_region_size(mr); + char *mem_dev; + + if (size % SPAPR_MEMORY_BLOCK_SIZE) { + error_setg(errp, "Hotplugged memory size must be a multiple of " + "%lld MB", SPAPR_MEMORY_BLOCK_SIZE / M_BYTE); + return; + } + + mem_dev = object_property_get_str(OBJECT(dimm), PC_DIMM_MEMDEV_PROP, NULL); + if (mem_dev && !kvmppc_is_mem_backend_page_size_ok(mem_dev)) { + error_setg(errp, "Memory backend has bad page size. " + "Use 'memory-backend-file' with correct mem-path."); + goto out; + } + +out: + g_free(mem_dev); +} + +struct sPAPRDIMMState { + PCDIMMDevice *dimm; uint32_t nr_lmbs; -} sPAPRDIMMState; + QTAILQ_ENTRY(sPAPRDIMMState) next; +}; -static void spapr_lmb_release(DeviceState *dev, void *opaque) +static sPAPRDIMMState *spapr_pending_dimm_unplugs_find(sPAPRMachineState *s, + PCDIMMDevice *dimm) { - sPAPRDIMMState *ds = (sPAPRDIMMState *)opaque; - HotplugHandler *hotplug_ctrl; + sPAPRDIMMState *dimm_state = NULL; - if (--ds->nr_lmbs) { - return; + QTAILQ_FOREACH(dimm_state, &s->pending_dimm_unplugs, next) { + if (dimm_state->dimm == dimm) { + break; + } } + return dimm_state; +} - g_free(ds); +static void spapr_pending_dimm_unplugs_add(sPAPRMachineState *spapr, + sPAPRDIMMState *dimm_state) +{ + g_assert(!spapr_pending_dimm_unplugs_find(spapr, dimm_state->dimm)); + QTAILQ_INSERT_HEAD(&spapr->pending_dimm_unplugs, dimm_state, next); +} - /* - * Now that all the LMBs have been removed by the guest, call the - * pc-dimm unplug handler to cleanup up the pc-dimm device. - */ - hotplug_ctrl = qdev_get_hotplug_handler(dev); - hotplug_handler_unplug(hotplug_ctrl, dev, &error_abort); +static void spapr_pending_dimm_unplugs_remove(sPAPRMachineState *spapr, + sPAPRDIMMState *dimm_state) +{ + QTAILQ_REMOVE(&spapr->pending_dimm_unplugs, dimm_state, next); + g_free(dimm_state); } -static void spapr_del_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size, - Error **errp) +static sPAPRDIMMState *spapr_recover_pending_dimm_state(sPAPRMachineState *ms, + PCDIMMDevice *dimm) { sPAPRDRConnector *drc; - sPAPRDRConnectorClass *drck; + PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); + MemoryRegion *mr = ddc->get_memory_region(dimm); + uint64_t size = memory_region_size(mr); uint32_t nr_lmbs = size / SPAPR_MEMORY_BLOCK_SIZE; + uint32_t avail_lmbs = 0; + uint64_t addr_start, addr; int i; - sPAPRDIMMState *ds = g_malloc0(sizeof(sPAPRDIMMState)); - uint64_t addr = addr_start; + sPAPRDIMMState *ds; - ds->nr_lmbs = nr_lmbs; + addr_start = object_property_get_int(OBJECT(dimm), PC_DIMM_ADDR_PROP, + &error_abort); + + addr = addr_start; for (i = 0; i < nr_lmbs; i++) { - drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_LMB, - addr / SPAPR_MEMORY_BLOCK_SIZE); + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, + addr / SPAPR_MEMORY_BLOCK_SIZE); g_assert(drc); - - drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - drck->detach(drc, dev, spapr_lmb_release, ds, errp); + if (drc->dev) { + avail_lmbs++; + } addr += SPAPR_MEMORY_BLOCK_SIZE; } - drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_LMB, - addr_start / SPAPR_MEMORY_BLOCK_SIZE); - drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - spapr_hotplug_req_remove_by_count_indexed(SPAPR_DR_CONNECTOR_TYPE_LMB, - nr_lmbs, - drck->get_index(drc)); + ds = g_malloc0(sizeof(sPAPRDIMMState)); + ds->nr_lmbs = avail_lmbs; + ds->dimm = dimm; + spapr_pending_dimm_unplugs_add(ms, ds); + return ds; } -static void spapr_memory_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, - Error **errp) +/* Callback to be called during DRC release. */ +void spapr_lmb_release(DeviceState *dev) { - sPAPRMachineState *ms = SPAPR_MACHINE(hotplug_dev); + sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_hotplug_handler(dev)); PCDIMMDevice *dimm = PC_DIMM(dev); PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); MemoryRegion *mr = ddc->get_memory_region(dimm); + sPAPRDIMMState *ds = spapr_pending_dimm_unplugs_find(spapr, PC_DIMM(dev)); - pc_dimm_memory_unplug(dev, &ms->hotplug_memory, mr); + /* This information will get lost if a migration occurs + * during the unplug process. In this case recover it. */ + if (ds == NULL) { + ds = spapr_recover_pending_dimm_state(spapr, PC_DIMM(dev)); + /* The DRC being examined by the caller at least must be counted */ + g_assert(ds->nr_lmbs); + } + + if (--ds->nr_lmbs) { + return; + } + + spapr_pending_dimm_unplugs_remove(spapr, ds); + + /* + * Now that all the LMBs have been removed by the guest, call the + * pc-dimm unplug handler to cleanup up the pc-dimm device. + */ + pc_dimm_memory_unplug(dev, &spapr->hotplug_memory, mr); object_unparent(OBJECT(dev)); } static void spapr_memory_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { + sPAPRMachineState *spapr = SPAPR_MACHINE(hotplug_dev); Error *local_err = NULL; PCDIMMDevice *dimm = PC_DIMM(dev); PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); MemoryRegion *mr = ddc->get_memory_region(dimm); uint64_t size = memory_region_size(mr); - uint64_t addr; + uint32_t nr_lmbs = size / SPAPR_MEMORY_BLOCK_SIZE; + uint64_t addr_start, addr; + int i; + sPAPRDRConnector *drc; + sPAPRDIMMState *ds; - addr = object_property_get_int(OBJECT(dimm), PC_DIMM_ADDR_PROP, &local_err); + addr_start = object_property_get_uint(OBJECT(dimm), PC_DIMM_ADDR_PROP, + &local_err); if (local_err) { goto out; } - spapr_del_lmbs(dev, addr, size, &error_abort); + ds = g_malloc0(sizeof(sPAPRDIMMState)); + ds->nr_lmbs = nr_lmbs; + ds->dimm = dimm; + spapr_pending_dimm_unplugs_add(spapr, ds); + + addr = addr_start; + for (i = 0; i < nr_lmbs; i++) { + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, + addr / SPAPR_MEMORY_BLOCK_SIZE); + g_assert(drc); + + spapr_drc_detach(drc); + addr += SPAPR_MEMORY_BLOCK_SIZE; + } + + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, + addr_start / SPAPR_MEMORY_BLOCK_SIZE); + spapr_hotplug_req_remove_by_count_indexed(SPAPR_DR_CONNECTOR_TYPE_LMB, + nr_lmbs, spapr_drc_index(drc)); out: error_propagate(errp, local_err); } -void *spapr_populate_hotplug_cpu_dt(CPUState *cs, int *fdt_offset, - sPAPRMachineState *spapr) +static void *spapr_populate_hotplug_cpu_dt(CPUState *cs, int *fdt_offset, + sPAPRMachineState *spapr) { PowerPCCPU *cpu = POWERPC_CPU(cs); DeviceClass *dc = DEVICE_GET_CLASS(cs); @@ -2708,23 +2994,31 @@ void *spapr_populate_hotplug_cpu_dt(CPUState *cs, int *fdt_offset, return fdt; } -static void spapr_core_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, - Error **errp) +/* Callback to be called during DRC release. */ +void spapr_core_release(DeviceState *dev) { - MachineState *ms = MACHINE(qdev_get_machine()); + MachineState *ms = MACHINE(qdev_get_hotplug_handler(dev)); + sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(ms); CPUCore *cc = CPU_CORE(dev); CPUArchId *core_slot = spapr_find_cpu_slot(ms, cc->core_id, NULL); - core_slot->cpu = NULL; - object_unparent(OBJECT(dev)); -} + if (smc->pre_2_10_has_unused_icps) { + sPAPRCPUCore *sc = SPAPR_CPU_CORE(OBJECT(dev)); + sPAPRCPUCoreClass *scc = SPAPR_CPU_CORE_GET_CLASS(OBJECT(cc)); + const char *typename = object_class_get_name(scc->cpu_class); + size_t size = object_type_get_instance_size(typename); + int i; -static void spapr_core_release(DeviceState *dev, void *opaque) -{ - HotplugHandler *hotplug_ctrl; + for (i = 0; i < cc->nr_threads; i++) { + CPUState *cs = CPU(sc->threads + i * size); + + pre_2_10_vmstate_register_dummy_icp(cs->cpu_index); + } + } - hotplug_ctrl = qdev_get_hotplug_handler(dev); - hotplug_handler_unplug(hotplug_ctrl, dev, &error_abort); + assert(core_slot); + core_slot->cpu = NULL; + object_unparent(OBJECT(dev)); } static @@ -2733,8 +3027,6 @@ void spapr_core_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev, { int index; sPAPRDRConnector *drc; - sPAPRDRConnectorClass *drck; - Error *local_err = NULL; CPUCore *cc = CPU_CORE(dev); int smt = kvmppc_smt_threads(); @@ -2748,15 +3040,10 @@ void spapr_core_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev, return; } - drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_CPU, index * smt); + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_CPU, index * smt); g_assert(drc); - drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - drck->detach(drc, dev, spapr_core_release, NULL, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } + spapr_drc_detach(drc); spapr_hotplug_req_remove_by_index(drc); } @@ -2766,16 +3053,16 @@ static void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev, { sPAPRMachineState *spapr = SPAPR_MACHINE(OBJECT(hotplug_dev)); MachineClass *mc = MACHINE_GET_CLASS(spapr); + sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc); sPAPRCPUCore *core = SPAPR_CPU_CORE(OBJECT(dev)); CPUCore *cc = CPU_CORE(dev); CPUState *cs = CPU(core->threads); sPAPRDRConnector *drc; Error *local_err = NULL; - void *fdt = NULL; - int fdt_offset = 0; int smt = kvmppc_smt_threads(); CPUArchId *core_slot; int index; + bool hotplugged = spapr_drc_hotplugged(dev); core_slot = spapr_find_cpu_slot(MACHINE(hotplug_dev), cc->core_id, &index); if (!core_slot) { @@ -2783,45 +3070,50 @@ static void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev, cc->core_id); return; } - drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_CPU, index * smt); + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_CPU, index * smt); g_assert(drc || !mc->has_hotpluggable_cpus); - /* - * Setup CPU DT entries only for hotplugged CPUs. For boot time or - * coldplugged CPUs DT entries are setup in spapr_build_fdt(). - */ - if (dev->hotplugged) { + if (drc) { + void *fdt; + int fdt_offset; + fdt = spapr_populate_hotplug_cpu_dt(cs, &fdt_offset, spapr); - } - if (drc) { - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - drck->attach(drc, dev, fdt, fdt_offset, !dev->hotplugged, &local_err); + spapr_drc_attach(drc, dev, fdt, fdt_offset, &local_err); if (local_err) { g_free(fdt); error_propagate(errp, local_err); return; } - } - if (dev->hotplugged) { - /* - * Send hotplug notification interrupt to the guest only in case - * of hotplugged CPUs. - */ - spapr_hotplug_req_add_by_index(drc); - } else { - /* - * Set the right DRC states for cold plugged CPU. - */ - if (drc) { - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - drck->set_allocation_state(drc, SPAPR_DR_ALLOCATION_STATE_USABLE); - drck->set_isolation_state(drc, SPAPR_DR_ISOLATION_STATE_UNISOLATED); + if (hotplugged) { + /* + * Send hotplug notification interrupt to the guest only + * in case of hotplugged CPUs. + */ + spapr_hotplug_req_add_by_index(drc); + } else { + spapr_drc_reset(drc); } } + core_slot->cpu = OBJECT(dev); + + if (smc->pre_2_10_has_unused_icps) { + sPAPRCPUCoreClass *scc = SPAPR_CPU_CORE_GET_CLASS(OBJECT(cc)); + const char *typename = object_class_get_name(scc->cpu_class); + size_t size = object_type_get_instance_size(typename); + int i; + + for (i = 0; i < cc->nr_threads; i++) { + sPAPRCPUCore *sc = SPAPR_CPU_CORE(dev); + void *obj = sc->threads + i * size; + + cs = CPU(obj); + pre_2_10_vmstate_unregister_dummy_icp(cs->cpu_index); + } + } } static void spapr_core_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, @@ -2831,11 +3123,9 @@ static void spapr_core_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, MachineClass *mc = MACHINE_GET_CLASS(hotplug_dev); Error *local_err = NULL; CPUCore *cc = CPU_CORE(dev); - sPAPRCPUCore *sc = SPAPR_CPU_CORE(dev); char *base_core_type = spapr_get_cpu_core_type(machine->cpu_model); const char *type = object_get_typename(OBJECT(dev)); CPUArchId *core_slot; - int node_id; int index; if (dev->hotplugged && !mc->has_hotpluggable_cpus) { @@ -2853,10 +3143,16 @@ static void spapr_core_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, goto out; } - if (cc->nr_threads != smp_threads) { - error_setg(errp, "invalid nr-threads %d, must be %d", + /* + * In general we should have homogeneous threads-per-core, but old + * (pre hotplug support) machine types allow the last core to have + * reduced threads as a compatibility hack for when we allowed + * total vcpus not a multiple of threads-per-core. + */ + if (mc->has_hotpluggable_cpus && (cc->nr_threads != smp_threads)) { + error_setg(&local_err, "invalid nr-threads %d, must be %d", cc->nr_threads, smp_threads); - return; + goto out; } core_slot = spapr_find_cpu_slot(MACHINE(hotplug_dev), cc->core_id, &index); @@ -2870,20 +3166,7 @@ static void spapr_core_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, goto out; } - node_id = core_slot->props.node_id; - if (!core_slot->props.has_node_id) { - /* by default CPUState::numa_node was 0 if it's not set via CLI - * keep it this way for now but in future we probably should - * refuse to start up with incomplete numa mapping */ - node_id = 0; - } - if (sc->node_id == CPU_UNSET_NUMA_NODE_ID) { - sc->node_id = node_id; - } else if (sc->node_id != node_id) { - error_setg(&local_err, "node-id %d must match numa node specified" - "with -numa option for cpu-index %d", sc->node_id, cc->core_id); - goto out; - } + numa_cpu_pre_plug(core_slot, dev, &local_err); out: g_free(base_core_type); @@ -2902,7 +3185,7 @@ static void spapr_machine_device_plug(HotplugHandler *hotplug_dev, error_setg(errp, "Memory hotplug not supported for this machine"); return; } - node = object_property_get_int(OBJECT(dev), PC_DIMM_NODE_PROP, errp); + node = object_property_get_uint(OBJECT(dev), PC_DIMM_NODE_PROP, errp); if (*errp) { return; } @@ -2939,27 +3222,6 @@ static void spapr_machine_device_plug(HotplugHandler *hotplug_dev, } } -static void spapr_machine_device_unplug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - sPAPRMachineState *sms = SPAPR_MACHINE(qdev_get_machine()); - MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); - - if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { - if (spapr_ovec_test(sms->ov5_cas, OV5_HP_EVT)) { - spapr_memory_unplug(hotplug_dev, dev, errp); - } else { - error_setg(errp, "Memory hot unplug not supported for this guest"); - } - } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) { - if (!mc->has_hotpluggable_cpus) { - error_setg(errp, "CPU hot unplug not supported on this machine"); - return; - } - spapr_core_unplug(hotplug_dev, dev, errp); - } -} - static void spapr_machine_device_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { @@ -2990,7 +3252,9 @@ static void spapr_machine_device_unplug_request(HotplugHandler *hotplug_dev, static void spapr_machine_device_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) { + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + spapr_memory_pre_plug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) { spapr_core_pre_plug(hotplug_dev, dev, errp); } } @@ -3175,7 +3439,6 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) mc->get_hotplug_handler = spapr_get_hotplug_handler; hc->pre_plug = spapr_machine_device_pre_plug; hc->plug = spapr_machine_device_plug; - hc->unplug = spapr_machine_device_unplug; mc->cpu_index_to_instance_props = spapr_cpu_index_to_props; mc->possible_cpu_arch_ids = spapr_possible_cpu_arch_ids; hc->unplug_request = spapr_machine_device_unplug_request; @@ -3183,6 +3446,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) smc->dr_lmb_enabled = true; smc->tcg_default_cpu = "POWER8"; mc->has_hotpluggable_cpus = true; + smc->resize_hpt_default = SPAPR_RESIZE_HPT_ENABLED; fwc->get_dev_path = spapr_get_fw_dev_path; nc->nmi_monitor_handler = spapr_nmi; smc->phb_placement = spapr_phb_placement; @@ -3269,7 +3533,12 @@ DEFINE_SPAPR_MACHINE(2_10, "2.10", true); * pseries-2.9 */ #define SPAPR_COMPAT_2_9 \ - HW_COMPAT_2_9 + HW_COMPAT_2_9 \ + { \ + .driver = TYPE_POWERPC_CPU, \ + .property = "pre-2.10-migration", \ + .value = "on", \ + }, \ static void spapr_machine_2_9_instance_options(MachineState *machine) { @@ -3278,9 +3547,13 @@ static void spapr_machine_2_9_instance_options(MachineState *machine) static void spapr_machine_2_9_class_options(MachineClass *mc) { + sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc); + spapr_machine_2_10_class_options(mc); SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_9); mc->numa_auto_assign_ram = numa_legacy_auto_assign_ram; + smc->pre_2_10_has_unused_icps = true; + smc->resize_hpt_default = SPAPR_RESIZE_HPT_DISABLED; } DEFINE_SPAPR_MACHINE(2_9, "2.9", false); @@ -3493,9 +3766,6 @@ DEFINE_SPAPR_MACHINE(2_4, "2.4", false); static void spapr_machine_2_3_instance_options(MachineState *machine) { spapr_machine_2_4_instance_options(machine); - savevm_skip_section_footers(); - global_state_set_optional(); - savevm_skip_configuration(); } static void spapr_machine_2_3_class_options(MachineClass *mc) diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index a17ea07ef1..ea278ce2a7 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -20,6 +20,57 @@ #include "sysemu/numa.h" #include "qemu/error-report.h" +void spapr_cpu_parse_features(sPAPRMachineState *spapr) +{ + /* + * Backwards compatibility hack: + * + * CPUs had a "compat=" property which didn't make sense for + * anything except pseries. It was replaced by "max-cpu-compat" + * machine option. This supports old command lines like + * -cpu POWER8,compat=power7 + * By stripping the compat option and applying it to the machine + * before passing it on to the cpu level parser. + */ + gchar **inpieces; + int i, j; + gchar *compat_str = NULL; + + inpieces = g_strsplit(MACHINE(spapr)->cpu_model, ",", 0); + + /* inpieces[0] is the actual model string */ + i = 1; + j = 1; + while (inpieces[i]) { + if (g_str_has_prefix(inpieces[i], "compat=")) { + /* in case of multiple compat= options */ + g_free(compat_str); + compat_str = inpieces[i]; + } else { + j++; + } + + i++; + /* Excise compat options from list */ + inpieces[j] = inpieces[i]; + } + + if (compat_str) { + char *val = compat_str + strlen("compat="); + gchar *newprops = g_strjoinv(",", inpieces); + + object_property_set_str(OBJECT(spapr), val, "max-cpu-compat", + &error_fatal); + + ppc_cpu_parse_features(newprops); + g_free(newprops); + } else { + ppc_cpu_parse_features(MACHINE(spapr)->cpu_model); + } + + g_strfreev(inpieces); +} + static void spapr_cpu_reset(void *opaque) { sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); @@ -53,9 +104,6 @@ static void spapr_cpu_reset(void *opaque) static void spapr_cpu_destroy(PowerPCCPU *cpu) { - sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); - - xics_cpu_destroy(XICS_FABRIC(spapr), cpu); qemu_unregister_reset(spapr_cpu_reset, cpu); } @@ -70,16 +118,6 @@ static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, /* Enable PAPR mode in TCG or KVM */ cpu_ppc_set_papr(cpu, PPC_VIRTUAL_HYPERVISOR(spapr)); - if (cpu->max_compat) { - Error *local_err = NULL; - - ppc_set_compat(cpu, cpu->max_compat, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - } - qemu_register_reset(spapr_cpu_reset, cpu); spapr_cpu_reset(cpu); } @@ -142,30 +180,33 @@ static void spapr_cpu_core_realize_child(Object *child, Error **errp) PowerPCCPU *cpu = POWERPC_CPU(cs); Object *obj; - obj = object_new(spapr->icp_type); - object_property_add_child(OBJECT(cpu), "icp", obj, NULL); - object_property_add_const_link(obj, "xics", OBJECT(spapr), &error_abort); - object_property_set_bool(obj, true, "realized", &local_err); + object_property_set_bool(child, true, "realized", &local_err); if (local_err) { - error_propagate(errp, local_err); - return; + goto error; } - object_property_set_bool(child, true, "realized", &local_err); + spapr_cpu_init(spapr, cpu, &local_err); if (local_err) { - object_unparent(obj); - error_propagate(errp, local_err); - return; + goto error; } - spapr_cpu_init(spapr, cpu, &local_err); + obj = object_new(spapr->icp_type); + object_property_add_child(child, "icp", obj, &error_abort); + object_unref(obj); + object_property_add_const_link(obj, ICP_PROP_XICS, OBJECT(spapr), + &error_abort); + object_property_add_const_link(obj, ICP_PROP_CPU, child, &error_abort); + object_property_set_bool(obj, true, "realized", &local_err); if (local_err) { - object_unparent(obj); - error_propagate(errp, local_err); - return; + goto free_icp; } - xics_cpu_setup(XICS_FABRIC(spapr), cpu, ICP(obj)); + return; + +free_icp: + object_unparent(obj); +error: + error_propagate(errp, local_err); } static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) @@ -183,15 +224,17 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) for (i = 0; i < cc->nr_threads; i++) { char id[32]; CPUState *cs; + PowerPCCPU *cpu; obj = sc->threads + i * size; object_initialize(obj, size, typename); cs = CPU(obj); + cpu = POWERPC_CPU(cs); cs->cpu_index = cc->core_id + i; /* Set NUMA node for the threads belonged to core */ - cs->numa_node = sc->node_id; + cpu->node_id = sc->node_id; snprintf(id, sizeof(id), "thread[%d]", i); object_property_add_child(OBJECT(sc), id, obj, &local_err); diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c index 9fa5545991..0ffcec6fb2 100644 --- a/hw/ppc/spapr_drc.c +++ b/hw/ppc/spapr_drc.c @@ -20,52 +20,95 @@ #include "qapi/visitor.h" #include "qemu/error-report.h" #include "hw/ppc/spapr.h" /* for RTAS return codes */ +#include "hw/pci-host/spapr.h" /* spapr_phb_remove_pci_device_cb callback */ #include "trace.h" #define DRC_CONTAINER_PATH "/dr-connector" #define DRC_INDEX_TYPE_SHIFT 28 #define DRC_INDEX_ID_MASK ((1ULL << DRC_INDEX_TYPE_SHIFT) - 1) -static sPAPRDRConnectorTypeShift get_type_shift(sPAPRDRConnectorType type) +sPAPRDRConnectorType spapr_drc_type(sPAPRDRConnector *drc) { - uint32_t shift = 0; - - /* make sure this isn't SPAPR_DR_CONNECTOR_TYPE_ANY, or some - * other wonky value. - */ - g_assert(is_power_of_2(type)); + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - while (type != (1 << shift)) { - shift++; - } - return shift; + return 1 << drck->typeshift; } -static uint32_t get_index(sPAPRDRConnector *drc) +uint32_t spapr_drc_index(sPAPRDRConnector *drc) { + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + /* no set format for a drc index: it only needs to be globally * unique. this is how we encode the DRC type on bare-metal * however, so might as well do that here */ - return (get_type_shift(drc->type) << DRC_INDEX_TYPE_SHIFT) | - (drc->id & DRC_INDEX_ID_MASK); + return (drck->typeshift << DRC_INDEX_TYPE_SHIFT) + | (drc->id & DRC_INDEX_ID_MASK); } -static uint32_t set_isolation_state(sPAPRDRConnector *drc, - sPAPRDRIsolationState state) +static uint32_t drc_isolate_physical(sPAPRDRConnector *drc) { - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + switch (drc->state) { + case SPAPR_DRC_STATE_PHYSICAL_POWERON: + return RTAS_OUT_SUCCESS; /* Nothing to do */ + case SPAPR_DRC_STATE_PHYSICAL_CONFIGURED: + break; /* see below */ + case SPAPR_DRC_STATE_PHYSICAL_UNISOLATE: + return RTAS_OUT_PARAM_ERROR; /* not allowed */ + default: + g_assert_not_reached(); + } - trace_spapr_drc_set_isolation_state(get_index(drc), state); + drc->state = SPAPR_DRC_STATE_PHYSICAL_POWERON; - if (state == SPAPR_DR_ISOLATION_STATE_UNISOLATED) { - /* cannot unisolate a non-existent resource, and, or resources - * which are in an 'UNUSABLE' allocation state. (PAPR 2.7, 13.5.3.5) - */ - if (!drc->dev || - drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_UNUSABLE) { - return RTAS_OUT_NO_SUCH_INDICATOR; - } + if (drc->unplug_requested) { + uint32_t drc_index = spapr_drc_index(drc); + trace_spapr_drc_set_isolation_state_finalizing(drc_index); + spapr_drc_detach(drc); + } + + return RTAS_OUT_SUCCESS; +} + +static uint32_t drc_unisolate_physical(sPAPRDRConnector *drc) +{ + switch (drc->state) { + case SPAPR_DRC_STATE_PHYSICAL_UNISOLATE: + case SPAPR_DRC_STATE_PHYSICAL_CONFIGURED: + return RTAS_OUT_SUCCESS; /* Nothing to do */ + case SPAPR_DRC_STATE_PHYSICAL_POWERON: + break; /* see below */ + default: + g_assert_not_reached(); + } + + /* cannot unisolate a non-existent resource, and, or resources + * which are in an 'UNUSABLE' allocation state. (PAPR 2.7, + * 13.5.3.5) + */ + if (!drc->dev) { + return RTAS_OUT_NO_SUCH_INDICATOR; + } + + drc->state = SPAPR_DRC_STATE_PHYSICAL_UNISOLATE; + drc->ccs_offset = drc->fdt_start_offset; + drc->ccs_depth = 0; + + return RTAS_OUT_SUCCESS; +} + +static uint32_t drc_isolate_logical(sPAPRDRConnector *drc) +{ + switch (drc->state) { + case SPAPR_DRC_STATE_LOGICAL_AVAILABLE: + case SPAPR_DRC_STATE_LOGICAL_UNUSABLE: + return RTAS_OUT_SUCCESS; /* Nothing to do */ + case SPAPR_DRC_STATE_LOGICAL_CONFIGURED: + break; /* see below */ + case SPAPR_DRC_STATE_LOGICAL_UNISOLATE: + return RTAS_OUT_PARAM_ERROR; /* not allowed */ + default: + g_assert_not_reached(); } /* @@ -78,124 +121,135 @@ static uint32_t set_isolation_state(sPAPRDRConnector *drc, * If the LMB being removed doesn't belong to a DIMM device that is * actually being unplugged, fail the isolation request here. */ - if (drc->type == SPAPR_DR_CONNECTOR_TYPE_LMB) { - if ((state == SPAPR_DR_ISOLATION_STATE_ISOLATED) && - !drc->awaiting_release) { - return RTAS_OUT_HW_ERROR; - } + if (spapr_drc_type(drc) == SPAPR_DR_CONNECTOR_TYPE_LMB + && !drc->unplug_requested) { + return RTAS_OUT_HW_ERROR; } - drc->isolation_state = state; - - if (drc->isolation_state == SPAPR_DR_ISOLATION_STATE_ISOLATED) { - /* if we're awaiting release, but still in an unconfigured state, - * it's likely the guest is still in the process of configuring - * the device and is transitioning the devices to an ISOLATED - * state as a part of that process. so we only complete the - * removal when this transition happens for a device in a - * configured state, as suggested by the state diagram from - * PAPR+ 2.7, 13.4 - */ - if (drc->awaiting_release) { - if (drc->configured) { - trace_spapr_drc_set_isolation_state_finalizing(get_index(drc)); - drck->detach(drc, DEVICE(drc->dev), drc->detach_cb, - drc->detach_cb_opaque, NULL); - } else { - trace_spapr_drc_set_isolation_state_deferring(get_index(drc)); - } - } - drc->configured = false; - } + drc->state = SPAPR_DRC_STATE_LOGICAL_AVAILABLE; + /* if we're awaiting release, but still in an unconfigured state, + * it's likely the guest is still in the process of configuring + * the device and is transitioning the devices to an ISOLATED + * state as a part of that process. so we only complete the + * removal when this transition happens for a device in a + * configured state, as suggested by the state diagram from PAPR+ + * 2.7, 13.4 + */ + if (drc->unplug_requested) { + uint32_t drc_index = spapr_drc_index(drc); + trace_spapr_drc_set_isolation_state_finalizing(drc_index); + spapr_drc_detach(drc); + } return RTAS_OUT_SUCCESS; } -static uint32_t set_indicator_state(sPAPRDRConnector *drc, - sPAPRDRIndicatorState state) +static uint32_t drc_unisolate_logical(sPAPRDRConnector *drc) { - trace_spapr_drc_set_indicator_state(get_index(drc), state); - drc->indicator_state = state; + switch (drc->state) { + case SPAPR_DRC_STATE_LOGICAL_UNISOLATE: + case SPAPR_DRC_STATE_LOGICAL_CONFIGURED: + return RTAS_OUT_SUCCESS; /* Nothing to do */ + case SPAPR_DRC_STATE_LOGICAL_AVAILABLE: + break; /* see below */ + case SPAPR_DRC_STATE_LOGICAL_UNUSABLE: + return RTAS_OUT_NO_SUCH_INDICATOR; /* not allowed */ + default: + g_assert_not_reached(); + } + + /* Move to AVAILABLE state should have ensured device was present */ + g_assert(drc->dev); + + drc->state = SPAPR_DRC_STATE_LOGICAL_UNISOLATE; + drc->ccs_offset = drc->fdt_start_offset; + drc->ccs_depth = 0; + return RTAS_OUT_SUCCESS; } -static uint32_t set_allocation_state(sPAPRDRConnector *drc, - sPAPRDRAllocationState state) +static uint32_t drc_set_usable(sPAPRDRConnector *drc) { - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - - trace_spapr_drc_set_allocation_state(get_index(drc), state); - - if (state == SPAPR_DR_ALLOCATION_STATE_USABLE) { - /* if there's no resource/device associated with the DRC, there's - * no way for us to put it in an allocation state consistent with - * being 'USABLE'. PAPR 2.7, 13.5.3.4 documents that this should - * result in an RTAS return code of -3 / "no such indicator" - */ - if (!drc->dev) { - return RTAS_OUT_NO_SUCH_INDICATOR; - } - if (drc->awaiting_release && drc->awaiting_allocation) { - /* kernel is acknowledging a previous hotplug event - * while we are already removing it. - * it's safe to ignore awaiting_allocation here since we know the - * situation is predicated on the guest either already having done - * so (boot-time hotplug), or never being able to acquire in the - * first place (hotplug followed by immediate unplug). - */ - drc->awaiting_allocation_skippable = true; - return RTAS_OUT_NO_SUCH_INDICATOR; - } + switch (drc->state) { + case SPAPR_DRC_STATE_LOGICAL_AVAILABLE: + case SPAPR_DRC_STATE_LOGICAL_UNISOLATE: + case SPAPR_DRC_STATE_LOGICAL_CONFIGURED: + return RTAS_OUT_SUCCESS; /* Nothing to do */ + case SPAPR_DRC_STATE_LOGICAL_UNUSABLE: + break; /* see below */ + default: + g_assert_not_reached(); } - if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI) { - drc->allocation_state = state; - if (drc->awaiting_release && - drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_UNUSABLE) { - trace_spapr_drc_set_allocation_state_finalizing(get_index(drc)); - drck->detach(drc, DEVICE(drc->dev), drc->detach_cb, - drc->detach_cb_opaque, NULL); - } else if (drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_USABLE) { - drc->awaiting_allocation = false; - } + /* if there's no resource/device associated with the DRC, there's + * no way for us to put it in an allocation state consistent with + * being 'USABLE'. PAPR 2.7, 13.5.3.4 documents that this should + * result in an RTAS return code of -3 / "no such indicator" + */ + if (!drc->dev) { + return RTAS_OUT_NO_SUCH_INDICATOR; + } + if (drc->unplug_requested) { + /* Don't allow the guest to move a device away from UNUSABLE + * state when we want to unplug it */ + return RTAS_OUT_NO_SUCH_INDICATOR; } - return RTAS_OUT_SUCCESS; -} -static uint32_t get_type(sPAPRDRConnector *drc) -{ - return drc->type; -} + drc->state = SPAPR_DRC_STATE_LOGICAL_AVAILABLE; -static const char *get_name(sPAPRDRConnector *drc) -{ - return drc->name; + return RTAS_OUT_SUCCESS; } -static const void *get_fdt(sPAPRDRConnector *drc, int *fdt_start_offset) +static uint32_t drc_set_unusable(sPAPRDRConnector *drc) { - if (fdt_start_offset) { - *fdt_start_offset = drc->fdt_start_offset; + switch (drc->state) { + case SPAPR_DRC_STATE_LOGICAL_UNUSABLE: + return RTAS_OUT_SUCCESS; /* Nothing to do */ + case SPAPR_DRC_STATE_LOGICAL_AVAILABLE: + break; /* see below */ + case SPAPR_DRC_STATE_LOGICAL_UNISOLATE: + case SPAPR_DRC_STATE_LOGICAL_CONFIGURED: + return RTAS_OUT_NO_SUCH_INDICATOR; /* not allowed */ + default: + g_assert_not_reached(); } - return drc->fdt; -} -static void set_configured(sPAPRDRConnector *drc) -{ - trace_spapr_drc_set_configured(get_index(drc)); - - if (drc->isolation_state != SPAPR_DR_ISOLATION_STATE_UNISOLATED) { - /* guest should be not configuring an isolated device */ - trace_spapr_drc_set_configured_skipping(get_index(drc)); - return; + drc->state = SPAPR_DRC_STATE_LOGICAL_UNUSABLE; + if (drc->unplug_requested) { + uint32_t drc_index = spapr_drc_index(drc); + trace_spapr_drc_set_allocation_state_finalizing(drc_index); + spapr_drc_detach(drc); } - drc->configured = true; + + return RTAS_OUT_SUCCESS; } -/* has the guest been notified of device attachment? */ -static void set_signalled(sPAPRDRConnector *drc) +static const char *spapr_drc_name(sPAPRDRConnector *drc) { - drc->signalled = true; + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + + /* human-readable name for a DRC to encode into the DT + * description. this is mainly only used within a guest in place + * of the unique DRC index. + * + * in the case of VIO/PCI devices, it corresponds to a "location + * code" that maps a logical device/function (DRC index) to a + * physical (or virtual in the case of VIO) location in the system + * by chaining together the "location label" for each + * encapsulating component. + * + * since this is more to do with diagnosing physical hardware + * issues than guest compatibility, we choose location codes/DRC + * names that adhere to the documented format, but avoid encoding + * the entire topology information into the label/code, instead + * just using the location codes based on the labels for the + * endpoints (VIO/PCI adaptor connectors), which is basically just + * "C" followed by an integer ID. + * + * DRC names as documented by PAPR+ v2.7, 13.5.2.4 + * location codes as documented by PAPR+ v2.7, 12.3.1.5 + */ + return g_strdup_printf("%s%d", drck->drc_name_prefix, drc->id); } /* @@ -205,74 +259,37 @@ static void set_signalled(sPAPRDRConnector *drc) * based on the current allocation/indicator/power states * for the DR connector. */ -static uint32_t entity_sense(sPAPRDRConnector *drc, sPAPRDREntitySense *state) -{ - if (drc->dev) { - if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI && - drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_UNUSABLE) { - /* for logical DR, we return a state of UNUSABLE - * iff the allocation state UNUSABLE. - * Otherwise, report the state as USABLE/PRESENT, - * as we would for PCI. - */ - *state = SPAPR_DR_ENTITY_SENSE_UNUSABLE; - } else { - /* this assumes all PCI devices are assigned to - * a 'live insertion' power domain, where QEMU - * manages power state automatically as opposed - * to the guest. present, non-PCI resources are - * unaffected by power state. - */ - *state = SPAPR_DR_ENTITY_SENSE_PRESENT; - } - } else { - if (drc->type == SPAPR_DR_CONNECTOR_TYPE_PCI) { - /* PCI devices, and only PCI devices, use EMPTY - * in cases where we'd otherwise use UNUSABLE - */ - *state = SPAPR_DR_ENTITY_SENSE_EMPTY; - } else { - *state = SPAPR_DR_ENTITY_SENSE_UNUSABLE; - } - } - - trace_spapr_drc_entity_sense(get_index(drc), *state); - return RTAS_OUT_SUCCESS; -} - -static void prop_get_index(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj); - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - uint32_t value = (uint32_t)drck->get_index(drc); - visit_type_uint32(v, name, &value, errp); -} - -static void prop_get_type(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) +static sPAPRDREntitySense physical_entity_sense(sPAPRDRConnector *drc) { - sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj); - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - uint32_t value = (uint32_t)drck->get_type(drc); - visit_type_uint32(v, name, &value, errp); + /* this assumes all PCI devices are assigned to a 'live insertion' + * power domain, where QEMU manages power state automatically as + * opposed to the guest. present, non-PCI resources are unaffected + * by power state. + */ + return drc->dev ? SPAPR_DR_ENTITY_SENSE_PRESENT + : SPAPR_DR_ENTITY_SENSE_EMPTY; } -static char *prop_get_name(Object *obj, Error **errp) +static sPAPRDREntitySense logical_entity_sense(sPAPRDRConnector *drc) { - sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj); - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - return g_strdup(drck->get_name(drc)); + switch (drc->state) { + case SPAPR_DRC_STATE_LOGICAL_UNUSABLE: + return SPAPR_DR_ENTITY_SENSE_UNUSABLE; + case SPAPR_DRC_STATE_LOGICAL_AVAILABLE: + case SPAPR_DRC_STATE_LOGICAL_UNISOLATE: + case SPAPR_DRC_STATE_LOGICAL_CONFIGURED: + g_assert(drc->dev); + return SPAPR_DR_ENTITY_SENSE_PRESENT; + default: + g_assert_not_reached(); + } } -static void prop_get_entity_sense(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) +static void prop_get_index(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj); - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - uint32_t value; - - drck->entity_sense(drc, &value); + uint32_t value = spapr_drc_index(drc); visit_type_uint32(v, name, &value, errp); } @@ -352,51 +369,22 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name, } while (fdt_depth != 0); } -static void attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt, - int fdt_start_offset, bool coldplug, Error **errp) +void spapr_drc_attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt, + int fdt_start_offset, Error **errp) { - trace_spapr_drc_attach(get_index(drc)); + trace_spapr_drc_attach(spapr_drc_index(drc)); - if (drc->isolation_state != SPAPR_DR_ISOLATION_STATE_ISOLATED) { + if (drc->dev) { error_setg(errp, "an attached device is still awaiting release"); return; } - if (drc->type == SPAPR_DR_CONNECTOR_TYPE_PCI) { - g_assert(drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_USABLE); - } - g_assert(fdt || coldplug); - - /* NOTE: setting initial isolation state to UNISOLATED means we can't - * detach unless guest has a userspace/kernel that moves this state - * back to ISOLATED in response to an unplug event, or this is done - * manually by the admin prior. if we force things while the guest - * may be accessing the device, we can easily crash the guest, so we - * we defer completion of removal in such cases to the reset() hook. - */ - if (drc->type == SPAPR_DR_CONNECTOR_TYPE_PCI) { - drc->isolation_state = SPAPR_DR_ISOLATION_STATE_UNISOLATED; - } - drc->indicator_state = SPAPR_DR_INDICATOR_STATE_ACTIVE; + g_assert((drc->state == SPAPR_DRC_STATE_LOGICAL_UNUSABLE) + || (drc->state == SPAPR_DRC_STATE_PHYSICAL_POWERON)); + g_assert(fdt); drc->dev = d; drc->fdt = fdt; drc->fdt_start_offset = fdt_start_offset; - drc->configured = coldplug; - /* 'logical' DR resources such as memory/cpus are in some cases treated - * as a pool of resources from which the guest is free to choose from - * based on only a count. for resources that can be assigned in this - * fashion, we must assume the resource is signalled immediately - * since a single hotplug request might make an arbitrary number of - * such attached resources available to the guest, as opposed to - * 'physical' DR resources such as PCI where each device/resource is - * signalled individually. - */ - drc->signalled = (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI) - ? true : coldplug; - - if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI) { - drc->awaiting_allocation = true; - } object_property_add_link(OBJECT(drc), "device", object_get_typename(OBJECT(drc->dev)), @@ -404,127 +392,105 @@ static void attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt, NULL, 0, NULL); } -static void detach(sPAPRDRConnector *drc, DeviceState *d, - spapr_drc_detach_cb *detach_cb, - void *detach_cb_opaque, Error **errp) +static void spapr_drc_release(sPAPRDRConnector *drc) { - trace_spapr_drc_detach(get_index(drc)); + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - drc->detach_cb = detach_cb; - drc->detach_cb_opaque = detach_cb_opaque; + drck->release(drc->dev); - /* if we've signalled device presence to the guest, or if the guest - * has gone ahead and configured the device (via manually-executed - * device add via drmgr in guest, namely), we need to wait - * for the guest to quiesce the device before completing detach. - * Otherwise, we can assume the guest hasn't seen it and complete the - * detach immediately. Note that there is a small race window - * just before, or during, configuration, which is this context - * refers mainly to fetching the device tree via RTAS. - * During this window the device access will be arbitrated by - * associated DRC, which will simply fail the RTAS calls as invalid. - * This is recoverable within guest and current implementations of - * drmgr should be able to cope. - */ - if (!drc->signalled && !drc->configured) { - /* if the guest hasn't seen the device we can't rely on it to - * set it back to an isolated state via RTAS, so do it here manually - */ - drc->isolation_state = SPAPR_DR_ISOLATION_STATE_ISOLATED; - } + drc->unplug_requested = false; + g_free(drc->fdt); + drc->fdt = NULL; + drc->fdt_start_offset = 0; + object_property_del(OBJECT(drc), "device", &error_abort); + drc->dev = NULL; +} - if (drc->isolation_state != SPAPR_DR_ISOLATION_STATE_ISOLATED) { - trace_spapr_drc_awaiting_isolated(get_index(drc)); - drc->awaiting_release = true; - return; - } +void spapr_drc_detach(sPAPRDRConnector *drc) +{ + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + + trace_spapr_drc_detach(spapr_drc_index(drc)); + + g_assert(drc->dev); - if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI && - drc->allocation_state != SPAPR_DR_ALLOCATION_STATE_UNUSABLE) { - trace_spapr_drc_awaiting_unusable(get_index(drc)); - drc->awaiting_release = true; + drc->unplug_requested = true; + + if (drc->state != drck->empty_state) { + trace_spapr_drc_awaiting_quiesce(spapr_drc_index(drc)); return; } - if (drc->awaiting_allocation) { - if (!drc->awaiting_allocation_skippable) { - drc->awaiting_release = true; - trace_spapr_drc_awaiting_allocation(get_index(drc)); - return; - } - } + spapr_drc_release(drc); +} - drc->indicator_state = SPAPR_DR_INDICATOR_STATE_INACTIVE; +void spapr_drc_reset(sPAPRDRConnector *drc) +{ + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + + trace_spapr_drc_reset(spapr_drc_index(drc)); - if (drc->detach_cb) { - drc->detach_cb(drc->dev, drc->detach_cb_opaque); + /* immediately upon reset we can safely assume DRCs whose devices + * are pending removal can be safely removed. + */ + if (drc->unplug_requested) { + spapr_drc_release(drc); } - drc->awaiting_release = false; - drc->awaiting_allocation_skippable = false; - g_free(drc->fdt); - drc->fdt = NULL; - drc->fdt_start_offset = 0; - object_property_del(OBJECT(drc), "device", NULL); - drc->dev = NULL; - drc->detach_cb = NULL; - drc->detach_cb_opaque = NULL; + if (drc->dev) { + /* A device present at reset is ready to go, same as coldplugged */ + drc->state = drck->ready_state; + } else { + drc->state = drck->empty_state; + } + + drc->ccs_offset = -1; + drc->ccs_depth = -1; } -static bool release_pending(sPAPRDRConnector *drc) +static void drc_reset(void *opaque) { - return drc->awaiting_release; + spapr_drc_reset(SPAPR_DR_CONNECTOR(opaque)); } -static void reset(DeviceState *d) +static bool spapr_drc_needed(void *opaque) { - sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(d); + sPAPRDRConnector *drc = (sPAPRDRConnector *)opaque; sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - sPAPRDREntitySense state; - - trace_spapr_drc_reset(drck->get_index(drc)); - /* immediately upon reset we can safely assume DRCs whose devices - * are pending removal can be safely removed, and that they will - * subsequently be left in an ISOLATED state. move the DRC to this - * state in these cases (which will in turn complete any pending - * device removals) - */ - if (drc->awaiting_release) { - drck->set_isolation_state(drc, SPAPR_DR_ISOLATION_STATE_ISOLATED); - /* generally this should also finalize the removal, but if the device - * hasn't yet been configured we normally defer removal under the - * assumption that this transition is taking place as part of device - * configuration. so check if we're still waiting after this, and - * force removal if we are - */ - if (drc->awaiting_release) { - drck->detach(drc, DEVICE(drc->dev), drc->detach_cb, - drc->detach_cb_opaque, NULL); - } + sPAPRDREntitySense value = drck->dr_entity_sense(drc); - /* non-PCI devices may be awaiting a transition to UNUSABLE */ - if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI && - drc->awaiting_release) { - drck->set_allocation_state(drc, SPAPR_DR_ALLOCATION_STATE_UNUSABLE); - } + /* If no dev is plugged in there is no need to migrate the DRC state */ + if (value != SPAPR_DR_ENTITY_SENSE_PRESENT) { + return false; } - drck->entity_sense(drc, &state); - if (state == SPAPR_DR_ENTITY_SENSE_PRESENT) { - drck->set_signalled(drc); - } + /* + * We need to migrate the state if it's not equal to the expected + * long-term state, which is the same as the coldplugged initial + * state */ + return (drc->state != drck->ready_state); } +static const VMStateDescription vmstate_spapr_drc = { + .name = "spapr_drc", + .version_id = 1, + .minimum_version_id = 1, + .needed = spapr_drc_needed, + .fields = (VMStateField []) { + VMSTATE_UINT32(state, sPAPRDRConnector), + VMSTATE_END_OF_LIST() + } +}; + static void realize(DeviceState *d, Error **errp) { sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(d); - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); Object *root_container; char link_name[256]; gchar *child_name; Error *err = NULL; - trace_spapr_drc_realize(drck->get_index(drc)); + trace_spapr_drc_realize(spapr_drc_index(drc)); /* NOTE: we do this as part of realize/unrealize due to the fact * that the guest will communicate with the DRC via RTAS calls * referencing the global DRC index. By unlinking the DRC @@ -533,9 +499,9 @@ static void realize(DeviceState *d, Error **errp) * existing in the composition tree */ root_container = container_get(object_get_root(), DRC_CONTAINER_PATH); - snprintf(link_name, sizeof(link_name), "%x", drck->get_index(drc)); + snprintf(link_name, sizeof(link_name), "%x", spapr_drc_index(drc)); child_name = object_get_canonical_path_component(OBJECT(drc)); - trace_spapr_drc_realize_child(drck->get_index(drc), child_name); + trace_spapr_drc_realize_child(spapr_drc_index(drc), child_name); object_property_add_alias(root_container, link_name, drc->owner, child_name, &err); if (err) { @@ -543,20 +509,22 @@ static void realize(DeviceState *d, Error **errp) object_unref(OBJECT(drc)); } g_free(child_name); - trace_spapr_drc_realize_complete(drck->get_index(drc)); + vmstate_register(DEVICE(drc), spapr_drc_index(drc), &vmstate_spapr_drc, + drc); + qemu_register_reset(drc_reset, drc); + trace_spapr_drc_realize_complete(spapr_drc_index(drc)); } static void unrealize(DeviceState *d, Error **errp) { sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(d); - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); Object *root_container; char name[256]; Error *err = NULL; - trace_spapr_drc_unrealize(drck->get_index(drc)); + trace_spapr_drc_unrealize(spapr_drc_index(drc)); root_container = container_get(object_get_root(), DRC_CONTAINER_PATH); - snprintf(name, sizeof(name), "%x", drck->get_index(drc)); + snprintf(name, sizeof(name), "%x", spapr_drc_index(drc)); object_property_del(root_container, name, &err); if (err) { error_report_err(err); @@ -564,120 +532,153 @@ static void unrealize(DeviceState *d, Error **errp) } } -sPAPRDRConnector *spapr_dr_connector_new(Object *owner, - sPAPRDRConnectorType type, +sPAPRDRConnector *spapr_dr_connector_new(Object *owner, const char *type, uint32_t id) { - sPAPRDRConnector *drc = - SPAPR_DR_CONNECTOR(object_new(TYPE_SPAPR_DR_CONNECTOR)); + sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(object_new(type)); char *prop_name; - g_assert(type); - - drc->type = type; drc->id = id; drc->owner = owner; - prop_name = g_strdup_printf("dr-connector[%"PRIu32"]", get_index(drc)); + prop_name = g_strdup_printf("dr-connector[%"PRIu32"]", + spapr_drc_index(drc)); object_property_add_child(owner, prop_name, OBJECT(drc), NULL); object_property_set_bool(OBJECT(drc), true, "realized", NULL); g_free(prop_name); - /* human-readable name for a DRC to encode into the DT - * description. this is mainly only used within a guest in place - * of the unique DRC index. - * - * in the case of VIO/PCI devices, it corresponds to a - * "location code" that maps a logical device/function (DRC index) - * to a physical (or virtual in the case of VIO) location in the - * system by chaining together the "location label" for each - * encapsulating component. - * - * since this is more to do with diagnosing physical hardware - * issues than guest compatibility, we choose location codes/DRC - * names that adhere to the documented format, but avoid encoding - * the entire topology information into the label/code, instead - * just using the location codes based on the labels for the - * endpoints (VIO/PCI adaptor connectors), which is basically - * just "C" followed by an integer ID. - * - * DRC names as documented by PAPR+ v2.7, 13.5.2.4 - * location codes as documented by PAPR+ v2.7, 12.3.1.5 - */ - switch (drc->type) { - case SPAPR_DR_CONNECTOR_TYPE_CPU: - drc->name = g_strdup_printf("CPU %d", id); - break; - case SPAPR_DR_CONNECTOR_TYPE_PHB: - drc->name = g_strdup_printf("PHB %d", id); - break; - case SPAPR_DR_CONNECTOR_TYPE_VIO: - case SPAPR_DR_CONNECTOR_TYPE_PCI: - drc->name = g_strdup_printf("C%d", id); - break; - case SPAPR_DR_CONNECTOR_TYPE_LMB: - drc->name = g_strdup_printf("LMB %d", id); - break; - default: - g_assert(false); - } - - /* PCI slot always start in a USABLE state, and stay there */ - if (drc->type == SPAPR_DR_CONNECTOR_TYPE_PCI) { - drc->allocation_state = SPAPR_DR_ALLOCATION_STATE_USABLE; - } - return drc; } static void spapr_dr_connector_instance_init(Object *obj) { sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj); + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - object_property_add_uint32_ptr(obj, "isolation-state", - &drc->isolation_state, NULL); - object_property_add_uint32_ptr(obj, "indicator-state", - &drc->indicator_state, NULL); - object_property_add_uint32_ptr(obj, "allocation-state", - &drc->allocation_state, NULL); object_property_add_uint32_ptr(obj, "id", &drc->id, NULL); object_property_add(obj, "index", "uint32", prop_get_index, NULL, NULL, NULL, NULL); - object_property_add(obj, "connector_type", "uint32", prop_get_type, - NULL, NULL, NULL, NULL); - object_property_add_str(obj, "name", prop_get_name, NULL, NULL); - object_property_add(obj, "entity-sense", "uint32", prop_get_entity_sense, - NULL, NULL, NULL, NULL); object_property_add(obj, "fdt", "struct", prop_get_fdt, NULL, NULL, NULL, NULL); + drc->state = drck->empty_state; } static void spapr_dr_connector_class_init(ObjectClass *k, void *data) { DeviceClass *dk = DEVICE_CLASS(k); - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); - dk->reset = reset; dk->realize = realize; dk->unrealize = unrealize; - drck->set_isolation_state = set_isolation_state; - drck->set_indicator_state = set_indicator_state; - drck->set_allocation_state = set_allocation_state; - drck->get_index = get_index; - drck->get_type = get_type; - drck->get_name = get_name; - drck->get_fdt = get_fdt; - drck->set_configured = set_configured; - drck->entity_sense = entity_sense; - drck->attach = attach; - drck->detach = detach; - drck->release_pending = release_pending; - drck->set_signalled = set_signalled; /* * Reason: it crashes FIXME find and document the real reason */ dk->user_creatable = false; } +static bool drc_physical_needed(void *opaque) +{ + sPAPRDRCPhysical *drcp = (sPAPRDRCPhysical *)opaque; + sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(drcp); + + if ((drc->dev && (drcp->dr_indicator == SPAPR_DR_INDICATOR_ACTIVE)) + || (!drc->dev && (drcp->dr_indicator == SPAPR_DR_INDICATOR_INACTIVE))) { + return false; + } + return true; +} + +static const VMStateDescription vmstate_spapr_drc_physical = { + .name = "spapr_drc/physical", + .version_id = 1, + .minimum_version_id = 1, + .needed = drc_physical_needed, + .fields = (VMStateField []) { + VMSTATE_UINT32(dr_indicator, sPAPRDRCPhysical), + VMSTATE_END_OF_LIST() + } +}; + +static void drc_physical_reset(void *opaque) +{ + sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(opaque); + sPAPRDRCPhysical *drcp = SPAPR_DRC_PHYSICAL(drc); + + if (drc->dev) { + drcp->dr_indicator = SPAPR_DR_INDICATOR_ACTIVE; + } else { + drcp->dr_indicator = SPAPR_DR_INDICATOR_INACTIVE; + } +} + +static void realize_physical(DeviceState *d, Error **errp) +{ + sPAPRDRCPhysical *drcp = SPAPR_DRC_PHYSICAL(d); + Error *local_err = NULL; + + realize(d, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + vmstate_register(DEVICE(drcp), spapr_drc_index(SPAPR_DR_CONNECTOR(drcp)), + &vmstate_spapr_drc_physical, drcp); + qemu_register_reset(drc_physical_reset, drcp); +} + +static void spapr_drc_physical_class_init(ObjectClass *k, void *data) +{ + DeviceClass *dk = DEVICE_CLASS(k); + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); + + dk->realize = realize_physical; + drck->dr_entity_sense = physical_entity_sense; + drck->isolate = drc_isolate_physical; + drck->unisolate = drc_unisolate_physical; + drck->ready_state = SPAPR_DRC_STATE_PHYSICAL_CONFIGURED; + drck->empty_state = SPAPR_DRC_STATE_PHYSICAL_POWERON; +} + +static void spapr_drc_logical_class_init(ObjectClass *k, void *data) +{ + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); + + drck->dr_entity_sense = logical_entity_sense; + drck->isolate = drc_isolate_logical; + drck->unisolate = drc_unisolate_logical; + drck->ready_state = SPAPR_DRC_STATE_LOGICAL_CONFIGURED; + drck->empty_state = SPAPR_DRC_STATE_LOGICAL_UNUSABLE; +} + +static void spapr_drc_cpu_class_init(ObjectClass *k, void *data) +{ + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); + + drck->typeshift = SPAPR_DR_CONNECTOR_TYPE_SHIFT_CPU; + drck->typename = "CPU"; + drck->drc_name_prefix = "CPU "; + drck->release = spapr_core_release; +} + +static void spapr_drc_pci_class_init(ObjectClass *k, void *data) +{ + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); + + drck->typeshift = SPAPR_DR_CONNECTOR_TYPE_SHIFT_PCI; + drck->typename = "28"; + drck->drc_name_prefix = "C"; + drck->release = spapr_phb_remove_pci_device_cb; +} + +static void spapr_drc_lmb_class_init(ObjectClass *k, void *data) +{ + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); + + drck->typeshift = SPAPR_DR_CONNECTOR_TYPE_SHIFT_LMB; + drck->typename = "MEM"; + drck->drc_name_prefix = "LMB "; + drck->release = spapr_lmb_release; +} + static const TypeInfo spapr_dr_connector_info = { .name = TYPE_SPAPR_DR_CONNECTOR, .parent = TYPE_DEVICE, @@ -685,18 +686,45 @@ static const TypeInfo spapr_dr_connector_info = { .instance_init = spapr_dr_connector_instance_init, .class_size = sizeof(sPAPRDRConnectorClass), .class_init = spapr_dr_connector_class_init, + .abstract = true, }; -static void spapr_drc_register_types(void) -{ - type_register_static(&spapr_dr_connector_info); -} +static const TypeInfo spapr_drc_physical_info = { + .name = TYPE_SPAPR_DRC_PHYSICAL, + .parent = TYPE_SPAPR_DR_CONNECTOR, + .instance_size = sizeof(sPAPRDRCPhysical), + .class_init = spapr_drc_physical_class_init, + .abstract = true, +}; -type_init(spapr_drc_register_types) +static const TypeInfo spapr_drc_logical_info = { + .name = TYPE_SPAPR_DRC_LOGICAL, + .parent = TYPE_SPAPR_DR_CONNECTOR, + .class_init = spapr_drc_logical_class_init, + .abstract = true, +}; + +static const TypeInfo spapr_drc_cpu_info = { + .name = TYPE_SPAPR_DRC_CPU, + .parent = TYPE_SPAPR_DRC_LOGICAL, + .class_init = spapr_drc_cpu_class_init, +}; + +static const TypeInfo spapr_drc_pci_info = { + .name = TYPE_SPAPR_DRC_PCI, + .parent = TYPE_SPAPR_DRC_PHYSICAL, + .class_init = spapr_drc_pci_class_init, +}; + +static const TypeInfo spapr_drc_lmb_info = { + .name = TYPE_SPAPR_DRC_LMB, + .parent = TYPE_SPAPR_DRC_LOGICAL, + .class_init = spapr_drc_lmb_class_init, +}; /* helper functions for external users */ -sPAPRDRConnector *spapr_dr_connector_by_index(uint32_t index) +sPAPRDRConnector *spapr_drc_by_index(uint32_t index) { Object *obj; char name[256]; @@ -707,37 +735,13 @@ sPAPRDRConnector *spapr_dr_connector_by_index(uint32_t index) return !obj ? NULL : SPAPR_DR_CONNECTOR(obj); } -sPAPRDRConnector *spapr_dr_connector_by_id(sPAPRDRConnectorType type, - uint32_t id) -{ - return spapr_dr_connector_by_index( - (get_type_shift(type) << DRC_INDEX_TYPE_SHIFT) | - (id & DRC_INDEX_ID_MASK)); -} - -/* generate a string the describes the DRC to encode into the - * device tree. - * - * as documented by PAPR+ v2.7, 13.5.2.6 and C.6.1 - */ -static const char *spapr_drc_get_type_str(sPAPRDRConnectorType type) +sPAPRDRConnector *spapr_drc_by_id(const char *type, uint32_t id) { - switch (type) { - case SPAPR_DR_CONNECTOR_TYPE_CPU: - return "CPU"; - case SPAPR_DR_CONNECTOR_TYPE_PHB: - return "PHB"; - case SPAPR_DR_CONNECTOR_TYPE_VIO: - return "SLOT"; - case SPAPR_DR_CONNECTOR_TYPE_PCI: - return "28"; - case SPAPR_DR_CONNECTOR_TYPE_LMB: - return "MEM"; - default: - g_assert(false); - } + sPAPRDRConnectorClass *drck + = SPAPR_DR_CONNECTOR_CLASS(object_class_by_name(type)); - return NULL; + return spapr_drc_by_index(drck->typeshift << DRC_INDEX_TYPE_SHIFT + | (id & DRC_INDEX_ID_MASK)); } /** @@ -802,14 +806,14 @@ int spapr_drc_populate_dt(void *fdt, int fdt_offset, Object *owner, continue; } - if ((drc->type & drc_type_mask) == 0) { + if ((spapr_drc_type(drc) & drc_type_mask) == 0) { continue; } drc_count++; /* ibm,drc-indexes */ - drc_index = cpu_to_be32(drck->get_index(drc)); + drc_index = cpu_to_be32(spapr_drc_index(drc)); g_array_append_val(drc_indexes, drc_index); /* ibm,drc-power-domains */ @@ -817,12 +821,11 @@ int spapr_drc_populate_dt(void *fdt, int fdt_offset, Object *owner, g_array_append_val(drc_power_domains, drc_power_domain); /* ibm,drc-names */ - drc_names = g_string_append(drc_names, drck->get_name(drc)); + drc_names = g_string_append(drc_names, spapr_drc_name(drc)); drc_names = g_string_insert_len(drc_names, -1, "\0", 1); /* ibm,drc-types */ - drc_types = g_string_append(drc_types, - spapr_drc_get_type_str(drc->type)); + drc_types = g_string_append(drc_types, drck->typename); drc_types = g_string_insert_len(drc_types, -1, "\0", 1); } @@ -872,3 +875,297 @@ out: return ret; } + +/* + * RTAS calls + */ + +static uint32_t rtas_set_isolation_state(uint32_t idx, uint32_t state) +{ + sPAPRDRConnector *drc = spapr_drc_by_index(idx); + sPAPRDRConnectorClass *drck; + + if (!drc) { + return RTAS_OUT_NO_SUCH_INDICATOR; + } + + trace_spapr_drc_set_isolation_state(spapr_drc_index(drc), state); + + drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + + switch (state) { + case SPAPR_DR_ISOLATION_STATE_ISOLATED: + return drck->isolate(drc); + + case SPAPR_DR_ISOLATION_STATE_UNISOLATED: + return drck->unisolate(drc); + + default: + return RTAS_OUT_PARAM_ERROR; + } +} + +static uint32_t rtas_set_allocation_state(uint32_t idx, uint32_t state) +{ + sPAPRDRConnector *drc = spapr_drc_by_index(idx); + + if (!drc || !object_dynamic_cast(OBJECT(drc), TYPE_SPAPR_DRC_LOGICAL)) { + return RTAS_OUT_NO_SUCH_INDICATOR; + } + + trace_spapr_drc_set_allocation_state(spapr_drc_index(drc), state); + + switch (state) { + case SPAPR_DR_ALLOCATION_STATE_USABLE: + return drc_set_usable(drc); + + case SPAPR_DR_ALLOCATION_STATE_UNUSABLE: + return drc_set_unusable(drc); + + default: + return RTAS_OUT_PARAM_ERROR; + } +} + +static uint32_t rtas_set_dr_indicator(uint32_t idx, uint32_t state) +{ + sPAPRDRConnector *drc = spapr_drc_by_index(idx); + + if (!drc || !object_dynamic_cast(OBJECT(drc), TYPE_SPAPR_DRC_PHYSICAL)) { + return RTAS_OUT_NO_SUCH_INDICATOR; + } + if ((state != SPAPR_DR_INDICATOR_INACTIVE) + && (state != SPAPR_DR_INDICATOR_ACTIVE) + && (state != SPAPR_DR_INDICATOR_IDENTIFY) + && (state != SPAPR_DR_INDICATOR_ACTION)) { + return RTAS_OUT_PARAM_ERROR; /* bad state parameter */ + } + + trace_spapr_drc_set_dr_indicator(idx, state); + SPAPR_DRC_PHYSICAL(drc)->dr_indicator = state; + return RTAS_OUT_SUCCESS; +} + +static void rtas_set_indicator(PowerPCCPU *cpu, sPAPRMachineState *spapr, + uint32_t token, + uint32_t nargs, target_ulong args, + uint32_t nret, target_ulong rets) +{ + uint32_t type, idx, state; + uint32_t ret = RTAS_OUT_SUCCESS; + + if (nargs != 3 || nret != 1) { + ret = RTAS_OUT_PARAM_ERROR; + goto out; + } + + type = rtas_ld(args, 0); + idx = rtas_ld(args, 1); + state = rtas_ld(args, 2); + + switch (type) { + case RTAS_SENSOR_TYPE_ISOLATION_STATE: + ret = rtas_set_isolation_state(idx, state); + break; + case RTAS_SENSOR_TYPE_DR: + ret = rtas_set_dr_indicator(idx, state); + break; + case RTAS_SENSOR_TYPE_ALLOCATION_STATE: + ret = rtas_set_allocation_state(idx, state); + break; + default: + ret = RTAS_OUT_NOT_SUPPORTED; + } + +out: + rtas_st(rets, 0, ret); +} + +static void rtas_get_sensor_state(PowerPCCPU *cpu, sPAPRMachineState *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, uint32_t nret, + target_ulong rets) +{ + uint32_t sensor_type; + uint32_t sensor_index; + uint32_t sensor_state = 0; + sPAPRDRConnector *drc; + sPAPRDRConnectorClass *drck; + uint32_t ret = RTAS_OUT_SUCCESS; + + if (nargs != 2 || nret != 2) { + ret = RTAS_OUT_PARAM_ERROR; + goto out; + } + + sensor_type = rtas_ld(args, 0); + sensor_index = rtas_ld(args, 1); + + if (sensor_type != RTAS_SENSOR_TYPE_ENTITY_SENSE) { + /* currently only DR-related sensors are implemented */ + trace_spapr_rtas_get_sensor_state_not_supported(sensor_index, + sensor_type); + ret = RTAS_OUT_NOT_SUPPORTED; + goto out; + } + + drc = spapr_drc_by_index(sensor_index); + if (!drc) { + trace_spapr_rtas_get_sensor_state_invalid(sensor_index); + ret = RTAS_OUT_PARAM_ERROR; + goto out; + } + drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + sensor_state = drck->dr_entity_sense(drc); + +out: + rtas_st(rets, 0, ret); + rtas_st(rets, 1, sensor_state); +} + +/* configure-connector work area offsets, int32_t units for field + * indexes, bytes for field offset/len values. + * + * as documented by PAPR+ v2.7, 13.5.3.5 + */ +#define CC_IDX_NODE_NAME_OFFSET 2 +#define CC_IDX_PROP_NAME_OFFSET 2 +#define CC_IDX_PROP_LEN 3 +#define CC_IDX_PROP_DATA_OFFSET 4 +#define CC_VAL_DATA_OFFSET ((CC_IDX_PROP_DATA_OFFSET + 1) * 4) +#define CC_WA_LEN 4096 + +static void configure_connector_st(target_ulong addr, target_ulong offset, + const void *buf, size_t len) +{ + cpu_physical_memory_write(ppc64_phys_to_real(addr + offset), + buf, MIN(len, CC_WA_LEN - offset)); +} + +static void rtas_ibm_configure_connector(PowerPCCPU *cpu, + sPAPRMachineState *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, uint32_t nret, + target_ulong rets) +{ + uint64_t wa_addr; + uint64_t wa_offset; + uint32_t drc_index; + sPAPRDRConnector *drc; + sPAPRDRConnectorClass *drck; + sPAPRDRCCResponse resp = SPAPR_DR_CC_RESPONSE_CONTINUE; + int rc; + + if (nargs != 2 || nret != 1) { + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); + return; + } + + wa_addr = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 0); + + drc_index = rtas_ld(wa_addr, 0); + drc = spapr_drc_by_index(drc_index); + if (!drc) { + trace_spapr_rtas_ibm_configure_connector_invalid(drc_index); + rc = RTAS_OUT_PARAM_ERROR; + goto out; + } + + if ((drc->state != SPAPR_DRC_STATE_LOGICAL_UNISOLATE) + && (drc->state != SPAPR_DRC_STATE_PHYSICAL_UNISOLATE)) { + /* Need to unisolate the device before configuring */ + rc = SPAPR_DR_CC_RESPONSE_NOT_CONFIGURABLE; + goto out; + } + + g_assert(drc->fdt); + + drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + + do { + uint32_t tag; + const char *name; + const struct fdt_property *prop; + int fdt_offset_next, prop_len; + + tag = fdt_next_tag(drc->fdt, drc->ccs_offset, &fdt_offset_next); + + switch (tag) { + case FDT_BEGIN_NODE: + drc->ccs_depth++; + name = fdt_get_name(drc->fdt, drc->ccs_offset, NULL); + + /* provide the name of the next OF node */ + wa_offset = CC_VAL_DATA_OFFSET; + rtas_st(wa_addr, CC_IDX_NODE_NAME_OFFSET, wa_offset); + configure_connector_st(wa_addr, wa_offset, name, strlen(name) + 1); + resp = SPAPR_DR_CC_RESPONSE_NEXT_CHILD; + break; + case FDT_END_NODE: + drc->ccs_depth--; + if (drc->ccs_depth == 0) { + uint32_t drc_index = spapr_drc_index(drc); + + /* done sending the device tree, move to configured state */ + trace_spapr_drc_set_configured(drc_index); + drc->state = drck->ready_state; + drc->ccs_offset = -1; + drc->ccs_depth = -1; + resp = SPAPR_DR_CC_RESPONSE_SUCCESS; + } else { + resp = SPAPR_DR_CC_RESPONSE_PREV_PARENT; + } + break; + case FDT_PROP: + prop = fdt_get_property_by_offset(drc->fdt, drc->ccs_offset, + &prop_len); + name = fdt_string(drc->fdt, fdt32_to_cpu(prop->nameoff)); + + /* provide the name of the next OF property */ + wa_offset = CC_VAL_DATA_OFFSET; + rtas_st(wa_addr, CC_IDX_PROP_NAME_OFFSET, wa_offset); + configure_connector_st(wa_addr, wa_offset, name, strlen(name) + 1); + + /* provide the length and value of the OF property. data gets + * placed immediately after NULL terminator of the OF property's + * name string + */ + wa_offset += strlen(name) + 1, + rtas_st(wa_addr, CC_IDX_PROP_LEN, prop_len); + rtas_st(wa_addr, CC_IDX_PROP_DATA_OFFSET, wa_offset); + configure_connector_st(wa_addr, wa_offset, prop->data, prop_len); + resp = SPAPR_DR_CC_RESPONSE_NEXT_PROPERTY; + break; + case FDT_END: + resp = SPAPR_DR_CC_RESPONSE_ERROR; + default: + /* keep seeking for an actionable tag */ + break; + } + if (drc->ccs_offset >= 0) { + drc->ccs_offset = fdt_offset_next; + } + } while (resp == SPAPR_DR_CC_RESPONSE_CONTINUE); + + rc = resp; +out: + rtas_st(rets, 0, rc); +} + +static void spapr_drc_register_types(void) +{ + type_register_static(&spapr_dr_connector_info); + type_register_static(&spapr_drc_physical_info); + type_register_static(&spapr_drc_logical_info); + type_register_static(&spapr_drc_cpu_info); + type_register_static(&spapr_drc_pci_info); + type_register_static(&spapr_drc_lmb_info); + + spapr_rtas_register(RTAS_SET_INDICATOR, "set-indicator", + rtas_set_indicator); + spapr_rtas_register(RTAS_GET_SENSOR_STATE, "get-sensor-state", + rtas_get_sensor_state); + spapr_rtas_register(RTAS_IBM_CONFIGURE_CONNECTOR, "ibm,configure-connector", + rtas_ibm_configure_connector); +} +type_init(spapr_drc_register_types) diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c index f0b28d8112..f952b78237 100644 --- a/hw/ppc/spapr_events.c +++ b/hw/ppc/spapr_events.c @@ -28,7 +28,6 @@ #include "qapi/error.h" #include "cpu.h" #include "sysemu/sysemu.h" -#include "sysemu/char.h" #include "hw/qdev.h" #include "sysemu/device_tree.h" @@ -43,8 +42,6 @@ #include "hw/ppc/spapr_ovec.h" #include <libfdt.h> -struct rtas_error_log { - uint32_t summary; #define RTAS_LOG_VERSION_MASK 0xff000000 #define RTAS_LOG_VERSION_6 0x06000000 #define RTAS_LOG_SEVERITY_MASK 0x00e00000 @@ -86,6 +83,9 @@ struct rtas_error_log { #define RTAS_LOG_TYPE_ECC_CORR 0x0000000a #define RTAS_LOG_TYPE_EPOW 0x00000040 #define RTAS_LOG_TYPE_HOTPLUG 0x000000e5 + +struct rtas_error_log { + uint32_t summary; uint32_t extended_length; } QEMU_PACKED; @@ -167,8 +167,7 @@ struct rtas_event_log_v6_epow { uint64_t reason_code; } QEMU_PACKED; -struct epow_log_full { - struct rtas_error_log hdr; +struct epow_extended_log { struct rtas_event_log_v6 v6hdr; struct rtas_event_log_v6_maina maina; struct rtas_event_log_v6_mainb mainb; @@ -206,8 +205,7 @@ struct rtas_event_log_v6_hp { union drc_identifier drc_id; } QEMU_PACKED; -struct hp_log_full { - struct rtas_error_log hdr; +struct hp_extended_log { struct rtas_event_log_v6 v6hdr; struct rtas_event_log_v6_maina maina; struct rtas_event_log_v6_mainb mainb; @@ -342,31 +340,26 @@ static int rtas_event_log_to_irq(sPAPRMachineState *spapr, int log_type) return source->irq; } -static void rtas_event_log_queue(int log_type, void *data, bool exception) +static uint32_t spapr_event_log_entry_type(sPAPREventLogEntry *entry) { - sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); - sPAPREventLogEntry *entry = g_new(sPAPREventLogEntry, 1); + return entry->summary & RTAS_LOG_TYPE_MASK; +} - g_assert(data); - entry->log_type = log_type; - entry->exception = exception; - entry->data = data; +static void rtas_event_log_queue(sPAPRMachineState *spapr, + sPAPREventLogEntry *entry) +{ QTAILQ_INSERT_TAIL(&spapr->pending_events, entry, next); } -static sPAPREventLogEntry *rtas_event_log_dequeue(uint32_t event_mask, - bool exception) +static sPAPREventLogEntry *rtas_event_log_dequeue(sPAPRMachineState *spapr, + uint32_t event_mask) { - sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); sPAPREventLogEntry *entry = NULL; QTAILQ_FOREACH(entry, &spapr->pending_events, next) { const sPAPREventSource *source = - rtas_event_log_to_source(spapr, entry->log_type); - - if (entry->exception != exception) { - continue; - } + rtas_event_log_to_source(spapr, + spapr_event_log_entry_type(entry)); if (source->mask & event_mask) { break; @@ -380,18 +373,15 @@ static sPAPREventLogEntry *rtas_event_log_dequeue(uint32_t event_mask, return entry; } -static bool rtas_event_log_contains(uint32_t event_mask, bool exception) +static bool rtas_event_log_contains(uint32_t event_mask) { sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); sPAPREventLogEntry *entry = NULL; QTAILQ_FOREACH(entry, &spapr->pending_events, next) { const sPAPREventSource *source = - rtas_event_log_to_source(spapr, entry->log_type); - - if (entry->exception != exception) { - continue; - } + rtas_event_log_to_source(spapr, + spapr_event_log_entry_type(entry)); if (source->mask & event_mask) { return true; @@ -439,27 +429,28 @@ static void spapr_init_maina(struct rtas_event_log_v6_maina *maina, static void spapr_powerdown_req(Notifier *n, void *opaque) { sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); - struct rtas_error_log *hdr; + sPAPREventLogEntry *entry; struct rtas_event_log_v6 *v6hdr; struct rtas_event_log_v6_maina *maina; struct rtas_event_log_v6_mainb *mainb; struct rtas_event_log_v6_epow *epow; - struct epow_log_full *new_epow; + struct epow_extended_log *new_epow; + entry = g_new(sPAPREventLogEntry, 1); new_epow = g_malloc0(sizeof(*new_epow)); - hdr = &new_epow->hdr; + entry->extended_log = new_epow; + v6hdr = &new_epow->v6hdr; maina = &new_epow->maina; mainb = &new_epow->mainb; epow = &new_epow->epow; - hdr->summary = cpu_to_be32(RTAS_LOG_VERSION_6 - | RTAS_LOG_SEVERITY_EVENT - | RTAS_LOG_DISPOSITION_NOT_RECOVERED - | RTAS_LOG_OPTIONAL_PART_PRESENT - | RTAS_LOG_TYPE_EPOW); - hdr->extended_length = cpu_to_be32(sizeof(*new_epow) - - sizeof(new_epow->hdr)); + entry->summary = RTAS_LOG_VERSION_6 + | RTAS_LOG_SEVERITY_EVENT + | RTAS_LOG_DISPOSITION_NOT_RECOVERED + | RTAS_LOG_OPTIONAL_PART_PRESENT + | RTAS_LOG_TYPE_EPOW; + entry->extended_length = sizeof(*new_epow); spapr_init_v6hdr(v6hdr); spapr_init_maina(maina, 3 /* Main-A, Main-B and EPOW */); @@ -479,47 +470,41 @@ static void spapr_powerdown_req(Notifier *n, void *opaque) epow->event_modifier = RTAS_LOG_V6_EPOW_MODIFIER_NORMAL; epow->extended_modifier = RTAS_LOG_V6_EPOW_XMODIFIER_PARTITION_SPECIFIC; - rtas_event_log_queue(RTAS_LOG_TYPE_EPOW, new_epow, true); + rtas_event_log_queue(spapr, entry); qemu_irq_pulse(xics_get_qirq(XICS_FABRIC(spapr), rtas_event_log_to_irq(spapr, RTAS_LOG_TYPE_EPOW))); } -static void spapr_hotplug_set_signalled(uint32_t drc_index) -{ - sPAPRDRConnector *drc = spapr_dr_connector_by_index(drc_index); - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - drck->set_signalled(drc); -} - static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action, sPAPRDRConnectorType drc_type, union drc_identifier *drc_id) { sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); - struct hp_log_full *new_hp; - struct rtas_error_log *hdr; + sPAPREventLogEntry *entry; + struct hp_extended_log *new_hp; struct rtas_event_log_v6 *v6hdr; struct rtas_event_log_v6_maina *maina; struct rtas_event_log_v6_mainb *mainb; struct rtas_event_log_v6_hp *hp; - new_hp = g_malloc0(sizeof(struct hp_log_full)); - hdr = &new_hp->hdr; + entry = g_new(sPAPREventLogEntry, 1); + new_hp = g_malloc0(sizeof(struct hp_extended_log)); + entry->extended_log = new_hp; + v6hdr = &new_hp->v6hdr; maina = &new_hp->maina; mainb = &new_hp->mainb; hp = &new_hp->hp; - hdr->summary = cpu_to_be32(RTAS_LOG_VERSION_6 - | RTAS_LOG_SEVERITY_EVENT - | RTAS_LOG_DISPOSITION_NOT_RECOVERED - | RTAS_LOG_OPTIONAL_PART_PRESENT - | RTAS_LOG_INITIATOR_HOTPLUG - | RTAS_LOG_TYPE_HOTPLUG); - hdr->extended_length = cpu_to_be32(sizeof(*new_hp) - - sizeof(new_hp->hdr)); + entry->summary = RTAS_LOG_VERSION_6 + | RTAS_LOG_SEVERITY_EVENT + | RTAS_LOG_DISPOSITION_NOT_RECOVERED + | RTAS_LOG_OPTIONAL_PART_PRESENT + | RTAS_LOG_INITIATOR_HOTPLUG + | RTAS_LOG_TYPE_HOTPLUG; + entry->extended_length = sizeof(*new_hp); spapr_init_v6hdr(v6hdr); spapr_init_maina(maina, 3 /* Main-A, Main-B, HP */); @@ -539,9 +524,6 @@ static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action, switch (drc_type) { case SPAPR_DR_CONNECTOR_TYPE_PCI: hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_PCI; - if (hp->hotplug_action == RTAS_LOG_V6_HP_ACTION_ADD) { - spapr_hotplug_set_signalled(drc_id->index); - } break; case SPAPR_DR_CONNECTOR_TYPE_LMB: hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_MEMORY; @@ -572,7 +554,7 @@ static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action, cpu_to_be32(drc_id->count_indexed.index); } - rtas_event_log_queue(RTAS_LOG_TYPE_HOTPLUG, new_hp, true); + rtas_event_log_queue(spapr, entry); qemu_irq_pulse(xics_get_qirq(XICS_FABRIC(spapr), rtas_event_log_to_irq(spapr, @@ -581,22 +563,20 @@ static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action, void spapr_hotplug_req_add_by_index(sPAPRDRConnector *drc) { - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - sPAPRDRConnectorType drc_type = drck->get_type(drc); + sPAPRDRConnectorType drc_type = spapr_drc_type(drc); union drc_identifier drc_id; - drc_id.index = drck->get_index(drc); + drc_id.index = spapr_drc_index(drc); spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_INDEX, RTAS_LOG_V6_HP_ACTION_ADD, drc_type, &drc_id); } void spapr_hotplug_req_remove_by_index(sPAPRDRConnector *drc) { - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - sPAPRDRConnectorType drc_type = drck->get_type(drc); + sPAPRDRConnectorType drc_type = spapr_drc_type(drc); union drc_identifier drc_id; - drc_id.index = drck->get_index(drc); + drc_id.index = spapr_drc_index(drc); spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_INDEX, RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, &drc_id); } @@ -651,7 +631,7 @@ static void check_exception(PowerPCCPU *cpu, sPAPRMachineState *spapr, uint32_t mask, buf, len, event_len; uint64_t xinfo; sPAPREventLogEntry *event; - struct rtas_error_log *hdr; + struct rtas_error_log header; int i; if ((nargs < 6) || (nargs > 7) || nret != 1) { @@ -667,21 +647,24 @@ static void check_exception(PowerPCCPU *cpu, sPAPRMachineState *spapr, xinfo |= (uint64_t)rtas_ld(args, 6) << 32; } - event = rtas_event_log_dequeue(mask, true); + event = rtas_event_log_dequeue(spapr, mask); if (!event) { goto out_no_events; } - hdr = event->data; - event_len = be32_to_cpu(hdr->extended_length) + sizeof(*hdr); + event_len = event->extended_length + sizeof(header); if (event_len < len) { len = event_len; } - cpu_physical_memory_write(buf, event->data, len); + header.summary = cpu_to_be32(event->summary); + header.extended_length = cpu_to_be32(event->extended_length); + cpu_physical_memory_write(buf, &header, sizeof(header)); + cpu_physical_memory_write(buf + sizeof(header), event->extended_log, + event->extended_length); rtas_st(rets, 0, RTAS_OUT_SUCCESS); - g_free(event->data); + g_free(event->extended_log); g_free(event); /* according to PAPR+, the IRQ must be left asserted, or re-asserted, if @@ -690,7 +673,7 @@ static void check_exception(PowerPCCPU *cpu, sPAPRMachineState *spapr, * interrupts. */ for (i = 0; i < EVENT_CLASS_MAX; i++) { - if (rtas_event_log_contains(EVENT_CLASS_MASK(i), true)) { + if (rtas_event_log_contains(EVENT_CLASS_MASK(i))) { const sPAPREventSource *source = spapr_event_sources_get_source(spapr->event_sources, i); @@ -710,38 +693,10 @@ static void event_scan(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong args, uint32_t nret, target_ulong rets) { - uint32_t mask, buf, len, event_len; - sPAPREventLogEntry *event; - struct rtas_error_log *hdr; - if (nargs != 4 || nret != 1) { rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } - - mask = rtas_ld(args, 0); - buf = rtas_ld(args, 2); - len = rtas_ld(args, 3); - - event = rtas_event_log_dequeue(mask, false); - if (!event) { - goto out_no_events; - } - - hdr = event->data; - event_len = be32_to_cpu(hdr->extended_length) + sizeof(*hdr); - - if (event_len < len) { - len = event_len; - } - - cpu_physical_memory_write(buf, event->data, len); - rtas_st(rets, 0, RTAS_OUT_SUCCESS); - g_free(event->data); - g_free(event); - return; - -out_no_events: rtas_st(rets, 0, RTAS_OUT_NO_ERRORS_FOUND); } diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 0d608d6e28..72ea5a8247 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -3,6 +3,7 @@ #include "sysemu/hw_accel.h" #include "sysemu/sysemu.h" #include "qemu/log.h" +#include "qemu/error-report.h" #include "cpu.h" #include "exec/exec-all.h" #include "helper_regs.h" @@ -354,6 +355,401 @@ static target_ulong h_read(PowerPCCPU *cpu, sPAPRMachineState *spapr, return H_SUCCESS; } +struct sPAPRPendingHPT { + /* These fields are read-only after initialization */ + int shift; + QemuThread thread; + + /* These fields are protected by the BQL */ + bool complete; + + /* These fields are private to the preparation thread if + * !complete, otherwise protected by the BQL */ + int ret; + void *hpt; +}; + +static void free_pending_hpt(sPAPRPendingHPT *pending) +{ + if (pending->hpt) { + qemu_vfree(pending->hpt); + } + + g_free(pending); +} + +static void *hpt_prepare_thread(void *opaque) +{ + sPAPRPendingHPT *pending = opaque; + size_t size = 1ULL << pending->shift; + + pending->hpt = qemu_memalign(size, size); + if (pending->hpt) { + memset(pending->hpt, 0, size); + pending->ret = H_SUCCESS; + } else { + pending->ret = H_NO_MEM; + } + + qemu_mutex_lock_iothread(); + + if (SPAPR_MACHINE(qdev_get_machine())->pending_hpt == pending) { + /* Ready to go */ + pending->complete = true; + } else { + /* We've been cancelled, clean ourselves up */ + free_pending_hpt(pending); + } + + qemu_mutex_unlock_iothread(); + return NULL; +} + +/* Must be called with BQL held */ +static void cancel_hpt_prepare(sPAPRMachineState *spapr) +{ + sPAPRPendingHPT *pending = spapr->pending_hpt; + + /* Let the thread know it's cancelled */ + spapr->pending_hpt = NULL; + + if (!pending) { + /* Nothing to do */ + return; + } + + if (!pending->complete) { + /* thread will clean itself up */ + return; + } + + free_pending_hpt(pending); +} + +/* Convert a return code from the KVM ioctl()s implementing resize HPT + * into a PAPR hypercall return code */ +static target_ulong resize_hpt_convert_rc(int ret) +{ + if (ret >= 100000) { + return H_LONG_BUSY_ORDER_100_SEC; + } else if (ret >= 10000) { + return H_LONG_BUSY_ORDER_10_SEC; + } else if (ret >= 1000) { + return H_LONG_BUSY_ORDER_1_SEC; + } else if (ret >= 100) { + return H_LONG_BUSY_ORDER_100_MSEC; + } else if (ret >= 10) { + return H_LONG_BUSY_ORDER_10_MSEC; + } else if (ret > 0) { + return H_LONG_BUSY_ORDER_1_MSEC; + } + + switch (ret) { + case 0: + return H_SUCCESS; + case -EPERM: + return H_AUTHORITY; + case -EINVAL: + return H_PARAMETER; + case -ENXIO: + return H_CLOSED; + case -ENOSPC: + return H_PTEG_FULL; + case -EBUSY: + return H_BUSY; + case -ENOMEM: + return H_NO_MEM; + default: + return H_HARDWARE; + } +} + +static target_ulong h_resize_hpt_prepare(PowerPCCPU *cpu, + sPAPRMachineState *spapr, + target_ulong opcode, + target_ulong *args) +{ + target_ulong flags = args[0]; + int shift = args[1]; + sPAPRPendingHPT *pending = spapr->pending_hpt; + uint64_t current_ram_size = MACHINE(spapr)->ram_size; + int rc; + + if (spapr->resize_hpt == SPAPR_RESIZE_HPT_DISABLED) { + return H_AUTHORITY; + } + + if (!spapr->htab_shift) { + /* Radix guest, no HPT */ + return H_NOT_AVAILABLE; + } + + trace_spapr_h_resize_hpt_prepare(flags, shift); + + if (flags != 0) { + return H_PARAMETER; + } + + if (shift && ((shift < 18) || (shift > 46))) { + return H_PARAMETER; + } + + current_ram_size = pc_existing_dimms_capacity(&error_fatal); + + /* We only allow the guest to allocate an HPT one order above what + * we'd normally give them (to stop a small guest claiming a huge + * chunk of resources in the HPT */ + if (shift > (spapr_hpt_shift_for_ramsize(current_ram_size) + 1)) { + return H_RESOURCE; + } + + rc = kvmppc_resize_hpt_prepare(cpu, flags, shift); + if (rc != -ENOSYS) { + return resize_hpt_convert_rc(rc); + } + + if (pending) { + /* something already in progress */ + if (pending->shift == shift) { + /* and it's suitable */ + if (pending->complete) { + return pending->ret; + } else { + return H_LONG_BUSY_ORDER_100_MSEC; + } + } + + /* not suitable, cancel and replace */ + cancel_hpt_prepare(spapr); + } + + if (!shift) { + /* nothing to do */ + return H_SUCCESS; + } + + /* start new prepare */ + + pending = g_new0(sPAPRPendingHPT, 1); + pending->shift = shift; + pending->ret = H_HARDWARE; + + qemu_thread_create(&pending->thread, "sPAPR HPT prepare", + hpt_prepare_thread, pending, QEMU_THREAD_DETACHED); + + spapr->pending_hpt = pending; + + /* In theory we could estimate the time more accurately based on + * the new size, but there's not much point */ + return H_LONG_BUSY_ORDER_100_MSEC; +} + +static uint64_t new_hpte_load0(void *htab, uint64_t pteg, int slot) +{ + uint8_t *addr = htab; + + addr += pteg * HASH_PTEG_SIZE_64; + addr += slot * HASH_PTE_SIZE_64; + return ldq_p(addr); +} + +static void new_hpte_store(void *htab, uint64_t pteg, int slot, + uint64_t pte0, uint64_t pte1) +{ + uint8_t *addr = htab; + + addr += pteg * HASH_PTEG_SIZE_64; + addr += slot * HASH_PTE_SIZE_64; + + stq_p(addr, pte0); + stq_p(addr + HASH_PTE_SIZE_64 / 2, pte1); +} + +static int rehash_hpte(PowerPCCPU *cpu, + const ppc_hash_pte64_t *hptes, + void *old_hpt, uint64_t oldsize, + void *new_hpt, uint64_t newsize, + uint64_t pteg, int slot) +{ + uint64_t old_hash_mask = (oldsize >> 7) - 1; + uint64_t new_hash_mask = (newsize >> 7) - 1; + target_ulong pte0 = ppc_hash64_hpte0(cpu, hptes, slot); + target_ulong pte1; + uint64_t avpn; + unsigned base_pg_shift; + uint64_t hash, new_pteg, replace_pte0; + + if (!(pte0 & HPTE64_V_VALID) || !(pte0 & HPTE64_V_BOLTED)) { + return H_SUCCESS; + } + + pte1 = ppc_hash64_hpte1(cpu, hptes, slot); + + base_pg_shift = ppc_hash64_hpte_page_shift_noslb(cpu, pte0, pte1); + assert(base_pg_shift); /* H_ENTER shouldn't allow a bad encoding */ + avpn = HPTE64_V_AVPN_VAL(pte0) & ~(((1ULL << base_pg_shift) - 1) >> 23); + + if (pte0 & HPTE64_V_SECONDARY) { + pteg = ~pteg; + } + + if ((pte0 & HPTE64_V_SSIZE) == HPTE64_V_SSIZE_256M) { + uint64_t offset, vsid; + + /* We only have 28 - 23 bits of offset in avpn */ + offset = (avpn & 0x1f) << 23; + vsid = avpn >> 5; + /* We can find more bits from the pteg value */ + if (base_pg_shift < 23) { + offset |= ((vsid ^ pteg) & old_hash_mask) << base_pg_shift; + } + + hash = vsid ^ (offset >> base_pg_shift); + } else if ((pte0 & HPTE64_V_SSIZE) == HPTE64_V_SSIZE_1T) { + uint64_t offset, vsid; + + /* We only have 40 - 23 bits of seg_off in avpn */ + offset = (avpn & 0x1ffff) << 23; + vsid = avpn >> 17; + if (base_pg_shift < 23) { + offset |= ((vsid ^ (vsid << 25) ^ pteg) & old_hash_mask) + << base_pg_shift; + } + + hash = vsid ^ (vsid << 25) ^ (offset >> base_pg_shift); + } else { + error_report("rehash_pte: Bad segment size in HPTE"); + return H_HARDWARE; + } + + new_pteg = hash & new_hash_mask; + if (pte0 & HPTE64_V_SECONDARY) { + assert(~pteg == (hash & old_hash_mask)); + new_pteg = ~new_pteg; + } else { + assert(pteg == (hash & old_hash_mask)); + } + assert((oldsize != newsize) || (pteg == new_pteg)); + replace_pte0 = new_hpte_load0(new_hpt, new_pteg, slot); + /* + * Strictly speaking, we don't need all these tests, since we only + * ever rehash bolted HPTEs. We might in future handle non-bolted + * HPTEs, though so make the logic correct for those cases as + * well. + */ + if (replace_pte0 & HPTE64_V_VALID) { + assert(newsize < oldsize); + if (replace_pte0 & HPTE64_V_BOLTED) { + if (pte0 & HPTE64_V_BOLTED) { + /* Bolted collision, nothing we can do */ + return H_PTEG_FULL; + } else { + /* Discard this hpte */ + return H_SUCCESS; + } + } + } + + new_hpte_store(new_hpt, new_pteg, slot, pte0, pte1); + return H_SUCCESS; +} + +static int rehash_hpt(PowerPCCPU *cpu, + void *old_hpt, uint64_t oldsize, + void *new_hpt, uint64_t newsize) +{ + uint64_t n_ptegs = oldsize >> 7; + uint64_t pteg; + int slot; + int rc; + + for (pteg = 0; pteg < n_ptegs; pteg++) { + hwaddr ptex = pteg * HPTES_PER_GROUP; + const ppc_hash_pte64_t *hptes + = ppc_hash64_map_hptes(cpu, ptex, HPTES_PER_GROUP); + + if (!hptes) { + return H_HARDWARE; + } + + for (slot = 0; slot < HPTES_PER_GROUP; slot++) { + rc = rehash_hpte(cpu, hptes, old_hpt, oldsize, new_hpt, newsize, + pteg, slot); + if (rc != H_SUCCESS) { + ppc_hash64_unmap_hptes(cpu, hptes, ptex, HPTES_PER_GROUP); + return rc; + } + } + ppc_hash64_unmap_hptes(cpu, hptes, ptex, HPTES_PER_GROUP); + } + + return H_SUCCESS; +} + +static target_ulong h_resize_hpt_commit(PowerPCCPU *cpu, + sPAPRMachineState *spapr, + target_ulong opcode, + target_ulong *args) +{ + target_ulong flags = args[0]; + target_ulong shift = args[1]; + sPAPRPendingHPT *pending = spapr->pending_hpt; + int rc; + size_t newsize; + + if (spapr->resize_hpt == SPAPR_RESIZE_HPT_DISABLED) { + return H_AUTHORITY; + } + + trace_spapr_h_resize_hpt_commit(flags, shift); + + rc = kvmppc_resize_hpt_commit(cpu, flags, shift); + if (rc != -ENOSYS) { + return resize_hpt_convert_rc(rc); + } + + if (flags != 0) { + return H_PARAMETER; + } + + if (!pending || (pending->shift != shift)) { + /* no matching prepare */ + return H_CLOSED; + } + + if (!pending->complete) { + /* prepare has not completed */ + return H_BUSY; + } + + /* Shouldn't have got past PREPARE without an HPT */ + g_assert(spapr->htab_shift); + + newsize = 1ULL << pending->shift; + rc = rehash_hpt(cpu, spapr->htab, HTAB_SIZE(spapr), + pending->hpt, newsize); + if (rc == H_SUCCESS) { + qemu_vfree(spapr->htab); + spapr->htab = pending->hpt; + spapr->htab_shift = pending->shift; + + if (kvm_enabled()) { + /* For KVM PR, update the HPT pointer */ + target_ulong sdr1 = (target_ulong)(uintptr_t)spapr->htab + | (spapr->htab_shift - 18); + kvmppc_update_sdr1(sdr1); + } + + pending->hpt = NULL; /* so it's not free()d */ + } + + /* Clean up */ + spapr->pending_hpt = NULL; + free_pending_hpt(pending); + + return rc; +} + static target_ulong h_set_sprg0(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { @@ -913,10 +1309,7 @@ static void spapr_check_setup_free_hpt(sPAPRMachineState *spapr, /* We assume RADIX, so this catches all the "Do Nothing" cases */ } else if (!(patbe_old & PATBE1_GR)) { /* HASH->RADIX : Free HPT */ - g_free(spapr->htab); - spapr->htab = NULL; - spapr->htab_shift = 0; - close_htab_fd(spapr); + spapr_free_hpt(spapr); } else if (!(patbe_new & PATBE1_GR)) { /* RADIX->HASH || NOTHING->HASH : Allocate HPT */ spapr_setup_hpt_and_vrma(spapr); @@ -995,9 +1388,10 @@ static target_ulong h_register_process_table(PowerPCCPU *cpu, /* Update the UPRT and GTSE bits in the LPCR for all cpus */ CPU_FOREACH(cs) { - set_spr(cs, SPR_LPCR, LPCR_UPRT | LPCR_GTSE, + set_spr(cs, SPR_LPCR, ((flags & (FLAG_RADIX | FLAG_HASH_PROC_TBL)) ? LPCR_UPRT : 0) | - ((flags & FLAG_GTSE) ? LPCR_GTSE : 0)); + ((flags & FLAG_GTSE) ? LPCR_GTSE : 0), + LPCR_UPRT | LPCR_GTSE); } if (kvm_enabled()) { @@ -1047,19 +1441,13 @@ static target_ulong h_signal_sys_reset(PowerPCCPU *cpu, } } -static target_ulong h_client_architecture_support(PowerPCCPU *cpu, - sPAPRMachineState *spapr, - target_ulong opcode, - target_ulong *args) +static uint32_t cas_check_pvr(sPAPRMachineState *spapr, PowerPCCPU *cpu, + target_ulong *addr, Error **errp) { - target_ulong list = ppc64_phys_to_real(args[0]); - target_ulong ov_table; bool explicit_match = false; /* Matched the CPU's real PVR */ - uint32_t max_compat = cpu->max_compat; + uint32_t max_compat = spapr->max_compat_pvr; uint32_t best_compat = 0; int i; - sPAPROptionVector *ov1_guest, *ov5_guest, *ov5_cas_old, *ov5_updates; - bool guest_radix; /* * We scan the supplied table of PVRs looking for two things @@ -1069,9 +1457,9 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, for (i = 0; i < 512; ++i) { uint32_t pvr, pvr_mask; - pvr_mask = ldl_be_phys(&address_space_memory, list); - pvr = ldl_be_phys(&address_space_memory, list + 4); - list += 8; + pvr_mask = ldl_be_phys(&address_space_memory, *addr); + pvr = ldl_be_phys(&address_space_memory, *addr + 4); + *addr += 8; if (~pvr_mask & pvr) { break; /* Terminator record */ @@ -1090,17 +1478,38 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, /* We couldn't find a suitable compatibility mode, and either * the guest doesn't support "raw" mode for this CPU, or raw * mode is disabled because a maximum compat mode is set */ - return H_HARDWARE; + error_setg(errp, "Couldn't negotiate a suitable PVR during CAS"); + return 0; } /* Parsing finished */ trace_spapr_cas_pvr(cpu->compat_pvr, explicit_match, best_compat); - /* Update CPUs */ - if (cpu->compat_pvr != best_compat) { - Error *local_err = NULL; + return best_compat; +} + +static target_ulong h_client_architecture_support(PowerPCCPU *cpu, + sPAPRMachineState *spapr, + target_ulong opcode, + target_ulong *args) +{ + /* Working address in data buffer */ + target_ulong addr = ppc64_phys_to_real(args[0]); + target_ulong ov_table; + uint32_t cas_pvr; + sPAPROptionVector *ov1_guest, *ov5_guest, *ov5_cas_old, *ov5_updates; + bool guest_radix; + Error *local_err = NULL; + + cas_pvr = cas_check_pvr(spapr, cpu, &addr, &local_err); + if (local_err) { + error_report_err(local_err); + return H_HARDWARE; + } - ppc_set_compat_all(best_compat, &local_err); + /* Update CPUs */ + if (cpu->compat_pvr != cas_pvr) { + ppc_set_compat_all(cas_pvr, &local_err); if (local_err) { error_report_err(local_err); return H_HARDWARE; @@ -1108,7 +1517,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, } /* For the future use: here @ov_table points to the first option vector */ - ov_table = list; + ov_table = addr; ov1_guest = spapr_ovec_parse_vector(ov_table, 1); ov5_guest = spapr_ovec_parse_vector(ov_table, 5); @@ -1120,6 +1529,45 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, guest_radix = spapr_ovec_test(ov5_guest, OV5_MMU_RADIX_300); spapr_ovec_clear(ov5_guest, OV5_MMU_RADIX_300); + /* + * HPT resizing is a bit of a special case, because when enabled + * we assume an HPT guest will support it until it says it + * doesn't, instead of assuming it won't support it until it says + * it does. Strictly speaking that approach could break for + * guests which don't make a CAS call, but those are so old we + * don't care about them. Without that assumption we'd have to + * make at least a temporary allocation of an HPT sized for max + * memory, which could be impossibly difficult under KVM HV if + * maxram is large. + */ + if (!guest_radix && !spapr_ovec_test(ov5_guest, OV5_HPT_RESIZE)) { + int maxshift = spapr_hpt_shift_for_ramsize(MACHINE(spapr)->maxram_size); + + if (spapr->resize_hpt == SPAPR_RESIZE_HPT_REQUIRED) { + error_report( + "h_client_architecture_support: Guest doesn't support HPT resizing, but resize-hpt=required"); + exit(1); + } + + if (spapr->htab_shift < maxshift) { + CPUState *cs; + + /* Guest doesn't know about HPT resizing, so we + * pre-emptively resize for the maximum permitted RAM. At + * the point this is called, nothing should have been + * entered into the existing HPT */ + spapr_reallocate_hpt(spapr, maxshift, &error_fatal); + CPU_FOREACH(cs) { + if (kvm_enabled()) { + /* For KVM PR, update the HPT pointer */ + target_ulong sdr1 = (target_ulong)(uintptr_t)spapr->htab + | (spapr->htab_shift - 18); + kvmppc_update_sdr1(sdr1); + } + } + } + } + /* NOTE: there are actually a number of ov5 bits where input from the * guest is always zero, and the platform/QEMU enables them independently * of guest input. To model these properly we'd want some sort of mask, @@ -1162,7 +1610,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, spapr_ovec_cleanup(ov5_updates); if (spapr->cas_reboot) { - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); } else { /* If ppc_spapr_reset() did not set up a HPT but one is necessary * (because the guest isn't going to use radix) then set it up here. */ @@ -1233,6 +1681,10 @@ static void hypercall_register_types(void) /* hcall-bulk */ spapr_register_hypercall(H_BULK_REMOVE, h_bulk_remove); + /* hcall-hpt-resize */ + spapr_register_hypercall(H_RESIZE_HPT_PREPARE, h_resize_hpt_prepare); + spapr_register_hypercall(H_RESIZE_HPT_COMMIT, h_resize_hpt_commit); + /* hcall-splpar */ spapr_register_hypercall(H_REGISTER_VPA, h_register_vpa); spapr_register_hypercall(H_CEDE, h_cede); diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index 29c80bb3c8..e614621a83 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -110,8 +110,9 @@ static void spapr_tce_free_table(uint64_t *table, int fd, uint32_t nb_table) } /* Called from RCU critical section */ -static IOMMUTLBEntry spapr_tce_translate_iommu(MemoryRegion *iommu, hwaddr addr, - bool is_write) +static IOMMUTLBEntry spapr_tce_translate_iommu(IOMMUMemoryRegion *iommu, + hwaddr addr, + IOMMUAccessFlags flag) { sPAPRTCETable *tcet = container_of(iommu, sPAPRTCETable, iommu); uint64_t tce; @@ -150,14 +151,14 @@ static void spapr_tce_table_pre_save(void *opaque) tcet->bus_offset, tcet->page_shift); } -static uint64_t spapr_tce_get_min_page_size(MemoryRegion *iommu) +static uint64_t spapr_tce_get_min_page_size(IOMMUMemoryRegion *iommu) { sPAPRTCETable *tcet = container_of(iommu, sPAPRTCETable, iommu); return 1ULL << tcet->page_shift; } -static void spapr_tce_notify_flag_changed(MemoryRegion *iommu, +static void spapr_tce_notify_flag_changed(IOMMUMemoryRegion *iommu, IOMMUNotifierFlag old, IOMMUNotifierFlag new) { @@ -231,7 +232,7 @@ static const VMStateDescription vmstate_spapr_tce_table = { .post_load = spapr_tce_table_post_load, .fields = (VMStateField []) { /* Sanity check */ - VMSTATE_UINT32_EQUAL(liobn, sPAPRTCETable), + VMSTATE_UINT32_EQUAL(liobn, sPAPRTCETable, NULL), /* IOMMU state */ VMSTATE_UINT32(mig_nb_table, sPAPRTCETable), @@ -247,12 +248,6 @@ static const VMStateDescription vmstate_spapr_tce_table = { } }; -static MemoryRegionIOMMUOps spapr_iommu_ops = { - .translate = spapr_tce_translate_iommu, - .get_min_page_size = spapr_tce_get_min_page_size, - .notify_flag_changed = spapr_tce_notify_flag_changed, -}; - static int spapr_tce_table_realize(DeviceState *dev) { sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev); @@ -265,7 +260,9 @@ static int spapr_tce_table_realize(DeviceState *dev) memory_region_init(&tcet->root, tcetobj, tmp, UINT64_MAX); snprintf(tmp, sizeof(tmp), "tce-iommu-%x", tcet->liobn); - memory_region_init_iommu(&tcet->iommu, tcetobj, &spapr_iommu_ops, tmp, 0); + memory_region_init_iommu(&tcet->iommu, sizeof(tcet->iommu), + TYPE_SPAPR_IOMMU_MEMORY_REGION, + tcetobj, tmp, 0); QLIST_INSERT_HEAD(&spapr_tce_tables, tcet, list); @@ -334,7 +331,7 @@ void spapr_tce_table_enable(sPAPRTCETable *tcet, uint32_t nb_table) { if (tcet->nb_table) { - error_report("Warning: trying to enable already enabled TCE table"); + warn_report("trying to enable already enabled TCE table"); return; } @@ -348,9 +345,10 @@ void spapr_tce_table_enable(sPAPRTCETable *tcet, &tcet->fd, tcet->need_vfio); - memory_region_set_size(&tcet->iommu, + memory_region_set_size(MEMORY_REGION(&tcet->iommu), (uint64_t)tcet->nb_table << tcet->page_shift); - memory_region_add_subregion(&tcet->root, tcet->bus_offset, &tcet->iommu); + memory_region_add_subregion(&tcet->root, tcet->bus_offset, + MEMORY_REGION(&tcet->iommu)); } void spapr_tce_table_disable(sPAPRTCETable *tcet) @@ -359,8 +357,8 @@ void spapr_tce_table_disable(sPAPRTCETable *tcet) return; } - memory_region_del_subregion(&tcet->root, &tcet->iommu); - memory_region_set_size(&tcet->iommu, 0); + memory_region_del_subregion(&tcet->root, MEMORY_REGION(&tcet->iommu)); + memory_region_set_size(MEMORY_REGION(&tcet->iommu), 0); spapr_tce_free_table(tcet->table, tcet->fd, tcet->nb_table); tcet->fd = -1; @@ -637,9 +635,25 @@ static TypeInfo spapr_tce_table_info = { .class_init = spapr_tce_table_class_init, }; +static void spapr_iommu_memory_region_class_init(ObjectClass *klass, void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = spapr_tce_translate_iommu; + imrc->get_min_page_size = spapr_tce_get_min_page_size; + imrc->notify_flag_changed = spapr_tce_notify_flag_changed; +} + +static const TypeInfo spapr_iommu_memory_region_info = { + .parent = TYPE_IOMMU_MEMORY_REGION, + .name = TYPE_SPAPR_IOMMU_MEMORY_REGION, + .class_init = spapr_iommu_memory_region_class_init, +}; + static void register_types(void) { type_register_static(&spapr_tce_table_info); + type_register_static(&spapr_iommu_memory_region_info); } type_init(register_types); diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index a7cff32bbf..6ecdf29d28 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -1344,32 +1344,8 @@ static int spapr_create_pci_child_dt(sPAPRPHBState *phb, PCIDevice *dev, return offset; } -static void spapr_phb_add_pci_device(sPAPRDRConnector *drc, - sPAPRPHBState *phb, - PCIDevice *pdev, - Error **errp) -{ - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - DeviceState *dev = DEVICE(pdev); - void *fdt = NULL; - int fdt_start_offset = 0, fdt_size; - - fdt = create_device_tree(&fdt_size); - fdt_start_offset = spapr_create_pci_child_dt(phb, pdev, fdt, 0); - if (!fdt_start_offset) { - error_setg(errp, "Failed to create pci child device tree node"); - goto out; - } - - drck->attach(drc, DEVICE(pdev), - fdt, fdt_start_offset, !dev->hotplugged, errp); -out: - if (*errp) { - g_free(fdt); - } -} - -static void spapr_phb_remove_pci_device_cb(DeviceState *dev, void *opaque) +/* Callback to be called during DRC release. */ +void spapr_phb_remove_pci_device_cb(DeviceState *dev) { /* some version guests do not wait for completion of a device * cleanup (generally done asynchronously by the kernel) before @@ -1385,24 +1361,12 @@ static void spapr_phb_remove_pci_device_cb(DeviceState *dev, void *opaque) object_unparent(OBJECT(dev)); } -static void spapr_phb_remove_pci_device(sPAPRDRConnector *drc, - sPAPRPHBState *phb, - PCIDevice *pdev, - Error **errp) -{ - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - - drck->detach(drc, DEVICE(pdev), spapr_phb_remove_pci_device_cb, phb, errp); -} - static sPAPRDRConnector *spapr_phb_get_pci_func_drc(sPAPRPHBState *phb, uint32_t busnr, int32_t devfn) { - return spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_PCI, - (phb->index << 16) | - (busnr << 8) | - devfn); + return spapr_drc_by_id(TYPE_SPAPR_DRC_PCI, + (phb->index << 16) | (busnr << 8) | devfn); } static sPAPRDRConnector *spapr_phb_get_pci_drc(sPAPRPHBState *phb, @@ -1416,18 +1380,16 @@ static uint32_t spapr_phb_get_pci_drc_index(sPAPRPHBState *phb, PCIDevice *pdev) { sPAPRDRConnector *drc = spapr_phb_get_pci_drc(phb, pdev); - sPAPRDRConnectorClass *drck; if (!drc) { return 0; } - drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - return drck->get_index(drc); + return spapr_drc_index(drc); } -static void spapr_phb_hot_plug_child(HotplugHandler *plug_handler, - DeviceState *plugged_dev, Error **errp) +static void spapr_pci_plug(HotplugHandler *plug_handler, + DeviceState *plugged_dev, Error **errp) { sPAPRPHBState *phb = SPAPR_PCI_HOST_BRIDGE(DEVICE(plug_handler)); PCIDevice *pdev = PCI_DEVICE(plugged_dev); @@ -1435,6 +1397,8 @@ static void spapr_phb_hot_plug_child(HotplugHandler *plug_handler, Error *local_err = NULL; PCIBus *bus = PCI_BUS(qdev_get_parent_bus(DEVICE(pdev))); uint32_t slotnr = PCI_SLOT(pdev->devfn); + void *fdt = NULL; + int fdt_start_offset, fdt_size; /* if DR is disabled we don't need to do anything in the case of * hotplug or coldplug callbacks @@ -1444,10 +1408,10 @@ static void spapr_phb_hot_plug_child(HotplugHandler *plug_handler, * we need to let them know it's not enabled */ if (plugged_dev->hotplugged) { - error_setg(errp, QERR_BUS_NO_HOTPLUG, + error_setg(&local_err, QERR_BUS_NO_HOTPLUG, object_get_typename(OBJECT(phb))); } - return; + goto out; } g_assert(drc); @@ -1458,22 +1422,30 @@ static void spapr_phb_hot_plug_child(HotplugHandler *plug_handler, */ if (plugged_dev->hotplugged && bus->devices[PCI_DEVFN(slotnr, 0)] && PCI_FUNC(pdev->devfn) != 0) { - error_setg(errp, "PCI: slot %d function 0 already ocuppied by %s," + error_setg(&local_err, "PCI: slot %d function 0 already ocuppied by %s," " additional functions can no longer be exposed to guest.", slotnr, bus->devices[PCI_DEVFN(slotnr, 0)]->name); - return; + goto out; + } + + fdt = create_device_tree(&fdt_size); + fdt_start_offset = spapr_create_pci_child_dt(phb, pdev, fdt, 0); + if (!fdt_start_offset) { + error_setg(&local_err, "Failed to create pci child device tree node"); + goto out; } - spapr_phb_add_pci_device(drc, phb, pdev, &local_err); + spapr_drc_attach(drc, DEVICE(pdev), fdt, fdt_start_offset, &local_err); if (local_err) { - error_propagate(errp, local_err); - return; + goto out; } /* If this is function 0, signal hotplug for all the device functions. * Otherwise defer sending the hotplug event. */ - if (plugged_dev->hotplugged && PCI_FUNC(pdev->devfn) == 0) { + if (!spapr_drc_hotplugged(plugged_dev)) { + spapr_drc_reset(drc); + } else if (PCI_FUNC(pdev->devfn) == 0) { int i; for (i = 0; i < 8; i++) { @@ -1484,23 +1456,27 @@ static void spapr_phb_hot_plug_child(HotplugHandler *plug_handler, func_drc = spapr_phb_get_pci_func_drc(phb, pci_bus_num(bus), PCI_DEVFN(slotnr, i)); func_drck = SPAPR_DR_CONNECTOR_GET_CLASS(func_drc); - func_drck->entity_sense(func_drc, &state); + state = func_drck->dr_entity_sense(func_drc); if (state == SPAPR_DR_ENTITY_SENSE_PRESENT) { spapr_hotplug_req_add_by_index(func_drc); } } } + +out: + if (local_err) { + error_propagate(errp, local_err); + g_free(fdt); + } } -static void spapr_phb_hot_unplug_child(HotplugHandler *plug_handler, - DeviceState *plugged_dev, Error **errp) +static void spapr_pci_unplug_request(HotplugHandler *plug_handler, + DeviceState *plugged_dev, Error **errp) { sPAPRPHBState *phb = SPAPR_PCI_HOST_BRIDGE(DEVICE(plug_handler)); PCIDevice *pdev = PCI_DEVICE(plugged_dev); - sPAPRDRConnectorClass *drck; sPAPRDRConnector *drc = spapr_phb_get_pci_drc(phb, pdev); - Error *local_err = NULL; if (!phb->dr_enabled) { error_setg(errp, QERR_BUS_NO_HOTPLUG, @@ -1509,9 +1485,9 @@ static void spapr_phb_hot_unplug_child(HotplugHandler *plug_handler, } g_assert(drc); + g_assert(drc->dev == plugged_dev); - drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - if (!drck->release_pending(drc)) { + if (!spapr_drc_unplug_requested(drc)) { PCIBus *bus = PCI_BUS(qdev_get_parent_bus(DEVICE(pdev))); uint32_t slotnr = PCI_SLOT(pdev->devfn); sPAPRDRConnector *func_drc; @@ -1525,9 +1501,9 @@ static void spapr_phb_hot_unplug_child(HotplugHandler *plug_handler, func_drc = spapr_phb_get_pci_func_drc(phb, pci_bus_num(bus), PCI_DEVFN(slotnr, i)); func_drck = SPAPR_DR_CONNECTOR_GET_CLASS(func_drc); - func_drck->entity_sense(func_drc, &state); + state = func_drck->dr_entity_sense(func_drc); if (state == SPAPR_DR_ENTITY_SENSE_PRESENT - && !func_drck->release_pending(func_drc)) { + && !spapr_drc_unplug_requested(func_drc)) { error_setg(errp, "PCI: slot %d, function %d still present. " "Must unplug all non-0 functions first.", @@ -1537,11 +1513,7 @@ static void spapr_phb_hot_unplug_child(HotplugHandler *plug_handler, } } - spapr_phb_remove_pci_device(drc, phb, pdev, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } + spapr_drc_detach(drc); /* if this isn't func 0, defer unplug event. otherwise signal removal * for all present functions @@ -1551,7 +1523,7 @@ static void spapr_phb_hot_unplug_child(HotplugHandler *plug_handler, func_drc = spapr_phb_get_pci_func_drc(phb, pci_bus_num(bus), PCI_DEVFN(slotnr, i)); func_drck = SPAPR_DR_CONNECTOR_GET_CLASS(func_drc); - func_drck->entity_sense(func_drc, &state); + state = func_drck->dr_entity_sense(func_drc); if (state == SPAPR_DR_ENTITY_SENSE_PRESENT) { spapr_hotplug_req_remove_by_index(func_drc); } @@ -1762,14 +1734,14 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) /* allocate connectors for child PCI devices */ if (sphb->dr_enabled) { for (i = 0; i < PCI_SLOT_MAX * 8; i++) { - spapr_dr_connector_new(OBJECT(phb), - SPAPR_DR_CONNECTOR_TYPE_PCI, + spapr_dr_connector_new(OBJECT(phb), TYPE_SPAPR_DRC_PCI, (sphb->index << 16) | i); } } /* DMA setup */ - if ((sphb->page_size_mask & qemu_getrampagesize()) == 0) { + if (((sphb->page_size_mask & qemu_getrampagesize()) == 0) + && kvm_enabled()) { error_report("System page size 0x%lx is not enabled in page_size_mask " "(0x%"PRIx64"). Performance may be slow", qemu_getrampagesize(), sphb->page_size_mask); @@ -1872,7 +1844,7 @@ static const VMStateDescription vmstate_spapr_pci_lsi = { .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { - VMSTATE_UINT32_EQUAL(irq, struct spapr_pci_lsi), + VMSTATE_UINT32_EQUAL(irq, struct spapr_pci_lsi, NULL), VMSTATE_END_OF_LIST() }, @@ -1897,20 +1869,6 @@ static void spapr_pci_pre_save(void *opaque) gpointer key, value; int i; - g_free(sphb->msi_devs); - sphb->msi_devs = NULL; - sphb->msi_devs_num = g_hash_table_size(sphb->msi); - if (!sphb->msi_devs_num) { - return; - } - sphb->msi_devs = g_malloc(sphb->msi_devs_num * sizeof(spapr_pci_msi_mig)); - - g_hash_table_iter_init(&iter, sphb->msi); - for (i = 0; g_hash_table_iter_next(&iter, &key, &value); ++i) { - sphb->msi_devs[i].key = *(uint32_t *) key; - sphb->msi_devs[i].value = *(spapr_pci_msi *) value; - } - if (sphb->pre_2_8_migration) { sphb->mig_liobn = sphb->dma_liobn[0]; sphb->mig_mem_win_addr = sphb->mem_win_addr; @@ -1924,6 +1882,20 @@ static void spapr_pci_pre_save(void *opaque) sphb->mig_mem_win_size += sphb->mem64_win_size; } } + + g_free(sphb->msi_devs); + sphb->msi_devs = NULL; + sphb->msi_devs_num = g_hash_table_size(sphb->msi); + if (!sphb->msi_devs_num) { + return; + } + sphb->msi_devs = g_malloc(sphb->msi_devs_num * sizeof(spapr_pci_msi_mig)); + + g_hash_table_iter_init(&iter, sphb->msi); + for (i = 0; g_hash_table_iter_next(&iter, &key, &value); ++i) { + sphb->msi_devs[i].key = *(uint32_t *) key; + sphb->msi_devs[i].value = *(spapr_pci_msi *) value; + } } static int spapr_pci_post_load(void *opaque, int version_id) @@ -1960,7 +1932,7 @@ static const VMStateDescription vmstate_spapr_pci = { .pre_save = spapr_pci_pre_save, .post_load = spapr_pci_post_load, .fields = (VMStateField[]) { - VMSTATE_UINT64_EQUAL(buid, sPAPRPHBState), + VMSTATE_UINT64_EQUAL(buid, sPAPRPHBState, NULL), VMSTATE_UINT32_TEST(mig_liobn, sPAPRPHBState, pre_2_8_migration), VMSTATE_UINT64_TEST(mig_mem_win_addr, sPAPRPHBState, pre_2_8_migration), VMSTATE_UINT64_TEST(mig_mem_win_size, sPAPRPHBState, pre_2_8_migration), @@ -1997,8 +1969,8 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data) /* Supported by TYPE_SPAPR_MACHINE */ dc->user_creatable = true; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - hp->plug = spapr_phb_hot_plug_child; - hp->unplug = spapr_phb_hot_unplug_child; + hp->plug = spapr_pci_plug; + hp->unplug_request = spapr_pci_unplug_request; } static const TypeInfo spapr_phb_info = { diff --git a/hw/ppc/spapr_rng.c b/hw/ppc/spapr_rng.c index 80515eb54d..d2acd61a15 100644 --- a/hw/ppc/spapr_rng.c +++ b/hw/ppc/spapr_rng.c @@ -96,17 +96,11 @@ static target_ulong h_random(PowerPCCPU *cpu, sPAPRMachineState *spapr, static void spapr_rng_instance_init(Object *obj) { - sPAPRRngState *rngstate = SPAPR_RNG(obj); - if (object_resolve_path_type("", TYPE_SPAPR_RNG, NULL) != NULL) { error_report("spapr-rng can not be instantiated twice!"); return; } - object_property_add_link(obj, "rng", TYPE_RNG_BACKEND, - (Object **)&rngstate->backend, - object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, NULL); object_property_set_description(obj, "rng", "ID of the random number generator backend", NULL); @@ -163,6 +157,8 @@ int spapr_rng_populate_dt(void *fdt) static Property spapr_rng_properties[] = { DEFINE_PROP_BOOL("use-kvm", sPAPRRngState, use_kvm, false), + DEFINE_PROP_LINK("rng", sPAPRRngState, backend, TYPE_RNG_BACKEND, + RngBackend *), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index 619f32c054..94a2799b99 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -29,7 +29,6 @@ #include "qemu/log.h" #include "qemu/error-report.h" #include "sysemu/sysemu.h" -#include "sysemu/char.h" #include "hw/qdev.h" #include "sysemu/device_tree.h" #include "sysemu/cpus.h" @@ -48,44 +47,6 @@ #include "trace.h" #include "hw/ppc/fdt.h" -static sPAPRConfigureConnectorState *spapr_ccs_find(sPAPRMachineState *spapr, - uint32_t drc_index) -{ - sPAPRConfigureConnectorState *ccs = NULL; - - QTAILQ_FOREACH(ccs, &spapr->ccs_list, next) { - if (ccs->drc_index == drc_index) { - break; - } - } - - return ccs; -} - -static void spapr_ccs_add(sPAPRMachineState *spapr, - sPAPRConfigureConnectorState *ccs) -{ - g_assert(!spapr_ccs_find(spapr, ccs->drc_index)); - QTAILQ_INSERT_HEAD(&spapr->ccs_list, ccs, next); -} - -static void spapr_ccs_remove(sPAPRMachineState *spapr, - sPAPRConfigureConnectorState *ccs) -{ - QTAILQ_REMOVE(&spapr->ccs_list, ccs, next); - g_free(ccs); -} - -void spapr_ccs_reset_hook(void *opaque) -{ - sPAPRMachineState *spapr = opaque; - sPAPRConfigureConnectorState *ccs, *ccs_tmp; - - QTAILQ_FOREACH_SAFE(ccs, &spapr->ccs_list, next, ccs_tmp) { - spapr_ccs_remove(spapr, ccs); - } -} - static void rtas_display_character(PowerPCCPU *cpu, sPAPRMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, @@ -110,7 +71,7 @@ static void rtas_power_off(PowerPCCPU *cpu, sPAPRMachineState *spapr, rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } - qemu_system_shutdown_request(); + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); cpu_stop_current(); rtas_st(rets, 0, RTAS_OUT_SUCCESS); } @@ -124,7 +85,7 @@ static void rtas_system_reboot(PowerPCCPU *cpu, sPAPRMachineState *spapr, rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); rtas_st(rets, 0, RTAS_OUT_SUCCESS); } @@ -332,12 +293,9 @@ static void rtas_ibm_os_term(PowerPCCPU *cpu, target_ulong args, uint32_t nret, target_ulong rets) { - target_ulong ret = 0; + qemu_system_guest_panicked(NULL); - qapi_event_send_guest_panicked(GUEST_PANIC_ACTION_PAUSE, false, NULL, - &error_abort); - - rtas_st(rets, 0, ret); + rtas_st(rets, 0, RTAS_OUT_SUCCESS); } static void rtas_set_power_level(PowerPCCPU *cpu, sPAPRMachineState *spapr, @@ -390,266 +348,6 @@ static void rtas_get_power_level(PowerPCCPU *cpu, sPAPRMachineState *spapr, rtas_st(rets, 1, 100); } -static bool sensor_type_is_dr(uint32_t sensor_type) -{ - switch (sensor_type) { - case RTAS_SENSOR_TYPE_ISOLATION_STATE: - case RTAS_SENSOR_TYPE_DR: - case RTAS_SENSOR_TYPE_ALLOCATION_STATE: - return true; - } - - return false; -} - -static void rtas_set_indicator(PowerPCCPU *cpu, sPAPRMachineState *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, uint32_t nret, - target_ulong rets) -{ - uint32_t sensor_type; - uint32_t sensor_index; - uint32_t sensor_state; - uint32_t ret = RTAS_OUT_SUCCESS; - sPAPRDRConnector *drc; - sPAPRDRConnectorClass *drck; - - if (nargs != 3 || nret != 1) { - ret = RTAS_OUT_PARAM_ERROR; - goto out; - } - - sensor_type = rtas_ld(args, 0); - sensor_index = rtas_ld(args, 1); - sensor_state = rtas_ld(args, 2); - - if (!sensor_type_is_dr(sensor_type)) { - goto out_unimplemented; - } - - /* if this is a DR sensor we can assume sensor_index == drc_index */ - drc = spapr_dr_connector_by_index(sensor_index); - if (!drc) { - trace_spapr_rtas_set_indicator_invalid(sensor_index); - ret = RTAS_OUT_PARAM_ERROR; - goto out; - } - drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - - switch (sensor_type) { - case RTAS_SENSOR_TYPE_ISOLATION_STATE: - /* if the guest is configuring a device attached to this - * DRC, we should reset the configuration state at this - * point since it may no longer be reliable (guest released - * device and needs to start over, or unplug occurred so - * the FDT is no longer valid) - */ - if (sensor_state == SPAPR_DR_ISOLATION_STATE_ISOLATED) { - sPAPRConfigureConnectorState *ccs = spapr_ccs_find(spapr, - sensor_index); - if (ccs) { - spapr_ccs_remove(spapr, ccs); - } - } - ret = drck->set_isolation_state(drc, sensor_state); - break; - case RTAS_SENSOR_TYPE_DR: - ret = drck->set_indicator_state(drc, sensor_state); - break; - case RTAS_SENSOR_TYPE_ALLOCATION_STATE: - ret = drck->set_allocation_state(drc, sensor_state); - break; - default: - goto out_unimplemented; - } - -out: - rtas_st(rets, 0, ret); - return; - -out_unimplemented: - /* currently only DR-related sensors are implemented */ - trace_spapr_rtas_set_indicator_not_supported(sensor_index, sensor_type); - rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED); -} - -static void rtas_get_sensor_state(PowerPCCPU *cpu, sPAPRMachineState *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, uint32_t nret, - target_ulong rets) -{ - uint32_t sensor_type; - uint32_t sensor_index; - uint32_t sensor_state = 0; - sPAPRDRConnector *drc; - sPAPRDRConnectorClass *drck; - uint32_t ret = RTAS_OUT_SUCCESS; - - if (nargs != 2 || nret != 2) { - ret = RTAS_OUT_PARAM_ERROR; - goto out; - } - - sensor_type = rtas_ld(args, 0); - sensor_index = rtas_ld(args, 1); - - if (sensor_type != RTAS_SENSOR_TYPE_ENTITY_SENSE) { - /* currently only DR-related sensors are implemented */ - trace_spapr_rtas_get_sensor_state_not_supported(sensor_index, - sensor_type); - ret = RTAS_OUT_NOT_SUPPORTED; - goto out; - } - - drc = spapr_dr_connector_by_index(sensor_index); - if (!drc) { - trace_spapr_rtas_get_sensor_state_invalid(sensor_index); - ret = RTAS_OUT_PARAM_ERROR; - goto out; - } - drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - ret = drck->entity_sense(drc, &sensor_state); - -out: - rtas_st(rets, 0, ret); - rtas_st(rets, 1, sensor_state); -} - -/* configure-connector work area offsets, int32_t units for field - * indexes, bytes for field offset/len values. - * - * as documented by PAPR+ v2.7, 13.5.3.5 - */ -#define CC_IDX_NODE_NAME_OFFSET 2 -#define CC_IDX_PROP_NAME_OFFSET 2 -#define CC_IDX_PROP_LEN 3 -#define CC_IDX_PROP_DATA_OFFSET 4 -#define CC_VAL_DATA_OFFSET ((CC_IDX_PROP_DATA_OFFSET + 1) * 4) -#define CC_WA_LEN 4096 - -static void configure_connector_st(target_ulong addr, target_ulong offset, - const void *buf, size_t len) -{ - cpu_physical_memory_write(ppc64_phys_to_real(addr + offset), - buf, MIN(len, CC_WA_LEN - offset)); -} - -static void rtas_ibm_configure_connector(PowerPCCPU *cpu, - sPAPRMachineState *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, uint32_t nret, - target_ulong rets) -{ - uint64_t wa_addr; - uint64_t wa_offset; - uint32_t drc_index; - sPAPRDRConnector *drc; - sPAPRDRConnectorClass *drck; - sPAPRConfigureConnectorState *ccs; - sPAPRDRCCResponse resp = SPAPR_DR_CC_RESPONSE_CONTINUE; - int rc; - const void *fdt; - - if (nargs != 2 || nret != 1) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - - wa_addr = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 0); - - drc_index = rtas_ld(wa_addr, 0); - drc = spapr_dr_connector_by_index(drc_index); - if (!drc) { - trace_spapr_rtas_ibm_configure_connector_invalid(drc_index); - rc = RTAS_OUT_PARAM_ERROR; - goto out; - } - - drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - fdt = drck->get_fdt(drc, NULL); - if (!fdt) { - trace_spapr_rtas_ibm_configure_connector_missing_fdt(drc_index); - rc = SPAPR_DR_CC_RESPONSE_NOT_CONFIGURABLE; - goto out; - } - - ccs = spapr_ccs_find(spapr, drc_index); - if (!ccs) { - ccs = g_new0(sPAPRConfigureConnectorState, 1); - (void)drck->get_fdt(drc, &ccs->fdt_offset); - ccs->drc_index = drc_index; - spapr_ccs_add(spapr, ccs); - } - - do { - uint32_t tag; - const char *name; - const struct fdt_property *prop; - int fdt_offset_next, prop_len; - - tag = fdt_next_tag(fdt, ccs->fdt_offset, &fdt_offset_next); - - switch (tag) { - case FDT_BEGIN_NODE: - ccs->fdt_depth++; - name = fdt_get_name(fdt, ccs->fdt_offset, NULL); - - /* provide the name of the next OF node */ - wa_offset = CC_VAL_DATA_OFFSET; - rtas_st(wa_addr, CC_IDX_NODE_NAME_OFFSET, wa_offset); - configure_connector_st(wa_addr, wa_offset, name, strlen(name) + 1); - resp = SPAPR_DR_CC_RESPONSE_NEXT_CHILD; - break; - case FDT_END_NODE: - ccs->fdt_depth--; - if (ccs->fdt_depth == 0) { - /* done sending the device tree, don't need to track - * the state anymore - */ - drck->set_configured(drc); - spapr_ccs_remove(spapr, ccs); - ccs = NULL; - resp = SPAPR_DR_CC_RESPONSE_SUCCESS; - } else { - resp = SPAPR_DR_CC_RESPONSE_PREV_PARENT; - } - break; - case FDT_PROP: - prop = fdt_get_property_by_offset(fdt, ccs->fdt_offset, - &prop_len); - name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); - - /* provide the name of the next OF property */ - wa_offset = CC_VAL_DATA_OFFSET; - rtas_st(wa_addr, CC_IDX_PROP_NAME_OFFSET, wa_offset); - configure_connector_st(wa_addr, wa_offset, name, strlen(name) + 1); - - /* provide the length and value of the OF property. data gets - * placed immediately after NULL terminator of the OF property's - * name string - */ - wa_offset += strlen(name) + 1, - rtas_st(wa_addr, CC_IDX_PROP_LEN, prop_len); - rtas_st(wa_addr, CC_IDX_PROP_DATA_OFFSET, wa_offset); - configure_connector_st(wa_addr, wa_offset, prop->data, prop_len); - resp = SPAPR_DR_CC_RESPONSE_NEXT_PROPERTY; - break; - case FDT_END: - resp = SPAPR_DR_CC_RESPONSE_ERROR; - default: - /* keep seeking for an actionable tag */ - break; - } - if (ccs) { - ccs->fdt_offset = fdt_offset_next; - } - } while (resp == SPAPR_DR_CC_RESPONSE_CONTINUE); - - rc = resp; -out: - rtas_st(rets, 0, rc); -} - static struct rtas_call { const char *name; spapr_rtas_fn fn; @@ -791,12 +489,6 @@ static void core_rtas_register_types(void) rtas_set_power_level); spapr_rtas_register(RTAS_GET_POWER_LEVEL, "get-power-level", rtas_get_power_level); - spapr_rtas_register(RTAS_SET_INDICATOR, "set-indicator", - rtas_set_indicator); - spapr_rtas_register(RTAS_GET_SENSOR_STATE, "get-sensor-state", - rtas_get_sensor_state); - spapr_rtas_register(RTAS_IBM_CONFIGURE_CONNECTOR, "ibm,configure-connector", - rtas_ibm_configure_connector); } type_init(core_rtas_register_types) diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c index a0ee4fd265..ea3bc8bd9e 100644 --- a/hw/ppc/spapr_vio.c +++ b/hw/ppc/spapr_vio.c @@ -557,8 +557,8 @@ const VMStateDescription vmstate_spapr_vio = { .minimum_version_id = 1, .fields = (VMStateField[]) { /* Sanity check */ - VMSTATE_UINT32_EQUAL(reg, VIOsPAPRDevice), - VMSTATE_UINT32_EQUAL(irq, VIOsPAPRDevice), + VMSTATE_UINT32_EQUAL(reg, VIOsPAPRDevice, NULL), + VMSTATE_UINT32_EQUAL(irq, VIOsPAPRDevice, NULL), /* General VIO device state */ VMSTATE_UINT64(signal_state, VIOsPAPRDevice), diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events index 43d265f351..0f7d9be4ef 100644 --- a/hw/ppc/trace-events +++ b/hw/ppc/trace-events @@ -16,6 +16,8 @@ spapr_cas_continue(unsigned long n) "Copy changes to the guest: %ld bytes" # hw/ppc/spapr_hcall.c spapr_cas_pvr_try(uint32_t pvr) "%x" spapr_cas_pvr(uint32_t cur_pvr, bool explicit_match, uint32_t new_pvr) "current=%x, explicit_match=%u, new=%x" +spapr_h_resize_hpt_prepare(uint64_t flags, uint64_t shift) "flags=0x%"PRIx64", shift=%"PRIu64 +spapr_h_resize_hpt_commit(uint64_t flags, uint64_t shift) "flags=0x%"PRIx64", shift=%"PRIu64 # hw/ppc/spapr_iommu.c spapr_iommu_put(uint64_t liobn, uint64_t ioba, uint64_t tce, uint64_t ret) "liobn=%"PRIx64" ioba=0x%"PRIx64" tce=0x%"PRIx64" ret=%"PRId64 @@ -39,16 +41,14 @@ spapr_iommu_ddw_reset(uint64_t buid, uint32_t cfgaddr) "buid=%"PRIx64" addr=%"PR spapr_drc_set_isolation_state(uint32_t index, int state) "drc: 0x%"PRIx32", state: %"PRIx32 spapr_drc_set_isolation_state_finalizing(uint32_t index) "drc: 0x%"PRIx32 spapr_drc_set_isolation_state_deferring(uint32_t index) "drc: 0x%"PRIx32 -spapr_drc_set_indicator_state(uint32_t index, int state) "drc: 0x%"PRIx32", state: 0x%x" +spapr_drc_set_dr_indicator(uint32_t index, int state) "drc: 0x%"PRIx32", state: 0x%x" spapr_drc_set_allocation_state(uint32_t index, int state) "drc: 0x%"PRIx32", state: 0x%x" spapr_drc_set_allocation_state_finalizing(uint32_t index) "drc: 0x%"PRIx32 spapr_drc_set_configured(uint32_t index) "drc: 0x%"PRIx32 spapr_drc_set_configured_skipping(uint32_t index) "drc: 0x%"PRIx32", isolated device" -spapr_drc_entity_sense(uint32_t index, int state) "drc: 0x%"PRIx32", state: 0x%x" spapr_drc_attach(uint32_t index) "drc: 0x%"PRIx32 spapr_drc_detach(uint32_t index) "drc: 0x%"PRIx32 -spapr_drc_awaiting_isolated(uint32_t index) "drc: 0x%"PRIx32 -spapr_drc_awaiting_unusable(uint32_t index) "drc: 0x%"PRIx32 +spapr_drc_awaiting_quiesce(uint32_t index) "drc: 0x%"PRIx32 spapr_drc_awaiting_allocation(uint32_t index) "drc: 0x%"PRIx32 spapr_drc_reset(uint32_t index) "drc: 0x%"PRIx32 spapr_drc_realize(uint32_t index) "drc: 0x%"PRIx32 @@ -61,8 +61,6 @@ spapr_ovec_parse_vector(int vector, int byte, uint16_t vec_len, uint8_t entry) " spapr_ovec_populate_dt(int byte, uint16_t vec_len, uint8_t entry) "encoding guest vector byte %3d / %3d: 0x%.2x" # hw/ppc/spapr_rtas.c -spapr_rtas_set_indicator_invalid(uint32_t index) "sensor index: 0x%"PRIx32 -spapr_rtas_set_indicator_not_supported(uint32_t index, uint32_t type) "sensor index: 0x%"PRIx32", type: %"PRIu32 spapr_rtas_get_sensor_state_not_supported(uint32_t index, uint32_t type) "sensor index: 0x%"PRIx32", type: %"PRIu32 spapr_rtas_get_sensor_state_invalid(uint32_t index) "sensor index: 0x%"PRIx32 spapr_rtas_ibm_configure_connector_invalid(uint32_t index) "DRC index: 0x%"PRIx32 diff --git a/hw/s390x/3270-ccw.c b/hw/s390x/3270-ccw.c index a7a5b412e4..1554aa2484 100644 --- a/hw/s390x/3270-ccw.c +++ b/hw/s390x/3270-ccw.c @@ -98,9 +98,13 @@ static void emulated_ccw_3270_realize(DeviceState *ds, Error **errp) EmulatedCcw3270Class *ck = EMULATED_CCW_3270_GET_CLASS(dev); CcwDevice *cdev = CCW_DEVICE(ds); CCWDeviceClass *cdk = CCW_DEVICE_GET_CLASS(cdev); - SubchDev *sch = css_create_virtual_sch(cdev->devno, errp); + DeviceState *parent = DEVICE(cdev); + BusState *qbus = qdev_get_parent_bus(parent); + VirtualCssBus *cbus = VIRTUAL_CSS_BUS(qbus); + SubchDev *sch; Error *err = NULL; + sch = css_create_sch(cdev->devno, true, cbus->squash_mcss, errp); if (!sch) { return; } @@ -122,6 +126,7 @@ static void emulated_ccw_3270_realize(DeviceState *ds, Error **errp) sch->id.cu_type = EMULATED_CCW_3270_CU_TYPE; css_sch_build_virtual_schib(sch, (uint8_t)chpid, EMULATED_CCW_3270_CHPID_TYPE); + sch->do_subchannel_work = do_subchannel_work_virtual; sch->ccw_cb = emulated_ccw_3270_cb; ck->init(dev, &err); diff --git a/hw/s390x/Makefile.objs b/hw/s390x/Makefile.objs index 36bd4b1645..b2aade2466 100644 --- a/hw/s390x/Makefile.objs +++ b/hw/s390x/Makefile.objs @@ -13,4 +13,7 @@ obj-y += css-bridge.o obj-y += ccw-device.o obj-y += s390-pci-bus.o s390-pci-inst.o obj-y += s390-skeys.o +obj-y += s390-stattrib.o obj-$(CONFIG_KVM) += s390-skeys-kvm.o +obj-$(CONFIG_KVM) += s390-stattrib-kvm.o +obj-y += s390-ccw.o diff --git a/hw/s390x/ccw-device.c b/hw/s390x/ccw-device.c index fb8d640a7e..f9bfa154d6 100644 --- a/hw/s390x/ccw-device.c +++ b/hw/s390x/ccw-device.c @@ -50,6 +50,16 @@ static void ccw_device_class_init(ObjectClass *klass, void *data) dc->props = ccw_device_properties; } +const VMStateDescription vmstate_ccw_dev = { + .name = "s390_ccw_dev", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT_POINTER(sch, CcwDevice, vmstate_subch_dev, SubchDev), + VMSTATE_END_OF_LIST() + } +}; + static const TypeInfo ccw_device_info = { .name = TYPE_CCW_DEVICE, .parent = TYPE_DEVICE, diff --git a/hw/s390x/ccw-device.h b/hw/s390x/ccw-device.h index 89c8e5dff7..4e6af287e7 100644 --- a/hw/s390x/ccw-device.h +++ b/hw/s390x/ccw-device.h @@ -27,6 +27,10 @@ typedef struct CcwDevice { CssDevId subch_id; } CcwDevice; +extern const VMStateDescription vmstate_ccw_dev; +#define VMSTATE_CCW_DEVICE(_field, _state) \ + VMSTATE_STRUCT(_field, _state, 1, vmstate_ccw_dev, CcwDevice) + typedef struct CCWDeviceClass { DeviceClass parent_class; void (*unplug)(HotplugHandler *, DeviceState *, Error **); diff --git a/hw/s390x/css-bridge.c b/hw/s390x/css-bridge.c index b54ac01d37..c4a9735d71 100644 --- a/hw/s390x/css-bridge.c +++ b/hw/s390x/css-bridge.c @@ -17,6 +17,7 @@ #include "hw/s390x/css.h" #include "ccw-device.h" #include "hw/s390x/css-bridge.h" +#include "cpu.h" /* * Invoke device-specific unplug handler, disable the subchannel @@ -103,12 +104,13 @@ VirtualCssBus *virtual_css_bus_init(void) /* Create bus on bridge device */ bus = qbus_create(TYPE_VIRTUAL_CSS_BUS, dev, "virtual-css"); cbus = VIRTUAL_CSS_BUS(bus); + cbus->squash_mcss = s390_get_squash_mcss(); /* Enable hotplugging */ qbus_set_hotplug_handler(bus, dev, &error_abort); css_register_io_adapters(CSS_IO_ADAPTER_VIRTIO, true, false, - &error_abort); + 0, &error_abort); return cbus; } diff --git a/hw/s390x/css.c b/hw/s390x/css.c index 15c4f4b249..6a42b95cee 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -13,40 +13,299 @@ #include "qapi/error.h" #include "qapi/visitor.h" #include "hw/qdev.h" +#include "qemu/error-report.h" #include "qemu/bitops.h" +#include "qemu/error-report.h" #include "exec/address-spaces.h" #include "cpu.h" #include "hw/s390x/ioinst.h" #include "hw/s390x/css.h" #include "trace.h" #include "hw/s390x/s390_flic.h" +#include "hw/s390x/s390-virtio-ccw.h" typedef struct CrwContainer { CRW crw; QTAILQ_ENTRY(CrwContainer) sibling; } CrwContainer; +static const VMStateDescription vmstate_crw = { + .name = "s390_crw", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT16(flags, CRW), + VMSTATE_UINT16(rsid, CRW), + VMSTATE_END_OF_LIST() + }, +}; + +static const VMStateDescription vmstate_crw_container = { + .name = "s390_crw_container", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(crw, CrwContainer, 0, vmstate_crw, CRW), + VMSTATE_END_OF_LIST() + }, +}; + typedef struct ChpInfo { uint8_t in_use; uint8_t type; uint8_t is_virtual; } ChpInfo; +static const VMStateDescription vmstate_chp_info = { + .name = "s390_chp_info", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(in_use, ChpInfo), + VMSTATE_UINT8(type, ChpInfo), + VMSTATE_UINT8(is_virtual, ChpInfo), + VMSTATE_END_OF_LIST() + } +}; + typedef struct SubchSet { SubchDev *sch[MAX_SCHID + 1]; unsigned long schids_used[BITS_TO_LONGS(MAX_SCHID + 1)]; unsigned long devnos_used[BITS_TO_LONGS(MAX_SCHID + 1)]; } SubchSet; +static const VMStateDescription vmstate_scsw = { + .name = "s390_scsw", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT16(flags, SCSW), + VMSTATE_UINT16(ctrl, SCSW), + VMSTATE_UINT32(cpa, SCSW), + VMSTATE_UINT8(dstat, SCSW), + VMSTATE_UINT8(cstat, SCSW), + VMSTATE_UINT16(count, SCSW), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_pmcw = { + .name = "s390_pmcw", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(intparm, PMCW), + VMSTATE_UINT16(flags, PMCW), + VMSTATE_UINT16(devno, PMCW), + VMSTATE_UINT8(lpm, PMCW), + VMSTATE_UINT8(pnom, PMCW), + VMSTATE_UINT8(lpum, PMCW), + VMSTATE_UINT8(pim, PMCW), + VMSTATE_UINT16(mbi, PMCW), + VMSTATE_UINT8(pom, PMCW), + VMSTATE_UINT8(pam, PMCW), + VMSTATE_UINT8_ARRAY(chpid, PMCW, 8), + VMSTATE_UINT32(chars, PMCW), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_schib = { + .name = "s390_schib", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(pmcw, SCHIB, 0, vmstate_pmcw, PMCW), + VMSTATE_STRUCT(scsw, SCHIB, 0, vmstate_scsw, SCSW), + VMSTATE_UINT64(mba, SCHIB), + VMSTATE_UINT8_ARRAY(mda, SCHIB, 4), + VMSTATE_END_OF_LIST() + } +}; + + +static const VMStateDescription vmstate_ccw1 = { + .name = "s390_ccw1", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(cmd_code, CCW1), + VMSTATE_UINT8(flags, CCW1), + VMSTATE_UINT16(count, CCW1), + VMSTATE_UINT32(cda, CCW1), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_ciw = { + .name = "s390_ciw", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(type, CIW), + VMSTATE_UINT8(command, CIW), + VMSTATE_UINT16(count, CIW), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_sense_id = { + .name = "s390_sense_id", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(reserved, SenseId), + VMSTATE_UINT16(cu_type, SenseId), + VMSTATE_UINT8(cu_model, SenseId), + VMSTATE_UINT16(dev_type, SenseId), + VMSTATE_UINT8(dev_model, SenseId), + VMSTATE_UINT8(unused, SenseId), + VMSTATE_STRUCT_ARRAY(ciw, SenseId, MAX_CIWS, 0, vmstate_ciw, CIW), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_orb = { + .name = "s390_orb", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(intparm, ORB), + VMSTATE_UINT16(ctrl0, ORB), + VMSTATE_UINT8(lpm, ORB), + VMSTATE_UINT8(ctrl1, ORB), + VMSTATE_UINT32(cpa, ORB), + VMSTATE_END_OF_LIST() + } +}; + +static bool vmstate_schdev_orb_needed(void *opaque) +{ + return css_migration_enabled(); +} + +static const VMStateDescription vmstate_schdev_orb = { + .name = "s390_subch_dev/orb", + .version_id = 1, + .minimum_version_id = 1, + .needed = vmstate_schdev_orb_needed, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(orb, SubchDev, 1, vmstate_orb, ORB), + VMSTATE_END_OF_LIST() + } +}; + +static int subch_dev_post_load(void *opaque, int version_id); +static void subch_dev_pre_save(void *opaque); + +const char err_hint_devno[] = "Devno mismatch, tried to load wrong section!" + " Likely reason: some sequences of plug and unplug can break" + " migration for machine versions prior to 2.7 (known design flaw)."; + +const VMStateDescription vmstate_subch_dev = { + .name = "s390_subch_dev", + .version_id = 1, + .minimum_version_id = 1, + .post_load = subch_dev_post_load, + .pre_save = subch_dev_pre_save, + .fields = (VMStateField[]) { + VMSTATE_UINT8_EQUAL(cssid, SubchDev, "Bug!"), + VMSTATE_UINT8_EQUAL(ssid, SubchDev, "Bug!"), + VMSTATE_UINT16(migrated_schid, SubchDev), + VMSTATE_UINT16_EQUAL(devno, SubchDev, err_hint_devno), + VMSTATE_BOOL(thinint_active, SubchDev), + VMSTATE_STRUCT(curr_status, SubchDev, 0, vmstate_schib, SCHIB), + VMSTATE_UINT8_ARRAY(sense_data, SubchDev, 32), + VMSTATE_UINT64(channel_prog, SubchDev), + VMSTATE_STRUCT(last_cmd, SubchDev, 0, vmstate_ccw1, CCW1), + VMSTATE_BOOL(last_cmd_valid, SubchDev), + VMSTATE_STRUCT(id, SubchDev, 0, vmstate_sense_id, SenseId), + VMSTATE_BOOL(ccw_fmt_1, SubchDev), + VMSTATE_UINT8(ccw_no_data_cnt, SubchDev), + VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * []) { + &vmstate_schdev_orb, + NULL + } +}; + +typedef struct IndAddrPtrTmp { + IndAddr **parent; + uint64_t addr; + int32_t len; +} IndAddrPtrTmp; + +static int post_load_ind_addr(void *opaque, int version_id) +{ + IndAddrPtrTmp *ptmp = opaque; + IndAddr **ind_addr = ptmp->parent; + + if (ptmp->len != 0) { + *ind_addr = get_indicator(ptmp->addr, ptmp->len); + } else { + *ind_addr = NULL; + } + return 0; +} + +static void pre_save_ind_addr(void *opaque) +{ + IndAddrPtrTmp *ptmp = opaque; + IndAddr *ind_addr = *(ptmp->parent); + + if (ind_addr != NULL) { + ptmp->len = ind_addr->len; + ptmp->addr = ind_addr->addr; + } else { + ptmp->len = 0; + ptmp->addr = 0L; + } +} + +const VMStateDescription vmstate_ind_addr_tmp = { + .name = "s390_ind_addr_tmp", + .pre_save = pre_save_ind_addr, + .post_load = post_load_ind_addr, + + .fields = (VMStateField[]) { + VMSTATE_INT32(len, IndAddrPtrTmp), + VMSTATE_UINT64(addr, IndAddrPtrTmp), + VMSTATE_END_OF_LIST() + } +}; + +const VMStateDescription vmstate_ind_addr = { + .name = "s390_ind_addr_tmp", + .fields = (VMStateField[]) { + VMSTATE_WITH_TMP(IndAddr*, IndAddrPtrTmp, vmstate_ind_addr_tmp), + VMSTATE_END_OF_LIST() + } +}; + typedef struct CssImage { SubchSet *sch_set[MAX_SSID + 1]; ChpInfo chpids[MAX_CHPID + 1]; } CssImage; +static const VMStateDescription vmstate_css_img = { + .name = "s390_css_img", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + /* Subchannel sets have no relevant state. */ + VMSTATE_STRUCT_ARRAY(chpids, CssImage, MAX_CHPID + 1, 0, + vmstate_chp_info, ChpInfo), + VMSTATE_END_OF_LIST() + } + +}; + typedef struct IoAdapter { uint32_t id; uint8_t type; uint8_t isc; + uint8_t flags; } IoAdapter; typedef struct ChannelSubSys { @@ -60,10 +319,34 @@ typedef struct ChannelSubSys { uint64_t chnmon_area; CssImage *css[MAX_CSSID + 1]; uint8_t default_cssid; + /* don't migrate, see css_register_io_adapters */ IoAdapter *io_adapters[CSS_IO_ADAPTER_TYPE_NUMS][MAX_ISC + 1]; + /* don't migrate, see get_indicator and IndAddrPtrTmp */ QTAILQ_HEAD(, IndAddr) indicator_addresses; } ChannelSubSys; +static const VMStateDescription vmstate_css = { + .name = "s390_css", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_QTAILQ_V(pending_crws, ChannelSubSys, 1, vmstate_crw_container, + CrwContainer, sibling), + VMSTATE_BOOL(sei_pending, ChannelSubSys), + VMSTATE_BOOL(do_crw_mchk, ChannelSubSys), + VMSTATE_BOOL(crws_lost, ChannelSubSys), + /* These were kind of migrated by virtio */ + VMSTATE_UINT8(max_cssid, ChannelSubSys), + VMSTATE_UINT8(max_ssid, ChannelSubSys), + VMSTATE_BOOL(chnmon_active, ChannelSubSys), + VMSTATE_UINT64(chnmon_area, ChannelSubSys), + VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(css, ChannelSubSys, MAX_CSSID + 1, + 0, vmstate_css_img, CssImage), + VMSTATE_UINT8(default_cssid, ChannelSubSys), + VMSTATE_END_OF_LIST() + } +}; + static ChannelSubSys channel_subsys = { .pending_crws = QTAILQ_HEAD_INITIALIZER(channel_subsys.pending_crws), .do_crw_mchk = true, @@ -75,6 +358,61 @@ static ChannelSubSys channel_subsys = { QTAILQ_HEAD_INITIALIZER(channel_subsys.indicator_addresses), }; +static void subch_dev_pre_save(void *opaque) +{ + SubchDev *s = opaque; + + /* Prepare remote_schid for save */ + s->migrated_schid = s->schid; +} + +static int subch_dev_post_load(void *opaque, int version_id) +{ + + SubchDev *s = opaque; + + /* Re-assign the subchannel to remote_schid if necessary */ + if (s->migrated_schid != s->schid) { + if (css_find_subch(true, s->cssid, s->ssid, s->schid) == s) { + /* + * Cleanup the slot before moving to s->migrated_schid provided + * it still belongs to us, i.e. it was not changed by previous + * invocation of this function. + */ + css_subch_assign(s->cssid, s->ssid, s->schid, s->devno, NULL); + } + /* It's OK to re-assign without a prior de-assign. */ + s->schid = s->migrated_schid; + css_subch_assign(s->cssid, s->ssid, s->schid, s->devno, s); + } + + if (css_migration_enabled()) { + /* No compat voodoo to do ;) */ + return 0; + } + /* + * Hack alert. If we don't migrate the channel subsystem status + * we still need to find out if the guest enabled mss/mcss-e. + * If the subchannel is enabled, it certainly was able to access it, + * so adjust the max_ssid/max_cssid values for relevant ssid/cssid + * values. This is not watertight, but better than nothing. + */ + if (s->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ENA) { + if (s->ssid) { + channel_subsys.max_ssid = MAX_SSID; + } + if (s->cssid != channel_subsys.default_cssid) { + channel_subsys.max_cssid = MAX_CSSID; + } + } + return 0; +} + +void css_register_vmstate(void) +{ + vmstate_register(NULL, 0, &vmstate_css, &channel_subsys); +} + IndAddr *get_indicator(hwaddr ind_addr, int len) { IndAddr *indicator; @@ -168,10 +506,12 @@ uint32_t css_get_adapter_id(CssIoAdapterType type, uint8_t isc) * * @swap: an indication if byte swap is needed. * @maskable: an indication if the adapter is subject to the mask operation. + * @flags: further characteristics of the adapter. + * e.g. suppressible, an indication if the adapter is subject to AIS. * @errp: location to store error information. */ void css_register_io_adapters(CssIoAdapterType type, bool swap, bool maskable, - Error **errp) + uint8_t flags, Error **errp) { uint32_t id; int ret, isc; @@ -189,12 +529,13 @@ void css_register_io_adapters(CssIoAdapterType type, bool swap, bool maskable, for (isc = 0; isc <= MAX_ISC; isc++) { id = (type << 3) | isc; - ret = fsc->register_io_adapter(fs, id, isc, swap, maskable); + ret = fsc->register_io_adapter(fs, id, isc, swap, maskable, flags); if (ret == 0) { adapter = g_new0(IoAdapter, 1); adapter->id = id; adapter->isc = isc; adapter->type = type; + adapter->flags = flags; channel_subsys.io_adapters[type][isc] = adapter; } else { error_setg_errno(errp, -ret, "Unexpected error %d when " @@ -258,7 +599,7 @@ uint16_t css_build_subchannel_id(SubchDev *sch) return css_do_build_subchannel_id(sch->cssid, sch->ssid); } -static void css_inject_io_interrupt(SubchDev *sch) +void css_inject_io_interrupt(SubchDev *sch) { uint8_t isc = (sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ISC) >> 11; @@ -293,12 +634,52 @@ void css_conditional_io_interrupt(SubchDev *sch) } } -void css_adapter_interrupt(uint8_t isc) +int css_do_sic(CPUS390XState *env, uint8_t isc, uint16_t mode) +{ + S390FLICState *fs = s390_get_flic(); + S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); + int r; + + if (env->psw.mask & PSW_MASK_PSTATE) { + r = -PGM_PRIVILEGED; + goto out; + } + + trace_css_do_sic(mode, isc); + switch (mode) { + case SIC_IRQ_MODE_ALL: + case SIC_IRQ_MODE_SINGLE: + break; + default: + r = -PGM_OPERAND; + goto out; + } + + r = fsc->modify_ais_mode(fs, isc, mode) ? -PGM_OPERATION : 0; +out: + return r; +} + +void css_adapter_interrupt(CssIoAdapterType type, uint8_t isc) { + S390FLICState *fs = s390_get_flic(); + S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); uint32_t io_int_word = (isc << 27) | IO_INT_WORD_AI; + IoAdapter *adapter = channel_subsys.io_adapters[type][isc]; + + if (!adapter) { + return; + } trace_css_adapter_interrupt(isc); - s390_io_interrupt(0, 0, 0, io_int_word); + if (fs->ais_supported) { + if (fsc->inject_airq(fs, type, isc, adapter->flags)) { + error_report("Failed to inject airq with AIS supported"); + exit(1); + } + } else { + s390_io_interrupt(0, 0, 0, io_int_word); + } } static void sch_handle_clear_func(SubchDev *sch) @@ -431,6 +812,11 @@ static int css_interpret_ccw(SubchDev *sch, hwaddr ccw_addr, return -EINVAL; } + /* We don't support MIDA. */ + if (ccw.flags & CCW_FLAG_MIDA) { + return -EINVAL; + } + if (ccw.flags & CCW_FLAG_SUSPEND) { return suspend_allowed ? -EINPROGRESS : -EINVAL; } @@ -523,7 +909,7 @@ static int css_interpret_ccw(SubchDev *sch, hwaddr ccw_addr, return ret; } -static void sch_handle_start_func(SubchDev *sch, ORB *orb) +static void sch_handle_start_func_virtual(SubchDev *sch) { PMCW *p = &sch->curr_status.pmcw; @@ -537,10 +923,10 @@ static void sch_handle_start_func(SubchDev *sch, ORB *orb) if (!(s->ctrl & SCSW_ACTL_SUSP)) { /* Start Function triggered via ssch, i.e. we have an ORB */ + ORB *orb = &sch->orb; s->cstat = 0; s->dstat = 0; /* Look at the orb and try to execute the channel program. */ - assert(orb != NULL); /* resume does not pass an orb */ p->intparm = orb->intparm; if (!(orb->lpm & path)) { /* Generate a deferred cc 3 condition. */ @@ -554,8 +940,7 @@ static void sch_handle_start_func(SubchDev *sch, ORB *orb) sch->ccw_no_data_cnt = 0; suspend_allowed = !!(orb->ctrl0 & ORB_CTRL0_MASK_SPND); } else { - /* Start Function resumed via rsch, i.e. we don't have an - * ORB */ + /* Start Function resumed via rsch */ s->ctrl &= ~(SCSW_ACTL_SUSP | SCSW_ACTL_RESUME_PEND); /* The channel program had been suspended before. */ suspend_allowed = true; @@ -625,13 +1010,59 @@ static void sch_handle_start_func(SubchDev *sch, ORB *orb) } +static int sch_handle_start_func_passthrough(SubchDev *sch) +{ + + PMCW *p = &sch->curr_status.pmcw; + SCSW *s = &sch->curr_status.scsw; + int ret; + + ORB *orb = &sch->orb; + if (!(s->ctrl & SCSW_ACTL_SUSP)) { + assert(orb != NULL); + p->intparm = orb->intparm; + } + + /* + * Only support prefetch enable mode. + * Only support 64bit addressing idal. + */ + if (!(orb->ctrl0 & ORB_CTRL0_MASK_PFCH) || + !(orb->ctrl0 & ORB_CTRL0_MASK_C64)) { + return -EINVAL; + } + + ret = s390_ccw_cmd_request(orb, s, sch->driver_data); + switch (ret) { + /* Currently we don't update control block and just return the cc code. */ + case 0: + break; + case -EBUSY: + break; + case -ENODEV: + break; + case -EACCES: + /* Let's reflect an inaccessible host device by cc 3. */ + ret = -ENODEV; + break; + default: + /* + * All other return codes will trigger a program check, + * or set cc to 1. + */ + break; + }; + + return ret; +} + /* * On real machines, this would run asynchronously to the main vcpus. * We might want to make some parts of the ssch handling (interpreting * read/writes) asynchronous later on if we start supporting more than * our current very simple devices. */ -static void do_subchannel_work(SubchDev *sch, ORB *orb) +int do_subchannel_work_virtual(SubchDev *sch) { SCSW *s = &sch->curr_status.scsw; @@ -642,12 +1073,45 @@ static void do_subchannel_work(SubchDev *sch, ORB *orb) sch_handle_halt_func(sch); } else if (s->ctrl & SCSW_FCTL_START_FUNC) { /* Triggered by both ssch and rsch. */ - sch_handle_start_func(sch, orb); + sch_handle_start_func_virtual(sch); } else { /* Cannot happen. */ - return; + return 0; } css_inject_io_interrupt(sch); + return 0; +} + +int do_subchannel_work_passthrough(SubchDev *sch) +{ + int ret; + SCSW *s = &sch->curr_status.scsw; + + if (s->ctrl & SCSW_FCTL_CLEAR_FUNC) { + /* TODO: Clear handling */ + sch_handle_clear_func(sch); + ret = 0; + } else if (s->ctrl & SCSW_FCTL_HALT_FUNC) { + /* TODO: Halt handling */ + sch_handle_halt_func(sch); + ret = 0; + } else if (s->ctrl & SCSW_FCTL_START_FUNC) { + ret = sch_handle_start_func_passthrough(sch); + } else { + /* Cannot happen. */ + return -ENODEV; + } + + return ret; +} + +static int do_subchannel_work(SubchDev *sch) +{ + if (sch->do_subchannel_work) { + return sch->do_subchannel_work(sch); + } else { + return -EINVAL; + } } static void copy_pmcw_to_guest(PMCW *dest, const PMCW *src) @@ -670,7 +1134,7 @@ static void copy_pmcw_to_guest(PMCW *dest, const PMCW *src) dest->chars = cpu_to_be32(src->chars); } -static void copy_scsw_to_guest(SCSW *dest, const SCSW *src) +void copy_scsw_to_guest(SCSW *dest, const SCSW *src) { dest->flags = cpu_to_be16(src->flags); dest->ctrl = cpu_to_be16(src->ctrl); @@ -851,7 +1315,7 @@ int css_do_csch(SubchDev *sch) s->ctrl &= ~(SCSW_CTRL_MASK_FCTL | SCSW_CTRL_MASK_ACTL); s->ctrl |= SCSW_FCTL_CLEAR_FUNC | SCSW_ACTL_CLEAR_PEND; - do_subchannel_work(sch, NULL); + do_subchannel_work(sch); ret = 0; out: @@ -892,7 +1356,7 @@ int css_do_hsch(SubchDev *sch) } s->ctrl |= SCSW_ACTL_HALT_PEND; - do_subchannel_work(sch, NULL); + do_subchannel_work(sch); ret = 0; out: @@ -961,13 +1425,13 @@ int css_do_ssch(SubchDev *sch, ORB *orb) if (channel_subsys.chnmon_active) { css_update_chnmon(sch); } + sch->orb = *orb; sch->channel_prog = orb->cpa; /* Trigger the start function. */ s->ctrl |= (SCSW_FCTL_START_FUNC | SCSW_ACTL_START_PEND); s->flags &= ~SCSW_FLAGS_MASK_PNO; - do_subchannel_work(sch, orb); - ret = 0; + ret = do_subchannel_work(sch); out: return ret; @@ -1246,7 +1710,7 @@ int css_do_rsch(SubchDev *sch) } s->ctrl |= SCSW_ACTL_RESUME_PEND; - do_subchannel_work(sch, NULL); + do_subchannel_work(sch); ret = 0; out: @@ -1326,7 +1790,8 @@ unsigned int css_find_free_chpid(uint8_t cssid) return MAX_CHPID + 1; } -static int css_add_virtual_chpid(uint8_t cssid, uint8_t chpid, uint8_t type) +static int css_add_chpid(uint8_t cssid, uint8_t chpid, uint8_t type, + bool is_virt) { CssImage *css; @@ -1340,7 +1805,7 @@ static int css_add_virtual_chpid(uint8_t cssid, uint8_t chpid, uint8_t type) } css->chpids[chpid].in_use = 1; css->chpids[chpid].type = type; - css->chpids[chpid].is_virtual = 1; + css->chpids[chpid].is_virtual = is_virt; css_generate_chp_crws(cssid, chpid); @@ -1364,7 +1829,7 @@ void css_sch_build_virtual_schib(SubchDev *sch, uint8_t chpid, uint8_t type) p->pam = 0x80; p->chpid[0] = chpid; if (!css->chpids[chpid].in_use) { - css_add_virtual_chpid(sch->cssid, chpid, type); + css_add_chpid(sch->cssid, chpid, type, true); } memset(s, 0, sizeof(SCSW)); @@ -1662,149 +2127,6 @@ int css_enable_mss(void) return 0; } -void subch_device_save(SubchDev *s, QEMUFile *f) -{ - int i; - - qemu_put_byte(f, s->cssid); - qemu_put_byte(f, s->ssid); - qemu_put_be16(f, s->schid); - qemu_put_be16(f, s->devno); - qemu_put_byte(f, s->thinint_active); - /* SCHIB */ - /* PMCW */ - qemu_put_be32(f, s->curr_status.pmcw.intparm); - qemu_put_be16(f, s->curr_status.pmcw.flags); - qemu_put_be16(f, s->curr_status.pmcw.devno); - qemu_put_byte(f, s->curr_status.pmcw.lpm); - qemu_put_byte(f, s->curr_status.pmcw.pnom); - qemu_put_byte(f, s->curr_status.pmcw.lpum); - qemu_put_byte(f, s->curr_status.pmcw.pim); - qemu_put_be16(f, s->curr_status.pmcw.mbi); - qemu_put_byte(f, s->curr_status.pmcw.pom); - qemu_put_byte(f, s->curr_status.pmcw.pam); - qemu_put_buffer(f, s->curr_status.pmcw.chpid, 8); - qemu_put_be32(f, s->curr_status.pmcw.chars); - /* SCSW */ - qemu_put_be16(f, s->curr_status.scsw.flags); - qemu_put_be16(f, s->curr_status.scsw.ctrl); - qemu_put_be32(f, s->curr_status.scsw.cpa); - qemu_put_byte(f, s->curr_status.scsw.dstat); - qemu_put_byte(f, s->curr_status.scsw.cstat); - qemu_put_be16(f, s->curr_status.scsw.count); - qemu_put_be64(f, s->curr_status.mba); - qemu_put_buffer(f, s->curr_status.mda, 4); - /* end SCHIB */ - qemu_put_buffer(f, s->sense_data, 32); - qemu_put_be64(f, s->channel_prog); - /* last cmd */ - qemu_put_byte(f, s->last_cmd.cmd_code); - qemu_put_byte(f, s->last_cmd.flags); - qemu_put_be16(f, s->last_cmd.count); - qemu_put_be32(f, s->last_cmd.cda); - qemu_put_byte(f, s->last_cmd_valid); - qemu_put_byte(f, s->id.reserved); - qemu_put_be16(f, s->id.cu_type); - qemu_put_byte(f, s->id.cu_model); - qemu_put_be16(f, s->id.dev_type); - qemu_put_byte(f, s->id.dev_model); - qemu_put_byte(f, s->id.unused); - for (i = 0; i < ARRAY_SIZE(s->id.ciw); i++) { - qemu_put_byte(f, s->id.ciw[i].type); - qemu_put_byte(f, s->id.ciw[i].command); - qemu_put_be16(f, s->id.ciw[i].count); - } - qemu_put_byte(f, s->ccw_fmt_1); - qemu_put_byte(f, s->ccw_no_data_cnt); -} - -int subch_device_load(SubchDev *s, QEMUFile *f) -{ - SubchDev *old_s; - uint16_t old_schid = s->schid; - int i; - - s->cssid = qemu_get_byte(f); - s->ssid = qemu_get_byte(f); - s->schid = qemu_get_be16(f); - s->devno = qemu_get_be16(f); - /* Re-assign subch. */ - if (old_schid != s->schid) { - old_s = channel_subsys.css[s->cssid]->sch_set[s->ssid]->sch[old_schid]; - /* - * (old_s != s) means that some other device has its correct - * subchannel already assigned (in load). - */ - if (old_s == s) { - css_subch_assign(s->cssid, s->ssid, old_schid, s->devno, NULL); - } - /* It's OK to re-assign without a prior de-assign. */ - css_subch_assign(s->cssid, s->ssid, s->schid, s->devno, s); - } - s->thinint_active = qemu_get_byte(f); - /* SCHIB */ - /* PMCW */ - s->curr_status.pmcw.intparm = qemu_get_be32(f); - s->curr_status.pmcw.flags = qemu_get_be16(f); - s->curr_status.pmcw.devno = qemu_get_be16(f); - s->curr_status.pmcw.lpm = qemu_get_byte(f); - s->curr_status.pmcw.pnom = qemu_get_byte(f); - s->curr_status.pmcw.lpum = qemu_get_byte(f); - s->curr_status.pmcw.pim = qemu_get_byte(f); - s->curr_status.pmcw.mbi = qemu_get_be16(f); - s->curr_status.pmcw.pom = qemu_get_byte(f); - s->curr_status.pmcw.pam = qemu_get_byte(f); - qemu_get_buffer(f, s->curr_status.pmcw.chpid, 8); - s->curr_status.pmcw.chars = qemu_get_be32(f); - /* SCSW */ - s->curr_status.scsw.flags = qemu_get_be16(f); - s->curr_status.scsw.ctrl = qemu_get_be16(f); - s->curr_status.scsw.cpa = qemu_get_be32(f); - s->curr_status.scsw.dstat = qemu_get_byte(f); - s->curr_status.scsw.cstat = qemu_get_byte(f); - s->curr_status.scsw.count = qemu_get_be16(f); - s->curr_status.mba = qemu_get_be64(f); - qemu_get_buffer(f, s->curr_status.mda, 4); - /* end SCHIB */ - qemu_get_buffer(f, s->sense_data, 32); - s->channel_prog = qemu_get_be64(f); - /* last cmd */ - s->last_cmd.cmd_code = qemu_get_byte(f); - s->last_cmd.flags = qemu_get_byte(f); - s->last_cmd.count = qemu_get_be16(f); - s->last_cmd.cda = qemu_get_be32(f); - s->last_cmd_valid = qemu_get_byte(f); - s->id.reserved = qemu_get_byte(f); - s->id.cu_type = qemu_get_be16(f); - s->id.cu_model = qemu_get_byte(f); - s->id.dev_type = qemu_get_be16(f); - s->id.dev_model = qemu_get_byte(f); - s->id.unused = qemu_get_byte(f); - for (i = 0; i < ARRAY_SIZE(s->id.ciw); i++) { - s->id.ciw[i].type = qemu_get_byte(f); - s->id.ciw[i].command = qemu_get_byte(f); - s->id.ciw[i].count = qemu_get_be16(f); - } - s->ccw_fmt_1 = qemu_get_byte(f); - s->ccw_no_data_cnt = qemu_get_byte(f); - /* - * Hack alert. We don't migrate the channel subsystem status (no - * device!), but we need to find out if the guest enabled mss/mcss-e. - * If the subchannel is enabled, it certainly was able to access it, - * so adjust the max_ssid/max_cssid values for relevant ssid/cssid - * values. This is not watertight, but better than nothing. - */ - if (s->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ENA) { - if (s->ssid) { - channel_subsys.max_ssid = MAX_SSID; - } - if (s->cssid != channel_subsys.default_cssid) { - channel_subsys.max_cssid = MAX_CSSID; - } - } - return 0; -} - void css_reset_sch(SubchDev *sch) { PMCW *p = &sch->curr_status.pmcw; @@ -1931,7 +2253,7 @@ out: g_free(str); } -PropertyInfo css_devid_propinfo = { +const PropertyInfo css_devid_propinfo = { .name = "str", .description = "Identifier of an I/O device in the channel " "subsystem, example: fe.1.23ab", @@ -1939,35 +2261,66 @@ PropertyInfo css_devid_propinfo = { .set = set_css_devid, }; -PropertyInfo css_devid_ro_propinfo = { +const PropertyInfo css_devid_ro_propinfo = { .name = "str", .description = "Read-only identifier of an I/O device in the channel " "subsystem, example: fe.1.23ab", .get = get_css_devid, }; -SubchDev *css_create_virtual_sch(CssDevId bus_id, Error **errp) +SubchDev *css_create_sch(CssDevId bus_id, bool is_virtual, bool squash_mcss, + Error **errp) { uint16_t schid = 0; SubchDev *sch; if (bus_id.valid) { - /* Enforce use of virtual cssid. */ - if (bus_id.cssid != VIRTUAL_CSSID) { - error_setg(errp, "cssid %hhx not valid for virtual devices", - bus_id.cssid); + if (is_virtual != (bus_id.cssid == VIRTUAL_CSSID)) { + error_setg(errp, "cssid %hhx not valid for %s devices", + bus_id.cssid, + (is_virtual ? "virtual" : "non-virtual")); return NULL; } + } + + if (bus_id.valid) { + if (squash_mcss) { + bus_id.cssid = channel_subsys.default_cssid; + } else if (!channel_subsys.css[bus_id.cssid]) { + css_create_css_image(bus_id.cssid, false); + } + if (!css_find_free_subch_for_devno(bus_id.cssid, bus_id.ssid, bus_id.devid, &schid, errp)) { return NULL; } - } else { - bus_id.cssid = VIRTUAL_CSSID; + } else if (squash_mcss || is_virtual) { + bus_id.cssid = channel_subsys.default_cssid; + if (!css_find_free_subch_and_devno(bus_id.cssid, &bus_id.ssid, &bus_id.devid, &schid, errp)) { return NULL; } + } else { + for (bus_id.cssid = 0; bus_id.cssid < MAX_CSSID; ++bus_id.cssid) { + if (bus_id.cssid == VIRTUAL_CSSID) { + continue; + } + + if (!channel_subsys.css[bus_id.cssid]) { + css_create_css_image(bus_id.cssid, false); + } + + if (css_find_free_subch_and_devno(bus_id.cssid, &bus_id.ssid, + &bus_id.devid, &schid, + NULL)) { + break; + } + if (bus_id.cssid == MAX_CSSID) { + error_setg(errp, "Virtual channel subsystem is full!"); + return NULL; + } + } } sch = g_malloc0(sizeof(*sch)); @@ -1978,3 +2331,147 @@ SubchDev *css_create_virtual_sch(CssDevId bus_id, Error **errp) css_subch_assign(sch->cssid, sch->ssid, schid, sch->devno, sch); return sch; } + +static int css_sch_get_chpids(SubchDev *sch, CssDevId *dev_id) +{ + char *fid_path; + FILE *fd; + uint32_t chpid[8]; + int i; + PMCW *p = &sch->curr_status.pmcw; + + fid_path = g_strdup_printf("/sys/bus/css/devices/%x.%x.%04x/chpids", + dev_id->cssid, dev_id->ssid, dev_id->devid); + fd = fopen(fid_path, "r"); + if (fd == NULL) { + error_report("%s: open %s failed", __func__, fid_path); + g_free(fid_path); + return -EINVAL; + } + + if (fscanf(fd, "%x %x %x %x %x %x %x %x", + &chpid[0], &chpid[1], &chpid[2], &chpid[3], + &chpid[4], &chpid[5], &chpid[6], &chpid[7]) != 8) { + fclose(fd); + g_free(fid_path); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(p->chpid); i++) { + p->chpid[i] = chpid[i]; + } + + fclose(fd); + g_free(fid_path); + + return 0; +} + +static int css_sch_get_path_masks(SubchDev *sch, CssDevId *dev_id) +{ + char *fid_path; + FILE *fd; + uint32_t pim, pam, pom; + PMCW *p = &sch->curr_status.pmcw; + + fid_path = g_strdup_printf("/sys/bus/css/devices/%x.%x.%04x/pimpampom", + dev_id->cssid, dev_id->ssid, dev_id->devid); + fd = fopen(fid_path, "r"); + if (fd == NULL) { + error_report("%s: open %s failed", __func__, fid_path); + g_free(fid_path); + return -EINVAL; + } + + if (fscanf(fd, "%x %x %x", &pim, &pam, &pom) != 3) { + fclose(fd); + g_free(fid_path); + return -EINVAL; + } + + p->pim = pim; + p->pam = pam; + p->pom = pom; + fclose(fd); + g_free(fid_path); + + return 0; +} + +static int css_sch_get_chpid_type(uint8_t chpid, uint32_t *type, + CssDevId *dev_id) +{ + char *fid_path; + FILE *fd; + + fid_path = g_strdup_printf("/sys/devices/css%x/chp0.%02x/type", + dev_id->cssid, chpid); + fd = fopen(fid_path, "r"); + if (fd == NULL) { + error_report("%s: open %s failed", __func__, fid_path); + g_free(fid_path); + return -EINVAL; + } + + if (fscanf(fd, "%x", type) != 1) { + fclose(fd); + g_free(fid_path); + return -EINVAL; + } + + fclose(fd); + g_free(fid_path); + + return 0; +} + +/* + * We currently retrieve the real device information from sysfs to build the + * guest subchannel information block without considering the migration feature. + * We need to revisit this problem when we want to add migration support. + */ +int css_sch_build_schib(SubchDev *sch, CssDevId *dev_id) +{ + CssImage *css = channel_subsys.css[sch->cssid]; + PMCW *p = &sch->curr_status.pmcw; + SCSW *s = &sch->curr_status.scsw; + uint32_t type; + int i, ret; + + assert(css != NULL); + memset(p, 0, sizeof(PMCW)); + p->flags |= PMCW_FLAGS_MASK_DNV; + /* We are dealing with I/O subchannels only. */ + p->devno = sch->devno; + + /* Grab path mask from sysfs. */ + ret = css_sch_get_path_masks(sch, dev_id); + if (ret) { + return ret; + } + + /* Grab chpids from sysfs. */ + ret = css_sch_get_chpids(sch, dev_id); + if (ret) { + return ret; + } + + /* Build chpid type. */ + for (i = 0; i < ARRAY_SIZE(p->chpid); i++) { + if (p->chpid[i] && !css->chpids[p->chpid[i]].in_use) { + ret = css_sch_get_chpid_type(p->chpid[i], &type, dev_id); + if (ret) { + return ret; + } + css_add_chpid(sch->cssid, p->chpid[i], type, false); + } + } + + memset(s, 0, sizeof(SCSW)); + sch->curr_status.mba = 0; + for (i = 0; i < ARRAY_SIZE(sch->curr_status.mda); i++) { + sch->curr_status.mda[i] = 0; + } + + return 0; +} diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index 75d3c681a4..cc360031ef 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -396,7 +396,7 @@ void s390_reipl_request(void) S390IPLState *ipl = get_ipl_device(); ipl->reipl_requested = true; - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); } void s390_ipl_prepare_cpu(S390CPU *cpu) @@ -418,7 +418,7 @@ void s390_ipl_prepare_cpu(S390CPU *cpu) error_report_err(err); vm_stop(RUN_STATE_INTERNAL_ERROR); } - ipl->iplb.ccw.netboot_start_addr = ipl->start_addr; + ipl->iplb.ccw.netboot_start_addr = cpu_to_be64(ipl->start_addr); } } diff --git a/hw/s390x/s390-ccw.c b/hw/s390x/s390-ccw.c new file mode 100644 index 0000000000..8614dda6f8 --- /dev/null +++ b/hw/s390x/s390-ccw.c @@ -0,0 +1,153 @@ +/* + * s390 CCW Assignment Support + * + * Copyright 2017 IBM Corp + * Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com> + * Xiao Feng Ren <renxiaof@linux.vnet.ibm.com> + * Pierre Morel <pmorel@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 "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/sysbus.h" +#include "libgen.h" +#include "hw/s390x/css.h" +#include "hw/s390x/css-bridge.h" +#include "hw/s390x/s390-ccw.h" + +int s390_ccw_cmd_request(ORB *orb, SCSW *scsw, void *data) +{ + S390CCWDeviceClass *cdc = S390_CCW_DEVICE_GET_CLASS(data); + + if (cdc->handle_request) { + return cdc->handle_request(orb, scsw, data); + } else { + return -ENOSYS; + } +} + +static void s390_ccw_get_dev_info(S390CCWDevice *cdev, + char *sysfsdev, + Error **errp) +{ + unsigned int cssid, ssid, devid; + char dev_path[PATH_MAX] = {0}, *tmp; + + if (!sysfsdev) { + error_setg(errp, "No host device provided"); + error_append_hint(errp, + "Use -device vfio-ccw,sysfsdev=PATH_TO_DEVICE\n"); + return; + } + + if (!realpath(sysfsdev, dev_path)) { + error_setg_errno(errp, errno, "Host device '%s' not found", sysfsdev); + return; + } + + cdev->mdevid = g_strdup(basename(dev_path)); + + tmp = basename(dirname(dev_path)); + if (sscanf(tmp, "%2x.%1x.%4x", &cssid, &ssid, &devid) != 3) { + error_setg_errno(errp, errno, "Failed to read %s", tmp); + return; + } + + cdev->hostid.cssid = cssid; + cdev->hostid.ssid = ssid; + cdev->hostid.devid = devid; + cdev->hostid.valid = true; +} + +static void s390_ccw_realize(S390CCWDevice *cdev, char *sysfsdev, Error **errp) +{ + CcwDevice *ccw_dev = CCW_DEVICE(cdev); + CCWDeviceClass *ck = CCW_DEVICE_GET_CLASS(ccw_dev); + DeviceState *parent = DEVICE(ccw_dev); + BusState *qbus = qdev_get_parent_bus(parent); + VirtualCssBus *cbus = VIRTUAL_CSS_BUS(qbus); + SubchDev *sch; + int ret; + Error *err = NULL; + + s390_ccw_get_dev_info(cdev, sysfsdev, &err); + if (err) { + goto out_err_propagate; + } + + sch = css_create_sch(ccw_dev->devno, false, cbus->squash_mcss, &err); + if (!sch) { + goto out_mdevid_free; + } + sch->driver_data = cdev; + sch->do_subchannel_work = do_subchannel_work_passthrough; + + ccw_dev->sch = sch; + ret = css_sch_build_schib(sch, &cdev->hostid); + if (ret) { + error_setg_errno(&err, -ret, "%s: Failed to build initial schib", + __func__); + goto out_err; + } + + ck->realize(ccw_dev, &err); + if (err) { + goto out_err; + } + + css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, + parent->hotplugged, 1); + return; + +out_err: + css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); + ccw_dev->sch = NULL; + g_free(sch); +out_mdevid_free: + g_free(cdev->mdevid); +out_err_propagate: + error_propagate(errp, err); +} + +static void s390_ccw_unrealize(S390CCWDevice *cdev, Error **errp) +{ + CcwDevice *ccw_dev = CCW_DEVICE(cdev); + SubchDev *sch = ccw_dev->sch; + + if (sch) { + css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); + g_free(sch); + ccw_dev->sch = NULL; + } + + g_free(cdev->mdevid); +} + +static void s390_ccw_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + S390CCWDeviceClass *cdc = S390_CCW_DEVICE_CLASS(klass); + + dc->bus_type = TYPE_VIRTUAL_CSS_BUS; + cdc->realize = s390_ccw_realize; + cdc->unrealize = s390_ccw_unrealize; +} + +static const TypeInfo s390_ccw_info = { + .name = TYPE_S390_CCW, + .parent = TYPE_CCW_DEVICE, + .instance_size = sizeof(S390CCWDevice), + .class_size = sizeof(S390CCWDeviceClass), + .class_init = s390_ccw_class_init, + .abstract = true, +}; + +static void register_s390_ccw_type(void) +{ + type_register_static(&s390_ccw_info); +} + +type_init(register_s390_ccw_type) diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 66a6fbeb8c..61cfd2138f 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -356,8 +356,8 @@ out: return pte; } -static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *mr, hwaddr addr, - bool is_write) +static IOMMUTLBEntry s390_translate_iommu(IOMMUMemoryRegion *mr, hwaddr addr, + IOMMUAccessFlags flag) { uint64_t pte; uint32_t flags; @@ -407,10 +407,6 @@ static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *mr, hwaddr addr, return ret; } -static const MemoryRegionIOMMUOps s390_iommu_ops = { - .translate = s390_translate_iommu, -}; - static S390PCIIOMMU *s390_pci_get_iommu(S390pciState *s, PCIBus *bus, int devfn) { @@ -504,7 +500,7 @@ static void s390_msi_ctrl_write(void *opaque, hwaddr addr, uint64_t data, 0x80 >> ((ind_bit + vec) % 8)); if (!set_ind_atomic(pbdev->routes.adapter.summary_addr + sum_bit / 8, 0x80 >> (sum_bit % 8))) { - css_adapter_interrupt(pbdev->isc); + css_adapter_interrupt(CSS_IO_ADAPTER_PCI, pbdev->isc); } } @@ -522,17 +518,18 @@ static const MemoryRegionOps s390_msi_ctrl_ops = { void s390_pci_iommu_enable(S390PCIIOMMU *iommu) { char *name = g_strdup_printf("iommu-s390-%04x", iommu->pbdev->uid); - memory_region_init_iommu(&iommu->iommu_mr, OBJECT(&iommu->mr), - &s390_iommu_ops, name, iommu->pal + 1); + memory_region_init_iommu(&iommu->iommu_mr, sizeof(iommu->iommu_mr), + TYPE_S390_IOMMU_MEMORY_REGION, OBJECT(&iommu->mr), + name, iommu->pal + 1); iommu->enabled = true; - memory_region_add_subregion(&iommu->mr, 0, &iommu->iommu_mr); + memory_region_add_subregion(&iommu->mr, 0, MEMORY_REGION(&iommu->iommu_mr)); g_free(name); } void s390_pci_iommu_disable(S390PCIIOMMU *iommu) { iommu->enabled = false; - memory_region_del_subregion(&iommu->mr, &iommu->iommu_mr); + memory_region_del_subregion(&iommu->mr, MEMORY_REGION(&iommu->iommu_mr)); object_unparent(OBJECT(&iommu->iommu_mr)); } @@ -582,7 +579,8 @@ static int s390_pcihost_init(SysBusDevice *dev) QTAILQ_INIT(&s->pending_sei); QTAILQ_INIT(&s->zpci_devs); - css_register_io_adapters(CSS_IO_ADAPTER_PCI, true, false, &error_abort); + css_register_io_adapters(CSS_IO_ADAPTER_PCI, true, false, + S390_ADAPTER_SUPPRESSIBLE, &error_abort); return 0; } @@ -1018,7 +1016,7 @@ static void s390_pci_set_fid(Object *obj, Visitor *v, const char *name, zpci->fid_defined = true; } -static PropertyInfo s390_pci_fid_propinfo = { +static const PropertyInfo s390_pci_fid_propinfo = { .name = "zpci_fid", .get = s390_pci_get_fid, .set = s390_pci_set_fid, @@ -1058,12 +1056,26 @@ static TypeInfo s390_pci_iommu_info = { .instance_size = sizeof(S390PCIIOMMU), }; +static void s390_iommu_memory_region_class_init(ObjectClass *klass, void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = s390_translate_iommu; +} + +static const TypeInfo s390_iommu_memory_region_info = { + .parent = TYPE_IOMMU_MEMORY_REGION, + .name = TYPE_S390_IOMMU_MEMORY_REGION, + .class_init = s390_iommu_memory_region_class_init, +}; + static void s390_pci_register_types(void) { type_register_static(&s390_pcihost_info); type_register_static(&s390_pcibus_info); type_register_static(&s390_pci_device_info); type_register_static(&s390_pci_iommu_info); + type_register_static(&s390_iommu_memory_region_info); } type_init(s390_pci_register_types) diff --git a/hw/s390x/s390-pci-bus.h b/hw/s390x/s390-pci-bus.h index cf142a3e68..67af2c12ff 100644 --- a/hw/s390x/s390-pci-bus.h +++ b/hw/s390x/s390-pci-bus.h @@ -24,6 +24,7 @@ #define TYPE_S390_PCI_BUS "s390-pcibus" #define TYPE_S390_PCI_DEVICE "zpci" #define TYPE_S390_PCI_IOMMU "s390-pci-iommu" +#define TYPE_S390_IOMMU_MEMORY_REGION "s390-iommu-memory-region" #define FH_MASK_ENABLE 0x80000000 #define FH_MASK_INSTANCE 0x7f000000 #define FH_MASK_SHM 0x00ff0000 @@ -266,7 +267,7 @@ typedef struct S390PCIIOMMU { S390PCIBusDevice *pbdev; AddressSpace as; MemoryRegion mr; - MemoryRegion iommu_mr; + IOMMUMemoryRegion iommu_mr; bool enabled; uint64_t g_iota; uint64_t pba; diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index 314a9cbad4..b7beb8c36a 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -563,7 +563,8 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) S390PCIIOMMU *iommu; hwaddr start, end; IOMMUTLBEntry entry; - MemoryRegion *mr; + IOMMUMemoryRegion *iommu_mr; + IOMMUMemoryRegionClass *imrc; cpu_synchronize_state(CPU(cpu)); @@ -622,9 +623,11 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) goto out; } - mr = &iommu->iommu_mr; + iommu_mr = &iommu->iommu_mr; + imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr); + while (start < end) { - entry = mr->iommu_ops->translate(mr, start, 0); + entry = imrc->translate(iommu_mr, start, IOMMU_NONE); if (!entry.translated_addr) { pbdev->state = ZPCI_FS_ERROR; @@ -635,7 +638,7 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) goto out; } - memory_region_notify_iommu(mr, entry); + memory_region_notify_iommu(iommu_mr, entry); start += entry.addr_mask + 1; } diff --git a/hw/s390x/s390-skeys.c b/hw/s390x/s390-skeys.c index e2d4e1af79..c0de3b0c35 100644 --- a/hw/s390x/s390-skeys.c +++ b/hw/s390x/s390-skeys.c @@ -12,10 +12,10 @@ #include "qemu/osdep.h" #include "hw/boards.h" #include "qmp-commands.h" -#include "migration/qemu-file.h" #include "hw/s390x/storage-keys.h" #include "qemu/error-report.h" #include "sysemu/kvm.h" +#include "migration/register.h" #define S390_SKEYS_BUFFER_SIZE 131072 /* Room for 128k storage keys */ #define S390_SKEYS_SAVE_FLAG_EOS 0x01 @@ -363,6 +363,11 @@ static inline bool s390_skeys_get_migration_enabled(Object *obj, Error **errp) return ss->migration_enabled; } +static SaveVMHandlers savevm_s390_storage_keys = { + .save_state = s390_storage_keys_save, + .load_state = s390_storage_keys_load, +}; + static inline void s390_skeys_set_migration_enabled(Object *obj, bool value, Error **errp) { @@ -376,8 +381,8 @@ static inline void s390_skeys_set_migration_enabled(Object *obj, bool value, ss->migration_enabled = value; if (ss->migration_enabled) { - register_savevm(NULL, TYPE_S390_SKEYS, 0, 1, s390_storage_keys_save, - s390_storage_keys_load, ss); + register_savevm_live(NULL, TYPE_S390_SKEYS, 0, 1, + &savevm_s390_storage_keys, ss); } else { unregister_savevm(DEVICE(ss), TYPE_S390_SKEYS, ss); } diff --git a/hw/s390x/s390-stattrib-kvm.c b/hw/s390x/s390-stattrib-kvm.c new file mode 100644 index 0000000000..ff3f89fd2d --- /dev/null +++ b/hw/s390x/s390-stattrib-kvm.c @@ -0,0 +1,190 @@ +/* + * s390 storage attributes device -- KVM object + * + * Copyright 2016 IBM Corp. + * Author(s): Claudio Imbrenda <imbrenda@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 "qemu/osdep.h" +#include "hw/boards.h" +#include "migration/qemu-file.h" +#include "hw/s390x/storage-attributes.h" +#include "qemu/error-report.h" +#include "sysemu/kvm.h" +#include "exec/ram_addr.h" +#include "cpu.h" + +Object *kvm_s390_stattrib_create(void) +{ + if (kvm_enabled() && + kvm_check_extension(kvm_state, KVM_CAP_S390_CMMA_MIGRATION)) { + return object_new(TYPE_KVM_S390_STATTRIB); + } + return NULL; +} + +static void kvm_s390_stattrib_instance_init(Object *obj) +{ + KVMS390StAttribState *sas = KVM_S390_STATTRIB(obj); + + sas->still_dirty = 0; +} + +static int kvm_s390_stattrib_read_helper(S390StAttribState *sa, + uint64_t *start_gfn, + uint32_t count, + uint8_t *values, + uint32_t flags) +{ + KVMS390StAttribState *sas = KVM_S390_STATTRIB(sa); + int r; + struct kvm_s390_cmma_log clog = { + .values = (uint64_t)values, + .start_gfn = *start_gfn, + .count = count, + .flags = flags, + }; + + r = kvm_vm_ioctl(kvm_state, KVM_S390_GET_CMMA_BITS, &clog); + if (r < 0) { + error_report("KVM_S390_GET_CMMA_BITS failed: %s", strerror(-r)); + return r; + } + + *start_gfn = clog.start_gfn; + sas->still_dirty = clog.remaining; + return clog.count; +} + +static int kvm_s390_stattrib_get_stattr(S390StAttribState *sa, + uint64_t *start_gfn, + uint32_t count, + uint8_t *values) +{ + return kvm_s390_stattrib_read_helper(sa, start_gfn, count, values, 0); +} + +static int kvm_s390_stattrib_peek_stattr(S390StAttribState *sa, + uint64_t start_gfn, + uint32_t count, + uint8_t *values) +{ + return kvm_s390_stattrib_read_helper(sa, &start_gfn, count, values, + KVM_S390_CMMA_PEEK); +} + +static int kvm_s390_stattrib_set_stattr(S390StAttribState *sa, + uint64_t start_gfn, + uint32_t count, + uint8_t *values) +{ + KVMS390StAttribState *sas = KVM_S390_STATTRIB(sa); + MachineState *machine = MACHINE(qdev_get_machine()); + unsigned long max = machine->maxram_size / TARGET_PAGE_SIZE; + + if (start_gfn + count > max) { + error_report("Out of memory bounds when setting storage attributes"); + return -1; + } + if (!sas->incoming_buffer) { + sas->incoming_buffer = g_malloc0(max); + } + + memcpy(sas->incoming_buffer + start_gfn, values, count); + + return 0; +} + +static void kvm_s390_stattrib_synchronize(S390StAttribState *sa) +{ + KVMS390StAttribState *sas = KVM_S390_STATTRIB(sa); + MachineState *machine = MACHINE(qdev_get_machine()); + unsigned long max = machine->maxram_size / TARGET_PAGE_SIZE; + unsigned long cx, len = 1 << 19; + int r; + struct kvm_s390_cmma_log clog = { + .flags = 0, + .mask = ~0ULL, + }; + + if (sas->incoming_buffer) { + for (cx = 0; cx + len <= max; cx += len) { + clog.start_gfn = cx; + clog.count = len; + clog.values = (uint64_t)(sas->incoming_buffer + cx * len); + r = kvm_vm_ioctl(kvm_state, KVM_S390_SET_CMMA_BITS, &clog); + if (r) { + error_report("KVM_S390_SET_CMMA_BITS failed: %s", strerror(-r)); + return; + } + } + if (cx < max) { + clog.start_gfn = cx; + clog.count = max - cx; + clog.values = (uint64_t)(sas->incoming_buffer + cx * len); + r = kvm_vm_ioctl(kvm_state, KVM_S390_SET_CMMA_BITS, &clog); + if (r) { + error_report("KVM_S390_SET_CMMA_BITS failed: %s", strerror(-r)); + } + } + g_free(sas->incoming_buffer); + sas->incoming_buffer = NULL; + } +} + +static int kvm_s390_stattrib_set_migrationmode(S390StAttribState *sa, bool val) +{ + struct kvm_device_attr attr = { + .group = KVM_S390_VM_MIGRATION, + .attr = val, + .addr = 0, + }; + return kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr); +} + +static long long kvm_s390_stattrib_get_dirtycount(S390StAttribState *sa) +{ + KVMS390StAttribState *sas = KVM_S390_STATTRIB(sa); + uint8_t val[8]; + + kvm_s390_stattrib_peek_stattr(sa, 0, 1, val); + return sas->still_dirty; +} + +static int kvm_s390_stattrib_get_active(S390StAttribState *sa) +{ + return kvm_s390_cmma_active() && sa->migration_enabled; +} + +static void kvm_s390_stattrib_class_init(ObjectClass *oc, void *data) +{ + S390StAttribClass *sac = S390_STATTRIB_CLASS(oc); + + sac->get_stattr = kvm_s390_stattrib_get_stattr; + sac->peek_stattr = kvm_s390_stattrib_peek_stattr; + sac->set_stattr = kvm_s390_stattrib_set_stattr; + sac->set_migrationmode = kvm_s390_stattrib_set_migrationmode; + sac->get_dirtycount = kvm_s390_stattrib_get_dirtycount; + sac->synchronize = kvm_s390_stattrib_synchronize; + sac->get_active = kvm_s390_stattrib_get_active; +} + +static const TypeInfo kvm_s390_stattrib_info = { + .name = TYPE_KVM_S390_STATTRIB, + .parent = TYPE_S390_STATTRIB, + .instance_init = kvm_s390_stattrib_instance_init, + .instance_size = sizeof(KVMS390StAttribState), + .class_init = kvm_s390_stattrib_class_init, + .class_size = sizeof(S390StAttribClass), +}; + +static void kvm_s390_stattrib_register_types(void) +{ + type_register_static(&kvm_s390_stattrib_info); +} + +type_init(kvm_s390_stattrib_register_types) diff --git a/hw/s390x/s390-stattrib.c b/hw/s390x/s390-stattrib.c new file mode 100644 index 0000000000..d14923f099 --- /dev/null +++ b/hw/s390x/s390-stattrib.c @@ -0,0 +1,404 @@ +/* + * s390 storage attributes device + * + * Copyright 2016 IBM Corp. + * Author(s): Claudio Imbrenda <imbrenda@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 "qemu/osdep.h" +#include "hw/boards.h" +#include "qmp-commands.h" +#include "migration/qemu-file.h" +#include "migration/register.h" +#include "hw/s390x/storage-attributes.h" +#include "qemu/error-report.h" +#include "sysemu/kvm.h" +#include "exec/ram_addr.h" +#include "qapi/error.h" + +#define CMMA_BLOCK_SIZE (1 << 10) + +#define STATTR_FLAG_EOS 0x01ULL +#define STATTR_FLAG_MORE 0x02ULL +#define STATTR_FLAG_ERROR 0x04ULL +#define STATTR_FLAG_DONE 0x08ULL + +static S390StAttribState *s390_get_stattrib_device(void) +{ + S390StAttribState *sas; + + sas = S390_STATTRIB(object_resolve_path_type("", TYPE_S390_STATTRIB, NULL)); + assert(sas); + return sas; +} + +void s390_stattrib_init(void) +{ + Object *obj; + + obj = kvm_s390_stattrib_create(); + if (!obj) { + obj = object_new(TYPE_QEMU_S390_STATTRIB); + } + + object_property_add_child(qdev_get_machine(), TYPE_S390_STATTRIB, + obj, NULL); + object_unref(obj); + + qdev_init_nofail(DEVICE(obj)); +} + +/* Console commands: */ + +void hmp_migrationmode(Monitor *mon, const QDict *qdict) +{ + S390StAttribState *sas = s390_get_stattrib_device(); + S390StAttribClass *sac = S390_STATTRIB_GET_CLASS(sas); + uint64_t what = qdict_get_int(qdict, "mode"); + int r; + + r = sac->set_migrationmode(sas, what); + if (r < 0) { + monitor_printf(mon, "Error: %s", strerror(-r)); + } +} + +void hmp_info_cmma(Monitor *mon, const QDict *qdict) +{ + S390StAttribState *sas = s390_get_stattrib_device(); + S390StAttribClass *sac = S390_STATTRIB_GET_CLASS(sas); + uint64_t addr = qdict_get_int(qdict, "addr"); + uint64_t buflen = qdict_get_try_int(qdict, "count", 8); + uint8_t *vals; + int cx, len; + + vals = g_try_malloc(buflen); + if (!vals) { + monitor_printf(mon, "Error: %s\n", strerror(errno)); + return; + } + + len = sac->peek_stattr(sas, addr / TARGET_PAGE_SIZE, buflen, vals); + if (len < 0) { + monitor_printf(mon, "Error: %s", strerror(-len)); + goto out; + } + + monitor_printf(mon, " CMMA attributes, " + "pages %" PRIu64 "+%d (0x%" PRIx64 "):\n", + addr / TARGET_PAGE_SIZE, len, addr & ~TARGET_PAGE_MASK); + for (cx = 0; cx < len; cx++) { + if (cx % 8 == 7) { + monitor_printf(mon, "%02x\n", vals[cx]); + } else { + monitor_printf(mon, "%02x", vals[cx]); + } + } + monitor_printf(mon, "\n"); + +out: + g_free(vals); +} + +/* Migration support: */ + +static int cmma_load(QEMUFile *f, void *opaque, int version_id) +{ + S390StAttribState *sas = S390_STATTRIB(opaque); + S390StAttribClass *sac = S390_STATTRIB_GET_CLASS(sas); + uint64_t count, cur_gfn; + int flags, ret = 0; + ram_addr_t addr; + uint8_t *buf; + + while (!ret) { + addr = qemu_get_be64(f); + flags = addr & ~TARGET_PAGE_MASK; + addr &= TARGET_PAGE_MASK; + + switch (flags) { + case STATTR_FLAG_MORE: { + cur_gfn = addr / TARGET_PAGE_SIZE; + count = qemu_get_be64(f); + buf = g_try_malloc(count); + if (!buf) { + error_report("cmma_load could not allocate memory"); + ret = -ENOMEM; + break; + } + + qemu_get_buffer(f, buf, count); + ret = sac->set_stattr(sas, cur_gfn, count, buf); + if (ret < 0) { + error_report("Error %d while setting storage attributes", ret); + } + g_free(buf); + break; + } + case STATTR_FLAG_ERROR: { + error_report("Storage attributes data is incomplete"); + ret = -EINVAL; + break; + } + case STATTR_FLAG_DONE: + /* This is after the last pre-copied value has been sent, nothing + * more will be sent after this. Pre-copy has finished, and we + * are done flushing all the remaining values. Now the target + * system is about to take over. We synchronize the buffer to + * apply the actual correct values where needed. + */ + sac->synchronize(sas); + break; + case STATTR_FLAG_EOS: + /* Normal exit */ + return 0; + default: + error_report("Unexpected storage attribute flag data: %#x", flags); + ret = -EINVAL; + } + } + + return ret; +} + +static int cmma_save_setup(QEMUFile *f, void *opaque) +{ + S390StAttribState *sas = S390_STATTRIB(opaque); + S390StAttribClass *sac = S390_STATTRIB_GET_CLASS(sas); + int res; + /* + * Signal that we want to start a migration, thus needing PGSTE dirty + * tracking. + */ + res = sac->set_migrationmode(sas, 1); + if (res) { + return res; + } + qemu_put_be64(f, STATTR_FLAG_EOS); + return 0; +} + +static void cmma_save_pending(QEMUFile *f, void *opaque, uint64_t max_size, + uint64_t *non_postcopiable_pending, + uint64_t *postcopiable_pending) +{ + S390StAttribState *sas = S390_STATTRIB(opaque); + S390StAttribClass *sac = S390_STATTRIB_GET_CLASS(sas); + long long res = sac->get_dirtycount(sas); + + if (res >= 0) { + *non_postcopiable_pending += res; + } +} + +static int cmma_save(QEMUFile *f, void *opaque, int final) +{ + S390StAttribState *sas = S390_STATTRIB(opaque); + S390StAttribClass *sac = S390_STATTRIB_GET_CLASS(sas); + uint8_t *buf; + int r, cx, reallen = 0, ret = 0; + uint32_t buflen = 1 << 19; /* 512kB cover 2GB of guest memory */ + uint64_t start_gfn = sas->migration_cur_gfn; + + buf = g_try_malloc(buflen); + if (!buf) { + error_report("Could not allocate memory to save storage attributes"); + return -ENOMEM; + } + + while (final ? 1 : qemu_file_rate_limit(f) == 0) { + reallen = sac->get_stattr(sas, &start_gfn, buflen, buf); + if (reallen < 0) { + g_free(buf); + return reallen; + } + + ret = 1; + if (!reallen) { + break; + } + qemu_put_be64(f, (start_gfn << TARGET_PAGE_BITS) | STATTR_FLAG_MORE); + qemu_put_be64(f, reallen); + for (cx = 0; cx < reallen; cx++) { + qemu_put_byte(f, buf[cx]); + } + if (!sac->get_dirtycount(sas)) { + break; + } + } + + sas->migration_cur_gfn = start_gfn + reallen; + g_free(buf); + if (final) { + qemu_put_be64(f, STATTR_FLAG_DONE); + } + qemu_put_be64(f, STATTR_FLAG_EOS); + + r = qemu_file_get_error(f); + if (r < 0) { + return r; + } + + return ret; +} + +static int cmma_save_iterate(QEMUFile *f, void *opaque) +{ + return cmma_save(f, opaque, 0); +} + +static int cmma_save_complete(QEMUFile *f, void *opaque) +{ + return cmma_save(f, opaque, 1); +} + +static void cmma_save_cleanup(void *opaque) +{ + S390StAttribState *sas = S390_STATTRIB(opaque); + S390StAttribClass *sac = S390_STATTRIB_GET_CLASS(sas); + sac->set_migrationmode(sas, 0); +} + +static bool cmma_active(void *opaque) +{ + S390StAttribState *sas = S390_STATTRIB(opaque); + S390StAttribClass *sac = S390_STATTRIB_GET_CLASS(sas); + return sac->get_active(sas); +} + +/* QEMU object: */ + +static void qemu_s390_stattrib_instance_init(Object *obj) +{ +} + +static int qemu_s390_peek_stattr_stub(S390StAttribState *sa, uint64_t start_gfn, + uint32_t count, uint8_t *values) +{ + return 0; +} +static void qemu_s390_synchronize_stub(S390StAttribState *sa) +{ +} +static int qemu_s390_get_stattr_stub(S390StAttribState *sa, uint64_t *start_gfn, + uint32_t count, uint8_t *values) +{ + return 0; +} +static long long qemu_s390_get_dirtycount_stub(S390StAttribState *sa) +{ + return 0; +} +static int qemu_s390_set_migrationmode_stub(S390StAttribState *sa, bool value) +{ + return 0; +} + +static int qemu_s390_get_active(S390StAttribState *sa) +{ + return sa->migration_enabled; +} + +static void qemu_s390_stattrib_class_init(ObjectClass *oc, void *data) +{ + S390StAttribClass *sa_cl = S390_STATTRIB_CLASS(oc); + + sa_cl->synchronize = qemu_s390_synchronize_stub; + sa_cl->get_stattr = qemu_s390_get_stattr_stub; + sa_cl->set_stattr = qemu_s390_peek_stattr_stub; + sa_cl->peek_stattr = qemu_s390_peek_stattr_stub; + sa_cl->set_migrationmode = qemu_s390_set_migrationmode_stub; + sa_cl->get_dirtycount = qemu_s390_get_dirtycount_stub; + sa_cl->get_active = qemu_s390_get_active; +} + +static const TypeInfo qemu_s390_stattrib_info = { + .name = TYPE_QEMU_S390_STATTRIB, + .parent = TYPE_S390_STATTRIB, + .instance_init = qemu_s390_stattrib_instance_init, + .instance_size = sizeof(QEMUS390StAttribState), + .class_init = qemu_s390_stattrib_class_init, + .class_size = sizeof(S390StAttribClass), +}; + +/* Generic abstract object: */ + +static void s390_stattrib_realize(DeviceState *dev, Error **errp) +{ + bool ambiguous = false; + + object_resolve_path_type("", TYPE_S390_STATTRIB, &ambiguous); + if (ambiguous) { + error_setg(errp, "storage_attributes device already exists"); + } +} + +static void s390_stattrib_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->hotpluggable = false; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + dc->realize = s390_stattrib_realize; +} + +static inline bool s390_stattrib_get_migration_enabled(Object *obj, Error **e) +{ + S390StAttribState *s = S390_STATTRIB(obj); + + return s->migration_enabled; +} + +static inline void s390_stattrib_set_migration_enabled(Object *obj, bool value, + Error **errp) +{ + S390StAttribState *s = S390_STATTRIB(obj); + + s->migration_enabled = value; +} + +static void s390_stattrib_instance_init(Object *obj) +{ + S390StAttribState *sas = S390_STATTRIB(obj); + SaveVMHandlers *ops; + + /* ops will always be freed by qemu when unregistering */ + ops = g_new0(SaveVMHandlers, 1); + + ops->save_setup = cmma_save_setup; + ops->save_live_iterate = cmma_save_iterate; + ops->save_live_complete_precopy = cmma_save_complete; + ops->save_live_pending = cmma_save_pending; + ops->save_cleanup = cmma_save_cleanup; + ops->load_state = cmma_load; + ops->is_active = cmma_active; + register_savevm_live(NULL, TYPE_S390_STATTRIB, 0, 0, ops, sas); + + object_property_add_bool(obj, "migration-enabled", + s390_stattrib_get_migration_enabled, + s390_stattrib_set_migration_enabled, NULL); + object_property_set_bool(obj, true, "migration-enabled", NULL); + sas->migration_cur_gfn = 0; +} + +static const TypeInfo s390_stattrib_info = { + .name = TYPE_S390_STATTRIB, + .parent = TYPE_DEVICE, + .instance_init = s390_stattrib_instance_init, + .instance_size = sizeof(S390StAttribState), + .class_init = s390_stattrib_class_init, + .class_size = sizeof(S390StAttribClass), + .abstract = true, +}; + +static void s390_stattrib_register_types(void) +{ + type_register_static(&s390_stattrib_info); + type_register_static(&qemu_s390_stattrib_info); +} + +type_init(s390_stattrib_register_types) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index fdd4384ff0..ce3921e4de 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -24,10 +24,13 @@ #include "qemu/config-file.h" #include "s390-pci-bus.h" #include "hw/s390x/storage-keys.h" +#include "hw/s390x/storage-attributes.h" #include "hw/compat.h" #include "ipl.h" #include "hw/s390x/s390-virtio-ccw.h" #include "hw/s390x/css-bridge.h" +#include "migration/register.h" +#include "cpu_models.h" static const char *const reset_dev_types[] = { TYPE_VIRTUAL_CSS_BRIDGE, @@ -102,8 +105,15 @@ void s390_memory_init(ram_addr_t mem_size) /* Initialize storage key device */ s390_skeys_init(); + /* Initialize storage attributes device */ + s390_stattrib_init(); } +static SaveVMHandlers savevm_gtod = { + .save_state = gtod_save, + .load_state = gtod_load, +}; + static void ccw_init(MachineState *machine) { int ret; @@ -113,6 +123,9 @@ static void ccw_init(MachineState *machine) s390_sclp_init(); s390_memory_init(machine->ram_size); + /* init CPUs */ + s390_init_cpus(machine); + s390_flic_init(); /* get a BUS */ @@ -129,25 +142,26 @@ static void ccw_init(MachineState *machine) /* register hypercalls */ virtio_ccw_register_hcalls(); - /* init CPUs */ - s390_init_cpus(machine); - if (kvm_enabled()) { kvm_s390_enable_css_support(s390_cpu_addr2state(0)); } /* - * Create virtual css and set it as default so that non mcss-e - * enabled guests only see virtio devices. + * Non mcss-e enabled guests only see the devices from the default + * css, which is determined by the value of the squash_mcss property. + * Note: we must not squash non virtual devices to css 0xFE. */ - ret = css_create_css_image(VIRTUAL_CSSID, true); + if (css_bus->squash_mcss) { + ret = css_create_css_image(0, true); + } else { + ret = css_create_css_image(VIRTUAL_CSSID, true); + } assert(ret == 0); /* Create VirtIO network adapters */ s390_create_virtio_net(BUS(css_bus), "virtio-net-ccw"); /* Register savevm handler for guest TOD clock */ - register_savevm(NULL, "todclock", 0, 1, - gtod_save, gtod_load, kvm_state); + register_savevm_live(NULL, "todclock", 0, 1, &savevm_gtod, kvm_state); } static void s390_cpu_plug(HotplugHandler *hotplug_dev, @@ -196,6 +210,8 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data) s390mc->ri_allowed = true; s390mc->cpu_model_allowed = true; + s390mc->css_migration_enabled = true; + s390mc->gs_allowed = true; mc->init = ccw_init; mc->reset = s390_machine_reset; mc->hot_add_cpu = s390_hot_add_cpu; @@ -242,36 +258,51 @@ static inline void machine_set_dea_key_wrap(Object *obj, bool value, ms->dea_key_wrap = value; } +static S390CcwMachineClass *current_mc; + +static S390CcwMachineClass *get_machine_class(void) +{ + if (unlikely(!current_mc)) { + /* + * No s390 ccw machine was instantiated, we are likely to + * be called for the 'none' machine. The properties will + * have their after-initialization values. + */ + current_mc = S390_MACHINE_CLASS( + object_class_by_name(TYPE_S390_CCW_MACHINE)); + } + return current_mc; +} + bool ri_allowed(void) { + if (!kvm_enabled()) { + return false; + } + /* for "none" machine this results in true */ + return get_machine_class()->ri_allowed; +} + +bool cpu_model_allowed(void) +{ + /* for "none" machine this results in true */ + return get_machine_class()->cpu_model_allowed; +} + +bool gs_allowed(void) +{ if (kvm_enabled()) { MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); if (object_class_dynamic_cast(OBJECT_CLASS(mc), TYPE_S390_CCW_MACHINE)) { S390CcwMachineClass *s390mc = S390_MACHINE_CLASS(mc); - return s390mc->ri_allowed; + return s390mc->gs_allowed; } - /* - * Make sure the "none" machine can have ri, otherwise it won't * be - * unlocked in KVM and therefore the host CPU model might be wrong. - */ + /* Make sure the "none" machine can have gs */ return true; } - return 0; -} - -bool cpu_model_allowed(void) -{ - MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); - if (object_class_dynamic_cast(OBJECT_CLASS(mc), - TYPE_S390_CCW_MACHINE)) { - S390CcwMachineClass *s390mc = S390_MACHINE_CLASS(mc); - - return s390mc->cpu_model_allowed; - } - /* allow CPU model qmp queries with the "none" machine */ - return true; + return false; } static char *machine_get_loadparm(Object *obj, Error **errp) @@ -303,6 +334,20 @@ static void machine_set_loadparm(Object *obj, const char *val, Error **errp) ms->loadparm[i] = ' '; /* pad right with spaces */ } } +static inline bool machine_get_squash_mcss(Object *obj, Error **errp) +{ + S390CcwMachineState *ms = S390_CCW_MACHINE(obj); + + return ms->s390_squash_mcss; +} + +static inline void machine_set_squash_mcss(Object *obj, bool value, + Error **errp) +{ + S390CcwMachineState *ms = S390_CCW_MACHINE(obj); + + ms->s390_squash_mcss = value; +} static inline void s390_machine_initfn(Object *obj) { @@ -328,6 +373,13 @@ static inline void s390_machine_initfn(Object *obj) " to upper case) to pass to machine loader, boot manager," " and guest kernel", NULL); + object_property_add_bool(obj, "s390-squash-mcss", + machine_get_squash_mcss, + machine_set_squash_mcss, NULL); + object_property_set_description(obj, "s390-squash-mcss", + "enable/disable squashing subchannels into the default css", + NULL); + object_property_set_bool(obj, false, "s390-squash-mcss", NULL); } static const TypeInfo ccw_machine_info = { @@ -345,6 +397,11 @@ static const TypeInfo ccw_machine_info = { }, }; +bool css_migration_enabled(void) +{ + return get_machine_class()->css_migration_enabled; +} + #define DEFINE_CCW_MACHINE(suffix, verstr, latest) \ static void ccw_machine_##suffix##_class_init(ObjectClass *oc, \ void *data) \ @@ -360,6 +417,7 @@ static const TypeInfo ccw_machine_info = { static void ccw_machine_##suffix##_instance_init(Object *obj) \ { \ MachineState *machine = MACHINE(obj); \ + current_mc = S390_MACHINE_CLASS(MACHINE_GET_CLASS(machine)); \ ccw_machine_##suffix##_instance_options(machine); \ } \ static const TypeInfo ccw_machine_##suffix##_info = { \ @@ -375,7 +433,12 @@ static const TypeInfo ccw_machine_info = { type_init(ccw_machine_register_##suffix) #define CCW_COMPAT_2_9 \ - HW_COMPAT_2_9 + HW_COMPAT_2_9 \ + {\ + .driver = TYPE_S390_STATTRIB,\ + .property = "migration-enabled",\ + .value = "off",\ + }, #define CCW_COMPAT_2_8 \ HW_COMPAT_2_8 \ @@ -445,6 +508,9 @@ static const TypeInfo ccw_machine_info = { static void ccw_machine_2_10_instance_options(MachineState *machine) { + if (css_migration_enabled()) { + css_register_vmstate(); + } } static void ccw_machine_2_10_class_options(MachineClass *mc) @@ -455,12 +521,21 @@ DEFINE_CCW_MACHINE(2_10, "2.10", true); static void ccw_machine_2_9_instance_options(MachineState *machine) { ccw_machine_2_10_instance_options(machine); + s390_cpudef_featoff_greater(12, 1, S390_FEAT_ESOP); + s390_cpudef_featoff_greater(12, 1, S390_FEAT_SIDE_EFFECT_ACCESS_ESOP2); + s390_cpudef_featoff_greater(12, 1, S390_FEAT_ZPCI); + s390_cpudef_featoff_greater(12, 1, S390_FEAT_ADAPTER_INT_SUPPRESSION); + s390_cpudef_featoff_greater(12, 1, S390_FEAT_ADAPTER_EVENT_NOTIFICATION); } static void ccw_machine_2_9_class_options(MachineClass *mc) { + S390CcwMachineClass *s390mc = S390_MACHINE_CLASS(mc); + + s390mc->gs_allowed = false; ccw_machine_2_10_class_options(mc); SET_MACHINE_COMPAT(mc, CCW_COMPAT_2_9); + s390mc->css_migration_enabled = false; } DEFINE_CCW_MACHINE(2_9, "2.9", false); diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c index 83d6023894..9253dbbc64 100644 --- a/hw/s390x/sclp.c +++ b/hw/s390x/sclp.c @@ -273,7 +273,6 @@ static void assign_storage(SCLPDevice *sclp, SCCB *sccb) * instead of doing it via the ref count of the MemoryRegion. */ object_ref(OBJECT(standby_ram)); object_unparent(OBJECT(standby_ram)); - vmstate_register_ram_global(standby_ram); memory_region_add_subregion(sysmem, offset, standby_ram); } /* The specified subregion is no longer in standby */ diff --git a/hw/s390x/trace-events b/hw/s390x/trace-events index 84ea964875..f07e974678 100644 --- a/hw/s390x/trace-events +++ b/hw/s390x/trace-events @@ -8,6 +8,7 @@ css_new_image(uint8_t cssid, const char *default_cssid) "CSS: add css image %02x css_assign_subch(const char *do_assign, uint8_t cssid, uint8_t ssid, uint16_t schid, uint16_t devno) "CSS: %s %x.%x.%04x (devno %04x)" css_io_interrupt(int cssid, int ssid, int schid, uint32_t intparm, uint8_t isc, const char *conditional) "CSS: I/O interrupt on sch %x.%x.%04x (intparm %08x, isc %x) %s" css_adapter_interrupt(uint8_t isc) "CSS: adapter I/O interrupt (isc %x)" +css_do_sic(uint16_t mode, uint8_t isc) "CSS: set interruption mode %x on isc %x" # hw/s390x/virtio-ccw.c virtio_ccw_interpret_ccw(int cssid, int ssid, int schid, int cmd_code) "VIRTIO-CCW: %x.%x.%04x: interpret command %x" diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index e7167e3d05..b1976fdd19 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -34,9 +34,87 @@ #include "virtio-ccw.h" #include "trace.h" #include "hw/s390x/css-bridge.h" +#include "hw/s390x/s390-virtio-ccw.h" #define NR_CLASSIC_INDICATOR_BITS 64 +static int virtio_ccw_dev_post_load(void *opaque, int version_id) +{ + VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(opaque); + CcwDevice *ccw_dev = CCW_DEVICE(dev); + CCWDeviceClass *ck = CCW_DEVICE_GET_CLASS(ccw_dev); + + ccw_dev->sch->driver_data = dev; + if (ccw_dev->sch->thinint_active) { + dev->routes.adapter.adapter_id = css_get_adapter_id( + CSS_IO_ADAPTER_VIRTIO, + dev->thinint_isc); + } + /* Re-fill subch_id after loading the subchannel states.*/ + if (ck->refill_ids) { + ck->refill_ids(ccw_dev); + } + return 0; +} + +typedef struct VirtioCcwDeviceTmp { + VirtioCcwDevice *parent; + uint16_t config_vector; +} VirtioCcwDeviceTmp; + +static void virtio_ccw_dev_tmp_pre_save(void *opaque) +{ + VirtioCcwDeviceTmp *tmp = opaque; + VirtioCcwDevice *dev = tmp->parent; + VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); + + tmp->config_vector = vdev->config_vector; +} + +static int virtio_ccw_dev_tmp_post_load(void *opaque, int version_id) +{ + VirtioCcwDeviceTmp *tmp = opaque; + VirtioCcwDevice *dev = tmp->parent; + VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); + + vdev->config_vector = tmp->config_vector; + return 0; +} + +const VMStateDescription vmstate_virtio_ccw_dev_tmp = { + .name = "s390_virtio_ccw_dev_tmp", + .pre_save = virtio_ccw_dev_tmp_pre_save, + .post_load = virtio_ccw_dev_tmp_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT16(config_vector, VirtioCcwDeviceTmp), + VMSTATE_END_OF_LIST() + } +}; + +const VMStateDescription vmstate_virtio_ccw_dev = { + .name = "s390_virtio_ccw_dev", + .version_id = 1, + .minimum_version_id = 1, + .post_load = virtio_ccw_dev_post_load, + .fields = (VMStateField[]) { + VMSTATE_CCW_DEVICE(parent_obj, VirtioCcwDevice), + VMSTATE_PTR_TO_IND_ADDR(indicators, VirtioCcwDevice), + VMSTATE_PTR_TO_IND_ADDR(indicators2, VirtioCcwDevice), + VMSTATE_PTR_TO_IND_ADDR(summary_indicator, VirtioCcwDevice), + /* + * Ugly hack because VirtIODevice does not migrate itself. + * This also makes legacy via vmstate_save_state possible. + */ + VMSTATE_WITH_TMP(VirtioCcwDevice, VirtioCcwDeviceTmp, + vmstate_virtio_ccw_dev_tmp), + VMSTATE_STRUCT(routes, VirtioCcwDevice, 1, vmstate_adapter_routes, + AdapterRoutes), + VMSTATE_UINT8(thinint_isc, VirtioCcwDevice), + VMSTATE_INT32(revision, VirtioCcwDevice), + VMSTATE_END_OF_LIST() + } +}; + static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size, VirtioCcwDevice *dev); @@ -680,9 +758,13 @@ static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp) VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_GET_CLASS(dev); CcwDevice *ccw_dev = CCW_DEVICE(dev); CCWDeviceClass *ck = CCW_DEVICE_GET_CLASS(ccw_dev); - SubchDev *sch = css_create_virtual_sch(ccw_dev->devno, errp); + DeviceState *parent = DEVICE(ccw_dev); + BusState *qbus = qdev_get_parent_bus(parent); + VirtualCssBus *cbus = VIRTUAL_CSS_BUS(qbus); + SubchDev *sch; Error *err = NULL; + sch = css_create_sch(ccw_dev->devno, true, cbus->squash_mcss, errp); if (!sch) { return; } @@ -697,6 +779,7 @@ static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp) sch->disable_cb = virtio_sch_disable_cb; sch->id.reserved = 0xff; sch->id.cu_type = VIRTIO_CCW_CU_TYPE; + sch->do_subchannel_work = do_subchannel_work_virtual; ccw_dev->sch = sch; dev->indicators = NULL; dev->revision = -1; @@ -706,7 +789,7 @@ static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp) sch->cssid, sch->ssid, sch->schid, sch->devno, ccw_dev->devno.valid ? "user-configured" : "auto-configured"); - if (!kvm_eventfds_enabled()) { + if (kvm_enabled() && !kvm_eventfds_enabled()) { dev->flags &= ~VIRTIO_CCW_FLAG_USE_IOEVENTFD; } @@ -784,8 +867,6 @@ static void virtio_ccw_blk_instance_init(Object *obj) virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_BLK); - object_property_add_alias(obj, "iothread", OBJECT(&dev->vdev),"iothread", - &error_abort); object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev), "bootindex", &error_abort); } @@ -869,8 +950,6 @@ static void virtio_ccw_scsi_instance_init(Object *obj) virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_SCSI); - object_property_add_alias(obj, "iothread", OBJECT(&dev->vdev), "iothread", - &error_abort); } #ifdef CONFIG_VHOST_SCSI @@ -991,7 +1070,7 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector) 0x80 >> ((ind_bit + vector) % 8)); if (!virtio_set_ind_atomic(sch, dev->summary_indicator->addr, 0x01)) { - css_adapter_interrupt(dev->thinint_isc); + css_adapter_interrupt(CSS_IO_ADAPTER_VIRTIO, dev->thinint_isc); } } else { indicators = address_space_ldq(&address_space_memory, @@ -1234,85 +1313,13 @@ static int virtio_ccw_load_queue(DeviceState *d, int n, QEMUFile *f) static void virtio_ccw_save_config(DeviceState *d, QEMUFile *f) { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); - CcwDevice *ccw_dev = CCW_DEVICE(d); - SubchDev *s = ccw_dev->sch; - VirtIODevice *vdev = virtio_ccw_get_vdev(s); - - subch_device_save(s, f); - if (dev->indicators != NULL) { - qemu_put_be32(f, dev->indicators->len); - qemu_put_be64(f, dev->indicators->addr); - } else { - qemu_put_be32(f, 0); - qemu_put_be64(f, 0UL); - } - if (dev->indicators2 != NULL) { - qemu_put_be32(f, dev->indicators2->len); - qemu_put_be64(f, dev->indicators2->addr); - } else { - qemu_put_be32(f, 0); - qemu_put_be64(f, 0UL); - } - if (dev->summary_indicator != NULL) { - qemu_put_be32(f, dev->summary_indicator->len); - qemu_put_be64(f, dev->summary_indicator->addr); - } else { - qemu_put_be32(f, 0); - qemu_put_be64(f, 0UL); - } - qemu_put_be16(f, vdev->config_vector); - qemu_put_be64(f, dev->routes.adapter.ind_offset); - qemu_put_byte(f, dev->thinint_isc); - qemu_put_be32(f, dev->revision); + vmstate_save_state(f, &vmstate_virtio_ccw_dev, dev, NULL); } static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f) { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); - CcwDevice *ccw_dev = CCW_DEVICE(d); - CCWDeviceClass *ck = CCW_DEVICE_GET_CLASS(ccw_dev); - SubchDev *s = ccw_dev->sch; - VirtIODevice *vdev = virtio_ccw_get_vdev(s); - int len; - - s->driver_data = dev; - subch_device_load(s, f); - /* Re-fill subch_id after loading the subchannel states.*/ - if (ck->refill_ids) { - ck->refill_ids(ccw_dev); - } - len = qemu_get_be32(f); - if (len != 0) { - dev->indicators = get_indicator(qemu_get_be64(f), len); - } else { - qemu_get_be64(f); - dev->indicators = NULL; - } - len = qemu_get_be32(f); - if (len != 0) { - dev->indicators2 = get_indicator(qemu_get_be64(f), len); - } else { - qemu_get_be64(f); - dev->indicators2 = NULL; - } - len = qemu_get_be32(f); - if (len != 0) { - dev->summary_indicator = get_indicator(qemu_get_be64(f), len); - } else { - qemu_get_be64(f); - dev->summary_indicator = NULL; - } - qemu_get_be16s(f, &vdev->config_vector); - dev->routes.adapter.ind_offset = qemu_get_be64(f); - dev->thinint_isc = qemu_get_byte(f); - dev->revision = qemu_get_be32(f); - if (s->thinint_active) { - dev->routes.adapter.adapter_id = css_get_adapter_id( - CSS_IO_ADAPTER_VIRTIO, - dev->thinint_isc); - } - - return 0; + return vmstate_load_state(f, &vmstate_virtio_ccw_dev, dev, 1); } static void virtio_ccw_pre_plugged(DeviceState *d, Error **errp) @@ -1541,8 +1548,6 @@ static void virtio_ccw_rng_instance_init(Object *obj) virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_RNG); - object_property_add_alias(obj, "rng", OBJECT(&dev->vdev), - "rng", &error_abort); } static Property virtio_ccw_rng_properties[] = { @@ -1589,9 +1594,6 @@ static void virtio_ccw_crypto_instance_init(Object *obj) ccw_dev->force_revision_1 = true; virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_CRYPTO); - - object_property_add_alias(obj, "cryptodev", OBJECT(&dev->vdev), - "cryptodev", &error_abort); } static void virtio_ccw_crypto_class_init(ObjectClass *klass, void *data) diff --git a/hw/scsi/Makefile.objs b/hw/scsi/Makefile.objs index 54d8754e9a..b188f7242b 100644 --- a/hw/scsi/Makefile.objs +++ b/hw/scsi/Makefile.objs @@ -11,4 +11,5 @@ obj-$(CONFIG_PSERIES) += spapr_vscsi.o ifeq ($(CONFIG_VIRTIO),y) obj-y += virtio-scsi.o virtio-scsi-dataplane.o obj-$(CONFIG_VHOST_SCSI) += vhost-scsi-common.o vhost-scsi.o +obj-$(CONFIG_VHOST_USER_SCSI) += vhost-scsi-common.o vhost-user-scsi.o endif diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c index 595c26017a..3e56ab267c 100644 --- a/hw/scsi/lsi53c895a.c +++ b/hw/scsi/lsi53c895a.c @@ -408,27 +408,25 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val); static void lsi_execute_script(LSIState *s); static void lsi_reselect(LSIState *s, lsi_request *p); -static inline int lsi_mem_read(LSIState *s, dma_addr_t addr, +static inline void lsi_mem_read(LSIState *s, dma_addr_t addr, void *buf, dma_addr_t len) { if (s->dmode & LSI_DMODE_SIOM) { address_space_read(&s->pci_io_as, addr, MEMTXATTRS_UNSPECIFIED, buf, len); - return 0; } else { - return pci_dma_read(PCI_DEVICE(s), addr, buf, len); + pci_dma_read(PCI_DEVICE(s), addr, buf, len); } } -static inline int lsi_mem_write(LSIState *s, dma_addr_t addr, +static inline void lsi_mem_write(LSIState *s, dma_addr_t addr, const void *buf, dma_addr_t len) { if (s->dmode & LSI_DMODE_DIOM) { address_space_write(&s->pci_io_as, addr, MEMTXATTRS_UNSPECIFIED, buf, len); - return 0; } else { - return pci_dma_write(PCI_DEVICE(s), addr, buf, len); + pci_dma_write(PCI_DEVICE(s), addr, buf, len); } } diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index 804122ab05..734fdaef90 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -63,6 +63,7 @@ typedef struct MegasasCmd { hwaddr pa; hwaddr pa_size; + uint32_t dcmd_opcode; union mfi_frame *frame; SCSIRequest *req; QEMUSGList qsg; @@ -309,9 +310,11 @@ static int megasas_build_sense(MegasasCmd *cmd, uint8_t *sense_ptr, PCIDevice *pcid = PCI_DEVICE(cmd->state); uint32_t pa_hi = 0, pa_lo; hwaddr pa; + int frame_sense_len; - if (sense_len > cmd->frame->header.sense_len) { - sense_len = cmd->frame->header.sense_len; + frame_sense_len = cmd->frame->header.sense_len; + if (sense_len > frame_sense_len) { + sense_len = frame_sense_len; } if (sense_len) { pa_lo = le32_to_cpu(cmd->frame->pass.sense_addr_lo); @@ -511,6 +514,7 @@ static MegasasCmd *megasas_enqueue_frame(MegasasState *s, cmd->context &= (uint64_t)0xFFFFFFFF; } cmd->count = count; + cmd->dcmd_opcode = -1; s->busy++; if (s->consumer_pa) { @@ -605,6 +609,9 @@ static void megasas_reset_frames(MegasasState *s) static void megasas_abort_command(MegasasCmd *cmd) { /* Never abort internal commands. */ + if (cmd->dcmd_opcode != -1) { + return; + } if (cmd->req != NULL) { scsi_req_cancel(cmd->req); } @@ -673,15 +680,16 @@ out: static int megasas_map_dcmd(MegasasState *s, MegasasCmd *cmd) { dma_addr_t iov_pa, iov_size; + int iov_count; cmd->flags = le16_to_cpu(cmd->frame->header.flags); - if (!cmd->frame->header.sge_count) { + iov_count = cmd->frame->header.sge_count; + if (!iov_count) { trace_megasas_dcmd_zero_sge(cmd->index); cmd->iov_size = 0; return 0; - } else if (cmd->frame->header.sge_count > 1) { - trace_megasas_dcmd_invalid_sge(cmd->index, - cmd->frame->header.sge_count); + } else if (iov_count > 1) { + trace_megasas_dcmd_invalid_sge(cmd->index, iov_count); cmd->iov_size = 0; return -EINVAL; } @@ -1012,7 +1020,6 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun, uint64_t pd_size; uint16_t pd_id = ((sdev->id & 0xFF) << 8) | (lun & 0xFF); uint8_t cmdbuf[6]; - SCSIRequest *req; size_t len, resid; if (!cmd->iov_buf) { @@ -1021,8 +1028,8 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun, info->inquiry_data[0] = 0x7f; /* Force PQual 0x3, PType 0x1f */ info->vpd_page83[0] = 0x7f; megasas_setup_inquiry(cmdbuf, 0, sizeof(info->inquiry_data)); - req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd); - if (!req) { + cmd->req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd); + if (!cmd->req) { trace_megasas_dcmd_req_alloc_failed(cmd->index, "PD get info std inquiry"); g_free(cmd->iov_buf); @@ -1031,26 +1038,26 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun, } trace_megasas_dcmd_internal_submit(cmd->index, "PD get info std inquiry", lun); - len = scsi_req_enqueue(req); + len = scsi_req_enqueue(cmd->req); if (len > 0) { cmd->iov_size = len; - scsi_req_continue(req); + scsi_req_continue(cmd->req); } return MFI_STAT_INVALID_STATUS; } else if (info->inquiry_data[0] != 0x7f && info->vpd_page83[0] == 0x7f) { megasas_setup_inquiry(cmdbuf, 0x83, sizeof(info->vpd_page83)); - req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd); - if (!req) { + cmd->req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd); + if (!cmd->req) { trace_megasas_dcmd_req_alloc_failed(cmd->index, "PD get info vpd inquiry"); return MFI_STAT_FLASH_ALLOC_FAIL; } trace_megasas_dcmd_internal_submit(cmd->index, "PD get info vpd inquiry", lun); - len = scsi_req_enqueue(req); + len = scsi_req_enqueue(cmd->req); if (len > 0) { cmd->iov_size = len; - scsi_req_continue(req); + scsi_req_continue(cmd->req); } return MFI_STAT_INVALID_STATUS; } @@ -1212,7 +1219,6 @@ static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun, struct mfi_ld_info *info = cmd->iov_buf; size_t dcmd_size = sizeof(struct mfi_ld_info); uint8_t cdb[6]; - SCSIRequest *req; ssize_t len, resid; uint16_t sdev_id = ((sdev->id & 0xFF) << 8) | (lun & 0xFF); uint64_t ld_size; @@ -1221,8 +1227,8 @@ static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun, cmd->iov_buf = g_malloc0(dcmd_size); info = cmd->iov_buf; megasas_setup_inquiry(cdb, 0x83, sizeof(info->vpd_page83)); - req = scsi_req_new(sdev, cmd->index, lun, cdb, cmd); - if (!req) { + cmd->req = scsi_req_new(sdev, cmd->index, lun, cdb, cmd); + if (!cmd->req) { trace_megasas_dcmd_req_alloc_failed(cmd->index, "LD get info vpd inquiry"); g_free(cmd->iov_buf); @@ -1231,10 +1237,10 @@ static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun, } trace_megasas_dcmd_internal_submit(cmd->index, "LD get info vpd inquiry", lun); - len = scsi_req_enqueue(req); + len = scsi_req_enqueue(cmd->req); if (len > 0) { cmd->iov_size = len; - scsi_req_continue(req); + scsi_req_continue(cmd->req); } return MFI_STAT_INVALID_STATUS; } @@ -1559,22 +1565,21 @@ static const struct dcmd_cmd_tbl_t { static int megasas_handle_dcmd(MegasasState *s, MegasasCmd *cmd) { - int opcode; int retval = 0; size_t len; const struct dcmd_cmd_tbl_t *cmdptr = dcmd_cmd_tbl; - opcode = le32_to_cpu(cmd->frame->dcmd.opcode); - trace_megasas_handle_dcmd(cmd->index, opcode); + cmd->dcmd_opcode = le32_to_cpu(cmd->frame->dcmd.opcode); + trace_megasas_handle_dcmd(cmd->index, cmd->dcmd_opcode); if (megasas_map_dcmd(s, cmd) < 0) { return MFI_STAT_MEMORY_NOT_AVAILABLE; } - while (cmdptr->opcode != -1 && cmdptr->opcode != opcode) { + while (cmdptr->opcode != -1 && cmdptr->opcode != cmd->dcmd_opcode) { cmdptr++; } len = cmd->iov_size; if (cmdptr->opcode == -1) { - trace_megasas_dcmd_unhandled(cmd->index, opcode, len); + trace_megasas_dcmd_unhandled(cmd->index, cmd->dcmd_opcode, len); retval = megasas_dcmd_dummy(s, cmd); } else { trace_megasas_dcmd_enter(cmd->index, cmdptr->desc, len); @@ -1587,15 +1592,14 @@ static int megasas_handle_dcmd(MegasasState *s, MegasasCmd *cmd) } static int megasas_finish_internal_dcmd(MegasasCmd *cmd, - SCSIRequest *req) + SCSIRequest *req, size_t resid) { - int opcode; int retval = MFI_STAT_OK; int lun = req->lun; - opcode = le32_to_cpu(cmd->frame->dcmd.opcode); - trace_megasas_dcmd_internal_finish(cmd->index, opcode, lun); - switch (opcode) { + trace_megasas_dcmd_internal_finish(cmd->index, cmd->dcmd_opcode, lun); + cmd->iov_size -= resid; + switch (cmd->dcmd_opcode) { case MFI_DCMD_PD_GET_INFO: retval = megasas_pd_get_info_submit(req->dev, lun, cmd); break; @@ -1603,7 +1607,7 @@ static int megasas_finish_internal_dcmd(MegasasCmd *cmd, retval = megasas_ld_get_info_submit(req->dev, lun, cmd); break; default: - trace_megasas_dcmd_internal_invalid(cmd->index, opcode); + trace_megasas_dcmd_internal_invalid(cmd->index, cmd->dcmd_opcode); retval = MFI_STAT_INVALID_DCMD; break; } @@ -1647,43 +1651,42 @@ static int megasas_enqueue_req(MegasasCmd *cmd, bool is_write) } static int megasas_handle_scsi(MegasasState *s, MegasasCmd *cmd, - bool is_logical) + int frame_cmd) { uint8_t *cdb; + int target_id, lun_id, cdb_len; bool is_write; struct SCSIDevice *sdev = NULL; + bool is_logical = (frame_cmd == MFI_CMD_LD_SCSI_IO); cdb = cmd->frame->pass.cdb; + target_id = cmd->frame->header.target_id; + lun_id = cmd->frame->header.lun_id; + cdb_len = cmd->frame->header.cdb_len; if (is_logical) { - if (cmd->frame->header.target_id >= MFI_MAX_LD || - cmd->frame->header.lun_id != 0) { + if (target_id >= MFI_MAX_LD || lun_id != 0) { trace_megasas_scsi_target_not_present( - mfi_frame_desc[cmd->frame->header.frame_cmd], is_logical, - cmd->frame->header.target_id, cmd->frame->header.lun_id); + mfi_frame_desc[frame_cmd], is_logical, target_id, lun_id); return MFI_STAT_DEVICE_NOT_FOUND; } } - sdev = scsi_device_find(&s->bus, 0, cmd->frame->header.target_id, - cmd->frame->header.lun_id); + sdev = scsi_device_find(&s->bus, 0, target_id, lun_id); cmd->iov_size = le32_to_cpu(cmd->frame->header.data_len); - trace_megasas_handle_scsi(mfi_frame_desc[cmd->frame->header.frame_cmd], - is_logical, cmd->frame->header.target_id, - cmd->frame->header.lun_id, sdev, cmd->iov_size); + trace_megasas_handle_scsi(mfi_frame_desc[frame_cmd], is_logical, + target_id, lun_id, sdev, cmd->iov_size); if (!sdev || (megasas_is_jbod(s) && is_logical)) { trace_megasas_scsi_target_not_present( - mfi_frame_desc[cmd->frame->header.frame_cmd], is_logical, - cmd->frame->header.target_id, cmd->frame->header.lun_id); + mfi_frame_desc[frame_cmd], is_logical, target_id, lun_id); return MFI_STAT_DEVICE_NOT_FOUND; } - if (cmd->frame->header.cdb_len > 16) { + if (cdb_len > 16) { trace_megasas_scsi_invalid_cdb_len( - mfi_frame_desc[cmd->frame->header.frame_cmd], is_logical, - cmd->frame->header.target_id, cmd->frame->header.lun_id, - cmd->frame->header.cdb_len); + mfi_frame_desc[frame_cmd], is_logical, + target_id, lun_id, cdb_len); megasas_write_sense(cmd, SENSE_CODE(INVALID_OPCODE)); cmd->frame->header.scsi_status = CHECK_CONDITION; s->event_count++; @@ -1697,12 +1700,10 @@ static int megasas_handle_scsi(MegasasState *s, MegasasCmd *cmd, return MFI_STAT_SCSI_DONE_WITH_ERROR; } - cmd->req = scsi_req_new(sdev, cmd->index, - cmd->frame->header.lun_id, cdb, cmd); + cmd->req = scsi_req_new(sdev, cmd->index, lun_id, cdb, cmd); if (!cmd->req) { trace_megasas_scsi_req_alloc_failed( - mfi_frame_desc[cmd->frame->header.frame_cmd], - cmd->frame->header.target_id, cmd->frame->header.lun_id); + mfi_frame_desc[frame_cmd], target_id, lun_id); megasas_write_sense(cmd, SENSE_CODE(NO_SENSE)); cmd->frame->header.scsi_status = BUSY; s->event_count++; @@ -1723,43 +1724,41 @@ static int megasas_handle_scsi(MegasasState *s, MegasasCmd *cmd, return MFI_STAT_INVALID_STATUS; } -static int megasas_handle_io(MegasasState *s, MegasasCmd *cmd) +static int megasas_handle_io(MegasasState *s, MegasasCmd *cmd, int frame_cmd) { uint32_t lba_count, lba_start_hi, lba_start_lo; uint64_t lba_start; - bool is_write = (cmd->frame->header.frame_cmd == MFI_CMD_LD_WRITE); + bool is_write = (frame_cmd == MFI_CMD_LD_WRITE); uint8_t cdb[16]; int len; struct SCSIDevice *sdev = NULL; + int target_id, lun_id, cdb_len; lba_count = le32_to_cpu(cmd->frame->io.header.data_len); lba_start_lo = le32_to_cpu(cmd->frame->io.lba_lo); lba_start_hi = le32_to_cpu(cmd->frame->io.lba_hi); lba_start = ((uint64_t)lba_start_hi << 32) | lba_start_lo; - if (cmd->frame->header.target_id < MFI_MAX_LD && - cmd->frame->header.lun_id == 0) { - sdev = scsi_device_find(&s->bus, 0, cmd->frame->header.target_id, - cmd->frame->header.lun_id); + target_id = cmd->frame->header.target_id; + lun_id = cmd->frame->header.lun_id; + cdb_len = cmd->frame->header.cdb_len; + + if (target_id < MFI_MAX_LD && lun_id == 0) { + sdev = scsi_device_find(&s->bus, 0, target_id, lun_id); } trace_megasas_handle_io(cmd->index, - mfi_frame_desc[cmd->frame->header.frame_cmd], - cmd->frame->header.target_id, - cmd->frame->header.lun_id, + mfi_frame_desc[frame_cmd], target_id, lun_id, (unsigned long)lba_start, (unsigned long)lba_count); if (!sdev) { trace_megasas_io_target_not_present(cmd->index, - mfi_frame_desc[cmd->frame->header.frame_cmd], - cmd->frame->header.target_id, cmd->frame->header.lun_id); + mfi_frame_desc[frame_cmd], target_id, lun_id); return MFI_STAT_DEVICE_NOT_FOUND; } - if (cmd->frame->header.cdb_len > 16) { + if (cdb_len > 16) { trace_megasas_scsi_invalid_cdb_len( - mfi_frame_desc[cmd->frame->header.frame_cmd], 1, - cmd->frame->header.target_id, cmd->frame->header.lun_id, - cmd->frame->header.cdb_len); + mfi_frame_desc[frame_cmd], 1, target_id, lun_id, cdb_len); megasas_write_sense(cmd, SENSE_CODE(INVALID_OPCODE)); cmd->frame->header.scsi_status = CHECK_CONDITION; s->event_count++; @@ -1776,11 +1775,10 @@ static int megasas_handle_io(MegasasState *s, MegasasCmd *cmd) megasas_encode_lba(cdb, lba_start, lba_count, is_write); cmd->req = scsi_req_new(sdev, cmd->index, - cmd->frame->header.lun_id, cdb, cmd); + lun_id, cdb, cmd); if (!cmd->req) { trace_megasas_scsi_req_alloc_failed( - mfi_frame_desc[cmd->frame->header.frame_cmd], - cmd->frame->header.target_id, cmd->frame->header.lun_id); + mfi_frame_desc[frame_cmd], target_id, lun_id); megasas_write_sense(cmd, SENSE_CODE(NO_SENSE)); cmd->frame->header.scsi_status = BUSY; s->event_count++; @@ -1797,23 +1795,11 @@ static int megasas_handle_io(MegasasState *s, MegasasCmd *cmd) return MFI_STAT_INVALID_STATUS; } -static int megasas_finish_internal_command(MegasasCmd *cmd, - SCSIRequest *req, size_t resid) -{ - int retval = MFI_STAT_INVALID_CMD; - - if (cmd->frame->header.frame_cmd == MFI_CMD_DCMD) { - cmd->iov_size -= resid; - retval = megasas_finish_internal_dcmd(cmd, req); - } - return retval; -} - static QEMUSGList *megasas_get_sg_list(SCSIRequest *req) { MegasasCmd *cmd = req->hba_private; - if (cmd->frame->header.frame_cmd == MFI_CMD_DCMD) { + if (cmd->dcmd_opcode != -1) { return NULL; } else { return &cmd->qsg; @@ -1824,18 +1810,16 @@ static void megasas_xfer_complete(SCSIRequest *req, uint32_t len) { MegasasCmd *cmd = req->hba_private; uint8_t *buf; - uint32_t opcode; trace_megasas_io_complete(cmd->index, len); - if (cmd->frame->header.frame_cmd != MFI_CMD_DCMD) { + if (cmd->dcmd_opcode != -1) { scsi_req_continue(req); return; } buf = scsi_req_get_buf(req); - opcode = le32_to_cpu(cmd->frame->dcmd.opcode); - if (opcode == MFI_DCMD_PD_GET_INFO && cmd->iov_buf) { + if (cmd->dcmd_opcode == MFI_DCMD_PD_GET_INFO && cmd->iov_buf) { struct mfi_pd_info *info = cmd->iov_buf; if (info->inquiry_data[0] == 0x7f) { @@ -1846,7 +1830,7 @@ static void megasas_xfer_complete(SCSIRequest *req, uint32_t len) memcpy(info->vpd_page83, buf, len); } scsi_req_continue(req); - } else if (opcode == MFI_DCMD_LD_GET_INFO) { + } else if (cmd->dcmd_opcode == MFI_DCMD_LD_GET_INFO) { struct mfi_ld_info *info = cmd->iov_buf; if (cmd->iov_buf) { @@ -1868,11 +1852,11 @@ static void megasas_command_complete(SCSIRequest *req, uint32_t status, return; } - if (cmd->req == NULL) { + if (cmd->dcmd_opcode != -1) { /* * Internal command complete */ - cmd_status = megasas_finish_internal_command(cmd, req, resid); + cmd_status = megasas_finish_internal_dcmd(cmd, req, resid); if (cmd_status == MFI_STAT_INVALID_STATUS) { return; } @@ -1943,6 +1927,7 @@ static void megasas_handle_frame(MegasasState *s, uint64_t frame_addr, { uint8_t frame_status = MFI_STAT_INVALID_CMD; uint64_t frame_context; + int frame_cmd; MegasasCmd *cmd; /* @@ -1961,7 +1946,8 @@ static void megasas_handle_frame(MegasasState *s, uint64_t frame_addr, s->event_count++; return; } - switch (cmd->frame->header.frame_cmd) { + frame_cmd = cmd->frame->header.frame_cmd; + switch (frame_cmd) { case MFI_CMD_INIT: frame_status = megasas_init_firmware(s, cmd); break; @@ -1972,18 +1958,15 @@ static void megasas_handle_frame(MegasasState *s, uint64_t frame_addr, frame_status = megasas_handle_abort(s, cmd); break; case MFI_CMD_PD_SCSI_IO: - frame_status = megasas_handle_scsi(s, cmd, 0); - break; case MFI_CMD_LD_SCSI_IO: - frame_status = megasas_handle_scsi(s, cmd, 1); + frame_status = megasas_handle_scsi(s, cmd, frame_cmd); break; case MFI_CMD_LD_READ: case MFI_CMD_LD_WRITE: - frame_status = megasas_handle_io(s, cmd); + frame_status = megasas_handle_io(s, cmd, frame_cmd); break; default: - trace_megasas_unhandled_frame_cmd(cmd->index, - cmd->frame->header.frame_cmd); + trace_megasas_unhandled_frame_cmd(cmd->index, frame_cmd); s->event_count++; break; } diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index f5574469c8..23c51de66a 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -282,9 +282,9 @@ void scsi_bus_legacy_handle_cmdline(SCSIBus *bus, bool deprecated) continue; /* claimed */ } if (!dinfo->is_default) { - error_report("warning: bus=%d,unit=%d is deprecated with this" - " machine type", - bus->busnr, unit); + warn_report("bus=%d,unit=%d is deprecated with this" + " machine type", + bus->busnr, unit); } } scsi_bus_legacy_add_drive(bus, blk_by_legacy_dinfo(dinfo), diff --git a/hw/scsi/vhost-scsi-common.c b/hw/scsi/vhost-scsi-common.c index e41c0314db..d434b3e99a 100644 --- a/hw/scsi/vhost-scsi-common.c +++ b/hw/scsi/vhost-scsi-common.c @@ -16,7 +16,6 @@ */ #include "qemu/osdep.h" -#include <linux/vhost.h> #include "qapi/error.h" #include "qemu/error-report.h" #include "migration/migration.h" diff --git a/hw/scsi/vhost-user-scsi.c b/hw/scsi/vhost-user-scsi.c new file mode 100644 index 0000000000..500fa6a067 --- /dev/null +++ b/hw/scsi/vhost-user-scsi.c @@ -0,0 +1,205 @@ +/* + * vhost-user-scsi host device + * + * Copyright (c) 2016 Nutanix Inc. All rights reserved. + * + * Author: + * Felipe Franciosi <felipe@nutanix.com> + * + * This work is largely based on the "vhost-scsi" implementation by: + * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com> + * Nicholas Bellinger <nab@risingtidesystems.com> + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/typedefs.h" +#include "qom/object.h" +#include "hw/fw-path-provider.h" +#include "hw/qdev-core.h" +#include "hw/virtio/vhost.h" +#include "hw/virtio/vhost-backend.h" +#include "hw/virtio/vhost-user-scsi.h" +#include "hw/virtio/virtio.h" +#include "hw/virtio/virtio-access.h" +#include "chardev/char-fe.h" + +/* Features supported by the host application */ +static const int user_feature_bits[] = { + VIRTIO_F_NOTIFY_ON_EMPTY, + VIRTIO_RING_F_INDIRECT_DESC, + VIRTIO_RING_F_EVENT_IDX, + VIRTIO_SCSI_F_HOTPLUG, + VHOST_INVALID_FEATURE_BIT +}; + +static void vhost_user_scsi_set_status(VirtIODevice *vdev, uint8_t status) +{ + VHostUserSCSI *s = (VHostUserSCSI *)vdev; + VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); + bool start = (status & VIRTIO_CONFIG_S_DRIVER_OK) && vdev->vm_running; + + if (vsc->dev.started == start) { + return; + } + + if (start) { + int ret; + + ret = vhost_scsi_common_start(vsc); + if (ret < 0) { + error_report("unable to start vhost-user-scsi: %s", strerror(-ret)); + exit(1); + } + } else { + vhost_scsi_common_stop(vsc); + } +} + +static void vhost_dummy_handle_output(VirtIODevice *vdev, VirtQueue *vq) +{ +} + +static void vhost_user_scsi_realize(DeviceState *dev, Error **errp) +{ + VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev); + VHostUserSCSI *s = VHOST_USER_SCSI(dev); + VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); + Error *err = NULL; + int ret; + + if (!vs->conf.chardev.chr) { + error_setg(errp, "vhost-user-scsi: missing chardev"); + return; + } + + virtio_scsi_common_realize(dev, vhost_dummy_handle_output, + vhost_dummy_handle_output, + vhost_dummy_handle_output, &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + + vsc->dev.nvqs = 2 + vs->conf.num_queues; + vsc->dev.vqs = g_new(struct vhost_virtqueue, vsc->dev.nvqs); + vsc->dev.vq_index = 0; + vsc->dev.backend_features = 0; + + ret = vhost_dev_init(&vsc->dev, (void *)&vs->conf.chardev, + VHOST_BACKEND_TYPE_USER, 0); + if (ret < 0) { + error_setg(errp, "vhost-user-scsi: vhost initialization failed: %s", + strerror(-ret)); + return; + } + + /* Channel and lun both are 0 for bootable vhost-user-scsi disk */ + vsc->channel = 0; + vsc->lun = 0; + vsc->target = vs->conf.boot_tpgt; +} + +static void vhost_user_scsi_unrealize(DeviceState *dev, Error **errp) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserSCSI *s = VHOST_USER_SCSI(dev); + VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); + + /* This will stop the vhost backend. */ + vhost_user_scsi_set_status(vdev, 0); + + vhost_dev_cleanup(&vsc->dev); + g_free(vsc->dev.vqs); + + virtio_scsi_common_unrealize(dev, errp); +} + +static uint64_t vhost_user_scsi_get_features(VirtIODevice *vdev, + uint64_t features, Error **errp) +{ + VHostUserSCSI *s = VHOST_USER_SCSI(vdev); + + /* Turn on predefined features supported by this device */ + features |= s->host_features; + + return vhost_scsi_common_get_features(vdev, features, errp); +} + +static Property vhost_user_scsi_properties[] = { + DEFINE_PROP_CHR("chardev", VirtIOSCSICommon, conf.chardev), + DEFINE_PROP_UINT32("boot_tpgt", VirtIOSCSICommon, conf.boot_tpgt, 0), + DEFINE_PROP_UINT32("num_queues", VirtIOSCSICommon, conf.num_queues, 1), + DEFINE_PROP_UINT32("max_sectors", VirtIOSCSICommon, conf.max_sectors, + 0xFFFF), + DEFINE_PROP_UINT32("cmd_per_lun", VirtIOSCSICommon, conf.cmd_per_lun, 128), + DEFINE_PROP_BIT64("hotplug", VHostUserSCSI, host_features, + VIRTIO_SCSI_F_HOTPLUG, + true), + DEFINE_PROP_BIT64("param_change", VHostUserSCSI, host_features, + VIRTIO_SCSI_F_CHANGE, + true), + DEFINE_PROP_END_OF_LIST(), +}; + +static const VMStateDescription vmstate_vhost_scsi = { + .name = "virtio-scsi", + .minimum_version_id = 1, + .version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_VIRTIO_DEVICE, + VMSTATE_END_OF_LIST() + }, +}; + +static void vhost_user_scsi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(klass); + + dc->props = vhost_user_scsi_properties; + dc->vmsd = &vmstate_vhost_scsi; + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); + vdc->realize = vhost_user_scsi_realize; + vdc->unrealize = vhost_user_scsi_unrealize; + vdc->get_features = vhost_user_scsi_get_features; + vdc->set_config = vhost_scsi_common_set_config; + vdc->set_status = vhost_user_scsi_set_status; + fwc->get_dev_path = vhost_scsi_common_get_fw_dev_path; +} + +static void vhost_user_scsi_instance_init(Object *obj) +{ + VHostSCSICommon *vsc = VHOST_SCSI_COMMON(obj); + + vsc->feature_bits = user_feature_bits; + + /* Add the bootindex property for this object */ + device_add_bootindex_property(obj, &vsc->bootindex, "bootindex", NULL, + DEVICE(vsc), NULL); +} + +static const TypeInfo vhost_user_scsi_info = { + .name = TYPE_VHOST_USER_SCSI, + .parent = TYPE_VHOST_SCSI_COMMON, + .instance_size = sizeof(VHostUserSCSI), + .class_init = vhost_user_scsi_class_init, + .instance_init = vhost_user_scsi_instance_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_FW_PATH_PROVIDER }, + { } + }, +}; + +static void virtio_register_types(void) +{ + type_register_static(&vhost_user_scsi_info); +} + +type_init(virtio_register_types) diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 46a3e3f280..eb639442d1 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -43,12 +43,13 @@ static inline SCSIDevice *virtio_scsi_device_find(VirtIOSCSI *s, uint8_t *lun) void virtio_scsi_init_req(VirtIOSCSI *s, VirtQueue *vq, VirtIOSCSIReq *req) { + VirtIODevice *vdev = VIRTIO_DEVICE(s); const size_t zero_skip = offsetof(VirtIOSCSIReq, resp_iov) + sizeof(req->resp_iov); req->vq = vq; req->dev = s; - qemu_sglist_init(&req->qsgl, DEVICE(s), 8, &address_space_memory); + qemu_sglist_init(&req->qsgl, DEVICE(s), 8, vdev->dma_as); qemu_iovec_init(&req->resp_iov, 1); memset((uint8_t *)req + zero_skip, 0, sizeof(*req) - zero_skip); } @@ -897,16 +898,6 @@ static void virtio_scsi_device_realize(DeviceState *dev, Error **errp) virtio_scsi_dataplane_setup(s, errp); } -static void virtio_scsi_instance_init(Object *obj) -{ - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(obj); - - object_property_add_link(obj, "iothread", TYPE_IOTHREAD, - (Object **)&vs->conf.iothread, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort); -} - void virtio_scsi_common_unrealize(DeviceState *dev, Error **errp) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); @@ -918,6 +909,9 @@ void virtio_scsi_common_unrealize(DeviceState *dev, Error **errp) static void virtio_scsi_device_unrealize(DeviceState *dev, Error **errp) { + VirtIOSCSI *s = VIRTIO_SCSI(dev); + + qbus_set_hotplug_handler(BUS(&s->bus), NULL, &error_abort); virtio_scsi_common_unrealize(dev, errp); } @@ -931,6 +925,8 @@ static Property virtio_scsi_properties[] = { VIRTIO_SCSI_F_HOTPLUG, true), DEFINE_PROP_BIT("param_change", VirtIOSCSI, host_features, VIRTIO_SCSI_F_CHANGE, true), + DEFINE_PROP_LINK("iothread", VirtIOSCSI, parent_obj.conf.iothread, + TYPE_IOTHREAD, IOThread *), DEFINE_PROP_END_OF_LIST(), }; @@ -985,7 +981,6 @@ static const TypeInfo virtio_scsi_info = { .name = TYPE_VIRTIO_SCSI, .parent = TYPE_VIRTIO_SCSI_COMMON, .instance_size = sizeof(VirtIOSCSI), - .instance_init = virtio_scsi_instance_init, .class_init = virtio_scsi_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c index 8f520cec1c..a0462adb97 100644 --- a/hw/sh4/r2d.c +++ b/hw/sh4/r2d.c @@ -164,7 +164,7 @@ r2d_fpga_write(void *opaque, hwaddr addr, uint64_t value, unsigned int size) break; case PA_POWOFF: if (value & 1) { - qemu_system_shutdown_request(); + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); } break; case PA_VERREG: @@ -260,7 +260,6 @@ static void r2d_init(MachineState *machine) /* Allocate memory space */ memory_region_init_ram(sdram, NULL, "r2d.sdram", SDRAM_SIZE, &error_fatal); - vmstate_register_ram_global(sdram); memory_region_add_subregion(address_space_mem, SDRAM_BASE, sdram); /* Register peripherals */ s = sh7750_init(cpu, address_space_mem); diff --git a/hw/sh4/shix.c b/hw/sh4/shix.c index fd00cc5ea2..e22eaf0c8f 100644 --- a/hw/sh4/shix.c +++ b/hw/sh4/shix.c @@ -64,16 +64,13 @@ static void shix_init(MachineState *machine) /* Allocate memory space */ memory_region_init_ram(rom, NULL, "shix.rom", 0x4000, &error_fatal); - vmstate_register_ram_global(rom); memory_region_set_readonly(rom, true); memory_region_add_subregion(sysmem, 0x00000000, rom); memory_region_init_ram(&sdram[0], NULL, "shix.sdram1", 0x01000000, &error_fatal); - vmstate_register_ram_global(&sdram[0]); memory_region_add_subregion(sysmem, 0x08000000, &sdram[0]); memory_region_init_ram(&sdram[1], NULL, "shix.sdram2", 0x01000000, &error_fatal); - vmstate_register_ram_global(&sdram[1]); memory_region_add_subregion(sysmem, 0x0c000000, &sdram[1]); /* Load BIOS in 0 (and access it through P2, 0xA0000000) */ diff --git a/hw/sparc/leon3.c b/hw/sparc/leon3.c index 6e16478413..d5ff188d9e 100644 --- a/hw/sparc/leon3.c +++ b/hw/sparc/leon3.c @@ -28,7 +28,6 @@ #include "hw/hw.h" #include "qemu/timer.h" #include "hw/ptimer.h" -#include "sysemu/char.h" #include "sysemu/sysemu.h" #include "sysemu/qtest.h" #include "hw/boards.h" @@ -161,7 +160,6 @@ static void leon3_generic_hw_init(MachineState *machine) /* Allocate BIOS */ prom_size = 8 * 1024 * 1024; /* 8Mb */ memory_region_init_ram(prom, NULL, "Leon3.bios", prom_size, &error_fatal); - vmstate_register_ram_global(prom); memory_region_set_readonly(prom, true); memory_region_add_subregion(address_space_mem, 0x00000000, prom); diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index 5f022cc08d..89dd8a96c3 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -585,30 +585,23 @@ typedef struct IDRegState { MemoryRegion mem; } IDRegState; -static int idreg_init1(SysBusDevice *dev) +static void idreg_init1(Object *obj) { - IDRegState *s = MACIO_ID_REGISTER(dev); + IDRegState *s = MACIO_ID_REGISTER(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); - memory_region_init_ram(&s->mem, OBJECT(s), + memory_region_init_ram_nomigrate(&s->mem, obj, "sun4m.idreg", sizeof(idreg_data), &error_fatal); vmstate_register_ram_global(&s->mem); memory_region_set_readonly(&s->mem, true); sysbus_init_mmio(dev, &s->mem); - return 0; -} - -static void idreg_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = idreg_init1; } static const TypeInfo idreg_info = { .name = TYPE_MACIO_ID_REGISTER, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(IDRegState), - .class_init = idreg_class_init, + .instance_init = idreg_init1, }; #define TYPE_TCX_AFX "tcx_afx" @@ -633,28 +626,21 @@ static void afx_init(hwaddr addr) sysbus_mmio_map(s, 0, addr); } -static int afx_init1(SysBusDevice *dev) +static void afx_init1(Object *obj) { - AFXState *s = TCX_AFX(dev); + AFXState *s = TCX_AFX(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); - memory_region_init_ram(&s->mem, OBJECT(s), "sun4m.afx", 4, &error_fatal); + memory_region_init_ram_nomigrate(&s->mem, obj, "sun4m.afx", 4, &error_fatal); vmstate_register_ram_global(&s->mem); sysbus_init_mmio(dev, &s->mem); - return 0; -} - -static void afx_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = afx_init1; } static const TypeInfo afx_info = { .name = TYPE_TCX_AFX, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(AFXState), - .class_init = afx_class_init, + .instance_init = afx_init1, }; #define TYPE_OPENPROM "openprom" @@ -707,16 +693,16 @@ static void prom_init(hwaddr addr, const char *bios_name) } } -static int prom_init1(SysBusDevice *dev) +static void prom_init1(Object *obj) { - PROMState *s = OPENPROM(dev); + PROMState *s = OPENPROM(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); - memory_region_init_ram(&s->prom, OBJECT(s), "sun4m.prom", PROM_SIZE_MAX, + memory_region_init_ram_nomigrate(&s->prom, obj, "sun4m.prom", PROM_SIZE_MAX, &error_fatal); vmstate_register_ram_global(&s->prom); memory_region_set_readonly(&s->prom, true); sysbus_init_mmio(dev, &s->prom); - return 0; } static Property prom_properties[] = { @@ -726,9 +712,7 @@ static Property prom_properties[] = { static void prom_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = prom_init1; dc->props = prom_properties; } @@ -737,6 +721,7 @@ static const TypeInfo prom_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PROMState), .class_init = prom_class_init, + .instance_init = prom_init1, }; #define TYPE_SUN4M_MEMORY "memory" @@ -750,14 +735,14 @@ typedef struct RamDevice { } RamDevice; /* System RAM */ -static int ram_init1(SysBusDevice *dev) +static void ram_realize(DeviceState *dev, Error **errp) { RamDevice *d = SUN4M_RAM(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); memory_region_allocate_system_memory(&d->ram, OBJECT(d), "sun4m.ram", d->size); - sysbus_init_mmio(dev, &d->ram); - return 0; + sysbus_init_mmio(sbd, &d->ram); } static void ram_init(hwaddr addr, ram_addr_t RAM_size, @@ -793,9 +778,8 @@ static Property ram_properties[] = { static void ram_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = ram_init1; + dc->realize = ram_realize; dc->props = ram_properties; } diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index d347b6616d..bbdb40c330 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -214,7 +214,7 @@ static void isa_irq_handler(void *opaque, int n, int level) qemu_irq *irqs = opaque; int ivec; - assert(n < 16); + assert(n < ARRAY_SIZE(isa_irq_to_ivec)); ivec = isa_irq_to_ivec[n]; EBUS_DPRINTF("Set ISA IRQ %d level %d -> ivec 0x%x\n", n, level, ivec); if (ivec) { @@ -329,16 +329,16 @@ static void prom_init(hwaddr addr, const char *bios_name) } } -static int prom_init1(SysBusDevice *dev) +static void prom_init1(Object *obj) { - PROMState *s = OPENPROM(dev); + PROMState *s = OPENPROM(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); - memory_region_init_ram(&s->prom, OBJECT(s), "sun4u.prom", PROM_SIZE_MAX, + memory_region_init_ram_nomigrate(&s->prom, obj, "sun4u.prom", PROM_SIZE_MAX, &error_fatal); vmstate_register_ram_global(&s->prom); memory_region_set_readonly(&s->prom, true); sysbus_init_mmio(dev, &s->prom); - return 0; } static Property prom_properties[] = { @@ -348,9 +348,7 @@ static Property prom_properties[] = { static void prom_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = prom_init1; dc->props = prom_properties; } @@ -359,6 +357,7 @@ static const TypeInfo prom_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PROMState), .class_init = prom_class_init, + .instance_init = prom_init1, }; @@ -373,15 +372,15 @@ typedef struct RamDevice { } RamDevice; /* System RAM */ -static int ram_init1(SysBusDevice *dev) +static void ram_realize(DeviceState *dev, Error **errp) { RamDevice *d = SUN4U_RAM(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - memory_region_init_ram(&d->ram, OBJECT(d), "sun4u.ram", d->size, + memory_region_init_ram_nomigrate(&d->ram, OBJECT(d), "sun4u.ram", d->size, &error_fatal); vmstate_register_ram_global(&d->ram); - sysbus_init_mmio(dev, &d->ram); - return 0; + sysbus_init_mmio(sbd, &d->ram); } static void ram_init(hwaddr addr, ram_addr_t RAM_size) @@ -409,9 +408,8 @@ static Property ram_properties[] = { static void ram_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = ram_init1; + dc->realize = ram_realize; dc->props = ram_properties; } diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c index da8adfa443..e833028393 100644 --- a/hw/ssi/xilinx_spips.c +++ b/hw/ssi/xilinx_spips.c @@ -496,6 +496,18 @@ static const MemoryRegionOps spips_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static void xilinx_qspips_invalidate_mmio_ptr(XilinxQSPIPS *q) +{ + XilinxSPIPS *s = &q->parent_obj; + + if (q->lqspi_cached_addr != ~0ULL) { + /* Invalidate the current mapped mmio */ + memory_region_invalidate_mmio_ptr(&s->mmlqspi, q->lqspi_cached_addr, + LQSPI_CACHE_SIZE); + q->lqspi_cached_addr = ~0ULL; + } +} + static void xilinx_qspips_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { @@ -505,7 +517,7 @@ static void xilinx_qspips_write(void *opaque, hwaddr addr, addr >>= 2; if (addr == R_LQSPI_CFG) { - q->lqspi_cached_addr = ~0ULL; + xilinx_qspips_invalidate_mmio_ptr(q); } } @@ -517,27 +529,20 @@ static const MemoryRegionOps qspips_ops = { #define LQSPI_CACHE_SIZE 1024 -static uint64_t -lqspi_read(void *opaque, hwaddr addr, unsigned int size) +static void lqspi_load_cache(void *opaque, hwaddr addr) { - int i; XilinxQSPIPS *q = opaque; XilinxSPIPS *s = opaque; - uint32_t ret; - - if (addr >= q->lqspi_cached_addr && - addr <= q->lqspi_cached_addr + LQSPI_CACHE_SIZE - 4) { - uint8_t *retp = &q->lqspi_buf[addr - q->lqspi_cached_addr]; - ret = cpu_to_le32(*(uint32_t *)retp); - DB_PRINT_L(1, "addr: %08x, data: %08x\n", (unsigned)addr, - (unsigned)ret); - return ret; - } else { - int flash_addr = (addr / num_effective_busses(s)); - int slave = flash_addr >> LQSPI_ADDRESS_BITS; - int cache_entry = 0; - uint32_t u_page_save = s->regs[R_LQSPI_STS] & ~LQSPI_CFG_U_PAGE; - + int i; + int flash_addr = ((addr & ~(LQSPI_CACHE_SIZE - 1)) + / num_effective_busses(s)); + int slave = flash_addr >> LQSPI_ADDRESS_BITS; + int cache_entry = 0; + uint32_t u_page_save = s->regs[R_LQSPI_STS] & ~LQSPI_CFG_U_PAGE; + + if (addr < q->lqspi_cached_addr || + addr > q->lqspi_cached_addr + LQSPI_CACHE_SIZE - 4) { + xilinx_qspips_invalidate_mmio_ptr(q); s->regs[R_LQSPI_STS] &= ~LQSPI_CFG_U_PAGE; s->regs[R_LQSPI_STS] |= slave ? LQSPI_CFG_U_PAGE : 0; @@ -589,12 +594,43 @@ lqspi_read(void *opaque, hwaddr addr, unsigned int size) xilinx_spips_update_cs_lines(s); q->lqspi_cached_addr = flash_addr * num_effective_busses(s); + } +} + +static void *lqspi_request_mmio_ptr(void *opaque, hwaddr addr, unsigned *size, + unsigned *offset) +{ + XilinxQSPIPS *q = opaque; + hwaddr offset_within_the_region = addr & ~(LQSPI_CACHE_SIZE - 1); + + lqspi_load_cache(opaque, offset_within_the_region); + *size = LQSPI_CACHE_SIZE; + *offset = offset_within_the_region; + return q->lqspi_buf; +} + +static uint64_t +lqspi_read(void *opaque, hwaddr addr, unsigned int size) +{ + XilinxQSPIPS *q = opaque; + uint32_t ret; + + if (addr >= q->lqspi_cached_addr && + addr <= q->lqspi_cached_addr + LQSPI_CACHE_SIZE - 4) { + uint8_t *retp = &q->lqspi_buf[addr - q->lqspi_cached_addr]; + ret = cpu_to_le32(*(uint32_t *)retp); + DB_PRINT_L(1, "addr: %08x, data: %08x\n", (unsigned)addr, + (unsigned)ret); + return ret; + } else { + lqspi_load_cache(opaque, addr); return lqspi_read(opaque, addr, size); } } static const MemoryRegionOps lqspi_ops = { .read = lqspi_read, + .request_ptr = lqspi_request_mmio_ptr, .endianness = DEVICE_NATIVE_ENDIAN, .valid = { .min_access_size = 1, diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs index dd6f27e2a3..15cce1c531 100644 --- a/hw/timer/Makefile.objs +++ b/hw/timer/Makefile.objs @@ -41,3 +41,4 @@ common-obj-$(CONFIG_STM32F2XX_TIMER) += stm32f2xx_timer.o common-obj-$(CONFIG_ASPEED_SOC) += aspeed_timer.o common-obj-$(CONFIG_SUN4V_RTC) += sun4v-rtc.o +common-obj-$(CONFIG_CMSDK_APB_TIMER) += cmsdk-apb-timer.o diff --git a/hw/timer/altera_timer.c b/hw/timer/altera_timer.c index 6d4862661d..c9a0fc5dca 100644 --- a/hw/timer/altera_timer.c +++ b/hw/timer/altera_timer.c @@ -204,7 +204,7 @@ static void altera_timer_reset(DeviceState *dev) ptimer_stop(t->ptimer); ptimer_set_limit(t->ptimer, 0xffffffff, 1); - memset(t->regs, 0, ARRAY_SIZE(t->regs)); + memset(t->regs, 0, sizeof(t->regs)); } static Property altera_timer_properties[] = { diff --git a/hw/timer/aspeed_timer.c b/hw/timer/aspeed_timer.c index 9b70ee09b0..50acbf530a 100644 --- a/hw/timer/aspeed_timer.c +++ b/hw/timer/aspeed_timer.c @@ -130,15 +130,26 @@ static uint64_t calculate_next(struct AspeedTimer *t) next = seq[1]; } else if (now < seq[2]) { next = seq[2]; - } else { + } else if (t->reload) { reload_ns = muldiv64(t->reload, NANOSECONDS_PER_SECOND, rate); t->start = now - ((now - t->start) % reload_ns); + } else { + /* no reload value, return 0 */ + break; } } return next; } +static void aspeed_timer_mod(AspeedTimer *t) +{ + uint64_t next = calculate_next(t); + if (next) { + timer_mod(&t->timer, next); + } +} + static void aspeed_timer_expire(void *opaque) { AspeedTimer *t = opaque; @@ -164,7 +175,7 @@ static void aspeed_timer_expire(void *opaque) qemu_set_irq(t->irq, t->level); } - timer_mod(&t->timer, calculate_next(t)); + aspeed_timer_mod(t); } static uint64_t aspeed_timer_get_value(AspeedTimer *t, int reg) @@ -227,10 +238,23 @@ static void aspeed_timer_set_value(AspeedTimerCtrlState *s, int timer, int reg, uint32_t value) { AspeedTimer *t; + uint32_t old_reload; trace_aspeed_timer_set_value(timer, reg, value); t = &s->timers[timer]; switch (reg) { + case TIMER_REG_RELOAD: + old_reload = t->reload; + t->reload = value; + + /* If the reload value was not previously set, or zero, and + * the current value is valid, try to start the timer if it is + * enabled. + */ + if (old_reload || !t->reload) { + break; + } + case TIMER_REG_STATUS: if (timer_enabled(t)) { uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); @@ -238,17 +262,14 @@ static void aspeed_timer_set_value(AspeedTimerCtrlState *s, int timer, int reg, uint32_t rate = calculate_rate(t); t->start += muldiv64(delta, NANOSECONDS_PER_SECOND, rate); - timer_mod(&t->timer, calculate_next(t)); + aspeed_timer_mod(t); } break; - case TIMER_REG_RELOAD: - t->reload = value; - break; case TIMER_REG_MATCH_FIRST: case TIMER_REG_MATCH_SECOND: t->match[reg - 2] = value; if (timer_enabled(t)) { - timer_mod(&t->timer, calculate_next(t)); + aspeed_timer_mod(t); } break; default: @@ -268,7 +289,7 @@ static void aspeed_timer_ctrl_enable(AspeedTimer *t, bool enable) trace_aspeed_timer_ctrl_enable(t->id, enable); if (enable) { t->start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - timer_mod(&t->timer, calculate_next(t)); + aspeed_timer_mod(t); } else { timer_del(&t->timer); } diff --git a/hw/timer/cmsdk-apb-timer.c b/hw/timer/cmsdk-apb-timer.c new file mode 100644 index 0000000000..9878746609 --- /dev/null +++ b/hw/timer/cmsdk-apb-timer.c @@ -0,0 +1,253 @@ +/* + * ARM CMSDK APB timer emulation + * + * Copyright (c) 2017 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* This is a model of the "APB timer" which is part of the Cortex-M + * System Design Kit (CMSDK) and documented in the Cortex-M System + * Design Kit Technical Reference Manual (ARM DDI0479C): + * https://developer.arm.com/products/system-design/system-design-kits/cortex-m-system-design-kit + * + * The hardware has an EXTIN input wire, which can be configured + * by the guest to act either as a 'timer enable' (timer does not run + * when EXTIN is low), or as a 'timer clock' (timer runs at frequency + * of EXTIN clock, not PCLK frequency). We don't model this. + * + * The documentation is not very clear about the exact behaviour; + * we choose to implement that the interrupt is triggered when + * the counter goes from 1 to 0, that the counter then holds at 0 + * for one clock cycle before reloading from the RELOAD register, + * and that if the RELOAD register is 0 this does not cause an + * interrupt (as there is no further 1->0 transition). + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/main-loop.h" +#include "qapi/error.h" +#include "trace.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/timer/cmsdk-apb-timer.h" + +REG32(CTRL, 0) + FIELD(CTRL, EN, 0, 1) + FIELD(CTRL, SELEXTEN, 1, 1) + FIELD(CTRL, SELEXTCLK, 2, 1) + FIELD(CTRL, IRQEN, 3, 1) +REG32(VALUE, 4) +REG32(RELOAD, 8) +REG32(INTSTATUS, 0xc) + FIELD(INTSTATUS, IRQ, 0, 1) +REG32(PID4, 0xFD0) +REG32(PID5, 0xFD4) +REG32(PID6, 0xFD8) +REG32(PID7, 0xFDC) +REG32(PID0, 0xFE0) +REG32(PID1, 0xFE4) +REG32(PID2, 0xFE8) +REG32(PID3, 0xFEC) +REG32(CID0, 0xFF0) +REG32(CID1, 0xFF4) +REG32(CID2, 0xFF8) +REG32(CID3, 0xFFC) + +/* PID/CID values */ +static const int timer_id[] = { + 0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */ + 0x22, 0xb8, 0x1b, 0x00, /* PID0..PID3 */ + 0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */ +}; + +static void cmsdk_apb_timer_update(CMSDKAPBTIMER *s) +{ + qemu_set_irq(s->timerint, !!(s->intstatus & R_INTSTATUS_IRQ_MASK)); +} + +static uint64_t cmsdk_apb_timer_read(void *opaque, hwaddr offset, unsigned size) +{ + CMSDKAPBTIMER *s = CMSDK_APB_TIMER(opaque); + uint64_t r; + + switch (offset) { + case A_CTRL: + r = s->ctrl; + break; + case A_VALUE: + r = ptimer_get_count(s->timer); + break; + case A_RELOAD: + r = ptimer_get_limit(s->timer); + break; + case A_INTSTATUS: + r = s->intstatus; + break; + case A_PID4 ... A_CID3: + r = timer_id[(offset - A_PID4) / 4]; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "CMSDK APB timer read: bad offset %x\n", (int) offset); + r = 0; + break; + } + trace_cmsdk_apb_timer_read(offset, r, size); + return r; +} + +static void cmsdk_apb_timer_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + CMSDKAPBTIMER *s = CMSDK_APB_TIMER(opaque); + + trace_cmsdk_apb_timer_write(offset, value, size); + + switch (offset) { + case A_CTRL: + if (value & 6) { + /* Bits [1] and [2] enable using EXTIN as either clock or + * an enable line. We don't model this. + */ + qemu_log_mask(LOG_UNIMP, + "CMSDK APB timer: EXTIN input not supported\n"); + } + s->ctrl = value & 0xf; + if (s->ctrl & R_CTRL_EN_MASK) { + ptimer_run(s->timer, 0); + } else { + ptimer_stop(s->timer); + } + break; + case A_RELOAD: + /* Writing to reload also sets the current timer value */ + ptimer_set_limit(s->timer, value, 1); + break; + case A_VALUE: + ptimer_set_count(s->timer, value); + break; + case A_INTSTATUS: + /* Just one bit, which is W1C. */ + value &= 1; + s->intstatus &= ~value; + cmsdk_apb_timer_update(s); + break; + case A_PID4 ... A_CID3: + qemu_log_mask(LOG_GUEST_ERROR, + "CMSDK APB timer write: write to RO offset 0x%x\n", + (int)offset); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "CMSDK APB timer write: bad offset 0x%x\n", (int) offset); + break; + } +} + +static const MemoryRegionOps cmsdk_apb_timer_ops = { + .read = cmsdk_apb_timer_read, + .write = cmsdk_apb_timer_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void cmsdk_apb_timer_tick(void *opaque) +{ + CMSDKAPBTIMER *s = CMSDK_APB_TIMER(opaque); + + if (s->ctrl & R_CTRL_IRQEN_MASK) { + s->intstatus |= R_INTSTATUS_IRQ_MASK; + cmsdk_apb_timer_update(s); + } +} + +static void cmsdk_apb_timer_reset(DeviceState *dev) +{ + CMSDKAPBTIMER *s = CMSDK_APB_TIMER(dev); + + trace_cmsdk_apb_timer_reset(); + s->ctrl = 0; + s->intstatus = 0; + ptimer_stop(s->timer); + /* Set the limit and the count */ + ptimer_set_limit(s->timer, 0, 1); +} + +static void cmsdk_apb_timer_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + CMSDKAPBTIMER *s = CMSDK_APB_TIMER(obj); + + memory_region_init_io(&s->iomem, obj, &cmsdk_apb_timer_ops, + s, "cmsdk-apb-timer", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->timerint); +} + +static void cmsdk_apb_timer_realize(DeviceState *dev, Error **errp) +{ + CMSDKAPBTIMER *s = CMSDK_APB_TIMER(dev); + QEMUBH *bh; + + if (s->pclk_frq == 0) { + error_setg(errp, "CMSDK APB timer: pclk-frq property must be set"); + return; + } + + bh = qemu_bh_new(cmsdk_apb_timer_tick, s); + s->timer = ptimer_init(bh, + PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD | + PTIMER_POLICY_NO_IMMEDIATE_TRIGGER | + PTIMER_POLICY_NO_IMMEDIATE_RELOAD | + PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); + + ptimer_set_freq(s->timer, s->pclk_frq); +} + +static const VMStateDescription cmsdk_apb_timer_vmstate = { + .name = "cmsdk-apb-timer", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_PTIMER(timer, CMSDKAPBTIMER), + VMSTATE_UINT32(ctrl, CMSDKAPBTIMER), + VMSTATE_UINT32(value, CMSDKAPBTIMER), + VMSTATE_UINT32(reload, CMSDKAPBTIMER), + VMSTATE_UINT32(intstatus, CMSDKAPBTIMER), + VMSTATE_END_OF_LIST() + } +}; + +static Property cmsdk_apb_timer_properties[] = { + DEFINE_PROP_UINT32("pclk-frq", CMSDKAPBTIMER, pclk_frq, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void cmsdk_apb_timer_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = cmsdk_apb_timer_realize; + dc->vmsd = &cmsdk_apb_timer_vmstate; + dc->reset = cmsdk_apb_timer_reset; + dc->props = cmsdk_apb_timer_properties; +} + +static const TypeInfo cmsdk_apb_timer_info = { + .name = TYPE_CMSDK_APB_TIMER, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(CMSDKAPBTIMER), + .instance_init = cmsdk_apb_timer_init, + .class_init = cmsdk_apb_timer_class_init, +}; + +static void cmsdk_apb_timer_register_types(void) +{ + type_register_static(&cmsdk_apb_timer_info); +} + +type_init(cmsdk_apb_timer_register_types); diff --git a/hw/timer/etraxfs_timer.c b/hw/timer/etraxfs_timer.c index 8e18236c5a..d13bc30b2d 100644 --- a/hw/timer/etraxfs_timer.c +++ b/hw/timer/etraxfs_timer.c @@ -207,7 +207,7 @@ static void watchdog_hit(void *opaque) qemu_irq_raise(t->nmi); } else - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); t->wd_hits++; } diff --git a/hw/timer/exynos4210_mct.c b/hw/timer/exynos4210_mct.c index a2ec3920f8..e4ef4cfd36 100644 --- a/hw/timer/exynos4210_mct.c +++ b/hw/timer/exynos4210_mct.c @@ -173,13 +173,10 @@ enum LocalTimerRegCntIndexes { L_REG_CNT_AMOUNT }; -#define MCT_NIRQ 6 #define MCT_SFR_SIZE 0x444 #define MCT_GT_CMP_NUM 4 -#define MCT_GT_MAX_VAL UINT64_MAX - #define MCT_GT_COUNTER_STEP 0x100000000ULL #define MCT_LT_COUNTER_STEP 0x100000000ULL #define MCT_LT_CNT_LOW_LIMIT 0x100 @@ -937,7 +934,7 @@ static void exynos4210_mct_update_freq(Exynos4210MCTState *s) { uint32_t freq = s->freq; s->freq = 24000000 / - ((MCT_CFG_GET_PRESCALER(s->reg_mct_cfg)+1) * + ((MCT_CFG_GET_PRESCALER(s->reg_mct_cfg) + 1) * MCT_CFG_GET_DIVIDER(s->reg_mct_cfg)); if (freq != s->freq) { @@ -1016,9 +1013,9 @@ static uint64_t exynos4210_mct_read(void *opaque, hwaddr offset, case G_COMP_L(0): case G_COMP_L(1): case G_COMP_L(2): case G_COMP_L(3): case G_COMP_U(0): case G_COMP_U(1): case G_COMP_U(2): case G_COMP_U(3): - index = GET_G_COMP_IDX(offset); - shift = 8 * (offset & 0x4); - value = UINT32_MAX & (s->g_timer.reg.comp[index] >> shift); + index = GET_G_COMP_IDX(offset); + shift = 8 * (offset & 0x4); + value = UINT32_MAX & (s->g_timer.reg.comp[index] >> shift); break; case G_TCON: @@ -1067,7 +1064,6 @@ static uint64_t exynos4210_mct_read(void *opaque, hwaddr offset, lt_i = GET_L_TIMER_IDX(offset); value = exynos4210_lfrc_get_count(&s->l_timer[lt_i]); - break; case L0_TCON: case L1_TCON: @@ -1153,23 +1149,23 @@ static void exynos4210_mct_write(void *opaque, hwaddr offset, case G_COMP_L(0): case G_COMP_L(1): case G_COMP_L(2): case G_COMP_L(3): case G_COMP_U(0): case G_COMP_U(1): case G_COMP_U(2): case G_COMP_U(3): - index = GET_G_COMP_IDX(offset); - shift = 8 * (offset & 0x4); - s->g_timer.reg.comp[index] = - (s->g_timer.reg.comp[index] & - (((uint64_t)UINT32_MAX << 32) >> shift)) + - (value << shift); + index = GET_G_COMP_IDX(offset); + shift = 8 * (offset & 0x4); + s->g_timer.reg.comp[index] = + (s->g_timer.reg.comp[index] & + (((uint64_t)UINT32_MAX << 32) >> shift)) + + (value << shift); - DPRINTF("comparator %d write 0x%llx val << %d\n", index, value, shift); + DPRINTF("comparator %d write 0x%llx val << %d\n", index, value, shift); - if (offset&0x4) { - s->g_timer.reg.wstat |= G_WSTAT_COMP_U(index); - } else { - s->g_timer.reg.wstat |= G_WSTAT_COMP_L(index); - } + if (offset & 0x4) { + s->g_timer.reg.wstat |= G_WSTAT_COMP_U(index); + } else { + s->g_timer.reg.wstat |= G_WSTAT_COMP_L(index); + } - exynos4210_gfrc_restart(s); - break; + exynos4210_gfrc_restart(s); + break; case G_TCON: old_val = s->g_timer.reg.tcon; @@ -1207,7 +1203,6 @@ static void exynos4210_mct_write(void *opaque, hwaddr offset, break; case G_INT_ENB: - /* Raise IRQ if transition from disabled to enabled and CSTAT pending */ for (i = 0; i < MCT_GT_CMP_NUM; i++) { if ((value & G_INT_ENABLE(i)) > (s->g_timer.reg.tcon & @@ -1288,7 +1283,6 @@ static void exynos4210_mct_write(void *opaque, hwaddr offset, break; case L0_TCNTB: case L1_TCNTB: - lt_i = GET_L_TIMER_IDX(offset); index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i); @@ -1316,7 +1310,6 @@ static void exynos4210_mct_write(void *opaque, hwaddr offset, break; case L0_ICNTB: case L1_ICNTB: - lt_i = GET_L_TIMER_IDX(offset); index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i); @@ -1353,13 +1346,12 @@ static void exynos4210_mct_write(void *opaque, hwaddr offset, if (icntb_max[lt_i] < value) { icntb_max[lt_i] = value; } -DPRINTF("local timer[%d] ICNTB write %llx; max=%x, min=%x\n\n", - lt_i, value, icntb_max[lt_i], icntb_min[lt_i]); + DPRINTF("local timer[%d] ICNTB write %llx; max=%x, min=%x\n\n", + lt_i, value, icntb_max[lt_i], icntb_min[lt_i]); #endif -break; + break; case L0_FRCNTB: case L1_FRCNTB: - lt_i = GET_L_TIMER_IDX(offset); index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i); diff --git a/hw/timer/m48t59.c b/hw/timer/m48t59.c index 474981a6ac..844aad540e 100644 --- a/hw/timer/m48t59.c +++ b/hw/timer/m48t59.c @@ -1,7 +1,7 @@ /* * QEMU M48T59 and M48T08 NVRAM emulation for PPC PREP and Sparc platforms * - * Copyright (c) 2003-2005, 2007 Jocelyn Mayer + * Copyright (c) 2003-2005, 2007, 2017 Jocelyn Mayer * Copyright (c) 2013 Hervé Poussineau * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -159,7 +159,7 @@ static void watchdog_cb (void *opaque) NVRAM->buffer[0x1FF7] = 0x00; NVRAM->buffer[0x1FFC] &= ~0x40; /* May it be a hw CPU Reset instead ? */ - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); } else { qemu_set_irq(NVRAM->IRQ, 1); qemu_set_irq(NVRAM->IRQ, 0); @@ -640,34 +640,33 @@ void m48t59_realize_common(M48t59State *s, Error **errp) s->wd_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &watchdog_cb, s); } qemu_get_timedate(&s->alarm, 0); - - vmstate_register(NULL, -1, &vmstate_m48t59, s); } -static int m48t59_init1(SysBusDevice *dev) +static void m48t59_init1(Object *obj) { - M48txxSysBusDeviceClass *u = M48TXX_SYS_BUS_GET_CLASS(dev); - M48txxSysBusState *d = M48TXX_SYS_BUS(dev); - Object *o = OBJECT(dev); + M48txxSysBusDeviceClass *u = M48TXX_SYS_BUS_GET_CLASS(obj); + M48txxSysBusState *d = M48TXX_SYS_BUS(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); M48t59State *s = &d->state; - Error *err = NULL; s->model = u->info.model; s->size = u->info.size; sysbus_init_irq(dev, &s->IRQ); - memory_region_init_io(&s->iomem, o, &nvram_ops, s, "m48t59.nvram", + memory_region_init_io(&s->iomem, obj, &nvram_ops, s, "m48t59.nvram", s->size); - memory_region_init_io(&d->io, o, &m48t59_io_ops, s, "m48t59", 4); - sysbus_init_mmio(dev, &s->iomem); - sysbus_init_mmio(dev, &d->io); - m48t59_realize_common(s, &err); - if (err != NULL) { - error_free(err); - return -1; - } + memory_region_init_io(&d->io, obj, &m48t59_io_ops, s, "m48t59", 4); +} + +static void m48t59_realize(DeviceState *dev, Error **errp) +{ + M48txxSysBusState *d = M48TXX_SYS_BUS(dev); + M48t59State *s = &d->state; + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - return 0; + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_mmio(sbd, &d->io); + m48t59_realize_common(s, errp); } static uint32_t m48txx_sysbus_read(Nvram *obj, uint32_t addr) @@ -696,12 +695,12 @@ static Property m48t59_sysbus_properties[] = { static void m48txx_sysbus_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); NvramClass *nc = NVRAM_CLASS(klass); - k->init = m48t59_init1; + dc->realize = m48t59_realize; dc->reset = m48t59_reset_sysbus; dc->props = m48t59_sysbus_properties; + dc->vmsd = &vmstate_m48t59; nc->read = m48txx_sysbus_read; nc->write = m48txx_sysbus_write; nc->toggle_lock = m48txx_sysbus_toggle_lock; @@ -725,6 +724,7 @@ static const TypeInfo m48txx_sysbus_type_info = { .name = TYPE_M48TXX_SYS_BUS, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(M48txxSysBusState), + .instance_init = m48t59_init1, .abstract = true, .class_init = m48txx_sysbus_class_init, .interfaces = (InterfaceInfo[]) { diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c index 93de3e1cc5..1b8d3d7d4c 100644 --- a/hw/timer/mc146818rtc.c +++ b/hw/timer/mc146818rtc.c @@ -112,7 +112,6 @@ static uint64_t get_guest_rtc_ns(RTCState *s) guest_clock - s->last_update + s->offset; } -#ifdef TARGET_I386 static void rtc_coalesced_timer_update(RTCState *s) { if (s->irq_coalesced == 0) { @@ -121,21 +120,39 @@ static void rtc_coalesced_timer_update(RTCState *s) /* divide each RTC interval to 2 - 8 smaller intervals */ int c = MIN(s->irq_coalesced, 7) + 1; int64_t next_clock = qemu_clock_get_ns(rtc_clock) + - muldiv64(s->period / c, NANOSECONDS_PER_SECOND, RTC_CLOCK_RATE); + periodic_clock_to_ns(s->period / c); timer_mod(s->coalesced_timer, next_clock); } } +static QLIST_HEAD(, RTCState) rtc_devices = + QLIST_HEAD_INITIALIZER(rtc_devices); + +#ifdef TARGET_I386 +void qmp_rtc_reset_reinjection(Error **errp) +{ + RTCState *s; + + QLIST_FOREACH(s, &rtc_devices, link) { + s->irq_coalesced = 0; + } +} + +static bool rtc_policy_slew_deliver_irq(RTCState *s) +{ + apic_reset_irq_delivered(); + qemu_irq_raise(s->irq); + return apic_get_irq_delivered(); +} + static void rtc_coalesced_timer(void *opaque) { RTCState *s = opaque; if (s->irq_coalesced != 0) { - apic_reset_irq_delivered(); s->cmos_data[RTC_REG_C] |= 0xc0; DPRINTF_C("cmos: injecting from timer\n"); - qemu_irq_raise(s->irq); - if (apic_get_irq_delivered()) { + if (rtc_policy_slew_deliver_irq(s)) { s->irq_coalesced--; DPRINTF_C("cmos: coalesced irqs decreased to %d\n", s->irq_coalesced); @@ -144,40 +161,101 @@ static void rtc_coalesced_timer(void *opaque) rtc_coalesced_timer_update(s); } +#else +static bool rtc_policy_slew_deliver_irq(RTCState *s) +{ + assert(0); + return false; +} #endif -/* handle periodic timer */ -static void periodic_timer_update(RTCState *s, int64_t current_time) +static uint32_t rtc_periodic_clock_ticks(RTCState *s) { - int period_code, period; - int64_t cur_clock, next_irq_clock; + int period_code; + + if (!(s->cmos_data[RTC_REG_B] & REG_B_PIE)) { + return 0; + } period_code = s->cmos_data[RTC_REG_A] & 0x0f; - if (period_code != 0 - && (s->cmos_data[RTC_REG_B] & REG_B_PIE)) { - if (period_code <= 2) - period_code += 7; - /* period in 32 Khz cycles */ - period = 1 << (period_code - 1); -#ifdef TARGET_I386 - if (period != s->period) { - s->irq_coalesced = (s->irq_coalesced * s->period) / period; - DPRINTF_C("cmos: coalesced irqs scaled to %d\n", s->irq_coalesced); - } - s->period = period; -#endif + + return periodic_period_to_clock(period_code); +} + +/* + * handle periodic timer. @old_period indicates the periodic timer update + * is just due to period adjustment. + */ +static void +periodic_timer_update(RTCState *s, int64_t current_time, uint32_t old_period) +{ + uint32_t period; + int64_t cur_clock, next_irq_clock, lost_clock = 0; + + period = rtc_periodic_clock_ticks(s); + + if (period) { /* compute 32 khz clock */ cur_clock = muldiv64(current_time, RTC_CLOCK_RATE, NANOSECONDS_PER_SECOND); - next_irq_clock = (cur_clock & ~(period - 1)) + period; - s->next_periodic_time = muldiv64(next_irq_clock, NANOSECONDS_PER_SECOND, - RTC_CLOCK_RATE) + 1; + /* + * if the periodic timer's update is due to period re-configuration, + * we should count the clock since last interrupt. + */ + if (old_period) { + int64_t last_periodic_clock, next_periodic_clock; + + next_periodic_clock = muldiv64(s->next_periodic_time, + RTC_CLOCK_RATE, NANOSECONDS_PER_SECOND); + last_periodic_clock = next_periodic_clock - old_period; + lost_clock = cur_clock - last_periodic_clock; + assert(lost_clock >= 0); + } + + /* + * s->irq_coalesced can change for two reasons: + * + * a) if one or more periodic timer interrupts have been lost, + * lost_clock will be more that a period. + * + * b) when the period may be reconfigured, we expect the OS to + * treat delayed tick as the new period. So, when switching + * from a shorter to a longer period, scale down the missing, + * because the OS will treat past delayed ticks as longer + * (leftovers are put back into lost_clock). When switching + * to a shorter period, scale up the missing ticks since the + * OS handler will treat past delayed ticks as shorter. + */ + if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) { + uint32_t old_irq_coalesced = s->irq_coalesced; + + s->period = period; + lost_clock += old_irq_coalesced * old_period; + s->irq_coalesced = lost_clock / s->period; + lost_clock %= s->period; + if (old_irq_coalesced != s->irq_coalesced || + old_period != s->period) { + DPRINTF_C("cmos: coalesced irqs scaled from %d to %d, " + "period scaled from %d to %d\n", old_irq_coalesced, + s->irq_coalesced, old_period, s->period); + rtc_coalesced_timer_update(s); + } + } else { + /* + * no way to compensate the interrupt if LOST_TICK_POLICY_SLEW + * is not used, we should make the time progress anyway. + */ + lost_clock = MIN(lost_clock, period); + } + + assert(lost_clock >= 0 && lost_clock <= period); + + next_irq_clock = cur_clock + period - lost_clock; + s->next_periodic_time = periodic_clock_to_ns(next_irq_clock) + 1; timer_mod(s->periodic_timer, s->next_periodic_time); } else { -#ifdef TARGET_I386 s->irq_coalesced = 0; -#endif timer_del(s->periodic_timer); } } @@ -186,25 +264,21 @@ static void rtc_periodic_timer(void *opaque) { RTCState *s = opaque; - periodic_timer_update(s, s->next_periodic_time); + periodic_timer_update(s, s->next_periodic_time, 0); s->cmos_data[RTC_REG_C] |= REG_C_PF; if (s->cmos_data[RTC_REG_B] & REG_B_PIE) { s->cmos_data[RTC_REG_C] |= REG_C_IRQF; -#ifdef TARGET_I386 if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) { if (s->irq_reinject_on_ack_count >= RTC_REINJECT_ON_ACK_COUNT) - s->irq_reinject_on_ack_count = 0; - apic_reset_irq_delivered(); - qemu_irq_raise(s->irq); - if (!apic_get_irq_delivered()) { + s->irq_reinject_on_ack_count = 0; + if (!rtc_policy_slew_deliver_irq(s)) { s->irq_coalesced++; rtc_coalesced_timer_update(s); DPRINTF_C("cmos: coalesced irqs increased to %d\n", s->irq_coalesced); } } else -#endif - qemu_irq_raise(s->irq); + qemu_irq_raise(s->irq); } } @@ -391,6 +465,8 @@ static void cmos_ioport_write(void *opaque, hwaddr addr, uint64_t data, unsigned size) { RTCState *s = opaque; + uint32_t old_period; + bool update_periodic_timer; if ((addr & 1) == 0) { s->cmos_index = data & 0x7f; @@ -423,6 +499,9 @@ static void cmos_ioport_write(void *opaque, hwaddr addr, } break; case RTC_REG_A: + update_periodic_timer = (s->cmos_data[RTC_REG_A] ^ data) & 0x0f; + old_period = rtc_periodic_clock_ticks(s); + if ((data & 0x60) == 0x60) { if (rtc_running(s)) { rtc_update_time(s); @@ -445,10 +524,19 @@ static void cmos_ioport_write(void *opaque, hwaddr addr, /* UIP bit is read only */ s->cmos_data[RTC_REG_A] = (data & ~REG_A_UIP) | (s->cmos_data[RTC_REG_A] & REG_A_UIP); - periodic_timer_update(s, qemu_clock_get_ns(rtc_clock)); + + if (update_periodic_timer) { + periodic_timer_update(s, qemu_clock_get_ns(rtc_clock), + old_period); + } + check_update_timer(s); break; case RTC_REG_B: + update_periodic_timer = (s->cmos_data[RTC_REG_B] ^ data) + & REG_B_PIE; + old_period = rtc_periodic_clock_ticks(s); + if (data & REG_B_SET) { /* update cmos to when the rtc was stopping */ if (rtc_running(s)) { @@ -475,7 +563,12 @@ static void cmos_ioport_write(void *opaque, hwaddr addr, qemu_irq_lower(s->irq); } s->cmos_data[RTC_REG_B] = data; - periodic_timer_update(s, qemu_clock_get_ns(rtc_clock)); + + if (update_periodic_timer) { + periodic_timer_update(s, qemu_clock_get_ns(rtc_clock), + old_period); + } + check_update_timer(s); break; case RTC_REG_C: @@ -529,20 +622,6 @@ static void rtc_get_time(RTCState *s, struct tm *tm) rtc_from_bcd(s, s->cmos_data[RTC_CENTURY]) * 100 - 1900; } -static QLIST_HEAD(, RTCState) rtc_devices = - QLIST_HEAD_INITIALIZER(rtc_devices); - -#ifdef TARGET_I386 -void qmp_rtc_reset_reinjection(Error **errp) -{ - RTCState *s; - - QLIST_FOREACH(s, &rtc_devices, link) { - s->irq_coalesced = 0; - } -} -#endif - static void rtc_set_time(RTCState *s) { struct tm tm; @@ -662,22 +741,19 @@ static uint64_t cmos_ioport_read(void *opaque, hwaddr addr, if (ret & (REG_C_UF | REG_C_AF)) { check_update_timer(s); } -#ifdef TARGET_I386 + if(s->irq_coalesced && (s->cmos_data[RTC_REG_B] & REG_B_PIE) && s->irq_reinject_on_ack_count < RTC_REINJECT_ON_ACK_COUNT) { s->irq_reinject_on_ack_count++; s->cmos_data[RTC_REG_C] |= REG_C_IRQF | REG_C_PF; - apic_reset_irq_delivered(); DPRINTF_C("cmos: injecting on ack\n"); - qemu_irq_raise(s->irq); - if (apic_get_irq_delivered()) { + if (rtc_policy_slew_deliver_irq(s)) { s->irq_coalesced--; DPRINTF_C("cmos: coalesced irqs decreased to %d\n", s->irq_coalesced); } } -#endif break; default: ret = s->cmos_data[s->cmos_index]; @@ -743,17 +819,15 @@ static int rtc_post_load(void *opaque, int version_id) uint64_t now = qemu_clock_get_ns(rtc_clock); if (now < s->next_periodic_time || now > (s->next_periodic_time + get_max_clock_jump())) { - periodic_timer_update(s, qemu_clock_get_ns(rtc_clock)); + periodic_timer_update(s, qemu_clock_get_ns(rtc_clock), 0); } } -#ifdef TARGET_I386 if (version_id >= 2) { if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) { rtc_coalesced_timer_update(s); } } -#endif return 0; } @@ -808,13 +882,12 @@ static void rtc_notify_clock_reset(Notifier *notifier, void *data) int64_t now = *(int64_t *)data; rtc_set_date_from_host(ISA_DEVICE(s)); - periodic_timer_update(s, now); + periodic_timer_update(s, now, 0); check_update_timer(s); -#ifdef TARGET_I386 + if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) { rtc_coalesced_timer_update(s); } -#endif } /* set CMOS shutdown status register (index 0xF) as S3_resume(0xFE) @@ -835,12 +908,10 @@ static void rtc_reset(void *opaque) qemu_irq_lower(s->irq); -#ifdef TARGET_I386 if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) { s->irq_coalesced = 0; s->irq_reinject_on_ack_count = 0; } -#endif } static const MemoryRegionOps cmos_ops = { @@ -886,19 +957,19 @@ static void rtc_realizefn(DeviceState *dev, Error **errp) rtc_set_date_from_host(isadev); -#ifdef TARGET_I386 switch (s->lost_tick_policy) { +#ifdef TARGET_I386 case LOST_TICK_POLICY_SLEW: s->coalesced_timer = timer_new_ns(rtc_clock, rtc_coalesced_timer, s); break; +#endif case LOST_TICK_POLICY_DISCARD: break; default: error_setg(errp, "Invalid lost tick policy."); return; } -#endif s->periodic_timer = timer_new_ns(rtc_clock, rtc_periodic_timer, s); s->update_timer = timer_new_ns(rtc_clock, rtc_update_timer, s); diff --git a/hw/timer/milkymist-sysctl.c b/hw/timer/milkymist-sysctl.c index 44885907c9..93bc6e1790 100644 --- a/hw/timer/milkymist-sysctl.c +++ b/hw/timer/milkymist-sysctl.c @@ -90,7 +90,7 @@ static void sysctl_icap_write(MilkymistSysctlState *s, uint32_t value) trace_milkymist_sysctl_icap_write(value); switch (value & 0xffff) { case 0x000e: - qemu_system_shutdown_request(); + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); break; } } @@ -195,7 +195,7 @@ static void sysctl_write(void *opaque, hwaddr addr, uint64_t value, s->regs[addr] = 1; break; case R_SYSTEM_ID: - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); break; case R_GPIO_IN: diff --git a/hw/timer/pxa2xx_timer.c b/hw/timer/pxa2xx_timer.c index 59002b407e..68ba5a70b3 100644 --- a/hw/timer/pxa2xx_timer.c +++ b/hw/timer/pxa2xx_timer.c @@ -401,7 +401,7 @@ static void pxa2xx_timer_tick(void *opaque) if (t->num == 3) if (i->reset3 & 1) { i->reset3 = 0; - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); } } diff --git a/hw/timer/slavio_timer.c b/hw/timer/slavio_timer.c index bfee1f3027..a8cc9c0148 100644 --- a/hw/timer/slavio_timer.c +++ b/hw/timer/slavio_timer.c @@ -373,9 +373,10 @@ static void slavio_timer_reset(DeviceState *d) s->cputimer_mode = 0; } -static int slavio_timer_init1(SysBusDevice *dev) +static void slavio_timer_init(Object *obj) { - SLAVIO_TIMERState *s = SLAVIO_TIMER(dev); + SLAVIO_TIMERState *s = SLAVIO_TIMER(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); QEMUBH *bh; unsigned int i; TimerContext *tc; @@ -394,14 +395,12 @@ static int slavio_timer_init1(SysBusDevice *dev) size = i == 0 ? SYS_TIMER_SIZE : CPU_TIMER_SIZE; snprintf(timer_name, sizeof(timer_name), "timer-%i", i); - memory_region_init_io(&tc->iomem, OBJECT(s), &slavio_timer_mem_ops, tc, + memory_region_init_io(&tc->iomem, obj, &slavio_timer_mem_ops, tc, timer_name, size); sysbus_init_mmio(dev, &tc->iomem); sysbus_init_irq(dev, &s->cputimer[i].irq); } - - return 0; } static Property slavio_timer_properties[] = { @@ -412,9 +411,7 @@ static Property slavio_timer_properties[] = { static void slavio_timer_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = slavio_timer_init1; dc->reset = slavio_timer_reset; dc->vmsd = &vmstate_slavio_timer; dc->props = slavio_timer_properties; @@ -424,6 +421,7 @@ static const TypeInfo slavio_timer_info = { .name = TYPE_SLAVIO_TIMER, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SLAVIO_TIMERState), + .instance_init = slavio_timer_init, .class_init = slavio_timer_class_init, }; diff --git a/hw/timer/trace-events b/hw/timer/trace-events index d17cfe6b39..fd8196be66 100644 --- a/hw/timer/trace-events +++ b/hw/timer/trace-events @@ -55,3 +55,8 @@ systick_reload(void) "systick reload" systick_timer_tick(void) "systick reload" systick_read(uint64_t addr, uint32_t value, unsigned size) "systick read addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u" systick_write(uint64_t addr, uint32_t value, unsigned size) "systick write addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u" + +# hw/char/cmsdk_apb_timer.c +cmsdk_apb_timer_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB timer read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +cmsdk_apb_timer_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB timer write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +cmsdk_apb_timer_reset(void) "CMSDK APB timer: reset" diff --git a/hw/tricore/tricore_testboard.c b/hw/tricore/tricore_testboard.c index 8910bf0f27..6c574231d5 100644 --- a/hw/tricore/tricore_testboard.c +++ b/hw/tricore/tricore_testboard.c @@ -80,24 +80,18 @@ static void tricore_testboard_init(MachineState *machine, int board_id) exit(1); } env = &cpu->env; - memory_region_init_ram(ext_cram, NULL, "powerlink_ext_c.ram", 2*1024*1024, + memory_region_init_ram(ext_cram, NULL, "powerlink_ext_c.ram", + 2 * 1024 * 1024, &error_fatal); + memory_region_init_ram(ext_dram, NULL, "powerlink_ext_d.ram", + 4 * 1024 * 1024, &error_fatal); + memory_region_init_ram(int_cram, NULL, "powerlink_int_c.ram", 48 * 1024, &error_fatal); - vmstate_register_ram_global(ext_cram); - memory_region_init_ram(ext_dram, NULL, "powerlink_ext_d.ram", 4*1024*1024, + memory_region_init_ram(int_dram, NULL, "powerlink_int_d.ram", 48 * 1024, &error_fatal); - vmstate_register_ram_global(ext_dram); - memory_region_init_ram(int_cram, NULL, "powerlink_int_c.ram", 48*1024, - &error_fatal); - vmstate_register_ram_global(int_cram); - memory_region_init_ram(int_dram, NULL, "powerlink_int_d.ram", 48*1024, - &error_fatal); - vmstate_register_ram_global(int_dram); - memory_region_init_ram(pcp_data, NULL, "powerlink_pcp_data.ram", 16*1024, - &error_fatal); - vmstate_register_ram_global(pcp_data); - memory_region_init_ram(pcp_text, NULL, "powerlink_pcp_text.ram", 32*1024, - &error_fatal); - vmstate_register_ram_global(pcp_text); + memory_region_init_ram(pcp_data, NULL, "powerlink_pcp_data.ram", + 16 * 1024, &error_fatal); + memory_region_init_ram(pcp_text, NULL, "powerlink_pcp_text.ram", + 32 * 1024, &error_fatal); memory_region_add_subregion(sysmem, 0x80000000, ext_cram); memory_region_add_subregion(sysmem, 0xa1000000, ext_dram); diff --git a/hw/unicore32/puv3.c b/hw/unicore32/puv3.c index 032078fd3e..e9d1a60b6f 100644 --- a/hw/unicore32/puv3.c +++ b/hw/unicore32/puv3.c @@ -80,7 +80,6 @@ static void puv3_board_init(CPUUniCore32State *env, ram_addr_t ram_size) /* SDRAM at address zero. */ memory_region_init_ram(ram_memory, NULL, "puv3.ram", ram_size, &error_fatal); - vmstate_register_ram_global(ram_memory); memory_region_add_subregion(get_system_memory(), 0, ram_memory); } diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs index 5958be8ce3..97f1c4561a 100644 --- a/hw/usb/Makefile.objs +++ b/hw/usb/Makefile.objs @@ -8,6 +8,7 @@ common-obj-$(CONFIG_USB_OHCI) += hcd-ohci.o common-obj-$(CONFIG_USB_EHCI) += hcd-ehci.o hcd-ehci-pci.o common-obj-$(CONFIG_USB_EHCI_SYSBUS) += hcd-ehci-sysbus.o common-obj-$(CONFIG_USB_XHCI) += hcd-xhci.o +common-obj-$(CONFIG_USB_XHCI_NEC) += hcd-xhci-nec.o common-obj-$(CONFIG_USB_MUSB) += hcd-musb.o obj-$(CONFIG_TUSB6010) += tusb6010.o diff --git a/hw/usb/ccid-card-emulated.c b/hw/usb/ccid-card-emulated.c index 99627860a3..e646eb243b 100644 --- a/hw/usb/ccid-card-emulated.c +++ b/hw/usb/ccid-card-emulated.c @@ -33,7 +33,7 @@ #include <vcard_emul.h> #include "qemu/thread.h" -#include "sysemu/char.h" +#include "qemu/main-loop.h" #include "ccid.h" #define DPRINTF(card, lvl, fmt, ...) \ diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c index a41b0d6ec5..45d96b03c6 100644 --- a/hw/usb/ccid-card-passthru.c +++ b/hw/usb/ccid-card-passthru.c @@ -9,7 +9,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/char.h" +#include "chardev/char-fe.h" #include "qemu/error-report.h" #include "qemu/sockets.h" #include "ccid.h" @@ -264,10 +264,7 @@ static void ccid_card_vscard_handle_message(PassthruState *card, static void ccid_card_vscard_drop_connection(PassthruState *card) { - Chardev *chr = qemu_chr_fe_get_driver(&card->cs); - - qemu_chr_fe_deinit(&card->cs); - object_unparent(OBJECT(chr)); + qemu_chr_fe_deinit(&card->cs, true); card->vscard_in_pos = card->vscard_in_hdr = 0; } @@ -325,7 +322,7 @@ static void passthru_apdu_from_guest( { PassthruState *card = PASSTHRU_CCID_CARD(base); - if (!qemu_chr_fe_get_driver(&card->cs)) { + if (!qemu_chr_fe_backend_connected(&card->cs)) { printf("ccid-passthru: no chardev, discarding apdu length %d\n", len); return; } @@ -346,12 +343,12 @@ static int passthru_initfn(CCIDCardState *base) card->vscard_in_pos = 0; card->vscard_in_hdr = 0; - if (qemu_chr_fe_get_driver(&card->cs)) { + if (qemu_chr_fe_backend_connected(&card->cs)) { DPRINTF(card, D_INFO, "initing chardev\n"); qemu_chr_fe_set_handlers(&card->cs, ccid_card_vscard_can_read, ccid_card_vscard_read, - ccid_card_vscard_event, card, NULL, true); + ccid_card_vscard_event, NULL, card, NULL, true); ccid_card_vscard_send_init(card); } else { error_report("missing chardev"); diff --git a/hw/usb/core.c b/hw/usb/core.c index 45fa00c517..241ae66b15 100644 --- a/hw/usb/core.c +++ b/hw/usb/core.c @@ -98,6 +98,14 @@ void usb_wakeup(USBEndpoint *ep, unsigned int stream) USBDevice *dev = ep->dev; USBBus *bus = usb_bus_from_device(dev); + if (!qdev_hotplug) { + /* + * This is machine init cold plug. No need to wakeup anyone, + * all devices will be reset anyway. And trying to wakeup can + * cause problems due to hitting uninitialized devices. + */ + return; + } if (dev->remote_wakeup && dev->port && dev->port->ops->wakeup) { dev->port->ops->wakeup(dev->port); } diff --git a/hw/usb/dev-hub.c b/hw/usb/dev-hub.c index 47b7519910..e82a6a6c44 100644 --- a/hw/usb/dev-hub.c +++ b/hw/usb/dev-hub.c @@ -402,7 +402,20 @@ static void usb_hub_handle_control(USBDevice *dev, USBPacket *p, port->wPortChange &= ~PORT_STAT_C_ENABLE; break; case PORT_SUSPEND: - port->wPortStatus &= ~PORT_STAT_SUSPEND; + if (port->wPortStatus & PORT_STAT_SUSPEND) { + port->wPortStatus &= ~PORT_STAT_SUSPEND; + + /* + * USB Spec rev2.0 11.24.2.7.2.3 C_PORT_SUSPEND + * "This bit is set on the following transitions: + * - On transition from the Resuming state to the + * SendEOP [sic] state" + * + * Note that this includes both remote wake-up and + * explicit ClearPortFeature(PORT_SUSPEND). + */ + port->wPortChange |= PORT_STAT_C_SUSPEND; + } break; case PORT_C_SUSPEND: port->wPortChange &= ~PORT_STAT_C_SUSPEND; diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c index 83a4f0e6fb..94b5c34afe 100644 --- a/hw/usb/dev-serial.c +++ b/hw/usb/dev-serial.c @@ -15,7 +15,8 @@ #include "qemu/error-report.h" #include "hw/usb.h" #include "hw/usb/desc.h" -#include "sysemu/char.h" +#include "chardev/char-serial.h" +#include "chardev/char-fe.h" //#define DEBUG_Serial @@ -483,13 +484,12 @@ static void usb_serial_realize(USBDevice *dev, Error **errp) { USBSerialState *s = USB_SERIAL_DEV(dev); Error *local_err = NULL; - Chardev *chr = qemu_chr_fe_get_driver(&s->cs); usb_desc_create_serial(dev); usb_desc_init(dev); dev->auto_attach = 0; - if (!chr) { + if (!qemu_chr_fe_backend_connected(&s->cs)) { error_setg(errp, "Property chardev is required"); return; } @@ -501,10 +501,10 @@ static void usb_serial_realize(USBDevice *dev, Error **errp) } qemu_chr_fe_set_handlers(&s->cs, usb_serial_can_read, usb_serial_read, - usb_serial_event, s, NULL, true); + usb_serial_event, NULL, s, NULL, true); usb_serial_handle_reset(dev); - if (chr->be_open && !dev->attached) { + if (qemu_chr_fe_backend_open(&s->cs) && !dev->attached) { usb_device_attach(dev, &error_abort); } } @@ -516,27 +516,16 @@ static USBDevice *usb_serial_init(USBBus *bus, const char *filename) char label[32]; static int index; - while (*filename && *filename != ':') { - const char *p; - - if (strstart(filename, "vendorid=", &p)) { - error_report("vendorid is not supported anymore"); - return NULL; - } else if (strstart(filename, "productid=", &p)) { - error_report("productid is not supported anymore"); - return NULL; - } else { - error_report("unrecognized serial USB option %s", filename); - return NULL; - } - while(*filename == ',') - filename++; + if (*filename == ':') { + filename++; + } else if (*filename) { + error_report("unrecognized serial USB option %s", filename); + return NULL; } if (!*filename) { error_report("character device specification needed"); return NULL; } - filename++; snprintf(label, sizeof(label), "usbserial%d", index++); cdrv = qemu_chr_new(label, filename); diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c index 49cb1829b5..bef1f03c42 100644 --- a/hw/usb/dev-smartcard-reader.c +++ b/hw/usb/dev-smartcard-reader.c @@ -1314,12 +1314,12 @@ static int ccid_card_init(DeviceState *qdev) int ret = 0; if (card->slot != 0) { - error_report("Warning: usb-ccid supports one slot, can't add %d", - card->slot); + warn_report("usb-ccid supports one slot, can't add %d", + card->slot); return -1; } if (s->card != NULL) { - error_report("Warning: usb-ccid card already full, not adding"); + warn_report("usb-ccid card already full, not adding"); return -1; } ret = ccid_card_initfn(card); diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 50ef817f93..604912cb3e 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -2232,15 +2232,20 @@ static void ehci_update_frindex(EHCIState *ehci, int uframes) ehci->frindex = (ehci->frindex + uframes) % 0x4000; } -static void ehci_frame_timer(void *opaque) +static void ehci_work_bh(void *opaque) { EHCIState *ehci = opaque; int need_timer = 0; int64_t expire_time, t_now; uint64_t ns_elapsed; - int uframes, skipped_uframes; + uint64_t uframes, skipped_uframes; int i; + if (ehci->working) { + return; + } + ehci->working = true; + t_now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); ns_elapsed = t_now - ehci->last_run_ns; uframes = ns_elapsed / UFRAME_TIMER_NS; @@ -2322,6 +2327,15 @@ static void ehci_frame_timer(void *opaque) } timer_mod(ehci->frame_timer, expire_time); } + + ehci->working = false; +} + +static void ehci_work_timer(void *opaque) +{ + EHCIState *ehci = opaque; + + qemu_bh_schedule(ehci->async_bh); } static const MemoryRegionOps ehci_mmio_caps_ops = { @@ -2469,6 +2483,11 @@ void usb_ehci_realize(EHCIState *s, DeviceState *dev, Error **errp) NB_PORTS); return; } + if (s->maxframes < 8 || s->maxframes > 512) { + error_setg(errp, "maxframes %d out if range (8 .. 512)", + s->maxframes); + return; + } usb_bus_new(&s->bus, sizeof(s->bus), s->companion_enable ? &ehci_bus_ops_companion : &ehci_bus_ops_standalone, dev); @@ -2478,8 +2497,8 @@ void usb_ehci_realize(EHCIState *s, DeviceState *dev, Error **errp) s->ports[i].dev = 0; } - s->frame_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, ehci_frame_timer, s); - s->async_bh = qemu_bh_new(ehci_frame_timer, s); + s->frame_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, ehci_work_timer, s); + s->async_bh = qemu_bh_new(ehci_work_bh, s); s->device = dev; s->vmstate = qemu_add_vm_change_state_handler(usb_ehci_vm_state_change, s); diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h index 938d8aa284..821f1ded43 100644 --- a/hw/usb/hcd-ehci.h +++ b/hw/usb/hcd-ehci.h @@ -297,6 +297,7 @@ struct EHCIState { */ QEMUTimer *frame_timer; QEMUBH *async_bh; + bool working; uint32_t astate; /* Current state in asynchronous schedule */ uint32_t pstate; /* Current state in periodic schedule */ USBPort ports[NB_PORTS]; diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index ca72a80f27..e3562a4c60 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -415,7 +415,7 @@ static const VMStateDescription vmstate_uhci = { .post_load = uhci_post_load, .fields = (VMStateField[]) { VMSTATE_PCI_DEVICE(dev, UHCIState), - VMSTATE_UINT8_EQUAL(num_ports_vmstate, UHCIState), + VMSTATE_UINT8_EQUAL(num_ports_vmstate, UHCIState, NULL), VMSTATE_STRUCT_ARRAY(ports, UHCIState, NB_PORTS, 1, vmstate_uhci_port, UHCIPort), VMSTATE_UINT16(cmd, UHCIState), diff --git a/hw/usb/hcd-xhci-nec.c b/hw/usb/hcd-xhci-nec.c new file mode 100644 index 0000000000..75715a048a --- /dev/null +++ b/hw/usb/hcd-xhci-nec.c @@ -0,0 +1,63 @@ +/* + * USB xHCI controller emulation + * + * Copyright (c) 2011 Securiforest + * Date: 2011-05-11 ; Author: Hector Martin <hector@marcansoft.com> + * Based on usb-ohci.c, emulates Renesas NEC USB 3.0 + * + * 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 "hw/hw.h" +#include "hw/usb.h" +#include "hw/pci/pci.h" + +#include "hcd-xhci.h" + +static Property nec_xhci_properties[] = { + DEFINE_PROP_ON_OFF_AUTO("msi", XHCIState, msi, ON_OFF_AUTO_AUTO), + DEFINE_PROP_ON_OFF_AUTO("msix", XHCIState, msix, ON_OFF_AUTO_AUTO), + DEFINE_PROP_BIT("superspeed-ports-first", + XHCIState, flags, XHCI_FLAG_SS_FIRST, true), + DEFINE_PROP_BIT("force-pcie-endcap", XHCIState, flags, + XHCI_FLAG_FORCE_PCIE_ENDCAP, false), + DEFINE_PROP_UINT32("intrs", XHCIState, numintrs, MAXINTRS), + DEFINE_PROP_UINT32("slots", XHCIState, numslots, MAXSLOTS), + DEFINE_PROP_END_OF_LIST(), +}; + +static void nec_xhci_class_init(ObjectClass *klass, void *data) +{ + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->props = nec_xhci_properties; + k->vendor_id = PCI_VENDOR_ID_NEC; + k->device_id = PCI_DEVICE_ID_NEC_UPD720200; + k->revision = 0x03; +} + +static const TypeInfo nec_xhci_info = { + .name = TYPE_NEC_XHCI, + .parent = TYPE_XHCI, + .class_init = nec_xhci_class_init, +}; + +static void nec_xhci_register_types(void) +{ + type_register_static(&nec_xhci_info); +} + +type_init(nec_xhci_register_types) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 77d8e1137a..204ea69d3f 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -29,6 +29,8 @@ #include "trace.h" #include "qapi/error.h" +#include "hcd-xhci.h" + //#define DEBUG_XHCI //#define DEBUG_DATA @@ -40,16 +42,6 @@ #define FIXME(_msg) do { fprintf(stderr, "FIXME %s:%d %s\n", \ __func__, __LINE__, _msg); abort(); } while (0) -#define MAXPORTS_2 15 -#define MAXPORTS_3 15 - -#define MAXPORTS (MAXPORTS_2+MAXPORTS_3) -#define MAXSLOTS 64 -#define MAXINTRS 16 - -/* Very pessimistic, let's hope it's enough for all cases */ -#define EV_QUEUE (((3 * 24) + 16) * MAXSLOTS) - #define TRB_LINK_LIMIT 32 #define COMMAND_LIMIT 256 #define TRANSFER_LIMIT 256 @@ -164,84 +156,8 @@ enum { PLS_RESUME = 15, }; -typedef enum TRBType { - TRB_RESERVED = 0, - TR_NORMAL, - TR_SETUP, - TR_DATA, - TR_STATUS, - TR_ISOCH, - TR_LINK, - TR_EVDATA, - TR_NOOP, - CR_ENABLE_SLOT, - CR_DISABLE_SLOT, - CR_ADDRESS_DEVICE, - CR_CONFIGURE_ENDPOINT, - CR_EVALUATE_CONTEXT, - CR_RESET_ENDPOINT, - CR_STOP_ENDPOINT, - CR_SET_TR_DEQUEUE, - CR_RESET_DEVICE, - CR_FORCE_EVENT, - CR_NEGOTIATE_BW, - CR_SET_LATENCY_TOLERANCE, - CR_GET_PORT_BANDWIDTH, - CR_FORCE_HEADER, - CR_NOOP, - ER_TRANSFER = 32, - ER_COMMAND_COMPLETE, - ER_PORT_STATUS_CHANGE, - ER_BANDWIDTH_REQUEST, - ER_DOORBELL, - ER_HOST_CONTROLLER, - ER_DEVICE_NOTIFICATION, - ER_MFINDEX_WRAP, - /* vendor specific bits */ - CR_VENDOR_NEC_FIRMWARE_REVISION = 49, - CR_VENDOR_NEC_CHALLENGE_RESPONSE = 50, -} TRBType; - #define CR_LINK TR_LINK -typedef enum TRBCCode { - CC_INVALID = 0, - CC_SUCCESS, - CC_DATA_BUFFER_ERROR, - CC_BABBLE_DETECTED, - CC_USB_TRANSACTION_ERROR, - CC_TRB_ERROR, - CC_STALL_ERROR, - CC_RESOURCE_ERROR, - CC_BANDWIDTH_ERROR, - CC_NO_SLOTS_ERROR, - CC_INVALID_STREAM_TYPE_ERROR, - CC_SLOT_NOT_ENABLED_ERROR, - CC_EP_NOT_ENABLED_ERROR, - CC_SHORT_PACKET, - CC_RING_UNDERRUN, - CC_RING_OVERRUN, - CC_VF_ER_FULL, - CC_PARAMETER_ERROR, - CC_BANDWIDTH_OVERRUN, - CC_CONTEXT_STATE_ERROR, - CC_NO_PING_RESPONSE_ERROR, - CC_EVENT_RING_FULL_ERROR, - CC_INCOMPATIBLE_DEVICE_ERROR, - CC_MISSED_SERVICE_ERROR, - CC_COMMAND_RING_STOPPED, - CC_COMMAND_ABORTED, - CC_STOPPED, - CC_STOPPED_LENGTH_INVALID, - CC_MAX_EXIT_LATENCY_TOO_LARGE_ERROR = 29, - CC_ISOCH_BUFFER_OVERRUN = 31, - CC_EVENT_LOST_ERROR, - CC_UNDEFINED_ERROR, - CC_INVALID_STREAM_ID_ERROR, - CC_SECONDARY_BANDWIDTH_ERROR, - CC_SPLIT_TRANSACTION_ERROR -} TRBCCode; - #define TRB_C (1<<0) #define TRB_TYPE_SHIFT 10 #define TRB_TYPE_MASK 0x3f @@ -301,10 +217,6 @@ typedef enum TRBCCode { #define SLOT_CONTEXT_ENTRIES_MASK 0x1f #define SLOT_CONTEXT_ENTRIES_SHIFT 27 -typedef struct XHCIState XHCIState; -typedef struct XHCIStreamContext XHCIStreamContext; -typedef struct XHCIEPContext XHCIEPContext; - #define get_field(data, field) \ (((data) >> field##_SHIFT) & field##_MASK) @@ -326,21 +238,6 @@ typedef enum EPType { ET_INTR_IN, } EPType; -typedef struct XHCIRing { - dma_addr_t dequeue; - bool ccs; -} XHCIRing; - -typedef struct XHCIPort { - XHCIState *xhci; - uint32_t portsc; - uint32_t portnr; - USBPort *uport; - uint32_t speedmask; - char name[16]; - MemoryRegion mem; -} XHCIPort; - typedef struct XHCITransfer { XHCIEPContext *epctx; USBPacket packet; @@ -402,101 +299,6 @@ struct XHCIEPContext { QEMUTimer *kick_timer; }; -typedef struct XHCISlot { - bool enabled; - bool addressed; - dma_addr_t ctx; - USBPort *uport; - XHCIEPContext * eps[31]; -} XHCISlot; - -typedef struct XHCIEvent { - TRBType type; - TRBCCode ccode; - uint64_t ptr; - uint32_t length; - uint32_t flags; - uint8_t slotid; - uint8_t epid; -} XHCIEvent; - -typedef struct XHCIInterrupter { - uint32_t iman; - uint32_t imod; - uint32_t erstsz; - uint32_t erstba_low; - uint32_t erstba_high; - uint32_t erdp_low; - uint32_t erdp_high; - - bool msix_used, er_pcs; - - dma_addr_t er_start; - uint32_t er_size; - unsigned int er_ep_idx; - - /* kept for live migration compat only */ - bool er_full_unused; - XHCIEvent ev_buffer[EV_QUEUE]; - unsigned int ev_buffer_put; - unsigned int ev_buffer_get; - -} XHCIInterrupter; - -struct XHCIState { - /*< private >*/ - PCIDevice parent_obj; - /*< public >*/ - - USBBus bus; - MemoryRegion mem; - MemoryRegion mem_cap; - MemoryRegion mem_oper; - MemoryRegion mem_runtime; - MemoryRegion mem_doorbell; - - /* properties */ - uint32_t numports_2; - uint32_t numports_3; - uint32_t numintrs; - uint32_t numslots; - uint32_t flags; - uint32_t max_pstreams_mask; - OnOffAuto msi; - OnOffAuto msix; - - /* Operational Registers */ - uint32_t usbcmd; - uint32_t usbsts; - uint32_t dnctrl; - uint32_t crcr_low; - uint32_t crcr_high; - uint32_t dcbaap_low; - uint32_t dcbaap_high; - uint32_t config; - - USBPort uports[MAX(MAXPORTS_2, MAXPORTS_3)]; - XHCIPort ports[MAXPORTS]; - XHCISlot slots[MAXSLOTS]; - uint32_t numports; - - /* Runtime Registers */ - int64_t mfindex_start; - QEMUTimer *mfwrap_timer; - XHCIInterrupter intr[MAXINTRS]; - - XHCIRing cmd_ring; - - bool nec_quirks; -}; - -#define TYPE_XHCI "base-xhci" -#define TYPE_NEC_XHCI "nec-usb-xhci" -#define TYPE_QEMU_XHCI "qemu-xhci" - -#define XHCI(obj) \ - OBJECT_CHECK(XHCIState, (obj), TYPE_XHCI) - typedef struct XHCIEvRingSeg { uint32_t addr_low; uint32_t addr_high; @@ -504,12 +306,6 @@ typedef struct XHCIEvRingSeg { uint32_t rsvd; } XHCIEvRingSeg; -enum xhci_flags { - XHCI_FLAG_SS_FIRST = 1, - XHCI_FLAG_FORCE_PCIE_ENDCAP, - XHCI_FLAG_ENABLE_STREAMS, -}; - static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid, unsigned int streamid); static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid); @@ -2116,6 +1912,8 @@ static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid) } assert(!xfer->running_retry); if (xfer->complete) { + /* update ring dequeue ptr */ + xhci_set_ep_state(xhci, epctx, stctx, epctx->state); xhci_ep_free_xfer(epctx->retry); } epctx->retry = NULL; @@ -2166,6 +1964,8 @@ static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid) xhci_fire_transfer(xhci, xfer, epctx); } if (xfer->complete) { + /* update ring dequeue ptr */ + xhci_set_ep_state(xhci, epctx, stctx, epctx->state); xhci_ep_free_xfer(xfer); xfer = NULL; } @@ -2183,8 +1983,6 @@ static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid) break; } } - /* update ring dequeue ptr */ - xhci_set_ep_state(xhci, epctx, stctx, epctx->state); epctx->kick_active--; ep = xhci_epid_to_usbep(epctx); @@ -3621,7 +3419,7 @@ static void usb_xhci_realize(struct PCIDevice *dev, Error **errp) if (pci_bus_is_express(dev->bus) || xhci_get_flag(xhci, XHCI_FLAG_FORCE_PCIE_ENDCAP)) { ret = pcie_endpoint_cap_init(dev, 0xa0); - assert(ret >= 0); + assert(ret > 0); } if (xhci->msix != ON_OFF_AUTO_OFF) { @@ -3843,18 +3641,6 @@ static const VMStateDescription vmstate_xhci = { } }; -static Property nec_xhci_properties[] = { - DEFINE_PROP_ON_OFF_AUTO("msi", XHCIState, msi, ON_OFF_AUTO_AUTO), - DEFINE_PROP_ON_OFF_AUTO("msix", XHCIState, msix, ON_OFF_AUTO_AUTO), - DEFINE_PROP_BIT("superspeed-ports-first", - XHCIState, flags, XHCI_FLAG_SS_FIRST, true), - DEFINE_PROP_BIT("force-pcie-endcap", XHCIState, flags, - XHCI_FLAG_FORCE_PCIE_ENDCAP, false), - DEFINE_PROP_UINT32("intrs", XHCIState, numintrs, MAXINTRS), - DEFINE_PROP_UINT32("slots", XHCIState, numslots, MAXSLOTS), - DEFINE_PROP_END_OF_LIST(), -}; - static Property xhci_properties[] = { DEFINE_PROP_BIT("streams", XHCIState, flags, XHCI_FLAG_ENABLE_STREAMS, true), @@ -3886,23 +3672,6 @@ static const TypeInfo xhci_info = { .abstract = true, }; -static void nec_xhci_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->props = nec_xhci_properties; - k->vendor_id = PCI_VENDOR_ID_NEC; - k->device_id = PCI_DEVICE_ID_NEC_UPD720200; - k->revision = 0x03; -} - -static const TypeInfo nec_xhci_info = { - .name = TYPE_NEC_XHCI, - .parent = TYPE_XHCI, - .class_init = nec_xhci_class_init, -}; - static void qemu_xhci_class_init(ObjectClass *klass, void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -3933,7 +3702,6 @@ static const TypeInfo qemu_xhci_info = { static void xhci_register_types(void) { type_register_static(&xhci_info); - type_register_static(&nec_xhci_info); type_register_static(&qemu_xhci_info); } diff --git a/hw/usb/hcd-xhci.h b/hw/usb/hcd-xhci.h new file mode 100644 index 0000000000..fc36a4c787 --- /dev/null +++ b/hw/usb/hcd-xhci.h @@ -0,0 +1,226 @@ +/* + * USB xHCI controller emulation + * + * Copyright (c) 2011 Securiforest + * Date: 2011-05-11 ; Author: Hector Martin <hector@marcansoft.com> + * Based on usb-ohci.c, emulates Renesas NEC USB 3.0 + * + * 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/>. + */ + +#define TYPE_XHCI "base-xhci" +#define TYPE_NEC_XHCI "nec-usb-xhci" +#define TYPE_QEMU_XHCI "qemu-xhci" + +#define XHCI(obj) \ + OBJECT_CHECK(XHCIState, (obj), TYPE_XHCI) + +#define MAXPORTS_2 15 +#define MAXPORTS_3 15 + +#define MAXPORTS (MAXPORTS_2 + MAXPORTS_3) +#define MAXSLOTS 64 +#define MAXINTRS 16 + +/* Very pessimistic, let's hope it's enough for all cases */ +#define EV_QUEUE (((3 * 24) + 16) * MAXSLOTS) + +typedef struct XHCIState XHCIState; +typedef struct XHCIStreamContext XHCIStreamContext; +typedef struct XHCIEPContext XHCIEPContext; + +enum xhci_flags { + XHCI_FLAG_SS_FIRST = 1, + XHCI_FLAG_FORCE_PCIE_ENDCAP, + XHCI_FLAG_ENABLE_STREAMS, +}; + +typedef enum TRBType { + TRB_RESERVED = 0, + TR_NORMAL, + TR_SETUP, + TR_DATA, + TR_STATUS, + TR_ISOCH, + TR_LINK, + TR_EVDATA, + TR_NOOP, + CR_ENABLE_SLOT, + CR_DISABLE_SLOT, + CR_ADDRESS_DEVICE, + CR_CONFIGURE_ENDPOINT, + CR_EVALUATE_CONTEXT, + CR_RESET_ENDPOINT, + CR_STOP_ENDPOINT, + CR_SET_TR_DEQUEUE, + CR_RESET_DEVICE, + CR_FORCE_EVENT, + CR_NEGOTIATE_BW, + CR_SET_LATENCY_TOLERANCE, + CR_GET_PORT_BANDWIDTH, + CR_FORCE_HEADER, + CR_NOOP, + ER_TRANSFER = 32, + ER_COMMAND_COMPLETE, + ER_PORT_STATUS_CHANGE, + ER_BANDWIDTH_REQUEST, + ER_DOORBELL, + ER_HOST_CONTROLLER, + ER_DEVICE_NOTIFICATION, + ER_MFINDEX_WRAP, + /* vendor specific bits */ + CR_VENDOR_NEC_FIRMWARE_REVISION = 49, + CR_VENDOR_NEC_CHALLENGE_RESPONSE = 50, +} TRBType; + +typedef enum TRBCCode { + CC_INVALID = 0, + CC_SUCCESS, + CC_DATA_BUFFER_ERROR, + CC_BABBLE_DETECTED, + CC_USB_TRANSACTION_ERROR, + CC_TRB_ERROR, + CC_STALL_ERROR, + CC_RESOURCE_ERROR, + CC_BANDWIDTH_ERROR, + CC_NO_SLOTS_ERROR, + CC_INVALID_STREAM_TYPE_ERROR, + CC_SLOT_NOT_ENABLED_ERROR, + CC_EP_NOT_ENABLED_ERROR, + CC_SHORT_PACKET, + CC_RING_UNDERRUN, + CC_RING_OVERRUN, + CC_VF_ER_FULL, + CC_PARAMETER_ERROR, + CC_BANDWIDTH_OVERRUN, + CC_CONTEXT_STATE_ERROR, + CC_NO_PING_RESPONSE_ERROR, + CC_EVENT_RING_FULL_ERROR, + CC_INCOMPATIBLE_DEVICE_ERROR, + CC_MISSED_SERVICE_ERROR, + CC_COMMAND_RING_STOPPED, + CC_COMMAND_ABORTED, + CC_STOPPED, + CC_STOPPED_LENGTH_INVALID, + CC_MAX_EXIT_LATENCY_TOO_LARGE_ERROR = 29, + CC_ISOCH_BUFFER_OVERRUN = 31, + CC_EVENT_LOST_ERROR, + CC_UNDEFINED_ERROR, + CC_INVALID_STREAM_ID_ERROR, + CC_SECONDARY_BANDWIDTH_ERROR, + CC_SPLIT_TRANSACTION_ERROR +} TRBCCode; + +typedef struct XHCIRing { + dma_addr_t dequeue; + bool ccs; +} XHCIRing; + +typedef struct XHCIPort { + XHCIState *xhci; + uint32_t portsc; + uint32_t portnr; + USBPort *uport; + uint32_t speedmask; + char name[16]; + MemoryRegion mem; +} XHCIPort; + +typedef struct XHCISlot { + bool enabled; + bool addressed; + dma_addr_t ctx; + USBPort *uport; + XHCIEPContext *eps[31]; +} XHCISlot; + +typedef struct XHCIEvent { + TRBType type; + TRBCCode ccode; + uint64_t ptr; + uint32_t length; + uint32_t flags; + uint8_t slotid; + uint8_t epid; +} XHCIEvent; + +typedef struct XHCIInterrupter { + uint32_t iman; + uint32_t imod; + uint32_t erstsz; + uint32_t erstba_low; + uint32_t erstba_high; + uint32_t erdp_low; + uint32_t erdp_high; + + bool msix_used, er_pcs; + + dma_addr_t er_start; + uint32_t er_size; + unsigned int er_ep_idx; + + /* kept for live migration compat only */ + bool er_full_unused; + XHCIEvent ev_buffer[EV_QUEUE]; + unsigned int ev_buffer_put; + unsigned int ev_buffer_get; + +} XHCIInterrupter; + +struct XHCIState { + /*< private >*/ + PCIDevice parent_obj; + /*< public >*/ + + USBBus bus; + MemoryRegion mem; + MemoryRegion mem_cap; + MemoryRegion mem_oper; + MemoryRegion mem_runtime; + MemoryRegion mem_doorbell; + + /* properties */ + uint32_t numports_2; + uint32_t numports_3; + uint32_t numintrs; + uint32_t numslots; + uint32_t flags; + uint32_t max_pstreams_mask; + OnOffAuto msi; + OnOffAuto msix; + + /* Operational Registers */ + uint32_t usbcmd; + uint32_t usbsts; + uint32_t dnctrl; + uint32_t crcr_low; + uint32_t crcr_high; + uint32_t dcbaap_low; + uint32_t dcbaap_high; + uint32_t config; + + USBPort uports[MAX(MAXPORTS_2, MAXPORTS_3)]; + XHCIPort ports[MAXPORTS]; + XHCISlot slots[MAXSLOTS]; + uint32_t numports; + + /* Runtime Registers */ + int64_t mfindex_start; + QEMUTimer *mfwrap_timer; + XHCIInterrupter intr[MAXINTRS]; + + XHCIRing cmd_ring; + + bool nec_quirks; +}; diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index f9c8eafe06..1b0be071cc 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -1107,7 +1107,7 @@ static void usb_host_detach_kernel(USBHostDevice *s) if (rc != 0) { return; } - for (i = 0; i < conf->bNumInterfaces; i++) { + for (i = 0; i < USB_MAX_INTERFACES; i++) { rc = libusb_kernel_driver_active(s->dh, i); usb_host_libusb_error("libusb_kernel_driver_active", rc); if (rc != 1) { @@ -1130,7 +1130,7 @@ static void usb_host_attach_kernel(USBHostDevice *s) if (rc != 0) { return; } - for (i = 0; i < conf->bNumInterfaces; i++) { + for (i = 0; i < USB_MAX_INTERFACES; i++) { if (!s->ifs[i].detached) { continue; } @@ -1145,7 +1145,7 @@ static int usb_host_claim_interfaces(USBHostDevice *s, int configuration) { USBDevice *udev = USB_DEVICE(s); struct libusb_config_descriptor *conf; - int rc, i; + int rc, i, claimed; for (i = 0; i < USB_MAX_INTERFACES; i++) { udev->altsetting[i] = 0; @@ -1164,14 +1164,19 @@ static int usb_host_claim_interfaces(USBHostDevice *s, int configuration) return USB_RET_STALL; } - for (i = 0; i < conf->bNumInterfaces; i++) { + claimed = 0; + for (i = 0; i < USB_MAX_INTERFACES; i++) { trace_usb_host_claim_interface(s->bus_num, s->addr, configuration, i); rc = libusb_claim_interface(s->dh, i); - usb_host_libusb_error("libusb_claim_interface", rc); - if (rc != 0) { - return USB_RET_STALL; + if (rc == 0) { + s->ifs[i].claimed = true; + if (++claimed == conf->bNumInterfaces) { + break; + } } - s->ifs[i].claimed = true; + } + if (claimed != conf->bNumInterfaces) { + return USB_RET_STALL; } udev->ninterfaces = conf->bNumInterfaces; @@ -1183,10 +1188,9 @@ static int usb_host_claim_interfaces(USBHostDevice *s, int configuration) static void usb_host_release_interfaces(USBHostDevice *s) { - USBDevice *udev = USB_DEVICE(s); int i, rc; - for (i = 0; i < udev->ninterfaces; i++) { + for (i = 0; i < USB_MAX_INTERFACES; i++) { if (!s->ifs[i].claimed) { continue; } diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index ad5ef783a6..5e42730449 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -33,7 +33,7 @@ #include "qapi/qmp/qerror.h" #include "qemu/error-report.h" #include "qemu/iov.h" -#include "sysemu/char.h" +#include "chardev/char-fe.h" #include <usbredirparser.h> #include <usbredirfilter.h> @@ -193,7 +193,7 @@ static void usbredir_handle_status(USBRedirDevice *dev, USBPacket *p, #define WARNING(...) \ do { \ if (dev->debug >= usbredirparser_warning) { \ - error_report("usb-redir warning: " __VA_ARGS__); \ + warn_report("" __VA_ARGS__); \ } \ } while (0) #define INFO(...) \ @@ -273,10 +273,9 @@ static gboolean usbredir_write_unblocked(GIOChannel *chan, GIOCondition cond, static int usbredir_write(void *priv, uint8_t *data, int count) { USBRedirDevice *dev = priv; - Chardev *chr = qemu_chr_fe_get_driver(&dev->cs); int r; - if (!chr->be_open) { + if (!qemu_chr_fe_backend_open(&dev->cs)) { return 0; } @@ -1366,7 +1365,7 @@ static void usbredir_realize(USBDevice *udev, Error **errp) USBRedirDevice *dev = USB_REDIRECT(udev); int i; - if (!qemu_chr_fe_get_driver(&dev->cs)) { + if (!qemu_chr_fe_backend_connected(&dev->cs)) { error_setg(errp, QERR_MISSING_PARAMETER, "chardev"); return; } @@ -1399,7 +1398,7 @@ static void usbredir_realize(USBDevice *udev, Error **errp) /* Let the backend know we are ready */ qemu_chr_fe_set_handlers(&dev->cs, usbredir_chardev_can_read, usbredir_chardev_read, usbredir_chardev_event, - dev, NULL, true); + NULL, dev, NULL, true); dev->vmstate = qemu_add_vm_change_state_handler(usbredir_vm_state_change, dev); @@ -1419,10 +1418,8 @@ static void usbredir_cleanup_device_queues(USBRedirDevice *dev) static void usbredir_unrealize(USBDevice *udev, Error **errp) { USBRedirDevice *dev = USB_REDIRECT(udev); - Chardev *chr = qemu_chr_fe_get_driver(&dev->cs); - qemu_chr_fe_deinit(&dev->cs); - object_unparent(OBJECT(chr)); + qemu_chr_fe_deinit(&dev->cs, true); /* Note must be done after qemu_chr_close, as that causes a close event */ qemu_bh_delete(dev->chardev_close_bh); diff --git a/hw/usb/xen-usb.c b/hw/usb/xen-usb.c index fe62183fe3..584a6f2442 100644 --- a/hw/usb/xen-usb.c +++ b/hw/usb/xen-usb.c @@ -30,7 +30,6 @@ #include "hw/xen/xen_backend.h" #include "monitor/qdev.h" #include "qapi/qmp/qbool.h" -#include "qapi/qmp/qint.h" #include "qapi/qmp/qstring.h" #include "hw/xen/io/ring.h" diff --git a/hw/vfio/Makefile.objs b/hw/vfio/Makefile.objs index 05e7fbb93f..c3ab9097f1 100644 --- a/hw/vfio/Makefile.objs +++ b/hw/vfio/Makefile.objs @@ -1,6 +1,7 @@ ifeq ($(CONFIG_LINUX), y) obj-$(CONFIG_SOFTMMU) += common.o obj-$(CONFIG_PCI) += pci.o pci-quirks.o +obj-$(CONFIG_VFIO_CCW) += ccw.o obj-$(CONFIG_SOFTMMU) += platform.o obj-$(CONFIG_VFIO_XGMAC) += calxeda-xgmac.o obj-$(CONFIG_VFIO_AMD_XGBE) += amd-xgbe.o diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c new file mode 100644 index 0000000000..12d0262336 --- /dev/null +++ b/hw/vfio/ccw.c @@ -0,0 +1,434 @@ +/* + * vfio based subchannel assignment support + * + * Copyright 2017 IBM Corp. + * Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com> + * Xiao Feng Ren <renxiaof@linux.vnet.ibm.com> + * Pierre Morel <pmorel@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or(at + * your option) any version. See the COPYING file in the top-level + * directory. + */ + +#include <linux/vfio.h> +#include <linux/vfio_ccw.h> +#include <sys/ioctl.h> + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/sysbus.h" +#include "hw/vfio/vfio.h" +#include "hw/vfio/vfio-common.h" +#include "hw/s390x/s390-ccw.h" +#include "hw/s390x/ccw-device.h" +#include "qemu/error-report.h" + +#define TYPE_VFIO_CCW "vfio-ccw" +typedef struct VFIOCCWDevice { + S390CCWDevice cdev; + VFIODevice vdev; + uint64_t io_region_size; + uint64_t io_region_offset; + struct ccw_io_region *io_region; + EventNotifier io_notifier; +} VFIOCCWDevice; + +static void vfio_ccw_compute_needs_reset(VFIODevice *vdev) +{ + vdev->needs_reset = false; +} + +/* + * We don't need vfio_hot_reset_multi and vfio_eoi operations for + * vfio_ccw device now. + */ +struct VFIODeviceOps vfio_ccw_ops = { + .vfio_compute_needs_reset = vfio_ccw_compute_needs_reset, +}; + +static int vfio_ccw_handle_request(ORB *orb, SCSW *scsw, void *data) +{ + S390CCWDevice *cdev = data; + VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev); + struct ccw_io_region *region = vcdev->io_region; + int ret; + + QEMU_BUILD_BUG_ON(sizeof(region->orb_area) != sizeof(ORB)); + QEMU_BUILD_BUG_ON(sizeof(region->scsw_area) != sizeof(SCSW)); + QEMU_BUILD_BUG_ON(sizeof(region->irb_area) != sizeof(IRB)); + + memset(region, 0, sizeof(*region)); + + memcpy(region->orb_area, orb, sizeof(ORB)); + memcpy(region->scsw_area, scsw, sizeof(SCSW)); + +again: + ret = pwrite(vcdev->vdev.fd, region, + vcdev->io_region_size, vcdev->io_region_offset); + if (ret != vcdev->io_region_size) { + if (errno == EAGAIN) { + goto again; + } + error_report("vfio-ccw: wirte I/O region failed with errno=%d", errno); + return -errno; + } + + return region->ret_code; +} + +static void vfio_ccw_reset(DeviceState *dev) +{ + CcwDevice *ccw_dev = DO_UPCAST(CcwDevice, parent_obj, dev); + S390CCWDevice *cdev = DO_UPCAST(S390CCWDevice, parent_obj, ccw_dev); + VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev); + + ioctl(vcdev->vdev.fd, VFIO_DEVICE_RESET); +} + +static void vfio_ccw_io_notifier_handler(void *opaque) +{ + VFIOCCWDevice *vcdev = opaque; + struct ccw_io_region *region = vcdev->io_region; + S390CCWDevice *cdev = S390_CCW_DEVICE(vcdev); + CcwDevice *ccw_dev = CCW_DEVICE(cdev); + SubchDev *sch = ccw_dev->sch; + SCSW *s = &sch->curr_status.scsw; + PMCW *p = &sch->curr_status.pmcw; + IRB irb; + int size; + + if (!event_notifier_test_and_clear(&vcdev->io_notifier)) { + return; + } + + size = pread(vcdev->vdev.fd, region, vcdev->io_region_size, + vcdev->io_region_offset); + if (size == -1) { + switch (errno) { + case ENODEV: + /* Generate a deferred cc 3 condition. */ + s->flags |= SCSW_FLAGS_MASK_CC; + s->ctrl &= ~SCSW_CTRL_MASK_STCTL; + s->ctrl |= (SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND); + goto read_err; + case EFAULT: + /* Memory problem, generate channel data check. */ + s->ctrl &= ~SCSW_ACTL_START_PEND; + s->cstat = SCSW_CSTAT_DATA_CHECK; + s->ctrl &= ~SCSW_CTRL_MASK_STCTL; + s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | + SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; + goto read_err; + default: + /* Error, generate channel program check. */ + s->ctrl &= ~SCSW_ACTL_START_PEND; + s->cstat = SCSW_CSTAT_PROG_CHECK; + s->ctrl &= ~SCSW_CTRL_MASK_STCTL; + s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | + SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; + goto read_err; + } + } else if (size != vcdev->io_region_size) { + /* Information transfer error, generate channel-control check. */ + s->ctrl &= ~SCSW_ACTL_START_PEND; + s->cstat = SCSW_CSTAT_CHN_CTRL_CHK; + s->ctrl &= ~SCSW_CTRL_MASK_STCTL; + s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | + SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; + goto read_err; + } + + memcpy(&irb, region->irb_area, sizeof(IRB)); + + /* Update control block via irb. */ + copy_scsw_to_guest(s, &irb.scsw); + + /* If a uint check is pending, copy sense data. */ + if ((s->dstat & SCSW_DSTAT_UNIT_CHECK) && + (p->chars & PMCW_CHARS_MASK_CSENSE)) { + memcpy(sch->sense_data, irb.ecw, sizeof(irb.ecw)); + } + +read_err: + css_inject_io_interrupt(sch); +} + +static void vfio_ccw_register_io_notifier(VFIOCCWDevice *vcdev, Error **errp) +{ + VFIODevice *vdev = &vcdev->vdev; + struct vfio_irq_info *irq_info; + struct vfio_irq_set *irq_set; + size_t argsz; + int32_t *pfd; + + if (vdev->num_irqs < VFIO_CCW_IO_IRQ_INDEX + 1) { + error_setg(errp, "vfio: unexpected number of io irqs %u", + vdev->num_irqs); + return; + } + + argsz = sizeof(*irq_set); + irq_info = g_malloc0(argsz); + irq_info->index = VFIO_CCW_IO_IRQ_INDEX; + irq_info->argsz = argsz; + if (ioctl(vdev->fd, VFIO_DEVICE_GET_IRQ_INFO, + irq_info) < 0 || irq_info->count < 1) { + error_setg_errno(errp, errno, "vfio: Error getting irq info"); + goto out_free_info; + } + + if (event_notifier_init(&vcdev->io_notifier, 0)) { + error_setg_errno(errp, errno, + "vfio: Unable to init event notifier for IO"); + goto out_free_info; + } + + argsz = sizeof(*irq_set) + sizeof(*pfd); + irq_set = g_malloc0(argsz); + irq_set->argsz = argsz; + irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | + VFIO_IRQ_SET_ACTION_TRIGGER; + irq_set->index = VFIO_CCW_IO_IRQ_INDEX; + irq_set->start = 0; + irq_set->count = 1; + pfd = (int32_t *) &irq_set->data; + + *pfd = event_notifier_get_fd(&vcdev->io_notifier); + qemu_set_fd_handler(*pfd, vfio_ccw_io_notifier_handler, NULL, vcdev); + if (ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, irq_set)) { + error_setg(errp, "vfio: Failed to set up io notification"); + qemu_set_fd_handler(*pfd, NULL, NULL, vcdev); + event_notifier_cleanup(&vcdev->io_notifier); + } + + g_free(irq_set); + +out_free_info: + g_free(irq_info); +} + +static void vfio_ccw_unregister_io_notifier(VFIOCCWDevice *vcdev) +{ + struct vfio_irq_set *irq_set; + size_t argsz; + int32_t *pfd; + + argsz = sizeof(*irq_set) + sizeof(*pfd); + irq_set = g_malloc0(argsz); + irq_set->argsz = argsz; + irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | + VFIO_IRQ_SET_ACTION_TRIGGER; + irq_set->index = VFIO_CCW_IO_IRQ_INDEX; + irq_set->start = 0; + irq_set->count = 1; + pfd = (int32_t *) &irq_set->data; + *pfd = -1; + + if (ioctl(vcdev->vdev.fd, VFIO_DEVICE_SET_IRQS, irq_set)) { + error_report("vfio: Failed to de-assign device io fd: %m"); + } + + qemu_set_fd_handler(event_notifier_get_fd(&vcdev->io_notifier), + NULL, NULL, vcdev); + event_notifier_cleanup(&vcdev->io_notifier); + + g_free(irq_set); +} + +static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp) +{ + VFIODevice *vdev = &vcdev->vdev; + struct vfio_region_info *info; + int ret; + + /* Sanity check device */ + if (!(vdev->flags & VFIO_DEVICE_FLAGS_CCW)) { + error_setg(errp, "vfio: Um, this isn't a vfio-ccw device"); + return; + } + + if (vdev->num_regions < VFIO_CCW_CONFIG_REGION_INDEX + 1) { + error_setg(errp, "vfio: Unexpected number of the I/O region %u", + vdev->num_regions); + return; + } + + ret = vfio_get_region_info(vdev, VFIO_CCW_CONFIG_REGION_INDEX, &info); + if (ret) { + error_setg_errno(errp, -ret, "vfio: Error getting config info"); + return; + } + + vcdev->io_region_size = info->size; + if (sizeof(*vcdev->io_region) != vcdev->io_region_size) { + error_setg(errp, "vfio: Unexpected size of the I/O region"); + g_free(info); + return; + } + + vcdev->io_region_offset = info->offset; + vcdev->io_region = g_malloc0(info->size); + + g_free(info); +} + +static void vfio_ccw_put_region(VFIOCCWDevice *vcdev) +{ + g_free(vcdev->io_region); +} + +static void vfio_put_device(VFIOCCWDevice *vcdev) +{ + g_free(vcdev->vdev.name); + vfio_put_base_device(&vcdev->vdev); +} + +static VFIOGroup *vfio_ccw_get_group(S390CCWDevice *cdev, Error **errp) +{ + char *tmp, group_path[PATH_MAX]; + ssize_t len; + int groupid; + + tmp = g_strdup_printf("/sys/bus/css/devices/%x.%x.%04x/%s/iommu_group", + cdev->hostid.cssid, cdev->hostid.ssid, + cdev->hostid.devid, cdev->mdevid); + len = readlink(tmp, group_path, sizeof(group_path)); + g_free(tmp); + + if (len <= 0 || len >= sizeof(group_path)) { + error_setg(errp, "vfio: no iommu_group found"); + return NULL; + } + + group_path[len] = 0; + + if (sscanf(basename(group_path), "%d", &groupid) != 1) { + error_setg(errp, "vfio: failed to read %s", group_path); + return NULL; + } + + return vfio_get_group(groupid, &address_space_memory, errp); +} + +static void vfio_ccw_realize(DeviceState *dev, Error **errp) +{ + VFIODevice *vbasedev; + VFIOGroup *group; + CcwDevice *ccw_dev = DO_UPCAST(CcwDevice, parent_obj, dev); + S390CCWDevice *cdev = DO_UPCAST(S390CCWDevice, parent_obj, ccw_dev); + VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev); + S390CCWDeviceClass *cdc = S390_CCW_DEVICE_GET_CLASS(cdev); + Error *err = NULL; + + /* Call the class init function for subchannel. */ + if (cdc->realize) { + cdc->realize(cdev, vcdev->vdev.sysfsdev, &err); + if (err) { + goto out_err_propagate; + } + } + + group = vfio_ccw_get_group(cdev, &err); + if (!group) { + goto out_group_err; + } + + vcdev->vdev.ops = &vfio_ccw_ops; + vcdev->vdev.type = VFIO_DEVICE_TYPE_CCW; + vcdev->vdev.name = g_strdup_printf("%x.%x.%04x", cdev->hostid.cssid, + cdev->hostid.ssid, cdev->hostid.devid); + QLIST_FOREACH(vbasedev, &group->device_list, next) { + if (strcmp(vbasedev->name, vcdev->vdev.name) == 0) { + error_setg(&err, "vfio: subchannel %s has already been attached", + vcdev->vdev.name); + goto out_device_err; + } + } + + if (vfio_get_device(group, cdev->mdevid, &vcdev->vdev, &err)) { + goto out_device_err; + } + + vfio_ccw_get_region(vcdev, &err); + if (err) { + goto out_region_err; + } + + vfio_ccw_register_io_notifier(vcdev, &err); + if (err) { + goto out_notifier_err; + } + + return; + +out_notifier_err: + vfio_ccw_put_region(vcdev); +out_region_err: + vfio_put_device(vcdev); +out_device_err: + vfio_put_group(group); +out_group_err: + if (cdc->unrealize) { + cdc->unrealize(cdev, NULL); + } +out_err_propagate: + error_propagate(errp, err); +} + +static void vfio_ccw_unrealize(DeviceState *dev, Error **errp) +{ + CcwDevice *ccw_dev = DO_UPCAST(CcwDevice, parent_obj, dev); + S390CCWDevice *cdev = DO_UPCAST(S390CCWDevice, parent_obj, ccw_dev); + VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev); + S390CCWDeviceClass *cdc = S390_CCW_DEVICE_GET_CLASS(cdev); + VFIOGroup *group = vcdev->vdev.group; + + vfio_ccw_unregister_io_notifier(vcdev); + vfio_ccw_put_region(vcdev); + vfio_put_device(vcdev); + vfio_put_group(group); + + if (cdc->unrealize) { + cdc->unrealize(cdev, errp); + } +} + +static Property vfio_ccw_properties[] = { + DEFINE_PROP_STRING("sysfsdev", VFIOCCWDevice, vdev.sysfsdev), + DEFINE_PROP_END_OF_LIST(), +}; + +static const VMStateDescription vfio_ccw_vmstate = { + .name = TYPE_VFIO_CCW, + .unmigratable = 1, +}; + +static void vfio_ccw_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + S390CCWDeviceClass *cdc = S390_CCW_DEVICE_CLASS(klass); + + dc->props = vfio_ccw_properties; + dc->vmsd = &vfio_ccw_vmstate; + dc->desc = "VFIO-based subchannel assignment"; + dc->realize = vfio_ccw_realize; + dc->unrealize = vfio_ccw_unrealize; + dc->reset = vfio_ccw_reset; + + cdc->handle_request = vfio_ccw_handle_request; +} + +static const TypeInfo vfio_ccw_info = { + .name = TYPE_VFIO_CCW, + .parent = TYPE_S390_CCW, + .instance_size = sizeof(VFIOCCWDevice), + .class_init = vfio_ccw_class_init, +}; + +static void register_vfio_ccw_type(void) +{ + type_register_static(&vfio_ccw_info); +} + +type_init(register_vfio_ccw_type) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index a8f12eeb35..7b2924c0ef 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -479,6 +479,7 @@ static void vfio_listener_region_add(MemoryListener *listener, if (memory_region_is_iommu(section->mr)) { VFIOGuestIOMMU *giommu; + IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(section->mr); trace_vfio_listener_region_add_iommu(iova, end); /* @@ -488,7 +489,7 @@ static void vfio_listener_region_add(MemoryListener *listener, * device emulation the VFIO iommu handles to use). */ giommu = g_malloc0(sizeof(*giommu)); - giommu->iommu = section->mr; + giommu->iommu = iommu_mr; giommu->iommu_offset = section->offset_within_address_space - section->offset_within_region; giommu->container = container; @@ -501,8 +502,8 @@ static void vfio_listener_region_add(MemoryListener *listener, int128_get64(llend)); QLIST_INSERT_HEAD(&container->giommu_list, giommu, giommu_next); - memory_region_register_iommu_notifier(giommu->iommu, &giommu->n); - memory_region_iommu_replay(giommu->iommu, &giommu->n, false); + memory_region_register_iommu_notifier(section->mr, &giommu->n); + memory_region_iommu_replay(giommu->iommu, &giommu->n); return; } @@ -569,9 +570,9 @@ static void vfio_listener_region_del(MemoryListener *listener, VFIOGuestIOMMU *giommu; QLIST_FOREACH(giommu, &container->giommu_list, giommu_next) { - if (giommu->iommu == section->mr && + if (MEMORY_REGION(giommu->iommu) == section->mr && giommu->n.start == section->offset_within_region) { - memory_region_unregister_iommu_notifier(giommu->iommu, + memory_region_unregister_iommu_notifier(section->mr, &giommu->n); QLIST_REMOVE(giommu, giommu_next); g_free(giommu); @@ -858,13 +859,15 @@ void vfio_reset_handler(void *opaque) QLIST_FOREACH(group, &vfio_group_list, next) { QLIST_FOREACH(vbasedev, &group->device_list, next) { - vbasedev->ops->vfio_compute_needs_reset(vbasedev); + if (vbasedev->dev->realized) { + vbasedev->ops->vfio_compute_needs_reset(vbasedev); + } } } QLIST_FOREACH(group, &vfio_group_list, next) { QLIST_FOREACH(vbasedev, &group->device_list, next) { - if (vbasedev->needs_reset) { + if (vbasedev->dev->realized && vbasedev->needs_reset) { vbasedev->ops->vfio_hot_reset_multi(vbasedev); } } @@ -1106,6 +1109,14 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, goto free_container_exit; } + vfio_kvm_device_add_group(group); + + QLIST_INIT(&container->group_list); + QLIST_INSERT_HEAD(&space->containers, container, next); + + group->container = container; + QLIST_INSERT_HEAD(&container->group_list, group, container_next); + container->listener = vfio_memory_listener; memory_listener_register(&container->listener, container->space->as); @@ -1119,14 +1130,11 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, container->initialized = true; - QLIST_INIT(&container->group_list); - QLIST_INSERT_HEAD(&space->containers, container, next); - - group->container = container; - QLIST_INSERT_HEAD(&container->group_list, group, container_next); - return 0; listener_release_exit: + QLIST_REMOVE(group, container_next); + QLIST_REMOVE(container, next); + vfio_kvm_device_del_group(group); vfio_listener_release(container); free_container_exit: @@ -1161,7 +1169,8 @@ static void vfio_disconnect_container(VFIOGroup *group) QLIST_REMOVE(container, next); QLIST_FOREACH_SAFE(giommu, &container->giommu_list, giommu_next, tmp) { - memory_region_unregister_iommu_notifier(giommu->iommu, &giommu->n); + memory_region_unregister_iommu_notifier( + MEMORY_REGION(giommu->iommu), &giommu->n); QLIST_REMOVE(giommu, giommu_next); g_free(giommu); } @@ -1230,8 +1239,6 @@ VFIOGroup *vfio_get_group(int groupid, AddressSpace *as, Error **errp) QLIST_INSERT_HEAD(&vfio_group_list, group, next); - vfio_kvm_device_add_group(group); - return group; close_fd_exit: diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 32aca77701..d4051cb951 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -1743,11 +1743,26 @@ static int vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size, PCI_EXP_LNKCAP_MLW | PCI_EXP_LNKCAP_SLS); } - pos = pci_add_capability(&vdev->pdev, PCI_CAP_ID_EXP, pos, size); - if (pos >= 0) { - vdev->pdev.exp.exp_cap = pos; + /* + * Intel 82599 SR-IOV VFs report an invalid PCIe capability version 0 + * (Niantic errate #35) causing Windows to error with a Code 10 for the + * device on Q35. Fixup any such devices to report version 1. If we + * were to remove the capability entirely the guest would lose extended + * config space. + */ + if ((flags & PCI_EXP_FLAGS_VERS) == 0) { + vfio_add_emulated_word(vdev, pos + PCI_CAP_FLAGS, + 1, PCI_EXP_FLAGS_VERS); } + pos = pci_add_capability(&vdev->pdev, PCI_CAP_ID_EXP, pos, size, + errp); + if (pos < 0) { + return pos; + } + + vdev->pdev.exp.exp_cap = pos; + return pos; } @@ -1834,14 +1849,14 @@ static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos, Error **errp) case PCI_CAP_ID_PM: vfio_check_pm_reset(vdev, pos); vdev->pm_cap = pos; - ret = pci_add_capability2(pdev, cap_id, pos, size, errp); + ret = pci_add_capability(pdev, cap_id, pos, size, errp); break; case PCI_CAP_ID_AF: vfio_check_af_flr(vdev, pos); - ret = pci_add_capability2(pdev, cap_id, pos, size, errp); + ret = pci_add_capability(pdev, cap_id, pos, size, errp); break; default: - ret = pci_add_capability2(pdev, cap_id, pos, size, errp); + ret = pci_add_capability(pdev, cap_id, pos, size, errp); break; } out: @@ -2113,7 +2128,8 @@ static int vfio_pci_hot_reset(VFIOPCIDevice *vdev, bool single) /* Prep dependent devices for reset and clear our marker. */ QLIST_FOREACH(vbasedev_iter, &group->device_list, next) { - if (vbasedev_iter->type != VFIO_DEVICE_TYPE_PCI) { + if (!vbasedev_iter->dev->realized || + vbasedev_iter->type != VFIO_DEVICE_TYPE_PCI) { continue; } tmp = container_of(vbasedev_iter, VFIOPCIDevice, vbasedev); @@ -2194,7 +2210,8 @@ out: } QLIST_FOREACH(vbasedev_iter, &group->device_list, next) { - if (vbasedev_iter->type != VFIO_DEVICE_TYPE_PCI) { + if (!vbasedev_iter->dev->realized || + vbasedev_iter->type != VFIO_DEVICE_TYPE_PCI) { continue; } tmp = container_of(vbasedev_iter, VFIOPCIDevice, vbasedev); @@ -2644,6 +2661,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) vdev->vbasedev.name = g_strdup(basename(vdev->vbasedev.sysfsdev)); vdev->vbasedev.ops = &vfio_pci_ops; vdev->vbasedev.type = VFIO_DEVICE_TYPE_PCI; + vdev->vbasedev.dev = &vdev->pdev.qdev; tmp = g_strdup_printf("%s/iommu_group", vdev->vbasedev.sysfsdev); len = readlink(tmp, group_path, sizeof(group_path)); diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c index a4663c918e..7c09deda61 100644 --- a/hw/vfio/platform.c +++ b/hw/vfio/platform.c @@ -640,6 +640,7 @@ static void vfio_platform_realize(DeviceState *dev, Error **errp) int i, ret; vbasedev->type = VFIO_DEVICE_TYPE_PLATFORM; + vbasedev->dev = dev; vbasedev->ops = &vfio_platform_ops; trace_vfio_platform_realize(vbasedev->sysfsdev ? diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index 4409bcc0d7..32fd6a9b54 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -143,7 +143,8 @@ int vfio_spapr_create_window(VFIOContainer *container, hwaddr *pgsize) { int ret; - unsigned pagesize = memory_region_iommu_get_min_page_size(section->mr); + IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(section->mr); + unsigned pagesize = memory_region_iommu_get_min_page_size(iommu_mr); unsigned entries, pages; struct vfio_iommu_spapr_tce_create create = { .argsz = sizeof(create) }; diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index 1f7a7c1ae1..e24d8fa997 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -1,6 +1,7 @@ # See docs/tracing.txt for syntax documentation. # hw/virtio/virtio.c +virtqueue_alloc_element(void *elem, size_t sz, unsigned in_num, unsigned out_num) "elem %p size %zd in_num %u out_num %u" virtqueue_fill(void *vq, const void *elem, unsigned int len, unsigned int idx) "vq %p elem %p len %u idx %u" virtqueue_flush(void *vq, unsigned int count) "vq %p count %u" virtqueue_pop(void *vq, void *elem, unsigned int in_num, unsigned int out_num) "vq %p elem %p in_num %u out_num %u" diff --git a/hw/virtio/vhost-backend.c b/hw/virtio/vhost-backend.c index be927b891e..cb055e8f21 100644 --- a/hw/virtio/vhost-backend.c +++ b/hw/virtio/vhost-backend.c @@ -192,7 +192,6 @@ static void vhost_kernel_iotlb_read(void *opaque) ssize_t len; while ((len = read((uintptr_t)dev->opaque, &msg, sizeof msg)) > 0) { - struct vhost_iotlb_msg *imsg = &msg.iotlb; if (len < sizeof msg) { error_report("Wrong vhost message len: %d", (int)len); break; @@ -201,70 +200,21 @@ static void vhost_kernel_iotlb_read(void *opaque) error_report("Unknown vhost iotlb message type"); break; } - switch (imsg->type) { - case VHOST_IOTLB_MISS: - vhost_device_iotlb_miss(dev, imsg->iova, - imsg->perm != VHOST_ACCESS_RO); - break; - case VHOST_IOTLB_UPDATE: - case VHOST_IOTLB_INVALIDATE: - error_report("Unexpected IOTLB message type"); - break; - case VHOST_IOTLB_ACCESS_FAIL: - /* FIXME: report device iotlb error */ - break; - default: - break; - } - } -} - -static int vhost_kernel_update_device_iotlb(struct vhost_dev *dev, - uint64_t iova, uint64_t uaddr, - uint64_t len, - IOMMUAccessFlags perm) -{ - struct vhost_msg msg; - msg.type = VHOST_IOTLB_MSG; - msg.iotlb.iova = iova; - msg.iotlb.uaddr = uaddr; - msg.iotlb.size = len; - msg.iotlb.type = VHOST_IOTLB_UPDATE; - - switch (perm) { - case IOMMU_RO: - msg.iotlb.perm = VHOST_ACCESS_RO; - break; - case IOMMU_WO: - msg.iotlb.perm = VHOST_ACCESS_WO; - break; - case IOMMU_RW: - msg.iotlb.perm = VHOST_ACCESS_RW; - break; - default: - g_assert_not_reached(); - } - if (write((uintptr_t)dev->opaque, &msg, sizeof msg) != sizeof msg) { - error_report("Fail to update device iotlb"); - return -EFAULT; + vhost_backend_handle_iotlb_msg(dev, &msg.iotlb); } - - return 0; } -static int vhost_kernel_invalidate_device_iotlb(struct vhost_dev *dev, - uint64_t iova, uint64_t len) +static int vhost_kernel_send_device_iotlb_msg(struct vhost_dev *dev, + struct vhost_iotlb_msg *imsg) { struct vhost_msg msg; msg.type = VHOST_IOTLB_MSG; - msg.iotlb.iova = iova; - msg.iotlb.size = len; - msg.iotlb.type = VHOST_IOTLB_INVALIDATE; + msg.iotlb = *imsg; if (write((uintptr_t)dev->opaque, &msg, sizeof msg) != sizeof msg) { - error_report("Fail to invalidate device iotlb"); + error_report("Fail to update device iotlb"); return -EFAULT; } @@ -311,8 +261,7 @@ static const VhostOps kernel_ops = { .vhost_vsock_set_running = vhost_kernel_vsock_set_running, #endif /* CONFIG_VHOST_VSOCK */ .vhost_set_iotlb_callback = vhost_kernel_set_iotlb_callback, - .vhost_update_device_iotlb = vhost_kernel_update_device_iotlb, - .vhost_invalidate_device_iotlb = vhost_kernel_invalidate_device_iotlb, + .vhost_send_device_iotlb_msg = vhost_kernel_send_device_iotlb_msg, }; int vhost_set_backend_type(struct vhost_dev *dev, VhostBackendType backend_type) @@ -333,3 +282,76 @@ int vhost_set_backend_type(struct vhost_dev *dev, VhostBackendType backend_type) return r; } + +int vhost_backend_update_device_iotlb(struct vhost_dev *dev, + uint64_t iova, uint64_t uaddr, + uint64_t len, + IOMMUAccessFlags perm) +{ + struct vhost_iotlb_msg imsg; + + imsg.iova = iova; + imsg.uaddr = uaddr; + imsg.size = len; + imsg.type = VHOST_IOTLB_UPDATE; + + switch (perm) { + case IOMMU_RO: + imsg.perm = VHOST_ACCESS_RO; + break; + case IOMMU_WO: + imsg.perm = VHOST_ACCESS_WO; + break; + case IOMMU_RW: + imsg.perm = VHOST_ACCESS_RW; + break; + default: + return -EINVAL; + } + + if (dev->vhost_ops && dev->vhost_ops->vhost_send_device_iotlb_msg) + return dev->vhost_ops->vhost_send_device_iotlb_msg(dev, &imsg); + + return -ENODEV; +} + +int vhost_backend_invalidate_device_iotlb(struct vhost_dev *dev, + uint64_t iova, uint64_t len) +{ + struct vhost_iotlb_msg imsg; + + imsg.iova = iova; + imsg.size = len; + imsg.type = VHOST_IOTLB_INVALIDATE; + + if (dev->vhost_ops && dev->vhost_ops->vhost_send_device_iotlb_msg) + return dev->vhost_ops->vhost_send_device_iotlb_msg(dev, &imsg); + + return -ENODEV; +} + +int vhost_backend_handle_iotlb_msg(struct vhost_dev *dev, + struct vhost_iotlb_msg *imsg) +{ + int ret = 0; + + switch (imsg->type) { + case VHOST_IOTLB_MISS: + ret = vhost_device_iotlb_miss(dev, imsg->iova, + imsg->perm != VHOST_ACCESS_RO); + break; + case VHOST_IOTLB_ACCESS_FAIL: + /* FIXME: report device iotlb error */ + error_report("Access failure IOTLB message type not supported"); + ret = -ENOTSUP; + break; + case VHOST_IOTLB_UPDATE: + case VHOST_IOTLB_INVALIDATE: + default: + error_report("Unexpected IOTLB message type"); + ret = -EINVAL; + break; + } + + return ret; +} diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index b87a176770..2203011125 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -13,7 +13,7 @@ #include "hw/virtio/vhost.h" #include "hw/virtio/vhost-backend.h" #include "hw/virtio/virtio-net.h" -#include "sysemu/char.h" +#include "chardev/char-fe.h" #include "sysemu/kvm.h" #include "qemu/error-report.h" #include "qemu/sockets.h" @@ -32,6 +32,7 @@ enum VhostUserProtocolFeature { VHOST_USER_PROTOCOL_F_RARP = 2, VHOST_USER_PROTOCOL_F_REPLY_ACK = 3, VHOST_USER_PROTOCOL_F_NET_MTU = 4, + VHOST_USER_PROTOCOL_F_SLAVE_REQ = 5, VHOST_USER_PROTOCOL_F_MAX }; @@ -60,9 +61,17 @@ typedef enum VhostUserRequest { VHOST_USER_SET_VRING_ENABLE = 18, VHOST_USER_SEND_RARP = 19, VHOST_USER_NET_SET_MTU = 20, + VHOST_USER_SET_SLAVE_REQ_FD = 21, + VHOST_USER_IOTLB_MSG = 22, VHOST_USER_MAX } VhostUserRequest; +typedef enum VhostUserSlaveRequest { + VHOST_USER_SLAVE_NONE = 0, + VHOST_USER_SLAVE_IOTLB_MSG = 1, + VHOST_USER_SLAVE_MAX +} VhostUserSlaveRequest; + typedef struct VhostUserMemoryRegion { uint64_t guest_phys_addr; uint64_t memory_size; @@ -97,6 +106,7 @@ typedef struct VhostUserMsg { struct vhost_vring_addr addr; VhostUserMemory memory; VhostUserLog log; + struct vhost_iotlb_msg iotlb; } payload; } QEMU_PACKED VhostUserMsg; @@ -110,6 +120,11 @@ static VhostUserMsg m __attribute__ ((unused)); /* The version of the protocol we support */ #define VHOST_USER_VERSION (0x1) +struct vhost_user { + CharBackend *chr; + int slave_fd; +}; + static bool ioeventfd_enabled(void) { return kvm_enabled() && kvm_eventfds_enabled(); @@ -117,7 +132,8 @@ static bool ioeventfd_enabled(void) static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg) { - CharBackend *chr = dev->opaque; + struct vhost_user *u = dev->opaque; + CharBackend *chr = u->chr; uint8_t *p = (uint8_t *) msg; int r, size = VHOST_USER_HDR_SIZE; @@ -162,11 +178,11 @@ fail: } static int process_message_reply(struct vhost_dev *dev, - VhostUserMsg msg) + const VhostUserMsg *msg) { VhostUserMsg msg_reply; - if ((msg.flags & VHOST_USER_NEED_REPLY_MASK) == 0) { + if ((msg->flags & VHOST_USER_NEED_REPLY_MASK) == 0) { return 0; } @@ -174,10 +190,10 @@ static int process_message_reply(struct vhost_dev *dev, return -1; } - if (msg_reply.request != msg.request) { + if (msg_reply.request != msg->request) { error_report("Received unexpected msg type." "Expected %d received %d", - msg.request, msg_reply.request); + msg->request, msg_reply.request); return -1; } @@ -202,7 +218,8 @@ static bool vhost_user_one_time_request(VhostUserRequest request) static int vhost_user_write(struct vhost_dev *dev, VhostUserMsg *msg, int *fds, int fd_num) { - CharBackend *chr = dev->opaque; + struct vhost_user *u = dev->opaque; + CharBackend *chr = u->chr; int ret, size = VHOST_USER_HDR_SIZE + msg->size; /* @@ -324,7 +341,7 @@ static int vhost_user_set_mem_table(struct vhost_dev *dev, } if (reply_supported) { - return process_message_reply(dev, msg); + return process_message_reply(dev, &msg); } return 0; @@ -572,14 +589,130 @@ static int vhost_user_reset_device(struct vhost_dev *dev) return 0; } +static void slave_read(void *opaque) +{ + struct vhost_dev *dev = opaque; + struct vhost_user *u = dev->opaque; + VhostUserMsg msg = { 0, }; + int size, ret = 0; + + /* Read header */ + size = read(u->slave_fd, &msg, VHOST_USER_HDR_SIZE); + if (size != VHOST_USER_HDR_SIZE) { + error_report("Failed to read from slave."); + goto err; + } + + if (msg.size > VHOST_USER_PAYLOAD_SIZE) { + error_report("Failed to read msg header." + " Size %d exceeds the maximum %zu.", msg.size, + VHOST_USER_PAYLOAD_SIZE); + goto err; + } + + /* Read payload */ + size = read(u->slave_fd, &msg.payload, msg.size); + if (size != msg.size) { + error_report("Failed to read payload from slave."); + goto err; + } + + switch (msg.request) { + case VHOST_USER_SLAVE_IOTLB_MSG: + ret = vhost_backend_handle_iotlb_msg(dev, &msg.payload.iotlb); + break; + default: + error_report("Received unexpected msg type."); + ret = -EINVAL; + } + + /* + * REPLY_ACK feature handling. Other reply types has to be managed + * directly in their request handlers. + */ + if (msg.flags & VHOST_USER_NEED_REPLY_MASK) { + msg.flags &= ~VHOST_USER_NEED_REPLY_MASK; + msg.flags |= VHOST_USER_REPLY_MASK; + + msg.payload.u64 = !!ret; + msg.size = sizeof(msg.payload.u64); + + size = write(u->slave_fd, &msg, VHOST_USER_HDR_SIZE + msg.size); + if (size != VHOST_USER_HDR_SIZE + msg.size) { + error_report("Failed to send msg reply to slave."); + goto err; + } + } + + return; + +err: + qemu_set_fd_handler(u->slave_fd, NULL, NULL, NULL); + close(u->slave_fd); + u->slave_fd = -1; + return; +} + +static int vhost_setup_slave_channel(struct vhost_dev *dev) +{ + VhostUserMsg msg = { + .request = VHOST_USER_SET_SLAVE_REQ_FD, + .flags = VHOST_USER_VERSION, + }; + struct vhost_user *u = dev->opaque; + int sv[2], ret = 0; + bool reply_supported = virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_REPLY_ACK); + + if (!virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_SLAVE_REQ)) { + return 0; + } + + if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) { + error_report("socketpair() failed"); + return -1; + } + + u->slave_fd = sv[0]; + qemu_set_fd_handler(u->slave_fd, slave_read, NULL, dev); + + if (reply_supported) { + msg.flags |= VHOST_USER_NEED_REPLY_MASK; + } + + ret = vhost_user_write(dev, &msg, &sv[1], 1); + if (ret) { + goto out; + } + + if (reply_supported) { + ret = process_message_reply(dev, &msg); + } + +out: + close(sv[1]); + if (ret) { + qemu_set_fd_handler(u->slave_fd, NULL, NULL, NULL); + close(u->slave_fd); + u->slave_fd = -1; + } + + return ret; +} + static int vhost_user_init(struct vhost_dev *dev, void *opaque) { - uint64_t features; + uint64_t features, protocol_features; + struct vhost_user *u; int err; assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); - dev->opaque = opaque; + u = g_new0(struct vhost_user, 1); + u->chr = opaque; + u->slave_fd = -1; + dev->opaque = u; err = vhost_user_get_features(dev, &features); if (err < 0) { @@ -590,12 +723,13 @@ static int vhost_user_init(struct vhost_dev *dev, void *opaque) dev->backend_features |= 1ULL << VHOST_USER_F_PROTOCOL_FEATURES; err = vhost_user_get_u64(dev, VHOST_USER_GET_PROTOCOL_FEATURES, - &features); + &protocol_features); if (err < 0) { return err; } - dev->protocol_features = features & VHOST_USER_PROTOCOL_FEATURE_MASK; + dev->protocol_features = + protocol_features & VHOST_USER_PROTOCOL_FEATURE_MASK; err = vhost_user_set_protocol_features(dev, dev->protocol_features); if (err < 0) { return err; @@ -609,6 +743,16 @@ static int vhost_user_init(struct vhost_dev *dev, void *opaque) return err; } } + + if (virtio_has_feature(features, VIRTIO_F_IOMMU_PLATFORM) && + !(virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_SLAVE_REQ) && + virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_REPLY_ACK))) { + error_report("IOMMU support requires reply-ack and " + "slave-req protocol features."); + return -1; + } } if (dev->migration_blocker == NULL && @@ -619,13 +763,27 @@ static int vhost_user_init(struct vhost_dev *dev, void *opaque) "VHOST_USER_PROTOCOL_F_LOG_SHMFD feature."); } + err = vhost_setup_slave_channel(dev); + if (err < 0) { + return err; + } + return 0; } static int vhost_user_cleanup(struct vhost_dev *dev) { + struct vhost_user *u; + assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); + u = dev->opaque; + if (u->slave_fd >= 0) { + qemu_set_fd_handler(u->slave_fd, NULL, NULL, NULL); + close(u->slave_fd); + u->slave_fd = -1; + } + g_free(u); dev->opaque = 0; return 0; @@ -716,12 +874,35 @@ static int vhost_user_net_set_mtu(struct vhost_dev *dev, uint16_t mtu) /* If reply_ack supported, slave has to ack specified MTU is valid */ if (reply_supported) { - return process_message_reply(dev, msg); + return process_message_reply(dev, &msg); } return 0; } +static int vhost_user_send_device_iotlb_msg(struct vhost_dev *dev, + struct vhost_iotlb_msg *imsg) +{ + VhostUserMsg msg = { + .request = VHOST_USER_IOTLB_MSG, + .size = sizeof(msg.payload.iotlb), + .flags = VHOST_USER_VERSION | VHOST_USER_NEED_REPLY_MASK, + .payload.iotlb = *imsg, + }; + + if (vhost_user_write(dev, &msg, NULL, 0) < 0) { + return -EFAULT; + } + + return process_message_reply(dev, &msg); +} + + +static void vhost_user_set_iotlb_callback(struct vhost_dev *dev, int enabled) +{ + /* No-op as the receive channel is not dedicated to IOTLB messages. */ +} + const VhostOps user_ops = { .backend_type = VHOST_BACKEND_TYPE_USER, .vhost_backend_init = vhost_user_init, @@ -746,4 +927,6 @@ const VhostOps user_ops = { .vhost_migration_done = vhost_user_migration_done, .vhost_backend_can_merge = vhost_user_can_merge, .vhost_net_set_mtu = vhost_user_net_set_mtu, + .vhost_set_iotlb_callback = vhost_user_set_iotlb_callback, + .vhost_send_device_iotlb_msg = vhost_user_send_device_iotlb_msg, }; diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 03a46a7429..6eddb099b0 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -724,8 +724,8 @@ static void vhost_iommu_unmap_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) struct vhost_dev *hdev = iommu->hdev; hwaddr iova = iotlb->iova + iommu->iommu_offset; - if (hdev->vhost_ops->vhost_invalidate_device_iotlb(hdev, iova, - iotlb->addr_mask + 1)) { + if (vhost_backend_invalidate_device_iotlb(hdev, iova, + iotlb->addr_mask + 1)) { error_report("Fail to invalidate device iotlb"); } } @@ -971,18 +971,20 @@ static int vhost_memory_region_lookup(struct vhost_dev *hdev, return -EFAULT; } -void vhost_device_iotlb_miss(struct vhost_dev *dev, uint64_t iova, int write) +int vhost_device_iotlb_miss(struct vhost_dev *dev, uint64_t iova, int write) { IOMMUTLBEntry iotlb; uint64_t uaddr, len; + int ret = -EFAULT; rcu_read_lock(); iotlb = address_space_get_iotlb_entry(dev->vdev->dma_as, iova, write); if (iotlb.target_as != NULL) { - if (vhost_memory_region_lookup(dev, iotlb.translated_addr, - &uaddr, &len)) { + ret = vhost_memory_region_lookup(dev, iotlb.translated_addr, + &uaddr, &len); + if (ret) { error_report("Fail to lookup the translated address " "%"PRIx64, iotlb.translated_addr); goto out; @@ -991,14 +993,17 @@ void vhost_device_iotlb_miss(struct vhost_dev *dev, uint64_t iova, int write) len = MIN(iotlb.addr_mask + 1, len); iova = iova & ~iotlb.addr_mask; - if (dev->vhost_ops->vhost_update_device_iotlb(dev, iova, uaddr, - len, iotlb.perm)) { + ret = vhost_backend_update_device_iotlb(dev, iova, uaddr, + len, iotlb.perm); + if (ret) { error_report("Fail to update device iotlb"); goto out; } } out: rcu_read_unlock(); + + return ret; } static int vhost_virtqueue_start(struct vhost_dev *dev, diff --git a/hw/virtio/virtio-crypto-pci.c b/hw/virtio/virtio-crypto-pci.c index 422aca3a98..bf64996e48 100644 --- a/hw/virtio/virtio-crypto-pci.c +++ b/hw/virtio/virtio-crypto-pci.c @@ -62,8 +62,6 @@ static void virtio_crypto_initfn(Object *obj) virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_CRYPTO); - object_property_add_alias(obj, "cryptodev", OBJECT(&dev->vdev), - "cryptodev", &error_abort); } static const TypeInfo virtio_crypto_pci_info = { diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c index 0353eb6d5d..19c82e0432 100644 --- a/hw/virtio/virtio-crypto.c +++ b/hw/virtio/virtio-crypto.c @@ -781,6 +781,11 @@ static void virtio_crypto_device_realize(DeviceState *dev, Error **errp) if (vcrypto->cryptodev == NULL) { error_setg(errp, "'cryptodev' parameter expects a valid object"); return; + } else if (cryptodev_backend_is_used(vcrypto->cryptodev)) { + char *path = object_get_canonical_path_component(OBJECT(vcrypto->conf.cryptodev)); + error_setg(errp, "can't use already used cryptodev backend: %s", path); + g_free(path); + return; } vcrypto->max_queues = MAX(vcrypto->cryptodev->conf.peers.queues, 1); @@ -845,6 +850,8 @@ static const VMStateDescription vmstate_virtio_crypto = { }; static Property virtio_crypto_properties[] = { + DEFINE_PROP_LINK("cryptodev", VirtIOCrypto, conf.cryptodev, + TYPE_CRYPTODEV_BACKEND, CryptoDevBackend *), DEFINE_PROP_END_OF_LIST(), }; @@ -888,20 +895,6 @@ static void virtio_crypto_class_init(ObjectClass *klass, void *data) vdc->reset = virtio_crypto_reset; } -static void -virtio_crypto_check_cryptodev_is_used(Object *obj, const char *name, - Object *val, Error **errp) -{ - if (cryptodev_backend_is_used(CRYPTODEV_BACKEND(val))) { - char *path = object_get_canonical_path_component(val); - error_setg(errp, - "can't use already used cryptodev backend: %s", path); - g_free(path); - } else { - qdev_prop_allow_set_link_before_realize(obj, name, val, errp); - } -} - static void virtio_crypto_instance_init(Object *obj) { VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(obj); @@ -911,12 +904,6 @@ static void virtio_crypto_instance_init(Object *obj) * Can be overriden with virtio_crypto_set_config_size. */ vcrypto->config_size = sizeof(struct virtio_crypto_config); - - object_property_add_link(obj, "cryptodev", - TYPE_CRYPTODEV_BACKEND, - (Object **)&vcrypto->conf.cryptodev, - virtio_crypto_check_cryptodev_is_used, - OBJ_PROP_LINK_UNREF_ON_RELEASE, NULL); } static const TypeInfo virtio_crypto_info = { diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index f9b7244808..5d14bd66dc 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -1162,8 +1162,8 @@ static int virtio_pci_add_mem_cap(VirtIOPCIProxy *proxy, PCIDevice *dev = &proxy->pci_dev; int offset; - offset = pci_add_capability(dev, PCI_CAP_ID_VNDR, 0, cap->cap_len); - assert(offset > 0); + offset = pci_add_capability(dev, PCI_CAP_ID_VNDR, 0, + cap->cap_len, &error_abort); assert(cap->cap_len >= sizeof *cap); memcpy(dev->config + offset + PCI_CAP_FLAGS, &cap->cap_len, @@ -1740,7 +1740,7 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) bool pcie_port = pci_bus_is_express(pci_dev->bus) && !pci_bus_is_root(pci_dev->bus); - if (!kvm_has_many_ioeventfds()) { + if (kvm_enabled() && !kvm_has_many_ioeventfds()) { proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD; } @@ -1810,8 +1810,12 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) pos = pcie_endpoint_cap_init(pci_dev, 0); assert(pos > 0); - pos = pci_add_capability(pci_dev, PCI_CAP_ID_PM, 0, PCI_PM_SIZEOF); - assert(pos > 0); + pos = pci_add_capability(pci_dev, PCI_CAP_ID_PM, 0, + PCI_PM_SIZEOF, errp); + if (pos < 0) { + return; + } + pci_dev->exp.pm_cap = pos; /* @@ -1996,8 +2000,6 @@ static void virtio_blk_pci_instance_init(Object *obj) virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_BLK); - object_property_add_alias(obj, "iothread", OBJECT(&dev->vdev),"iothread", - &error_abort); object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev), "bootindex", &error_abort); } @@ -2067,8 +2069,6 @@ static void virtio_scsi_pci_instance_init(Object *obj) virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_SCSI); - object_property_add_alias(obj, "iothread", OBJECT(&dev->vdev), "iothread", - &error_abort); } static const TypeInfo virtio_scsi_pci_info = { @@ -2135,6 +2135,61 @@ static const TypeInfo vhost_scsi_pci_info = { }; #endif +#ifdef CONFIG_LINUX +/* vhost-user-scsi-pci */ +static Property vhost_user_scsi_pci_properties[] = { + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, + DEV_NVECTORS_UNSPECIFIED), + DEFINE_PROP_END_OF_LIST(), +}; + +static void vhost_user_scsi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + VHostUserSCSIPCI *dev = VHOST_USER_SCSI_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); + + if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { + vpci_dev->nvectors = vs->conf.num_queues + 3; + } + + qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); + object_property_set_bool(OBJECT(vdev), true, "realized", errp); +} + +static void vhost_user_scsi_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); + k->realize = vhost_user_scsi_pci_realize; + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); + dc->props = vhost_user_scsi_pci_properties; + pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; + pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_SCSI; + pcidev_k->revision = 0x00; + pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI; +} + +static void vhost_user_scsi_pci_instance_init(Object *obj) +{ + VHostUserSCSIPCI *dev = VHOST_USER_SCSI_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VHOST_USER_SCSI); + object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev), + "bootindex", &error_abort); +} + +static const TypeInfo vhost_user_scsi_pci_info = { + .name = TYPE_VHOST_USER_SCSI_PCI, + .parent = TYPE_VIRTIO_PCI, + .instance_size = sizeof(VHostUserSCSIPCI), + .instance_init = vhost_user_scsi_pci_instance_init, + .class_init = vhost_user_scsi_pci_class_init, +}; +#endif + /* vhost-vsock-pci */ #ifdef CONFIG_VHOST_VSOCK @@ -2408,8 +2463,6 @@ static void virtio_rng_initfn(Object *obj) virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_RNG); - object_property_add_alias(obj, "rng", OBJECT(&dev->vdev), "rng", - &error_abort); } static const TypeInfo virtio_rng_pci_info = { @@ -2612,6 +2665,9 @@ static void virtio_pci_register_types(void) #ifdef CONFIG_VHOST_SCSI type_register_static(&vhost_scsi_pci_info); #endif +#ifdef CONFIG_LINUX + type_register_static(&vhost_user_scsi_pci_info); +#endif #ifdef CONFIG_VHOST_VSOCK type_register_static(&vhost_vsock_pci_info); #endif diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h index b095dfc6d9..69f5959623 100644 --- a/hw/virtio/virtio-pci.h +++ b/hw/virtio/virtio-pci.h @@ -26,6 +26,7 @@ #include "hw/virtio/virtio-input.h" #include "hw/virtio/virtio-gpu.h" #include "hw/virtio/virtio-crypto.h" +#include "hw/virtio/vhost-user-scsi.h" #ifdef CONFIG_VIRTFS #include "hw/9pfs/virtio-9p.h" @@ -44,6 +45,7 @@ typedef struct VirtIOBalloonPCI VirtIOBalloonPCI; typedef struct VirtIOSerialPCI VirtIOSerialPCI; typedef struct VirtIONetPCI VirtIONetPCI; typedef struct VHostSCSIPCI VHostSCSIPCI; +typedef struct VHostUserSCSIPCI VHostUserSCSIPCI; typedef struct VirtIORngPCI VirtIORngPCI; typedef struct VirtIOInputPCI VirtIOInputPCI; typedef struct VirtIOInputHIDPCI VirtIOInputHIDPCI; @@ -230,6 +232,15 @@ struct VHostSCSIPCI { }; #endif +#define TYPE_VHOST_USER_SCSI_PCI "vhost-user-scsi-pci" +#define VHOST_USER_SCSI_PCI(obj) \ + OBJECT_CHECK(VHostUserSCSIPCI, (obj), TYPE_VHOST_USER_SCSI_PCI) + +struct VHostUserSCSIPCI { + VirtIOPCIProxy parent_obj; + VHostUserSCSI vdev; +}; + /* * virtio-blk-pci: This extends VirtioPCIProxy. */ diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c index a6ee501051..289bbcac03 100644 --- a/hw/virtio/virtio-rng.c +++ b/hw/virtio/virtio-rng.c @@ -246,6 +246,7 @@ static Property virtio_rng_properties[] = { */ DEFINE_PROP_UINT64("max-bytes", VirtIORNG, conf.max_bytes, INT64_MAX), DEFINE_PROP_UINT32("period", VirtIORNG, conf.period_ms, 1 << 16), + DEFINE_PROP_LINK("rng", VirtIORNG, conf.rng, TYPE_RNG_BACKEND, RngBackend *), DEFINE_PROP_END_OF_LIST(), }; @@ -262,21 +263,10 @@ static void virtio_rng_class_init(ObjectClass *klass, void *data) vdc->get_features = get_features; } -static void virtio_rng_initfn(Object *obj) -{ - VirtIORNG *vrng = VIRTIO_RNG(obj); - - object_property_add_link(obj, "rng", TYPE_RNG_BACKEND, - (Object **)&vrng->conf.rng, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, NULL); -} - static const TypeInfo virtio_rng_info = { .name = TYPE_VIRTIO_RNG, .parent = TYPE_VIRTIO_DEVICE, .instance_size = sizeof(VirtIORNG), - .instance_init = virtio_rng_initfn, .class_init = virtio_rng_class_init, }; diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index f99d99fd78..464947f76d 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -815,6 +815,7 @@ static void *virtqueue_alloc_element(size_t sz, unsigned out_num, unsigned in_nu assert(sz >= sizeof(VirtQueueElement)); elem = g_malloc(out_sg_end); + trace_virtqueue_alloc_element(elem, sz, in_num, out_num); elem->out_num = out_num; elem->in_num = in_num; elem->in_addr = (void *)elem + in_addr_ofs; diff --git a/hw/watchdog/watchdog.c b/hw/watchdog/watchdog.c index 2aeaf1fbc9..0c5c9cde1c 100644 --- a/hw/watchdog/watchdog.c +++ b/hw/watchdog/watchdog.c @@ -110,7 +110,7 @@ void watchdog_perform_action(void) switch (watchdog_action) { case WDT_RESET: /* same as 'system_reset' in monitor */ qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_RESET, &error_abort); - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); break; case WDT_SHUTDOWN: /* same as 'system_powerdown' in monitor */ diff --git a/hw/xen/xen-common.c b/hw/xen/xen-common.c index a9055e9eba..632a938dcc 100644 --- a/hw/xen/xen-common.c +++ b/hw/xen/xen-common.c @@ -11,9 +11,10 @@ #include "qemu/osdep.h" #include "hw/xen/xen_backend.h" #include "qmp-commands.h" -#include "sysemu/char.h" +#include "chardev/char.h" #include "sysemu/accel.h" -#include "migration/migration.h" +#include "migration/misc.h" +#include "migration/global_state.h" //#define DEBUG_XEN @@ -137,20 +138,35 @@ static int xen_init(MachineState *ms) return -1; } qemu_add_vm_change_state_handler(xen_change_state_handler, NULL); - - global_state_set_optional(); - savevm_skip_configuration(); - savevm_skip_section_footers(); - return 0; } +static GlobalProperty xen_compat_props[] = { + { + .driver = "migration", + .property = "store-global-state", + .value = "off", + }, + { + .driver = "migration", + .property = "send-configuration", + .value = "off", + }, + { + .driver = "migration", + .property = "send-section-footer", + .value = "off", + }, + { /* end of list */ }, +}; + static void xen_accel_class_init(ObjectClass *oc, void *data) { AccelClass *ac = ACCEL_CLASS(oc); ac->name = "Xen"; ac->init_machine = xen_init; ac->allowed = &xen_allowed; + ac->global_props = xen_compat_props; } #define TYPE_XEN_ACCEL ACCEL_CLASS_NAME("xen") diff --git a/hw/xen/xen_backend.c b/hw/xen/xen_backend.c index 3570f37e56..c46cbb0759 100644 --- a/hw/xen/xen_backend.c +++ b/hw/xen/xen_backend.c @@ -28,7 +28,6 @@ #include "hw/hw.h" #include "hw/sysbus.h" #include "hw/boards.h" -#include "sysemu/char.h" #include "qemu/log.h" #include "qapi/error.h" #include "hw/xen/xen_backend.h" diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c index b6d71bb52a..375efa68f6 100644 --- a/hw/xen/xen_pt.c +++ b/hw/xen/xen_pt.c @@ -85,7 +85,7 @@ void xen_pt_log(const PCIDevice *d, const char *f, ...) static int xen_pt_pci_config_access_check(PCIDevice *d, uint32_t addr, int len) { /* check offset range */ - if (addr >= 0xFF) { + if (addr > 0xFF) { XEN_PT_ERR(d, "Failed to access register with offset exceeding 0xFF. " "(addr: 0x%02x, len: %d)\n", addr, len); return -1; diff --git a/hw/xenpv/xen_domainbuild.c b/hw/xenpv/xen_domainbuild.c index 457a8976c3..c89ced2e88 100644 --- a/hw/xenpv/xen_domainbuild.c +++ b/hw/xenpv/xen_domainbuild.c @@ -148,7 +148,7 @@ static void xen_domain_poll(void *opaque) return; quit: - qemu_system_shutdown_request(); + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); } static int xen_domain_watcher(void) diff --git a/hw/xenpv/xen_machine_pv.c b/hw/xenpv/xen_machine_pv.c index 79aef4ecc3..31d2f25627 100644 --- a/hw/xenpv/xen_machine_pv.c +++ b/hw/xenpv/xen_machine_pv.c @@ -94,9 +94,6 @@ static void xen_init_pv(MachineState *machine) /* config cleanup hook */ atexit(xen_config_cleanup); - - /* setup framebuffer */ - xen_init_display(xen_domid); } static void xenpv_machine_init(MachineClass *mc) diff --git a/hw/xtensa/sim.c b/hw/xtensa/sim.c index d2d1d3a6fd..249cd1e8c9 100644 --- a/hw/xtensa/sim.c +++ b/hw/xtensa/sim.c @@ -41,21 +41,19 @@ static void xtensa_create_memory_regions(const XtensaMemory *memory, const char *name) { unsigned i; - char *num_name = malloc(strlen(name) + sizeof(i) * 3 + 1); + GString *num_name = g_string_new(NULL); for (i = 0; i < memory->num; ++i) { MemoryRegion *m; - sprintf(num_name, "%s%u", name, i); - m = g_malloc(sizeof(*m)); - memory_region_init_ram(m, NULL, num_name, - memory->location[i].size, - &error_fatal); - vmstate_register_ram_global(m); + g_string_printf(num_name, "%s%u", name, i); + m = g_new(MemoryRegion, 1); + memory_region_init_ram(m, NULL, num_name->str, + memory->location[i].size, &error_fatal); memory_region_add_subregion(get_system_memory(), memory->location[i].addr, m); } - free(num_name); + g_string_free(num_name, true); } static uint64_t translate_phys_addr(void *opaque, uint64_t addr) @@ -114,6 +112,9 @@ static void xtensa_sim_init(MachineState *machine) xtensa_create_memory_regions(&sysram, "xtensa.sysram"); } + if (serial_hds[0]) { + xtensa_sim_open_console(serial_hds[0]); + } if (kernel_filename) { uint64_t elf_entry; uint64_t elf_lowaddr; @@ -136,6 +137,7 @@ static void xtensa_sim_machine_init(MachineClass *mc) mc->is_default = true; mc->init = xtensa_sim_init; mc->max_cpus = 4; + mc->no_serial = 1; } DEFINE_MACHINE("sim", xtensa_sim_machine_init) diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c index 11176e26bd..635a4d4ec3 100644 --- a/hw/xtensa/xtfpga.c +++ b/hw/xtensa/xtfpga.c @@ -40,7 +40,7 @@ #include "hw/sysbus.h" #include "hw/block/flash.h" #include "sysemu/block-backend.h" -#include "sysemu/char.h" +#include "chardev/char.h" #include "sysemu/device_tree.h" #include "qemu/error-report.h" #include "bootparam.h" @@ -100,7 +100,7 @@ static void lx60_fpga_write(void *opaque, hwaddr addr, case 0x10: /*board reset*/ if (val == 0xdead) { - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); } break; } @@ -147,7 +147,7 @@ static void lx60_net_init(MemoryRegion *address_space, sysbus_mmio_get_region(s, 1)); ram = g_malloc(sizeof(*ram)); - memory_region_init_ram(ram, OBJECT(s), "open_eth.ram", 16384, + memory_region_init_ram_nomigrate(ram, OBJECT(s), "open_eth.ram", 16384, &error_fatal); vmstate_register_ram_global(ram); memory_region_add_subregion(address_space, buffers, ram); @@ -251,7 +251,6 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine) ram = g_malloc(sizeof(*ram)); memory_region_init_ram(ram, NULL, "lx60.dram", machine->ram_size, &error_fatal); - vmstate_register_ram_global(ram); memory_region_add_subregion(system_memory, 0, ram); system_io = g_malloc(sizeof(*system_io)); @@ -294,7 +293,6 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine) rom = g_malloc(sizeof(*rom)); memory_region_init_ram(rom, NULL, "lx60.sram", board->sram_size, &error_fatal); - vmstate_register_ram_global(rom); memory_region_add_subregion(system_memory, 0xfe000000, rom); if (kernel_cmdline) { |