diff options
author | Michael S. Tsirkin <mst@redhat.com> | 2011-06-14 17:51:11 +0300 |
---|---|---|
committer | Michael S. Tsirkin <mst@redhat.com> | 2011-06-15 18:27:15 +0300 |
commit | befeac45d4d9afb587eca9a27d975db4a7950960 (patch) | |
tree | aab24c856a3ea944e287d7f2591bb4bab6a56eb4 /hw | |
parent | 35f754620615138aaae0ef72602f84c88fd8de0f (diff) | |
parent | 0b862cedf36d927818c50584ddd611b0370673df (diff) |
Merge remote-tracking branch 'origin/master' into pci
Conflicts:
hw/virtio-pci.c
Diffstat (limited to 'hw')
105 files changed, 5163 insertions, 3048 deletions
diff --git a/hw/9pfs/virtio-9p-debug.c b/hw/9pfs/virtio-9p-debug.c index 6b18842fd4..4636ad51f0 100644 --- a/hw/9pfs/virtio-9p-debug.c +++ b/hw/9pfs/virtio-9p-debug.c @@ -10,8 +10,9 @@ * the COPYING file in the top-level directory. * */ -#include "virtio.h" -#include "pc.h" + +#include "hw/virtio.h" +#include "hw/pc.h" #include "virtio-9p.h" #include "virtio-9p-debug.h" diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c new file mode 100644 index 0000000000..f235236ea0 --- /dev/null +++ b/hw/9pfs/virtio-9p-device.c @@ -0,0 +1,173 @@ +/* + * Virtio 9p backend + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#include "hw/virtio.h" +#include "hw/pc.h" +#include "qemu_socket.h" +#include "hw/virtio-pci.h" +#include "virtio-9p.h" +#include "fsdev/qemu-fsdev.h" +#include "virtio-9p-xattr.h" + +static uint32_t virtio_9p_get_features(VirtIODevice *vdev, uint32_t features) +{ + features |= 1 << VIRTIO_9P_MOUNT_TAG; + return features; +} + +static V9fsState *to_virtio_9p(VirtIODevice *vdev) +{ + return (V9fsState *)vdev; +} + +static void virtio_9p_get_config(VirtIODevice *vdev, uint8_t *config) +{ + struct virtio_9p_config *cfg; + V9fsState *s = to_virtio_9p(vdev); + + cfg = qemu_mallocz(sizeof(struct virtio_9p_config) + + s->tag_len); + stw_raw(&cfg->tag_len, s->tag_len); + memcpy(cfg->tag, s->tag, s->tag_len); + memcpy(config, cfg, s->config_size); + qemu_free(cfg); +} + +VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf) + { + V9fsState *s; + int i, len; + struct stat stat; + FsTypeEntry *fse; + + + s = (V9fsState *)virtio_common_init("virtio-9p", + VIRTIO_ID_9P, + sizeof(struct virtio_9p_config)+ + MAX_TAG_LEN, + sizeof(V9fsState)); + + /* initialize pdu allocator */ + QLIST_INIT(&s->free_list); + for (i = 0; i < (MAX_REQ - 1); i++) { + QLIST_INSERT_HEAD(&s->free_list, &s->pdus[i], next); + } + + s->vq = virtio_add_queue(&s->vdev, MAX_REQ, handle_9p_output); + + fse = get_fsdev_fsentry(conf->fsdev_id); + + if (!fse) { + /* We don't have a fsdev identified by fsdev_id */ + fprintf(stderr, "Virtio-9p device couldn't find fsdev with the " + "id = %s\n", conf->fsdev_id ? conf->fsdev_id : "NULL"); + exit(1); + } + + if (!fse->path || !conf->tag) { + /* we haven't specified a mount_tag or the path */ + fprintf(stderr, "fsdev with id %s needs path " + "and Virtio-9p device needs mount_tag arguments\n", + conf->fsdev_id); + exit(1); + } + + if (!strcmp(fse->security_model, "passthrough")) { + /* Files on the Fileserver set to client user credentials */ + s->ctx.fs_sm = SM_PASSTHROUGH; + s->ctx.xops = passthrough_xattr_ops; + } else if (!strcmp(fse->security_model, "mapped")) { + /* Files on the fileserver are set to QEMU credentials. + * Client user credentials are saved in extended attributes. + */ + s->ctx.fs_sm = SM_MAPPED; + s->ctx.xops = mapped_xattr_ops; + } else if (!strcmp(fse->security_model, "none")) { + /* + * Files on the fileserver are set to QEMU credentials. + */ + s->ctx.fs_sm = SM_NONE; + s->ctx.xops = none_xattr_ops; + } else { + fprintf(stderr, "Default to security_model=none. You may want" + " enable advanced security model using " + "security option:\n\t security_model=passthrough\n\t " + "security_model=mapped\n"); + s->ctx.fs_sm = SM_NONE; + s->ctx.xops = none_xattr_ops; + } + + if (lstat(fse->path, &stat)) { + fprintf(stderr, "share path %s does not exist\n", fse->path); + exit(1); + } else if (!S_ISDIR(stat.st_mode)) { + fprintf(stderr, "share path %s is not a directory\n", fse->path); + exit(1); + } + + s->ctx.fs_root = qemu_strdup(fse->path); + len = strlen(conf->tag); + if (len > MAX_TAG_LEN) { + len = MAX_TAG_LEN; + } + /* s->tag is non-NULL terminated string */ + s->tag = qemu_malloc(len); + memcpy(s->tag, conf->tag, len); + s->tag_len = len; + s->ctx.uid = -1; + + s->ops = fse->ops; + s->vdev.get_features = virtio_9p_get_features; + s->config_size = sizeof(struct virtio_9p_config) + + s->tag_len; + s->vdev.get_config = virtio_9p_get_config; + + return &s->vdev; +} + +static int virtio_9p_init_pci(PCIDevice *pci_dev) +{ + VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); + VirtIODevice *vdev; + + vdev = virtio_9p_init(&pci_dev->qdev, &proxy->fsconf); + vdev->nvectors = proxy->nvectors; + virtio_init_pci(proxy, vdev); + /* make the actual value visible */ + proxy->nvectors = vdev->nvectors; + return 0; +} + +static PCIDeviceInfo virtio_9p_info = { + .qdev.name = "virtio-9p-pci", + .qdev.size = sizeof(VirtIOPCIProxy), + .init = virtio_9p_init_pci, + .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET, + .device_id = 0x1009, + .revision = VIRTIO_PCI_ABI_VERSION, + .class_id = 0x2, + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), + DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features), + DEFINE_PROP_STRING("mount_tag", VirtIOPCIProxy, fsconf.tag), + DEFINE_PROP_STRING("fsdev", VirtIOPCIProxy, fsconf.fsdev_id), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static void virtio_9p_register_devices(void) +{ + pci_qdev_register(&virtio_9p_info); +} + +device_init(virtio_9p_register_devices) diff --git a/hw/9pfs/virtio-9p-local.c b/hw/9pfs/virtio-9p-local.c index 0a015de9a5..77904c37bd 100644 --- a/hw/9pfs/virtio-9p-local.c +++ b/hw/9pfs/virtio-9p-local.c @@ -10,7 +10,8 @@ * the COPYING file in the top-level directory. * */ -#include "virtio.h" + +#include "hw/virtio.h" #include "virtio-9p.h" #include "virtio-9p-xattr.h" #include <arpa/inet.h> @@ -24,7 +25,8 @@ static int local_lstat(FsContext *fs_ctx, const char *path, struct stat *stbuf) { int err; - err = lstat(rpath(fs_ctx, path), stbuf); + char buffer[PATH_MAX]; + err = lstat(rpath(fs_ctx, path, buffer), stbuf); if (err) { return err; } @@ -34,19 +36,19 @@ static int local_lstat(FsContext *fs_ctx, const char *path, struct stat *stbuf) gid_t tmp_gid; mode_t tmp_mode; dev_t tmp_dev; - if (getxattr(rpath(fs_ctx, path), "user.virtfs.uid", &tmp_uid, + if (getxattr(rpath(fs_ctx, path, buffer), "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) { stbuf->st_uid = tmp_uid; } - if (getxattr(rpath(fs_ctx, path), "user.virtfs.gid", &tmp_gid, + if (getxattr(rpath(fs_ctx, path, buffer), "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) { stbuf->st_gid = tmp_gid; } - if (getxattr(rpath(fs_ctx, path), "user.virtfs.mode", &tmp_mode, - sizeof(mode_t)) > 0) { + if (getxattr(rpath(fs_ctx, path, buffer), "user.virtfs.mode", + &tmp_mode, sizeof(mode_t)) > 0) { stbuf->st_mode = tmp_mode; } - if (getxattr(rpath(fs_ctx, path), "user.virtfs.rdev", &tmp_dev, + if (getxattr(rpath(fs_ctx, path, buffer), "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) { stbuf->st_rdev = tmp_dev; } @@ -91,10 +93,12 @@ static int local_set_xattr(const char *path, FsCred *credp) static int local_post_create_passthrough(FsContext *fs_ctx, const char *path, FsCred *credp) { - if (chmod(rpath(fs_ctx, path), credp->fc_mode & 07777) < 0) { + char buffer[PATH_MAX]; + if (chmod(rpath(fs_ctx, path, buffer), credp->fc_mode & 07777) < 0) { return -1; } - if (lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid) < 0) { + if (lchown(rpath(fs_ctx, path, buffer), credp->fc_uid, + credp->fc_gid) < 0) { /* * If we fail to change ownership and if we are * using security model none. Ignore the error @@ -110,9 +114,10 @@ static ssize_t local_readlink(FsContext *fs_ctx, const char *path, char *buf, size_t bufsz) { ssize_t tsize = -1; + char buffer[PATH_MAX]; if (fs_ctx->fs_sm == SM_MAPPED) { int fd; - fd = open(rpath(fs_ctx, path), O_RDONLY); + fd = open(rpath(fs_ctx, path, buffer), O_RDONLY); if (fd == -1) { return -1; } @@ -123,7 +128,7 @@ static ssize_t local_readlink(FsContext *fs_ctx, const char *path, return tsize; } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) || (fs_ctx->fs_sm == SM_NONE)) { - tsize = readlink(rpath(fs_ctx, path), buf, bufsz); + tsize = readlink(rpath(fs_ctx, path, buffer), buf, bufsz); } return tsize; } @@ -140,12 +145,14 @@ static int local_closedir(FsContext *ctx, DIR *dir) static int local_open(FsContext *ctx, const char *path, int flags) { - return open(rpath(ctx, path), flags); + char buffer[PATH_MAX]; + return open(rpath(ctx, path, buffer), flags); } static DIR *local_opendir(FsContext *ctx, const char *path) { - return opendir(rpath(ctx, path)); + char buffer[PATH_MAX]; + return opendir(rpath(ctx, path, buffer)); } static void local_rewinddir(FsContext *ctx, DIR *dir) @@ -200,11 +207,12 @@ static ssize_t local_pwritev(FsContext *ctx, int fd, const struct iovec *iov, static int local_chmod(FsContext *fs_ctx, const char *path, FsCred *credp) { + char buffer[PATH_MAX]; if (fs_ctx->fs_sm == SM_MAPPED) { - return local_set_xattr(rpath(fs_ctx, path), credp); + return local_set_xattr(rpath(fs_ctx, path, buffer), credp); } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) || (fs_ctx->fs_sm == SM_NONE)) { - return chmod(rpath(fs_ctx, path), credp->fc_mode); + return chmod(rpath(fs_ctx, path, buffer), credp->fc_mode); } return -1; } @@ -213,21 +221,24 @@ static int local_mknod(FsContext *fs_ctx, const char *path, FsCred *credp) { int err = -1; int serrno = 0; + char buffer[PATH_MAX]; /* Determine the security model */ if (fs_ctx->fs_sm == SM_MAPPED) { - err = mknod(rpath(fs_ctx, path), SM_LOCAL_MODE_BITS|S_IFREG, 0); + err = mknod(rpath(fs_ctx, path, buffer), + SM_LOCAL_MODE_BITS|S_IFREG, 0); if (err == -1) { return err; } - local_set_xattr(rpath(fs_ctx, path), credp); + local_set_xattr(rpath(fs_ctx, path, buffer), credp); if (err == -1) { serrno = errno; goto err_end; } } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) || (fs_ctx->fs_sm == SM_NONE)) { - err = mknod(rpath(fs_ctx, path), credp->fc_mode, credp->fc_rdev); + err = mknod(rpath(fs_ctx, path, buffer), credp->fc_mode, + credp->fc_rdev); if (err == -1) { return err; } @@ -240,7 +251,7 @@ static int local_mknod(FsContext *fs_ctx, const char *path, FsCred *credp) return err; err_end: - remove(rpath(fs_ctx, path)); + remove(rpath(fs_ctx, path, buffer)); errno = serrno; return err; } @@ -249,22 +260,23 @@ static int local_mkdir(FsContext *fs_ctx, const char *path, FsCred *credp) { int err = -1; int serrno = 0; + char buffer[PATH_MAX]; /* Determine the security model */ if (fs_ctx->fs_sm == SM_MAPPED) { - err = mkdir(rpath(fs_ctx, path), SM_LOCAL_DIR_MODE_BITS); + err = mkdir(rpath(fs_ctx, path, buffer), SM_LOCAL_DIR_MODE_BITS); if (err == -1) { return err; } credp->fc_mode = credp->fc_mode|S_IFDIR; - err = local_set_xattr(rpath(fs_ctx, path), credp); + err = local_set_xattr(rpath(fs_ctx, path, buffer), credp); if (err == -1) { serrno = errno; goto err_end; } } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) || (fs_ctx->fs_sm == SM_NONE)) { - err = mkdir(rpath(fs_ctx, path), credp->fc_mode); + err = mkdir(rpath(fs_ctx, path, buffer), credp->fc_mode); if (err == -1) { return err; } @@ -277,7 +289,7 @@ static int local_mkdir(FsContext *fs_ctx, const char *path, FsCred *credp) return err; err_end: - remove(rpath(fs_ctx, path)); + remove(rpath(fs_ctx, path, buffer)); errno = serrno; return err; } @@ -318,23 +330,24 @@ static int local_open2(FsContext *fs_ctx, const char *path, int flags, int fd = -1; int err = -1; int serrno = 0; + char buffer[PATH_MAX]; /* Determine the security model */ if (fs_ctx->fs_sm == SM_MAPPED) { - fd = open(rpath(fs_ctx, path), flags, SM_LOCAL_MODE_BITS); + fd = open(rpath(fs_ctx, path, buffer), flags, SM_LOCAL_MODE_BITS); if (fd == -1) { return fd; } credp->fc_mode = credp->fc_mode|S_IFREG; /* Set cleint credentials in xattr */ - err = local_set_xattr(rpath(fs_ctx, path), credp); + err = local_set_xattr(rpath(fs_ctx, path, buffer), credp); if (err == -1) { serrno = errno; goto err_end; } } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) || (fs_ctx->fs_sm == SM_NONE)) { - fd = open(rpath(fs_ctx, path), flags, credp->fc_mode); + fd = open(rpath(fs_ctx, path, buffer), flags, credp->fc_mode); if (fd == -1) { return fd; } @@ -348,7 +361,7 @@ static int local_open2(FsContext *fs_ctx, const char *path, int flags, err_end: close(fd); - remove(rpath(fs_ctx, path)); + remove(rpath(fs_ctx, path, buffer)); errno = serrno; return err; } @@ -359,12 +372,13 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath, { int err = -1; int serrno = 0; + char buffer[PATH_MAX]; /* Determine the security model */ if (fs_ctx->fs_sm == SM_MAPPED) { int fd; ssize_t oldpath_size, write_size; - fd = open(rpath(fs_ctx, newpath), O_CREAT|O_EXCL|O_RDWR, + fd = open(rpath(fs_ctx, newpath, buffer), O_CREAT|O_EXCL|O_RDWR, SM_LOCAL_MODE_BITS); if (fd == -1) { return fd; @@ -384,18 +398,19 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath, close(fd); /* Set cleint credentials in symlink's xattr */ credp->fc_mode = credp->fc_mode|S_IFLNK; - err = local_set_xattr(rpath(fs_ctx, newpath), credp); + err = local_set_xattr(rpath(fs_ctx, newpath, buffer), credp); if (err == -1) { serrno = errno; goto err_end; } } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) || (fs_ctx->fs_sm == SM_NONE)) { - err = symlink(oldpath, rpath(fs_ctx, newpath)); + err = symlink(oldpath, rpath(fs_ctx, newpath, buffer)); if (err) { return err; } - err = lchown(rpath(fs_ctx, newpath), credp->fc_uid, credp->fc_gid); + err = lchown(rpath(fs_ctx, newpath, buffer), credp->fc_uid, + credp->fc_gid); if (err == -1) { /* * If we fail to change ownership and if we are @@ -411,70 +426,45 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath, return err; err_end: - remove(rpath(fs_ctx, newpath)); + remove(rpath(fs_ctx, newpath, buffer)); errno = serrno; return err; } static int local_link(FsContext *ctx, const char *oldpath, const char *newpath) { - char *tmp = qemu_strdup(rpath(ctx, oldpath)); - int err, serrno = 0; - - if (tmp == NULL) { - return -ENOMEM; - } - - err = link(tmp, rpath(ctx, newpath)); - if (err == -1) { - serrno = errno; - } - - qemu_free(tmp); - - if (err == -1) { - errno = serrno; - } + char buffer[PATH_MAX], buffer1[PATH_MAX]; - return err; + return link(rpath(ctx, oldpath, buffer), rpath(ctx, newpath, buffer1)); } static int local_truncate(FsContext *ctx, const char *path, off_t size) { - return truncate(rpath(ctx, path), size); + char buffer[PATH_MAX]; + return truncate(rpath(ctx, path, buffer), size); } static int local_rename(FsContext *ctx, const char *oldpath, const char *newpath) { - char *tmp; - int err; - - tmp = qemu_strdup(rpath(ctx, oldpath)); - - err = rename(tmp, rpath(ctx, newpath)); - if (err == -1) { - int serrno = errno; - qemu_free(tmp); - errno = serrno; - } else { - qemu_free(tmp); - } - - return err; + char buffer[PATH_MAX], buffer1[PATH_MAX]; + return rename(rpath(ctx, oldpath, buffer), rpath(ctx, newpath, buffer1)); } static int local_chown(FsContext *fs_ctx, const char *path, FsCred *credp) { + char buffer[PATH_MAX]; if ((credp->fc_uid == -1 && credp->fc_gid == -1) || (fs_ctx->fs_sm == SM_PASSTHROUGH)) { - return lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid); + return lchown(rpath(fs_ctx, path, buffer), credp->fc_uid, + credp->fc_gid); } else if (fs_ctx->fs_sm == SM_MAPPED) { - return local_set_xattr(rpath(fs_ctx, path), credp); + return local_set_xattr(rpath(fs_ctx, path, buffer), credp); } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) || (fs_ctx->fs_sm == SM_NONE)) { - return lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid); + return lchown(rpath(fs_ctx, path, buffer), credp->fc_uid, + credp->fc_gid); } return -1; } @@ -482,12 +472,15 @@ static int local_chown(FsContext *fs_ctx, const char *path, FsCred *credp) static int local_utimensat(FsContext *s, const char *path, const struct timespec *buf) { - return qemu_utimensat(AT_FDCWD, rpath(s, path), buf, AT_SYMLINK_NOFOLLOW); + char buffer[PATH_MAX]; + return qemu_utimensat(AT_FDCWD, rpath(s, path, buffer), buf, + AT_SYMLINK_NOFOLLOW); } static int local_remove(FsContext *ctx, const char *path) { - return remove(rpath(ctx, path)); + char buffer[PATH_MAX]; + return remove(rpath(ctx, path, buffer)); } static int local_fsync(FsContext *ctx, int fd, int datasync) @@ -501,7 +494,8 @@ static int local_fsync(FsContext *ctx, int fd, int datasync) static int local_statfs(FsContext *s, const char *path, struct statfs *stbuf) { - return statfs(rpath(s, path), stbuf); + char buffer[PATH_MAX]; + return statfs(rpath(s, path, buffer), stbuf); } static ssize_t local_lgetxattr(FsContext *ctx, const char *path, diff --git a/hw/9pfs/virtio-9p-posix-acl.c b/hw/9pfs/virtio-9p-posix-acl.c index 575abe86b0..f5b392e180 100644 --- a/hw/9pfs/virtio-9p-posix-acl.c +++ b/hw/9pfs/virtio-9p-posix-acl.c @@ -13,7 +13,7 @@ #include <sys/types.h> #include <attr/xattr.h> -#include "virtio.h" +#include "hw/virtio.h" #include "virtio-9p.h" #include "fsdev/file-op-9p.h" #include "virtio-9p-xattr.h" @@ -26,7 +26,8 @@ static ssize_t mp_pacl_getxattr(FsContext *ctx, const char *path, const char *name, void *value, size_t size) { - return lgetxattr(rpath(ctx, path), MAP_ACL_ACCESS, value, size); + char buffer[PATH_MAX]; + return lgetxattr(rpath(ctx, path, buffer), MAP_ACL_ACCESS, value, size); } static ssize_t mp_pacl_listxattr(FsContext *ctx, const char *path, @@ -50,14 +51,17 @@ static ssize_t mp_pacl_listxattr(FsContext *ctx, const char *path, static int mp_pacl_setxattr(FsContext *ctx, const char *path, const char *name, void *value, size_t size, int flags) { - return lsetxattr(rpath(ctx, path), MAP_ACL_ACCESS, value, size, flags); + char buffer[PATH_MAX]; + return lsetxattr(rpath(ctx, path, buffer), MAP_ACL_ACCESS, value, + size, flags); } static int mp_pacl_removexattr(FsContext *ctx, const char *path, const char *name) { int ret; - ret = lremovexattr(rpath(ctx, path), MAP_ACL_ACCESS); + char buffer[PATH_MAX]; + ret = lremovexattr(rpath(ctx, path, buffer), MAP_ACL_ACCESS); if (ret == -1 && errno == ENODATA) { /* * We don't get ENODATA error when trying to remove a @@ -73,7 +77,8 @@ static int mp_pacl_removexattr(FsContext *ctx, static ssize_t mp_dacl_getxattr(FsContext *ctx, const char *path, const char *name, void *value, size_t size) { - return lgetxattr(rpath(ctx, path), MAP_ACL_DEFAULT, value, size); + char buffer[PATH_MAX]; + return lgetxattr(rpath(ctx, path, buffer), MAP_ACL_DEFAULT, value, size); } static ssize_t mp_dacl_listxattr(FsContext *ctx, const char *path, @@ -97,14 +102,17 @@ static ssize_t mp_dacl_listxattr(FsContext *ctx, const char *path, static int mp_dacl_setxattr(FsContext *ctx, const char *path, const char *name, void *value, size_t size, int flags) { - return lsetxattr(rpath(ctx, path), MAP_ACL_DEFAULT, value, size, flags); + char buffer[PATH_MAX]; + return lsetxattr(rpath(ctx, path, buffer), MAP_ACL_DEFAULT, value, + size, flags); } static int mp_dacl_removexattr(FsContext *ctx, const char *path, const char *name) { int ret; - ret = lremovexattr(rpath(ctx, path), MAP_ACL_DEFAULT); + char buffer[PATH_MAX]; + ret = lremovexattr(rpath(ctx, path, buffer), MAP_ACL_DEFAULT); if (ret == -1 && errno == ENODATA) { /* * We don't get ENODATA error when trying to remove a diff --git a/hw/9pfs/virtio-9p-xattr-user.c b/hw/9pfs/virtio-9p-xattr-user.c index bba13ce643..5044a3e5ab 100644 --- a/hw/9pfs/virtio-9p-xattr-user.c +++ b/hw/9pfs/virtio-9p-xattr-user.c @@ -12,7 +12,7 @@ */ #include <sys/types.h> -#include "virtio.h" +#include "hw/virtio.h" #include "virtio-9p.h" #include "fsdev/file-op-9p.h" #include "virtio-9p-xattr.h" @@ -21,6 +21,7 @@ static ssize_t mp_user_getxattr(FsContext *ctx, const char *path, const char *name, void *value, size_t size) { + char buffer[PATH_MAX]; if (strncmp(name, "user.virtfs.", 12) == 0) { /* * Don't allow fetch of user.virtfs namesapce @@ -29,7 +30,7 @@ static ssize_t mp_user_getxattr(FsContext *ctx, const char *path, errno = ENOATTR; return -1; } - return lgetxattr(rpath(ctx, path), name, value, size); + return lgetxattr(rpath(ctx, path, buffer), name, value, size); } static ssize_t mp_user_listxattr(FsContext *ctx, const char *path, @@ -67,6 +68,7 @@ static ssize_t mp_user_listxattr(FsContext *ctx, const char *path, static int mp_user_setxattr(FsContext *ctx, const char *path, const char *name, void *value, size_t size, int flags) { + char buffer[PATH_MAX]; if (strncmp(name, "user.virtfs.", 12) == 0) { /* * Don't allow fetch of user.virtfs namesapce @@ -75,12 +77,13 @@ static int mp_user_setxattr(FsContext *ctx, const char *path, const char *name, errno = EACCES; return -1; } - return lsetxattr(rpath(ctx, path), name, value, size, flags); + return lsetxattr(rpath(ctx, path, buffer), name, value, size, flags); } static int mp_user_removexattr(FsContext *ctx, const char *path, const char *name) { + char buffer[PATH_MAX]; if (strncmp(name, "user.virtfs.", 12) == 0) { /* * Don't allow fetch of user.virtfs namesapce @@ -89,7 +92,7 @@ static int mp_user_removexattr(FsContext *ctx, errno = EACCES; return -1; } - return lremovexattr(rpath(ctx, path), name); + return lremovexattr(rpath(ctx, path, buffer), name); } XattrOperations mapped_user_xattr = { diff --git a/hw/9pfs/virtio-9p-xattr.c b/hw/9pfs/virtio-9p-xattr.c index 03c3d3f6bb..bde0b7fb4f 100644 --- a/hw/9pfs/virtio-9p-xattr.c +++ b/hw/9pfs/virtio-9p-xattr.c @@ -11,7 +11,7 @@ * */ -#include "virtio.h" +#include "hw/virtio.h" #include "virtio-9p.h" #include "fsdev/file-op-9p.h" #include "virtio-9p-xattr.h" @@ -66,20 +66,21 @@ ssize_t v9fs_list_xattr(FsContext *ctx, const char *path, void *value, size_t vsize) { ssize_t size = 0; + char buffer[PATH_MAX]; void *ovalue = value; XattrOperations *xops; char *orig_value, *orig_value_start; ssize_t xattr_len, parsed_len = 0, attr_len; /* Get the actual len */ - xattr_len = llistxattr(rpath(ctx, path), value, 0); + xattr_len = llistxattr(rpath(ctx, path, buffer), value, 0); if (xattr_len <= 0) { return xattr_len; } /* Now fetch the xattr and find the actual size */ orig_value = qemu_malloc(xattr_len); - xattr_len = llistxattr(rpath(ctx, path), orig_value, xattr_len); + xattr_len = llistxattr(rpath(ctx, path, buffer), orig_value, xattr_len); /* store the orig pointer */ orig_value_start = orig_value; diff --git a/hw/9pfs/virtio-9p-xattr.h b/hw/9pfs/virtio-9p-xattr.h index 2bbae2dcb5..247e414ebd 100644 --- a/hw/9pfs/virtio-9p-xattr.h +++ b/hw/9pfs/virtio-9p-xattr.h @@ -54,20 +54,23 @@ ssize_t pt_listxattr(FsContext *ctx, const char *path, char *name, void *value, static inline ssize_t pt_getxattr(FsContext *ctx, const char *path, const char *name, void *value, size_t size) { - return lgetxattr(rpath(ctx, path), name, value, size); + char buffer[PATH_MAX]; + return lgetxattr(rpath(ctx, path, buffer), name, value, size); } static inline int pt_setxattr(FsContext *ctx, const char *path, const char *name, void *value, size_t size, int flags) { - return lsetxattr(rpath(ctx, path), name, value, size, flags); + char buffer[PATH_MAX]; + return lsetxattr(rpath(ctx, path, buffer), name, value, size, flags); } static inline int pt_removexattr(FsContext *ctx, const char *path, const char *name) { - return lremovexattr(rpath(ctx, path), name); + char buffer[PATH_MAX]; + return lremovexattr(rpath(ctx, path, buffer), name); } static inline ssize_t notsup_getxattr(FsContext *ctx, const char *path, diff --git a/hw/9pfs/virtio-9p.c b/hw/9pfs/virtio-9p.c index b5fc52b3eb..4890df6f75 100644 --- a/hw/9pfs/virtio-9p.c +++ b/hw/9pfs/virtio-9p.c @@ -11,9 +11,10 @@ * */ -#include "virtio.h" -#include "pc.h" +#include "hw/virtio.h" +#include "hw/pc.h" #include "qemu_socket.h" +#include "hw/virtio-pci.h" #include "virtio-9p.h" #include "fsdev/qemu-fsdev.h" #include "virtio-9p-debug.h" @@ -194,7 +195,6 @@ static int v9fs_do_open2(V9fsState *s, char *fullname, uid_t uid, gid_t gid, cred.fc_uid = uid; cred.fc_gid = gid; cred.fc_mode = mode & 07777; - flags = flags; return s->ops->open2(&s->ctx, fullname, flags, &cred); } @@ -423,6 +423,22 @@ static void v9fs_string_copy(V9fsString *lhs, V9fsString *rhs) v9fs_string_sprintf(lhs, "%s", rhs->data); } +/* + * Return TRUE if s1 is an ancestor of s2. + * + * E.g. "a/b" is an ancestor of "a/b/c" but not of "a/bc/d". + * As a special case, We treat s1 as ancestor of s2 if they are same! + */ +static int v9fs_path_is_ancestor(V9fsString *s1, V9fsString *s2) +{ + if (!strncmp(s1->data, s2->data, s1->size)) { + if (s2->data[s1->size] == '\0' || s2->data[s1->size] == '/') { + return 1; + } + } + return 0; +} + static size_t v9fs_string_size(V9fsString *str) { return str->size; @@ -2805,13 +2821,13 @@ static int v9fs_complete_rename(V9fsState *s, V9fsRenameState *vs) for (fidp = s->fid_list; fidp; fidp = fidp->next) { if (vs->fidp == fidp) { /* - * we replace name of this fid towards the end - * so that our below strcmp will work + * we replace name of this fid towards the end so + * that our below v9fs_path_is_ancestor check will + * work */ continue; } - if (!strncmp(vs->fidp->path.data, fidp->path.data, - strlen(vs->fidp->path.data))) { + if (v9fs_path_is_ancestor(&vs->fidp->path, &fidp->path)) { /* replace the name */ v9fs_fix_path(&fidp->path, &vs->name, strlen(vs->fidp->path.data)); @@ -3589,6 +3605,11 @@ static pdu_handler_t *pdu_handlers[] = { [P9_TREMOVE] = v9fs_remove, }; +static void v9fs_op_not_supp(V9fsState *s, V9fsPDU *pdu) +{ + complete_pdu(s, pdu, -EOPNOTSUPP); +} + static void submit_pdu(V9fsState *s, V9fsPDU *pdu) { pdu_handler_t *handler; @@ -3596,16 +3617,16 @@ static void submit_pdu(V9fsState *s, V9fsPDU *pdu) if (debug_9p_pdu) { pprint_pdu(pdu); } - - BUG_ON(pdu->id >= ARRAY_SIZE(pdu_handlers)); - - handler = pdu_handlers[pdu->id]; - BUG_ON(handler == NULL); - + if (pdu->id >= ARRAY_SIZE(pdu_handlers) || + (pdu_handlers[pdu->id] == NULL)) { + handler = v9fs_op_not_supp; + } else { + handler = pdu_handlers[pdu->id]; + } handler(s, pdu); } -static void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq) +void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq) { V9fsState *s = (V9fsState *)vdev; V9fsPDU *pdu; @@ -3629,119 +3650,3 @@ static void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq) free_pdu(s, pdu); } - -static uint32_t virtio_9p_get_features(VirtIODevice *vdev, uint32_t features) -{ - features |= 1 << VIRTIO_9P_MOUNT_TAG; - return features; -} - -static V9fsState *to_virtio_9p(VirtIODevice *vdev) -{ - return (V9fsState *)vdev; -} - -static void virtio_9p_get_config(VirtIODevice *vdev, uint8_t *config) -{ - struct virtio_9p_config *cfg; - V9fsState *s = to_virtio_9p(vdev); - - cfg = qemu_mallocz(sizeof(struct virtio_9p_config) + - s->tag_len); - stw_raw(&cfg->tag_len, s->tag_len); - memcpy(cfg->tag, s->tag, s->tag_len); - memcpy(config, cfg, s->config_size); - qemu_free(cfg); -} - -VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf) - { - V9fsState *s; - int i, len; - struct stat stat; - FsTypeEntry *fse; - - - s = (V9fsState *)virtio_common_init("virtio-9p", - VIRTIO_ID_9P, - sizeof(struct virtio_9p_config)+ - MAX_TAG_LEN, - sizeof(V9fsState)); - - /* initialize pdu allocator */ - QLIST_INIT(&s->free_list); - for (i = 0; i < (MAX_REQ - 1); i++) { - QLIST_INSERT_HEAD(&s->free_list, &s->pdus[i], next); - } - - s->vq = virtio_add_queue(&s->vdev, MAX_REQ, handle_9p_output); - - fse = get_fsdev_fsentry(conf->fsdev_id); - - if (!fse) { - /* We don't have a fsdev identified by fsdev_id */ - fprintf(stderr, "Virtio-9p device couldn't find fsdev with the " - "id = %s\n", conf->fsdev_id ? conf->fsdev_id : "NULL"); - exit(1); - } - - if (!fse->path || !conf->tag) { - /* we haven't specified a mount_tag or the path */ - fprintf(stderr, "fsdev with id %s needs path " - "and Virtio-9p device needs mount_tag arguments\n", - conf->fsdev_id); - exit(1); - } - - if (!strcmp(fse->security_model, "passthrough")) { - /* Files on the Fileserver set to client user credentials */ - s->ctx.fs_sm = SM_PASSTHROUGH; - s->ctx.xops = passthrough_xattr_ops; - } else if (!strcmp(fse->security_model, "mapped")) { - /* Files on the fileserver are set to QEMU credentials. - * Client user credentials are saved in extended attributes. - */ - s->ctx.fs_sm = SM_MAPPED; - s->ctx.xops = mapped_xattr_ops; - } else if (!strcmp(fse->security_model, "none")) { - /* - * Files on the fileserver are set to QEMU credentials. - */ - s->ctx.fs_sm = SM_NONE; - s->ctx.xops = none_xattr_ops; - } else { - fprintf(stderr, "Default to security_model=none. You may want" - " enable advanced security model using " - "security option:\n\t security_model=passthrough \n\t " - "security_model=mapped\n"); - s->ctx.fs_sm = SM_NONE; - s->ctx.xops = none_xattr_ops; - } - - if (lstat(fse->path, &stat)) { - fprintf(stderr, "share path %s does not exist\n", fse->path); - exit(1); - } else if (!S_ISDIR(stat.st_mode)) { - fprintf(stderr, "share path %s is not a directory \n", fse->path); - exit(1); - } - - s->ctx.fs_root = qemu_strdup(fse->path); - len = strlen(conf->tag); - if (len > MAX_TAG_LEN) { - len = MAX_TAG_LEN; - } - /* s->tag is non-NULL terminated string */ - s->tag = qemu_malloc(len); - memcpy(s->tag, conf->tag, len); - s->tag_len = len; - s->ctx.uid = -1; - - s->ops = fse->ops; - s->vdev.get_features = virtio_9p_get_features; - s->config_size = sizeof(struct virtio_9p_config) + - s->tag_len; - s->vdev.get_config = virtio_9p_get_config; - - return &s->vdev; -} diff --git a/hw/9pfs/virtio-9p.h b/hw/9pfs/virtio-9p.h index 622928fce5..2bfbe622af 100644 --- a/hw/9pfs/virtio-9p.h +++ b/hw/9pfs/virtio-9p.h @@ -101,6 +101,11 @@ enum p9_proto_version { #define P9_NOTAG (u16)(~0) #define P9_NOFID (u32)(~0) #define P9_MAXWELEM 16 +static inline const char *rpath(FsContext *ctx, const char *path, char *buffer) +{ + snprintf(buffer, PATH_MAX, "%s/%s", ctx->fs_root, path); + return buffer; +} /* * ample room for Twrite/Rread header @@ -504,4 +509,6 @@ static inline size_t do_pdu_unpack(void *dst, struct iovec *sg, int sg_count, return pdu_packunpack(dst, sg, sg_count, offset, size, 0); } +extern void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq); + #endif @@ -1001,8 +1001,6 @@ static int write_audio (AC97LinkState *s, AC97BusMasterRegs *r, static void write_bup (AC97LinkState *s, int elapsed) { - int written = 0; - dolog ("write_bup\n"); if (!(s->bup_flag & BUP_SET)) { if (s->bup_flag & BUP_LAST) { @@ -1026,7 +1024,6 @@ static void write_bup (AC97LinkState *s, int elapsed) return; temp -= copied; elapsed -= copied; - written += copied; } } } @@ -1069,7 +1066,7 @@ static int read_audio (AC97LinkState *s, AC97BusMasterRegs *r, static void transfer_audio (AC97LinkState *s, int index, int elapsed) { AC97BusMasterRegs *r = &s->bm_regs[index]; - int written = 0, stop = 0; + int stop = 0; if (s->invalid_freq[index]) { AUD_log ("ac97", "attempt to use voice %d with invalid frequency %d\n", @@ -1114,7 +1111,6 @@ static void transfer_audio (AC97LinkState *s, int index, int elapsed) switch (index) { case PO_INDEX: temp = write_audio (s, r, elapsed, &stop); - written += temp; elapsed -= temp; r->picb -= (temp >> 1); break; diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c index 232008dd93..350558b859 100644 --- a/hw/acpi_piix4.c +++ b/hw/acpi_piix4.c @@ -471,11 +471,13 @@ static void pciej_write(void *opaque, uint32_t addr, uint32_t val) BusState *bus = opaque; DeviceState *qdev, *next; PCIDevice *dev; + PCIDeviceInfo *info; int slot = ffs(val) - 1; QLIST_FOREACH_SAFE(qdev, &bus->children, sibling, next) { dev = DO_UPCAST(PCIDevice, qdev, qdev); - if (PCI_SLOT(dev->devfn) == slot) { + info = container_of(qdev->info, PCIDeviceInfo, qdev); + if (PCI_SLOT(dev->devfn) == slot && !info->no_hotplug) { qdev_free(qdev); } } diff --git a/hw/alpha_palcode.c b/hw/alpha_palcode.c deleted file mode 100644 index 033b54201c..0000000000 --- a/hw/alpha_palcode.c +++ /dev/null @@ -1,1048 +0,0 @@ -/* - * Alpha emulation - PALcode emulation for qemu. - * - * Copyright (c) 2007 Jocelyn Mayer - * - * 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 <stdint.h> -#include <stdlib.h> -#include <stdio.h> - -#include "cpu.h" -#include "exec-all.h" - -/* Shared handlers */ -static void pal_reset (CPUState *env); -/* Console handlers */ -static void pal_console_call (CPUState *env, uint32_t palcode); -/* OpenVMS handlers */ -static void pal_openvms_call (CPUState *env, uint32_t palcode); -/* UNIX / Linux handlers */ -static void pal_unix_call (CPUState *env, uint32_t palcode); - -pal_handler_t pal_handlers[] = { - /* Console handler */ - { - .reset = &pal_reset, - .call_pal = &pal_console_call, - }, - /* OpenVMS handler */ - { - .reset = &pal_reset, - .call_pal = &pal_openvms_call, - }, - /* UNIX / Linux handler */ - { - .reset = &pal_reset, - .call_pal = &pal_unix_call, - }, -}; - -#if 0 -/* One must explicitly check that the TB is valid and the FOE bit is reset */ -static void update_itb (void) -{ - /* This writes into a temp register, not the actual one */ - mtpr(TB_TAG); - mtpr(TB_CTL); - /* This commits the TB update */ - mtpr(ITB_PTE); -} - -static void update_dtb (void); -{ - mtpr(TB_CTL); - /* This write into a temp register, not the actual one */ - mtpr(TB_TAG); - /* This commits the TB update */ - mtpr(DTB_PTE); -} -#endif - -static void pal_reset (CPUState *env) -{ -} - -static void do_swappal (CPUState *env, uint64_t palid) -{ - pal_handler_t *pal_handler; - - switch (palid) { - case 0 ... 2: - pal_handler = &pal_handlers[palid]; - env->pal_handler = pal_handler; - env->ipr[IPR_PAL_BASE] = -1ULL; - (*pal_handler->reset)(env); - break; - case 3 ... 255: - /* Unknown identifier */ - env->ir[0] = 1; - return; - default: - /* We were given the entry point address */ - env->pal_handler = NULL; - env->ipr[IPR_PAL_BASE] = palid; - env->pc = env->ipr[IPR_PAL_BASE]; - cpu_loop_exit(); - } -} - -static void pal_console_call (CPUState *env, uint32_t palcode) -{ - uint64_t palid; - - if (palcode < 0x00000080) { - /* Privileged palcodes */ - if (!(env->ps >> 3)) { - /* TODO: generate privilege exception */ - } - } - switch (palcode) { - case 0x00000000: - /* HALT */ - /* REQUIRED */ - break; - case 0x00000001: - /* CFLUSH */ - break; - case 0x00000002: - /* DRAINA */ - /* REQUIRED */ - /* Implemented as no-op */ - break; - case 0x00000009: - /* CSERVE */ - /* REQUIRED */ - break; - case 0x0000000A: - /* SWPPAL */ - /* REQUIRED */ - palid = env->ir[16]; - do_swappal(env, palid); - break; - case 0x00000080: - /* BPT */ - /* REQUIRED */ - break; - case 0x00000081: - /* BUGCHK */ - /* REQUIRED */ - break; - case 0x00000086: - /* IMB */ - /* REQUIRED */ - /* Implemented as no-op */ - break; - case 0x0000009E: - /* RDUNIQUE */ - /* REQUIRED */ - break; - case 0x0000009F: - /* WRUNIQUE */ - /* REQUIRED */ - break; - case 0x000000AA: - /* GENTRAP */ - /* REQUIRED */ - break; - default: - break; - } -} - -static void pal_openvms_call (CPUState *env, uint32_t palcode) -{ - uint64_t palid, val, oldval; - - if (palcode < 0x00000080) { - /* Privileged palcodes */ - if (!(env->ps >> 3)) { - /* TODO: generate privilege exception */ - } - } - switch (palcode) { - case 0x00000000: - /* HALT */ - /* REQUIRED */ - break; - case 0x00000001: - /* CFLUSH */ - break; - case 0x00000002: - /* DRAINA */ - /* REQUIRED */ - /* Implemented as no-op */ - break; - case 0x00000003: - /* LDQP */ - break; - case 0x00000004: - /* STQP */ - break; - case 0x00000005: - /* SWPCTX */ - break; - case 0x00000006: - /* MFPR_ASN */ - if (cpu_alpha_mfpr(env, IPR_ASN, &val) == 0) - env->ir[0] = val; - break; - case 0x00000007: - /* MTPR_ASTEN */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_ASTEN, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x00000008: - /* MTPR_ASTSR */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_ASTSR, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x00000009: - /* CSERVE */ - /* REQUIRED */ - break; - case 0x0000000A: - /* SWPPAL */ - /* REQUIRED */ - palid = env->ir[16]; - do_swappal(env, palid); - break; - case 0x0000000B: - /* MFPR_FEN */ - if (cpu_alpha_mfpr(env, IPR_FEN, &val) == 0) - env->ir[0] = val; - break; - case 0x0000000C: - /* MTPR_FEN */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_FEN, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x0000000D: - /* MTPR_IPIR */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_IPIR, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x0000000E: - /* MFPR_IPL */ - if (cpu_alpha_mfpr(env, IPR_IPL, &val) == 0) - env->ir[0] = val; - break; - case 0x0000000F: - /* MTPR_IPL */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_IPL, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x00000010: - /* MFPR_MCES */ - if (cpu_alpha_mfpr(env, IPR_MCES, &val) == 0) - env->ir[0] = val; - break; - case 0x00000011: - /* MTPR_MCES */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_MCES, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x00000012: - /* MFPR_PCBB */ - if (cpu_alpha_mfpr(env, IPR_PCBB, &val) == 0) - env->ir[0] = val; - break; - case 0x00000013: - /* MFPR_PRBR */ - if (cpu_alpha_mfpr(env, IPR_PRBR, &val) == 0) - env->ir[0] = val; - break; - case 0x00000014: - /* MTPR_PRBR */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_PRBR, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x00000015: - /* MFPR_PTBR */ - if (cpu_alpha_mfpr(env, IPR_PTBR, &val) == 0) - env->ir[0] = val; - break; - case 0x00000016: - /* MFPR_SCBB */ - if (cpu_alpha_mfpr(env, IPR_SCBB, &val) == 0) - env->ir[0] = val; - break; - case 0x00000017: - /* MTPR_SCBB */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_SCBB, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x00000018: - /* MTPR_SIRR */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_SIRR, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x00000019: - /* MFPR_SISR */ - if (cpu_alpha_mfpr(env, IPR_SISR, &val) == 0) - env->ir[0] = val; - break; - case 0x0000001A: - /* MFPR_TBCHK */ - if (cpu_alpha_mfpr(env, IPR_TBCHK, &val) == 0) - env->ir[0] = val; - break; - case 0x0000001B: - /* MTPR_TBIA */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_TBIA, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x0000001C: - /* MTPR_TBIAP */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_TBIAP, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x0000001D: - /* MTPR_TBIS */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_TBIS, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x0000001E: - /* MFPR_ESP */ - if (cpu_alpha_mfpr(env, IPR_ESP, &val) == 0) - env->ir[0] = val; - break; - case 0x0000001F: - /* MTPR_ESP */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_ESP, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x00000020: - /* MFPR_SSP */ - if (cpu_alpha_mfpr(env, IPR_SSP, &val) == 0) - env->ir[0] = val; - break; - case 0x00000021: - /* MTPR_SSP */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_SSP, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x00000022: - /* MFPR_USP */ - if (cpu_alpha_mfpr(env, IPR_USP, &val) == 0) - env->ir[0] = val; - break; - case 0x00000023: - /* MTPR_USP */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_USP, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x00000024: - /* MTPR_TBISD */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_TBISD, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x00000025: - /* MTPR_TBISI */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_TBISI, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x00000026: - /* MFPR_ASTEN */ - if (cpu_alpha_mfpr(env, IPR_ASTEN, &val) == 0) - env->ir[0] = val; - break; - case 0x00000027: - /* MFPR_ASTSR */ - if (cpu_alpha_mfpr(env, IPR_ASTSR, &val) == 0) - env->ir[0] = val; - break; - case 0x00000029: - /* MFPR_VPTB */ - if (cpu_alpha_mfpr(env, IPR_VPTB, &val) == 0) - env->ir[0] = val; - break; - case 0x0000002A: - /* MTPR_VPTB */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_VPTB, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x0000002B: - /* MTPR_PERFMON */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_PERFMON, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x0000002E: - /* MTPR_DATFX */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_DATFX, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x0000003E: - /* WTINT */ - break; - case 0x0000003F: - /* MFPR_WHAMI */ - if (cpu_alpha_mfpr(env, IPR_WHAMI, &val) == 0) - env->ir[0] = val; - break; - case 0x00000080: - /* BPT */ - /* REQUIRED */ - break; - case 0x00000081: - /* BUGCHK */ - /* REQUIRED */ - break; - case 0x00000082: - /* CHME */ - break; - case 0x00000083: - /* CHMK */ - break; - case 0x00000084: - /* CHMS */ - break; - case 0x00000085: - /* CHMU */ - break; - case 0x00000086: - /* IMB */ - /* REQUIRED */ - /* Implemented as no-op */ - break; - case 0x00000087: - /* INSQHIL */ - break; - case 0x00000088: - /* INSQTIL */ - break; - case 0x00000089: - /* INSQHIQ */ - break; - case 0x0000008A: - /* INSQTIQ */ - break; - case 0x0000008B: - /* INSQUEL */ - break; - case 0x0000008C: - /* INSQUEQ */ - break; - case 0x0000008D: - /* INSQUEL/D */ - break; - case 0x0000008E: - /* INSQUEQ/D */ - break; - case 0x0000008F: - /* PROBER */ - break; - case 0x00000090: - /* PROBEW */ - break; - case 0x00000091: - /* RD_PS */ - break; - case 0x00000092: - /* REI */ - break; - case 0x00000093: - /* REMQHIL */ - break; - case 0x00000094: - /* REMQTIL */ - break; - case 0x00000095: - /* REMQHIQ */ - break; - case 0x00000096: - /* REMQTIQ */ - break; - case 0x00000097: - /* REMQUEL */ - break; - case 0x00000098: - /* REMQUEQ */ - break; - case 0x00000099: - /* REMQUEL/D */ - break; - case 0x0000009A: - /* REMQUEQ/D */ - break; - case 0x0000009B: - /* SWASTEN */ - break; - case 0x0000009C: - /* WR_PS_SW */ - break; - case 0x0000009D: - /* RSCC */ - break; - case 0x0000009E: - /* READ_UNQ */ - /* REQUIRED */ - break; - case 0x0000009F: - /* WRITE_UNQ */ - /* REQUIRED */ - break; - case 0x000000A0: - /* AMOVRR */ - break; - case 0x000000A1: - /* AMOVRM */ - break; - case 0x000000A2: - /* INSQHILR */ - break; - case 0x000000A3: - /* INSQTILR */ - break; - case 0x000000A4: - /* INSQHIQR */ - break; - case 0x000000A5: - /* INSQTIQR */ - break; - case 0x000000A6: - /* REMQHILR */ - break; - case 0x000000A7: - /* REMQTILR */ - break; - case 0x000000A8: - /* REMQHIQR */ - break; - case 0x000000A9: - /* REMQTIQR */ - break; - case 0x000000AA: - /* GENTRAP */ - /* REQUIRED */ - break; - case 0x000000AE: - /* CLRFEN */ - break; - default: - break; - } -} - -static void pal_unix_call (CPUState *env, uint32_t palcode) -{ - uint64_t palid, val, oldval; - - if (palcode < 0x00000080) { - /* Privileged palcodes */ - if (!(env->ps >> 3)) { - /* TODO: generate privilege exception */ - } - } - switch (palcode) { - case 0x00000000: - /* HALT */ - /* REQUIRED */ - break; - case 0x00000001: - /* CFLUSH */ - break; - case 0x00000002: - /* DRAINA */ - /* REQUIRED */ - /* Implemented as no-op */ - break; - case 0x00000009: - /* CSERVE */ - /* REQUIRED */ - break; - case 0x0000000A: - /* SWPPAL */ - /* REQUIRED */ - palid = env->ir[16]; - do_swappal(env, palid); - break; - case 0x0000000D: - /* WRIPIR */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_IPIR, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x00000010: - /* RDMCES */ - if (cpu_alpha_mfpr(env, IPR_MCES, &val) == 0) - env->ir[0] = val; - break; - case 0x00000011: - /* WRMCES */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_MCES, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x0000002B: - /* WRFEN */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_PERFMON, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x0000002D: - /* WRVPTPTR */ - break; - case 0x00000030: - /* SWPCTX */ - break; - case 0x00000031: - /* WRVAL */ - break; - case 0x00000032: - /* RDVAL */ - break; - case 0x00000033: - /* TBI */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_TBIS, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x00000034: - /* WRENT */ - break; - case 0x00000035: - /* SWPIPL */ - break; - case 0x00000036: - /* RDPS */ - break; - case 0x00000037: - /* WRKGP */ - break; - case 0x00000038: - /* WRUSP */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_USP, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x00000039: - /* WRPERFMON */ - val = env->ir[16]; - if (cpu_alpha_mtpr(env, IPR_PERFMON, val, &oldval) == 1) - env->ir[0] = val; - break; - case 0x0000003A: - /* RDUSP */ - if (cpu_alpha_mfpr(env, IPR_USP, &val) == 0) - env->ir[0] = val; - break; - case 0x0000003C: - /* WHAMI */ - if (cpu_alpha_mfpr(env, IPR_WHAMI, &val) == 0) - env->ir[0] = val; - break; - case 0x0000003D: - /* RETSYS */ - break; - case 0x0000003E: - /* WTINT */ - break; - case 0x0000003F: - /* RTI */ - if (cpu_alpha_mfpr(env, IPR_WHAMI, &val) == 0) - env->ir[0] = val; - break; - case 0x00000080: - /* BPT */ - /* REQUIRED */ - break; - case 0x00000081: - /* BUGCHK */ - /* REQUIRED */ - break; - case 0x00000083: - /* CALLSYS */ - break; - case 0x00000086: - /* IMB */ - /* REQUIRED */ - /* Implemented as no-op */ - break; - case 0x00000092: - /* URTI */ - break; - case 0x0000009E: - /* RDUNIQUE */ - /* REQUIRED */ - break; - case 0x0000009F: - /* WRUNIQUE */ - /* REQUIRED */ - break; - case 0x000000AA: - /* GENTRAP */ - /* REQUIRED */ - break; - case 0x000000AE: - /* CLRFEN */ - break; - default: - break; - } -} - -void call_pal (CPUState *env) -{ - pal_handler_t *pal_handler = env->pal_handler; - - switch (env->exception_index) { - case EXCP_RESET: - (*pal_handler->reset)(env); - break; - case EXCP_MCHK: - (*pal_handler->machine_check)(env); - break; - case EXCP_ARITH: - (*pal_handler->arithmetic)(env); - break; - case EXCP_INTERRUPT: - (*pal_handler->interrupt)(env); - break; - case EXCP_DFAULT: - (*pal_handler->dfault)(env); - break; - case EXCP_DTB_MISS_PAL: - (*pal_handler->dtb_miss_pal)(env); - break; - case EXCP_DTB_MISS_NATIVE: - (*pal_handler->dtb_miss_native)(env); - break; - case EXCP_UNALIGN: - (*pal_handler->unalign)(env); - break; - case EXCP_ITB_MISS: - (*pal_handler->itb_miss)(env); - break; - case EXCP_ITB_ACV: - (*pal_handler->itb_acv)(env); - break; - case EXCP_OPCDEC: - (*pal_handler->opcdec)(env); - break; - case EXCP_FEN: - (*pal_handler->fen)(env); - break; - default: - if (env->exception_index >= EXCP_CALL_PAL && - env->exception_index < EXCP_CALL_PALP) { - /* Unprivileged PAL call */ - (*pal_handler->call_pal) - (env, (env->exception_index - EXCP_CALL_PAL) >> 6); - } else if (env->exception_index >= EXCP_CALL_PALP && - env->exception_index < EXCP_CALL_PALE) { - /* Privileged PAL call */ - (*pal_handler->call_pal) - (env, ((env->exception_index - EXCP_CALL_PALP) >> 6) + 0x80); - } else { - /* Should never happen */ - } - break; - } - env->ipr[IPR_EXC_ADDR] &= ~1; -} - -void pal_init (CPUState *env) -{ - do_swappal(env, 0); -} - -#if 0 -static uint64_t get_ptebase (CPUState *env, uint64_t vaddr) -{ - uint64_t virbnd, ptbr; - - if ((env->features & FEATURE_VIRBND)) { - cpu_alpha_mfpr(env, IPR_VIRBND, &virbnd); - if (vaddr >= virbnd) - cpu_alpha_mfpr(env, IPR_SYSPTBR, &ptbr); - else - cpu_alpha_mfpr(env, IPR_PTBR, &ptbr); - } else { - cpu_alpha_mfpr(env, IPR_PTBR, &ptbr); - } - - return ptbr; -} - -static int get_page_bits (CPUState *env) -{ - /* XXX */ - return 13; -} - -static int get_pte (uint64_t *pfnp, int *zbitsp, int *protp, - uint64_t ptebase, int page_bits, uint64_t level, - int mmu_idx, int rw) -{ - uint64_t pteaddr, pte, pfn; - uint8_t gh; - int ure, uwe, kre, kwe, foE, foR, foW, v, ret, ar, is_user; - - /* XXX: TOFIX */ - is_user = mmu_idx == MMU_USER_IDX; - pteaddr = (ptebase << page_bits) + (8 * level); - pte = ldq_raw(pteaddr); - /* Decode all interresting PTE fields */ - pfn = pte >> 32; - uwe = (pte >> 13) & 1; - kwe = (pte >> 12) & 1; - ure = (pte >> 9) & 1; - kre = (pte >> 8) & 1; - gh = (pte >> 5) & 3; - foE = (pte >> 3) & 1; - foW = (pte >> 2) & 1; - foR = (pte >> 1) & 1; - v = pte & 1; - ret = 0; - if (!v) - ret = 0x1; - /* Check access rights */ - ar = 0; - if (is_user) { - if (ure) - ar |= PAGE_READ; - if (uwe) - ar |= PAGE_WRITE; - if (rw == 1 && !uwe) - ret |= 0x2; - if (rw != 1 && !ure) - ret |= 0x2; - } else { - if (kre) - ar |= PAGE_READ; - if (kwe) - ar |= PAGE_WRITE; - if (rw == 1 && !kwe) - ret |= 0x2; - if (rw != 1 && !kre) - ret |= 0x2; - } - if (rw == 0 && foR) - ret |= 0x4; - if (rw == 2 && foE) - ret |= 0x8; - if (rw == 1 && foW) - ret |= 0xC; - *pfnp = pfn; - if (zbitsp != NULL) - *zbitsp = page_bits + (3 * gh); - if (protp != NULL) - *protp = ar; - - return ret; -} - -static int paddr_from_pte (uint64_t *paddr, int *zbitsp, int *prot, - uint64_t ptebase, int page_bits, - uint64_t vaddr, int mmu_idx, int rw) -{ - uint64_t pfn, page_mask, lvl_mask, level1, level2, level3; - int lvl_bits, ret; - - page_mask = (1ULL << page_bits) - 1ULL; - lvl_bits = page_bits - 3; - lvl_mask = (1ULL << lvl_bits) - 1ULL; - level3 = (vaddr >> page_bits) & lvl_mask; - level2 = (vaddr >> (page_bits + lvl_bits)) & lvl_mask; - level1 = (vaddr >> (page_bits + (2 * lvl_bits))) & lvl_mask; - /* Level 1 PTE */ - ret = get_pte(&pfn, NULL, NULL, ptebase, page_bits, level1, 0, 0); - switch (ret) { - case 3: - /* Access violation */ - return 2; - case 2: - /* translation not valid */ - return 1; - default: - /* OK */ - break; - } - /* Level 2 PTE */ - ret = get_pte(&pfn, NULL, NULL, pfn, page_bits, level2, 0, 0); - switch (ret) { - case 3: - /* Access violation */ - return 2; - case 2: - /* translation not valid */ - return 1; - default: - /* OK */ - break; - } - /* Level 3 PTE */ - ret = get_pte(&pfn, zbitsp, prot, pfn, page_bits, level3, mmu_idx, rw); - if (ret & 0x1) { - /* Translation not valid */ - ret = 1; - } else if (ret & 2) { - /* Access violation */ - ret = 2; - } else { - switch (ret & 0xC) { - case 0: - /* OK */ - ret = 0; - break; - case 0x4: - /* Fault on read */ - ret = 3; - break; - case 0x8: - /* Fault on execute */ - ret = 4; - break; - case 0xC: - /* Fault on write */ - ret = 5; - break; - } - } - *paddr = (pfn << page_bits) | (vaddr & page_mask); - - return 0; -} - -static int virtual_to_physical (CPUState *env, uint64_t *physp, - int *zbitsp, int *protp, - uint64_t virtual, int mmu_idx, int rw) -{ - uint64_t sva, ptebase; - int seg, page_bits, ret; - - sva = ((int64_t)(virtual << (64 - VA_BITS))) >> (64 - VA_BITS); - if (sva != virtual) - seg = -1; - else - seg = sva >> (VA_BITS - 2); - virtual &= ~(0xFFFFFC0000000000ULL << (VA_BITS - 43)); - ptebase = get_ptebase(env, virtual); - page_bits = get_page_bits(env); - ret = 0; - switch (seg) { - case 0: - /* seg1: 3 levels of PTE */ - ret = paddr_from_pte(physp, zbitsp, protp, ptebase, page_bits, - virtual, mmu_idx, rw); - break; - case 1: - /* seg1: 2 levels of PTE */ - ret = paddr_from_pte(physp, zbitsp, protp, ptebase, page_bits, - virtual, mmu_idx, rw); - break; - case 2: - /* kernel segment */ - if (mmu_idx != 0) { - ret = 2; - } else { - *physp = virtual; - } - break; - case 3: - /* seg1: TB mapped */ - ret = paddr_from_pte(physp, zbitsp, protp, ptebase, page_bits, - virtual, mmu_idx, rw); - break; - default: - ret = 1; - break; - } - - return ret; -} - -/* XXX: code provision */ -int cpu_ppc_handle_mmu_fault (CPUState *env, uint32_t address, int rw, - int mmu_idx, int is_softmmu) -{ - uint64_t physical, page_size, end; - int prot, zbits, ret; - - ret = virtual_to_physical(env, &physical, &zbits, &prot, - address, mmu_idx, rw); - - switch (ret) { - case 0: - /* No fault */ - page_size = 1ULL << zbits; - address &= ~(page_size - 1); - /* FIXME: page_size should probably be passed to tlb_set_page, - and this loop removed. */ - for (end = physical + page_size; physical < end; physical += 0x1000) { - tlb_set_page(env, address, physical, prot, mmu_idx, - TARGET_PAGE_SIZE); - address += 0x1000; - } - ret = 0; - break; -#if 0 - case 1: - env->exception_index = EXCP_DFAULT; - env->ipr[IPR_EXC_ADDR] = address; - ret = 1; - break; - case 2: - env->exception_index = EXCP_ACCESS_VIOLATION; - env->ipr[IPR_EXC_ADDR] = address; - ret = 1; - break; - case 3: - env->exception_index = EXCP_FAULT_ON_READ; - env->ipr[IPR_EXC_ADDR] = address; - ret = 1; - break; - case 4: - env->exception_index = EXCP_FAULT_ON_EXECUTE; - env->ipr[IPR_EXC_ADDR] = address; - ret = 1; - case 5: - env->exception_index = EXCP_FAULT_ON_WRITE; - env->ipr[IPR_EXC_ADDR] = address; - ret = 1; -#endif - default: - /* Should never happen */ - env->exception_index = EXCP_MCHK; - env->ipr[IPR_EXC_ADDR] = address; - ret = 1; - break; - } - - return ret; -} -#endif diff --git a/hw/bitbang_i2c.c b/hw/bitbang_i2c.c index 4ee99a18b9..2937b5c4a1 100644 --- a/hw/bitbang_i2c.c +++ b/hw/bitbang_i2c.c @@ -38,7 +38,8 @@ typedef enum bitbang_i2c_state { RECEIVING_BIT2, RECEIVING_BIT1, RECEIVING_BIT0, - SENDING_ACK + SENDING_ACK, + SENT_NACK } bitbang_i2c_state; struct bitbang_i2c_interface { @@ -115,6 +116,7 @@ int bitbang_i2c_set(bitbang_i2c_interface *i2c, int line, int level) } switch (i2c->state) { case STOPPED: + case SENT_NACK: return bitbang_i2c_ret(i2c, 1); case SENDING_BIT7 ... SENDING_BIT0: @@ -155,6 +157,7 @@ int bitbang_i2c_set(bitbang_i2c_interface *i2c, int line, int level) i2c->state = RECEIVING_BIT7; if (data != 0) { DPRINTF("NACKED\n"); + i2c->state = SENT_NACK; i2c_nack(i2c->bus); } else { DPRINTF("ACKED\n"); diff --git a/hw/boards.h b/hw/boards.h index 6f0f0d7925..716fd7b1a6 100644 --- a/hw/boards.h +++ b/hw/boards.h @@ -27,6 +27,7 @@ typedef struct QEMUMachine { no_cdrom:1, no_sdcard:1; int is_default; + const char *default_machine_opts; GlobalProperty *compat_props; struct QEMUMachine *next; } QEMUMachine; diff --git a/hw/bt-hid.c b/hw/bt-hid.c index abdfd35e86..09120af074 100644 --- a/hw/bt-hid.c +++ b/hw/bt-hid.c @@ -323,7 +323,7 @@ static void bt_hid_control_transaction(struct bt_hid_device_s *s, break; } s->proto = parameter; - s->usbdev->info->handle_control(s->usbdev, SET_PROTOCOL, s->proto, 0, 0, + s->usbdev->info->handle_control(s->usbdev, NULL, SET_PROTOCOL, s->proto, 0, 0, NULL); ret = BT_HS_SUCCESSFUL; break; @@ -333,7 +333,7 @@ static void bt_hid_control_transaction(struct bt_hid_device_s *s, ret = BT_HS_ERR_INVALID_PARAMETER; break; } - s->usbdev->info->handle_control(s->usbdev, GET_IDLE, 0, 0, 1, + s->usbdev->info->handle_control(s->usbdev, NULL, GET_IDLE, 0, 0, 1, s->control->sdu_out(s->control, 1)); s->control->sdu_submit(s->control); break; @@ -346,7 +346,7 @@ static void bt_hid_control_transaction(struct bt_hid_device_s *s, /* We don't need to know about the Idle Rate here really, * so just pass it on to the device. */ - ret = s->usbdev->info->handle_control(s->usbdev, + ret = s->usbdev->info->handle_control(s->usbdev, NULL, SET_IDLE, data[1], 0, 0, NULL) ? BT_HS_SUCCESSFUL : BT_HS_ERR_INVALID_PARAMETER; /* XXX: Does this generate a handshake? */ @@ -1441,7 +1441,7 @@ typedef struct { #define EVT_FLUSH_OCCURRED 0x11 typedef struct { uint16_t handle; -} __attribute__ ((packed)) evt_flush_occured; +} __attribute__ ((packed)) evt_flush_occurred; #define EVT_FLUSH_OCCURRED_SIZE 2 #define EVT_ROLE_CHANGE 0x12 diff --git a/hw/eepro100.c b/hw/eepro100.c index 84b98c0929..9b6f4a5cd8 100644 --- a/hw/eepro100.c +++ b/hw/eepro100.c @@ -1112,7 +1112,7 @@ static void eepro100_write_eeprom(eeprom_t * eeprom, uint8_t val) { TRACE(EEPROM, logout("val=0x%02x\n", val)); - /* mask unwriteable bits */ + /* mask unwritable bits */ #if 0 val = SET_MASKED(val, 0x31, eeprom->value); #endif diff --git a/hw/eeprom93xx.c b/hw/eeprom93xx.c index 660b28f225..7b21f98e22 100644 --- a/hw/eeprom93xx.c +++ b/hw/eeprom93xx.c @@ -75,7 +75,7 @@ struct _eeprom_t { uint8_t tick; uint8_t address; uint8_t command; - uint8_t writeable; + uint8_t writable; uint8_t eecs; uint8_t eesk; @@ -130,7 +130,7 @@ static const VMStateDescription vmstate_eeprom = { VMSTATE_UINT8(tick, eeprom_t), VMSTATE_UINT8(address, eeprom_t), VMSTATE_UINT8(command, eeprom_t), - VMSTATE_UINT8(writeable, eeprom_t), + VMSTATE_UINT8(writable, eeprom_t), VMSTATE_UINT8(eecs, eeprom_t), VMSTATE_UINT8(eesk, eeprom_t), @@ -165,7 +165,7 @@ void eeprom93xx_write(eeprom_t *eeprom, int eecs, int eesk, int eedi) address = 0x0; } else if (eeprom->eecs && ! eecs) { /* End chip select cycle. This triggers write / erase. */ - if (eeprom->writeable) { + if (eeprom->writable) { uint8_t subcommand = address >> (eeprom->addrbits - 2); if (command == 0 && subcommand == 2) { /* Erase all. */ @@ -232,7 +232,7 @@ void eeprom93xx_write(eeprom_t *eeprom, int eecs, int eesk, int eedi) switch (address >> (eeprom->addrbits - 2)) { case 0: logout("write disable command\n"); - eeprom->writeable = 0; + eeprom->writable = 0; break; case 1: logout("write all command\n"); @@ -242,7 +242,7 @@ void eeprom93xx_write(eeprom_t *eeprom, int eecs, int eesk, int eedi) break; case 3: logout("write enable command\n"); - eeprom->writeable = 1; + eeprom->writable = 1; break; } } else { @@ -61,10 +61,11 @@ struct ESPState { int32_t ti_size; uint32_t ti_rptr, ti_wptr; uint8_t ti_buf[TI_BUFSZ]; - uint32_t sense; + uint32_t status; uint32_t dma; SCSIBus bus; SCSIDevice *current_dev; + SCSIRequest *current_req; uint8_t cmdbuf[TI_BUFSZ]; uint32_t cmdlen; uint32_t do_cmd; @@ -187,6 +188,17 @@ static void esp_dma_enable(void *opaque, int irq, int level) } } +static void esp_request_cancelled(SCSIRequest *req) +{ + ESPState *s = DO_UPCAST(ESPState, busdev.qdev, req->bus->qbus.parent); + + if (req == s->current_req) { + scsi_req_unref(s->current_req); + s->current_req = NULL; + s->current_dev = NULL; + } +} + static uint32_t get_cmd(ESPState *s, uint8_t *buf) { uint32_t dmalen; @@ -209,7 +221,7 @@ static uint32_t get_cmd(ESPState *s, uint8_t *buf) if (s->current_dev) { /* Started a new command before the old one finished. Cancel it. */ - s->current_dev->info->cancel_io(s->current_dev, 0); + scsi_req_cancel(s->current_req); s->async_len = 0; } @@ -232,7 +244,8 @@ static void do_busid_cmd(ESPState *s, uint8_t *buf, uint8_t busid) DPRINTF("do_busid_cmd: busid 0x%x\n", busid); lun = busid & 7; - datalen = s->current_dev->info->send_command(s->current_dev, 0, buf, lun); + s->current_req = scsi_req_new(s->current_dev, 0, lun); + datalen = scsi_req_enqueue(s->current_req, buf); s->ti_size = datalen; if (datalen != 0) { s->rregs[ESP_RSTAT] = STAT_TC; @@ -240,11 +253,10 @@ static void do_busid_cmd(ESPState *s, uint8_t *buf, uint8_t busid) s->dma_counter = 0; if (datalen > 0) { s->rregs[ESP_RSTAT] |= STAT_DI; - s->current_dev->info->read_data(s->current_dev, 0); } else { s->rregs[ESP_RSTAT] |= STAT_DO; - s->current_dev->info->write_data(s->current_dev, 0); } + scsi_req_continue(s->current_req); } s->rregs[ESP_RINTR] = INTR_BS | INTR_FC; s->rregs[ESP_RSEQ] = SEQ_CD; @@ -306,8 +318,8 @@ static void handle_satn_stop(ESPState *s) static void write_response(ESPState *s) { - DPRINTF("Transfer status (sense=%d)\n", s->sense); - s->ti_buf[0] = s->sense; + DPRINTF("Transfer status (status=%d)\n", s->status); + s->ti_buf[0] = s->status; s->ti_buf[1] = 0; if (s->dma) { s->dma_memory_write(s->dma_opaque, s->ti_buf, 2); @@ -370,53 +382,56 @@ static void esp_do_dma(ESPState *s) else s->ti_size -= len; if (s->async_len == 0) { - if (to_device) { - // ti_size is negative - s->current_dev->info->write_data(s->current_dev, 0); - } else { - s->current_dev->info->read_data(s->current_dev, 0); - /* If there is still data to be read from the device then - complete the DMA operation immediately. Otherwise defer - until the scsi layer has completed. */ - if (s->dma_left == 0 && s->ti_size > 0) { - esp_dma_done(s); - } + scsi_req_continue(s->current_req); + /* If there is still data to be read from the device then + complete the DMA operation immediately. Otherwise defer + until the scsi layer has completed. */ + if (to_device || s->dma_left != 0 || s->ti_size == 0) { + return; } - } else { - /* Partially filled a scsi buffer. Complete immediately. */ - esp_dma_done(s); } + + /* Partially filled a scsi buffer. Complete immediately. */ + esp_dma_done(s); } -static void esp_command_complete(SCSIBus *bus, int reason, uint32_t tag, - uint32_t arg) +static void esp_command_complete(SCSIRequest *req, uint32_t status) { - ESPState *s = DO_UPCAST(ESPState, busdev.qdev, bus->qbus.parent); + ESPState *s = DO_UPCAST(ESPState, busdev.qdev, req->bus->qbus.parent); - if (reason == SCSI_REASON_DONE) { - DPRINTF("SCSI Command complete\n"); - if (s->ti_size != 0) - DPRINTF("SCSI command completed unexpectedly\n"); - s->ti_size = 0; - s->dma_left = 0; - s->async_len = 0; - if (arg) - DPRINTF("Command failed\n"); - s->sense = arg; - s->rregs[ESP_RSTAT] = STAT_ST; - esp_dma_done(s); + DPRINTF("SCSI Command complete\n"); + if (s->ti_size != 0) { + DPRINTF("SCSI command completed unexpectedly\n"); + } + s->ti_size = 0; + s->dma_left = 0; + s->async_len = 0; + if (status) { + DPRINTF("Command failed\n"); + } + s->status = status; + s->rregs[ESP_RSTAT] = STAT_ST; + esp_dma_done(s); + if (s->current_req) { + scsi_req_unref(s->current_req); + s->current_req = NULL; s->current_dev = NULL; - } else { - DPRINTF("transfer %d/%d\n", s->dma_left, s->ti_size); - s->async_len = arg; - s->async_buf = s->current_dev->info->get_buf(s->current_dev, 0); - if (s->dma_left) { - esp_do_dma(s); - } else if (s->dma_counter != 0 && s->ti_size <= 0) { - /* If this was the last part of a DMA transfer then the - completion interrupt is deferred to here. */ - esp_dma_done(s); - } + } +} + +static void esp_transfer_data(SCSIRequest *req, uint32_t len) +{ + ESPState *s = DO_UPCAST(ESPState, busdev.qdev, req->bus->qbus.parent); + + DPRINTF("transfer %d/%d\n", s->dma_left, s->ti_size); + s->async_len = len; + s->async_buf = scsi_req_get_buf(req); + if (s->dma_left) { + esp_do_dma(s); + } else if (s->dma_counter != 0 && s->ti_size <= 0) { + /* If this was the last part of a DMA transfer then the + completion interrupt is deferred to here. */ + esp_dma_done(s); } } @@ -678,7 +693,7 @@ static const VMStateDescription vmstate_esp = { VMSTATE_UINT32(ti_rptr, ESPState), VMSTATE_UINT32(ti_wptr, ESPState), VMSTATE_BUFFER(ti_buf, ESPState), - VMSTATE_UINT32(sense, ESPState), + VMSTATE_UINT32(status, ESPState), VMSTATE_UINT32(dma, ESPState), VMSTATE_BUFFER(cmdbuf, ESPState), VMSTATE_UINT32(cmdlen, ESPState), @@ -714,6 +729,12 @@ void esp_init(target_phys_addr_t espaddr, int it_shift, *dma_enable = qdev_get_gpio_in(dev, 1); } +static const struct SCSIBusOps esp_scsi_ops = { + .transfer_data = esp_transfer_data, + .complete = esp_command_complete, + .cancel = esp_request_cancelled +}; + static int esp_init1(SysBusDevice *dev) { ESPState *s = FROM_SYSBUS(ESPState, dev); @@ -728,7 +749,7 @@ static int esp_init1(SysBusDevice *dev) qdev_init_gpio_in(&dev->qdev, esp_gpio_demux, 2); - scsi_bus_new(&s->bus, &dev->qdev, 0, ESP_MAX_DEVS, esp_command_complete); + scsi_bus_new(&s->bus, &dev->qdev, 0, ESP_MAX_DEVS, &esp_scsi_ops); return scsi_bus_legacy_handle_cmdline(&s->bus); } diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index c6e0c7767e..1f008a3dda 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -884,8 +884,31 @@ static int handle_cmd(AHCIState *s, int port, int slot) } if (ide_state->drive_kind != IDE_CD) { - ide_set_sector(ide_state, (cmd_fis[6] << 16) | (cmd_fis[5] << 8) | - cmd_fis[4]); + /* + * We set the sector depending on the sector defined in the FIS. + * Unfortunately, the spec isn't exactly obvious on this one. + * + * Apparently LBA48 commands set fis bytes 10,9,8,6,5,4 to the + * 48 bit sector number. ATA_CMD_READ_DMA_EXT is an example for + * such a command. + * + * Non-LBA48 commands however use 7[lower 4 bits],6,5,4 to define a + * 28-bit sector number. ATA_CMD_READ_DMA is an example for such + * a command. + * + * Since the spec doesn't explicitly state what each field should + * do, I simply assume non-used fields as reserved and OR everything + * together, independent of the command. + */ + ide_set_sector(ide_state, ((uint64_t)cmd_fis[10] << 40) + | ((uint64_t)cmd_fis[9] << 32) + /* This is used for LBA48 commands */ + | ((uint64_t)cmd_fis[8] << 24) + /* This is used for non-LBA48 commands */ + | ((uint64_t)(cmd_fis[7] & 0xf) << 24) + | ((uint64_t)cmd_fis[6] << 16) + | ((uint64_t)cmd_fis[5] << 8) + | cmd_fis[4]); } /* Copy the ACMD field (ATAPI packet, if any) from the AHCI command @@ -1066,9 +1089,11 @@ static int ahci_dma_set_inactive(IDEDMA *dma) ad->dma_cb = NULL; - /* maybe we still have something to process, check later */ - ad->check_bh = qemu_bh_new(ahci_check_cmd_bh, ad); - qemu_bh_schedule(ad->check_bh); + if (!ad->check_bh) { + /* maybe we still have something to process, check later */ + ad->check_bh = qemu_bh_new(ahci_check_cmd_bh, ad); + qemu_bh_schedule(ad->check_bh); + } return 0; } diff --git a/hw/ide/core.c b/hw/ide/core.c index 90f553b69b..95beb175b3 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -430,7 +430,6 @@ void ide_dma_error(IDEState *s) s->error = ABRT_ERR; s->status = READY_STAT | ERR_STAT; ide_set_inactive(s); - s->bus->dma->ops->add_status(s->bus->dma, BM_STATUS_INT); ide_set_irq(s->bus); } @@ -500,8 +499,11 @@ handle_rw_error: n = s->nsector; s->io_buffer_index = 0; s->io_buffer_size = n * 512; - if (s->bus->dma->ops->prepare_buf(s->bus->dma, s->is_read) == 0) + if (s->bus->dma->ops->prepare_buf(s->bus->dma, s->is_read) == 0) { + /* The PRDs were too short. Reset the Active bit, but don't raise an + * interrupt. */ goto eot; + } #ifdef DEBUG_AIO printf("ide_dma_cb: sector_num=%" PRId64 " n=%d, is_read=%d\n", @@ -523,7 +525,6 @@ handle_rw_error: return; eot: - s->bus->dma->ops->add_status(s->bus->dma, BM_STATUS_INT); ide_set_inactive(s); } @@ -1592,13 +1593,15 @@ void ide_bus_reset(IDEBus *bus) bus->dma->ops->reset(bus->dma); } -int ide_init_drive(IDEState *s, BlockDriverState *bs, +int ide_init_drive(IDEState *s, BlockDriverState *bs, IDEDriveKind kind, const char *version, const char *serial) { int cylinders, heads, secs; uint64_t nb_sectors; s->bs = bs; + s->drive_kind = kind; + bdrv_get_geometry(bs, &nb_sectors); bdrv_guess_geometry(bs, &cylinders, &heads, &secs); if (cylinders < 1 || cylinders > 16383) { @@ -1623,8 +1626,7 @@ int ide_init_drive(IDEState *s, BlockDriverState *bs, s->smart_autosave = 1; s->smart_errors = 0; s->smart_selftest_count = 0; - if (bdrv_get_type_hint(bs) == BDRV_TYPE_CDROM) { - s->drive_kind = IDE_CD; + if (kind == IDE_CD) { bdrv_set_change_cb(bs, cdrom_change_cb, s); bs->buffer_alignment = 2048; } else { @@ -1729,7 +1731,8 @@ void ide_init2_with_non_qdev_drives(IDEBus *bus, DriveInfo *hd0, dinfo = i == 0 ? hd0 : hd1; ide_init1(bus, i); if (dinfo) { - if (ide_init_drive(&bus->ifs[i], dinfo->bdrv, NULL, + if (ide_init_drive(&bus->ifs[i], dinfo->bdrv, + dinfo->media_cd ? IDE_CD : IDE_HD, NULL, *dinfo->serial ? dinfo->serial : NULL) < 0) { error_report("Can't set up IDE drive %s", dinfo->id); exit(1); diff --git a/hw/ide/ich.c b/hw/ide/ich.c index 976cc9244b..054e0734e4 100644 --- a/hw/ide/ich.c +++ b/hw/ide/ich.c @@ -90,12 +90,12 @@ static int pci_ich9_ahci_init(PCIDevice *dev) qemu_register_reset(ahci_reset, d); - /* XXX BAR size should be 1k, but that breaks, so bump it to 4k for now */ - pci_register_bar_simple(&d->card, 5, 0x1000, 0, d->ahci.mem); - msi_init(dev, 0x50, 1, true, false); d->ahci.irq = d->card.irq[0]; + /* XXX BAR size should be 1k, but that breaks, so bump it to 4k for now */ + pci_register_bar_simple(&d->card, 5, 0x1000, 0, d->ahci.mem); + return 0; } diff --git a/hw/ide/internal.h b/hw/ide/internal.h index aa198b6b12..c2b35ec5e6 100644 --- a/hw/ide/internal.h +++ b/hw/ide/internal.h @@ -558,7 +558,7 @@ uint32_t ide_data_readw(void *opaque, uint32_t addr); void ide_data_writel(void *opaque, uint32_t addr, uint32_t val); uint32_t ide_data_readl(void *opaque, uint32_t addr); -int ide_init_drive(IDEState *s, BlockDriverState *bs, +int ide_init_drive(IDEState *s, BlockDriverState *bs, IDEDriveKind kind, const char *version, const char *serial); void ide_init2(IDEBus *bus, qemu_irq irq); void ide_init2_with_non_qdev_drives(IDEBus *bus, DriveInfo *hd0, diff --git a/hw/ide/pci.c b/hw/ide/pci.c index 65cb56c38c..a4726adbea 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -296,12 +296,8 @@ void bmdma_cmd_writeb(void *opaque, uint32_t addr, uint32_t val) */ if (bm->bus->dma->aiocb) { qemu_aio_flush(); -#ifdef DEBUG_IDE - if (bm->bus->dma->aiocb) - printf("ide_dma_cancel: aiocb still pending"); - if (bm->status & BM_STATUS_DMAING) - printf("ide_dma_cancel: BM_STATUS_DMAING still pending"); -#endif + assert(bm->bus->dma->aiocb == NULL); + assert((bm->status & BM_STATUS_DMAING) == 0); } } else { bm->cur_addr = bm->addr; diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c index 2bb5c27341..3f9dc89c6d 100644 --- a/hw/ide/qdev.c +++ b/hw/ide/qdev.c @@ -98,7 +98,7 @@ IDEDevice *ide_create_drive(IDEBus *bus, int unit, DriveInfo *drive) { DeviceState *dev; - dev = qdev_create(&bus->qbus, "ide-drive"); + dev = qdev_create(&bus->qbus, drive->media_cd ? "ide-cd" : "ide-hd"); qdev_prop_set_uint32(dev, "unit", unit); qdev_prop_set_drive_nofail(dev, "drive", drive->bdrv); qdev_init_nofail(dev); @@ -118,7 +118,7 @@ typedef struct IDEDrive { IDEDevice dev; } IDEDrive; -static int ide_drive_initfn(IDEDevice *dev) +static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind) { IDEBus *bus = DO_UPCAST(IDEBus, qbus, dev->qdev.parent_bus); IDEState *s = bus->ifs + dev->unit; @@ -134,7 +134,7 @@ static int ide_drive_initfn(IDEDevice *dev) } } - if (ide_init_drive(s, dev->conf.bs, dev->version, serial) < 0) { + if (ide_init_drive(s, dev->conf.bs, kind, dev->version, serial) < 0) { return -1; } @@ -151,22 +151,69 @@ static int ide_drive_initfn(IDEDevice *dev) return 0; } -static IDEDeviceInfo ide_drive_info = { - .qdev.name = "ide-drive", - .qdev.fw_name = "drive", - .qdev.size = sizeof(IDEDrive), - .init = ide_drive_initfn, - .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("unit", IDEDrive, dev.unit, -1), - DEFINE_BLOCK_PROPERTIES(IDEDrive, dev.conf), - DEFINE_PROP_STRING("ver", IDEDrive, dev.version), - DEFINE_PROP_STRING("serial", IDEDrive, dev.serial), - DEFINE_PROP_END_OF_LIST(), +static int ide_hd_initfn(IDEDevice *dev) +{ + return ide_dev_initfn(dev, IDE_HD); +} + +static int ide_cd_initfn(IDEDevice *dev) +{ + return ide_dev_initfn(dev, IDE_CD); +} + +static int ide_drive_initfn(IDEDevice *dev) +{ + DriveInfo *dinfo = drive_get_by_blockdev(dev->conf.bs); + + return ide_dev_initfn(dev, dinfo->media_cd ? IDE_CD : IDE_HD); +} + +#define DEFINE_IDE_DEV_PROPERTIES() \ + DEFINE_PROP_UINT32("unit", IDEDrive, dev.unit, -1), \ + DEFINE_BLOCK_PROPERTIES(IDEDrive, dev.conf), \ + DEFINE_PROP_STRING("ver", IDEDrive, dev.version), \ + DEFINE_PROP_STRING("serial", IDEDrive, dev.serial) + +static IDEDeviceInfo ide_dev_info[] = { + { + .qdev.name = "ide-hd", + .qdev.fw_name = "drive", + .qdev.desc = "virtual IDE disk", + .qdev.size = sizeof(IDEDrive), + .init = ide_hd_initfn, + .qdev.props = (Property[]) { + DEFINE_IDE_DEV_PROPERTIES(), + DEFINE_PROP_END_OF_LIST(), + } + },{ + .qdev.name = "ide-cd", + .qdev.fw_name = "drive", + .qdev.desc = "virtual IDE CD-ROM", + .qdev.size = sizeof(IDEDrive), + .init = ide_cd_initfn, + .qdev.props = (Property[]) { + DEFINE_IDE_DEV_PROPERTIES(), + DEFINE_PROP_END_OF_LIST(), + } + },{ + .qdev.name = "ide-drive", /* legacy -device ide-drive */ + .qdev.fw_name = "drive", + .qdev.desc = "virtual IDE disk or CD-ROM (legacy)", + .qdev.size = sizeof(IDEDrive), + .init = ide_drive_initfn, + .qdev.props = (Property[]) { + DEFINE_IDE_DEV_PROPERTIES(), + DEFINE_PROP_END_OF_LIST(), + } } }; -static void ide_drive_register(void) +static void ide_dev_register(void) { - ide_qdev_register(&ide_drive_info); + int i; + + for (i = 0; i < ARRAY_SIZE(ide_dev_info); i++) { + ide_qdev_register(&ide_dev_info[i]); + } } -device_init(ide_drive_register); +device_init(ide_dev_register); diff --git a/hw/lan9118.c b/hw/lan9118.c index 2dc8d18549..4c42fe94c2 100644 --- a/hw/lan9118.c +++ b/hw/lan9118.c @@ -721,7 +721,7 @@ static void do_phy_write(lan9118_state *s, int reg, uint32_t val) break; } s->phy_control = val & 0x7980; - /* Complete autonegotiation imediately. */ + /* Complete autonegotiation immediately. */ if (val & 0x1000) { s->phy_status |= 0x0020; } diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c index 6b9c904ab6..3b75467da4 100644 --- a/hw/lsi53c895a.c +++ b/hw/lsi53c895a.c @@ -174,6 +174,7 @@ do { fprintf(stderr, "lsi_scsi: error: " fmt , ## __VA_ARGS__);} while (0) #define LSI_TAG_VALID (1 << 16) typedef struct lsi_request { + SCSIRequest *req; uint32_t tag; uint32_t dma_len; uint8_t *dma_buf; @@ -189,7 +190,7 @@ typedef struct { uint32_t script_ram_base; int carry; /* ??? Should this be an a visible register somewhere? */ - int sense; + int status; /* Action to take at the end of a MSG IN phase. 0 = COMMAND, 1 = disconnect, 2 = DATA OUT, 3 = DATA IN. */ int msg_action; @@ -567,11 +568,9 @@ static void lsi_do_dma(LSIState *s, int out) s->csbc += count; s->dnad += count; s->dbc -= count; - - if (s->current->dma_buf == NULL) { - s->current->dma_buf = dev->info->get_buf(dev, s->current->tag); + if (s->current->dma_buf == NULL) { + s->current->dma_buf = scsi_req_get_buf(s->current->req); } - /* ??? Set SFBR to first data byte. */ if (out) { cpu_physical_memory_read(addr, s->current->dma_buf, count); @@ -581,13 +580,7 @@ static void lsi_do_dma(LSIState *s, int out) s->current->dma_len -= count; if (s->current->dma_len == 0) { s->current->dma_buf = NULL; - if (out) { - /* Write the data. */ - dev->info->write_data(dev, s->current->tag); - } else { - /* Request any remaining data. */ - dev->info->read_data(dev, s->current->tag); - } + scsi_req_continue(s->current->req); } else { s->current->dma_buf += count; lsi_resume_script(s); @@ -652,82 +645,123 @@ static void lsi_reselect(LSIState *s, lsi_request *p) } } -/* Record that data is available for a queued command. Returns zero if - the device was reselected, nonzero if the IO is deferred. */ -static int lsi_queue_tag(LSIState *s, uint32_t tag, uint32_t arg) +static lsi_request *lsi_find_by_tag(LSIState *s, uint32_t tag) { lsi_request *p; QTAILQ_FOREACH(p, &s->queue, next) { if (p->tag == tag) { - if (p->pending) { - BADF("Multiple IO pending for tag %d\n", tag); - } - p->pending = arg; - /* Reselect if waiting for it, or if reselection triggers an IRQ - and the bus is free. - Since no interrupt stacking is implemented in the emulation, it - is also required that there are no pending interrupts waiting - for service from the device driver. */ - if (s->waiting == 1 || - (lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON) && - !(s->istat0 & (LSI_ISTAT0_SIP | LSI_ISTAT0_DIP)))) { - /* Reselect device. */ - lsi_reselect(s, p); - return 0; - } else { - DPRINTF("Queueing IO tag=0x%x\n", tag); - p->pending = arg; - return 1; - } + return p; } } - BADF("IO with unknown tag %d\n", tag); - return 1; + + return NULL; +} + +static void lsi_request_cancelled(SCSIRequest *req) +{ + LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent); + lsi_request *p; + + if (s->current && req == s->current->req) { + scsi_req_unref(req); + qemu_free(s->current); + s->current = NULL; + return; + } + + p = lsi_find_by_tag(s, req->tag); + if (p) { + QTAILQ_REMOVE(&s->queue, p, next); + scsi_req_unref(req); + qemu_free(p); + } } -/* Callback to indicate that the SCSI layer has completed a transfer. */ -static void lsi_command_complete(SCSIBus *bus, int reason, uint32_t tag, - uint32_t arg) +/* Record that data is available for a queued command. Returns zero if + the device was reselected, nonzero if the IO is deferred. */ +static int lsi_queue_tag(LSIState *s, uint32_t tag, uint32_t len) { - LSIState *s = DO_UPCAST(LSIState, dev.qdev, bus->qbus.parent); + lsi_request *p; + + p = lsi_find_by_tag(s, tag); + if (!p) { + BADF("IO with unknown tag %d\n", tag); + return 1; + } + + if (p->pending) { + BADF("Multiple IO pending for tag %d\n", tag); + } + p->pending = len; + /* Reselect if waiting for it, or if reselection triggers an IRQ + and the bus is free. + Since no interrupt stacking is implemented in the emulation, it + is also required that there are no pending interrupts waiting + for service from the device driver. */ + if (s->waiting == 1 || + (lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON) && + !(s->istat0 & (LSI_ISTAT0_SIP | LSI_ISTAT0_DIP)))) { + /* Reselect device. */ + lsi_reselect(s, p); + return 0; + } else { + DPRINTF("Queueing IO tag=0x%x\n", tag); + p->pending = len; + return 1; + } +} + + /* Callback to indicate that the SCSI layer has completed a command. */ +static void lsi_command_complete(SCSIRequest *req, uint32_t status) +{ + LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent); int out; out = (s->sstat1 & PHASE_MASK) == PHASE_DO; - if (reason == SCSI_REASON_DONE) { - DPRINTF("Command complete sense=%d\n", (int)arg); - s->sense = arg; - s->command_complete = 2; - if (s->waiting && s->dbc != 0) { - /* Raise phase mismatch for short transfers. */ - lsi_bad_phase(s, out, PHASE_ST); - } else { - lsi_set_phase(s, PHASE_ST); - } + DPRINTF("Command complete status=%d\n", (int)status); + s->status = status; + s->command_complete = 2; + if (s->waiting && s->dbc != 0) { + /* Raise phase mismatch for short transfers. */ + lsi_bad_phase(s, out, PHASE_ST); + } else { + lsi_set_phase(s, PHASE_ST); + } + if (s->current && req == s->current->req) { + scsi_req_unref(s->current->req); qemu_free(s->current); s->current = NULL; - - lsi_resume_script(s); - return; } + lsi_resume_script(s); +} + + /* Callback to indicate that the SCSI layer has completed a transfer. */ +static void lsi_transfer_data(SCSIRequest *req, uint32_t len) +{ + LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent); + int out; - if (s->waiting == 1 || !s->current || tag != s->current->tag || + if (s->waiting == 1 || !s->current || req->tag != s->current->tag || (lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON))) { - if (lsi_queue_tag(s, tag, arg)) + if (lsi_queue_tag(s, req->tag, len)) { return; + } } + out = (s->sstat1 & PHASE_MASK) == PHASE_DO; + /* host adapter (re)connected */ - DPRINTF("Data ready tag=0x%x len=%d\n", tag, arg); - s->current->dma_len = arg; + DPRINTF("Data ready tag=0x%x len=%d\n", req->tag, len); + s->current->dma_len = len; s->command_complete = 1; - if (!s->waiting) - return; - if (s->waiting == 1 || s->dbc == 0) { - lsi_resume_script(s); - } else { - lsi_do_dma(s, out); + if (s->waiting) { + if (s->waiting == 1 || s->dbc == 0) { + lsi_resume_script(s); + } else { + lsi_do_dma(s, out); + } } } @@ -755,16 +789,17 @@ static void lsi_do_command(LSIState *s) assert(s->current == NULL); s->current = qemu_mallocz(sizeof(lsi_request)); s->current->tag = s->select_tag; + s->current->req = scsi_req_new(dev, s->current->tag, s->current_lun); - n = dev->info->send_command(dev, s->current->tag, buf, s->current_lun); - if (n > 0) { - lsi_set_phase(s, PHASE_DI); - dev->info->read_data(dev, s->current->tag); - } else if (n < 0) { - lsi_set_phase(s, PHASE_DO); - dev->info->write_data(dev, s->current->tag); + n = scsi_req_enqueue(s->current->req, buf); + if (n) { + if (n > 0) { + lsi_set_phase(s, PHASE_DI); + } else if (n < 0) { + lsi_set_phase(s, PHASE_DO); + } + scsi_req_continue(s->current->req); } - if (!s->command_complete) { if (n) { /* Command did not complete immediately so disconnect. */ @@ -783,14 +818,14 @@ static void lsi_do_command(LSIState *s) static void lsi_do_status(LSIState *s) { - uint8_t sense; - DPRINTF("Get status len=%d sense=%d\n", s->dbc, s->sense); + uint8_t status; + DPRINTF("Get status len=%d status=%d\n", s->dbc, s->status); if (s->dbc != 1) BADF("Bad Status move\n"); s->dbc = 1; - sense = s->sense; - s->sfbr = sense; - cpu_physical_memory_write(s->dnad, &sense, 1); + status = s->status; + s->sfbr = status; + cpu_physical_memory_write(s->dnad, &status, 1); lsi_set_phase(s, PHASE_MI); s->msg_action = 1; lsi_add_msg_byte(s, 0); /* COMMAND COMPLETE */ @@ -855,13 +890,15 @@ static void lsi_do_msgout(LSIState *s) int len; uint32_t current_tag; SCSIDevice *current_dev; - lsi_request *p, *p_next; + lsi_request *current_req, *p, *p_next; int id; if (s->current) { current_tag = s->current->tag; + current_req = s->current; } else { current_tag = s->select_tag; + current_req = lsi_find_by_tag(s, current_tag); } id = (current_tag >> 8) & 0xf; current_dev = s->bus.devs[id]; @@ -913,7 +950,9 @@ static void lsi_do_msgout(LSIState *s) case 0x0d: /* The ABORT TAG message clears the current I/O process only. */ DPRINTF("MSG: ABORT TAG tag=0x%x\n", current_tag); - current_dev->info->cancel_io(current_dev, current_tag); + if (current_req) { + scsi_req_cancel(current_req->req); + } lsi_disconnect(s); break; case 0x06: @@ -936,7 +975,9 @@ static void lsi_do_msgout(LSIState *s) } /* clear the current I/O process */ - current_dev->info->cancel_io(current_dev, current_tag); + if (s->current) { + scsi_req_cancel(s->current->req); + } /* As the current implemented devices scsi_disk and scsi_generic only support one LUN, we don't need to keep track of LUNs. @@ -948,8 +989,7 @@ static void lsi_do_msgout(LSIState *s) id = current_tag & 0x0000ff00; QTAILQ_FOREACH_SAFE(p, &s->queue, next, p_next) { if ((p->tag & 0x0000ff00) == id) { - current_dev->info->cancel_io(current_dev, p->tag); - QTAILQ_REMOVE(&s->queue, p, next); + scsi_req_cancel(p->req); } } @@ -2122,7 +2162,7 @@ static const VMStateDescription vmstate_lsi_scsi = { VMSTATE_PCI_DEVICE(dev, LSIState), VMSTATE_INT32(carry, LSIState), - VMSTATE_INT32(sense, LSIState), + VMSTATE_INT32(status, LSIState), VMSTATE_INT32(msg_action, LSIState), VMSTATE_INT32(msg_len, LSIState), VMSTATE_BUFFER(msg, LSIState), @@ -2205,6 +2245,12 @@ static int lsi_scsi_uninit(PCIDevice *d) return 0; } +static const struct SCSIBusOps lsi_scsi_ops = { + .transfer_data = lsi_transfer_data, + .complete = lsi_command_complete, + .cancel = lsi_request_cancelled +}; + static int lsi_scsi_init(PCIDevice *dev) { LSIState *s = DO_UPCAST(LSIState, dev, dev); @@ -2232,7 +2278,7 @@ static int lsi_scsi_init(PCIDevice *dev) PCI_BASE_ADDRESS_SPACE_MEMORY, lsi_ram_mapfunc); QTAILQ_INIT(&s->queue); - scsi_bus_new(&s->bus, &dev->qdev, 1, LSI_MAX_DEVS, lsi_command_complete); + scsi_bus_new(&s->bus, &dev->qdev, 1, LSI_MAX_DEVS, &lsi_scsi_ops); if (!dev->qdev.hotplugged) { return scsi_bus_legacy_handle_cmdline(&s->bus); } @@ -155,7 +155,7 @@ int msi_init(struct PCIDevice *dev, uint8_t offset, pci_set_word(dev->wmask + msi_data_off(dev, msi64bit), 0xffff); if (msi_per_vector_mask) { - /* Make mask bits 0 to nr_vectors - 1 writeable. */ + /* Make mask bits 0 to nr_vectors - 1 writable. */ pci_set_long(dev->wmask + msi_mask_off(dev, msi64bit), 0xffffffff >> (PCI_MSI_VECTORS_MAX - nr_vectors)); } @@ -76,7 +76,7 @@ static int msix_add_config(struct PCIDevice *pdev, unsigned short nentries, pci_set_long(config + PCI_MSIX_PBA, (bar_size + MSIX_PAGE_PENDING) | bar_nr); pdev->msix_cap = config_offset; - /* Make flags bit writeable. */ + /* Make flags bit writable. */ pdev->wmask[config_offset + MSIX_CONTROL_OFFSET] |= MSIX_ENABLE_MASK | MSIX_MASKALL_MASK; return 0; diff --git a/hw/mst_fpga.c b/hw/mst_fpga.c index a04355cc7f..4e47574b63 100644 --- a/hw/mst_fpga.c +++ b/hw/mst_fpga.c @@ -154,7 +154,7 @@ mst_fpga_writeb(void *opaque, target_phys_addr_t addr, uint32_t value) case MST_MSCRD: s->mscrd = value; break; - case MST_INTMSKENA: /* Mask interupt */ + case MST_INTMSKENA: /* Mask interrupt */ s->intmskena = (value & 0xFEEFF); qemu_set_irq(s->parent, s->intsetclr & s->intmskena); break; diff --git a/hw/multiboot.c b/hw/multiboot.c index 394ed0136e..6e6cfb9531 100644 --- a/hw/multiboot.c +++ b/hw/multiboot.c @@ -307,7 +307,7 @@ int load_multiboot(void *fw_cfg, | MULTIBOOT_FLAGS_MMAP); stl_p(bootinfo + MBI_MEM_LOWER, 640); stl_p(bootinfo + MBI_MEM_UPPER, (ram_size / 1024) - 1024); - stl_p(bootinfo + MBI_BOOT_DEVICE, 0x8001ffff); /* XXX: use the -boot switch? */ + stl_p(bootinfo + MBI_BOOT_DEVICE, 0x8000ffff); /* XXX: use the -boot switch? */ stl_p(bootinfo + MBI_MMAP_ADDR, ADDR_E820_MAP); mb_debug("multiboot: mh_entry_addr = %#x\n", mh_entry_addr); @@ -957,29 +957,18 @@ void pc_cpus_init(const char *cpu_model) } } -void pc_memory_init(ram_addr_t ram_size, - const char *kernel_filename, +void pc_memory_init(const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, - ram_addr_t *below_4g_mem_size_p, - ram_addr_t *above_4g_mem_size_p) + ram_addr_t below_4g_mem_size, + ram_addr_t above_4g_mem_size) { char *filename; int ret, linux_boot, i; ram_addr_t ram_addr, bios_offset, option_rom_offset; - ram_addr_t below_4g_mem_size, above_4g_mem_size = 0; int bios_size, isa_bios_size; void *fw_cfg; - if (ram_size >= 0xe0000000 ) { - above_4g_mem_size = ram_size - 0xe0000000; - below_4g_mem_size = 0xe0000000; - } else { - below_4g_mem_size = ram_size; - } - *above_4g_mem_size_p = above_4g_mem_size; - *below_4g_mem_size_p = below_4g_mem_size; - linux_boot = (kernel_filename != NULL); /* allocate RAM */ @@ -1081,6 +1070,15 @@ void pc_vga_init(PCIBus *pci_bus) isa_vga_init(); } } + + /* + * sga does not suppress normal vga output. So a machine can have both a + * vga card and sga manually enabled. Output will be seen on both. + * For nographic case, sga is enabled at all times + */ + if (display_type == DT_NOGRAPHIC) { + isa_create_simple("sga"); + } } static void cpu_request_exit(void *opaque, int irq, int level) @@ -1093,7 +1091,8 @@ static void cpu_request_exit(void *opaque, int irq, int level) } void pc_basic_device_init(qemu_irq *isa_irq, - ISADevice **rtc_state) + ISADevice **rtc_state, + bool no_vmport) { int i; DriveInfo *fd[MAX_FD]; @@ -1138,8 +1137,12 @@ void pc_basic_device_init(qemu_irq *isa_irq, a20_line = qemu_allocate_irqs(handle_a20_line_change, first_cpu, 2); i8042 = isa_create_simple("i8042"); i8042_setup_a20_line(i8042, &a20_line[0]); - vmport_init(); - vmmouse = isa_try_create("vmmouse"); + if (!no_vmport) { + vmport_init(); + vmmouse = isa_try_create("vmmouse"); + } else { + vmmouse = NULL; + } if (vmmouse) { qdev_prop_set_ptr(&vmmouse->qdev, "ps2_mouse", i8042); qdev_init_nofail(&vmmouse->qdev); @@ -129,16 +129,16 @@ void pc_cmos_set_s3_resume(void *opaque, int irq, int level); void pc_acpi_smi_interrupt(void *opaque, int irq, int level); void pc_cpus_init(const char *cpu_model); -void pc_memory_init(ram_addr_t ram_size, - const char *kernel_filename, +void pc_memory_init(const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, - ram_addr_t *below_4g_mem_size_p, - ram_addr_t *above_4g_mem_size_p); + ram_addr_t below_4g_mem_size, + ram_addr_t above_4g_mem_size); qemu_irq *pc_allocate_cpu_irq(void); void pc_vga_init(PCIBus *pci_bus); void pc_basic_device_init(qemu_irq *isa_irq, - ISADevice **rtc_state); + ISADevice **rtc_state, + bool no_vmport); void pc_init_ne2k_isa(NICInfo *nd); void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size, const char *boot_device, @@ -176,6 +176,7 @@ struct PCII440FXState; typedef struct PCII440FXState PCII440FXState; PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix_devfn, qemu_irq *pic, ram_addr_t ram_size); +PCIBus *i440fx_xen_init(PCII440FXState **pi440fx_state, int *piix3_devfn, qemu_irq *pic, ram_addr_t ram_size); void i440fx_init_memory_mappings(PCII440FXState *d); /* piix4.c */ diff --git a/hw/pc_piix.c b/hw/pc_piix.c index 1aba09f8f6..90861257d8 100644 --- a/hw/pc_piix.c +++ b/hw/pc_piix.c @@ -38,6 +38,10 @@ #include "arch_init.h" #include "blockdev.h" #include "smbus.h" +#include "xen.h" +#ifdef CONFIG_XEN +# include <xen/hvm/hvm_info_table.h> +#endif #define MAX_IDE_BUS 2 @@ -92,12 +96,26 @@ static void pc_init1(ram_addr_t ram_size, kvmclock_create(); } + if (ram_size >= 0xe0000000 ) { + above_4g_mem_size = ram_size - 0xe0000000; + below_4g_mem_size = 0xe0000000; + } else { + above_4g_mem_size = 0; + below_4g_mem_size = ram_size; + } + /* allocate ram and load rom/bios */ - pc_memory_init(ram_size, kernel_filename, kernel_cmdline, initrd_filename, - &below_4g_mem_size, &above_4g_mem_size); + if (!xen_enabled()) { + pc_memory_init(kernel_filename, kernel_cmdline, initrd_filename, + below_4g_mem_size, above_4g_mem_size); + } - cpu_irq = pc_allocate_cpu_irq(); - i8259 = i8259_init(cpu_irq[0]); + if (!xen_enabled()) { + cpu_irq = pc_allocate_cpu_irq(); + i8259 = i8259_init(cpu_irq[0]); + } else { + i8259 = xen_interrupt_controller_init(); + } isa_irq_state = qemu_mallocz(sizeof(*isa_irq_state)); isa_irq_state->i8259 = i8259; if (pci_enabled) { @@ -106,7 +124,11 @@ static void pc_init1(ram_addr_t ram_size, isa_irq = qemu_allocate_irqs(isa_irq_handler, isa_irq_state, 24); if (pci_enabled) { - pci_bus = i440fx_init(&i440fx_state, &piix3_devfn, isa_irq, ram_size); + if (!xen_enabled()) { + pci_bus = i440fx_init(&i440fx_state, &piix3_devfn, isa_irq, ram_size); + } else { + pci_bus = i440fx_xen_init(&i440fx_state, &piix3_devfn, isa_irq, ram_size); + } } else { pci_bus = NULL; i440fx_state = NULL; @@ -119,7 +141,7 @@ static void pc_init1(ram_addr_t ram_size, pc_vga_init(pci_enabled? pci_bus: NULL); /* init basic PC hardware */ - pc_basic_device_init(isa_irq, &rtc_state); + pc_basic_device_init(isa_irq, &rtc_state, xen_enabled()); for(i = 0; i < nb_nics; i++) { NICInfo *nd = &nd_table[i]; @@ -157,7 +179,11 @@ static void pc_init1(ram_addr_t ram_size, if (pci_enabled && acpi_enabled) { i2c_bus *smbus; - cmos_s3 = qemu_allocate_irqs(pc_cmos_set_s3_resume, rtc_state, 1); + if (!xen_enabled()) { + cmos_s3 = qemu_allocate_irqs(pc_cmos_set_s3_resume, rtc_state, 1); + } else { + cmos_s3 = qemu_allocate_irqs(xen_cmos_set_s3_resume, rtc_state, 1); + } smi_irq = qemu_allocate_irqs(pc_acpi_smi_interrupt, first_cpu, 1); /* TODO: Populate SPD eeprom data. */ smbus = piix4_pm_init(pci_bus, piix3_devfn + 3, 0xb100, @@ -213,6 +239,24 @@ static void pc_init_isa(ram_addr_t ram_size, initrd_filename, cpu_model, 0, 1); } +#ifdef CONFIG_XEN +static void pc_xen_hvm_init(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model) +{ + if (xen_hvm_init() != 0) { + hw_error("xen hardware virtual machine initialisation failed"); + } + pc_init_pci_no_kvmclock(ram_size, boot_device, + kernel_filename, kernel_cmdline, + initrd_filename, cpu_model); + xen_vcpu_init(); +} +#endif + static QEMUMachine pc_machine = { .name = "pc-0.14", .alias = "pc", @@ -425,6 +469,16 @@ static QEMUMachine isapc_machine = { .max_cpus = 1, }; +#ifdef CONFIG_XEN +static QEMUMachine xenfv_machine = { + .name = "xenfv", + .desc = "Xen Fully-virtualized PC", + .init = pc_xen_hvm_init, + .max_cpus = HVM_MAX_VCPUS, + .default_machine_opts = "accel=xen", +}; +#endif + static void pc_machine_init(void) { qemu_register_machine(&pc_machine); @@ -433,6 +487,9 @@ static void pc_machine_init(void) qemu_register_machine(&pc_machine_v0_11); qemu_register_machine(&pc_machine_v0_10); qemu_register_machine(&isapc_machine); +#ifdef CONFIG_XEN + qemu_register_machine(&xenfv_machine); +#endif } machine_init(pc_machine_init); @@ -168,7 +168,7 @@ void pci_device_reset(PCIDevice *dev) dev->irq_state = 0; pci_update_irq_status(dev); pci_device_deassert_intx(dev); - /* Clear all writeable bits */ + /* Clear all writable bits */ pci_word_test_and_clear_mask(dev->config + PCI_COMMAND, pci_get_word(dev->wmask + PCI_COMMAND) | pci_get_word(dev->w1cmask + PCI_COMMAND)); @@ -891,7 +891,7 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, wmask = ~(size - 1); addr = pci_bar(pci_dev, region_num); if (region_num == PCI_ROM_SLOT) { - /* ROM enable bit is writeable */ + /* ROM enable bit is writable */ wmask |= PCI_ROM_ADDRESS_ENABLE; } pci_set_long(pci_dev->config + addr, type); @@ -1940,6 +1940,8 @@ static int pci_add_option_rom(PCIDevice *pdev, bool is_default_rom) pci_patch_ids(pdev, ptr, size); } + qemu_put_ram_ptr(ptr); + pci_register_bar(pdev, PCI_ROM_SLOT, size, 0, pci_map_option_rom); @@ -1993,7 +1995,7 @@ void pci_del_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size) if (!offset) return; pdev->config[prev] = pdev->config[offset + PCI_CAP_LIST_NEXT]; - /* Make capability writeable again */ + /* Make capability writable again */ memset(pdev->wmask + offset, 0xff, size); memset(pdev->w1cmask + offset, 0, size); /* Clear cmask as device-specific registers can't be checked */ @@ -132,7 +132,7 @@ struct PCIDevice { /* PCI config space */ uint8_t *config; - /* Used to enable config checks on load. Note that writeable bits are + /* Used to enable config checks on load. Note that writable bits are * never checked even if set in cmask. */ uint8_t *cmask; diff --git a/hw/pci_ids.h b/hw/pci_ids.h index ea3418cef2..d9457ed3f4 100644 --- a/hw/pci_ids.h +++ b/hw/pci_ids.h @@ -100,6 +100,7 @@ #define PCI_VENDOR_ID_INTEL 0x8086 #define PCI_DEVICE_ID_INTEL_82441 0x1237 #define PCI_DEVICE_ID_INTEL_82801AA_5 0x2415 +#define PCI_DEVICE_ID_INTEL_82801D 0x24CD #define PCI_DEVICE_ID_INTEL_ESB_9 0x25ab #define PCI_DEVICE_ID_INTEL_82371SB_0 0x7000 #define PCI_DEVICE_ID_INTEL_82371SB_1 0x7010 diff --git a/hw/pci_regs.h b/hw/pci_regs.h index 63e8956868..e8840964ac 100644 --- a/hw/pci_regs.h +++ b/hw/pci_regs.h @@ -223,7 +223,7 @@ #define PCI_PM_CAP_PME_CLOCK 0x0008 /* PME clock required */ #define PCI_PM_CAP_RESERVED 0x0010 /* Reserved field */ #define PCI_PM_CAP_DSI 0x0020 /* Device specific initialization */ -#define PCI_PM_CAP_AUX_POWER 0x01C0 /* Auxilliary power support mask */ +#define PCI_PM_CAP_AUX_POWER 0x01C0 /* Auxiliary power support mask */ #define PCI_PM_CAP_D1 0x0200 /* D1 power state support */ #define PCI_PM_CAP_D2 0x0400 /* D2 power state support */ #define PCI_PM_CAP_PME 0x0800 /* PME pin supported */ @@ -176,7 +176,7 @@ static void hotplug_event_notify(PCIDevice *dev) } /* - * A PCI Express Hot-Plug Event has occured, so update slot status register + * A PCI Express Hot-Plug Event has occurred, so update slot status register * and notify OS of the event if necessary. * * 6.7.3 PCI Express Hot-Plug Events @@ -40,7 +40,7 @@ typedef enum { * * Not all the bits of slot control register match with the ones of * slot status. Not some bits of slot status register is used to - * show status, not to report event occurence. + * show status, not to report event occurrence. * So such bits must be masked out when checking the software * notification condition. */ diff --git a/hw/pcie_aer.c b/hw/pcie_aer.c index b87201026b..be019c7c0a 100644 --- a/hw/pcie_aer.c +++ b/hw/pcie_aer.c @@ -617,7 +617,7 @@ static bool pcie_aer_inject_uncor_error(PCIEAERInject *inj, bool is_fatal) /* * non-Function specific error must be recorded in all functions. * It is the responsibility of the caller of this function. - * It is also caller's responsiblity to determine which function should + * It is also caller's responsibility to determine which function should * report the rerror. * * 6.2.4 Error Logging diff --git a/hw/pflash_cfi02.c b/hw/pflash_cfi02.c index 14bbc34e16..725cd1e78c 100644 --- a/hw/pflash_cfi02.c +++ b/hw/pflash_cfi02.c @@ -188,7 +188,7 @@ static uint32_t pflash_read (pflash_t *pfl, target_phys_addr_t offset, default: goto flash_read; } - DPRINTF("%s: ID " TARGET_FMT_pld " %x\n", __func__, boff, ret); + DPRINTF("%s: ID " TARGET_FMT_plx " %x\n", __func__, boff, ret); break; case 0xA0: case 0x10: @@ -367,7 +367,7 @@ static void pflash_write (pflash_t *pfl, target_phys_addr_t offset, case 4: switch (pfl->cmd) { case 0xA0: - /* Ignore writes while flash data write is occuring */ + /* Ignore writes while flash data write is occurring */ /* As we suppose write is immediate, this should never happen */ return; case 0x80: diff --git a/hw/piix_pci.c b/hw/piix_pci.c index b927f01bdc..e0da0bdf91 100644 --- a/hw/piix_pci.c +++ b/hw/piix_pci.c @@ -29,6 +29,7 @@ #include "isa.h" #include "sysbus.h" #include "range.h" +#include "xen.h" /* * I440FX chipset data sheet. @@ -172,6 +173,13 @@ static void i440fx_write_config(PCIDevice *dev, } } +static void i440fx_write_config_xen(PCIDevice *dev, + uint32_t address, uint32_t val, int len) +{ + xen_piix_pci_write_config_client(address, val, len); + i440fx_write_config(dev, address, val, len); +} + static int i440fx_load_old(QEMUFile* f, void *opaque, int version_id) { PCII440FXState *d = opaque; @@ -234,7 +242,10 @@ static int i440fx_initfn(PCIDevice *dev) return 0; } -PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix3_devfn, qemu_irq *pic, ram_addr_t ram_size) +static PCIBus *i440fx_common_init(const char *device_name, + PCII440FXState **pi440fx_state, + int *piix3_devfn, + qemu_irq *pic, ram_addr_t ram_size) { DeviceState *dev; PCIBus *b; @@ -248,13 +259,13 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix3_devfn, qemu_irq * s->bus = b; qdev_init_nofail(dev); - d = pci_create_simple(b, 0, "i440FX"); + d = pci_create_simple(b, 0, device_name); *pi440fx_state = DO_UPCAST(PCII440FXState, dev, d); piix3 = DO_UPCAST(PIIX3State, dev, pci_create_simple_multifunction(b, -1, true, "PIIX3")); piix3->pic = pic; - pci_bus_irqs(b, piix3_set_irq, pci_slot_get_pirq, piix3, PIIX_NUM_PIRQS); + (*pi440fx_state)->piix3 = piix3; *piix3_devfn = piix3->dev.devfn; @@ -267,12 +278,36 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix3_devfn, qemu_irq * return b; } +PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix3_devfn, + qemu_irq *pic, ram_addr_t ram_size) +{ + PCIBus *b; + + b = i440fx_common_init("i440FX", pi440fx_state, piix3_devfn, pic, ram_size); + pci_bus_irqs(b, piix3_set_irq, pci_slot_get_pirq, (*pi440fx_state)->piix3, + PIIX_NUM_PIRQS); + + return b; +} + +PCIBus *i440fx_xen_init(PCII440FXState **pi440fx_state, int *piix3_devfn, + qemu_irq *pic, ram_addr_t ram_size) +{ + PCIBus *b; + + b = i440fx_common_init("i440FX-xen", pi440fx_state, piix3_devfn, pic, ram_size); + pci_bus_irqs(b, xen_piix3_set_irq, xen_pci_slot_get_pirq, + (*pi440fx_state)->piix3, PIIX_NUM_PIRQS); + + return b; +} + /* PIIX3 PCI to ISA bridge */ static void piix3_set_irq_pic(PIIX3State *piix3, int pic_irq) { qemu_set_irq(piix3->pic[pic_irq], !!(piix3->pic_levels & - (((1UL << PIIX_NUM_PIRQS) - 1) << + (((1ULL << PIIX_NUM_PIRQS) - 1) << (pic_irq * PIIX_NUM_PIRQS)))); } @@ -422,6 +457,14 @@ static PCIDeviceInfo i440fx_info[] = { .revision = 0x02, .class_id = PCI_CLASS_BRIDGE_HOST, },{ + .qdev.name = "i440FX-xen", + .qdev.desc = "Host bridge", + .qdev.size = sizeof(PCII440FXState), + .qdev.vmsd = &vmstate_i440fx, + .qdev.no_user = 1, + .init = i440fx_initfn, + .config_write = i440fx_write_config_xen, + },{ .qdev.name = "PIIX3", .qdev.desc = "ISA bridge", .qdev.size = sizeof(PIIX3State), diff --git a/hw/pl031.c b/hw/pl031.c index 8c2f9d0bc7..017a313fda 100644 --- a/hw/pl031.c +++ b/hw/pl031.c @@ -161,7 +161,7 @@ static void pl031_write(void * opaque, target_phys_addr_t offset, pl031_update(s); break; case RTC_ICR: - /* The PL031 documentation (DDI0224B) states that the interupt is + /* The PL031 documentation (DDI0224B) states that the interrupt is cleared when bit 0 of the written value is set. However the arm926e documentation (DDI0287B) states that the interrupt is cleared when any value is written. */ diff --git a/hw/pl061.c b/hw/pl061.c index 2e181f8c2f..372dfc2da2 100644 --- a/hw/pl061.c +++ b/hw/pl061.c @@ -98,7 +98,7 @@ static uint32_t pl061_read(void *opaque, target_phys_addr_t offset) return s->isense; case 0x408: /* Interrupt both edges */ return s->ibe; - case 0x40c: /* Interupt event */ + case 0x40c: /* Interrupt event */ return s->iev; case 0x410: /* Interrupt mask */ return s->im; @@ -156,7 +156,7 @@ static void pl061_write(void *opaque, target_phys_addr_t offset, case 0x408: /* Interrupt both edges */ s->ibe = value; break; - case 0x40c: /* Interupt event */ + case 0x40c: /* Interrupt event */ s->iev = value; break; case 0x410: /* Interrupt mask */ @@ -452,6 +452,10 @@ uint64_t cpu_ppc_load_tbl (CPUState *env) ppc_tb_t *tb_env = env->tb_env; uint64_t tb; + if (kvm_enabled()) { + return env->spr[SPR_TBL]; + } + tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->tb_offset); LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb); @@ -471,6 +475,10 @@ static inline uint32_t _cpu_ppc_load_tbu(CPUState *env) uint32_t cpu_ppc_load_tbu (CPUState *env) { + if (kvm_enabled()) { + return env->spr[SPR_TBU]; + } + return _cpu_ppc_load_tbu(env); } @@ -616,6 +624,10 @@ uint32_t cpu_ppc_load_decr (CPUState *env) { ppc_tb_t *tb_env = env->tb_env; + if (kvm_enabled()) { + return env->spr[SPR_DECR]; + } + return _cpu_ppc_load_decr(env, tb_env->decr_next); } diff --git a/hw/ppc4xx_devs.c b/hw/ppc4xx_devs.c index 7f9ed17138..68bdfaacc7 100644 --- a/hw/ppc4xx_devs.c +++ b/hw/ppc4xx_devs.c @@ -38,7 +38,7 @@ #endif /*****************************************************************************/ -/* Generic PowerPC 4xx processor instanciation */ +/* Generic PowerPC 4xx processor instantiation */ CPUState *ppc4xx_init (const char *cpu_model, clk_setup_t *cpu_clk, clk_setup_t *tb_clk, uint32_t sysclk) diff --git a/hw/ppce500.h b/hw/ppce500.h deleted file mode 100644 index 24d49bb871..0000000000 --- a/hw/ppce500.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * QEMU PowerPC E500 emulation shared definitions - * - * Copyright (C) 2009 Freescale Semiconductor, Inc. All rights reserved. - * - * Author: Yu Liu, <yu.liu@freescale.com> - * - * This file is derived from hw/ppc440.h - * the copyright for that material belongs to the original owners. - * - * This 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. - */ - -#if !defined(PPC_E500_H) -#define PPC_E500_H - -PCIBus *ppce500_pci_init(qemu_irq *pic, target_phys_addr_t registers); - -#endif /* !defined(PPC_E500_H) */ diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c index e111dda5f4..6b57fbf597 100644 --- a/hw/ppce500_mpc8544ds.c +++ b/hw/ppce500_mpc8544ds.c @@ -28,9 +28,10 @@ #include "kvm_ppc.h" #include "device_tree.h" #include "openpic.h" -#include "ppce500.h" +#include "ppc.h" #include "loader.h" #include "elf.h" +#include "sysbus.h" #define BINARY_DEVICE_TREE_FILE "mpc8544ds.dtb" #define UIMAGE_LOAD_BASE 0 @@ -50,6 +51,12 @@ #define MPC8544_PCI_IO 0xE1000000 #define MPC8544_PCI_IOLEN 0x10000 +struct boot_info +{ + uint32_t dt_base; + uint32_t entry; +}; + #ifdef CONFIG_FDT static int mpc8544_copy_soc_cell(void *fdt, const char *node, const char *prop) { @@ -82,7 +89,7 @@ static int mpc8544_load_device_tree(target_phys_addr_t addr, { int ret = -1; #ifdef CONFIG_FDT - uint32_t mem_reg_property[] = {0, ramsize}; + uint32_t mem_reg_property[] = {0, cpu_to_be32(ramsize)}; char *filename; int fdt_size; void *fdt; @@ -103,15 +110,19 @@ static int mpc8544_load_device_tree(target_phys_addr_t addr, if (ret < 0) fprintf(stderr, "couldn't set /memory/reg\n"); - ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-start", - initrd_base); - if (ret < 0) - fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n"); + if (initrd_size) { + ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-start", + initrd_base); + if (ret < 0) { + fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n"); + } - ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-end", - (initrd_base + initrd_size)); - if (ret < 0) - fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n"); + ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-end", + (initrd_base + initrd_size)); + if (ret < 0) { + fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n"); + } + } ret = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline); @@ -145,6 +156,13 @@ static int mpc8544_load_device_tree(target_phys_addr_t addr, mpc8544_copy_soc_cell(fdt, buf, "clock-frequency"); mpc8544_copy_soc_cell(fdt, buf, "timebase-frequency"); + } else { + const uint32_t freq = 400000000; + + qemu_devtree_setprop_cell(fdt, "/cpus/PowerPC,8544@0", + "clock-frequency", freq); + qemu_devtree_setprop_cell(fdt, "/cpus/PowerPC,8544@0", + "timebase-frequency", freq); } ret = rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); @@ -156,6 +174,35 @@ out: return ret; } +/* Create -kernel TLB entries for BookE, linearly spanning 256MB. */ +static void mmubooke_create_initial_mapping(CPUState *env, + target_ulong va, + target_phys_addr_t pa) +{ + ppcemb_tlb_t *tlb = booke206_get_tlbe(env, 1, 0, 0); + + tlb->attr = 0; + tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4); + tlb->size = 256 * 1024 * 1024; + tlb->EPN = va & TARGET_PAGE_MASK; + tlb->RPN = pa & TARGET_PAGE_MASK; + tlb->PID = 0; +} + +static void mpc8544ds_cpu_reset(void *opaque) +{ + CPUState *env = opaque; + struct boot_info *bi = env->load_info; + + cpu_reset(env); + + /* Set initial guest state. */ + env->gpr[1] = (16<<20) - 8; + env->gpr[3] = bi->dt_base; + env->nip = bi->entry; + mmubooke_create_initial_mapping(env, 0, 0); +} + static void mpc8544ds_init(ram_addr_t ram_size, const char *boot_device, const char *kernel_filename, @@ -175,15 +222,28 @@ static void mpc8544ds_init(ram_addr_t ram_size, target_long initrd_size=0; int i=0; unsigned int pci_irq_nrs[4] = {1, 2, 3, 4}; - qemu_irq *irqs, *mpic, *pci_irqs; + qemu_irq *irqs, *mpic; + DeviceState *dev; + struct boot_info *boot_info; /* Setup CPU */ - env = cpu_ppc_init("e500v2_v30"); + if (cpu_model == NULL) { + cpu_model = "e500v2_v30"; + } + + env = cpu_ppc_init(cpu_model); if (!env) { fprintf(stderr, "Unable to initialize CPU!\n"); exit(1); } + /* XXX register timer? */ + ppc_emb_timers_init(env, 400000000, PPC_INTERRUPT_DECR); + ppc_dcr_init(env, NULL, NULL); + + /* Register reset handler */ + qemu_register_reset(mpc8544ds_cpu_reset, env); + /* Fixup Memory size on a alignment boundary */ ram_size &= ~(RAM_SIZES_ALIGN - 1); @@ -211,12 +271,11 @@ static void mpc8544ds_init(ram_addr_t ram_size, } /* PCI */ - pci_irqs = qemu_malloc(sizeof(qemu_irq) * 4); - pci_irqs[0] = mpic[pci_irq_nrs[0]]; - pci_irqs[1] = mpic[pci_irq_nrs[1]]; - pci_irqs[2] = mpic[pci_irq_nrs[2]]; - pci_irqs[3] = mpic[pci_irq_nrs[3]]; - pci_bus = ppce500_pci_init(pci_irqs, MPC8544_PCI_REGS_BASE); + dev = sysbus_create_varargs("e500-pcihost", MPC8544_PCI_REGS_BASE, + mpic[pci_irq_nrs[0]], mpic[pci_irq_nrs[1]], + mpic[pci_irq_nrs[2]], mpic[pci_irq_nrs[3]], + NULL); + pci_bus = (PCIBus *)qdev_get_child_bus(dev, "pci.0"); if (!pci_bus) printf("couldn't create PCI controller!\n"); @@ -259,8 +318,13 @@ static void mpc8544ds_init(ram_addr_t ram_size, } } + boot_info = qemu_mallocz(sizeof(struct boot_info)); + /* If we're loading a kernel directly, we must load the device tree too. */ if (kernel_filename) { +#ifndef CONFIG_FDT + cpu_abort(env, "Compiled without FDT support - can't load kernel\n"); +#endif dt_base = (kernel_size + DTC_LOAD_PAD) & ~DTC_PAD_MASK; if (mpc8544_load_device_tree(dt_base, ram_size, initrd_base, initrd_size, kernel_cmdline) < 0) { @@ -268,17 +332,14 @@ static void mpc8544ds_init(ram_addr_t ram_size, exit(1); } - /* Set initial guest state. */ - env->gpr[1] = (16<<20) - 8; - env->gpr[3] = dt_base; - env->nip = entry; - /* XXX we currently depend on KVM to create some initial TLB entries. */ + boot_info->entry = entry; + boot_info->dt_base = dt_base; } + env->load_info = boot_info; - if (kvm_enabled()) + if (kvm_enabled()) { kvmppc_init(); - - return; + } } static QEMUMachine mpc8544ds_machine = { diff --git a/hw/ppce500_pci.c b/hw/ppce500_pci.c index 83a20e4620..069af9691a 100644 --- a/hw/ppce500_pci.c +++ b/hw/ppce500_pci.c @@ -15,7 +15,6 @@ */ #include "hw.h" -#include "ppce500.h" #include "pci.h" #include "pci_host.h" #include "bswap.h" @@ -29,7 +28,8 @@ #define PCIE500_CFGADDR 0x0 #define PCIE500_CFGDATA 0x4 #define PCIE500_REG_BASE 0xC00 -#define PCIE500_REG_SIZE (0x1000 - PCIE500_REG_BASE) +#define PCIE500_ALL_SIZE 0x1000 +#define PCIE500_REG_SIZE (PCIE500_ALL_SIZE - PCIE500_REG_BASE) #define PPCE500_PCI_CONFIG_ADDR 0x0 #define PPCE500_PCI_CONFIG_DATA 0x4 @@ -73,11 +73,15 @@ struct pci_inbound { }; struct PPCE500PCIState { + PCIHostState pci_state; struct pci_outbound pob[PPCE500_PCI_NR_POBS]; struct pci_inbound pib[PPCE500_PCI_NR_PIBS]; uint32_t gasket_time; - PCIHostState pci_state; - PCIDevice *pci_dev; + qemu_irq irq[4]; + /* mmio maps */ + int cfgaddr; + int cfgdata; + int reg; }; typedef struct PPCE500PCIState PPCE500PCIState; @@ -250,7 +254,6 @@ static const VMStateDescription vmstate_ppce500_pci = { .minimum_version_id = 1, .minimum_version_id_old = 1, .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE_POINTER(pci_dev, PPCE500PCIState), VMSTATE_STRUCT_ARRAY(pob, PPCE500PCIState, PPCE500_PCI_NR_POBS, 1, vmstate_pci_outbound, struct pci_outbound), VMSTATE_STRUCT_ARRAY(pib, PPCE500PCIState, PPCE500_PCI_NR_PIBS, 1, @@ -260,60 +263,73 @@ static const VMStateDescription vmstate_ppce500_pci = { } }; -PCIBus *ppce500_pci_init(qemu_irq pci_irqs[4], target_phys_addr_t registers) +static void e500_pci_map(SysBusDevice *dev, target_phys_addr_t base) +{ + PCIHostState *h = FROM_SYSBUS(PCIHostState, sysbus_from_qdev(dev)); + PPCE500PCIState *s = DO_UPCAST(PPCE500PCIState, pci_state, h); + + cpu_register_physical_memory(base + PCIE500_CFGADDR, 4, s->cfgaddr); + cpu_register_physical_memory(base + PCIE500_CFGDATA, 4, s->cfgdata); + cpu_register_physical_memory(base + PCIE500_REG_BASE, PCIE500_REG_SIZE, + s->reg); +} + +static int e500_pcihost_initfn(SysBusDevice *dev) +{ + PCIHostState *h; + PPCE500PCIState *s; + PCIBus *b; + int i; + + h = FROM_SYSBUS(PCIHostState, sysbus_from_qdev(dev)); + s = DO_UPCAST(PPCE500PCIState, pci_state, h); + + for (i = 0; i < ARRAY_SIZE(s->irq); i++) { + sysbus_init_irq(dev, &s->irq[i]); + } + + b = pci_register_bus(&s->pci_state.busdev.qdev, NULL, mpc85xx_pci_set_irq, + mpc85xx_pci_map_irq, s->irq, PCI_DEVFN(0x11, 0), 4); + s->pci_state.bus = b; + + pci_create_simple(b, 0, "e500-host-bridge"); + + s->cfgaddr = pci_host_conf_register_mmio(&s->pci_state, DEVICE_BIG_ENDIAN); + s->cfgdata = pci_host_data_register_mmio(&s->pci_state, + DEVICE_LITTLE_ENDIAN); + s->reg = cpu_register_io_memory(e500_pci_reg_read, e500_pci_reg_write, s, + DEVICE_BIG_ENDIAN); + sysbus_init_mmio_cb(dev, PCIE500_ALL_SIZE, e500_pci_map); + + return 0; +} + +static int e500_host_bridge_initfn(PCIDevice *dev) +{ + pci_config_set_vendor_id(dev->config, PCI_VENDOR_ID_FREESCALE); + pci_config_set_device_id(dev->config, PCI_DEVICE_ID_MPC8533E); + pci_config_set_class(dev->config, PCI_CLASS_PROCESSOR_POWERPC); + + return 0; +} + +static PCIDeviceInfo e500_host_bridge_info = { + .qdev.name = "e500-host-bridge", + .qdev.desc = "Host bridge", + .qdev.size = sizeof(PCIDevice), + .init = e500_host_bridge_initfn, +}; + +static SysBusDeviceInfo e500_pcihost_info = { + .init = e500_pcihost_initfn, + .qdev.name = "e500-pcihost", + .qdev.size = sizeof(PPCE500PCIState), + .qdev.vmsd = &vmstate_ppce500_pci, +}; + +static void e500_pci_register(void) { - PPCE500PCIState *controller; - PCIDevice *d; - int index; - static int ppce500_pci_id; - - controller = qemu_mallocz(sizeof(PPCE500PCIState)); - - controller->pci_state.bus = pci_register_bus(NULL, "pci", - mpc85xx_pci_set_irq, - mpc85xx_pci_map_irq, - pci_irqs, PCI_DEVFN(0x11, 0), - 4); - d = pci_register_device(controller->pci_state.bus, - "host bridge", sizeof(PCIDevice), - 0, NULL, NULL); - - pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_FREESCALE); - pci_config_set_device_id(d->config, PCI_DEVICE_ID_MPC8533E); - pci_config_set_class(d->config, PCI_CLASS_PROCESSOR_POWERPC); - - controller->pci_dev = d; - - /* CFGADDR */ - index = pci_host_conf_register_mmio(&controller->pci_state, - DEVICE_BIG_ENDIAN); - if (index < 0) - goto free; - cpu_register_physical_memory(registers + PCIE500_CFGADDR, 4, index); - - /* CFGDATA */ - index = pci_host_data_register_mmio(&controller->pci_state, - DEVICE_BIG_ENDIAN); - if (index < 0) - goto free; - cpu_register_physical_memory(registers + PCIE500_CFGDATA, 4, index); - - index = cpu_register_io_memory(e500_pci_reg_read, - e500_pci_reg_write, controller, - DEVICE_NATIVE_ENDIAN); - if (index < 0) - goto free; - cpu_register_physical_memory(registers + PCIE500_REG_BASE, - PCIE500_REG_SIZE, index); - - /* XXX load/save code not tested. */ - vmstate_register(&d->qdev, ppce500_pci_id++, &vmstate_ppce500_pci, - controller); - - return controller->pci_state.bus; - -free: - printf("%s error\n", __func__); - qemu_free(controller); - return NULL; + sysbus_register_withprop(&e500_pcihost_info); + pci_qdev_register(&e500_host_bridge_info); } +device_init(e500_pci_register); diff --git a/hw/qxl-render.c b/hw/qxl-render.c index 58965e0179..1316066599 100644 --- a/hw/qxl-render.c +++ b/hw/qxl-render.c @@ -185,7 +185,6 @@ void qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext) QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id); QXLCursor *cursor; QEMUCursor *c; - int x = -1, y = -1; if (!qxl->ssd.ds->mouse_set || !qxl->ssd.ds->cursor_define) { return; @@ -198,8 +197,6 @@ void qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext) } switch (cmd->type) { case QXL_CURSOR_SET: - x = cmd->u.set.position.x; - y = cmd->u.set.position.y; cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id); if (cursor->chunk.data_size != cursor->data_size) { fprintf(stderr, "%s: multiple chunks\n", __FUNCTION__); @@ -209,18 +206,20 @@ void qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext) if (c == NULL) { c = cursor_builtin_left_ptr(); } - qemu_mutex_lock_iothread(); - qxl->ssd.ds->cursor_define(c); - qxl->ssd.ds->mouse_set(x, y, 1); - qemu_mutex_unlock_iothread(); - cursor_put(c); + qemu_mutex_lock(&qxl->ssd.lock); + if (qxl->ssd.cursor) { + cursor_put(qxl->ssd.cursor); + } + qxl->ssd.cursor = c; + qxl->ssd.mouse_x = cmd->u.set.position.x; + qxl->ssd.mouse_y = cmd->u.set.position.y; + qemu_mutex_unlock(&qxl->ssd.lock); break; case QXL_CURSOR_MOVE: - x = cmd->u.position.x; - y = cmd->u.position.y; - qemu_mutex_lock_iothread(); - qxl->ssd.ds->mouse_set(x, y, 1); - qemu_mutex_unlock_iothread(); + qemu_mutex_lock(&qxl->ssd.lock); + qxl->ssd.mouse_x = cmd->u.position.x; + qxl->ssd.mouse_y = cmd->u.position.y; + qemu_mutex_unlock(&qxl->ssd.lock); break; } } @@ -343,18 +343,24 @@ static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext) SimpleSpiceUpdate *update; QXLCommandRing *ring; QXLCommand *cmd; - int notify; + int notify, ret; switch (qxl->mode) { case QXL_MODE_VGA: dprint(qxl, 2, "%s: vga\n", __FUNCTION__); - update = qemu_spice_create_update(&qxl->ssd); - if (update == NULL) { - return false; + ret = false; + qemu_mutex_lock(&qxl->ssd.lock); + if (qxl->ssd.update != NULL) { + update = qxl->ssd.update; + qxl->ssd.update = NULL; + *ext = update->ext; + ret = true; } - *ext = update->ext; - qxl_log_command(qxl, "vga", ext); - return true; + qemu_mutex_unlock(&qxl->ssd.lock); + if (ret) { + qxl_log_command(qxl, "vga", ext); + } + return ret; case QXL_MODE_COMPAT: case QXL_MODE_NATIVE: case QXL_MODE_UNDEFINED: @@ -662,10 +668,8 @@ static void qxl_hard_reset(PCIQXLDevice *d, int loadvm) dprint(d, 1, "%s: start%s\n", __FUNCTION__, loadvm ? " (loadvm)" : ""); - qemu_mutex_unlock_iothread(); d->ssd.worker->reset_cursor(d->ssd.worker); d->ssd.worker->reset_image_cache(d->ssd.worker); - qemu_mutex_lock_iothread(); qxl_reset_surfaces(d); qxl_reset_memslots(d); @@ -795,9 +799,7 @@ static void qxl_reset_surfaces(PCIQXLDevice *d) { dprint(d, 1, "%s:\n", __FUNCTION__); d->mode = QXL_MODE_UNDEFINED; - qemu_mutex_unlock_iothread(); d->ssd.worker->destroy_surfaces(d->ssd.worker); - qemu_mutex_lock_iothread(); memset(&d->guest_surfaces.cmds, 0, sizeof(d->guest_surfaces.cmds)); } @@ -866,9 +868,7 @@ static void qxl_destroy_primary(PCIQXLDevice *d) dprint(d, 1, "%s\n", __FUNCTION__); d->mode = QXL_MODE_UNDEFINED; - qemu_mutex_unlock_iothread(); d->ssd.worker->destroy_primary_surface(d->ssd.worker, 0); - qemu_mutex_lock_iothread(); } static void qxl_set_mode(PCIQXLDevice *d, int modenr, int loadvm) @@ -938,10 +938,8 @@ static void ioport_write(void *opaque, uint32_t addr, uint32_t val) case QXL_IO_UPDATE_AREA: { QXLRect update = d->ram->update_area; - qemu_mutex_unlock_iothread(); d->ssd.worker->update_area(d->ssd.worker, d->ram->update_surface, &update, NULL, 0, 0); - qemu_mutex_lock_iothread(); break; } case QXL_IO_NOTIFY_CMD: @@ -1303,6 +1301,9 @@ static int qxl_init_primary(PCIDevice *dev) vga->ds = graphic_console_init(qxl_hw_update, qxl_hw_invalidate, qxl_hw_screen_dump, qxl_hw_text_update, qxl); qxl->ssd.ds = vga->ds; + qemu_mutex_init(&qxl->ssd.lock); + qxl->ssd.mouse_x = -1; + qxl->ssd.mouse_y = -1; qxl->ssd.bufsize = (16 * 1024 * 1024); qxl->ssd.buf = qemu_malloc(qxl->ssd.bufsize); diff --git a/hw/realview.c b/hw/realview.c index 96fb9da241..82f3d82d44 100644 --- a/hw/realview.c +++ b/hw/realview.c @@ -17,7 +17,6 @@ #include "sysemu.h" #include "boards.h" #include "bitbang_i2c.h" -#include "sysbus.h" #include "blockdev.h" #define SMP_BOOT_ADDR 0xe0000000 diff --git a/hw/rtl8139.c b/hw/rtl8139.c index 34e3a9eec7..5214b8cb7c 100644 --- a/hw/rtl8139.c +++ b/hw/rtl8139.c @@ -1399,7 +1399,7 @@ static void rtl8139_ChipCmd_write(RTL8139State *s, uint32_t val) s->currCPlusTxDesc = 0; } - /* mask unwriteable bits */ + /* mask unwritable bits */ val = SET_MASKED(val, 0xe3, s->bChipCmdState); /* Deassert reset pin before next read */ @@ -1443,7 +1443,7 @@ static void rtl8139_CpCmd_write(RTL8139State *s, uint32_t val) s->cplus_enabled = 1; - /* mask unwriteable bits */ + /* mask unwritable bits */ val = SET_MASKED(val, 0xff84, s->CpCmd); s->CpCmd = val; @@ -1472,7 +1472,7 @@ static uint32_t rtl8139_IntrMitigate_read(RTL8139State *s) return ret; } -static int rtl8139_config_writeable(RTL8139State *s) +static int rtl8139_config_writable(RTL8139State *s) { if (s->Cfg9346 & Cfg9346_Unlock) { @@ -1490,10 +1490,10 @@ static void rtl8139_BasicModeCtrl_write(RTL8139State *s, uint32_t val) DPRINTF("BasicModeCtrl register write(w) val=0x%04x\n", val); - /* mask unwriteable bits */ + /* mask unwritable bits */ uint32_t mask = 0x4cff; - if (1 || !rtl8139_config_writeable(s)) + if (1 || !rtl8139_config_writable(s)) { /* Speed setting and autonegotiation enable bits are read-only */ mask |= 0x3000; @@ -1521,7 +1521,7 @@ static void rtl8139_BasicModeStatus_write(RTL8139State *s, uint32_t val) DPRINTF("BasicModeStatus register write(w) val=0x%04x\n", val); - /* mask unwriteable bits */ + /* mask unwritable bits */ val = SET_MASKED(val, 0xff3f, s->BasicModeStatus); s->BasicModeStatus = val; @@ -1542,7 +1542,7 @@ static void rtl8139_Cfg9346_write(RTL8139State *s, uint32_t val) DPRINTF("Cfg9346 write val=0x%02x\n", val); - /* mask unwriteable bits */ + /* mask unwritable bits */ val = SET_MASKED(val, 0x31, s->Cfg9346); uint32_t opmode = val & 0xc0; @@ -1594,10 +1594,11 @@ static void rtl8139_Config0_write(RTL8139State *s, uint32_t val) DPRINTF("Config0 write val=0x%02x\n", val); - if (!rtl8139_config_writeable(s)) + if (!rtl8139_config_writable(s)) { return; + } - /* mask unwriteable bits */ + /* mask unwritable bits */ val = SET_MASKED(val, 0xf8, s->Config0); s->Config0 = val; @@ -1618,10 +1619,11 @@ static void rtl8139_Config1_write(RTL8139State *s, uint32_t val) DPRINTF("Config1 write val=0x%02x\n", val); - if (!rtl8139_config_writeable(s)) + if (!rtl8139_config_writable(s)) { return; + } - /* mask unwriteable bits */ + /* mask unwritable bits */ val = SET_MASKED(val, 0xC, s->Config1); s->Config1 = val; @@ -1642,10 +1644,11 @@ static void rtl8139_Config3_write(RTL8139State *s, uint32_t val) DPRINTF("Config3 write val=0x%02x\n", val); - if (!rtl8139_config_writeable(s)) + if (!rtl8139_config_writable(s)) { return; + } - /* mask unwriteable bits */ + /* mask unwritable bits */ val = SET_MASKED(val, 0x8F, s->Config3); s->Config3 = val; @@ -1666,10 +1669,11 @@ static void rtl8139_Config4_write(RTL8139State *s, uint32_t val) DPRINTF("Config4 write val=0x%02x\n", val); - if (!rtl8139_config_writeable(s)) + if (!rtl8139_config_writable(s)) { return; + } - /* mask unwriteable bits */ + /* mask unwritable bits */ val = SET_MASKED(val, 0x0a, s->Config4); s->Config4 = val; @@ -1690,7 +1694,7 @@ static void rtl8139_Config5_write(RTL8139State *s, uint32_t val) DPRINTF("Config5 write val=0x%02x\n", val); - /* mask unwriteable bits */ + /* mask unwritable bits */ val = SET_MASKED(val, 0x80, s->Config5); s->Config5 = val; @@ -1743,7 +1747,7 @@ static void rtl8139_RxConfig_write(RTL8139State *s, uint32_t val) { DPRINTF("RxConfig write val=0x%08x\n", val); - /* mask unwriteable bits */ + /* mask unwritable bits */ val = SET_MASKED(val, 0xf0fc0040, s->RxConfig); s->RxConfig = val; @@ -2610,7 +2614,7 @@ static void rtl8139_IntrMask_write(RTL8139State *s, uint32_t val) { DPRINTF("IntrMask write(w) val=0x%04x\n", val); - /* mask unwriteable bits */ + /* mask unwritable bits */ val = SET_MASKED(val, 0x1e00, s->IntrMask); s->IntrMask = val; @@ -2642,7 +2646,7 @@ static void rtl8139_IntrStatus_write(RTL8139State *s, uint32_t val) #else uint16_t newStatus = s->IntrStatus & ~val; - /* mask unwriteable bits */ + /* mask unwritable bits */ newStatus = SET_MASKED(newStatus, 0x1e00, s->IntrStatus); /* writing 1 to interrupt status register bit clears it */ @@ -2686,7 +2690,7 @@ static void rtl8139_MultiIntr_write(RTL8139State *s, uint32_t val) { DPRINTF("MultiIntr write(w) val=0x%04x\n", val); - /* mask unwriteable bits */ + /* mask unwritable bits */ val = SET_MASKED(val, 0xf000, s->MultiIntr); s->MultiIntr = val; diff --git a/hw/s390-virtio-bus.c b/hw/s390-virtio-bus.c index bb49e393ec..d4a12f7531 100644 --- a/hw/s390-virtio-bus.c +++ b/hw/s390-virtio-bus.c @@ -60,6 +60,9 @@ static const VirtIOBindings virtio_s390_bindings; static ram_addr_t s390_virtio_device_num_vq(VirtIOS390Device *dev); +/* length of VirtIO device pages */ +const target_phys_addr_t virtio_size = S390_DEVICE_PAGES * TARGET_PAGE_SIZE; + VirtIOS390Bus *s390_virtio_bus_init(ram_addr_t *ram_size) { VirtIOS390Bus *bus; diff --git a/hw/s390-virtio-bus.h b/hw/s390-virtio-bus.h index edf6d04570..0c412d08e1 100644 --- a/hw/s390-virtio-bus.h +++ b/hw/s390-virtio-bus.h @@ -33,7 +33,7 @@ #define VIRTIO_VQCONFIG_LEN 24 #define VIRTIO_RING_LEN (TARGET_PAGE_SIZE * 3) -#define S390_DEVICE_PAGES 256 +#define S390_DEVICE_PAGES 512 typedef struct VirtIOS390Device { DeviceState qdev; diff --git a/hw/s390-virtio.c b/hw/s390-virtio.c index 698ff6f345..3eba7ea1e8 100644 --- a/hw/s390-virtio.c +++ b/hw/s390-virtio.c @@ -131,7 +131,7 @@ int s390_virtio_hypercall(CPUState *env, uint64_t mem, uint64_t hypercall) } /* PC hardware initialisation */ -static void s390_init(ram_addr_t ram_size, +static void s390_init(ram_addr_t my_ram_size, const char *boot_device, const char *kernel_filename, const char *kernel_cmdline, @@ -143,19 +143,29 @@ static void s390_init(ram_addr_t ram_size, ram_addr_t kernel_size = 0; ram_addr_t initrd_offset; ram_addr_t initrd_size = 0; + int shift = 0; uint8_t *storage_keys; int i; + /* s390x ram size detection needs a 16bit multiplier + an increment. So + guests > 64GB can be specified in 2MB steps etc. */ + while ((my_ram_size >> (20 + shift)) > 65535) { + shift++; + } + my_ram_size = my_ram_size >> (20 + shift) << (20 + shift); + + /* lets propagate the changed ram size into the global variable. */ + ram_size = my_ram_size; /* get a BUS */ - s390_bus = s390_virtio_bus_init(&ram_size); + s390_bus = s390_virtio_bus_init(&my_ram_size); /* allocate RAM */ - ram_addr = qemu_ram_alloc(NULL, "s390.ram", ram_size); - cpu_register_physical_memory(0, ram_size, ram_addr); + ram_addr = qemu_ram_alloc(NULL, "s390.ram", my_ram_size); + cpu_register_physical_memory(0, my_ram_size, ram_addr); /* allocate storage keys */ - storage_keys = qemu_mallocz(ram_size / TARGET_PAGE_SIZE); + storage_keys = qemu_mallocz(my_ram_size / TARGET_PAGE_SIZE); /* init CPUs */ if (cpu_model == NULL) { diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index ceeb4ecb91..ad6a730be0 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -4,6 +4,7 @@ #include "scsi-defs.h" #include "qdev.h" #include "blockdev.h" +#include "trace.h" static char *scsibus_get_fw_dev_path(DeviceState *dev); @@ -20,13 +21,13 @@ static int next_scsi_bus; /* Create a scsi bus, and attach devices to it. */ void scsi_bus_new(SCSIBus *bus, DeviceState *host, int tcq, int ndev, - scsi_completionfn complete) + const SCSIBusOps *ops) { qbus_create_inplace(&bus->qbus, &scsi_bus_info, host, NULL); bus->busnr = next_scsi_bus++; bus->tcq = tcq; bus->ndev = ndev; - bus->complete = complete; + bus->ops = ops; bus->qbus.allow_hotplug = 1; } @@ -135,42 +136,60 @@ SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t l SCSIRequest *req; req = qemu_mallocz(size); + req->refcount = 1; req->bus = scsi_bus_from_device(d); req->dev = d; req->tag = tag; req->lun = lun; req->status = -1; - req->enqueued = true; - QTAILQ_INSERT_TAIL(&d->requests, req, next); + trace_scsi_req_alloc(req->dev->id, req->lun, req->tag); return req; } -SCSIRequest *scsi_req_find(SCSIDevice *d, uint32_t tag) +SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun) { - SCSIRequest *req; + return d->info->alloc_req(d, tag, lun); +} - QTAILQ_FOREACH(req, &d->requests, next) { - if (req->tag == tag) { - return req; - } +uint8_t *scsi_req_get_buf(SCSIRequest *req) +{ + return req->dev->info->get_buf(req); +} + +int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len) +{ + if (req->dev->info->get_sense) { + return req->dev->info->get_sense(req, buf, len); + } else { + return 0; } - return NULL; +} + +int32_t scsi_req_enqueue(SCSIRequest *req, uint8_t *buf) +{ + int32_t rc; + + assert(!req->enqueued); + scsi_req_ref(req); + req->enqueued = true; + QTAILQ_INSERT_TAIL(&req->dev->requests, req, next); + + scsi_req_ref(req); + rc = req->dev->info->send_command(req, buf); + scsi_req_unref(req); + return rc; } static void scsi_req_dequeue(SCSIRequest *req) { + trace_scsi_req_dequeue(req->dev->id, req->lun, req->tag); if (req->enqueued) { QTAILQ_REMOVE(&req->dev->requests, req, next); req->enqueued = false; + scsi_req_unref(req); } } -void scsi_req_free(SCSIRequest *req) -{ - scsi_req_dequeue(req); - qemu_free(req); -} - static int scsi_req_length(SCSIRequest *req, uint8_t *cmd) { switch (cmd[0] >> 5) { @@ -195,6 +214,7 @@ static int scsi_req_length(SCSIRequest *req, uint8_t *cmd) req->cmd.len = 12; break; default: + trace_scsi_req_parse_bad(req->dev->id, req->lun, req->tag, cmd[0]); return -1; } @@ -392,9 +412,104 @@ int scsi_req_parse(SCSIRequest *req, uint8_t *buf) memcpy(req->cmd.buf, buf, req->cmd.len); scsi_req_xfer_mode(req); req->cmd.lba = scsi_req_lba(req); + trace_scsi_req_parsed(req->dev->id, req->lun, req->tag, buf[0], + req->cmd.mode, req->cmd.xfer); + if (req->cmd.lba != -1) { + trace_scsi_req_parsed_lba(req->dev->id, req->lun, req->tag, buf[0], + req->cmd.lba); + } return 0; } +/* + * Predefined sense codes + */ + +/* No sense data available */ +const struct SCSISense sense_code_NO_SENSE = { + .key = NO_SENSE , .asc = 0x00 , .ascq = 0x00 +}; + +/* LUN not ready, Manual intervention required */ +const struct SCSISense sense_code_LUN_NOT_READY = { + .key = NOT_READY, .asc = 0x04, .ascq = 0x03 +}; + +/* LUN not ready, Medium not present */ +const struct SCSISense sense_code_NO_MEDIUM = { + .key = NOT_READY, .asc = 0x3a, .ascq = 0x00 +}; + +/* Hardware error, internal target failure */ +const struct SCSISense sense_code_TARGET_FAILURE = { + .key = HARDWARE_ERROR, .asc = 0x44, .ascq = 0x00 +}; + +/* Illegal request, invalid command operation code */ +const struct SCSISense sense_code_INVALID_OPCODE = { + .key = ILLEGAL_REQUEST, .asc = 0x20, .ascq = 0x00 +}; + +/* Illegal request, LBA out of range */ +const struct SCSISense sense_code_LBA_OUT_OF_RANGE = { + .key = ILLEGAL_REQUEST, .asc = 0x21, .ascq = 0x00 +}; + +/* Illegal request, Invalid field in CDB */ +const struct SCSISense sense_code_INVALID_FIELD = { + .key = ILLEGAL_REQUEST, .asc = 0x24, .ascq = 0x00 +}; + +/* Illegal request, LUN not supported */ +const struct SCSISense sense_code_LUN_NOT_SUPPORTED = { + .key = ILLEGAL_REQUEST, .asc = 0x25, .ascq = 0x00 +}; + +/* Command aborted, I/O process terminated */ +const struct SCSISense sense_code_IO_ERROR = { + .key = ABORTED_COMMAND, .asc = 0x00, .ascq = 0x06 +}; + +/* Command aborted, I_T Nexus loss occurred */ +const struct SCSISense sense_code_I_T_NEXUS_LOSS = { + .key = ABORTED_COMMAND, .asc = 0x29, .ascq = 0x07 +}; + +/* Command aborted, Logical Unit failure */ +const struct SCSISense sense_code_LUN_FAILURE = { + .key = ABORTED_COMMAND, .asc = 0x3e, .ascq = 0x01 +}; + +/* + * scsi_build_sense + * + * Build a sense buffer + */ +int scsi_build_sense(SCSISense sense, uint8_t *buf, int len, int fixed) +{ + if (!fixed && len < 8) { + return 0; + } + + memset(buf, 0, len); + if (fixed) { + /* Return fixed format sense buffer */ + buf[0] = 0xf0; + buf[2] = sense.key; + buf[7] = 7; + buf[12] = sense.asc; + buf[13] = sense.ascq; + return MIN(len, 18); + } else { + /* Return descriptor format sense buffer */ + buf[0] = 0x72; + buf[1] = sense.key; + buf[2] = sense.asc; + buf[3] = sense.ascq; + return 8; + } +} + static const char *scsi_command_name(uint8_t cmd) { static const char *names[] = { @@ -489,6 +604,43 @@ static const char *scsi_command_name(uint8_t cmd) return names[cmd]; } +SCSIRequest *scsi_req_ref(SCSIRequest *req) +{ + req->refcount++; + return req; +} + +void scsi_req_unref(SCSIRequest *req) +{ + if (--req->refcount == 0) { + if (req->dev->info->free_req) { + req->dev->info->free_req(req); + } + qemu_free(req); + } +} + +/* Tell the device that we finished processing this chunk of I/O. It + will start the next chunk or complete the command. */ +void scsi_req_continue(SCSIRequest *req) +{ + trace_scsi_req_continue(req->dev->id, req->lun, req->tag); + if (req->cmd.mode == SCSI_XFER_TO_DEV) { + req->dev->info->write_data(req); + } else { + req->dev->info->read_data(req); + } +} + +/* Called by the devices when data is ready for the HBA. The HBA should + start a DMA operation to read or fill the device's data buffer. + Once it completes, calling scsi_req_continue will restart I/O. */ +void scsi_req_data(SCSIRequest *req, int len) +{ + trace_scsi_req_data(req->dev->id, req->lun, req->tag, len); + req->bus->ops->transfer_data(req, len); +} + void scsi_req_print(SCSIRequest *req) { FILE *fp = stderr; @@ -520,10 +672,42 @@ void scsi_req_print(SCSIRequest *req) void scsi_req_complete(SCSIRequest *req) { assert(req->status != -1); + scsi_req_ref(req); scsi_req_dequeue(req); - req->bus->complete(req->bus, SCSI_REASON_DONE, - req->tag, - req->status); + req->bus->ops->complete(req, req->status); + scsi_req_unref(req); +} + +void scsi_req_cancel(SCSIRequest *req) +{ + if (req->dev && req->dev->info->cancel_io) { + req->dev->info->cancel_io(req); + } + scsi_req_ref(req); + scsi_req_dequeue(req); + if (req->bus->ops->cancel) { + req->bus->ops->cancel(req); + } + scsi_req_unref(req); +} + +void scsi_req_abort(SCSIRequest *req, int status) +{ + req->status = status; + if (req->dev && req->dev->info->cancel_io) { + req->dev->info->cancel_io(req); + } + scsi_req_complete(req); +} + +void scsi_device_purge_requests(SCSIDevice *sdev) +{ + SCSIRequest *req; + + while (!QTAILQ_EMPTY(&sdev->requests)) { + req = QTAILQ_FIRST(&sdev->requests); + scsi_req_cancel(req); + } } static char *scsibus_get_fw_dev_path(DeviceState *dev) diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index b05e6547df..a8c7372d3e 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -49,14 +49,8 @@ do { fprintf(stderr, "scsi-disk: " fmt , ## __VA_ARGS__); } while (0) typedef struct SCSIDiskState SCSIDiskState; -typedef struct SCSISense { - uint8_t key; -} SCSISense; - typedef struct SCSIDiskReq { SCSIRequest req; - /* ??? We should probably keep track of whether the data transfer is - a read or a write. Currently we rely on the host getting it right. */ /* Both sector and sector_count are in terms of qemu 512 byte blocks. */ uint64_t sector; uint32_t sector_count; @@ -65,6 +59,8 @@ typedef struct SCSIDiskReq { uint32_t status; } SCSIDiskReq; +typedef enum { SCSI_HD, SCSI_CD } SCSIDriveKind; + struct SCSIDiskState { SCSIDevice qdev; @@ -78,32 +74,30 @@ struct SCSIDiskState char *version; char *serial; SCSISense sense; + SCSIDriveKind drive_kind; }; static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type); static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf); -static SCSIDiskReq *scsi_new_request(SCSIDiskState *s, uint32_t tag, +static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun) { + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); SCSIRequest *req; SCSIDiskReq *r; req = scsi_req_alloc(sizeof(SCSIDiskReq), &s->qdev, tag, lun); r = DO_UPCAST(SCSIDiskReq, req, req); r->iov.iov_base = qemu_blockalign(s->bs, SCSI_DMA_BUF_SIZE); - return r; + return req; } -static void scsi_remove_request(SCSIDiskReq *r) +static void scsi_free_request(SCSIRequest *req) { - qemu_vfree(r->iov.iov_base); - scsi_req_free(&r->req); -} + SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); -static SCSIDiskReq *scsi_find_request(SCSIDiskState *s, uint32_t tag) -{ - return DO_UPCAST(SCSIDiskReq, req, scsi_req_find(&s->qdev, tag)); + qemu_vfree(r->iov.iov_base); } static void scsi_disk_clear_sense(SCSIDiskState *s) @@ -111,42 +105,33 @@ static void scsi_disk_clear_sense(SCSIDiskState *s) memset(&s->sense, 0, sizeof(s->sense)); } -static void scsi_disk_set_sense(SCSIDiskState *s, uint8_t key) -{ - s->sense.key = key; -} - -static void scsi_req_set_status(SCSIDiskReq *r, int status, int sense_code) +static void scsi_req_set_status(SCSIDiskReq *r, int status, SCSISense sense) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); r->req.status = status; - scsi_disk_set_sense(s, sense_code); + s->sense = sense; } /* Helper function for command completion. */ -static void scsi_command_complete(SCSIDiskReq *r, int status, int sense) +static void scsi_command_complete(SCSIDiskReq *r, int status, SCSISense sense) { - DPRINTF("Command complete tag=0x%x status=%d sense=%d\n", - r->req.tag, status, sense); + DPRINTF("Command complete tag=0x%x status=%d sense=%d/%d/%d\n", + r->req.tag, status, sense.key, sense.asc, sense.ascq); scsi_req_set_status(r, status, sense); scsi_req_complete(&r->req); - scsi_remove_request(r); } /* Cancel a pending data transfer. */ -static void scsi_cancel_io(SCSIDevice *d, uint32_t tag) +static void scsi_cancel_io(SCSIRequest *req) { - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - SCSIDiskReq *r; - DPRINTF("Cancel tag=0x%x\n", tag); - r = scsi_find_request(s, tag); - if (r) { - if (r->req.aiocb) - bdrv_aio_cancel(r->req.aiocb); - r->req.aiocb = NULL; - scsi_remove_request(r); + SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); + + DPRINTF("Cancel tag=0x%x\n", req->tag); + if (r->req.aiocb) { + bdrv_aio_cancel(r->req.aiocb); } + r->req.aiocb = NULL; } static void scsi_read_complete(void * opaque, int ret) @@ -167,30 +152,38 @@ static void scsi_read_complete(void * opaque, int ret) n = r->iov.iov_len / 512; r->sector += n; r->sector_count -= n; - r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, r->iov.iov_len); + scsi_req_data(&r->req, r->iov.iov_len); } -static void scsi_read_request(SCSIDiskReq *r) +/* Read more data from scsi device into buffer. */ +static void scsi_read_data(SCSIRequest *req) { + SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); uint32_t n; if (r->sector_count == (uint32_t)-1) { DPRINTF("Read buf_len=%zd\n", r->iov.iov_len); r->sector_count = 0; - r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, r->iov.iov_len); + scsi_req_data(&r->req, r->iov.iov_len); return; } DPRINTF("Read sector_count=%d\n", r->sector_count); if (r->sector_count == 0) { - scsi_command_complete(r, GOOD, NO_SENSE); + scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE)); return; } /* No data transfer may already be in progress */ assert(r->req.aiocb == NULL); + if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { + DPRINTF("Data transfer direction invalid\n"); + scsi_read_complete(r, -EINVAL); + return; + } + n = r->sector_count; if (n > SCSI_DMA_BUF_SIZE / 512) n = SCSI_DMA_BUF_SIZE / 512; @@ -204,23 +197,6 @@ static void scsi_read_request(SCSIDiskReq *r) } } -/* Read more data from scsi device into buffer. */ -static void scsi_read_data(SCSIDevice *d, uint32_t tag) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - SCSIDiskReq *r; - - r = scsi_find_request(s, tag); - if (!r) { - BADF("Bad read tag 0x%x\n", tag); - /* ??? This is the wrong error. */ - scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR); - return; - } - - scsi_read_request(r); -} - static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type) { int is_read = (type == SCSI_REQ_STATUS_RETRY_READ); @@ -242,13 +218,24 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type) vm_stop(VMSTOP_DISKFULL); } else { if (type == SCSI_REQ_STATUS_RETRY_READ) { - r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, 0); + scsi_req_data(&r->req, 0); + } + switch (error) { + case ENOMEM: + scsi_command_complete(r, CHECK_CONDITION, + SENSE_CODE(TARGET_FAILURE)); + break; + case EINVAL: + scsi_command_complete(r, CHECK_CONDITION, + SENSE_CODE(INVALID_FIELD)); + break; + default: + scsi_command_complete(r, CHECK_CONDITION, + SENSE_CODE(IO_ERROR)); + break; } - scsi_command_complete(r, CHECK_CONDITION, - HARDWARE_ERROR); bdrv_mon_event(s->bs, BDRV_ACTION_REPORT, is_read); } - return 1; } @@ -270,7 +257,7 @@ static void scsi_write_complete(void * opaque, int ret) r->sector += n; r->sector_count -= n; if (r->sector_count == 0) { - scsi_command_complete(r, GOOD, NO_SENSE); + scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE)); } else { len = r->sector_count * 512; if (len > SCSI_DMA_BUF_SIZE) { @@ -278,25 +265,32 @@ static void scsi_write_complete(void * opaque, int ret) } r->iov.iov_len = len; DPRINTF("Write complete tag=0x%x more=%d\n", r->req.tag, len); - r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, len); + scsi_req_data(&r->req, len); } } -static void scsi_write_request(SCSIDiskReq *r) +static void scsi_write_data(SCSIRequest *req) { + SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); uint32_t n; /* No data transfer may already be in progress */ assert(r->req.aiocb == NULL); + if (r->req.cmd.mode != SCSI_XFER_TO_DEV) { + DPRINTF("Data transfer direction invalid\n"); + scsi_write_complete(r, -EINVAL); + return; + } + n = r->iov.iov_len / 512; if (n) { qemu_iovec_init_external(&r->qiov, &r->iov, 1); r->req.aiocb = bdrv_aio_writev(s->bs, r->sector, &r->qiov, n, scsi_write_complete, r); if (r->req.aiocb == NULL) { - scsi_write_complete(r, -EIO); + scsi_write_complete(r, -ENOMEM); } } else { /* Invoke completion routine to fetch data from host. */ @@ -304,26 +298,6 @@ static void scsi_write_request(SCSIDiskReq *r) } } -/* Write data to a scsi device. Returns nonzero on failure. - The transfer may complete asynchronously. */ -static int scsi_write_data(SCSIDevice *d, uint32_t tag) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - SCSIDiskReq *r; - - DPRINTF("Write data tag=0x%x\n", tag); - r = scsi_find_request(s, tag); - if (!r) { - BADF("Bad write tag 0x%x\n", tag); - scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR); - return 1; - } - - scsi_write_request(r); - - return 0; -} - static void scsi_dma_restart_bh(void *opaque) { SCSIDiskState *s = opaque; @@ -344,15 +318,15 @@ static void scsi_dma_restart_bh(void *opaque) switch (status & SCSI_REQ_STATUS_RETRY_TYPE_MASK) { case SCSI_REQ_STATUS_RETRY_READ: - scsi_read_request(r); + scsi_read_data(&r->req); break; case SCSI_REQ_STATUS_RETRY_WRITE: - scsi_write_request(r); + scsi_write_data(&r->req); break; case SCSI_REQ_STATUS_RETRY_FLUSH: ret = scsi_disk_emulate_command(r, r->iov.iov_base); if (ret == 0) { - scsi_command_complete(r, GOOD, NO_SENSE); + scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE)); } } } @@ -373,19 +347,21 @@ static void scsi_dma_restart_cb(void *opaque, int running, int reason) } /* Return a pointer to the data buffer. */ -static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag) +static uint8_t *scsi_get_buf(SCSIRequest *req) { - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - SCSIDiskReq *r; + SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); - r = scsi_find_request(s, tag); - if (!r) { - BADF("Bad buffer tag 0x%x\n", tag); - return NULL; - } return (uint8_t *)r->iov.iov_base; } +/* Copy sense information into the provided buffer */ +static int scsi_get_sense(SCSIRequest *req, uint8_t *outbuf, int len) +{ + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); + + return scsi_build_sense(s->sense, outbuf, len, len > 14); +} + static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); @@ -406,7 +382,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) return -1; } - if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM) { + if (s->drive_kind == SCSI_CD) { outbuf[buflen++] = 5; } else { outbuf[buflen++] = 0; @@ -424,7 +400,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) outbuf[buflen++] = 0x00; // list of supported pages (this page) outbuf[buflen++] = 0x80; // unit serial number outbuf[buflen++] = 0x83; // device identification - if (bdrv_get_type_hint(s->bs) != BDRV_TYPE_CDROM) { + if (s->drive_kind == SCSI_HD) { outbuf[buflen++] = 0xb0; // block limits outbuf[buflen++] = 0xb2; // thin provisioning } @@ -477,7 +453,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) unsigned int opt_io_size = s->qdev.conf.opt_io_size / s->qdev.blocksize; - if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM) { + if (s->drive_kind == SCSI_CD) { DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n", page_code); return -1; @@ -542,12 +518,12 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) memset(outbuf, 0, buflen); - if (req->lun || req->cmd.buf[1] >> 5) { + if (req->lun) { outbuf[0] = 0x7f; /* LUN not supported */ return buflen; } - if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM) { + if (s->drive_kind == SCSI_CD) { outbuf[0] = 5; outbuf[1] = 0x80; memcpy(&outbuf[16], "QEMU CD-ROM ", 16); @@ -678,7 +654,7 @@ static int mode_sense_page(SCSIRequest *req, int page, uint8_t *p, return p[1] + 2; case 0x2a: /* CD Capabilities and Mechanical Status page. */ - if (bdrv_get_type_hint(bdrv) != BDRV_TYPE_CDROM) + if (s->drive_kind != SCSI_CD) return 0; p[0] = 0x2a; p[1] = 0x14; @@ -857,19 +833,8 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf) case REQUEST_SENSE: if (req->cmd.xfer < 4) goto illegal_request; - memset(outbuf, 0, 4); - buflen = 4; - if (s->sense.key == NOT_READY && req->cmd.xfer >= 18) { - memset(outbuf, 0, 18); - buflen = 18; - outbuf[7] = 10; - /* asc 0x3a, ascq 0: Medium not present */ - outbuf[12] = 0x3a; - outbuf[13] = 0; - } - outbuf[0] = 0xf0; - outbuf[1] = 0; - outbuf[2] = s->sense.key; + buflen = scsi_build_sense(s->sense, outbuf, req->cmd.xfer, + req->cmd.xfer > 13); scsi_disk_clear_sense(s); break; case INQUIRY: @@ -905,7 +870,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf) goto illegal_request; break; case START_STOP: - if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM && (req->cmd.buf[4] & 2)) { + if (s->drive_kind == SCSI_CD && (req->cmd.buf[4] & 2)) { /* load/eject medium */ bdrv_eject(s->bs, !(req->cmd.buf[4] & 1)); } @@ -1007,17 +972,22 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf) } break; default: - goto illegal_request; + scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE)); + return -1; } - scsi_req_set_status(r, GOOD, NO_SENSE); + scsi_req_set_status(r, GOOD, SENSE_CODE(NO_SENSE)); return buflen; not_ready: - scsi_command_complete(r, CHECK_CONDITION, NOT_READY); + if (!bdrv_is_inserted(s->bs)) { + scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(NO_MEDIUM)); + } else { + scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(LUN_NOT_READY)); + } return -1; illegal_request: - scsi_command_complete(r, CHECK_CONDITION, ILLEGAL_REQUEST); + scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_FIELD)); return -1; } @@ -1026,33 +996,23 @@ illegal_request: (eg. disk reads), negative for transfers to the device (eg. disk writes), and zero if the command does not transfer any data. */ -static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, - uint8_t *buf, int lun) +static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) { - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - uint32_t len; - int is_write; + SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); + int32_t len; uint8_t command; uint8_t *outbuf; - SCSIDiskReq *r; int rc; command = buf[0]; - r = scsi_find_request(s, tag); - if (r) { - BADF("Tag 0x%x already in use\n", tag); - scsi_cancel_io(d, tag); - } - /* ??? Tags are not unique for different luns. We only implement a - single lun, so this should not matter. */ - r = scsi_new_request(s, tag, lun); outbuf = (uint8_t *)r->iov.iov_base; - is_write = 0; DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", lun, tag, buf[0]); if (scsi_req_parse(&r->req, buf) != 0) { BADF("Unsupported command length, command %x\n", command); - goto fail; + scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE)); + return 0; } #ifdef DEBUG_SCSI { @@ -1064,11 +1024,14 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, } #endif - if (lun || buf[1] >> 5) { + if (req->lun) { /* Only LUN 0 supported. */ - DPRINTF("Unimplemented LUN %d\n", lun ? lun : buf[1] >> 5); - if (command != REQUEST_SENSE && command != INQUIRY) - goto fail; + DPRINTF("Unimplemented LUN %d\n", req->lun); + if (command != REQUEST_SENSE && command != INQUIRY) { + scsi_command_complete(r, CHECK_CONDITION, + SENSE_CODE(LUN_NOT_SUPPORTED)); + return 0; + } } switch (command) { case TEST_UNIT_READY: @@ -1101,7 +1064,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, case READ_10: case READ_12: case READ_16: - len = r->req.cmd.xfer / d->blocksize; + len = r->req.cmd.xfer / s->qdev.blocksize; DPRINTF("Read (sector %" PRId64 ", count %d)\n", r->req.cmd.lba, len); if (r->req.cmd.lba > s->max_lba) goto illegal_lba; @@ -1115,7 +1078,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, case WRITE_VERIFY: case WRITE_VERIFY_12: case WRITE_VERIFY_16: - len = r->req.cmd.xfer / d->blocksize; + len = r->req.cmd.xfer / s->qdev.blocksize; DPRINTF("Write %s(sector %" PRId64 ", count %d)\n", (command & 0xe) == 0xe ? "And Verify " : "", r->req.cmd.lba, len); @@ -1123,7 +1086,6 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, goto illegal_lba; r->sector = r->req.cmd.lba * s->cluster_size; r->sector_count = len * s->cluster_size; - is_write = 1; break; case MODE_SELECT: DPRINTF("Mode Select(6) (len %lu)\n", (long)r->req.cmd.xfer); @@ -1150,7 +1112,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, } break; case WRITE_SAME_16: - len = r->req.cmd.xfer / d->blocksize; + len = r->req.cmd.xfer / s->qdev.blocksize; DPRINTF("WRITE SAME(16) (sector %" PRId64 ", count %d)\n", r->req.cmd.lba, len); @@ -1176,18 +1138,20 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, break; default: DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]); + scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE)); + return 0; fail: - scsi_command_complete(r, CHECK_CONDITION, ILLEGAL_REQUEST); + scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_FIELD)); return 0; illegal_lba: - scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR); + scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(LBA_OUT_OF_RANGE)); return 0; } if (r->sector_count == 0 && r->iov.iov_len == 0) { - scsi_command_complete(r, GOOD, NO_SENSE); + scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE)); } len = r->sector_count * 512 + r->iov.iov_len; - if (is_write) { + if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { return -len; } else { if (!r->sector_count) @@ -1196,25 +1160,12 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, } } -static void scsi_disk_purge_requests(SCSIDiskState *s) -{ - SCSIDiskReq *r; - - while (!QTAILQ_EMPTY(&s->qdev.requests)) { - r = DO_UPCAST(SCSIDiskReq, req, QTAILQ_FIRST(&s->qdev.requests)); - if (r->req.aiocb) { - bdrv_aio_cancel(r->req.aiocb); - } - scsi_remove_request(r); - } -} - static void scsi_disk_reset(DeviceState *dev) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev.qdev, dev); uint64_t nb_sectors; - scsi_disk_purge_requests(s); + scsi_device_purge_requests(&s->qdev); bdrv_get_geometry(s->bs, &nb_sectors); nb_sectors /= s->cluster_size; @@ -1228,14 +1179,13 @@ static void scsi_destroy(SCSIDevice *dev) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); - scsi_disk_purge_requests(s); + scsi_device_purge_requests(&s->qdev); blockdev_mark_auto_del(s->qdev.conf.bs); } -static int scsi_disk_initfn(SCSIDevice *dev) +static int scsi_initfn(SCSIDevice *dev, SCSIDriveKind kind) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); - int is_cd; DriveInfo *dinfo; if (!s->qdev.conf.bs) { @@ -1243,9 +1193,9 @@ static int scsi_disk_initfn(SCSIDevice *dev) return -1; } s->bs = s->qdev.conf.bs; - is_cd = bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM; + s->drive_kind = kind; - if (!is_cd && !bdrv_is_inserted(s->bs)) { + if (kind == SCSI_HD && !bdrv_is_inserted(s->bs)) { error_report("Device needs media, but drive is empty"); return -1; } @@ -1265,7 +1215,7 @@ static int scsi_disk_initfn(SCSIDevice *dev) return -1; } - if (is_cd) { + if (kind == SCSI_CD) { s->qdev.blocksize = 2048; } else { s->qdev.blocksize = s->qdev.conf.logical_block_size; @@ -1275,35 +1225,113 @@ static int scsi_disk_initfn(SCSIDevice *dev) s->qdev.type = TYPE_DISK; qemu_add_vm_change_state_handler(scsi_dma_restart_cb, s); - bdrv_set_removable(s->bs, is_cd); + bdrv_set_removable(s->bs, kind == SCSI_CD); add_boot_device_path(s->qdev.conf.bootindex, &dev->qdev, ",0"); return 0; } -static SCSIDeviceInfo scsi_disk_info = { - .qdev.name = "scsi-disk", - .qdev.fw_name = "disk", - .qdev.desc = "virtual scsi disk or cdrom", - .qdev.size = sizeof(SCSIDiskState), - .qdev.reset = scsi_disk_reset, - .init = scsi_disk_initfn, - .destroy = scsi_destroy, - .send_command = scsi_send_command, - .read_data = scsi_read_data, - .write_data = scsi_write_data, - .cancel_io = scsi_cancel_io, - .get_buf = scsi_get_buf, - .qdev.props = (Property[]) { - DEFINE_BLOCK_PROPERTIES(SCSIDiskState, qdev.conf), - DEFINE_PROP_STRING("ver", SCSIDiskState, version), - DEFINE_PROP_STRING("serial", SCSIDiskState, serial), - DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false), - DEFINE_PROP_END_OF_LIST(), - }, +static int scsi_hd_initfn(SCSIDevice *dev) +{ + return scsi_initfn(dev, SCSI_HD); +} + +static int scsi_cd_initfn(SCSIDevice *dev) +{ + return scsi_initfn(dev, SCSI_CD); +} + +static int scsi_disk_initfn(SCSIDevice *dev) +{ + SCSIDriveKind kind; + DriveInfo *dinfo; + + if (!dev->conf.bs) { + kind = SCSI_HD; /* will die in scsi_initfn() */ + } else { + dinfo = drive_get_by_blockdev(dev->conf.bs); + kind = dinfo->media_cd ? SCSI_CD : SCSI_HD; + } + + return scsi_initfn(dev, kind); +} + +#define DEFINE_SCSI_DISK_PROPERTIES() \ + DEFINE_BLOCK_PROPERTIES(SCSIDiskState, qdev.conf), \ + DEFINE_PROP_STRING("ver", SCSIDiskState, version), \ + DEFINE_PROP_STRING("serial", SCSIDiskState, serial) + +static SCSIDeviceInfo scsi_disk_info[] = { + { + .qdev.name = "scsi-hd", + .qdev.fw_name = "disk", + .qdev.desc = "virtual SCSI disk", + .qdev.size = sizeof(SCSIDiskState), + .qdev.reset = scsi_disk_reset, + .init = scsi_hd_initfn, + .destroy = scsi_destroy, + .alloc_req = scsi_new_request, + .free_req = scsi_free_request, + .send_command = scsi_send_command, + .read_data = scsi_read_data, + .write_data = scsi_write_data, + .cancel_io = scsi_cancel_io, + .get_buf = scsi_get_buf, + .get_sense = scsi_get_sense, + .qdev.props = (Property[]) { + DEFINE_SCSI_DISK_PROPERTIES(), + DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false), + DEFINE_PROP_END_OF_LIST(), + } + },{ + .qdev.name = "scsi-cd", + .qdev.fw_name = "disk", + .qdev.desc = "virtual SCSI CD-ROM", + .qdev.size = sizeof(SCSIDiskState), + .qdev.reset = scsi_disk_reset, + .init = scsi_cd_initfn, + .destroy = scsi_destroy, + .alloc_req = scsi_new_request, + .free_req = scsi_free_request, + .send_command = scsi_send_command, + .read_data = scsi_read_data, + .write_data = scsi_write_data, + .cancel_io = scsi_cancel_io, + .get_buf = scsi_get_buf, + .get_sense = scsi_get_sense, + .qdev.props = (Property[]) { + DEFINE_SCSI_DISK_PROPERTIES(), + DEFINE_PROP_END_OF_LIST(), + }, + },{ + .qdev.name = "scsi-disk", /* legacy -device scsi-disk */ + .qdev.fw_name = "disk", + .qdev.desc = "virtual SCSI disk or CD-ROM (legacy)", + .qdev.size = sizeof(SCSIDiskState), + .qdev.reset = scsi_disk_reset, + .init = scsi_disk_initfn, + .destroy = scsi_destroy, + .alloc_req = scsi_new_request, + .free_req = scsi_free_request, + .send_command = scsi_send_command, + .read_data = scsi_read_data, + .write_data = scsi_write_data, + .cancel_io = scsi_cancel_io, + .get_buf = scsi_get_buf, + .get_sense = scsi_get_sense, + .qdev.props = (Property[]) { + DEFINE_SCSI_DISK_PROPERTIES(), + DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false), + DEFINE_PROP_END_OF_LIST(), + } + } }; static void scsi_disk_register_devices(void) { - scsi_qdev_register(&scsi_disk_info); + int i; + + for (i = 0; i < ARRAY_SIZE(scsi_disk_info); i++) { + scsi_qdev_register(&scsi_disk_info[i]); + } } device_init(scsi_disk_register_devices) diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c index 9be1cca4c3..8e59c7ee89 100644 --- a/hw/scsi-generic.c +++ b/hw/scsi-generic.c @@ -66,23 +66,49 @@ struct SCSIGenericState uint8_t senselen; }; -static SCSIGenericReq *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun) +static void scsi_set_sense(SCSIGenericState *s, SCSISense sense) { - SCSIRequest *req; + s->senselen = scsi_build_sense(sense, s->sensebuf, SCSI_SENSE_BUF_SIZE, 0); + s->driver_status = SG_ERR_DRIVER_SENSE; +} - req = scsi_req_alloc(sizeof(SCSIGenericReq), d, tag, lun); - return DO_UPCAST(SCSIGenericReq, req, req); +static void scsi_clear_sense(SCSIGenericState *s) +{ + memset(s->sensebuf, 0, SCSI_SENSE_BUF_SIZE); + s->senselen = 0; + s->driver_status = 0; } -static void scsi_remove_request(SCSIGenericReq *r) +static int scsi_get_sense(SCSIRequest *req, uint8_t *outbuf, int len) { - qemu_free(r->buf); - scsi_req_free(&r->req); + SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev); + int size = SCSI_SENSE_BUF_SIZE; + + if (!(s->driver_status & SG_ERR_DRIVER_SENSE)) { + size = scsi_build_sense(SENSE_CODE(NO_SENSE), s->sensebuf, + SCSI_SENSE_BUF_SIZE, 0); + } + if (size > len) { + size = len; + } + memcpy(outbuf, s->sensebuf, size); + + return size; } -static SCSIGenericReq *scsi_find_request(SCSIGenericState *s, uint32_t tag) +static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun) { - return DO_UPCAST(SCSIGenericReq, req, scsi_req_find(&s->qdev, tag)); + SCSIRequest *req; + + req = scsi_req_alloc(sizeof(SCSIGenericReq), d, tag, lun); + return req; +} + +static void scsi_free_request(SCSIRequest *req) +{ + SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); + + qemu_free(r->buf); } /* Helper function for command completion. */ @@ -91,13 +117,30 @@ static void scsi_command_complete(void *opaque, int ret) SCSIGenericReq *r = (SCSIGenericReq *)opaque; SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev); + r->req.aiocb = NULL; s->driver_status = r->io_header.driver_status; if (s->driver_status & SG_ERR_DRIVER_SENSE) s->senselen = r->io_header.sb_len_wr; - if (ret != 0) - r->req.status = BUSY; - else { + if (ret != 0) { + switch (ret) { + case -EDOM: + r->req.status = TASK_SET_FULL; + break; + case -EINVAL: + r->req.status = CHECK_CONDITION; + scsi_set_sense(s, SENSE_CODE(INVALID_FIELD)); + break; + case -ENOMEM: + r->req.status = CHECK_CONDITION; + scsi_set_sense(s, SENSE_CODE(TARGET_FAILURE)); + break; + default: + r->req.status = CHECK_CONDITION; + scsi_set_sense(s, SENSE_CODE(IO_ERROR)); + break; + } + } else { if (s->driver_status & SG_ERR_DRIVER_TIMEOUT) { r->req.status = BUSY; BADF("Driver Timeout\n"); @@ -112,23 +155,18 @@ static void scsi_command_complete(void *opaque, int ret) r, r->req.tag, r->req.status); scsi_req_complete(&r->req); - scsi_remove_request(r); } /* Cancel a pending data transfer. */ -static void scsi_cancel_io(SCSIDevice *d, uint32_t tag) +static void scsi_cancel_io(SCSIRequest *req) { - DPRINTF("scsi_cancel_io 0x%x\n", tag); - SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d); - SCSIGenericReq *r; - DPRINTF("Cancel tag=0x%x\n", tag); - r = scsi_find_request(s, tag); - if (r) { - if (r->req.aiocb) - bdrv_aio_cancel(r->req.aiocb); - r->req.aiocb = NULL; - scsi_remove_request(r); + SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); + + DPRINTF("Cancel tag=0x%x\n", req->tag); + if (r->req.aiocb) { + bdrv_aio_cancel(r->req.aiocb); } + r->req.aiocb = NULL; } static int execute_command(BlockDriverState *bdrv, @@ -152,7 +190,7 @@ static int execute_command(BlockDriverState *bdrv, r->req.aiocb = bdrv_aio_ioctl(bdrv, SG_IO, &r->io_header, complete, r); if (r->req.aiocb == NULL) { BADF("execute_command: read failed !\n"); - return -1; + return -ENOMEM; } return 0; @@ -163,6 +201,7 @@ static void scsi_read_complete(void * opaque, int ret) SCSIGenericReq *r = (SCSIGenericReq *)opaque; int len; + r->req.aiocb = NULL; if (ret) { DPRINTF("IO error ret %d\n", ret); scsi_command_complete(r, ret); @@ -172,27 +211,21 @@ static void scsi_read_complete(void * opaque, int ret) DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, len); r->len = -1; - r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, len); - if (len == 0) + if (len == 0) { scsi_command_complete(r, 0); + } else { + scsi_req_data(&r->req, len); + } } /* Read more data from scsi device into buffer. */ -static void scsi_read_data(SCSIDevice *d, uint32_t tag) +static void scsi_read_data(SCSIRequest *req) { - SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d); - SCSIGenericReq *r; + SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); + SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev); int ret; - DPRINTF("scsi_read_data 0x%x\n", tag); - r = scsi_find_request(s, tag); - if (!r) { - BADF("Bad read tag 0x%x\n", tag); - /* ??? This is the wrong error. */ - scsi_command_complete(r, -EINVAL); - return; - } - + DPRINTF("scsi_read_data 0x%x\n", req->tag); if (r->len == -1) { scsi_command_complete(r, 0); return; @@ -210,13 +243,15 @@ static void scsi_read_data(SCSIDevice *d, uint32_t tag) DPRINTF("Sense: %d %d %d %d %d %d %d %d\n", r->buf[0], r->buf[1], r->buf[2], r->buf[3], r->buf[4], r->buf[5], r->buf[6], r->buf[7]); - r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, s->senselen); + scsi_req_data(&r->req, s->senselen); + /* Clear sensebuf after REQUEST_SENSE */ + scsi_clear_sense(s); return; } ret = execute_command(s->bs, r, SG_DXFER_FROM_DEV, scsi_read_complete); - if (ret == -1) { - scsi_command_complete(r, -EINVAL); + if (ret < 0) { + scsi_command_complete(r, ret); return; } } @@ -227,6 +262,7 @@ static void scsi_write_complete(void * opaque, int ret) SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev); DPRINTF("scsi_write_complete() ret = %d\n", ret); + r->req.aiocb = NULL; if (ret) { DPRINTF("IO error\n"); scsi_command_complete(r, ret); @@ -244,46 +280,30 @@ static void scsi_write_complete(void * opaque, int ret) /* Write data to a scsi device. Returns nonzero on failure. The transfer may complete asynchronously. */ -static int scsi_write_data(SCSIDevice *d, uint32_t tag) +static void scsi_write_data(SCSIRequest *req) { - SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d); - SCSIGenericReq *r; + SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev); + SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); int ret; - DPRINTF("scsi_write_data 0x%x\n", tag); - r = scsi_find_request(s, tag); - if (!r) { - BADF("Bad write tag 0x%x\n", tag); - /* ??? This is the wrong error. */ - scsi_command_complete(r, -EINVAL); - return 0; - } - + DPRINTF("scsi_write_data 0x%x\n", req->tag); if (r->len == 0) { r->len = r->buflen; - r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, r->len); - return 0; + scsi_req_data(&r->req, r->len); + return; } ret = execute_command(s->bs, r, SG_DXFER_TO_DEV, scsi_write_complete); - if (ret == -1) { - scsi_command_complete(r, -EINVAL); - return 1; + if (ret < 0) { + scsi_command_complete(r, ret); } - - return 0; } /* Return a pointer to the data buffer. */ -static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag) +static uint8_t *scsi_get_buf(SCSIRequest *req) { - SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d); - SCSIGenericReq *r; - r = scsi_find_request(s, tag); - if (!r) { - BADF("Bad buffer tag 0x%x\n", tag); - return NULL; - } + SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); + return r->buf; } @@ -311,42 +331,23 @@ static void scsi_req_fixup(SCSIRequest *req) (eg. disk reads), negative for transfers to the device (eg. disk writes), and zero if the command does not transfer any data. */ -static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, - uint8_t *cmd, int lun) +static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd) { - SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d); - SCSIGenericReq *r; - SCSIBus *bus; + SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev); + SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); int ret; - if (cmd[0] != REQUEST_SENSE && - (lun != s->lun || (cmd[1] >> 5) != s->lun)) { - DPRINTF("Unimplemented LUN %d\n", lun ? lun : cmd[1] >> 5); - - s->sensebuf[0] = 0x70; - s->sensebuf[1] = 0x00; - s->sensebuf[2] = ILLEGAL_REQUEST; - s->sensebuf[3] = 0x00; - s->sensebuf[4] = 0x00; - s->sensebuf[5] = 0x00; - s->sensebuf[6] = 0x00; - s->senselen = 7; - s->driver_status = SG_ERR_DRIVER_SENSE; - bus = scsi_bus_from_device(d); - bus->complete(bus, SCSI_REASON_DONE, tag, CHECK_CONDITION); + if (cmd[0] != REQUEST_SENSE && req->lun != s->lun) { + DPRINTF("Unimplemented LUN %d\n", req->lun); + scsi_set_sense(s, SENSE_CODE(LUN_NOT_SUPPORTED)); + r->req.status = CHECK_CONDITION; + scsi_req_complete(&r->req); return 0; } - r = scsi_find_request(s, tag); - if (r) { - BADF("Tag 0x%x already in use %p\n", tag, r); - scsi_cancel_io(d, tag); - } - r = scsi_new_request(d, tag, lun); - if (-1 == scsi_req_parse(&r->req, cmd)) { BADF("Unsupported command length, command %x\n", cmd[0]); - scsi_remove_request(r); + scsi_command_complete(r, -EINVAL); return 0; } scsi_req_fixup(&r->req); @@ -370,8 +371,8 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, r->buflen = 0; r->buf = NULL; ret = execute_command(s->bs, r, SG_DXFER_NONE, scsi_command_complete); - if (ret == -1) { - scsi_command_complete(r, -EINVAL); + if (ret < 0) { + scsi_command_complete(r, ret); return 0; } return 0; @@ -389,9 +390,9 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { r->len = 0; return -r->req.cmd.xfer; + } else { + return r->req.cmd.xfer; } - - return r->req.cmd.xfer; } static int get_blocksize(BlockDriverState *bdrv) @@ -455,31 +456,18 @@ static int get_stream_blocksize(BlockDriverState *bdrv) return (buf[9] << 16) | (buf[10] << 8) | buf[11]; } -static void scsi_generic_purge_requests(SCSIGenericState *s) -{ - SCSIGenericReq *r; - - while (!QTAILQ_EMPTY(&s->qdev.requests)) { - r = DO_UPCAST(SCSIGenericReq, req, QTAILQ_FIRST(&s->qdev.requests)); - if (r->req.aiocb) { - bdrv_aio_cancel(r->req.aiocb); - } - scsi_remove_request(r); - } -} - static void scsi_generic_reset(DeviceState *dev) { SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev.qdev, dev); - scsi_generic_purge_requests(s); + scsi_device_purge_requests(&s->qdev); } static void scsi_destroy(SCSIDevice *d) { SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d); - scsi_generic_purge_requests(s); + scsi_device_purge_requests(&s->qdev); blockdev_mark_auto_del(s->qdev.conf.bs); } @@ -556,11 +544,14 @@ static SCSIDeviceInfo scsi_generic_info = { .qdev.reset = scsi_generic_reset, .init = scsi_generic_initfn, .destroy = scsi_destroy, + .alloc_req = scsi_new_request, + .free_req = scsi_free_request, .send_command = scsi_send_command, .read_data = scsi_read_data, .write_data = scsi_write_data, .cancel_io = scsi_cancel_io, .get_buf = scsi_get_buf, + .get_sense = scsi_get_sense, .qdev.props = (Property[]) { DEFINE_BLOCK_PROPERTIES(SCSIGenericState, qdev.conf), DEFINE_PROP_END_OF_LIST(), @@ -9,17 +9,11 @@ #define SCSI_CMD_BUF_SIZE 16 -/* scsi-disk.c */ -enum scsi_reason { - SCSI_REASON_DONE, /* Command complete. */ - SCSI_REASON_DATA /* Transfer complete, more data required. */ -}; - typedef struct SCSIBus SCSIBus; +typedef struct SCSIBusOps SCSIBusOps; typedef struct SCSIDevice SCSIDevice; typedef struct SCSIDeviceInfo SCSIDeviceInfo; -typedef void (*scsi_completionfn)(SCSIBus *bus, int reason, uint32_t tag, - uint32_t arg); +typedef struct SCSIRequest SCSIRequest; enum SCSIXferMode { SCSI_XFER_NONE, /* TEST_UNIT_READY, ... */ @@ -27,9 +21,16 @@ enum SCSIXferMode { SCSI_XFER_TO_DEV, /* WRITE, MODE_SELECT, ... */ }; -typedef struct SCSIRequest { +typedef struct SCSISense { + uint8_t key; + uint8_t asc; + uint8_t ascq; +} SCSISense; + +struct SCSIRequest { SCSIBus *bus; SCSIDevice *dev; + uint32_t refcount; uint32_t tag; uint32_t lun; uint32_t status; @@ -43,7 +44,7 @@ typedef struct SCSIRequest { BlockDriverAIOCB *aiocb; bool enqueued; QTAILQ_ENTRY(SCSIRequest) next; -} SCSIRequest; +}; struct SCSIDevice { @@ -66,28 +67,34 @@ struct SCSIDeviceInfo { DeviceInfo qdev; scsi_qdev_initfn init; void (*destroy)(SCSIDevice *s); - int32_t (*send_command)(SCSIDevice *s, uint32_t tag, uint8_t *buf, - int lun); - void (*read_data)(SCSIDevice *s, uint32_t tag); - int (*write_data)(SCSIDevice *s, uint32_t tag); - void (*cancel_io)(SCSIDevice *s, uint32_t tag); - uint8_t *(*get_buf)(SCSIDevice *s, uint32_t tag); + SCSIRequest *(*alloc_req)(SCSIDevice *s, uint32_t tag, uint32_t lun); + void (*free_req)(SCSIRequest *req); + int32_t (*send_command)(SCSIRequest *req, uint8_t *buf); + void (*read_data)(SCSIRequest *req); + void (*write_data)(SCSIRequest *req); + void (*cancel_io)(SCSIRequest *req); + uint8_t *(*get_buf)(SCSIRequest *req); + int (*get_sense)(SCSIRequest *req, uint8_t *buf, int len); +}; + +struct SCSIBusOps { + void (*transfer_data)(SCSIRequest *req, uint32_t arg); + void (*complete)(SCSIRequest *req, uint32_t arg); + void (*cancel)(SCSIRequest *req); }; -typedef void (*SCSIAttachFn)(DeviceState *host, BlockDriverState *bdrv, - int unit); struct SCSIBus { BusState qbus; int busnr; int tcq, ndev; - scsi_completionfn complete; + const SCSIBusOps *ops; SCSIDevice *devs[MAX_SCSI_DEVS]; }; void scsi_bus_new(SCSIBus *bus, DeviceState *host, int tcq, int ndev, - scsi_completionfn complete); + const SCSIBusOps *ops); void scsi_qdev_register(SCSIDeviceInfo *info); static inline SCSIBus *scsi_bus_from_device(SCSIDevice *d) @@ -99,12 +106,54 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv, int unit, bool removable); int scsi_bus_legacy_handle_cmdline(SCSIBus *bus); +/* + * Predefined sense codes + */ + +/* No sense data available */ +extern const struct SCSISense sense_code_NO_SENSE; +/* LUN not ready, Manual intervention required */ +extern const struct SCSISense sense_code_LUN_NOT_READY; +/* LUN not ready, Medium not present */ +extern const struct SCSISense sense_code_NO_MEDIUM; +/* Hardware error, internal target failure */ +extern const struct SCSISense sense_code_TARGET_FAILURE; +/* Illegal request, invalid command operation code */ +extern const struct SCSISense sense_code_INVALID_OPCODE; +/* Illegal request, LBA out of range */ +extern const struct SCSISense sense_code_LBA_OUT_OF_RANGE; +/* Illegal request, Invalid field in CDB */ +extern const struct SCSISense sense_code_INVALID_FIELD; +/* Illegal request, LUN not supported */ +extern const struct SCSISense sense_code_LUN_NOT_SUPPORTED; +/* Command aborted, I/O process terminated */ +extern const struct SCSISense sense_code_IO_ERROR; +/* Command aborted, I_T Nexus loss occurred */ +extern const struct SCSISense sense_code_I_T_NEXUS_LOSS; +/* Command aborted, Logical Unit failure */ +extern const struct SCSISense sense_code_LUN_FAILURE; + +#define SENSE_CODE(x) sense_code_ ## x + +int scsi_build_sense(SCSISense sense, uint8_t *buf, int len, int fixed); +int scsi_sense_valid(SCSISense sense); + SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun); -SCSIRequest *scsi_req_find(SCSIDevice *d, uint32_t tag); +SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun); +int32_t scsi_req_enqueue(SCSIRequest *req, uint8_t *buf); void scsi_req_free(SCSIRequest *req); +SCSIRequest *scsi_req_ref(SCSIRequest *req); +void scsi_req_unref(SCSIRequest *req); int scsi_req_parse(SCSIRequest *req, uint8_t *buf); void scsi_req_print(SCSIRequest *req); +void scsi_req_continue(SCSIRequest *req); +void scsi_req_data(SCSIRequest *req, int len); void scsi_req_complete(SCSIRequest *req); +uint8_t *scsi_req_get_buf(SCSIRequest *req); +int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len); +void scsi_req_abort(SCSIRequest *req, int status); +void scsi_req_cancel(SCSIRequest *req); +void scsi_device_purge_requests(SCSIDevice *sdev); #endif @@ -1104,6 +1104,17 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, } break; + case 52: + case 53: + /* CMD52, CMD53: reserved for SDIO cards + * (see the SDIO Simplified Specification V2.0) + * Handle as illegal command but do not complain + * on stderr, as some OSes may use these in their + * probing for presence of an SDIO card. + */ + sd->card_status |= ILLEGAL_COMMAND; + return sd_r0; + /* Application specific commands (Class 8) */ case 55: /* CMD55: APP_CMD */ if (sd->rca != rca) diff --git a/hw/sga.c b/hw/sga.c new file mode 100644 index 0000000000..7ef750adf6 --- /dev/null +++ b/hw/sga.c @@ -0,0 +1,56 @@ +/* + * QEMU dummy ISA device for loading sgabios option rom. + * + * Copyright (c) 2011 Glauber Costa, Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * sgabios code originally available at code.google.com/p/sgabios + * + */ +#include "pci.h" +#include "pc.h" +#include "loader.h" +#include "sysemu.h" + +#define SGABIOS_FILENAME "sgabios.bin" + +typedef struct ISAGAState { + ISADevice dev; +} ISASGAState; + +static int isa_cirrus_vga_initfn(ISADevice *dev) +{ + rom_add_vga(SGABIOS_FILENAME); + return 0; +} + +static ISADeviceInfo sga_info = { + .qdev.name = "sga", + .qdev.desc = "Serial Graphics Adapter", + .qdev.size = sizeof(ISASGAState), + .init = isa_cirrus_vga_initfn, +}; + +static void sga_register(void) +{ + isa_qdev_register(&sga_info); +} + +device_init(sga_register); diff --git a/hw/sh7750_regs.h b/hw/sh7750_regs.h index 5a23a2ca20..6ec13ab6fe 100644 --- a/hw/sh7750_regs.h +++ b/hw/sh7750_regs.h @@ -23,9 +23,9 @@ * All register has 2 addresses: in 0xff000000 - 0xffffffff (P4 address) and * in 0x1f000000 - 0x1fffffff (area 7 address) */ -#define SH7750_P4_BASE 0xff000000 /* Accessable only in - priveleged mode */ -#define SH7750_A7_BASE 0x1f000000 /* Accessable only using TLB */ +#define SH7750_P4_BASE 0xff000000 /* Accessible only in + privileged mode */ +#define SH7750_A7_BASE 0x1f000000 /* Accessible only using TLB */ #define SH7750_P4_REG32(ofs) (SH7750_P4_BASE + (ofs)) #define SH7750_A7_REG32(ofs) (SH7750_A7_BASE + (ofs)) diff --git a/hw/spapr.c b/hw/spapr.c index 1782cc0a94..109b77459a 100644 --- a/hw/spapr.c +++ b/hw/spapr.c @@ -51,7 +51,7 @@ #define TIMEBASE_FREQ 512000000ULL -#define MAX_CPUS 32 +#define MAX_CPUS 256 #define XICS_IRQS 1024 sPAPREnvironment *spapr; @@ -93,7 +93,7 @@ static void *spapr_create_fdt_skel(const char *cpu_model, /* Root node */ _FDT((fdt_begin_node(fdt, ""))); _FDT((fdt_property_string(fdt, "device_type", "chrp"))); - _FDT((fdt_property_string(fdt, "model", "qemu,emulated-pSeries-LPAR"))); + _FDT((fdt_property_string(fdt, "model", "IBM pSeries (emulated by qemu)"))); _FDT((fdt_property_cell(fdt, "#address-cells", 0x2))); _FDT((fdt_property_cell(fdt, "#size-cells", 0x2))); @@ -362,8 +362,9 @@ static void ppc_spapr_init(ram_addr_t ram_size, for (i = 0; i < MAX_SERIAL_PORTS; i++, irq++) { if (serial_hds[i]) { - spapr_vty_create(spapr->vio_bus, i, serial_hds[i], - xics_find_qirq(spapr->icp, irq), irq); + spapr_vty_create(spapr->vio_bus, SPAPR_VTY_BASE_ADDRESS + i, + serial_hds[i], xics_find_qirq(spapr->icp, irq), + irq); } } diff --git a/hw/spapr_hcall.c b/hw/spapr_hcall.c index f88e1d2083..43c441dc7d 100644 --- a/hw/spapr_hcall.c +++ b/hw/spapr_hcall.c @@ -100,22 +100,18 @@ static target_ulong h_enter(CPUState *env, sPAPREnvironment *spapr, target_ulong pte_index = args[1]; target_ulong pteh = args[2]; target_ulong ptel = args[3]; - target_ulong porder; - target_ulong i, pa; + target_ulong i; uint8_t *hpte; /* only handle 4k and 16M pages for now */ - porder = 12; if (pteh & HPTE_V_LARGE) { #if 0 /* We don't support 64k pages yet */ if ((ptel & 0xf000) == 0x1000) { /* 64k page */ - porder = 16; } else #endif if ((ptel & 0xff000) == 0) { /* 16M page */ - porder = 24; /* lowest AVA bit must be 0 for 16M pages */ if (pteh & 0x80) { return H_PARAMETER; @@ -125,7 +121,6 @@ static target_ulong h_enter(CPUState *env, sPAPREnvironment *spapr, } } - pa = ptel & HPTE_R_RPN; /* FIXME: bounds check the pa? */ /* Check WIMG */ @@ -455,8 +450,8 @@ static target_ulong h_rtas(CPUState *env, sPAPREnvironment *spapr, nret, rtas_r3 + 12 + 4*nargs); } -spapr_hcall_fn papr_hypercall_table[(MAX_HCALL_OPCODE / 4) + 1]; -spapr_hcall_fn kvmppc_hypercall_table[KVMPPC_HCALL_MAX - KVMPPC_HCALL_BASE]; +static spapr_hcall_fn papr_hypercall_table[(MAX_HCALL_OPCODE / 4) + 1]; +static spapr_hcall_fn kvmppc_hypercall_table[KVMPPC_HCALL_MAX - KVMPPC_HCALL_BASE + 1]; void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn) { diff --git a/hw/spapr_llan.c b/hw/spapr_llan.c index ff3a78f729..c18efc7ee6 100644 --- a/hw/spapr_llan.c +++ b/hw/spapr_llan.c @@ -185,9 +185,6 @@ static NetClientInfo net_spapr_vlan_info = { static int spapr_vlan_init(VIOsPAPRDevice *sdev) { VIOsPAPRVLANDevice *dev = (VIOsPAPRVLANDevice *)sdev; - VIOsPAPRBus *bus; - - bus = DO_UPCAST(VIOsPAPRBus, bus, sdev->qdev.parent_bus); qemu_macaddr_default_if_unset(&dev->nicconf.macaddr); diff --git a/hw/spapr_rtas.c b/hw/spapr_rtas.c index 16b65422b6..00c8ce5a15 100644 --- a/hw/spapr_rtas.c +++ b/hw/spapr_rtas.c @@ -44,7 +44,8 @@ static void rtas_display_character(sPAPREnvironment *spapr, uint32_t nret, target_ulong rets) { uint8_t c = rtas_ld(args, 0); - VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, 0); + VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, + SPAPR_VTY_BASE_ADDRESS); if (!sdev) { rtas_st(rets, 0, -1); diff --git a/hw/spapr_vio.h b/hw/spapr_vio.h index 841b04351a..603a8c43a3 100644 --- a/hw/spapr_vio.h +++ b/hw/spapr_vio.h @@ -32,6 +32,8 @@ enum VIOsPAPR_TCEAccess { SPAPR_TCE_RW = 3, }; +#define SPAPR_VTY_BASE_ADDRESS 0x30000000 + struct VIOsPAPRDevice; typedef struct VIOsPAPR_RTCE { diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c index 992833450c..1c901ef6eb 100644 --- a/hw/spapr_vscsi.c +++ b/hw/spapr_vscsi.c @@ -74,7 +74,7 @@ typedef struct vscsi_req { union viosrp_iu iu; /* SCSI request tracking */ - SCSIDevice *sdev; + SCSIRequest *sreq; uint32_t qtag; /* qemu tag != srp tag */ int lun; int active; @@ -123,11 +123,16 @@ static struct vscsi_req *vscsi_get_req(VSCSIState *s) static void vscsi_put_req(VSCSIState *s, vscsi_req *req) { + if (req->sreq != NULL) { + scsi_req_unref(req->sreq); + } + req->sreq = NULL; req->active = 0; } -static vscsi_req *vscsi_find_req(VSCSIState *s, uint32_t tag) +static vscsi_req *vscsi_find_req(VSCSIState *s, SCSIRequest *req) { + uint32_t tag = req->tag; if (tag >= VSCSI_REQ_LIMIT || !s->reqs[tag].active) { return NULL; } @@ -442,10 +447,18 @@ static int vscsi_preprocess_desc(vscsi_req *req) static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req) { - SCSIDevice *sdev = req->sdev; uint8_t *cdb = req->iu.srp.cmd.cdb; int n; + n = scsi_req_get_sense(req->sreq, req->sense, sizeof(req->sense)); + if (n) { + req->senselen = n; + vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0); + vscsi_put_req(s, req); + return; + } + + dprintf("VSCSI: Got CHECK_CONDITION, requesting sense...\n"); cdb[0] = 3; cdb[1] = 0; cdb[2] = 0; @@ -453,66 +466,92 @@ static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req) cdb[4] = 96; cdb[5] = 0; req->sensing = 1; - n = sdev->info->send_command(sdev, req->qtag, cdb, req->lun); + n = scsi_req_enqueue(req->sreq, cdb); dprintf("VSCSI: Queued request sense tag 0x%x\n", req->qtag); if (n < 0) { fprintf(stderr, "VSCSI: REQUEST_SENSE wants write data !?!?!?\n"); - sdev->info->cancel_io(sdev, req->qtag); vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0); - vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0); - vscsi_put_req(s, req); + scsi_req_abort(req->sreq, CHECK_CONDITION); return; } else if (n == 0) { return; } - sdev->info->read_data(sdev, req->qtag); + scsi_req_continue(req->sreq); } /* Callback to indicate that the SCSI layer has completed a transfer. */ -static void vscsi_command_complete(SCSIBus *bus, int reason, uint32_t tag, - uint32_t arg) +static void vscsi_transfer_data(SCSIRequest *sreq, uint32_t len) { - VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, bus->qbus.parent); - vscsi_req *req = vscsi_find_req(s, tag); - SCSIDevice *sdev; + VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent); + vscsi_req *req = vscsi_find_req(s, sreq); uint8_t *buf; - int32_t res_in = 0, res_out = 0; - int len, rc = 0; + int rc = 0; - dprintf("VSCSI: SCSI cmd complete, r=0x%x tag=0x%x arg=0x%x, req=%p\n", - reason, tag, arg, req); + dprintf("VSCSI: SCSI xfer complete tag=0x%x len=0x%x, req=%p\n", + sreq->tag, len, req); if (req == NULL) { - fprintf(stderr, "VSCSI: Can't find request for tag 0x%x\n", tag); + fprintf(stderr, "VSCSI: Can't find request for tag 0x%x\n", sreq->tag); return; } - sdev = req->sdev; if (req->sensing) { - if (reason == SCSI_REASON_DONE) { - dprintf("VSCSI: Sense done !\n"); - vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0); - vscsi_put_req(s, req); - } else { - uint8_t *buf = sdev->info->get_buf(sdev, tag); - - len = MIN(arg, SCSI_SENSE_BUF_SIZE); - dprintf("VSCSI: Sense data, %d bytes:\n", len); - dprintf(" %02x %02x %02x %02x %02x %02x %02x %02x\n", - buf[0], buf[1], buf[2], buf[3], - buf[4], buf[5], buf[6], buf[7]); - dprintf(" %02x %02x %02x %02x %02x %02x %02x %02x\n", - buf[8], buf[9], buf[10], buf[11], - buf[12], buf[13], buf[14], buf[15]); - memcpy(req->sense, buf, len); - req->senselen = len; - sdev->info->read_data(sdev, req->qtag); - } + uint8_t *buf = scsi_req_get_buf(sreq); + + len = MIN(len, SCSI_SENSE_BUF_SIZE); + dprintf("VSCSI: Sense data, %d bytes:\n", len); + dprintf(" %02x %02x %02x %02x %02x %02x %02x %02x\n", + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7]); + dprintf(" %02x %02x %02x %02x %02x %02x %02x %02x\n", + buf[8], buf[9], buf[10], buf[11], + buf[12], buf[13], buf[14], buf[15]); + memcpy(req->sense, buf, len); + req->senselen = len; + scsi_req_continue(req->sreq); + return; + } + + if (len) { + buf = scsi_req_get_buf(sreq); + rc = vscsi_srp_transfer_data(s, req, req->writing, buf, len); + } + if (rc < 0) { + fprintf(stderr, "VSCSI: RDMA error rc=%d!\n", rc); + vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0); + scsi_req_abort(req->sreq, CHECK_CONDITION); + return; + } + + /* Start next chunk */ + req->data_len -= rc; + scsi_req_continue(sreq); +} + +/* Callback to indicate that the SCSI layer has completed a transfer. */ +static void vscsi_command_complete(SCSIRequest *sreq, uint32_t status) +{ + VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent); + vscsi_req *req = vscsi_find_req(s, sreq); + int32_t res_in = 0, res_out = 0; + + dprintf("VSCSI: SCSI cmd complete, r=0x%x tag=0x%x status=0x%x, req=%p\n", + reason, sreq->tag, status, req); + if (req == NULL) { + fprintf(stderr, "VSCSI: Can't find request for tag 0x%x\n", sreq->tag); + return; + } + + if (!req->sensing && status == CHECK_CONDITION) { + vscsi_send_request_sense(s, req); return; } - if (reason == SCSI_REASON_DONE) { - dprintf("VSCSI: Command complete err=%d\n", arg); - if (arg == 0) { + if (req->sensing) { + dprintf("VSCSI: Sense done !\n"); + status = CHECK_CONDITION; + } else { + dprintf("VSCSI: Command complete err=%d\n", status); + if (status == 0) { /* We handle overflows, not underflows for normal commands, * but hopefully nobody cares */ @@ -521,41 +560,18 @@ static void vscsi_command_complete(SCSIBus *bus, int reason, uint32_t tag, } else { res_in = req->data_len; } - vscsi_send_rsp(s, req, 0, res_in, res_out); - } else if (arg == CHECK_CONDITION) { - dprintf("VSCSI: Got CHECK_CONDITION, requesting sense...\n"); - vscsi_send_request_sense(s, req); - return; - } else { - vscsi_send_rsp(s, req, arg, 0, 0); } - vscsi_put_req(s, req); - return; } + vscsi_send_rsp(s, req, 0, res_in, res_out); + vscsi_put_req(s, req); +} - /* "arg" is how much we have read for reads and how much we want - * to write for writes (ie, how much is to be DMA'd) - */ - if (arg) { - buf = sdev->info->get_buf(sdev, tag); - rc = vscsi_srp_transfer_data(s, req, req->writing, buf, arg); - } - if (rc < 0) { - fprintf(stderr, "VSCSI: RDMA error rc=%d!\n", rc); - sdev->info->cancel_io(sdev, req->qtag); - vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0); - vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0); - vscsi_put_req(s, req); - return; - } +static void vscsi_request_cancelled(SCSIRequest *sreq) +{ + VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent); + vscsi_req *req = vscsi_find_req(s, sreq); - /* Start next chunk */ - req->data_len -= rc; - if (req->writing) { - sdev->info->write_data(sdev, req->qtag); - } else { - sdev->info->read_data(sdev, req->qtag); - } + vscsi_put_req(s, req); } static void vscsi_process_login(VSCSIState *s, vscsi_req *req) @@ -642,9 +658,9 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req) } return 1; } - req->sdev = sdev; req->lun = lun; - n = sdev->info->send_command(sdev, req->qtag, srp->cmd.cdb, lun); + req->sreq = scsi_req_new(sdev, req->qtag, lun); + n = scsi_req_enqueue(req->sreq, srp->cmd.cdb); dprintf("VSCSI: Queued command tag 0x%x CMD 0x%x ID %d LUN %d ret: %d\n", req->qtag, srp->cmd.cdb[0], id, lun, n); @@ -657,15 +673,14 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req) /* Preprocess RDMA descriptors */ vscsi_preprocess_desc(req); - } - /* Get transfer direction and initiate transfer */ - if (n > 0) { - req->data_len = n; - sdev->info->read_data(sdev, req->qtag); - } else if (n < 0) { - req->data_len = -n; - sdev->info->write_data(sdev, req->qtag); + /* Get transfer direction and initiate transfer */ + if (n > 0) { + req->data_len = n; + } else if (n < 0) { + req->data_len = -n; + } + scsi_req_continue(req->sreq); } /* Don't touch req here, it may have been recycled already */ @@ -907,6 +922,12 @@ static int vscsi_do_crq(struct VIOsPAPRDevice *dev, uint8_t *crq_data) return 0; } +static const struct SCSIBusOps vscsi_scsi_ops = { + .transfer_data = vscsi_transfer_data, + .complete = vscsi_command_complete, + .cancel = vscsi_request_cancelled +}; + static int spapr_vscsi_init(VIOsPAPRDevice *dev) { VSCSIState *s = DO_UPCAST(VSCSIState, vdev, dev); @@ -923,7 +944,7 @@ static int spapr_vscsi_init(VIOsPAPRDevice *dev) dev->crq.SendFunc = vscsi_do_crq; scsi_bus_new(&s->bus, &dev->qdev, 1, VSCSI_REQ_LIMIT, - vscsi_command_complete); + &vscsi_scsi_ops); if (!dev->qdev.hotplugged) { scsi_bus_legacy_handle_cmdline(&s->bus); } diff --git a/hw/ssd0303.c b/hw/ssd0303.c index 108c0683c8..b39e2596fb 100644 --- a/hw/ssd0303.c +++ b/hw/ssd0303.c @@ -93,7 +93,7 @@ static int ssd0303_send(i2c_slave *i2c, uint8_t data) DPRINTF("cmd 0x%02x\n", data); s->mode = SSD0303_IDLE; switch (data) { - case 0x00 ... 0x0f: /* Set lower colum address. */ + case 0x00 ... 0x0f: /* Set lower column address. */ s->col = (s->col & 0xf0) | (data & 0xf); break; case 0x10 ... 0x20: /* Set higher column address. */ diff --git a/hw/sun4m_iommu.c b/hw/sun4m_iommu.c index bba69eef92..7f5dad535c 100644 --- a/hw/sun4m_iommu.c +++ b/hw/sun4m_iommu.c @@ -118,7 +118,7 @@ #define IOPTE_PAGE 0xffffff00 /* Physical page number (PA[35:12]) */ #define IOPTE_CACHE 0x00000080 /* Cached (in vme IOCACHE or Viking/MXCC) */ -#define IOPTE_WRITE 0x00000004 /* Writeable */ +#define IOPTE_WRITE 0x00000004 /* Writable */ #define IOPTE_VALID 0x00000002 /* IOPTE is valid */ #define IOPTE_WAZ 0x00000001 /* Write as zeros */ diff --git a/hw/syborg_serial.c b/hw/syborg_serial.c index df2950fe88..2ef71758b5 100644 --- a/hw/syborg_serial.c +++ b/hw/syborg_serial.c @@ -126,7 +126,7 @@ static void do_dma_tx(SyborgSerialState *s, uint32_t count) s->dma_tx_ptr += count; } /* QEMU char backends do not have a nonblocking mode, so we transmit all - the data imediately and the interrupt status will be unchanged. */ + the data immediately and the interrupt status will be unchanged. */ } /* Initiate RX DMA, and transfer data from the FIFO. */ diff --git a/hw/usb-bt.c b/hw/usb-bt.c index 22e6845049..baae4876ea 100644 --- a/hw/usb-bt.c +++ b/hw/usb-bt.c @@ -372,13 +372,13 @@ static void usb_bt_handle_reset(USBDevice *dev) s->altsetting = 0; } -static int usb_bt_handle_control(USBDevice *dev, int request, int value, - int index, int length, uint8_t *data) +static int usb_bt_handle_control(USBDevice *dev, USBPacket *p, + int request, int value, int index, int length, uint8_t *data) { struct USBBtState *s = (struct USBBtState *) dev->opaque; int ret; - ret = usb_desc_handle_control(dev, request, value, index, length, data); + ret = usb_desc_handle_control(dev, p, request, value, index, length, data); if (ret >= 0) { switch (request) { case DeviceRequest | USB_REQ_GET_CONFIGURATION: diff --git a/hw/usb-ccid.c b/hw/usb-ccid.c index 079b4a2555..5b6878bea4 100644 --- a/hw/usb-ccid.c +++ b/hw/usb-ccid.c @@ -602,8 +602,8 @@ static void ccid_handle_reset(USBDevice *dev) ccid_reset(s); } -static int ccid_handle_control(USBDevice *dev, int request, int value, - int index, int length, uint8_t *data) +static int ccid_handle_control(USBDevice *dev, USBPacket *p, int request, + int value, int index, int length, uint8_t *data) { USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev); int ret = 0; diff --git a/hw/usb-desc.c b/hw/usb-desc.c index 62591f20aa..e4a4680fee 100644 --- a/hw/usb-desc.c +++ b/hw/usb-desc.c @@ -76,7 +76,7 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len) { uint8_t bLength = 0x09; uint16_t wTotalLength = 0; - int i, rc, count; + int i, rc; if (len < bLength) { return -1; @@ -91,8 +91,19 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len) dest[0x08] = conf->bMaxPower; wTotalLength += bLength; - count = conf->nif ? conf->nif : conf->bNumInterfaces; - for (i = 0; i < count; i++) { + /* handle grouped interfaces if any*/ + for (i = 0; i < conf->nif_groups; i++) { + rc = usb_desc_iface_group(&(conf->if_groups[i]), + dest + wTotalLength, + len - wTotalLength); + if (rc < 0) { + return rc; + } + wTotalLength += rc; + } + + /* handle normal (ungrouped / no IAD) interfaces if any */ + for (i = 0; i < conf->nif; i++) { rc = usb_desc_iface(conf->ifs + i, dest + wTotalLength, len - wTotalLength); if (rc < 0) { return rc; @@ -105,6 +116,41 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len) return wTotalLength; } +int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest, + size_t len) +{ + int pos = 0; + int i = 0; + + /* handle interface association descriptor */ + uint8_t bLength = 0x08; + + if (len < bLength) { + return -1; + } + + dest[0x00] = bLength; + dest[0x01] = USB_DT_INTERFACE_ASSOC; + dest[0x02] = iad->bFirstInterface; + dest[0x03] = iad->bInterfaceCount; + dest[0x04] = iad->bFunctionClass; + dest[0x05] = iad->bFunctionSubClass; + dest[0x06] = iad->bFunctionProtocol; + dest[0x07] = iad->iFunction; + pos += bLength; + + /* handle associated interfaces in this group */ + for (i = 0; i < iad->nif; i++) { + int rc = usb_desc_iface(&(iad->ifs[i]), dest + pos, len - pos); + if (rc < 0) { + return rc; + } + pos += rc; + } + + return pos; +} + int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len) { uint8_t bLength = 0x09; @@ -344,8 +390,8 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len return ret; } -int usb_desc_handle_control(USBDevice *dev, int request, int value, - int index, int length, uint8_t *data) +int usb_desc_handle_control(USBDevice *dev, USBPacket *p, + int request, int value, int index, int length, uint8_t *data) { const USBDesc *desc = dev->info->usb_desc; int i, ret = -1; diff --git a/hw/usb-desc.h b/hw/usb-desc.h index ac734ab088..9d7ed599ce 100644 --- a/hw/usb-desc.h +++ b/hw/usb-desc.h @@ -30,6 +30,24 @@ struct USBDescConfig { uint8_t bmAttributes; uint8_t bMaxPower; + /* grouped interfaces */ + uint8_t nif_groups; + const USBDescIfaceAssoc *if_groups; + + /* "normal" interfaces */ + uint8_t nif; + const USBDescIface *ifs; +}; + +/* conceptually an Interface Association Descriptor, and releated interfaces */ +struct USBDescIfaceAssoc { + uint8_t bFirstInterface; + uint8_t bInterfaceCount; + uint8_t bFunctionClass; + uint8_t bFunctionSubClass; + uint8_t bFunctionProtocol; + uint8_t iFunction; + uint8_t nif; const USBDescIface *ifs; }; @@ -75,6 +93,8 @@ int usb_desc_device(const USBDescID *id, const USBDescDevice *dev, int usb_desc_device_qualifier(const USBDescDevice *dev, uint8_t *dest, size_t len); int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len); +int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest, + size_t len); int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len); int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len); int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len); @@ -86,7 +106,7 @@ void usb_desc_set_string(USBDevice *dev, uint8_t index, const char *str); const char *usb_desc_get_string(USBDevice *dev, uint8_t index); int usb_desc_string(USBDevice *dev, int index, uint8_t *dest, size_t len); int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len); -int usb_desc_handle_control(USBDevice *dev, int request, int value, - int index, int length, uint8_t *data); +int usb_desc_handle_control(USBDevice *dev, USBPacket *p, + int request, int value, int index, int length, uint8_t *data); #endif /* QEMU_HW_USB_DESC_H */ diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c new file mode 100644 index 0000000000..f63519ecf9 --- /dev/null +++ b/hw/usb-ehci.c @@ -0,0 +1,2037 @@ +/* + * QEMU USB EHCI Emulation + * + * Copyright(c) 2008 Emutex Ltd. (address@hidden) + * + * EHCI project was started by Mark Burkley, with contributions by + * Niels de Vos. David S. Ahern continued working on it. Kevin Wolf, + * Jan Kiszka and Vincent Palatin contributed bugfixes. + * + * + * 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 General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * TODO: + * o Downstream port handoff + */ + +#include "hw.h" +#include "qemu-timer.h" +#include "usb.h" +#include "pci.h" +#include "monitor.h" + +#define EHCI_DEBUG 0 +#define STATE_DEBUG 0 /* state transitions */ + +#if EHCI_DEBUG || STATE_DEBUG +#define DPRINTF printf +#else +#define DPRINTF(...) +#endif + +#if STATE_DEBUG +#define DPRINTF_ST DPRINTF +#else +#define DPRINTF_ST(...) +#endif + +/* internal processing - reset HC to try and recover */ +#define USB_RET_PROCERR (-99) + +#define MMIO_SIZE 0x1000 + +/* Capability Registers Base Address - section 2.2 */ +#define CAPREGBASE 0x0000 +#define CAPLENGTH CAPREGBASE + 0x0000 // 1-byte, 0x0001 reserved +#define HCIVERSION CAPREGBASE + 0x0002 // 2-bytes, i/f version # +#define HCSPARAMS CAPREGBASE + 0x0004 // 4-bytes, structural params +#define HCCPARAMS CAPREGBASE + 0x0008 // 4-bytes, capability params +#define EECP HCCPARAMS + 1 +#define HCSPPORTROUTE1 CAPREGBASE + 0x000c +#define HCSPPORTROUTE2 CAPREGBASE + 0x0010 + +#define OPREGBASE 0x0020 // Operational Registers Base Address + +#define USBCMD OPREGBASE + 0x0000 +#define USBCMD_RUNSTOP (1 << 0) // run / Stop +#define USBCMD_HCRESET (1 << 1) // HC Reset +#define USBCMD_FLS (3 << 2) // Frame List Size +#define USBCMD_FLS_SH 2 // Frame List Size Shift +#define USBCMD_PSE (1 << 4) // Periodic Schedule Enable +#define USBCMD_ASE (1 << 5) // Asynch Schedule Enable +#define USBCMD_IAAD (1 << 6) // Int Asynch Advance Doorbell +#define USBCMD_LHCR (1 << 7) // Light Host Controller Reset +#define USBCMD_ASPMC (3 << 8) // Async Sched Park Mode Count +#define USBCMD_ASPME (1 << 11) // Async Sched Park Mode Enable +#define USBCMD_ITC (0x7f << 16) // Int Threshold Control +#define USBCMD_ITC_SH 16 // Int Threshold Control Shift + +#define USBSTS OPREGBASE + 0x0004 +#define USBSTS_RO_MASK 0x0000003f +#define USBSTS_INT (1 << 0) // USB Interrupt +#define USBSTS_ERRINT (1 << 1) // Error Interrupt +#define USBSTS_PCD (1 << 2) // Port Change Detect +#define USBSTS_FLR (1 << 3) // Frame List Rollover +#define USBSTS_HSE (1 << 4) // Host System Error +#define USBSTS_IAA (1 << 5) // Interrupt on Async Advance +#define USBSTS_HALT (1 << 12) // HC Halted +#define USBSTS_REC (1 << 13) // Reclamation +#define USBSTS_PSS (1 << 14) // Periodic Schedule Status +#define USBSTS_ASS (1 << 15) // Asynchronous Schedule Status + +/* + * Interrupt enable bits correspond to the interrupt active bits in USBSTS + * so no need to redefine here. + */ +#define USBINTR OPREGBASE + 0x0008 +#define USBINTR_MASK 0x0000003f + +#define FRINDEX OPREGBASE + 0x000c +#define CTRLDSSEGMENT OPREGBASE + 0x0010 +#define PERIODICLISTBASE OPREGBASE + 0x0014 +#define ASYNCLISTADDR OPREGBASE + 0x0018 +#define ASYNCLISTADDR_MASK 0xffffffe0 + +#define CONFIGFLAG OPREGBASE + 0x0040 + +#define PORTSC (OPREGBASE + 0x0044) +#define PORTSC_BEGIN PORTSC +#define PORTSC_END (PORTSC + 4 * NB_PORTS) +/* + * Bits that are reserverd or are read-only are masked out of values + * written to us by software + */ +#define PORTSC_RO_MASK 0x007021c5 +#define PORTSC_RWC_MASK 0x0000002a +#define PORTSC_WKOC_E (1 << 22) // Wake on Over Current Enable +#define PORTSC_WKDS_E (1 << 21) // Wake on Disconnect Enable +#define PORTSC_WKCN_E (1 << 20) // Wake on Connect Enable +#define PORTSC_PTC (15 << 16) // Port Test Control +#define PORTSC_PTC_SH 16 // Port Test Control shift +#define PORTSC_PIC (3 << 14) // Port Indicator Control +#define PORTSC_PIC_SH 14 // Port Indicator Control Shift +#define PORTSC_POWNER (1 << 13) // Port Owner +#define PORTSC_PPOWER (1 << 12) // Port Power +#define PORTSC_LINESTAT (3 << 10) // Port Line Status +#define PORTSC_LINESTAT_SH 10 // Port Line Status Shift +#define PORTSC_PRESET (1 << 8) // Port Reset +#define PORTSC_SUSPEND (1 << 7) // Port Suspend +#define PORTSC_FPRES (1 << 6) // Force Port Resume +#define PORTSC_OCC (1 << 5) // Over Current Change +#define PORTSC_OCA (1 << 4) // Over Current Active +#define PORTSC_PEDC (1 << 3) // Port Enable/Disable Change +#define PORTSC_PED (1 << 2) // Port Enable/Disable +#define PORTSC_CSC (1 << 1) // Connect Status Change +#define PORTSC_CONNECT (1 << 0) // Current Connect Status + +#define FRAME_TIMER_FREQ 1000 +#define FRAME_TIMER_USEC (1000000 / FRAME_TIMER_FREQ) + +#define NB_MAXINTRATE 8 // Max rate at which controller issues ints +#define NB_PORTS 4 // Number of downstream ports +#define BUFF_SIZE 5*4096 // Max bytes to transfer per transaction +#define MAX_ITERATIONS 20 // Max number of QH before we break the loop +#define MAX_QH 100 // Max allowable queue heads in a chain + +/* Internal periodic / asynchronous schedule state machine states + */ +typedef enum { + EST_INACTIVE = 1000, + EST_ACTIVE, + EST_EXECUTING, + EST_SLEEPING, + /* The following states are internal to the state machine function + */ + EST_WAITLISTHEAD, + EST_FETCHENTRY, + EST_FETCHQH, + EST_FETCHITD, + EST_ADVANCEQUEUE, + EST_FETCHQTD, + EST_EXECUTE, + EST_WRITEBACK, + EST_HORIZONTALQH +} EHCI_STATES; + +/* macros for accessing fields within next link pointer entry */ +#define NLPTR_GET(x) ((x) & 0xffffffe0) +#define NLPTR_TYPE_GET(x) (((x) >> 1) & 3) +#define NLPTR_TBIT(x) ((x) & 1) // 1=invalid, 0=valid + +/* link pointer types */ +#define NLPTR_TYPE_ITD 0 // isoc xfer descriptor +#define NLPTR_TYPE_QH 1 // queue head +#define NLPTR_TYPE_STITD 2 // split xaction, isoc xfer descriptor +#define NLPTR_TYPE_FSTN 3 // frame span traversal node + + +/* EHCI spec version 1.0 Section 3.3 + */ +typedef struct EHCIitd { + uint32_t next; + + uint32_t transact[8]; +#define ITD_XACT_ACTIVE (1 << 31) +#define ITD_XACT_DBERROR (1 << 30) +#define ITD_XACT_BABBLE (1 << 29) +#define ITD_XACT_XACTERR (1 << 28) +#define ITD_XACT_LENGTH_MASK 0x0fff0000 +#define ITD_XACT_LENGTH_SH 16 +#define ITD_XACT_IOC (1 << 15) +#define ITD_XACT_PGSEL_MASK 0x00007000 +#define ITD_XACT_PGSEL_SH 12 +#define ITD_XACT_OFFSET_MASK 0x00000fff + + uint32_t bufptr[7]; +#define ITD_BUFPTR_MASK 0xfffff000 +#define ITD_BUFPTR_SH 12 +#define ITD_BUFPTR_EP_MASK 0x00000f00 +#define ITD_BUFPTR_EP_SH 8 +#define ITD_BUFPTR_DEVADDR_MASK 0x0000007f +#define ITD_BUFPTR_DEVADDR_SH 0 +#define ITD_BUFPTR_DIRECTION (1 << 11) +#define ITD_BUFPTR_MAXPKT_MASK 0x000007ff +#define ITD_BUFPTR_MAXPKT_SH 0 +#define ITD_BUFPTR_MULT_MASK 0x00000003 +} EHCIitd; + +/* EHCI spec version 1.0 Section 3.4 + */ +typedef struct EHCIsitd { + uint32_t next; // Standard next link pointer + uint32_t epchar; +#define SITD_EPCHAR_IO (1 << 31) +#define SITD_EPCHAR_PORTNUM_MASK 0x7f000000 +#define SITD_EPCHAR_PORTNUM_SH 24 +#define SITD_EPCHAR_HUBADD_MASK 0x007f0000 +#define SITD_EPCHAR_HUBADDR_SH 16 +#define SITD_EPCHAR_EPNUM_MASK 0x00000f00 +#define SITD_EPCHAR_EPNUM_SH 8 +#define SITD_EPCHAR_DEVADDR_MASK 0x0000007f + + uint32_t uframe; +#define SITD_UFRAME_CMASK_MASK 0x0000ff00 +#define SITD_UFRAME_CMASK_SH 8 +#define SITD_UFRAME_SMASK_MASK 0x000000ff + + uint32_t results; +#define SITD_RESULTS_IOC (1 << 31) +#define SITD_RESULTS_PGSEL (1 << 30) +#define SITD_RESULTS_TBYTES_MASK 0x03ff0000 +#define SITD_RESULTS_TYBYTES_SH 16 +#define SITD_RESULTS_CPROGMASK_MASK 0x0000ff00 +#define SITD_RESULTS_CPROGMASK_SH 8 +#define SITD_RESULTS_ACTIVE (1 << 7) +#define SITD_RESULTS_ERR (1 << 6) +#define SITD_RESULTS_DBERR (1 << 5) +#define SITD_RESULTS_BABBLE (1 << 4) +#define SITD_RESULTS_XACTERR (1 << 3) +#define SITD_RESULTS_MISSEDUF (1 << 2) +#define SITD_RESULTS_SPLITXSTATE (1 << 1) + + uint32_t bufptr[2]; +#define SITD_BUFPTR_MASK 0xfffff000 +#define SITD_BUFPTR_CURROFF_MASK 0x00000fff +#define SITD_BUFPTR_TPOS_MASK 0x00000018 +#define SITD_BUFPTR_TPOS_SH 3 +#define SITD_BUFPTR_TCNT_MASK 0x00000007 + + uint32_t backptr; // Standard next link pointer +} EHCIsitd; + +/* EHCI spec version 1.0 Section 3.5 + */ +typedef struct EHCIqtd { + uint32_t next; // Standard next link pointer + uint32_t altnext; // Standard next link pointer + uint32_t token; +#define QTD_TOKEN_DTOGGLE (1 << 31) +#define QTD_TOKEN_TBYTES_MASK 0x7fff0000 +#define QTD_TOKEN_TBYTES_SH 16 +#define QTD_TOKEN_IOC (1 << 15) +#define QTD_TOKEN_CPAGE_MASK 0x00007000 +#define QTD_TOKEN_CPAGE_SH 12 +#define QTD_TOKEN_CERR_MASK 0x00000c00 +#define QTD_TOKEN_CERR_SH 10 +#define QTD_TOKEN_PID_MASK 0x00000300 +#define QTD_TOKEN_PID_SH 8 +#define QTD_TOKEN_ACTIVE (1 << 7) +#define QTD_TOKEN_HALT (1 << 6) +#define QTD_TOKEN_DBERR (1 << 5) +#define QTD_TOKEN_BABBLE (1 << 4) +#define QTD_TOKEN_XACTERR (1 << 3) +#define QTD_TOKEN_MISSEDUF (1 << 2) +#define QTD_TOKEN_SPLITXSTATE (1 << 1) +#define QTD_TOKEN_PING (1 << 0) + + uint32_t bufptr[5]; // Standard buffer pointer +#define QTD_BUFPTR_MASK 0xfffff000 +} EHCIqtd; + +/* EHCI spec version 1.0 Section 3.6 + */ +typedef struct EHCIqh { + uint32_t next; // Standard next link pointer + + /* endpoint characteristics */ + uint32_t epchar; +#define QH_EPCHAR_RL_MASK 0xf0000000 +#define QH_EPCHAR_RL_SH 28 +#define QH_EPCHAR_C (1 << 27) +#define QH_EPCHAR_MPLEN_MASK 0x07FF0000 +#define QH_EPCHAR_MPLEN_SH 16 +#define QH_EPCHAR_H (1 << 15) +#define QH_EPCHAR_DTC (1 << 14) +#define QH_EPCHAR_EPS_MASK 0x00003000 +#define QH_EPCHAR_EPS_SH 12 +#define EHCI_QH_EPS_FULL 0 +#define EHCI_QH_EPS_LOW 1 +#define EHCI_QH_EPS_HIGH 2 +#define EHCI_QH_EPS_RESERVED 3 + +#define QH_EPCHAR_EP_MASK 0x00000f00 +#define QH_EPCHAR_EP_SH 8 +#define QH_EPCHAR_I (1 << 7) +#define QH_EPCHAR_DEVADDR_MASK 0x0000007f +#define QH_EPCHAR_DEVADDR_SH 0 + + /* endpoint capabilities */ + uint32_t epcap; +#define QH_EPCAP_MULT_MASK 0xc0000000 +#define QH_EPCAP_MULT_SH 30 +#define QH_EPCAP_PORTNUM_MASK 0x3f800000 +#define QH_EPCAP_PORTNUM_SH 23 +#define QH_EPCAP_HUBADDR_MASK 0x007f0000 +#define QH_EPCAP_HUBADDR_SH 16 +#define QH_EPCAP_CMASK_MASK 0x0000ff00 +#define QH_EPCAP_CMASK_SH 8 +#define QH_EPCAP_SMASK_MASK 0x000000ff +#define QH_EPCAP_SMASK_SH 0 + + uint32_t current_qtd; // Standard next link pointer + uint32_t next_qtd; // Standard next link pointer + uint32_t altnext_qtd; +#define QH_ALTNEXT_NAKCNT_MASK 0x0000001e +#define QH_ALTNEXT_NAKCNT_SH 1 + + uint32_t token; // Same as QTD token + uint32_t bufptr[5]; // Standard buffer pointer +#define BUFPTR_CPROGMASK_MASK 0x000000ff +#define BUFPTR_FRAMETAG_MASK 0x0000001f +#define BUFPTR_SBYTES_MASK 0x00000fe0 +#define BUFPTR_SBYTES_SH 5 +} EHCIqh; + +/* EHCI spec version 1.0 Section 3.7 + */ +typedef struct EHCIfstn { + uint32_t next; // Standard next link pointer + uint32_t backptr; // Standard next link pointer +} EHCIfstn; + +typedef struct { + PCIDevice dev; + qemu_irq irq; + target_phys_addr_t mem_base; + int mem; + int num_ports; + /* + * EHCI spec version 1.0 Section 2.3 + * Host Controller Operational Registers + */ + union { + uint8_t mmio[MMIO_SIZE]; + struct { + uint8_t cap[OPREGBASE]; + uint32_t usbcmd; + uint32_t usbsts; + uint32_t usbintr; + uint32_t frindex; + uint32_t ctrldssegment; + uint32_t periodiclistbase; + uint32_t asynclistaddr; + uint32_t notused[9]; + uint32_t configflag; + uint32_t portsc[NB_PORTS]; + }; + }; + /* + * Internal states, shadow registers, etc + */ + uint32_t sofv; + QEMUTimer *frame_timer; + int attach_poll_counter; + int astate; // Current state in asynchronous schedule + int pstate; // Current state in periodic schedule + USBPort ports[NB_PORTS]; + uint8_t buffer[BUFF_SIZE]; + uint32_t usbsts_pending; + + /* cached data from guest - needs to be flushed + * when guest removes an entry (doorbell, handshake sequence) + */ + EHCIqh qh; // copy of current QH (being worked on) + uint32_t qhaddr; // address QH read from + + EHCIqtd qtd; // copy of current QTD (being worked on) + uint32_t qtdaddr; // address QTD read from + + uint32_t itdaddr; // current ITD + + uint32_t fetch_addr; // which address to look at next + + USBBus bus; + USBPacket usb_packet; + int async_complete; + uint32_t tbytes; + int pid; + int exec_status; + int isoch_pause; + uint32_t last_run_usec; + uint32_t frame_end_usec; +} EHCIState; + +#define SET_LAST_RUN_CLOCK(s) \ + (s)->last_run_usec = qemu_get_clock_ns(vm_clock) / 1000; + +/* nifty macros from Arnon's EHCI version */ +#define get_field(data, field) \ + (((data) & field##_MASK) >> field##_SH) + +#define set_field(data, newval, field) do { \ + uint32_t val = *data; \ + val &= ~ field##_MASK; \ + val |= ((newval) << field##_SH) & field##_MASK; \ + *data = val; \ + } while(0) + + +#if EHCI_DEBUG +static const char *addr2str(unsigned addr) +{ + const char *r = " unknown"; + const char *n[] = { + [ CAPLENGTH ] = " CAPLENGTH", + [ HCIVERSION ] = "HCIVERSION", + [ HCSPARAMS ] = " HCSPARAMS", + [ HCCPARAMS ] = " HCCPARAMS", + [ USBCMD ] = " COMMAND", + [ USBSTS ] = " STATUS", + [ USBINTR ] = " INTERRUPT", + [ FRINDEX ] = " FRAME IDX", + [ PERIODICLISTBASE ] = "P-LIST BASE", + [ ASYNCLISTADDR ] = "A-LIST ADDR", + [ PORTSC_BEGIN ... + PORTSC_END ] = "PORT STATUS", + [ CONFIGFLAG ] = "CONFIG FLAG", + }; + + if (addr < ARRAY_SIZE(n) && n[addr] != NULL) { + return n[addr]; + } else { + return r; + } +} +#endif + + +static inline void ehci_set_interrupt(EHCIState *s, int intr) +{ + int level = 0; + + // TODO honour interrupt threshold requests + + s->usbsts |= intr; + + if ((s->usbsts & USBINTR_MASK) & s->usbintr) { + level = 1; + } + + qemu_set_irq(s->irq, level); +} + +static inline void ehci_record_interrupt(EHCIState *s, int intr) +{ + s->usbsts_pending |= intr; +} + +static inline void ehci_commit_interrupt(EHCIState *s) +{ + if (!s->usbsts_pending) { + return; + } + ehci_set_interrupt(s, s->usbsts_pending); + s->usbsts_pending = 0; +} + +/* Attach or detach a device on root hub */ + +static void ehci_attach(USBPort *port) +{ + EHCIState *s = port->opaque; + uint32_t *portsc = &s->portsc[port->index]; + + DPRINTF("ehci_attach invoked for index %d, portsc 0x%x, desc %s\n", + port->index, *portsc, port->dev->product_desc); + + *portsc |= PORTSC_CONNECT; + *portsc |= PORTSC_CSC; + + /* + * If a high speed device is attached then we own this port(indicated + * by zero in the PORTSC_POWNER bit field) so set the status bit + * and set an interrupt if enabled. + */ + if ( !(*portsc & PORTSC_POWNER)) { + ehci_set_interrupt(s, USBSTS_PCD); + } +} + +static void ehci_detach(USBPort *port) +{ + EHCIState *s = port->opaque; + uint32_t *portsc = &s->portsc[port->index]; + + DPRINTF("ehci_attach invoked for index %d, portsc 0x%x\n", + port->index, *portsc); + + *portsc &= ~PORTSC_CONNECT; + *portsc |= PORTSC_CSC; + + /* + * If a high speed device is attached then we own this port(indicated + * by zero in the PORTSC_POWNER bit field) so set the status bit + * and set an interrupt if enabled. + */ + if ( !(*portsc & PORTSC_POWNER)) { + ehci_set_interrupt(s, USBSTS_PCD); + } +} + +/* 4.1 host controller initialization */ +static void ehci_reset(void *opaque) +{ + EHCIState *s = opaque; + uint8_t *pci_conf; + int i; + + pci_conf = s->dev.config; + + memset(&s->mmio[OPREGBASE], 0x00, MMIO_SIZE - OPREGBASE); + + s->usbcmd = NB_MAXINTRATE << USBCMD_ITC_SH; + s->usbsts = USBSTS_HALT; + + s->astate = EST_INACTIVE; + s->pstate = EST_INACTIVE; + s->async_complete = 0; + s->isoch_pause = -1; + s->attach_poll_counter = 0; + + for(i = 0; i < NB_PORTS; i++) { + s->portsc[i] = PORTSC_POWNER | PORTSC_PPOWER; + + if (s->ports[i].dev) { + usb_attach(&s->ports[i], s->ports[i].dev); + } + } +} + +static uint32_t ehci_mem_readb(void *ptr, target_phys_addr_t addr) +{ + EHCIState *s = ptr; + uint32_t val; + + val = s->mmio[addr]; + + return val; +} + +static uint32_t ehci_mem_readw(void *ptr, target_phys_addr_t addr) +{ + EHCIState *s = ptr; + uint32_t val; + + val = s->mmio[addr] | (s->mmio[addr+1] << 8); + + return val; +} + +static uint32_t ehci_mem_readl(void *ptr, target_phys_addr_t addr) +{ + EHCIState *s = ptr; + uint32_t val; + + val = s->mmio[addr] | (s->mmio[addr+1] << 8) | + (s->mmio[addr+2] << 16) | (s->mmio[addr+3] << 24); + + return val; +} + +static void ehci_mem_writeb(void *ptr, target_phys_addr_t addr, uint32_t val) +{ + fprintf(stderr, "EHCI doesn't handle byte writes to MMIO\n"); + exit(1); +} + +static void ehci_mem_writew(void *ptr, target_phys_addr_t addr, uint32_t val) +{ + fprintf(stderr, "EHCI doesn't handle 16-bit writes to MMIO\n"); + exit(1); +} + +static void handle_port_status_write(EHCIState *s, int port, uint32_t val) +{ + uint32_t *portsc = &s->portsc[port]; + int rwc; + USBDevice *dev = s->ports[port].dev; + + DPRINTF("port_status_write: " + "PORTSC (port %d) curr %08X new %08X rw-clear %08X rw %08X\n", + port, *portsc, val, (val & PORTSC_RWC_MASK), val & PORTSC_RO_MASK); + + rwc = val & PORTSC_RWC_MASK; + val &= PORTSC_RO_MASK; + + // handle_read_write_clear(&val, portsc, PORTSC_PEDC | PORTSC_CSC); + + *portsc &= ~rwc; + + if ((val & PORTSC_PRESET) && !(*portsc & PORTSC_PRESET)) { + DPRINTF("port_status_write: USBTRAN Port %d reset begin\n", port); + } + + if (!(val & PORTSC_PRESET) &&(*portsc & PORTSC_PRESET)) { + DPRINTF("port_status_write: USBTRAN Port %d reset done\n", port); + usb_attach(&s->ports[port], dev); + + // TODO how to handle reset of ports with no device + if (dev) { + usb_send_msg(dev, USB_MSG_RESET); + } + + if (s->ports[port].dev) { + DPRINTF("port_status_write: " + "Device was connected before reset, clearing CSC bit\n"); + *portsc &= ~PORTSC_CSC; + } + + /* Table 2.16 Set the enable bit(and enable bit change) to indicate + * to SW that this port has a high speed device attached + * + * TODO - when to disable? + */ + val |= PORTSC_PED; + val |= PORTSC_PEDC; + } + + *portsc &= ~PORTSC_RO_MASK; + *portsc |= val; + DPRINTF("port_status_write: Port %d status set to 0x%08x\n", port, *portsc); +} + +static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val) +{ + EHCIState *s = ptr; + int i; +#if EHCI_DEBUG + const char *str; +#endif + + /* Only aligned reads are allowed on OHCI */ + if (addr & 3) { + fprintf(stderr, "usb-ehci: Mis-aligned write to addr 0x" + TARGET_FMT_plx "\n", addr); + return; + } + + if (addr >= PORTSC && addr < PORTSC + 4 * NB_PORTS) { + handle_port_status_write(s, (addr-PORTSC)/4, val); + return; + } + + if (addr < OPREGBASE) { + fprintf(stderr, "usb-ehci: write attempt to read-only register" + TARGET_FMT_plx "\n", addr); + return; + } + + + /* Do any register specific pre-write processing here. */ +#if EHCI_DEBUG + str = addr2str((unsigned) addr); +#endif + switch(addr) { + case USBCMD: + DPRINTF("ehci_mem_writel: USBCMD val=0x%08X, current cmd=0x%08X\n", + val, s->usbcmd); + + if ((val & USBCMD_RUNSTOP) && !(s->usbcmd & USBCMD_RUNSTOP)) { + DPRINTF("ehci_mem_writel: %s run, clear halt\n", str); + qemu_mod_timer(s->frame_timer, qemu_get_clock_ns(vm_clock)); + SET_LAST_RUN_CLOCK(s); + s->usbsts &= ~USBSTS_HALT; + } + + if (!(val & USBCMD_RUNSTOP) && (s->usbcmd & USBCMD_RUNSTOP)) { + DPRINTF(" ** STOP **\n"); + qemu_del_timer(s->frame_timer); + // TODO - should finish out some stuff before setting halt + s->usbsts |= USBSTS_HALT; + } + + if (val & USBCMD_HCRESET) { + DPRINTF("ehci_mem_writel: %s run, resetting\n", str); + ehci_reset(s); + val &= ~USBCMD_HCRESET; + } + + /* not supporting dynamic frame list size at the moment */ + if ((val & USBCMD_FLS) && !(s->usbcmd & USBCMD_FLS)) { + fprintf(stderr, "attempt to set frame list size -- value %d\n", + val & USBCMD_FLS); + val &= ~USBCMD_FLS; + } +#if EHCI_DEBUG + if ((val & USBCMD_PSE) && !(s->usbcmd & USBCMD_PSE)) { + DPRINTF("periodic scheduling enabled\n"); + } + if (!(val & USBCMD_PSE) && (s->usbcmd & USBCMD_PSE)) { + DPRINTF("periodic scheduling disabled\n"); + } + if ((val & USBCMD_ASE) && !(s->usbcmd & USBCMD_ASE)) { + DPRINTF("asynchronous scheduling enabled\n"); + } + if (!(val & USBCMD_ASE) && (s->usbcmd & USBCMD_ASE)) { + DPRINTF("asynchronous scheduling disabled\n"); + } + if ((val & USBCMD_IAAD) && !(s->usbcmd & USBCMD_IAAD)) { + DPRINTF("doorbell request received\n"); + } + if ((val & USBCMD_LHCR) && !(s->usbcmd & USBCMD_LHCR)) { + DPRINTF("light host controller reset received\n"); + } + if ((val & USBCMD_ITC) != (s->usbcmd & USBCMD_ITC)) { + DPRINTF("interrupt threshold control set to %x\n", + (val & USBCMD_ITC)>>USBCMD_ITC_SH); + } +#endif + break; + + + case USBSTS: + val &= USBSTS_RO_MASK; // bits 6 thru 31 are RO + DPRINTF("ehci_mem_writel: %s RWC set to 0x%08X\n", str, val); + + val = (s->usbsts &= ~val); // bits 0 thru 5 are R/WC + + DPRINTF("ehci_mem_writel: %s updating interrupt condition\n", str); + ehci_set_interrupt(s, 0); + break; + + + case USBINTR: + val &= USBINTR_MASK; + DPRINTF("ehci_mem_writel: %s set to 0x%08X\n", str, val); + break; + + case FRINDEX: + s->sofv = val >> 3; + DPRINTF("ehci_mem_writel: %s set to 0x%08X\n", str, val); + break; + + case CONFIGFLAG: + DPRINTF("ehci_mem_writel: %s set to 0x%08X\n", str, val); + val &= 0x1; + if (val) { + for(i = 0; i < NB_PORTS; i++) + s->portsc[i] &= ~PORTSC_POWNER; + } + break; + + case PERIODICLISTBASE: + if ((s->usbcmd & USBCMD_PSE) && (s->usbcmd & USBCMD_RUNSTOP)) { + fprintf(stderr, + "ehci: PERIODIC list base register set while periodic schedule\n" + " is enabled and HC is enabled\n"); + } + DPRINTF("ehci_mem_writel: P-LIST BASE set to 0x%08X\n", val); + break; + + case ASYNCLISTADDR: + if ((s->usbcmd & USBCMD_ASE) && (s->usbcmd & USBCMD_RUNSTOP)) { + fprintf(stderr, + "ehci: ASYNC list address register set while async schedule\n" + " is enabled and HC is enabled\n"); + } + DPRINTF("ehci_mem_writel: A-LIST ADDR set to 0x%08X\n", val); + break; + } + + *(uint32_t *)(&s->mmio[addr]) = val; +} + + +// TODO : Put in common header file, duplication from usb-ohci.c + +/* Get an array of dwords from main memory */ +static inline int get_dwords(uint32_t addr, uint32_t *buf, int num) +{ + int i; + + for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { + cpu_physical_memory_rw(addr,(uint8_t *)buf, sizeof(*buf), 0); + *buf = le32_to_cpu(*buf); + } + + return 1; +} + +/* Put an array of dwords in to main memory */ +static inline int put_dwords(uint32_t addr, uint32_t *buf, int num) +{ + int i; + + for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { + uint32_t tmp = cpu_to_le32(*buf); + cpu_physical_memory_rw(addr,(uint8_t *)&tmp, sizeof(tmp), 1); + } + + return 1; +} + +// 4.10.2 + +static int ehci_qh_do_overlay(EHCIState *ehci, EHCIqh *qh, EHCIqtd *qtd) +{ + int i; + int dtoggle; + int ping; + int eps; + int reload; + + // remember values in fields to preserve in qh after overlay + + dtoggle = qh->token & QTD_TOKEN_DTOGGLE; + ping = qh->token & QTD_TOKEN_PING; + + DPRINTF("setting qh.current from %08X to 0x%08X\n", qh->current_qtd, + ehci->qtdaddr); + qh->current_qtd = ehci->qtdaddr; + qh->next_qtd = qtd->next; + qh->altnext_qtd = qtd->altnext; + qh->token = qtd->token; + + + eps = get_field(qh->epchar, QH_EPCHAR_EPS); + if (eps == EHCI_QH_EPS_HIGH) { + qh->token &= ~QTD_TOKEN_PING; + qh->token |= ping; + } + + reload = get_field(qh->epchar, QH_EPCHAR_RL); + set_field(&qh->altnext_qtd, reload, QH_ALTNEXT_NAKCNT); + + for (i = 0; i < 5; i++) { + qh->bufptr[i] = qtd->bufptr[i]; + } + + if (!(qh->epchar & QH_EPCHAR_DTC)) { + // preserve QH DT bit + qh->token &= ~QTD_TOKEN_DTOGGLE; + qh->token |= dtoggle; + } + + qh->bufptr[1] &= ~BUFPTR_CPROGMASK_MASK; + qh->bufptr[2] &= ~BUFPTR_FRAMETAG_MASK; + + put_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) qh, sizeof(EHCIqh) >> 2); + + return 0; +} + +static int ehci_buffer_rw(uint8_t *buffer, EHCIqh *qh, int bytes, int rw) +{ + int bufpos = 0; + int cpage, offset; + uint32_t head; + uint32_t tail; + + + if (!bytes) { + return 0; + } + + cpage = get_field(qh->token, QTD_TOKEN_CPAGE); + if (cpage > 4) { + fprintf(stderr, "cpage out of range (%d)\n", cpage); + return USB_RET_PROCERR; + } + + offset = qh->bufptr[0] & ~QTD_BUFPTR_MASK; + DPRINTF("ehci_buffer_rw: %sing %d bytes %08x cpage %d offset %d\n", + rw ? "writ" : "read", bytes, qh->bufptr[0], cpage, offset); + + do { + /* start and end of this page */ + head = qh->bufptr[cpage] & QTD_BUFPTR_MASK; + tail = head + ~QTD_BUFPTR_MASK + 1; + /* add offset into page */ + head |= offset; + + if (bytes <= (tail - head)) { + tail = head + bytes; + } + + DPRINTF("DATA %s cpage:%d head:%08X tail:%08X target:%08X\n", + rw ? "WRITE" : "READ ", cpage, head, tail, bufpos); + + cpu_physical_memory_rw(head, &buffer[bufpos], tail - head, rw); + + bufpos += (tail - head); + bytes -= (tail - head); + + if (bytes > 0) { + cpage++; + offset = 0; + } + } while (bytes > 0); + + /* save cpage */ + set_field(&qh->token, cpage, QTD_TOKEN_CPAGE); + + /* save offset into cpage */ + offset = tail - head; + qh->bufptr[0] &= ~QTD_BUFPTR_MASK; + qh->bufptr[0] |= offset; + + return 0; +} + +static void ehci_async_complete_packet(USBDevice *dev, USBPacket *packet) +{ + EHCIState *ehci = container_of(packet, EHCIState, usb_packet); + + DPRINTF("Async packet complete\n"); + ehci->async_complete = 1; + ehci->exec_status = packet->len; +} + +static int ehci_execute_complete(EHCIState *ehci, EHCIqh *qh, int ret) +{ + int c_err, reload; + + if (ret == USB_RET_ASYNC && !ehci->async_complete) { + DPRINTF("not done yet\n"); + return ret; + } + + ehci->async_complete = 0; + + DPRINTF("execute_complete: qhaddr 0x%x, next %x, qtdaddr 0x%x, status %d\n", + ehci->qhaddr, qh->next, ehci->qtdaddr, ret); + + if (ret < 0) { +err: + /* TO-DO: put this is in a function that can be invoked below as well */ + c_err = get_field(qh->token, QTD_TOKEN_CERR); + c_err--; + set_field(&qh->token, c_err, QTD_TOKEN_CERR); + + switch(ret) { + case USB_RET_NODEV: + fprintf(stderr, "USB no device\n"); + break; + case USB_RET_STALL: + fprintf(stderr, "USB stall\n"); + qh->token |= QTD_TOKEN_HALT; + ehci_record_interrupt(ehci, USBSTS_ERRINT); + break; + case USB_RET_NAK: + /* 4.10.3 */ + reload = get_field(qh->epchar, QH_EPCHAR_RL); + if ((ehci->pid == USB_TOKEN_IN) && reload) { + int nakcnt = get_field(qh->altnext_qtd, QH_ALTNEXT_NAKCNT); + nakcnt--; + set_field(&qh->altnext_qtd, nakcnt, QH_ALTNEXT_NAKCNT); + } else if (!reload) { + return USB_RET_NAK; + } + break; + case USB_RET_BABBLE: + fprintf(stderr, "USB babble TODO\n"); + qh->token |= QTD_TOKEN_BABBLE; + ehci_record_interrupt(ehci, USBSTS_ERRINT); + break; + default: + fprintf(stderr, "USB invalid response %d to handle\n", ret); + /* TO-DO: transaction error */ + ret = USB_RET_PROCERR; + break; + } + } else { + // DPRINTF("Short packet condition\n"); + // TODO check 4.12 for splits + + if ((ret > ehci->tbytes) && (ehci->pid == USB_TOKEN_IN)) { + ret = USB_RET_BABBLE; + goto err; + } + + if (ehci->tbytes && ehci->pid == USB_TOKEN_IN) { + if (ehci_buffer_rw(ehci->buffer, qh, ret, 1) != 0) { + return USB_RET_PROCERR; + } + ehci->tbytes -= ret; + } else { + ehci->tbytes = 0; + } + + DPRINTF("updating tbytes to %d\n", ehci->tbytes); + set_field(&qh->token, ehci->tbytes, QTD_TOKEN_TBYTES); + } + + qh->token ^= QTD_TOKEN_DTOGGLE; + qh->token &= ~QTD_TOKEN_ACTIVE; + + if ((ret >= 0) && (qh->token & QTD_TOKEN_IOC)) { + ehci_record_interrupt(ehci, USBSTS_INT); + } + + return ret; +} + +// 4.10.3 + +static int ehci_execute(EHCIState *ehci, EHCIqh *qh) +{ + USBPort *port; + USBDevice *dev; + int ret; + int i; + int endp; + int devadr; + + if ( !(qh->token & QTD_TOKEN_ACTIVE)) { + fprintf(stderr, "Attempting to execute inactive QH\n"); + return USB_RET_PROCERR; + } + + ehci->tbytes = (qh->token & QTD_TOKEN_TBYTES_MASK) >> QTD_TOKEN_TBYTES_SH; + if (ehci->tbytes > BUFF_SIZE) { + fprintf(stderr, "Request for more bytes than allowed\n"); + return USB_RET_PROCERR; + } + + ehci->pid = (qh->token & QTD_TOKEN_PID_MASK) >> QTD_TOKEN_PID_SH; + switch(ehci->pid) { + case 0: ehci->pid = USB_TOKEN_OUT; break; + case 1: ehci->pid = USB_TOKEN_IN; break; + case 2: ehci->pid = USB_TOKEN_SETUP; break; + default: fprintf(stderr, "bad token\n"); break; + } + + if ((ehci->tbytes && ehci->pid != USB_TOKEN_IN) && + (ehci_buffer_rw(ehci->buffer, qh, ehci->tbytes, 0) != 0)) { + return USB_RET_PROCERR; + } + + endp = get_field(qh->epchar, QH_EPCHAR_EP); + devadr = get_field(qh->epchar, QH_EPCHAR_DEVADDR); + + ret = USB_RET_NODEV; + + // TO-DO: associating device with ehci port + for(i = 0; i < NB_PORTS; i++) { + port = &ehci->ports[i]; + dev = port->dev; + + // TODO sometime we will also need to check if we are the port owner + + if (!(ehci->portsc[i] &(PORTSC_CONNECT))) { + DPRINTF("Port %d, no exec, not connected(%08X)\n", + i, ehci->portsc[i]); + continue; + } + + ehci->usb_packet.pid = ehci->pid; + ehci->usb_packet.devaddr = devadr; + ehci->usb_packet.devep = endp; + ehci->usb_packet.data = ehci->buffer; + ehci->usb_packet.len = ehci->tbytes; + + ret = usb_handle_packet(dev, &ehci->usb_packet); + + DPRINTF("submit: qh %x next %x qtd %x pid %x len %d (total %d) endp %x ret %d\n", + ehci->qhaddr, qh->next, ehci->qtdaddr, ehci->pid, + ehci->usb_packet.len, ehci->tbytes, endp, ret); + + if (ret != USB_RET_NODEV) { + break; + } + } + + if (ret > BUFF_SIZE) { + fprintf(stderr, "ret from usb_handle_packet > BUFF_SIZE\n"); + return USB_RET_PROCERR; + } + + if (ret == USB_RET_ASYNC) { + ehci->async_complete = 0; + } + + return ret; +} + +/* 4.7.2 + */ + +static int ehci_process_itd(EHCIState *ehci, + EHCIitd *itd) +{ + USBPort *port; + USBDevice *dev; + int ret; + int i, j; + int ptr; + int pid; + int pg; + int len; + int dir; + int devadr; + int endp; + int maxpkt; + + dir =(itd->bufptr[1] & ITD_BUFPTR_DIRECTION); + devadr = get_field(itd->bufptr[0], ITD_BUFPTR_DEVADDR); + endp = get_field(itd->bufptr[0], ITD_BUFPTR_EP); + maxpkt = get_field(itd->bufptr[1], ITD_BUFPTR_MAXPKT); + + for(i = 0; i < 8; i++) { + if (itd->transact[i] & ITD_XACT_ACTIVE) { + DPRINTF("ISOCHRONOUS active for frame %d, interval %d\n", + ehci->frindex >> 3, i); + + pg = get_field(itd->transact[i], ITD_XACT_PGSEL); + ptr = (itd->bufptr[pg] & ITD_BUFPTR_MASK) | + (itd->transact[i] & ITD_XACT_OFFSET_MASK); + len = get_field(itd->transact[i], ITD_XACT_LENGTH); + + if (len > BUFF_SIZE) { + return USB_RET_PROCERR; + } + + DPRINTF("ISOCH: buffer %08X len %d\n", ptr, len); + + if (!dir) { + cpu_physical_memory_rw(ptr, &ehci->buffer[0], len, 0); + pid = USB_TOKEN_OUT; + } else + pid = USB_TOKEN_IN; + + ret = USB_RET_NODEV; + + for (j = 0; j < NB_PORTS; j++) { + port = &ehci->ports[j]; + dev = port->dev; + + // TODO sometime we will also need to check if we are the port owner + + if (!(ehci->portsc[j] &(PORTSC_CONNECT))) { + DPRINTF("Port %d, no exec, not connected(%08X)\n", + j, ehci->portsc[j]); + continue; + } + + ehci->usb_packet.pid = ehci->pid; + ehci->usb_packet.devaddr = devadr; + ehci->usb_packet.devep = endp; + ehci->usb_packet.data = ehci->buffer; + ehci->usb_packet.len = len; + + DPRINTF("calling usb_handle_packet\n"); + ret = usb_handle_packet(dev, &ehci->usb_packet); + + if (ret != USB_RET_NODEV) { + break; + } + } + + /* In isoch, there is no facility to indicate a NAK so let's + * instead just complete a zero-byte transaction. Setting + * DBERR seems too draconian. + */ + + if (ret == USB_RET_NAK) { + if (ehci->isoch_pause > 0) { + DPRINTF("ISOCH: received a NAK but paused so returning\n"); + ehci->isoch_pause--; + return 0; + } else if (ehci->isoch_pause == -1) { + DPRINTF("ISOCH: recv NAK & isoch pause inactive, setting\n"); + // Pause frindex for up to 50 msec waiting for data from + // remote + ehci->isoch_pause = 50; + return 0; + } else { + DPRINTF("ISOCH: isoch pause timeout! return 0\n"); + ret = 0; + } + } else { + DPRINTF("ISOCH: received ACK, clearing pause\n"); + ehci->isoch_pause = -1; + } + + if (ret >= 0) { + itd->transact[i] &= ~ITD_XACT_ACTIVE; + + if (itd->transact[i] & ITD_XACT_IOC) { + ehci_record_interrupt(ehci, USBSTS_INT); + } + } + + if (ret >= 0 && dir) { + cpu_physical_memory_rw(ptr, &ehci->buffer[0], len, 1); + + if (ret != len) { + DPRINTF("ISOCH IN expected %d, got %d\n", + len, ret); + set_field(&itd->transact[i], ret, ITD_XACT_LENGTH); + } + } + } + } + return 0; +} + +/* This state is the entry point for asynchronous schedule + * processing. Entry here consitutes a EHCI start event state (4.8.5) + */ +static int ehci_state_waitlisthead(EHCIState *ehci, int async, int *state) +{ + EHCIqh *qh = &ehci->qh; + int i = 0; + int again = 0; + uint32_t entry = ehci->asynclistaddr; + + /* set reclamation flag at start event (4.8.6) */ + if (async) { + ehci->usbsts |= USBSTS_REC; + } + + /* Find the head of the list (4.9.1.1) */ + for(i = 0; i < MAX_QH; i++) { + get_dwords(NLPTR_GET(entry), (uint32_t *) qh, sizeof(EHCIqh) >> 2); + + if (qh->epchar & QH_EPCHAR_H) { + DPRINTF_ST("WAITLISTHEAD: QH %08X is the HEAD of the list\n", + entry); + if (async) { + entry |= (NLPTR_TYPE_QH << 1); + } + + ehci->fetch_addr = entry; + *state = EST_FETCHENTRY; + again = 1; + goto out; + } + + DPRINTF_ST("WAITLISTHEAD: QH %08X is NOT the HEAD of the list\n", + entry); + entry = qh->next; + if (entry == ehci->asynclistaddr) { + DPRINTF("WAITLISTHEAD: reached beginning of QH list\n"); + break; + } + } + + /* no head found for list. */ + + *state = EST_ACTIVE; + +out: + return again; +} + + +/* This state is the entry point for periodic schedule processing as + * well as being a continuation state for async processing. + */ +static int ehci_state_fetchentry(EHCIState *ehci, int async, int *state) +{ + int again = 0; + uint32_t entry = ehci->fetch_addr; + +#if EHCI_DEBUG == 0 + if (qemu_get_clock_ns(vm_clock) / 1000 >= ehci->frame_end_usec) { + if (async) { + DPRINTF("FETCHENTRY: FRAME timer elapsed, exit state machine\n"); + goto out; + } else { + DPRINTF("FETCHENTRY: WARNING " + "- frame timer elapsed during periodic\n"); + } + } +#endif + if (entry < 0x1000) { + DPRINTF("fetchentry: entry invalid (0x%08x)\n", entry); + *state = EST_ACTIVE; + goto out; + } + + /* section 4.8, only QH in async schedule */ + if (async && (NLPTR_TYPE_GET(entry) != NLPTR_TYPE_QH)) { + fprintf(stderr, "non queue head request in async schedule\n"); + return -1; + } + + switch (NLPTR_TYPE_GET(entry)) { + case NLPTR_TYPE_QH: + DPRINTF_ST("FETCHENTRY: entry %X is a Queue Head\n", entry); + *state = EST_FETCHQH; + ehci->qhaddr = entry; + again = 1; + break; + + case NLPTR_TYPE_ITD: + DPRINTF_ST("FETCHENTRY: entry %X is an ITD\n", entry); + *state = EST_FETCHITD; + ehci->itdaddr = entry; + again = 1; + break; + + default: + // TODO: handle siTD and FSTN types + fprintf(stderr, "FETCHENTRY: entry at %X is of type %d " + "which is not supported yet\n", entry, NLPTR_TYPE_GET(entry)); + return -1; + } + +out: + return again; +} + +static int ehci_state_fetchqh(EHCIState *ehci, int async, int *state) +{ + EHCIqh *qh = &ehci->qh; + int reload; + int again = 0; + + get_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) qh, sizeof(EHCIqh) >> 2); + + if (async && (qh->epchar & QH_EPCHAR_H)) { + + /* EHCI spec version 1.0 Section 4.8.3 & 4.10.1 */ + if (ehci->usbsts & USBSTS_REC) { + ehci->usbsts &= ~USBSTS_REC; + } else { + DPRINTF("FETCHQH: QH 0x%08x. H-bit set, reclamation status reset" + " - done processing\n", ehci->qhaddr); + *state = EST_ACTIVE; + goto out; + } + } + +#if EHCI_DEBUG + if (ehci->qhaddr != qh->next) { + DPRINTF("FETCHQH: QH 0x%08x (h %x halt %x active %x) next 0x%08x\n", + ehci->qhaddr, + qh->epchar & QH_EPCHAR_H, + qh->token & QTD_TOKEN_HALT, + qh->token & QTD_TOKEN_ACTIVE, + qh->next); + } +#endif + + reload = get_field(qh->epchar, QH_EPCHAR_RL); + if (reload) { + DPRINTF_ST("FETCHQH: reloading nakcnt to %d\n", reload); + set_field(&qh->altnext_qtd, reload, QH_ALTNEXT_NAKCNT); + } + + if (qh->token & QTD_TOKEN_HALT) { + DPRINTF_ST("FETCHQH: QH Halted, go horizontal\n"); + *state = EST_HORIZONTALQH; + again = 1; + + } else if ((qh->token & QTD_TOKEN_ACTIVE) && (qh->current_qtd > 0x1000)) { + DPRINTF_ST("FETCHQH: Active, !Halt, execute - fetch qTD\n"); + ehci->qtdaddr = qh->current_qtd; + *state = EST_FETCHQTD; + again = 1; + + } else { + /* EHCI spec version 1.0 Section 4.10.2 */ + DPRINTF_ST("FETCHQH: !Active, !Halt, advance queue\n"); + *state = EST_ADVANCEQUEUE; + again = 1; + } + +out: + return again; +} + +static int ehci_state_fetchitd(EHCIState *ehci, int async, int *state) +{ + EHCIitd itd; + + get_dwords(NLPTR_GET(ehci->itdaddr),(uint32_t *) &itd, + sizeof(EHCIitd) >> 2); + DPRINTF_ST("FETCHITD: Fetched ITD at address %08X " "(next is %08X)\n", + ehci->itdaddr, itd.next); + + if (ehci_process_itd(ehci, &itd) != 0) { + return -1; + } + + put_dwords(NLPTR_GET(ehci->itdaddr), (uint32_t *) &itd, + sizeof(EHCIitd) >> 2); + ehci->fetch_addr = itd.next; + *state = EST_FETCHENTRY; + + return 1; +} + +/* Section 4.10.2 - paragraph 3 */ +static int ehci_state_advqueue(EHCIState *ehci, int async, int *state) +{ +#if 0 + /* TO-DO: 4.10.2 - paragraph 2 + * if I-bit is set to 1 and QH is not active + * go to horizontal QH + */ + if (I-bit set) { + *state = EST_HORIZONTALQH; + goto out; + } +#endif + + /* + * want data and alt-next qTD is valid + */ + if (((ehci->qh.token & QTD_TOKEN_TBYTES_MASK) != 0) && + (ehci->qh.altnext_qtd > 0x1000) && + (NLPTR_TBIT(ehci->qh.altnext_qtd) == 0)) { + DPRINTF_ST("ADVQUEUE: goto alt next qTD. " + "curr 0x%08x next 0x%08x alt 0x%08x (next qh %x)\n", + ehci->qh.current_qtd, ehci->qh.altnext_qtd, + ehci->qh.next_qtd, ehci->qh.next); + ehci->qtdaddr = ehci->qh.altnext_qtd; + *state = EST_FETCHQTD; + + /* + * next qTD is valid + */ + } else if ((ehci->qh.next_qtd > 0x1000) && + (NLPTR_TBIT(ehci->qh.next_qtd) == 0)) { + DPRINTF_ST("ADVQUEUE: next qTD. " + "curr 0x%08x next 0x%08x alt 0x%08x (next qh %x)\n", + ehci->qh.current_qtd, ehci->qh.altnext_qtd, + ehci->qh.next_qtd, ehci->qh.next); + ehci->qtdaddr = ehci->qh.next_qtd; + *state = EST_FETCHQTD; + + /* + * no valid qTD, try next QH + */ + } else { + DPRINTF_ST("ADVQUEUE: go to horizontal QH\n"); + *state = EST_HORIZONTALQH; + } + + return 1; +} + +/* Section 4.10.2 - paragraph 4 */ +static int ehci_state_fetchqtd(EHCIState *ehci, int async, int *state) +{ + EHCIqtd *qtd = &ehci->qtd; + int again = 0; + + get_dwords(NLPTR_GET(ehci->qtdaddr),(uint32_t *) qtd, sizeof(EHCIqtd) >> 2); + + if (qtd->token & QTD_TOKEN_ACTIVE) { + *state = EST_EXECUTE; + again = 1; + } else { + *state = EST_HORIZONTALQH; + again = 1; + } + + return again; +} + +static int ehci_state_horizqh(EHCIState *ehci, int async, int *state) +{ + int again = 0; + + if (ehci->fetch_addr != ehci->qh.next) { + ehci->fetch_addr = ehci->qh.next; + *state = EST_FETCHENTRY; + again = 1; + } else { + *state = EST_ACTIVE; + } + + return again; +} + +static int ehci_state_execute(EHCIState *ehci, int async, int *state) +{ + EHCIqh *qh = &ehci->qh; + EHCIqtd *qtd = &ehci->qtd; + int again = 0; + int reload, nakcnt; + int smask; + + if (async) { + DPRINTF_ST(">>>>> ASYNC STATE MACHINE execute QH 0x%08x, QTD 0x%08x\n", + ehci->qhaddr, ehci->qtdaddr); + } else { + DPRINTF_ST(">>>>> PERIODIC STATE MACHINE execute\n"); + } + + if (ehci_qh_do_overlay(ehci, qh, qtd) != 0) { + return -1; + } + + smask = get_field(qh->epcap, QH_EPCAP_SMASK); + + if (!smask) { + reload = get_field(qh->epchar, QH_EPCHAR_RL); + nakcnt = get_field(qh->altnext_qtd, QH_ALTNEXT_NAKCNT); + if (reload && !nakcnt) { + DPRINTF_ST("EXECUTE: RL != 0 but NakCnt == 0 -- no execute\n"); + *state = EST_HORIZONTALQH; + again = 1; + goto out; + } + } + + // TODO verify enough time remains in the uframe as in 4.4.1.1 + // TODO write back ptr to async list when done or out of time + // TODO Windows does not seem to ever set the MULT field + + if (!async) { + int transactCtr = get_field(qh->epcap, QH_EPCAP_MULT); + if (!transactCtr) { + DPRINTF("ZERO transactctr for int qh, go HORIZ\n"); + *state = EST_HORIZONTALQH; + again = 1; + goto out; + } + } + + if (async) { + ehci->usbsts |= USBSTS_REC; + } + + ehci->exec_status = ehci_execute(ehci, qh); + if (ehci->exec_status == USB_RET_PROCERR) { + again = -1; + goto out; + } + *state = EST_EXECUTING; + + if (ehci->exec_status != USB_RET_ASYNC) { + again = 1; + } + +out: + return again; +} + +static int ehci_state_executing(EHCIState *ehci, int async, int *state) +{ + EHCIqh *qh = &ehci->qh; + int again = 0; + int reload, nakcnt; + + ehci->exec_status = ehci_execute_complete(ehci, qh, ehci->exec_status); + if (ehci->exec_status == USB_RET_ASYNC) { + goto out; + } + if (ehci->exec_status == USB_RET_PROCERR) { + again = -1; + goto out; + } + + // 4.10.3 + if (!async) { + int transactCtr = get_field(qh->epcap, QH_EPCAP_MULT); + transactCtr--; + set_field(&qh->epcap, transactCtr, QH_EPCAP_MULT); + // 4.10.3, bottom of page 82, should exit this state when transaction + // counter decrements to 0 + } + + + reload = get_field(qh->epchar, QH_EPCHAR_RL); + if (reload) { + nakcnt = get_field(qh->altnext_qtd, QH_ALTNEXT_NAKCNT); + if (ehci->exec_status == USB_RET_NAK) { + if (nakcnt) { + nakcnt--; + } + DPRINTF_ST("EXECUTING: Nak occured and RL != 0, dec NakCnt to %d\n", + nakcnt); + } else { + nakcnt = reload; + DPRINTF_ST("EXECUTING: Nak didn't occur, reloading to %d\n", + nakcnt); + } + set_field(&qh->altnext_qtd, nakcnt, QH_ALTNEXT_NAKCNT); + } + + /* + * Write the qh back to guest physical memory. This step isn't + * in the EHCI spec but we need to do it since we don't share + * physical memory with our guest VM. + */ + + DPRINTF("EXECUTING: write QH to VM memory: qhaddr 0x%x, next 0x%x\n", + ehci->qhaddr, qh->next); + put_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) qh, sizeof(EHCIqh) >> 2); + + /* 4.10.5 */ + if ((ehci->exec_status == USB_RET_NAK) || (qh->token & QTD_TOKEN_ACTIVE)) { + *state = EST_HORIZONTALQH; + } else { + *state = EST_WRITEBACK; + } + + again = 1; + +out: + return again; +} + + +static int ehci_state_writeback(EHCIState *ehci, int async, int *state) +{ + EHCIqh *qh = &ehci->qh; + int again = 0; + + /* Write back the QTD from the QH area */ + DPRINTF_ST("WRITEBACK: write QTD to VM memory\n"); + put_dwords(NLPTR_GET(ehci->qtdaddr),(uint32_t *) &qh->next_qtd, + sizeof(EHCIqtd) >> 2); + + /* TODO confirm next state. For now, keep going if async + * but stop after one qtd if periodic + */ + //if (async) { + *state = EST_ADVANCEQUEUE; + again = 1; + //} else { + // *state = EST_ACTIVE; + //} + return again; +} + +/* + * This is the state machine that is common to both async and periodic + */ + +static int ehci_advance_state(EHCIState *ehci, + int async, + int state) +{ + int again; + int iter = 0; + + do { + if (state == EST_FETCHQH) { + iter++; + /* if we are roaming a lot of QH without executing a qTD + * something is wrong with the linked list. TO-DO: why is + * this hack needed? + */ + if (iter > MAX_ITERATIONS) { + DPRINTF("\n*** advance_state: bailing on MAX ITERATIONS***\n"); + state = EST_ACTIVE; + break; + } + } + switch(state) { + case EST_WAITLISTHEAD: + again = ehci_state_waitlisthead(ehci, async, &state); + break; + + case EST_FETCHENTRY: + again = ehci_state_fetchentry(ehci, async, &state); + break; + + case EST_FETCHQH: + again = ehci_state_fetchqh(ehci, async, &state); + break; + + case EST_FETCHITD: + again = ehci_state_fetchitd(ehci, async, &state); + break; + + case EST_ADVANCEQUEUE: + again = ehci_state_advqueue(ehci, async, &state); + break; + + case EST_FETCHQTD: + again = ehci_state_fetchqtd(ehci, async, &state); + break; + + case EST_HORIZONTALQH: + again = ehci_state_horizqh(ehci, async, &state); + break; + + case EST_EXECUTE: + iter = 0; + again = ehci_state_execute(ehci, async, &state); + break; + + case EST_EXECUTING: + again = ehci_state_executing(ehci, async, &state); + break; + + case EST_WRITEBACK: + again = ehci_state_writeback(ehci, async, &state); + break; + + default: + fprintf(stderr, "Bad state!\n"); + again = -1; + break; + } + + if (again < 0) { + fprintf(stderr, "processing error - resetting ehci HC\n"); + ehci_reset(ehci); + again = 0; + } + } + while (again); + + ehci_commit_interrupt(ehci); + return state; +} + +static void ehci_advance_async_state(EHCIState *ehci) +{ + EHCIqh qh; + int state = ehci->astate; + + switch(state) { + case EST_INACTIVE: + if (!(ehci->usbcmd & USBCMD_ASE)) { + break; + } + ehci->usbsts |= USBSTS_ASS; + ehci->astate = EST_ACTIVE; + // No break, fall through to ACTIVE + + case EST_ACTIVE: + if ( !(ehci->usbcmd & USBCMD_ASE)) { + ehci->usbsts &= ~USBSTS_ASS; + ehci->astate = EST_INACTIVE; + break; + } + + /* If the doorbell is set, the guest wants to make a change to the + * schedule. The host controller needs to release cached data. + * (section 4.8.2) + */ + if (ehci->usbcmd & USBCMD_IAAD) { + DPRINTF("ASYNC: doorbell request acknowledged\n"); + ehci->usbcmd &= ~USBCMD_IAAD; + ehci_set_interrupt(ehci, USBSTS_IAA); + break; + } + + /* make sure guest has acknowledged */ + /* TO-DO: is this really needed? */ + if (ehci->usbsts & USBSTS_IAA) { + DPRINTF("IAA status bit still set.\n"); + break; + } + + DPRINTF_ST("ASYNC: waiting for listhead, starting at %08x\n", + ehci->asynclistaddr); + /* check that address register has been set */ + if (ehci->asynclistaddr == 0) { + break; + } + + state = EST_WAITLISTHEAD; + /* fall through */ + + case EST_FETCHENTRY: + /* fall through */ + + case EST_EXECUTING: + get_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) &qh, + sizeof(EHCIqh) >> 2); + ehci->astate = ehci_advance_state(ehci, 1, state); + break; + + default: + /* this should only be due to a developer mistake */ + fprintf(stderr, "ehci: Bad asynchronous state %d. " + "Resetting to active\n", ehci->astate); + ehci->astate = EST_ACTIVE; + } +} + +static void ehci_advance_periodic_state(EHCIState *ehci) +{ + uint32_t entry; + uint32_t list; + + // 4.6 + + switch(ehci->pstate) { + case EST_INACTIVE: + if ( !(ehci->frindex & 7) && (ehci->usbcmd & USBCMD_PSE)) { + DPRINTF("PERIODIC going active\n"); + ehci->usbsts |= USBSTS_PSS; + ehci->pstate = EST_ACTIVE; + // No break, fall through to ACTIVE + } else + break; + + case EST_ACTIVE: + if ( !(ehci->frindex & 7) && !(ehci->usbcmd & USBCMD_PSE)) { + DPRINTF("PERIODIC going inactive\n"); + ehci->usbsts &= ~USBSTS_PSS; + ehci->pstate = EST_INACTIVE; + break; + } + + list = ehci->periodiclistbase & 0xfffff000; + /* check that register has been set */ + if (list == 0) { + break; + } + list |= ((ehci->frindex & 0x1ff8) >> 1); + + cpu_physical_memory_rw(list, (uint8_t *) &entry, sizeof entry, 0); + entry = le32_to_cpu(entry); + + DPRINTF("PERIODIC state adv fr=%d. [%08X] -> %08X\n", + ehci->frindex / 8, list, entry); + ehci->fetch_addr = entry; + ehci->pstate = ehci_advance_state(ehci, 0, EST_FETCHENTRY); + break; + + case EST_EXECUTING: + DPRINTF("PERIODIC state adv for executing\n"); + ehci->pstate = ehci_advance_state(ehci, 0, EST_EXECUTING); + break; + + default: + /* this should only be due to a developer mistake */ + fprintf(stderr, "ehci: Bad periodic state %d. " + "Resetting to active\n", ehci->pstate); + ehci->pstate = EST_ACTIVE; + } +} + +static void ehci_frame_timer(void *opaque) +{ + EHCIState *ehci = opaque; + int64_t expire_time, t_now; + int usec_elapsed; + int frames; + int usec_now; + int i; + int skipped_frames = 0; + + + t_now = qemu_get_clock_ns(vm_clock); + expire_time = t_now + (get_ticks_per_sec() / FRAME_TIMER_FREQ); + if (expire_time == t_now) { + expire_time++; + } + + usec_now = t_now / 1000; + usec_elapsed = usec_now - ehci->last_run_usec; + frames = usec_elapsed / FRAME_TIMER_USEC; + ehci->frame_end_usec = usec_now + FRAME_TIMER_USEC - 10; + + for (i = 0; i < frames; i++) { + if ( !(ehci->usbsts & USBSTS_HALT)) { + if (ehci->isoch_pause <= 0) { + ehci->frindex += 8; + } + + if (ehci->frindex > 0x00001fff) { + ehci->frindex = 0; + ehci_set_interrupt(ehci, USBSTS_FLR); + } + + ehci->sofv = (ehci->frindex - 1) >> 3; + ehci->sofv &= 0x000003ff; + } + + if (frames - i > 10) { + skipped_frames++; + } else { + // TODO could this cause periodic frames to get skipped if async + // active? + if (ehci->astate != EST_EXECUTING) { + ehci_advance_periodic_state(ehci); + } + } + + ehci->last_run_usec += FRAME_TIMER_USEC; + } + +#if 0 + if (skipped_frames) { + DPRINTF("WARNING - EHCI skipped %d frames\n", skipped_frames); + } +#endif + + /* Async is not inside loop since it executes everything it can once + * called + */ + if (ehci->pstate != EST_EXECUTING) { + ehci_advance_async_state(ehci); + } + + qemu_mod_timer(ehci->frame_timer, expire_time); +} + +static CPUReadMemoryFunc *ehci_readfn[3]={ + ehci_mem_readb, + ehci_mem_readw, + ehci_mem_readl +}; + +static CPUWriteMemoryFunc *ehci_writefn[3]={ + ehci_mem_writeb, + ehci_mem_writew, + ehci_mem_writel +}; + +static void ehci_map(PCIDevice *pci_dev, int region_num, + pcibus_t addr, pcibus_t size, int type) +{ + EHCIState *s =(EHCIState *)pci_dev; + + DPRINTF("ehci_map: region %d, addr %08" PRIx64 ", size %" PRId64 ", s->mem %08X\n", + region_num, addr, size, s->mem); + s->mem_base = addr; + cpu_register_physical_memory(addr, size, s->mem); +} + +static int usb_ehci_initfn(PCIDevice *dev); + +static USBPortOps ehci_port_ops = { + .attach = ehci_attach, + .detach = ehci_detach, + .complete = ehci_async_complete_packet, +}; + +static PCIDeviceInfo ehci_info = { + .qdev.name = "usb-ehci", + .qdev.size = sizeof(EHCIState), + .init = usb_ehci_initfn, +}; + +static int usb_ehci_initfn(PCIDevice *dev) +{ + EHCIState *s = DO_UPCAST(EHCIState, dev, dev); + uint8_t *pci_conf = s->dev.config; + int i; + + pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL); + pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82801D); + pci_set_byte(&pci_conf[PCI_REVISION_ID], 0x10); + pci_set_byte(&pci_conf[PCI_CLASS_PROG], 0x20); + pci_config_set_class(pci_conf, PCI_CLASS_SERIAL_USB); + pci_set_byte(&pci_conf[PCI_HEADER_TYPE], PCI_HEADER_TYPE_NORMAL); + + /* capabilities pointer */ + pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x00); + //pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x50); + + pci_set_byte(&pci_conf[PCI_INTERRUPT_PIN], 4); // interrupt pin 3 + pci_set_byte(&pci_conf[PCI_MIN_GNT], 0); + pci_set_byte(&pci_conf[PCI_MAX_LAT], 0); + + // pci_conf[0x50] = 0x01; // power management caps + + pci_set_byte(&pci_conf[0x60], 0x20); // spec release number (2.1.4) + pci_set_byte(&pci_conf[0x61], 0x20); // frame length adjustment (2.1.5) + pci_set_word(&pci_conf[0x62], 0x00); // port wake up capability (2.1.6) + + pci_conf[0x64] = 0x00; + pci_conf[0x65] = 0x00; + pci_conf[0x66] = 0x00; + pci_conf[0x67] = 0x00; + pci_conf[0x68] = 0x01; + pci_conf[0x69] = 0x00; + pci_conf[0x6a] = 0x00; + pci_conf[0x6b] = 0x00; // USBLEGSUP + pci_conf[0x6c] = 0x00; + pci_conf[0x6d] = 0x00; + pci_conf[0x6e] = 0x00; + pci_conf[0x6f] = 0xc0; // USBLEFCTLSTS + + // 2.2 host controller interface version + s->mmio[0x00] = (uint8_t) OPREGBASE; + s->mmio[0x01] = 0x00; + s->mmio[0x02] = 0x00; + s->mmio[0x03] = 0x01; // HC version + s->mmio[0x04] = NB_PORTS; // Number of downstream ports + s->mmio[0x05] = 0x00; // No companion ports at present + s->mmio[0x06] = 0x00; + s->mmio[0x07] = 0x00; + s->mmio[0x08] = 0x80; // We can cache whole frame, not 64-bit capable + s->mmio[0x09] = 0x68; // EECP + s->mmio[0x0a] = 0x00; + s->mmio[0x0b] = 0x00; + + s->irq = s->dev.irq[3]; + + usb_bus_new(&s->bus, &s->dev.qdev); + for(i = 0; i < NB_PORTS; i++) { + usb_register_port(&s->bus, &s->ports[i], s, i, &ehci_port_ops, + USB_SPEED_MASK_HIGH); + usb_port_location(&s->ports[i], NULL, i+1); + s->ports[i].dev = 0; + } + + s->frame_timer = qemu_new_timer_ns(vm_clock, ehci_frame_timer, s); + + qemu_register_reset(ehci_reset, s); + + s->mem = cpu_register_io_memory(ehci_readfn, ehci_writefn, s, + DEVICE_LITTLE_ENDIAN); + + pci_register_bar(&s->dev, 0, MMIO_SIZE, PCI_BASE_ADDRESS_SPACE_MEMORY, + ehci_map); + + fprintf(stderr, "*** EHCI support is under development ***\n"); + + return 0; +} + +static void ehci_register(void) +{ + pci_qdev_register(&ehci_info); +} +device_init(ehci_register); + +/* + * vim: expandtab ts=4 + */ diff --git a/hw/usb-hid.c b/hw/usb-hid.c index 89c293c466..53b261c3b9 100644 --- a/hw/usb-hid.c +++ b/hw/usb-hid.c @@ -211,6 +211,7 @@ static const USBDescDevice desc_device_mouse = { .iConfiguration = STR_CONFIG_MOUSE, .bmAttributes = 0xa0, .bMaxPower = 50, + .nif = 1, .ifs = &desc_iface_mouse, }, }, @@ -227,6 +228,7 @@ static const USBDescDevice desc_device_tablet = { .iConfiguration = STR_CONFIG_TABLET, .bmAttributes = 0xa0, .bMaxPower = 50, + .nif = 1, .ifs = &desc_iface_tablet, }, }, @@ -243,6 +245,7 @@ static const USBDescDevice desc_device_keyboard = { .iConfiguration = STR_CONFIG_KEYBOARD, .bmAttributes = 0xa0, .bMaxPower = 50, + .nif = 1, .ifs = &desc_iface_keyboard, }, }, @@ -724,13 +727,13 @@ static void usb_hid_set_next_idle(USBHIDState *s, int64_t curtime) s->next_idle_clock = curtime + (get_ticks_per_sec() * s->idle * 4) / 1000; } -static int usb_hid_handle_control(USBDevice *dev, int request, int value, - int index, int length, uint8_t *data) +static int usb_hid_handle_control(USBDevice *dev, USBPacket *p, + int request, int value, int index, int length, uint8_t *data) { USBHIDState *s = (USBHIDState *)dev; int ret; - ret = usb_desc_handle_control(dev, request, value, index, length, data); + ret = usb_desc_handle_control(dev, p, request, value, index, length, data); if (ret >= 0) { return ret; } diff --git a/hw/usb-hub.c b/hw/usb-hub.c index 3dd31ba31f..6e2a35839d 100644 --- a/hw/usb-hub.c +++ b/hw/usb-hub.c @@ -119,6 +119,7 @@ static const USBDescDevice desc_device_hub = { .bNumInterfaces = 1, .bConfigurationValue = 1, .bmAttributes = 0xe0, + .nif = 1, .ifs = &desc_iface_hub, }, }, @@ -256,6 +257,19 @@ static void usb_hub_wakeup(USBDevice *dev) } } +static void usb_hub_complete(USBDevice *dev, USBPacket *packet) +{ + USBHubState *s = dev->port->opaque; + + /* + * Just pass it along upstream for now. + * + * If we ever inplement usb 2.0 split transactions this will + * become a little more complicated ... + */ + usb_packet_complete(&s->dev, packet); +} + static void usb_hub_handle_attach(USBDevice *dev) { USBHubState *s = DO_UPCAST(USBHubState, dev, dev); @@ -271,13 +285,13 @@ static void usb_hub_handle_reset(USBDevice *dev) /* XXX: do it */ } -static int usb_hub_handle_control(USBDevice *dev, int request, int value, - int index, int length, uint8_t *data) +static int usb_hub_handle_control(USBDevice *dev, USBPacket *p, + int request, int value, int index, int length, uint8_t *data) { USBHubState *s = (USBHubState *)dev; int ret; - ret = usb_desc_handle_control(dev, request, value, index, length, data); + ret = usb_desc_handle_control(dev, p, request, value, index, length, data); if (ret >= 0) { return ret; } @@ -481,7 +495,7 @@ static int usb_hub_broadcast_packet(USBHubState *s, USBPacket *p) port = &s->ports[i]; dev = port->port.dev; if (dev && (port->wPortStatus & PORT_STAT_ENABLE)) { - ret = dev->info->handle_packet(dev, p); + ret = usb_handle_packet(dev, p); if (ret != USB_RET_NODEV) { return ret; } @@ -524,6 +538,7 @@ static USBPortOps usb_hub_port_ops = { .attach = usb_hub_attach, .detach = usb_hub_detach, .wakeup = usb_hub_wakeup, + .complete = usb_hub_complete, }; static int usb_hub_initfn(USBDevice *dev) diff --git a/hw/usb-msd.c b/hw/usb-msd.c index 947fd3f83c..c59797b27e 100644 --- a/hw/usb-msd.c +++ b/hw/usb-msd.c @@ -48,6 +48,7 @@ typedef struct { uint32_t data_len; uint32_t residue; uint32_t tag; + SCSIRequest *req; SCSIBus bus; BlockConf conf; SCSIDevice *scsi_dev; @@ -119,6 +120,7 @@ static const USBDescDevice desc_device_full = { .bConfigurationValue = 1, .iConfiguration = STR_CONFIG_FULL, .bmAttributes = 0xc0, + .nif = 1, .ifs = &desc_iface_full, }, }, @@ -153,6 +155,7 @@ static const USBDescDevice desc_device_high = { .bConfigurationValue = 1, .iConfiguration = STR_CONFIG_HIGH, .bmAttributes = 0xc0, + .nif = 1, .ifs = &desc_iface_high, }, }, @@ -189,11 +192,7 @@ static void usb_msd_copy_data(MSDState *s) s->scsi_buf += len; s->data_len -= len; if (s->scsi_len == 0 || s->data_len == 0) { - if (s->mode == USB_MSDM_DATAIN) { - s->scsi_dev->info->read_data(s->scsi_dev, s->tag); - } else if (s->mode == USB_MSDM_DATAOUT) { - s->scsi_dev->info->write_data(s->scsi_dev, s->tag); - } + scsi_req_continue(s->req); } } @@ -211,54 +210,78 @@ static void usb_msd_send_status(MSDState *s, USBPacket *p) memcpy(p->data, &csw, len); } -static void usb_msd_command_complete(SCSIBus *bus, int reason, uint32_t tag, - uint32_t arg) +static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len) { - MSDState *s = DO_UPCAST(MSDState, dev.qdev, bus->qbus.parent); + MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); USBPacket *p = s->packet; - if (tag != s->tag) { - fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", tag); - } - if (reason == SCSI_REASON_DONE) { - DPRINTF("Command complete %d\n", arg); - s->residue = s->data_len; - s->result = arg != 0; - if (s->packet) { - if (s->data_len == 0 && s->mode == USB_MSDM_DATAOUT) { - /* A deferred packet with no write data remaining must be - the status read packet. */ - usb_msd_send_status(s, p); - s->mode = USB_MSDM_CBW; - } else { - if (s->data_len) { - s->data_len -= s->usb_len; - if (s->mode == USB_MSDM_DATAIN) - memset(s->usb_buf, 0, s->usb_len); - s->usb_len = 0; - } - if (s->data_len == 0) - s->mode = USB_MSDM_CSW; - } - s->packet = NULL; - usb_packet_complete(p); - } else if (s->data_len == 0) { - s->mode = USB_MSDM_CSW; - } - return; + if (req->tag != s->tag) { + fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", req->tag); } - s->scsi_len = arg; - s->scsi_buf = s->scsi_dev->info->get_buf(s->scsi_dev, tag); + + assert((s->mode == USB_MSDM_DATAOUT) == (req->cmd.mode == SCSI_XFER_TO_DEV)); + s->scsi_len = len; + s->scsi_buf = scsi_req_get_buf(req); if (p) { usb_msd_copy_data(s); - if (s->usb_len == 0) { + if (s->packet && s->usb_len == 0) { /* Set s->packet to NULL before calling usb_packet_complete because another request may be issued before usb_packet_complete returns. */ DPRINTF("Packet complete %p\n", p); s->packet = NULL; - usb_packet_complete(p); + usb_packet_complete(&s->dev, p); + } + } +} + +static void usb_msd_command_complete(SCSIRequest *req, uint32_t status) +{ + MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); + USBPacket *p = s->packet; + + if (req->tag != s->tag) { + fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", req->tag); + } + DPRINTF("Command complete %d\n", status); + s->residue = s->data_len; + s->result = status != 0; + if (s->packet) { + if (s->data_len == 0 && s->mode == USB_MSDM_DATAOUT) { + /* A deferred packet with no write data remaining must be + the status read packet. */ + usb_msd_send_status(s, p); + s->mode = USB_MSDM_CBW; + } else { + if (s->data_len) { + s->data_len -= s->usb_len; + if (s->mode == USB_MSDM_DATAIN) { + memset(s->usb_buf, 0, s->usb_len); + } + s->usb_len = 0; + } + if (s->data_len == 0) { + s->mode = USB_MSDM_CSW; + } } + s->packet = NULL; + usb_packet_complete(&s->dev, p); + } else if (s->data_len == 0) { + s->mode = USB_MSDM_CSW; + } + scsi_req_unref(req); + s->req = NULL; +} + +static void usb_msd_request_cancelled(SCSIRequest *req) +{ + MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); + + if (req == s->req) { + scsi_req_unref(s->req); + s->req = NULL; + s->packet = NULL; + s->scsi_len = 0; } } @@ -270,13 +293,13 @@ static void usb_msd_handle_reset(USBDevice *dev) s->mode = USB_MSDM_CBW; } -static int usb_msd_handle_control(USBDevice *dev, int request, int value, - int index, int length, uint8_t *data) +static int usb_msd_handle_control(USBDevice *dev, USBPacket *p, + int request, int value, int index, int length, uint8_t *data) { MSDState *s = (MSDState *)dev; int ret; - ret = usb_desc_handle_control(dev, request, value, index, length, data); + ret = usb_desc_handle_control(dev, p, request, value, index, length, data); if (ret >= 0) { return ret; } @@ -313,12 +336,10 @@ static int usb_msd_handle_control(USBDevice *dev, int request, int value, return ret; } -static void usb_msd_cancel_io(USBPacket *p, void *opaque) +static void usb_msd_cancel_io(USBDevice *dev, USBPacket *p) { - MSDState *s = opaque; - s->scsi_dev->info->cancel_io(s->scsi_dev, s->tag); - s->packet = NULL; - s->scsi_len = 0; + MSDState *s = DO_UPCAST(MSDState, dev, dev); + scsi_req_cancel(s->req); } static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) @@ -364,15 +385,13 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) DPRINTF("Command tag 0x%x flags %08x len %d data %d\n", s->tag, cbw.flags, cbw.cmd_len, s->data_len); s->residue = 0; - s->scsi_dev->info->send_command(s->scsi_dev, s->tag, cbw.cmd, 0); + s->scsi_len = 0; + s->req = scsi_req_new(s->scsi_dev, s->tag, 0); + scsi_req_enqueue(s->req, cbw.cmd); /* ??? Should check that USB and SCSI data transfer directions match. */ - if (s->residue == 0) { - if (s->mode == USB_MSDM_DATAIN) { - s->scsi_dev->info->read_data(s->scsi_dev, s->tag); - } else if (s->mode == USB_MSDM_DATAOUT) { - s->scsi_dev->info->write_data(s->scsi_dev, s->tag); - } + if (s->mode != USB_MSDM_CSW && s->residue == 0) { + scsi_req_continue(s->req); } ret = len; break; @@ -395,7 +414,6 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) } if (s->usb_len) { DPRINTF("Deferring packet %p\n", p); - usb_defer_packet(p, usb_msd_cancel_io, s); s->packet = p; ret = USB_RET_ASYNC; } else { @@ -418,7 +436,6 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) if (s->data_len != 0 || len < 13) goto fail; /* Waiting for SCSI write to complete. */ - usb_defer_packet(p, usb_msd_cancel_io, s); s->packet = p; ret = USB_RET_ASYNC; break; @@ -452,7 +469,6 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) } if (s->usb_len) { DPRINTF("Deferring packet %p\n", p); - usb_defer_packet(p, usb_msd_cancel_io, s); s->packet = p; ret = USB_RET_ASYNC; } else { @@ -486,6 +502,12 @@ static void usb_msd_password_cb(void *opaque, int err) qdev_unplug(&s->dev.qdev); } +static const struct SCSIBusOps usb_msd_scsi_ops = { + .transfer_data = usb_msd_transfer_data, + .complete = usb_msd_command_complete, + .cancel = usb_msd_request_cancelled +}; + static int usb_msd_initfn(USBDevice *dev) { MSDState *s = DO_UPCAST(MSDState, dev, dev); @@ -515,7 +537,7 @@ static int usb_msd_initfn(USBDevice *dev) } usb_desc_init(dev); - scsi_bus_new(&s->bus, &s->dev.qdev, 0, 1, usb_msd_command_complete); + scsi_bus_new(&s->bus, &s->dev.qdev, 0, 1, &usb_msd_scsi_ops); s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, bs, 0, !!s->removable); if (!s->scsi_dev) { return -1; @@ -601,6 +623,7 @@ static struct USBDeviceInfo msd_info = { .usb_desc = &desc, .init = usb_msd_initfn, .handle_packet = usb_generic_handle_packet, + .cancel_packet = usb_msd_cancel_io, .handle_attach = usb_desc_attach, .handle_reset = usb_msd_handle_reset, .handle_control = usb_msd_handle_control, diff --git a/hw/usb-musb.c b/hw/usb-musb.c index 15bc549a85..6037193db8 100644 --- a/hw/usb-musb.c +++ b/hw/usb-musb.c @@ -261,13 +261,24 @@ static void musb_attach(USBPort *port); static void musb_detach(USBPort *port); +static void musb_schedule_cb(USBDevice *dev, USBPacket *p); static USBPortOps musb_port_ops = { .attach = musb_attach, .detach = musb_detach, + .complete = musb_schedule_cb, }; -typedef struct { +typedef struct MUSBPacket MUSBPacket; +typedef struct MUSBEndPoint MUSBEndPoint; + +struct MUSBPacket { + USBPacket p; + MUSBEndPoint *ep; + int dir; +}; + +struct MUSBEndPoint { uint16_t faddr[2]; uint8_t haddr[2]; uint8_t hport[2]; @@ -284,7 +295,7 @@ typedef struct { int fifolen[2]; int fifostart[2]; int fifoaddr[2]; - USBPacket packey[2]; + MUSBPacket packey[2]; int status[2]; int ext_size[2]; @@ -294,7 +305,7 @@ typedef struct { MUSBState *musb; USBCallback *delayed_cb[2]; QEMUTimer *intv_timer[2]; -} MUSBEndPoint; +}; struct MUSBState { qemu_irq *irqs; @@ -321,7 +332,9 @@ struct MUSBState { /* Duplicating the world since 2008!... probably we should have 32 * logical, single endpoints instead. */ MUSBEndPoint ep[16]; -} *musb_init(qemu_irq *irqs) +}; + +struct MUSBState *musb_init(qemu_irq *irqs) { MUSBState *s = qemu_mallocz(sizeof(*s)); int i; @@ -484,25 +497,27 @@ static void musb_detach(USBPort *port) musb_session_update(s, 1, s->session); } -static inline void musb_cb_tick0(void *opaque) +static void musb_cb_tick0(void *opaque) { MUSBEndPoint *ep = (MUSBEndPoint *) opaque; - ep->delayed_cb[0](&ep->packey[0], opaque); + ep->delayed_cb[0](&ep->packey[0].p, opaque); } -static inline void musb_cb_tick1(void *opaque) +static void musb_cb_tick1(void *opaque) { MUSBEndPoint *ep = (MUSBEndPoint *) opaque; - ep->delayed_cb[1](&ep->packey[1], opaque); + ep->delayed_cb[1](&ep->packey[1].p, opaque); } #define musb_cb_tick (dir ? musb_cb_tick1 : musb_cb_tick0) -static inline void musb_schedule_cb(USBPacket *packey, void *opaque, int dir) +static void musb_schedule_cb(USBDevice *dev, USBPacket *packey) { - MUSBEndPoint *ep = (MUSBEndPoint *) opaque; + MUSBPacket *p = container_of(packey, MUSBPacket, p); + MUSBEndPoint *ep = p->ep; + int dir = p->dir; int timeout = 0; if (ep->status[dir] == USB_RET_NAK) @@ -510,25 +525,15 @@ static inline void musb_schedule_cb(USBPacket *packey, void *opaque, int dir) else if (ep->interrupt[dir]) timeout = 8; else - return musb_cb_tick(opaque); + return musb_cb_tick(ep); if (!ep->intv_timer[dir]) - ep->intv_timer[dir] = qemu_new_timer_ns(vm_clock, musb_cb_tick, opaque); + ep->intv_timer[dir] = qemu_new_timer_ns(vm_clock, musb_cb_tick, ep); qemu_mod_timer(ep->intv_timer[dir], qemu_get_clock_ns(vm_clock) + muldiv64(timeout, get_ticks_per_sec(), 8000)); } -static void musb_schedule0_cb(USBPacket *packey, void *opaque) -{ - return musb_schedule_cb(packey, opaque, 0); -} - -static void musb_schedule1_cb(USBPacket *packey, void *opaque) -{ - return musb_schedule_cb(packey, opaque, 1); -} - static int musb_timeout(int ttype, int speed, int val) { #if 1 @@ -567,7 +572,7 @@ static int musb_timeout(int ttype, int speed, int val) hw_error("bad interval\n"); } -static inline void musb_packet(MUSBState *s, MUSBEndPoint *ep, +static void musb_packet(MUSBState *s, MUSBEndPoint *ep, int epnum, int pid, int len, USBCallback cb, int dir) { int ret; @@ -585,19 +590,18 @@ static inline void musb_packet(MUSBState *s, MUSBEndPoint *ep, ep->type[idx] >> 6, ep->interval[idx]); ep->interrupt[dir] = ttype == USB_ENDPOINT_XFER_INT; ep->delayed_cb[dir] = cb; - cb = dir ? musb_schedule1_cb : musb_schedule0_cb; - ep->packey[dir].pid = pid; + ep->packey[dir].p.pid = pid; /* A wild guess on the FADDR semantics... */ - ep->packey[dir].devaddr = ep->faddr[idx]; - ep->packey[dir].devep = ep->type[idx] & 0xf; - ep->packey[dir].data = (void *) ep->buf[idx]; - ep->packey[dir].len = len; - ep->packey[dir].complete_cb = cb; - ep->packey[dir].complete_opaque = ep; + ep->packey[dir].p.devaddr = ep->faddr[idx]; + ep->packey[dir].p.devep = ep->type[idx] & 0xf; + ep->packey[dir].p.data = (void *) ep->buf[idx]; + ep->packey[dir].p.len = len; + ep->packey[dir].ep = ep; + ep->packey[dir].dir = dir; if (s->port.dev) - ret = s->port.dev->info->handle_packet(s->port.dev, &ep->packey[dir]); + ret = usb_handle_packet(s->port.dev, &ep->packey[dir].p); else ret = USB_RET_NODEV; @@ -607,7 +611,7 @@ static inline void musb_packet(MUSBState *s, MUSBEndPoint *ep, } ep->status[dir] = ret; - usb_packet_complete(&ep->packey[dir]); + usb_packet_complete(s->port.dev, &ep->packey[dir].p); } static void musb_tx_packet_complete(USBPacket *packey, void *opaque) @@ -821,14 +825,14 @@ static void musb_rx_req(MUSBState *s, int epnum) /* If we already have a packet, which didn't fit into the * 64 bytes of the FIFO, only move the FIFO start and return. (Obsolete) */ - if (ep->packey[1].pid == USB_TOKEN_IN && ep->status[1] >= 0 && + if (ep->packey[1].p.pid == USB_TOKEN_IN && ep->status[1] >= 0 && (ep->fifostart[1]) + ep->rxcount < - ep->packey[1].len) { + ep->packey[1].p.len) { TRACE("0x%08x, %d", ep->fifostart[1], ep->rxcount ); ep->fifostart[1] += ep->rxcount; ep->fifolen[1] = 0; - ep->rxcount = MIN(ep->packey[0].len - (ep->fifostart[1]), + ep->rxcount = MIN(ep->packey[0].p.len - (ep->fifostart[1]), ep->maxp[1]); ep->csr[1] &= ~MGC_M_RXCSR_H_REQPKT; @@ -866,10 +870,11 @@ static void musb_rx_req(MUSBState *s, int epnum) #ifdef SETUPLEN_HACK /* Why should *we* do that instead of Linux? */ if (!epnum) { - if (ep->packey[0].devaddr == 2) + if (ep->packey[0].p.devaddr == 2) { total = MIN(s->setup_len, 8); - else + } else { total = MIN(s->setup_len, 64); + } s->setup_len -= total; } #endif diff --git a/hw/usb-net.c b/hw/usb-net.c index bf51bb3890..9be709f7cf 100644 --- a/hw/usb-net.c +++ b/hw/usb-net.c @@ -1042,13 +1042,13 @@ static void usb_net_handle_reset(USBDevice *dev) { } -static int usb_net_handle_control(USBDevice *dev, int request, int value, - int index, int length, uint8_t *data) +static int usb_net_handle_control(USBDevice *dev, USBPacket *p, + int request, int value, int index, int length, uint8_t *data) { USBNetState *s = (USBNetState *) dev; int ret; - ret = usb_desc_handle_control(dev, request, value, index, length, data); + ret = usb_desc_handle_control(dev, p, request, value, index, length, data); if (ret >= 0) { return ret; } diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c index 7ff23228cc..a67556af41 100644 --- a/hw/usb-ohci.c +++ b/hw/usb-ohci.c @@ -575,9 +575,9 @@ static void ohci_copy_iso_td(OHCIState *ohci, static void ohci_process_lists(OHCIState *ohci, int completion); -static void ohci_async_complete_packet(USBPacket *packet, void *opaque) +static void ohci_async_complete_packet(USBDevice *dev, USBPacket *packet) { - OHCIState *ohci = opaque; + OHCIState *ohci = container_of(packet, OHCIState, usb_packet); #ifdef DEBUG_PACKET DPRINTF("Async packet complete\n"); #endif @@ -748,9 +748,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN); ohci->usb_packet.data = ohci->usb_buf; ohci->usb_packet.len = len; - ohci->usb_packet.complete_cb = ohci_async_complete_packet; - ohci->usb_packet.complete_opaque = ohci; - ret = dev->info->handle_packet(dev, &ohci->usb_packet); + ret = usb_handle_packet(dev, &ohci->usb_packet); if (ret != USB_RET_NODEV) break; } @@ -946,9 +944,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN); ohci->usb_packet.data = ohci->usb_buf; ohci->usb_packet.len = len; - ohci->usb_packet.complete_cb = ohci_async_complete_packet; - ohci->usb_packet.complete_opaque = ohci; - ret = dev->info->handle_packet(dev, &ohci->usb_packet); + ret = usb_handle_packet(dev, &ohci->usb_packet); if (ret != USB_RET_NODEV) break; } @@ -1665,6 +1661,7 @@ static CPUWriteMemoryFunc * const ohci_writefn[3]={ static USBPortOps ohci_port_ops = { .attach = ohci_attach, .detach = ohci_detach, + .complete = ohci_async_complete_packet, }; static void usb_ohci_init(OHCIState *ohci, DeviceState *dev, diff --git a/hw/usb-serial.c b/hw/usb-serial.c index 6763d52040..59cb0fb2f7 100644 --- a/hw/usb-serial.c +++ b/hw/usb-serial.c @@ -146,6 +146,7 @@ static const USBDescDevice desc_device = { .bConfigurationValue = 1, .bmAttributes = 0x80, .bMaxPower = 50, + .nif = 1, .ifs = &desc_iface0, }, }, @@ -218,14 +219,14 @@ static uint8_t usb_get_modem_lines(USBSerialState *s) return ret; } -static int usb_serial_handle_control(USBDevice *dev, int request, int value, - int index, int length, uint8_t *data) +static int usb_serial_handle_control(USBDevice *dev, USBPacket *p, + int request, int value, int index, int length, uint8_t *data) { USBSerialState *s = (USBSerialState *)dev; int ret; DPRINTF("got control %x, value %x\n",request, value); - ret = usb_desc_handle_control(dev, request, value, index, length, data); + ret = usb_desc_handle_control(dev, p, request, value, index, length, data); if (ret >= 0) { return ret; } diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c index 536c24c825..872a995575 100644 --- a/hw/usb-uhci.c +++ b/hw/usb-uhci.c @@ -106,6 +106,8 @@ static void dump_data(const uint8_t *data, int len) static void dump_data(const uint8_t *data, int len) {} #endif +typedef struct UHCIState UHCIState; + /* * Pending async transaction. * 'packet' must be the first field because completion @@ -113,7 +115,8 @@ static void dump_data(const uint8_t *data, int len) {} */ typedef struct UHCIAsync { USBPacket packet; - struct UHCIAsync *next; + UHCIState *uhci; + QTAILQ_ENTRY(UHCIAsync) next; uint32_t td; uint32_t token; int8_t valid; @@ -127,7 +130,7 @@ typedef struct UHCIPort { uint16_t ctrl; } UHCIPort; -typedef struct UHCIState { +struct UHCIState { PCIDevice dev; USBBus bus; uint16_t cmd; /* cmd register */ @@ -145,10 +148,9 @@ typedef struct UHCIState { uint32_t pending_int_mask; /* Active packets */ - UHCIAsync *async_pending; - UHCIAsync *async_pool; + QTAILQ_HEAD(,UHCIAsync) async_pending; uint8_t num_ports_vmstate; -} UHCIState; +}; typedef struct UHCI_TD { uint32_t link; @@ -167,12 +169,12 @@ static UHCIAsync *uhci_async_alloc(UHCIState *s) UHCIAsync *async = qemu_malloc(sizeof(UHCIAsync)); memset(&async->packet, 0, sizeof(async->packet)); + async->uhci = s; async->valid = 0; async->td = 0; async->token = 0; async->done = 0; async->isoc = 0; - async->next = NULL; return async; } @@ -184,24 +186,12 @@ static void uhci_async_free(UHCIState *s, UHCIAsync *async) static void uhci_async_link(UHCIState *s, UHCIAsync *async) { - async->next = s->async_pending; - s->async_pending = async; + QTAILQ_INSERT_HEAD(&s->async_pending, async, next); } static void uhci_async_unlink(UHCIState *s, UHCIAsync *async) { - UHCIAsync *curr = s->async_pending; - UHCIAsync **prev = &s->async_pending; - - while (curr) { - if (curr == async) { - *prev = curr->next; - return; - } - - prev = &curr->next; - curr = curr->next; - } + QTAILQ_REMOVE(&s->async_pending, async, next); } static void uhci_async_cancel(UHCIState *s, UHCIAsync *async) @@ -220,11 +210,10 @@ static void uhci_async_cancel(UHCIState *s, UHCIAsync *async) */ static UHCIAsync *uhci_async_validate_begin(UHCIState *s) { - UHCIAsync *async = s->async_pending; + UHCIAsync *async; - while (async) { + QTAILQ_FOREACH(async, &s->async_pending, next) { async->valid--; - async = async->next; } return NULL; } @@ -234,47 +223,30 @@ static UHCIAsync *uhci_async_validate_begin(UHCIState *s) */ static void uhci_async_validate_end(UHCIState *s) { - UHCIAsync *curr = s->async_pending; - UHCIAsync **prev = &s->async_pending; - UHCIAsync *next; + UHCIAsync *curr, *n; - while (curr) { + QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) { if (curr->valid > 0) { - prev = &curr->next; - curr = curr->next; continue; } - - next = curr->next; - - /* Unlink */ - *prev = next; - + uhci_async_unlink(s, curr); uhci_async_cancel(s, curr); - - curr = next; } } static void uhci_async_cancel_all(UHCIState *s) { - UHCIAsync *curr = s->async_pending; - UHCIAsync *next; - - while (curr) { - next = curr->next; + UHCIAsync *curr, *n; + QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) { + uhci_async_unlink(s, curr); uhci_async_cancel(s, curr); - - curr = next; } - - s->async_pending = NULL; } static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, uint32_t token) { - UHCIAsync *async = s->async_pending; + UHCIAsync *async; UHCIAsync *match = NULL; int count = 0; @@ -291,7 +263,7 @@ static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, uint32_t token * If we ever do we'd want to optimize this algorithm. */ - while (async) { + QTAILQ_FOREACH(async, &s->async_pending, next) { if (async->token == token) { /* Good match */ match = async; @@ -301,8 +273,6 @@ static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, uint32_t token break; } } - - async = async->next; count++; } @@ -662,7 +632,7 @@ static int uhci_broadcast_packet(UHCIState *s, USBPacket *p) USBDevice *dev = port->port.dev; if (dev && (port->ctrl & UHCI_PORT_EN)) - ret = dev->info->handle_packet(dev, p); + ret = usb_handle_packet(dev, p); } DPRINTF("uhci: packet exit. ret %d len %d\n", ret, p->len); @@ -672,7 +642,7 @@ static int uhci_broadcast_packet(UHCIState *s, USBPacket *p) return ret; } -static void uhci_async_complete(USBPacket * packet, void *opaque); +static void uhci_async_complete(USBDevice *dev, USBPacket *packet); static void uhci_process_frame(UHCIState *s); /* return -1 if fatal error (frame must be stopped) @@ -732,11 +702,15 @@ out: case USB_RET_STALL: td->ctrl |= TD_CTRL_STALL; td->ctrl &= ~TD_CTRL_ACTIVE; + s->status |= UHCI_STS_USBERR; + uhci_update_irq(s); return 1; case USB_RET_BABBLE: td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL; td->ctrl &= ~TD_CTRL_ACTIVE; + s->status |= UHCI_STS_USBERR; + uhci_update_irq(s); /* frame interrupted */ return -1; @@ -825,8 +799,6 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in async->packet.devep = (td->token >> 15) & 0xf; async->packet.data = async->buffer; async->packet.len = max_len; - async->packet.complete_cb = uhci_async_complete; - async->packet.complete_opaque = s; switch(pid) { case USB_TOKEN_OUT: @@ -862,10 +834,10 @@ done: return len; } -static void uhci_async_complete(USBPacket *packet, void *opaque) +static void uhci_async_complete(USBDevice *dev, USBPacket *packet) { - UHCIState *s = opaque; - UHCIAsync *async = (UHCIAsync *) packet; + UHCIAsync *async = container_of(packet, UHCIAsync, packet); + UHCIState *s = async->uhci; DPRINTF("uhci: async complete. td 0x%x token 0x%x\n", async->td, async->token); @@ -1113,6 +1085,7 @@ static USBPortOps uhci_port_ops = { .attach = uhci_attach, .detach = uhci_detach, .wakeup = uhci_wakeup, + .complete = uhci_async_complete, }; static int usb_uhci_common_initfn(PCIDevice *dev) @@ -1136,6 +1109,7 @@ static int usb_uhci_common_initfn(PCIDevice *dev) s->expire_time = qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() / FRAME_TIMER_FREQ); s->num_ports_vmstate = NB_PORTS; + QTAILQ_INIT(&s->async_pending); qemu_register_reset(uhci_reset, s); diff --git a/hw/usb-wacom.c b/hw/usb-wacom.c index 16be7a20cf..9d348e170e 100644 --- a/hw/usb-wacom.c +++ b/hw/usb-wacom.c @@ -108,6 +108,7 @@ static const USBDescDevice desc_device_wacom = { .bConfigurationValue = 1, .bmAttributes = 0x80, .bMaxPower = 40, + .nif = 1, .ifs = &desc_iface_wacom, }, }, @@ -249,13 +250,13 @@ static void usb_wacom_handle_reset(USBDevice *dev) s->mode = WACOM_MODE_HID; } -static int usb_wacom_handle_control(USBDevice *dev, int request, int value, - int index, int length, uint8_t *data) +static int usb_wacom_handle_control(USBDevice *dev, USBPacket *p, + int request, int value, int index, int length, uint8_t *data) { USBWacomState *s = (USBWacomState *) dev; int ret; - ret = usb_desc_handle_control(dev, request, value, index, length, data); + ret = usb_desc_handle_control(dev, p, request, value, index, length, data); if (ret >= 0) { return ret; } @@ -63,9 +63,10 @@ void usb_wakeup(USBDevice *dev) protocol) */ -#define SETUP_STATE_IDLE 0 -#define SETUP_STATE_DATA 1 -#define SETUP_STATE_ACK 2 +#define SETUP_STATE_IDLE 0 +#define SETUP_STATE_SETUP 1 +#define SETUP_STATE_DATA 2 +#define SETUP_STATE_ACK 3 static int do_token_setup(USBDevice *s, USBPacket *p) { @@ -82,10 +83,14 @@ static int do_token_setup(USBDevice *s, USBPacket *p) request = (s->setup_buf[0] << 8) | s->setup_buf[1]; value = (s->setup_buf[3] << 8) | s->setup_buf[2]; index = (s->setup_buf[5] << 8) | s->setup_buf[4]; - + if (s->setup_buf[0] & USB_DIR_IN) { - ret = s->info->handle_control(s, request, value, index, + ret = s->info->handle_control(s, p, request, value, index, s->setup_len, s->data_buf); + if (ret == USB_RET_ASYNC) { + s->setup_state = SETUP_STATE_SETUP; + return USB_RET_ASYNC; + } if (ret < 0) return ret; @@ -93,6 +98,12 @@ static int do_token_setup(USBDevice *s, USBPacket *p) s->setup_len = ret; s->setup_state = SETUP_STATE_DATA; } else { + if (s->setup_len > sizeof(s->data_buf)) { + fprintf(stderr, + "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n", + s->setup_len, sizeof(s->data_buf)); + return USB_RET_STALL; + } if (s->setup_len == 0) s->setup_state = SETUP_STATE_ACK; else @@ -117,9 +128,12 @@ static int do_token_in(USBDevice *s, USBPacket *p) switch(s->setup_state) { case SETUP_STATE_ACK: if (!(s->setup_buf[0] & USB_DIR_IN)) { - s->setup_state = SETUP_STATE_IDLE; - ret = s->info->handle_control(s, request, value, index, + ret = s->info->handle_control(s, p, request, value, index, s->setup_len, s->data_buf); + if (ret == USB_RET_ASYNC) { + return USB_RET_ASYNC; + } + s->setup_state = SETUP_STATE_IDLE; if (ret > 0) return 0; return ret; @@ -232,6 +246,36 @@ int usb_generic_handle_packet(USBDevice *s, USBPacket *p) } } +/* ctrl complete function for devices which use usb_generic_handle_packet and + may return USB_RET_ASYNC from their handle_control callback. Device code + which does this *must* call this function instead of the normal + usb_packet_complete to complete their async control packets. */ +void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p) +{ + if (p->len < 0) { + s->setup_state = SETUP_STATE_IDLE; + } + + switch (s->setup_state) { + case SETUP_STATE_SETUP: + if (p->len < s->setup_len) { + s->setup_len = p->len; + } + s->setup_state = SETUP_STATE_DATA; + p->len = 8; + break; + + case SETUP_STATE_ACK: + s->setup_state = SETUP_STATE_IDLE; + p->len = 0; + break; + + default: + break; + } + usb_packet_complete(s, p); +} + /* XXX: fix overflow */ int set_usb_string(uint8_t *buf, const char *str) { @@ -253,9 +297,54 @@ int set_usb_string(uint8_t *buf, const char *str) void usb_send_msg(USBDevice *dev, int msg) { USBPacket p; + int ret; + memset(&p, 0, sizeof(p)); p.pid = msg; - dev->info->handle_packet(dev, &p); - + ret = usb_handle_packet(dev, &p); /* This _must_ be synchronous */ + assert(ret != USB_RET_ASYNC); +} + +/* Hand over a packet to a device for processing. Return value + USB_RET_ASYNC indicates the processing isn't finished yet, the + driver will call usb_packet_complete() when done processing it. */ +int usb_handle_packet(USBDevice *dev, USBPacket *p) +{ + int ret; + + assert(p->owner == NULL); + ret = dev->info->handle_packet(dev, p); + if (ret == USB_RET_ASYNC) { + if (p->owner == NULL) { + p->owner = dev; + } else { + /* We'll end up here when usb_handle_packet is called + * recursively due to a hub being in the chain. Nothing + * to do. Leave p->owner pointing to the device, not the + * hub. */; + } + } + return ret; +} + +/* Notify the controller that an async packet is complete. This should only + be called for packets previously deferred by returning USB_RET_ASYNC from + handle_packet. */ +void usb_packet_complete(USBDevice *dev, USBPacket *p) +{ + /* Note: p->owner != dev is possible in case dev is a hub */ + assert(p->owner != NULL); + dev->port->ops->complete(dev, p); + p->owner = NULL; +} + +/* Cancel an active packet. The packed must have been deferred by + returning USB_RET_ASYNC from handle_packet, and not yet + completed. */ +void usb_cancel_packet(USBPacket * p) +{ + assert(p->owner != NULL); + p->owner->info->cancel_packet(p->owner, p); + p->owner = NULL; } @@ -124,6 +124,7 @@ #define USB_DT_ENDPOINT 0x05 #define USB_DT_DEVICE_QUALIFIER 0x06 #define USB_DT_OTHER_SPEED_CONFIG 0x07 +#define USB_DT_INTERFACE_ASSOC 0x0B #define USB_ENDPOINT_XFER_CONTROL 0 #define USB_ENDPOINT_XFER_ISOC 1 @@ -140,6 +141,7 @@ typedef struct USBDesc USBDesc; typedef struct USBDescID USBDescID; typedef struct USBDescDevice USBDescDevice; typedef struct USBDescConfig USBDescConfig; +typedef struct USBDescIfaceAssoc USBDescIfaceAssoc; typedef struct USBDescIface USBDescIface; typedef struct USBDescEndpoint USBDescEndpoint; typedef struct USBDescOther USBDescOther; @@ -167,7 +169,7 @@ struct USBDevice { int32_t state; uint8_t setup_buf[8]; - uint8_t data_buf[1024]; + uint8_t data_buf[4096]; int32_t remote_wakeup; int32_t setup_state; int32_t setup_len; @@ -192,6 +194,11 @@ struct USBDeviceInfo { int (*handle_packet)(USBDevice *dev, USBPacket *p); /* + * Called when a packet is canceled. + */ + void (*cancel_packet)(USBDevice *dev, USBPacket *p); + + /* * Called when device is destroyed. */ void (*handle_destroy)(USBDevice *dev); @@ -212,7 +219,7 @@ struct USBDeviceInfo { * * Returns length or one of the USB_RET_ codes. */ - int (*handle_control)(USBDevice *dev, int request, int value, + int (*handle_control)(USBDevice *dev, USBPacket *p, int request, int value, int index, int length, uint8_t *data); /* @@ -235,6 +242,7 @@ typedef struct USBPortOps { void (*attach)(USBPort *port); void (*detach)(USBPort *port); void (*wakeup)(USBDevice *dev); + void (*complete)(USBDevice *dev, USBPacket *p); } USBPortOps; /* USB port on which a device can be connected */ @@ -259,40 +267,17 @@ struct USBPacket { uint8_t *data; int len; /* Internal use by the USB layer. */ - USBCallback *complete_cb; - void *complete_opaque; - USBCallback *cancel_cb; - void *cancel_opaque; + USBDevice *owner; }; -/* Defer completion of a USB packet. The hadle_packet routine should then - return USB_RET_ASYNC. Packets that complete immediately (before - handle_packet returns) should not call this method. */ -static inline void usb_defer_packet(USBPacket *p, USBCallback *cancel, - void * opaque) -{ - p->cancel_cb = cancel; - p->cancel_opaque = opaque; -} - -/* Notify the controller that an async packet is complete. This should only - be called for packets previously deferred with usb_defer_packet, and - should never be called from within handle_packet. */ -static inline void usb_packet_complete(USBPacket *p) -{ - p->complete_cb(p, p->complete_opaque); -} - -/* Cancel an active packet. The packed must have been deferred with - usb_defer_packet, and not yet completed. */ -static inline void usb_cancel_packet(USBPacket * p) -{ - p->cancel_cb(p, p->cancel_opaque); -} +int usb_handle_packet(USBDevice *dev, USBPacket *p); +void usb_packet_complete(USBDevice *dev, USBPacket *p); +void usb_cancel_packet(USBPacket * p); void usb_attach(USBPort *port, USBDevice *dev); void usb_wakeup(USBDevice *dev); int usb_generic_handle_packet(USBDevice *s, USBPacket *p); +void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p); int set_usb_string(uint8_t *buf, const char *str); void usb_send_msg(USBDevice *dev, int msg); diff --git a/hw/vga-isa.c b/hw/vga-isa.c index fde0d56fd3..245841f18b 100644 --- a/hw/vga-isa.c +++ b/hw/vga-isa.c @@ -77,7 +77,6 @@ static ISADeviceInfo vga_info = { .qdev.size = sizeof(ISAVGAState), .qdev.vmsd = &vmstate_vga_common, .qdev.reset = vga_reset_isa, - .qdev.no_user = 1, .init = vga_initfn, }; diff --git a/hw/virtio-console.c b/hw/virtio-console.c index de539c4eac..b076331d37 100644 --- a/hw/virtio-console.c +++ b/hw/virtio-console.c @@ -74,25 +74,26 @@ static void chr_event(void *opaque, int event) } } -static int generic_port_init(VirtConsole *vcon, VirtIOSerialPort *port) +static int virtconsole_initfn(VirtIOSerialPort *port) { + VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port); + VirtIOSerialPortInfo *info = DO_UPCAST(VirtIOSerialPortInfo, qdev, + vcon->port.dev.info); + + if (port->id == 0 && !info->is_console) { + error_report("Port number 0 on virtio-serial devices reserved for virtconsole devices for backward compatibility."); + return -1; + } + if (vcon->chr) { qemu_chr_add_handlers(vcon->chr, chr_can_read, chr_read, chr_event, vcon); - vcon->port.info->have_data = flush_buf; - vcon->port.info->guest_open = guest_open; - vcon->port.info->guest_close = guest_close; + info->have_data = flush_buf; + info->guest_open = guest_open; + info->guest_close = guest_close; } - return 0; -} -/* Virtio Console Ports */ -static int virtconsole_initfn(VirtIOSerialPort *port) -{ - VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port); - - port->is_console = true; - return generic_port_init(vcon, port); + return 0; } static int virtconsole_exitfn(VirtIOSerialPort *port) @@ -113,10 +114,10 @@ static int virtconsole_exitfn(VirtIOSerialPort *port) static VirtIOSerialPortInfo virtconsole_info = { .qdev.name = "virtconsole", .qdev.size = sizeof(VirtConsole), + .is_console = true, .init = virtconsole_initfn, .exit = virtconsole_exitfn, .qdev.props = (Property[]) { - DEFINE_PROP_UINT8("is_console", VirtConsole, port.is_console, 1), DEFINE_PROP_UINT32("nr", VirtConsole, port.id, VIRTIO_CONSOLE_BAD_ID), DEFINE_PROP_CHR("chardev", VirtConsole, chr), DEFINE_PROP_STRING("name", VirtConsole, port.name), @@ -130,26 +131,10 @@ static void virtconsole_register(void) } device_init(virtconsole_register) -/* Generic Virtio Serial Ports */ -static int virtserialport_initfn(VirtIOSerialPort *port) -{ - VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port); - - if (port->id == 0) { - /* - * Disallow a generic port at id 0, that's reserved for - * console ports. - */ - error_report("Port number 0 on virtio-serial devices reserved for virtconsole devices for backward compatibility."); - return -1; - } - return generic_port_init(vcon, port); -} - static VirtIOSerialPortInfo virtserialport_info = { .qdev.name = "virtserialport", .qdev.size = sizeof(VirtConsole), - .init = virtserialport_initfn, + .init = virtconsole_initfn, .exit = virtconsole_exitfn, .qdev.props = (Property[]) { DEFINE_PROP_UINT32("nr", VirtConsole, port.id, VIRTIO_CONSOLE_BAD_ID), diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c index b86c441623..b3e7ba5d12 100644 --- a/hw/virtio-pci.c +++ b/hw/virtio-pci.c @@ -26,6 +26,7 @@ #include "loader.h" #include "kvm.h" #include "blockdev.h" +#include "virtio-pci.h" /* from Linux's linux/virtio_pci.h */ @@ -74,9 +75,6 @@ VIRTIO_PCI_CONFIG_MSI : \ VIRTIO_PCI_CONFIG_NOMSI) -/* Virtio ABI version, if we increment this, we break the guest driver. */ -#define VIRTIO_PCI_ABI_VERSION 0 - /* How many bits to shift physical queue address written to QUEUE_PFN. * 12 is historical, and due to x86 page size. */ #define VIRTIO_PCI_QUEUE_ADDR_SHIFT 12 @@ -95,27 +93,6 @@ */ #define wmb() do { } while (0) -/* PCI bindings. */ - -typedef struct { - PCIDevice pci_dev; - VirtIODevice *vdev; - uint32_t flags; - uint32_t addr; - uint32_t class_code; - uint32_t nvectors; - BlockConf block; - NICConf nic; - uint32_t host_features; -#ifdef CONFIG_LINUX - V9fsConf fsconf; -#endif - virtio_serial_conf serial; - virtio_net_conf net; - bool ioeventfd_disabled; - bool ioeventfd_started; -} VirtIOPCIProxy; - /* virtio device */ static void virtio_pci_notify(void *opaque, uint16_t vector) @@ -671,7 +648,7 @@ static const VirtIOBindings virtio_pci_bindings = { .vmstate_change = virtio_pci_vmstate_change, }; -static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev) +void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev) { uint8_t *config; uint32_t size; @@ -814,21 +791,6 @@ static int virtio_balloon_init_pci(PCIDevice *pci_dev) return 0; } -#ifdef CONFIG_VIRTFS -static int virtio_9p_init_pci(PCIDevice *pci_dev) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - VirtIODevice *vdev; - - vdev = virtio_9p_init(&pci_dev->qdev, &proxy->fsconf); - vdev->nvectors = proxy->nvectors; - virtio_init_pci(proxy, vdev); - /* make the actual value visible */ - proxy->nvectors = vdev->nvectors; - return 0; -} -#endif - static PCIDeviceInfo virtio_info[] = { { .qdev.name = "virtio-blk-pci", @@ -913,24 +875,6 @@ static PCIDeviceInfo virtio_info[] = { }, .qdev.reset = virtio_pci_reset, },{ -#ifdef CONFIG_VIRTFS - .qdev.name = "virtio-9p-pci", - .qdev.alias = "virtio-9p", - .qdev.size = sizeof(VirtIOPCIProxy), - .init = virtio_9p_init_pci, - .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET, - .device_id = 0x1009, - .revision = VIRTIO_PCI_ABI_VERSION, - .class_id = 0x2, - .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), - DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features), - DEFINE_PROP_STRING("mount_tag", VirtIOPCIProxy, fsconf.tag), - DEFINE_PROP_STRING("fsdev", VirtIOPCIProxy, fsconf.fsdev_id), - DEFINE_PROP_END_OF_LIST(), - }, - }, { -#endif /* end of list */ } }; diff --git a/hw/virtio-pci.h b/hw/virtio-pci.h new file mode 100644 index 0000000000..b5189172de --- /dev/null +++ b/hw/virtio-pci.h @@ -0,0 +1,45 @@ +/* + * Virtio PCI Bindings + * + * Copyright IBM, Corp. 2007 + * Copyright (c) 2009 CodeSourcery + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com> + * Paul Brook <paul@codesourcery.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#ifndef QEMU_VIRTIO_PCI_H +#define QEMU_VIRTIO_PCI_H + +#include "virtio-net.h" +#include "virtio-serial.h" + +typedef struct { + PCIDevice pci_dev; + VirtIODevice *vdev; + uint32_t flags; + uint32_t addr; + uint32_t class_code; + uint32_t nvectors; + BlockConf block; + NICConf nic; + uint32_t host_features; +#ifdef CONFIG_LINUX + V9fsConf fsconf; +#endif + virtio_serial_conf serial; + virtio_net_conf net; + bool ioeventfd_disabled; + bool ioeventfd_started; +} VirtIOPCIProxy; + +void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev); + +/* Virtio ABI version, if we increment this, we break the guest driver. */ +#define VIRTIO_PCI_ABI_VERSION 0 + +#endif diff --git a/hw/virtio-serial-bus.c b/hw/virtio-serial-bus.c index f10d48fdb0..9a12104982 100644 --- a/hw/virtio-serial-bus.c +++ b/hw/virtio-serial-bus.c @@ -39,7 +39,7 @@ struct VirtIOSerial { /* Arrays of ivqs and ovqs: one per port */ VirtQueue **ivqs, **ovqs; - VirtIOSerialBus *bus; + VirtIOSerialBus bus; DeviceState *qdev; @@ -129,9 +129,13 @@ static void discard_vq_data(VirtQueue *vq, VirtIODevice *vdev) static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq, VirtIODevice *vdev) { + VirtIOSerialPortInfo *info; + assert(port); assert(virtio_queue_ready(vq)); + info = DO_UPCAST(VirtIOSerialPortInfo, qdev, port->dev.info); + while (!port->throttled) { unsigned int i; @@ -149,10 +153,10 @@ static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq, ssize_t ret; buf_size = port->elem.out_sg[i].iov_len - port->iov_offset; - ret = port->info->have_data(port, - port->elem.out_sg[i].iov_base - + port->iov_offset, - buf_size); + ret = info->have_data(port, + port->elem.out_sg[i].iov_base + + port->iov_offset, + buf_size); if (ret < 0 && ret != -EAGAIN) { /* We don't handle any other type of errors here */ abort(); @@ -285,6 +289,13 @@ size_t virtio_serial_guest_ready(VirtIOSerialPort *port) return 0; } +static void flush_queued_data_bh(void *opaque) +{ + VirtIOSerialPort *port = opaque; + + flush_queued_data(port); +} + void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle) { if (!port) { @@ -295,14 +306,14 @@ void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle) if (throttle) { return; } - - flush_queued_data(port); + qemu_bh_schedule(port->bh); } /* Guest wants to notify us of some event */ static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len) { struct VirtIOSerialPort *port; + struct VirtIOSerialPortInfo *info; struct virtio_console_control cpkt, *gcpkt; uint8_t *buffer; size_t buffer_len; @@ -321,11 +332,13 @@ static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len) if (!port && cpkt.event != VIRTIO_CONSOLE_DEVICE_READY) return; + info = DO_UPCAST(VirtIOSerialPortInfo, qdev, port->dev.info); + switch(cpkt.event) { case VIRTIO_CONSOLE_DEVICE_READY: if (!cpkt.value) { error_report("virtio-serial-bus: Guest failure in adding device %s\n", - vser->bus->qbus.name); + vser->bus.qbus.name); break; } /* @@ -340,7 +353,7 @@ static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len) case VIRTIO_CONSOLE_PORT_READY: if (!cpkt.value) { error_report("virtio-serial-bus: Guest failure in adding port %u for device %s\n", - port->id, vser->bus->qbus.name); + port->id, vser->bus.qbus.name); break; } /* @@ -350,7 +363,7 @@ static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len) * this port is a console port so that the guest can hook it * up to hvc. */ - if (port->is_console) { + if (info->is_console) { send_control_event(port, VIRTIO_CONSOLE_CONSOLE_PORT, 1); } @@ -379,21 +392,21 @@ static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len) * initialised. If some app is interested in knowing about * this event, let it know. */ - if (port->info->guest_ready) { - port->info->guest_ready(port); + if (info->guest_ready) { + info->guest_ready(port); } break; case VIRTIO_CONSOLE_PORT_OPEN: port->guest_connected = cpkt.value; - if (cpkt.value && port->info->guest_open) { + if (cpkt.value && info->guest_open) { /* Send the guest opened notification if an app is interested */ - port->info->guest_open(port); + info->guest_open(port); } - if (!cpkt.value && port->info->guest_close) { + if (!cpkt.value && info->guest_close) { /* Send the guest closed notification if an app is interested */ - port->info->guest_close(port); + info->guest_close(port); } break; } @@ -442,11 +455,13 @@ static void handle_output(VirtIODevice *vdev, VirtQueue *vq) { VirtIOSerial *vser; VirtIOSerialPort *port; + VirtIOSerialPortInfo *info; vser = DO_UPCAST(VirtIOSerial, vdev, vdev); port = find_port_by_vq(vser, vq); + info = port ? DO_UPCAST(VirtIOSerialPortInfo, qdev, port->dev.info) : NULL; - if (!port || !port->host_connected || !port->info->have_data) { + if (!port || !port->host_connected || !info->have_data) { discard_vq_data(vq, vdev); return; } @@ -467,7 +482,7 @@ static uint32_t get_features(VirtIODevice *vdev, uint32_t features) vser = DO_UPCAST(VirtIOSerial, vdev, vdev); - if (vser->bus->max_nr_ports > 1) { + if (vser->bus.max_nr_ports > 1) { features |= (1 << VIRTIO_CONSOLE_F_MULTIPORT); } return features; @@ -644,16 +659,6 @@ static struct BusInfo virtser_bus_info = { .print_dev = virtser_bus_dev_print, }; -static VirtIOSerialBus *virtser_bus_new(DeviceState *dev) -{ - VirtIOSerialBus *bus; - - bus = FROM_QBUS(VirtIOSerialBus, qbus_create(&virtser_bus_info, dev, NULL)); - bus->qbus.allow_hotplug = 1; - - return bus; -} - static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent) { VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, qdev); @@ -726,13 +731,14 @@ static int virtser_port_qdev_init(DeviceState *qdev, DeviceInfo *base) bool plugging_port0; port->vser = bus->vser; + port->bh = qemu_bh_new(flush_queued_data_bh, port); /* * Is the first console port we're seeing? If so, put it up at * location 0. This is done for backward compatibility (old * kernel, new qemu). */ - plugging_port0 = port->is_console && !find_port_by_id(port->vser, 0); + plugging_port0 = info->is_console && !find_port_by_id(port->vser, 0); if (find_port_by_id(port->vser, port->id)) { error_report("virtio-serial-bus: A port already exists at id %u\n", @@ -759,7 +765,6 @@ static int virtser_port_qdev_init(DeviceState *qdev, DeviceInfo *base) return -1; } - port->info = info; ret = info->init(port); if (ret) { return ret; @@ -790,15 +795,18 @@ static int virtser_port_qdev_init(DeviceState *qdev, DeviceInfo *base) static int virtser_port_qdev_exit(DeviceState *qdev) { VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, qdev); + VirtIOSerialPortInfo *info = DO_UPCAST(VirtIOSerialPortInfo, qdev, + port->dev.info); VirtIOSerial *vser = port->vser; + qemu_bh_delete(port->bh); remove_port(port->vser, port->id); QTAILQ_REMOVE(&vser->ports, port, next); - if (port->info->exit) - port->info->exit(port); - + if (info->exit) { + info->exit(port); + } return 0; } @@ -835,11 +843,12 @@ VirtIODevice *virtio_serial_init(DeviceState *dev, virtio_serial_conf *conf) vser = DO_UPCAST(VirtIOSerial, vdev, vdev); /* Spawn a new virtio-serial bus on which the ports will ride as devices */ - vser->bus = virtser_bus_new(dev); - vser->bus->vser = vser; + qbus_create_inplace(&vser->bus.qbus, &virtser_bus_info, dev, NULL); + vser->bus.qbus.allow_hotplug = 1; + vser->bus.vser = vser; QTAILQ_INIT(&vser->ports); - vser->bus->max_nr_ports = conf->max_virtserial_ports; + vser->bus.max_nr_ports = conf->max_virtserial_ports; vser->ivqs = qemu_malloc(conf->max_virtserial_ports * sizeof(VirtQueue *)); vser->ovqs = qemu_malloc(conf->max_virtserial_ports * sizeof(VirtQueue *)); @@ -859,7 +868,7 @@ VirtIODevice *virtio_serial_init(DeviceState *dev, virtio_serial_conf *conf) /* control queue: guest to host */ vser->c_ovq = virtio_add_queue(vdev, 32, control_out); - for (i = 1; i < vser->bus->max_nr_ports; i++) { + for (i = 1; i < vser->bus.max_nr_ports; i++) { /* Add a per-port queue for host to guest transfers */ vser->ivqs[i] = virtio_add_queue(vdev, 128, handle_input); /* Add a per-per queue for guest to host transfers */ diff --git a/hw/virtio-serial.h b/hw/virtio-serial.h index 5eb948e3fd..36e9d222e5 100644 --- a/hw/virtio-serial.h +++ b/hw/virtio-serial.h @@ -75,7 +75,6 @@ typedef struct VirtIOSerialPortInfo VirtIOSerialPortInfo; */ struct VirtIOSerialPort { DeviceState dev; - VirtIOSerialPortInfo *info; QTAILQ_ENTRY(VirtIOSerialPort) next; @@ -119,8 +118,10 @@ struct VirtIOSerialPort { uint32_t iov_idx; uint64_t iov_offset; - /* Identify if this is a port that binds with hvc in the guest */ - uint8_t is_console; + /* + * When unthrottling we use a bottom-half to call flush_queued_data. + */ + QEMUBH *bh; /* Is the corresponding guest device open? */ bool guest_connected; @@ -132,6 +133,10 @@ struct VirtIOSerialPort { struct VirtIOSerialPortInfo { DeviceInfo qdev; + + /* Is this a device that binds with hvc in the guest? */ + bool is_console; + /* * The per-port (or per-app) init function that's called when a * new device is found on the bus. @@ -8,6 +8,8 @@ */ #include <inttypes.h> +#include "qemu-common.h" + /* xen-machine.c */ enum xen_mode { XEN_EMULATE = 0, // xen emulation, using xenner (default) @@ -18,4 +20,43 @@ enum xen_mode { extern uint32_t xen_domid; extern enum xen_mode xen_mode; +extern int xen_allowed; + +static inline int xen_enabled(void) +{ +#ifdef CONFIG_XEN + return xen_allowed; +#else + return 0; +#endif +} + +static inline int xen_mapcache_enabled(void) +{ +#ifdef CONFIG_XEN_MAPCACHE + return xen_enabled(); +#else + return 0; +#endif +} + +int xen_pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num); +void xen_piix3_set_irq(void *opaque, int irq_num, int level); +void xen_piix_pci_write_config_client(uint32_t address, uint32_t val, int len); +void xen_cmos_set_s3_resume(void *opaque, int irq, int level); + +qemu_irq *xen_interrupt_controller_init(void); + +int xen_init(void); +int xen_hvm_init(void); +void xen_vcpu_init(void); + +#if defined(NEED_CPU_H) && !defined(CONFIG_USER_ONLY) +void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size); +#endif + +#if defined(CONFIG_XEN) && CONFIG_XEN_CTRL_INTERFACE_VERSION < 400 +# define HVM_MAX_VCPUS 32 +#endif + #endif /* QEMU_HW_XEN_H */ diff --git a/hw/xen_backend.c b/hw/xen_backend.c index a2e408fa0e..d881fa2f70 100644 --- a/hw/xen_backend.c +++ b/hw/xen_backend.c @@ -43,7 +43,8 @@ /* ------------------------------------------------------------- */ /* public */ -int xen_xc; +XenXC xen_xc = XC_HANDLER_INITIAL_VALUE; +XenGnttab xen_xcg = XC_HANDLER_INITIAL_VALUE; struct xs_handle *xenstore = NULL; const char *xen_protocol; @@ -58,8 +59,9 @@ int xenstore_write_str(const char *base, const char *node, const char *val) char abspath[XEN_BUFSIZE]; snprintf(abspath, sizeof(abspath), "%s/%s", base, node); - if (!xs_write(xenstore, 0, abspath, val, strlen(val))) - return -1; + if (!xs_write(xenstore, 0, abspath, val, strlen(val))) { + return -1; + } return 0; } @@ -94,8 +96,9 @@ int xenstore_read_int(const char *base, const char *node, int *ival) int rc = -1; val = xenstore_read_str(base, node); - if (val && 1 == sscanf(val, "%d", ival)) - rc = 0; + if (val && 1 == sscanf(val, "%d", ival)) { + rc = 0; + } qemu_free(val); return rc; } @@ -134,16 +137,16 @@ int xenstore_read_fe_int(struct XenDevice *xendev, const char *node, int *ival) const char *xenbus_strstate(enum xenbus_state state) { - static const char *const name[] = { - [ XenbusStateUnknown ] = "Unknown", - [ XenbusStateInitialising ] = "Initialising", - [ XenbusStateInitWait ] = "InitWait", - [ XenbusStateInitialised ] = "Initialised", - [ XenbusStateConnected ] = "Connected", - [ XenbusStateClosing ] = "Closing", - [ XenbusStateClosed ] = "Closed", - }; - return (state < ARRAY_SIZE(name)) ? name[state] : "INVALID"; + static const char *const name[] = { + [ XenbusStateUnknown ] = "Unknown", + [ XenbusStateInitialising ] = "Initialising", + [ XenbusStateInitWait ] = "InitWait", + [ XenbusStateInitialised ] = "Initialised", + [ XenbusStateConnected ] = "Connected", + [ XenbusStateClosing ] = "Closing", + [ XenbusStateClosed ] = "Closed", + }; + return (state < ARRAY_SIZE(name)) ? name[state] : "INVALID"; } int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state) @@ -151,10 +154,11 @@ int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state) int rc; rc = xenstore_write_be_int(xendev, "state", state); - if (rc < 0) - return rc; + if (rc < 0) { + return rc; + } xen_be_printf(xendev, 1, "backend state: %s -> %s\n", - xenbus_strstate(xendev->be_state), xenbus_strstate(state)); + xenbus_strstate(xendev->be_state), xenbus_strstate(state)); xendev->be_state = state; return 0; } @@ -166,13 +170,16 @@ struct XenDevice *xen_be_find_xendev(const char *type, int dom, int dev) struct XenDevice *xendev; QTAILQ_FOREACH(xendev, &xendevs, next) { - if (xendev->dom != dom) - continue; - if (xendev->dev != dev) - continue; - if (strcmp(xendev->type, type) != 0) - continue; - return xendev; + if (xendev->dom != dom) { + continue; + } + if (xendev->dev != dev) { + continue; + } + if (strcmp(xendev->type, type) != 0) { + continue; + } + return xendev; } return NULL; } @@ -187,8 +194,9 @@ static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev, char *dom0; xendev = xen_be_find_xendev(type, dom, dev); - if (xendev) - return xendev; + if (xendev) { + return xendev; + } /* init new xendev */ xendev = qemu_mallocz(ops->size); @@ -199,38 +207,39 @@ static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev, dom0 = xs_get_domain_path(xenstore, 0); snprintf(xendev->be, sizeof(xendev->be), "%s/backend/%s/%d/%d", - dom0, xendev->type, xendev->dom, xendev->dev); + dom0, xendev->type, xendev->dom, xendev->dev); snprintf(xendev->name, sizeof(xendev->name), "%s-%d", - xendev->type, xendev->dev); + xendev->type, xendev->dev); free(dom0); xendev->debug = debug; xendev->local_port = -1; - xendev->evtchndev = xc_evtchn_open(); - if (xendev->evtchndev < 0) { - xen_be_printf(NULL, 0, "can't open evtchn device\n"); - qemu_free(xendev); - return NULL; + xendev->evtchndev = xen_xc_evtchn_open(NULL, 0); + if (xendev->evtchndev == XC_HANDLER_INITIAL_VALUE) { + xen_be_printf(NULL, 0, "can't open evtchn device\n"); + qemu_free(xendev); + return NULL; } fcntl(xc_evtchn_fd(xendev->evtchndev), F_SETFD, FD_CLOEXEC); if (ops->flags & DEVOPS_FLAG_NEED_GNTDEV) { - xendev->gnttabdev = xc_gnttab_open(); - if (xendev->gnttabdev < 0) { - xen_be_printf(NULL, 0, "can't open gnttab device\n"); - xc_evtchn_close(xendev->evtchndev); - qemu_free(xendev); - return NULL; - } + xendev->gnttabdev = xen_xc_gnttab_open(NULL, 0); + if (xendev->gnttabdev == XC_HANDLER_INITIAL_VALUE) { + xen_be_printf(NULL, 0, "can't open gnttab device\n"); + xc_evtchn_close(xendev->evtchndev); + qemu_free(xendev); + return NULL; + } } else { - xendev->gnttabdev = -1; + xendev->gnttabdev = XC_HANDLER_INITIAL_VALUE; } QTAILQ_INSERT_TAIL(&xendevs, xendev, next); - if (xendev->ops->alloc) - xendev->ops->alloc(xendev); + if (xendev->ops->alloc) { + xendev->ops->alloc(xendev); + } return xendev; } @@ -251,28 +260,33 @@ static struct XenDevice *xen_be_del_xendev(int dom, int dev) xendev = xnext; xnext = xendev->next.tqe_next; - if (xendev->dom != dom) - continue; - if (xendev->dev != dev && dev != -1) - continue; - - if (xendev->ops->free) - xendev->ops->free(xendev); - - if (xendev->fe) { - char token[XEN_BUFSIZE]; - snprintf(token, sizeof(token), "fe:%p", xendev); - xs_unwatch(xenstore, xendev->fe, token); - qemu_free(xendev->fe); - } - - if (xendev->evtchndev >= 0) - xc_evtchn_close(xendev->evtchndev); - if (xendev->gnttabdev >= 0) - xc_gnttab_close(xendev->gnttabdev); - - QTAILQ_REMOVE(&xendevs, xendev, next); - qemu_free(xendev); + if (xendev->dom != dom) { + continue; + } + if (xendev->dev != dev && dev != -1) { + continue; + } + + if (xendev->ops->free) { + xendev->ops->free(xendev); + } + + if (xendev->fe) { + char token[XEN_BUFSIZE]; + snprintf(token, sizeof(token), "fe:%p", xendev); + xs_unwatch(xenstore, xendev->fe, token); + qemu_free(xendev->fe); + } + + if (xendev->evtchndev != XC_HANDLER_INITIAL_VALUE) { + xc_evtchn_close(xendev->evtchndev); + } + if (xendev->gnttabdev != XC_HANDLER_INITIAL_VALUE) { + xc_gnttab_close(xendev->gnttabdev); + } + + QTAILQ_REMOVE(&xendevs, xendev, next); + qemu_free(xendev); } return NULL; } @@ -285,14 +299,16 @@ static struct XenDevice *xen_be_del_xendev(int dom, int dev) static void xen_be_backend_changed(struct XenDevice *xendev, const char *node) { if (node == NULL || strcmp(node, "online") == 0) { - if (xenstore_read_be_int(xendev, "online", &xendev->online) == -1) - xendev->online = 0; + if (xenstore_read_be_int(xendev, "online", &xendev->online) == -1) { + xendev->online = 0; + } } if (node) { - xen_be_printf(xendev, 2, "backend update: %s\n", node); - if (xendev->ops->backend_changed) - xendev->ops->backend_changed(xendev, node); + xen_be_printf(xendev, 2, "backend update: %s\n", node); + if (xendev->ops->backend_changed) { + xendev->ops->backend_changed(xendev, node); + } } } @@ -301,25 +317,29 @@ static void xen_be_frontend_changed(struct XenDevice *xendev, const char *node) int fe_state; if (node == NULL || strcmp(node, "state") == 0) { - if (xenstore_read_fe_int(xendev, "state", &fe_state) == -1) - fe_state = XenbusStateUnknown; - if (xendev->fe_state != fe_state) - xen_be_printf(xendev, 1, "frontend state: %s -> %s\n", - xenbus_strstate(xendev->fe_state), - xenbus_strstate(fe_state)); - xendev->fe_state = fe_state; + if (xenstore_read_fe_int(xendev, "state", &fe_state) == -1) { + fe_state = XenbusStateUnknown; + } + if (xendev->fe_state != fe_state) { + xen_be_printf(xendev, 1, "frontend state: %s -> %s\n", + xenbus_strstate(xendev->fe_state), + xenbus_strstate(fe_state)); + } + xendev->fe_state = fe_state; } if (node == NULL || strcmp(node, "protocol") == 0) { - qemu_free(xendev->protocol); - xendev->protocol = xenstore_read_fe_str(xendev, "protocol"); - if (xendev->protocol) - xen_be_printf(xendev, 1, "frontend protocol: %s\n", xendev->protocol); + qemu_free(xendev->protocol); + xendev->protocol = xenstore_read_fe_str(xendev, "protocol"); + if (xendev->protocol) { + xen_be_printf(xendev, 1, "frontend protocol: %s\n", xendev->protocol); + } } if (node) { - xen_be_printf(xendev, 2, "frontend update: %s\n", node); - if (xendev->ops->frontend_changed) - xendev->ops->frontend_changed(xendev, node); + xen_be_printf(xendev, 2, "frontend update: %s\n", node); + if (xendev->ops->frontend_changed) { + xendev->ops->frontend_changed(xendev, node); + } } } @@ -340,28 +360,28 @@ static int xen_be_try_setup(struct XenDevice *xendev) int be_state; if (xenstore_read_be_int(xendev, "state", &be_state) == -1) { - xen_be_printf(xendev, 0, "reading backend state failed\n"); - return -1; + xen_be_printf(xendev, 0, "reading backend state failed\n"); + return -1; } if (be_state != XenbusStateInitialising) { - xen_be_printf(xendev, 0, "initial backend state is wrong (%s)\n", - xenbus_strstate(be_state)); - return -1; + xen_be_printf(xendev, 0, "initial backend state is wrong (%s)\n", + xenbus_strstate(be_state)); + return -1; } xendev->fe = xenstore_read_be_str(xendev, "frontend"); if (xendev->fe == NULL) { - xen_be_printf(xendev, 0, "reading frontend path failed\n"); - return -1; + xen_be_printf(xendev, 0, "reading frontend path failed\n"); + return -1; } /* setup frontend watch */ snprintf(token, sizeof(token), "fe:%p", xendev); if (!xs_watch(xenstore, xendev->fe, token)) { - xen_be_printf(xendev, 0, "watching frontend path (%s) failed\n", - xendev->fe); - return -1; + xen_be_printf(xendev, 0, "watching frontend path (%s) failed\n", + xendev->fe); + return -1; } xen_be_set_state(xendev, XenbusStateInitialising); @@ -383,15 +403,16 @@ static int xen_be_try_init(struct XenDevice *xendev) int rc = 0; if (!xendev->online) { - xen_be_printf(xendev, 1, "not online\n"); - return -1; + xen_be_printf(xendev, 1, "not online\n"); + return -1; } - if (xendev->ops->init) - rc = xendev->ops->init(xendev); + if (xendev->ops->init) { + rc = xendev->ops->init(xendev); + } if (rc != 0) { - xen_be_printf(xendev, 1, "init() failed\n"); - return rc; + xen_be_printf(xendev, 1, "init() failed\n"); + return rc; } xenstore_write_be_str(xendev, "hotplug-status", "connected"); @@ -411,20 +432,21 @@ static int xen_be_try_connect(struct XenDevice *xendev) int rc = 0; if (xendev->fe_state != XenbusStateInitialised && - xendev->fe_state != XenbusStateConnected) { - if (xendev->ops->flags & DEVOPS_FLAG_IGNORE_STATE) { - xen_be_printf(xendev, 2, "frontend not ready, ignoring\n"); - } else { - xen_be_printf(xendev, 2, "frontend not ready (yet)\n"); - return -1; - } + xendev->fe_state != XenbusStateConnected) { + if (xendev->ops->flags & DEVOPS_FLAG_IGNORE_STATE) { + xen_be_printf(xendev, 2, "frontend not ready, ignoring\n"); + } else { + xen_be_printf(xendev, 2, "frontend not ready (yet)\n"); + return -1; + } } - if (xendev->ops->connect) - rc = xendev->ops->connect(xendev); + if (xendev->ops->connect) { + rc = xendev->ops->connect(xendev); + } if (rc != 0) { - xen_be_printf(xendev, 0, "connect() failed\n"); - return rc; + xen_be_printf(xendev, 0, "connect() failed\n"); + return rc; } xen_be_set_state(xendev, XenbusStateConnected); @@ -440,10 +462,12 @@ static void xen_be_disconnect(struct XenDevice *xendev, enum xenbus_state state) { if (xendev->be_state != XenbusStateClosing && xendev->be_state != XenbusStateClosed && - xendev->ops->disconnect) - xendev->ops->disconnect(xendev); - if (xendev->be_state != state) + xendev->ops->disconnect) { + xendev->ops->disconnect(xendev); + } + if (xendev->be_state != state) { xen_be_set_state(xendev, state); + } } /* @@ -451,8 +475,9 @@ static void xen_be_disconnect(struct XenDevice *xendev, enum xenbus_state state) */ static int xen_be_try_reset(struct XenDevice *xendev) { - if (xendev->fe_state != XenbusStateInitialising) + if (xendev->fe_state != XenbusStateInitialising) { return -1; + } xen_be_printf(xendev, 1, "device reset (for re-connect)\n"); xen_be_set_state(xendev, XenbusStateInitialising); @@ -468,31 +493,32 @@ void xen_be_check_state(struct XenDevice *xendev) /* frontend may request shutdown from almost anywhere */ if (xendev->fe_state == XenbusStateClosing || - xendev->fe_state == XenbusStateClosed) { - xen_be_disconnect(xendev, xendev->fe_state); - return; + xendev->fe_state == XenbusStateClosed) { + xen_be_disconnect(xendev, xendev->fe_state); + return; } /* check for possible backend state transitions */ for (;;) { - switch (xendev->be_state) { - case XenbusStateUnknown: - rc = xen_be_try_setup(xendev); - break; - case XenbusStateInitialising: - rc = xen_be_try_init(xendev); - break; - case XenbusStateInitWait: - rc = xen_be_try_connect(xendev); - break; + switch (xendev->be_state) { + case XenbusStateUnknown: + rc = xen_be_try_setup(xendev); + break; + case XenbusStateInitialising: + rc = xen_be_try_init(xendev); + break; + case XenbusStateInitWait: + rc = xen_be_try_connect(xendev); + break; case XenbusStateClosed: rc = xen_be_try_reset(xendev); break; - default: - rc = -1; - } - if (rc != 0) - break; + default: + rc = -1; + } + if (rc != 0) { + break; + } } } @@ -511,26 +537,28 @@ static int xenstore_scan(const char *type, int dom, struct XenDevOps *ops) snprintf(path, sizeof(path), "%s/backend/%s/%d", dom0, type, dom); free(dom0); if (!xs_watch(xenstore, path, token)) { - xen_be_printf(NULL, 0, "xen be: watching backend path (%s) failed\n", path); - return -1; + xen_be_printf(NULL, 0, "xen be: watching backend path (%s) failed\n", path); + return -1; } /* look for backends */ dev = xs_directory(xenstore, 0, path, &cdev); - if (!dev) - return 0; + if (!dev) { + return 0; + } for (j = 0; j < cdev; j++) { - xendev = xen_be_get_xendev(type, dom, atoi(dev[j]), ops); - if (xendev == NULL) - continue; - xen_be_check_state(xendev); + xendev = xen_be_get_xendev(type, dom, atoi(dev[j]), ops); + if (xendev == NULL) { + continue; + } + xen_be_check_state(xendev); } free(dev); return 0; } static void xenstore_update_be(char *watch, char *type, int dom, - struct XenDevOps *ops) + struct XenDevOps *ops) { struct XenDevice *xendev; char path[XEN_BUFSIZE], *dom0; @@ -539,25 +567,28 @@ static void xenstore_update_be(char *watch, char *type, int dom, dom0 = xs_get_domain_path(xenstore, 0); len = snprintf(path, sizeof(path), "%s/backend/%s/%d", dom0, type, dom); free(dom0); - if (strncmp(path, watch, len) != 0) - return; + if (strncmp(path, watch, len) != 0) { + return; + } if (sscanf(watch+len, "/%u/%255s", &dev, path) != 2) { - strcpy(path, ""); - if (sscanf(watch+len, "/%u", &dev) != 1) - dev = -1; + strcpy(path, ""); + if (sscanf(watch+len, "/%u", &dev) != 1) { + dev = -1; + } + } + if (dev == -1) { + return; } - if (dev == -1) - return; if (0) { - /* FIXME: detect devices being deleted from xenstore ... */ - xen_be_del_xendev(dom, dev); + /* FIXME: detect devices being deleted from xenstore ... */ + xen_be_del_xendev(dom, dev); } xendev = xen_be_get_xendev(type, dom, dev, ops); if (xendev != NULL) { - xen_be_backend_changed(xendev, path); - xen_be_check_state(xendev); + xen_be_backend_changed(xendev, path); + xen_be_check_state(xendev); } } @@ -567,10 +598,12 @@ static void xenstore_update_fe(char *watch, struct XenDevice *xendev) unsigned int len; len = strlen(xendev->fe); - if (strncmp(xendev->fe, watch, len) != 0) - return; - if (watch[len] != '/') - return; + if (strncmp(xendev->fe, watch, len) != 0) { + return; + } + if (watch[len] != '/') { + return; + } node = watch + len + 1; xen_be_frontend_changed(xendev, node); @@ -584,14 +617,17 @@ static void xenstore_update(void *unused) unsigned int dom, count; vec = xs_read_watch(xenstore, &count); - if (vec == NULL) - goto cleanup; + if (vec == NULL) { + goto cleanup; + } if (sscanf(vec[XS_WATCH_TOKEN], "be:%" PRIxPTR ":%d:%" PRIxPTR, - &type, &dom, &ops) == 3) - xenstore_update_be(vec[XS_WATCH_PATH], (void*)type, dom, (void*)ops); - if (sscanf(vec[XS_WATCH_TOKEN], "fe:%" PRIxPTR, &ptr) == 1) - xenstore_update_fe(vec[XS_WATCH_PATH], (void*)ptr); + &type, &dom, &ops) == 3) { + xenstore_update_be(vec[XS_WATCH_PATH], (void*)type, dom, (void*)ops); + } + if (sscanf(vec[XS_WATCH_TOKEN], "fe:%" PRIxPTR, &ptr) == 1) { + xenstore_update_fe(vec[XS_WATCH_PATH], (void*)ptr); + } cleanup: free(vec); @@ -604,14 +640,15 @@ static void xen_be_evtchn_event(void *opaque) port = xc_evtchn_pending(xendev->evtchndev); if (port != xendev->local_port) { - xen_be_printf(xendev, 0, "xc_evtchn_pending returned %d (expected %d)\n", - port, xendev->local_port); - return; + xen_be_printf(xendev, 0, "xc_evtchn_pending returned %d (expected %d)\n", + port, xendev->local_port); + return; } xc_evtchn_unmask(xendev->evtchndev, port); - if (xendev->ops->event) - xendev->ops->event(xendev); + if (xendev->ops->event) { + xendev->ops->event(xendev); + } } /* -------------------------------------------------------------------- */ @@ -620,17 +657,17 @@ int xen_be_init(void) { xenstore = xs_daemon_open(); if (!xenstore) { - xen_be_printf(NULL, 0, "can't connect to xenstored\n"); - return -1; + xen_be_printf(NULL, 0, "can't connect to xenstored\n"); + return -1; } - if (qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL) < 0) - goto err; + if (qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL) < 0) { + goto err; + } - xen_xc = xc_interface_open(); - if (xen_xc == -1) { - xen_be_printf(NULL, 0, "can't open xen interface\n"); - goto err; + if (xen_xc == XC_HANDLER_INITIAL_VALUE) { + /* Check if xen_init() have been called */ + goto err; } return 0; @@ -649,24 +686,26 @@ int xen_be_register(const char *type, struct XenDevOps *ops) int xen_be_bind_evtchn(struct XenDevice *xendev) { - if (xendev->local_port != -1) - return 0; + if (xendev->local_port != -1) { + return 0; + } xendev->local_port = xc_evtchn_bind_interdomain - (xendev->evtchndev, xendev->dom, xendev->remote_port); + (xendev->evtchndev, xendev->dom, xendev->remote_port); if (xendev->local_port == -1) { - xen_be_printf(xendev, 0, "xc_evtchn_bind_interdomain failed\n"); - return -1; + xen_be_printf(xendev, 0, "xc_evtchn_bind_interdomain failed\n"); + return -1; } xen_be_printf(xendev, 2, "bind evtchn port %d\n", xendev->local_port); qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev), - xen_be_evtchn_event, NULL, xendev); + xen_be_evtchn_event, NULL, xendev); return 0; } void xen_be_unbind_evtchn(struct XenDevice *xendev) { - if (xendev->local_port == -1) - return; + if (xendev->local_port == -1) { + return; + } qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev), NULL, NULL, NULL); xc_evtchn_unbind(xendev->evtchndev, xendev->local_port); xen_be_printf(xendev, 2, "unbind evtchn port %d\n", xendev->local_port); @@ -690,17 +729,21 @@ void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ... va_list args; if (xendev) { - if (msg_level > xendev->debug) + if (msg_level > xendev->debug) { return; + } qemu_log("xen be: %s: ", xendev->name); - if (msg_level == 0) + if (msg_level == 0) { fprintf(stderr, "xen be: %s: ", xendev->name); + } } else { - if (msg_level > debug) + if (msg_level > debug) { return; + } qemu_log("xen be core: "); - if (msg_level == 0) + if (msg_level == 0) { fprintf(stderr, "xen be core: "); + } } va_start(args, fmt); qemu_log_vprintf(fmt, args); diff --git a/hw/xen_backend.h b/hw/xen_backend.h index 1b428e3bf4..6401c85a7e 100644 --- a/hw/xen_backend.h +++ b/hw/xen_backend.h @@ -45,8 +45,8 @@ struct XenDevice { int remote_port; int local_port; - int evtchndev; - int gnttabdev; + XenEvtchn evtchndev; + XenGnttab gnttabdev; struct XenDevOps *ops; QTAILQ_ENTRY(XenDevice) next; @@ -55,7 +55,7 @@ struct XenDevice { /* ------------------------------------------------------------- */ /* variables */ -extern int xen_xc; +extern XenXC xen_xc; extern struct xs_handle *xenstore; extern const char *xen_protocol; diff --git a/hw/xen_common.h b/hw/xen_common.h index 8a55b44f0b..a1958a0af1 100644 --- a/hw/xen_common.h +++ b/hw/xen_common.h @@ -1,6 +1,8 @@ #ifndef QEMU_HW_XEN_COMMON_H #define QEMU_HW_XEN_COMMON_H 1 +#include "config-host.h" + #include <stddef.h> #include <inttypes.h> @@ -13,22 +15,98 @@ #include "qemu-queue.h" /* - * tweaks needed to build with different xen versions - * 0x00030205 -> 3.1.0 - * 0x00030207 -> 3.2.0 - * 0x00030208 -> unstable + * We don't support Xen prior to 3.3.0. */ -#include <xen/xen-compat.h> -#if __XEN_LATEST_INTERFACE_VERSION__ < 0x00030205 -# define evtchn_port_or_error_t int -#endif -#if __XEN_LATEST_INTERFACE_VERSION__ < 0x00030207 -# define xc_map_foreign_pages xc_map_foreign_batch + +/* Xen before 4.0 */ +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 400 +static inline void *xc_map_foreign_bulk(int xc_handle, uint32_t dom, int prot, + xen_pfn_t *arr, int *err, + unsigned int num) +{ + return xc_map_foreign_batch(xc_handle, dom, prot, arr, num); +} #endif -#if __XEN_LATEST_INTERFACE_VERSION__ < 0x00030208 -# define xen_mb() mb() -# define xen_rmb() rmb() -# define xen_wmb() wmb() + + +/* Xen before 4.1 */ +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 410 + +typedef int XenXC; +typedef int XenEvtchn; +typedef int XenGnttab; + +# define XC_INTERFACE_FMT "%i" +# define XC_HANDLER_INITIAL_VALUE -1 + +static inline XenEvtchn xen_xc_evtchn_open(void *logger, + unsigned int open_flags) +{ + return xc_evtchn_open(); +} + +static inline XenGnttab xen_xc_gnttab_open(void *logger, + unsigned int open_flags) +{ + return xc_gnttab_open(); +} + +static inline XenXC xen_xc_interface_open(void *logger, void *dombuild_logger, + unsigned int open_flags) +{ + return xc_interface_open(); +} + +static inline int xc_fd(int xen_xc) +{ + return xen_xc; +} + + +static inline int xc_domain_populate_physmap_exact + (XenXC xc_handle, uint32_t domid, unsigned long nr_extents, + unsigned int extent_order, unsigned int mem_flags, xen_pfn_t *extent_start) +{ + return xc_domain_memory_populate_physmap + (xc_handle, domid, nr_extents, extent_order, mem_flags, extent_start); +} + + +/* Xen 4.1 */ +#else + +typedef xc_interface *XenXC; +typedef xc_evtchn *XenEvtchn; +typedef xc_gnttab *XenGnttab; + +# define XC_INTERFACE_FMT "%p" +# define XC_HANDLER_INITIAL_VALUE NULL + +static inline XenEvtchn xen_xc_evtchn_open(void *logger, + unsigned int open_flags) +{ + return xc_evtchn_open(logger, open_flags); +} + +static inline XenGnttab xen_xc_gnttab_open(void *logger, + unsigned int open_flags) +{ + return xc_gnttab_open(logger, open_flags); +} + +static inline XenXC xen_xc_interface_open(void *logger, void *dombuild_logger, + unsigned int open_flags) +{ + return xc_interface_open(logger, dombuild_logger, open_flags); +} + +/* FIXME There is now way to have the xen fd */ +static inline int xc_fd(xc_interface *xen_xc) +{ + return -1; +} #endif +void destroy_hvm_domain(void); + #endif /* QEMU_HW_XEN_COMMON_H */ diff --git a/hw/xen_devconfig.c b/hw/xen_devconfig.c index 8d50216c04..3a9215566d 100644 --- a/hw/xen_devconfig.c +++ b/hw/xen_devconfig.c @@ -96,7 +96,7 @@ int xen_config_dev_blk(DriveInfo *disk) { char fe[256], be[256]; int vdev = 202 * 256 + 16 * disk->unit; - int cdrom = disk->bdrv->type == BDRV_TYPE_CDROM; + int cdrom = disk->media_cd; const char *devtype = cdrom ? "cdrom" : "disk"; const char *mode = cdrom ? "r" : "w"; diff --git a/hw/xen_disk.c b/hw/xen_disk.c index 558bf8ae25..0c298afa8d 100644 --- a/hw/xen_disk.c +++ b/hw/xen_disk.c @@ -120,17 +120,18 @@ static struct ioreq *ioreq_start(struct XenBlkDev *blkdev) struct ioreq *ioreq = NULL; if (QLIST_EMPTY(&blkdev->freelist)) { - if (blkdev->requests_total >= max_requests) - goto out; - /* allocate new struct */ - ioreq = qemu_mallocz(sizeof(*ioreq)); - ioreq->blkdev = blkdev; - blkdev->requests_total++; + if (blkdev->requests_total >= max_requests) { + goto out; + } + /* allocate new struct */ + ioreq = qemu_mallocz(sizeof(*ioreq)); + ioreq->blkdev = blkdev; + blkdev->requests_total++; qemu_iovec_init(&ioreq->v, BLKIF_MAX_SEGMENTS_PER_REQUEST); } else { - /* get one from freelist */ - ioreq = QLIST_FIRST(&blkdev->freelist); - QLIST_REMOVE(ioreq, list); + /* get one from freelist */ + ioreq = QLIST_FIRST(&blkdev->freelist); + QLIST_REMOVE(ioreq, list); qemu_iovec_reset(&ioreq->v); } QLIST_INSERT_HEAD(&blkdev->inflight, ioreq, list); @@ -173,30 +174,32 @@ static int ioreq_parse(struct ioreq *ioreq) int i; xen_be_printf(&blkdev->xendev, 3, - "op %d, nr %d, handle %d, id %" PRId64 ", sector %" PRId64 "\n", - ioreq->req.operation, ioreq->req.nr_segments, - ioreq->req.handle, ioreq->req.id, ioreq->req.sector_number); + "op %d, nr %d, handle %d, id %" PRId64 ", sector %" PRId64 "\n", + ioreq->req.operation, ioreq->req.nr_segments, + ioreq->req.handle, ioreq->req.id, ioreq->req.sector_number); switch (ioreq->req.operation) { case BLKIF_OP_READ: - ioreq->prot = PROT_WRITE; /* to memory */ - break; + ioreq->prot = PROT_WRITE; /* to memory */ + break; case BLKIF_OP_WRITE_BARRIER: if (!ioreq->req.nr_segments) { ioreq->presync = 1; return 0; } - if (!syncwrite) - ioreq->presync = ioreq->postsync = 1; - /* fall through */ + if (!syncwrite) { + ioreq->presync = ioreq->postsync = 1; + } + /* fall through */ case BLKIF_OP_WRITE: - ioreq->prot = PROT_READ; /* from memory */ - if (syncwrite) - ioreq->postsync = 1; - break; + ioreq->prot = PROT_READ; /* from memory */ + if (syncwrite) { + ioreq->postsync = 1; + } + break; default: - xen_be_printf(&blkdev->xendev, 0, "error: unknown operation (%d)\n", - ioreq->req.operation); - goto err; + xen_be_printf(&blkdev->xendev, 0, "error: unknown operation (%d)\n", + ioreq->req.operation); + goto err; }; if (ioreq->req.operation != BLKIF_OP_READ && blkdev->mode[0] != 'w') { @@ -206,29 +209,29 @@ static int ioreq_parse(struct ioreq *ioreq) ioreq->start = ioreq->req.sector_number * blkdev->file_blk; for (i = 0; i < ioreq->req.nr_segments; i++) { - if (i == BLKIF_MAX_SEGMENTS_PER_REQUEST) { - xen_be_printf(&blkdev->xendev, 0, "error: nr_segments too big\n"); - goto err; - } - if (ioreq->req.seg[i].first_sect > ioreq->req.seg[i].last_sect) { - xen_be_printf(&blkdev->xendev, 0, "error: first > last sector\n"); - goto err; - } - if (ioreq->req.seg[i].last_sect * BLOCK_SIZE >= XC_PAGE_SIZE) { - xen_be_printf(&blkdev->xendev, 0, "error: page crossing\n"); - goto err; - } - - ioreq->domids[i] = blkdev->xendev.dom; - ioreq->refs[i] = ioreq->req.seg[i].gref; - - mem = ioreq->req.seg[i].first_sect * blkdev->file_blk; - len = (ioreq->req.seg[i].last_sect - ioreq->req.seg[i].first_sect + 1) * blkdev->file_blk; + if (i == BLKIF_MAX_SEGMENTS_PER_REQUEST) { + xen_be_printf(&blkdev->xendev, 0, "error: nr_segments too big\n"); + goto err; + } + if (ioreq->req.seg[i].first_sect > ioreq->req.seg[i].last_sect) { + xen_be_printf(&blkdev->xendev, 0, "error: first > last sector\n"); + goto err; + } + if (ioreq->req.seg[i].last_sect * BLOCK_SIZE >= XC_PAGE_SIZE) { + xen_be_printf(&blkdev->xendev, 0, "error: page crossing\n"); + goto err; + } + + ioreq->domids[i] = blkdev->xendev.dom; + ioreq->refs[i] = ioreq->req.seg[i].gref; + + mem = ioreq->req.seg[i].first_sect * blkdev->file_blk; + len = (ioreq->req.seg[i].last_sect - ioreq->req.seg[i].first_sect + 1) * blkdev->file_blk; qemu_iovec_add(&ioreq->v, (void*)mem, len); } if (ioreq->start + ioreq->v.size > blkdev->file_size) { - xen_be_printf(&blkdev->xendev, 0, "error: access beyond end of file\n"); - goto err; + xen_be_printf(&blkdev->xendev, 0, "error: access beyond end of file\n"); + goto err; } return 0; @@ -239,66 +242,73 @@ err: static void ioreq_unmap(struct ioreq *ioreq) { - int gnt = ioreq->blkdev->xendev.gnttabdev; + XenGnttab gnt = ioreq->blkdev->xendev.gnttabdev; int i; - if (ioreq->v.niov == 0) + if (ioreq->v.niov == 0) { return; + } if (batch_maps) { - if (!ioreq->pages) - return; - if (xc_gnttab_munmap(gnt, ioreq->pages, ioreq->v.niov) != 0) - xen_be_printf(&ioreq->blkdev->xendev, 0, "xc_gnttab_munmap failed: %s\n", - strerror(errno)); - ioreq->blkdev->cnt_map -= ioreq->v.niov; - ioreq->pages = NULL; + if (!ioreq->pages) { + return; + } + if (xc_gnttab_munmap(gnt, ioreq->pages, ioreq->v.niov) != 0) { + xen_be_printf(&ioreq->blkdev->xendev, 0, "xc_gnttab_munmap failed: %s\n", + strerror(errno)); + } + ioreq->blkdev->cnt_map -= ioreq->v.niov; + ioreq->pages = NULL; } else { - for (i = 0; i < ioreq->v.niov; i++) { - if (!ioreq->page[i]) - continue; - if (xc_gnttab_munmap(gnt, ioreq->page[i], 1) != 0) - xen_be_printf(&ioreq->blkdev->xendev, 0, "xc_gnttab_munmap failed: %s\n", - strerror(errno)); - ioreq->blkdev->cnt_map--; - ioreq->page[i] = NULL; - } + for (i = 0; i < ioreq->v.niov; i++) { + if (!ioreq->page[i]) { + continue; + } + if (xc_gnttab_munmap(gnt, ioreq->page[i], 1) != 0) { + xen_be_printf(&ioreq->blkdev->xendev, 0, "xc_gnttab_munmap failed: %s\n", + strerror(errno)); + } + ioreq->blkdev->cnt_map--; + ioreq->page[i] = NULL; + } } } static int ioreq_map(struct ioreq *ioreq) { - int gnt = ioreq->blkdev->xendev.gnttabdev; + XenGnttab gnt = ioreq->blkdev->xendev.gnttabdev; int i; - if (ioreq->v.niov == 0) + if (ioreq->v.niov == 0) { return 0; + } if (batch_maps) { - ioreq->pages = xc_gnttab_map_grant_refs - (gnt, ioreq->v.niov, ioreq->domids, ioreq->refs, ioreq->prot); - if (ioreq->pages == NULL) { - xen_be_printf(&ioreq->blkdev->xendev, 0, - "can't map %d grant refs (%s, %d maps)\n", - ioreq->v.niov, strerror(errno), ioreq->blkdev->cnt_map); - return -1; - } - for (i = 0; i < ioreq->v.niov; i++) - ioreq->v.iov[i].iov_base = ioreq->pages + i * XC_PAGE_SIZE + - (uintptr_t)ioreq->v.iov[i].iov_base; - ioreq->blkdev->cnt_map += ioreq->v.niov; + ioreq->pages = xc_gnttab_map_grant_refs + (gnt, ioreq->v.niov, ioreq->domids, ioreq->refs, ioreq->prot); + if (ioreq->pages == NULL) { + xen_be_printf(&ioreq->blkdev->xendev, 0, + "can't map %d grant refs (%s, %d maps)\n", + ioreq->v.niov, strerror(errno), ioreq->blkdev->cnt_map); + return -1; + } + for (i = 0; i < ioreq->v.niov; i++) { + ioreq->v.iov[i].iov_base = ioreq->pages + i * XC_PAGE_SIZE + + (uintptr_t)ioreq->v.iov[i].iov_base; + } + ioreq->blkdev->cnt_map += ioreq->v.niov; } else { - for (i = 0; i < ioreq->v.niov; i++) { - ioreq->page[i] = xc_gnttab_map_grant_ref - (gnt, ioreq->domids[i], ioreq->refs[i], ioreq->prot); - if (ioreq->page[i] == NULL) { - xen_be_printf(&ioreq->blkdev->xendev, 0, - "can't map grant ref %d (%s, %d maps)\n", - ioreq->refs[i], strerror(errno), ioreq->blkdev->cnt_map); - ioreq_unmap(ioreq); - return -1; - } - ioreq->v.iov[i].iov_base = ioreq->page[i] + (uintptr_t)ioreq->v.iov[i].iov_base; - ioreq->blkdev->cnt_map++; - } + for (i = 0; i < ioreq->v.niov; i++) { + ioreq->page[i] = xc_gnttab_map_grant_ref + (gnt, ioreq->domids[i], ioreq->refs[i], ioreq->prot); + if (ioreq->page[i] == NULL) { + xen_be_printf(&ioreq->blkdev->xendev, 0, + "can't map grant ref %d (%s, %d maps)\n", + ioreq->refs[i], strerror(errno), ioreq->blkdev->cnt_map); + ioreq_unmap(ioreq); + return -1; + } + ioreq->v.iov[i].iov_base = ioreq->page[i] + (uintptr_t)ioreq->v.iov[i].iov_base; + ioreq->blkdev->cnt_map++; + } } return 0; } @@ -306,57 +316,59 @@ static int ioreq_map(struct ioreq *ioreq) static int ioreq_runio_qemu_sync(struct ioreq *ioreq) { struct XenBlkDev *blkdev = ioreq->blkdev; - int i, rc, len = 0; + int i, rc; off_t pos; - if (ioreq->req.nr_segments && ioreq_map(ioreq) == -1) - goto err_no_map; - if (ioreq->presync) - bdrv_flush(blkdev->bs); + if (ioreq->req.nr_segments && ioreq_map(ioreq) == -1) { + goto err_no_map; + } + if (ioreq->presync) { + bdrv_flush(blkdev->bs); + } switch (ioreq->req.operation) { case BLKIF_OP_READ: - pos = ioreq->start; - for (i = 0; i < ioreq->v.niov; i++) { - rc = bdrv_read(blkdev->bs, pos / BLOCK_SIZE, - ioreq->v.iov[i].iov_base, - ioreq->v.iov[i].iov_len / BLOCK_SIZE); - if (rc != 0) { - xen_be_printf(&blkdev->xendev, 0, "rd I/O error (%p, len %zd)\n", - ioreq->v.iov[i].iov_base, - ioreq->v.iov[i].iov_len); - goto err; - } - len += ioreq->v.iov[i].iov_len; - pos += ioreq->v.iov[i].iov_len; - } - break; + pos = ioreq->start; + for (i = 0; i < ioreq->v.niov; i++) { + rc = bdrv_read(blkdev->bs, pos / BLOCK_SIZE, + ioreq->v.iov[i].iov_base, + ioreq->v.iov[i].iov_len / BLOCK_SIZE); + if (rc != 0) { + xen_be_printf(&blkdev->xendev, 0, "rd I/O error (%p, len %zd)\n", + ioreq->v.iov[i].iov_base, + ioreq->v.iov[i].iov_len); + goto err; + } + pos += ioreq->v.iov[i].iov_len; + } + break; case BLKIF_OP_WRITE: case BLKIF_OP_WRITE_BARRIER: - if (!ioreq->req.nr_segments) + if (!ioreq->req.nr_segments) { break; - pos = ioreq->start; - for (i = 0; i < ioreq->v.niov; i++) { - rc = bdrv_write(blkdev->bs, pos / BLOCK_SIZE, - ioreq->v.iov[i].iov_base, - ioreq->v.iov[i].iov_len / BLOCK_SIZE); - if (rc != 0) { - xen_be_printf(&blkdev->xendev, 0, "wr I/O error (%p, len %zd)\n", - ioreq->v.iov[i].iov_base, - ioreq->v.iov[i].iov_len); - goto err; - } - len += ioreq->v.iov[i].iov_len; - pos += ioreq->v.iov[i].iov_len; - } - break; + } + pos = ioreq->start; + for (i = 0; i < ioreq->v.niov; i++) { + rc = bdrv_write(blkdev->bs, pos / BLOCK_SIZE, + ioreq->v.iov[i].iov_base, + ioreq->v.iov[i].iov_len / BLOCK_SIZE); + if (rc != 0) { + xen_be_printf(&blkdev->xendev, 0, "wr I/O error (%p, len %zd)\n", + ioreq->v.iov[i].iov_base, + ioreq->v.iov[i].iov_len); + goto err; + } + pos += ioreq->v.iov[i].iov_len; + } + break; default: - /* unknown operation (shouldn't happen -- parse catches this) */ - goto err; + /* unknown operation (shouldn't happen -- parse catches this) */ + goto err; } - if (ioreq->postsync) - bdrv_flush(blkdev->bs); + if (ioreq->postsync) { + bdrv_flush(blkdev->bs); + } ioreq->status = BLKIF_RSP_OKAY; ioreq_unmap(ioreq); @@ -382,8 +394,9 @@ static void qemu_aio_complete(void *opaque, int ret) } ioreq->aio_inflight--; - if (ioreq->aio_inflight > 0) + if (ioreq->aio_inflight > 0) { return; + } ioreq->status = ioreq->aio_errors ? BLKIF_RSP_ERROR : BLKIF_RSP_OKAY; ioreq_unmap(ioreq); @@ -395,12 +408,14 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq) { struct XenBlkDev *blkdev = ioreq->blkdev; - if (ioreq->req.nr_segments && ioreq_map(ioreq) == -1) - goto err_no_map; + if (ioreq->req.nr_segments && ioreq_map(ioreq) == -1) { + goto err_no_map; + } ioreq->aio_inflight++; - if (ioreq->presync) - bdrv_flush(blkdev->bs); /* FIXME: aio_flush() ??? */ + if (ioreq->presync) { + bdrv_flush(blkdev->bs); /* FIXME: aio_flush() ??? */ + } switch (ioreq->req.operation) { case BLKIF_OP_READ: @@ -408,23 +423,25 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq) bdrv_aio_readv(blkdev->bs, ioreq->start / BLOCK_SIZE, &ioreq->v, ioreq->v.size / BLOCK_SIZE, qemu_aio_complete, ioreq); - break; + break; case BLKIF_OP_WRITE: case BLKIF_OP_WRITE_BARRIER: - if (!ioreq->req.nr_segments) + if (!ioreq->req.nr_segments) { break; + } ioreq->aio_inflight++; bdrv_aio_writev(blkdev->bs, ioreq->start / BLOCK_SIZE, &ioreq->v, ioreq->v.size / BLOCK_SIZE, qemu_aio_complete, ioreq); - break; + break; default: - /* unknown operation (shouldn't happen -- parse catches this) */ - goto err; + /* unknown operation (shouldn't happen -- parse catches this) */ + goto err; } - if (ioreq->postsync) - bdrv_flush(blkdev->bs); /* FIXME: aio_flush() ??? */ + if (ioreq->postsync) { + bdrv_flush(blkdev->bs); /* FIXME: aio_flush() ??? */ + } qemu_aio_complete(ioreq, 0); return 0; @@ -452,36 +469,37 @@ static int blk_send_response_one(struct ioreq *ioreq) /* 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); - break; + dst = 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); - break; + break; case BLKIF_PROTOCOL_X86_64: dst = RING_GET_RESPONSE(&blkdev->rings.x86_64_part, blkdev->rings.x86_64_part.rsp_prod_pvt); - break; + break; default: - dst = NULL; + dst = NULL; } memcpy(dst, &resp, sizeof(resp)); blkdev->rings.common.rsp_prod_pvt++; RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&blkdev->rings.common, send_notify); if (blkdev->rings.common.rsp_prod_pvt == blkdev->rings.common.req_cons) { - /* - * Tail check for pending requests. Allows frontend to avoid - * notifications if requests are already in flight (lower - * overheads and promotes batching). - */ - RING_FINAL_CHECK_FOR_REQUESTS(&blkdev->rings.common, have_requests); + /* + * Tail check for pending requests. Allows frontend to avoid + * notifications if requests are already in flight (lower + * overheads and promotes batching). + */ + RING_FINAL_CHECK_FOR_REQUESTS(&blkdev->rings.common, have_requests); } else if (RING_HAS_UNCONSUMED_REQUESTS(&blkdev->rings.common)) { - have_requests = 1; + have_requests = 1; } - if (have_requests) - blkdev->more_work++; + if (have_requests) { + blkdev->more_work++; + } return send_notify; } @@ -493,28 +511,29 @@ static void blk_send_response_all(struct XenBlkDev *blkdev) while (!QLIST_EMPTY(&blkdev->finished)) { ioreq = QLIST_FIRST(&blkdev->finished); - send_notify += blk_send_response_one(ioreq); - ioreq_release(ioreq); + send_notify += blk_send_response_one(ioreq); + ioreq_release(ioreq); + } + if (send_notify) { + xen_be_send_notify(&blkdev->xendev); } - if (send_notify) - xen_be_send_notify(&blkdev->xendev); } static int blk_get_request(struct XenBlkDev *blkdev, struct ioreq *ioreq, RING_IDX rc) { switch (blkdev->protocol) { case BLKIF_PROTOCOL_NATIVE: - memcpy(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.native, rc), - sizeof(ioreq->req)); - break; + memcpy(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.native, rc), + sizeof(ioreq->req)); + break; case BLKIF_PROTOCOL_X86_32: blkif_get_x86_32_req(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.x86_32_part, rc)); - break; + break; case BLKIF_PROTOCOL_X86_64: blkif_get_x86_64_req(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.x86_64_part, rc)); - break; + break; } return 0; } @@ -530,12 +549,14 @@ static void blk_handle_requests(struct XenBlkDev *blkdev) rp = blkdev->rings.common.sring->req_prod; xen_rmb(); /* Ensure we see queued requests up to 'rp'. */ - if (use_aio) + if (use_aio) { blk_send_response_all(blkdev); + } while (rc != rp) { /* pull request from ring */ - if (RING_REQUEST_CONS_OVERFLOW(&blkdev->rings.common, rc)) + if (RING_REQUEST_CONS_OVERFLOW(&blkdev->rings.common, rc)) { break; + } ioreq = ioreq_start(blkdev); if (ioreq == NULL) { blkdev->more_work++; @@ -546,8 +567,9 @@ static void blk_handle_requests(struct XenBlkDev *blkdev) /* parse them */ if (ioreq_parse(ioreq) != 0) { - if (blk_send_response_one(ioreq)) + if (blk_send_response_one(ioreq)) { xen_be_send_notify(&blkdev->xendev); + } ioreq_release(ioreq); continue; } @@ -560,11 +582,13 @@ static void blk_handle_requests(struct XenBlkDev *blkdev) ioreq_runio_qemu_sync(ioreq); } } - if (!use_aio) + if (!use_aio) { blk_send_response_all(blkdev); + } - if (blkdev->more_work && blkdev->requests_inflight < max_requests) + if (blkdev->more_work && blkdev->requests_inflight < max_requests) { qemu_bh_schedule(blkdev->bh); + } } /* ------------------------------------------------------------- */ @@ -583,8 +607,9 @@ static void blk_alloc(struct XenDevice *xendev) QLIST_INIT(&blkdev->finished); QLIST_INIT(&blkdev->freelist); blkdev->bh = qemu_bh_new(blk_bh, blkdev); - if (xen_mode != XEN_EMULATE) + if (xen_mode != XEN_EMULATE) { batch_maps = 1; + } } static int blk_init(struct XenDevice *xendev) @@ -595,44 +620,50 @@ static int blk_init(struct XenDevice *xendev) /* read xenstore entries */ if (blkdev->params == NULL) { - blkdev->params = xenstore_read_be_str(&blkdev->xendev, "params"); + blkdev->params = xenstore_read_be_str(&blkdev->xendev, "params"); h = strchr(blkdev->params, ':'); - if (h != NULL) { - blkdev->fileproto = blkdev->params; - blkdev->filename = h+1; - *h = 0; - } else { - blkdev->fileproto = "<unset>"; - blkdev->filename = blkdev->params; - } - } - if (blkdev->mode == NULL) - blkdev->mode = xenstore_read_be_str(&blkdev->xendev, "mode"); - if (blkdev->type == NULL) - blkdev->type = xenstore_read_be_str(&blkdev->xendev, "type"); - if (blkdev->dev == NULL) - blkdev->dev = xenstore_read_be_str(&blkdev->xendev, "dev"); - if (blkdev->devtype == NULL) - blkdev->devtype = xenstore_read_be_str(&blkdev->xendev, "device-type"); + if (h != NULL) { + blkdev->fileproto = blkdev->params; + blkdev->filename = h+1; + *h = 0; + } else { + blkdev->fileproto = "<unset>"; + blkdev->filename = blkdev->params; + } + } + if (blkdev->mode == NULL) { + blkdev->mode = xenstore_read_be_str(&blkdev->xendev, "mode"); + } + if (blkdev->type == NULL) { + blkdev->type = xenstore_read_be_str(&blkdev->xendev, "type"); + } + if (blkdev->dev == NULL) { + blkdev->dev = xenstore_read_be_str(&blkdev->xendev, "dev"); + } + if (blkdev->devtype == NULL) { + blkdev->devtype = xenstore_read_be_str(&blkdev->xendev, "device-type"); + } /* do we have all we need? */ if (blkdev->params == NULL || - blkdev->mode == NULL || - blkdev->type == NULL || - blkdev->dev == NULL) - return -1; + blkdev->mode == NULL || + blkdev->type == NULL || + blkdev->dev == NULL) { + return -1; + } /* read-only ? */ if (strcmp(blkdev->mode, "w") == 0) { - qflags = BDRV_O_RDWR; + qflags = BDRV_O_RDWR; } else { - qflags = 0; - info |= VDISK_READONLY; + qflags = 0; + info |= VDISK_READONLY; } /* cdrom ? */ - if (blkdev->devtype && !strcmp(blkdev->devtype, "cdrom")) - info |= VDISK_CDROM; + if (blkdev->devtype && !strcmp(blkdev->devtype, "cdrom")) { + info |= VDISK_CDROM; + } /* init qemu block driver */ index = (blkdev->xendev.dev - 202 * 256) / 16; @@ -649,7 +680,7 @@ static int blk_init(struct XenDevice *xendev) } else { /* setup via qemu cmdline -> already setup for us */ xen_be_printf(&blkdev->xendev, 2, "get configured bdrv (cmdline setup)\n"); - blkdev->bs = blkdev->dinfo->bdrv; + blkdev->bs = blkdev->dinfo->bdrv; } blkdev->file_blk = BLOCK_SIZE; blkdev->file_size = bdrv_getlength(blkdev->bs); @@ -657,21 +688,21 @@ static int blk_init(struct XenDevice *xendev) xen_be_printf(&blkdev->xendev, 1, "bdrv_getlength: %d (%s) | drv %s\n", (int)blkdev->file_size, strerror(-blkdev->file_size), blkdev->bs->drv ? blkdev->bs->drv->format_name : "-"); - blkdev->file_size = 0; + blkdev->file_size = 0; } have_barriers = blkdev->bs->drv && blkdev->bs->drv->bdrv_flush ? 1 : 0; xen_be_printf(xendev, 1, "type \"%s\", fileproto \"%s\", filename \"%s\"," - " size %" PRId64 " (%" PRId64 " MB)\n", - blkdev->type, blkdev->fileproto, blkdev->filename, - blkdev->file_size, blkdev->file_size >> 20); + " size %" PRId64 " (%" PRId64 " MB)\n", + blkdev->type, blkdev->fileproto, blkdev->filename, + blkdev->file_size, blkdev->file_size >> 20); /* fill info */ xenstore_write_be_int(&blkdev->xendev, "feature-barrier", have_barriers); xenstore_write_be_int(&blkdev->xendev, "info", info); xenstore_write_be_int(&blkdev->xendev, "sector-size", blkdev->file_blk); xenstore_write_be_int(&blkdev->xendev, "sectors", - blkdev->file_size / blkdev->file_blk); + blkdev->file_size / blkdev->file_blk); return 0; } @@ -679,57 +710,62 @@ static int blk_connect(struct XenDevice *xendev) { struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); - if (xenstore_read_fe_int(&blkdev->xendev, "ring-ref", &blkdev->ring_ref) == -1) - return -1; + if (xenstore_read_fe_int(&blkdev->xendev, "ring-ref", &blkdev->ring_ref) == -1) { + return -1; + } if (xenstore_read_fe_int(&blkdev->xendev, "event-channel", - &blkdev->xendev.remote_port) == -1) - return -1; + &blkdev->xendev.remote_port) == -1) { + return -1; + } blkdev->protocol = BLKIF_PROTOCOL_NATIVE; if (blkdev->xendev.protocol) { - if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_32) == 0) + if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_32) == 0) { blkdev->protocol = BLKIF_PROTOCOL_X86_32; - if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_64) == 0) + } + if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_64) == 0) { blkdev->protocol = BLKIF_PROTOCOL_X86_64; + } } blkdev->sring = xc_gnttab_map_grant_ref(blkdev->xendev.gnttabdev, - blkdev->xendev.dom, - blkdev->ring_ref, - PROT_READ | PROT_WRITE); - if (!blkdev->sring) - return -1; + blkdev->xendev.dom, + blkdev->ring_ref, + PROT_READ | PROT_WRITE); + 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); - break; + blkif_sring_t *sring_native = blkdev->sring; + BACK_RING_INIT(&blkdev->rings.native, sring_native, XC_PAGE_SIZE); + break; } case BLKIF_PROTOCOL_X86_32: { - blkif_x86_32_sring_t *sring_x86_32 = blkdev->sring; + blkif_x86_32_sring_t *sring_x86_32 = blkdev->sring; BACK_RING_INIT(&blkdev->rings.x86_32_part, sring_x86_32, XC_PAGE_SIZE); - break; + break; } case BLKIF_PROTOCOL_X86_64: { - blkif_x86_64_sring_t *sring_x86_64 = blkdev->sring; + blkif_x86_64_sring_t *sring_x86_64 = blkdev->sring; BACK_RING_INIT(&blkdev->rings.x86_64_part, sring_x86_64, XC_PAGE_SIZE); - break; + break; } } xen_be_bind_evtchn(&blkdev->xendev); xen_be_printf(&blkdev->xendev, 1, "ok: proto %s, ring-ref %d, " - "remote port %d, local port %d\n", - blkdev->xendev.protocol, blkdev->ring_ref, - blkdev->xendev.remote_port, blkdev->xendev.local_port); + "remote port %d, local port %d\n", + blkdev->xendev.protocol, blkdev->ring_ref, + blkdev->xendev.remote_port, blkdev->xendev.local_port); return 0; } @@ -743,14 +779,14 @@ static void blk_disconnect(struct XenDevice *xendev) bdrv_close(blkdev->bs); bdrv_delete(blkdev->bs); } - blkdev->bs = NULL; + blkdev->bs = NULL; } xen_be_unbind_evtchn(&blkdev->xendev); if (blkdev->sring) { - xc_gnttab_munmap(blkdev->xendev.gnttabdev, blkdev->sring, 1); - blkdev->cnt_map--; - blkdev->sring = NULL; + xc_gnttab_munmap(blkdev->xendev.gnttabdev, blkdev->sring, 1); + blkdev->cnt_map--; + blkdev->sring = NULL; } } @@ -760,10 +796,10 @@ static int blk_free(struct XenDevice *xendev) struct ioreq *ioreq; while (!QLIST_EMPTY(&blkdev->freelist)) { - ioreq = QLIST_FIRST(&blkdev->freelist); + ioreq = QLIST_FIRST(&blkdev->freelist); QLIST_REMOVE(ioreq, list); qemu_iovec_destroy(&ioreq->v); - qemu_free(ioreq); + qemu_free(ioreq); } qemu_free(blkdev->params); diff --git a/hw/xen_domainbuild.c b/hw/xen_domainbuild.c index 4093587df1..a6a12e5930 100644 --- a/hw/xen_domainbuild.c +++ b/hw/xen_domainbuild.c @@ -175,8 +175,9 @@ static int xen_domain_watcher(void) for (i = 3; i < n; i++) { if (i == fd[0]) continue; - if (i == xen_xc) + if (i == xc_fd(xen_xc)) { continue; + } close(i); } diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c index 0d7f73ed82..7985d11d5a 100644 --- a/hw/xen_machine_pv.c +++ b/hw/xen_machine_pv.c @@ -113,6 +113,7 @@ static QEMUMachine xenpv_machine = { .desc = "Xen Para-virtualized PC", .init = xen_init_pv, .max_cpus = 1, + .default_machine_opts = "accel=xen", }; static void xenpv_machine_init(void) diff --git a/hw/xen_nic.c b/hw/xen_nic.c index 08055b83ff..ff86491cfa 100644 --- a/hw/xen_nic.c +++ b/hw/xen_nic.c @@ -74,20 +74,23 @@ static void net_tx_response(struct XenNetDev *netdev, netif_tx_request_t *txp, i resp->status = st; #if 0 - if (txp->flags & NETTXF_extra_info) - RING_GET_RESPONSE(&netdev->tx_ring, ++i)->status = NETIF_RSP_NULL; + if (txp->flags & NETTXF_extra_info) { + RING_GET_RESPONSE(&netdev->tx_ring, ++i)->status = NETIF_RSP_NULL; + } #endif netdev->tx_ring.rsp_prod_pvt = ++i; RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->tx_ring, notify); - if (notify) - xen_be_send_notify(&netdev->xendev); + if (notify) { + xen_be_send_notify(&netdev->xendev); + } if (i == netdev->tx_ring.req_cons) { - int more_to_do; - RING_FINAL_CHECK_FOR_REQUESTS(&netdev->tx_ring, more_to_do); - if (more_to_do) - netdev->tx_work++; + int more_to_do; + RING_FINAL_CHECK_FOR_REQUESTS(&netdev->tx_ring, more_to_do); + if (more_to_do) { + netdev->tx_work++; + } } } @@ -101,10 +104,11 @@ static void net_tx_error(struct XenNetDev *netdev, netif_tx_request_t *txp, RING RING_IDX cons = netdev->tx_ring.req_cons; do { - make_tx_response(netif, txp, NETIF_RSP_ERROR); - if (cons >= end) - break; - txp = RING_GET_REQUEST(&netdev->tx_ring, cons++); + make_tx_response(netif, txp, NETIF_RSP_ERROR); + if (cons >= end) { + break; + } + txp = RING_GET_REQUEST(&netdev->tx_ring, cons++); } while (1); netdev->tx_ring.req_cons = cons; netif_schedule_work(netif); @@ -122,75 +126,78 @@ static void net_tx_packets(struct XenNetDev *netdev) void *tmpbuf = NULL; for (;;) { - rc = netdev->tx_ring.req_cons; - rp = netdev->tx_ring.sring->req_prod; - xen_rmb(); /* Ensure we see queued requests up to 'rp'. */ + rc = netdev->tx_ring.req_cons; + rp = netdev->tx_ring.sring->req_prod; + xen_rmb(); /* Ensure we see queued requests up to 'rp'. */ - while ((rc != rp)) { - if (RING_REQUEST_CONS_OVERFLOW(&netdev->tx_ring, rc)) - break; - memcpy(&txreq, RING_GET_REQUEST(&netdev->tx_ring, rc), sizeof(txreq)); - netdev->tx_ring.req_cons = ++rc; + while ((rc != rp)) { + if (RING_REQUEST_CONS_OVERFLOW(&netdev->tx_ring, rc)) { + break; + } + memcpy(&txreq, RING_GET_REQUEST(&netdev->tx_ring, rc), sizeof(txreq)); + netdev->tx_ring.req_cons = ++rc; #if 1 - /* should not happen in theory, we don't announce the * - * feature-{sg,gso,whatelse} flags in xenstore (yet?) */ - if (txreq.flags & NETTXF_extra_info) { - xen_be_printf(&netdev->xendev, 0, "FIXME: extra info flag\n"); - net_tx_error(netdev, &txreq, rc); - continue; - } - if (txreq.flags & NETTXF_more_data) { - xen_be_printf(&netdev->xendev, 0, "FIXME: more data flag\n"); - net_tx_error(netdev, &txreq, rc); - continue; - } + /* should not happen in theory, we don't announce the * + * feature-{sg,gso,whatelse} flags in xenstore (yet?) */ + if (txreq.flags & NETTXF_extra_info) { + xen_be_printf(&netdev->xendev, 0, "FIXME: extra info flag\n"); + net_tx_error(netdev, &txreq, rc); + continue; + } + if (txreq.flags & NETTXF_more_data) { + xen_be_printf(&netdev->xendev, 0, "FIXME: more data flag\n"); + net_tx_error(netdev, &txreq, rc); + continue; + } #endif - if (txreq.size < 14) { - xen_be_printf(&netdev->xendev, 0, "bad packet size: %d\n", txreq.size); - net_tx_error(netdev, &txreq, rc); - continue; - } - - if ((txreq.offset + txreq.size) > XC_PAGE_SIZE) { - xen_be_printf(&netdev->xendev, 0, "error: page crossing\n"); - net_tx_error(netdev, &txreq, rc); - continue; - } - - xen_be_printf(&netdev->xendev, 3, "tx packet ref %d, off %d, len %d, flags 0x%x%s%s%s%s\n", - txreq.gref, txreq.offset, txreq.size, txreq.flags, - (txreq.flags & NETTXF_csum_blank) ? " csum_blank" : "", - (txreq.flags & NETTXF_data_validated) ? " data_validated" : "", - (txreq.flags & NETTXF_more_data) ? " more_data" : "", - (txreq.flags & NETTXF_extra_info) ? " extra_info" : ""); - - page = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, - netdev->xendev.dom, - txreq.gref, PROT_READ); - if (page == NULL) { - xen_be_printf(&netdev->xendev, 0, "error: tx gref dereference failed (%d)\n", + if (txreq.size < 14) { + xen_be_printf(&netdev->xendev, 0, "bad packet size: %d\n", txreq.size); + net_tx_error(netdev, &txreq, rc); + continue; + } + + if ((txreq.offset + txreq.size) > XC_PAGE_SIZE) { + xen_be_printf(&netdev->xendev, 0, "error: page crossing\n"); + net_tx_error(netdev, &txreq, rc); + continue; + } + + xen_be_printf(&netdev->xendev, 3, "tx packet ref %d, off %d, len %d, flags 0x%x%s%s%s%s\n", + txreq.gref, txreq.offset, txreq.size, txreq.flags, + (txreq.flags & NETTXF_csum_blank) ? " csum_blank" : "", + (txreq.flags & NETTXF_data_validated) ? " data_validated" : "", + (txreq.flags & NETTXF_more_data) ? " more_data" : "", + (txreq.flags & NETTXF_extra_info) ? " extra_info" : ""); + + page = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + netdev->xendev.dom, + txreq.gref, PROT_READ); + if (page == NULL) { + xen_be_printf(&netdev->xendev, 0, "error: tx gref dereference failed (%d)\n", txreq.gref); - net_tx_error(netdev, &txreq, rc); - continue; - } - if (txreq.flags & NETTXF_csum_blank) { + net_tx_error(netdev, &txreq, rc); + continue; + } + if (txreq.flags & NETTXF_csum_blank) { /* have read-only mapping -> can't fill checksum in-place */ - if (!tmpbuf) + if (!tmpbuf) { tmpbuf = qemu_malloc(XC_PAGE_SIZE); + } memcpy(tmpbuf, page + txreq.offset, txreq.size); - net_checksum_calculate(tmpbuf, txreq.size); + net_checksum_calculate(tmpbuf, txreq.size); qemu_send_packet(&netdev->nic->nc, tmpbuf, txreq.size); } else { qemu_send_packet(&netdev->nic->nc, page + txreq.offset, txreq.size); } - xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1); - net_tx_response(netdev, &txreq, NETIF_RSP_OKAY); - } - if (!netdev->tx_work) - break; - netdev->tx_work = 0; + xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1); + net_tx_response(netdev, &txreq, NETIF_RSP_OKAY); + } + if (!netdev->tx_work) { + break; + } + netdev->tx_work = 0; } qemu_free(tmpbuf); } @@ -198,9 +205,9 @@ static void net_tx_packets(struct XenNetDev *netdev) /* ------------------------------------------------------------- */ static void net_rx_response(struct XenNetDev *netdev, - netif_rx_request_t *req, int8_t st, - uint16_t offset, uint16_t size, - uint16_t flags) + netif_rx_request_t *req, int8_t st, + uint16_t offset, uint16_t size, + uint16_t flags) { RING_IDX i = netdev->rx_ring.rsp_prod_pvt; netif_rx_response_t *resp; @@ -211,16 +218,18 @@ static void net_rx_response(struct XenNetDev *netdev, resp->flags = flags; resp->id = req->id; resp->status = (int16_t)size; - if (st < 0) - resp->status = (int16_t)st; + if (st < 0) { + resp->status = (int16_t)st; + } xen_be_printf(&netdev->xendev, 3, "rx response: idx %d, status %d, flags 0x%x\n", - i, resp->status, resp->flags); + i, resp->status, resp->flags); netdev->rx_ring.rsp_prod_pvt = ++i; RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->rx_ring, notify); - if (notify) - xen_be_send_notify(&netdev->xendev); + if (notify) { + xen_be_send_notify(&netdev->xendev); + } } #define NET_IP_ALIGN 2 @@ -230,17 +239,18 @@ static int net_rx_ok(VLANClientState *nc) struct XenNetDev *netdev = DO_UPCAST(NICState, nc, nc)->opaque; RING_IDX rc, rp; - if (netdev->xendev.be_state != XenbusStateConnected) - return 0; + if (netdev->xendev.be_state != XenbusStateConnected) { + return 0; + } rc = netdev->rx_ring.req_cons; rp = netdev->rx_ring.sring->req_prod; xen_rmb(); if (rc == rp || RING_REQUEST_CONS_OVERFLOW(&netdev->rx_ring, rc)) { - xen_be_printf(&netdev->xendev, 2, "%s: no rx buffers (%d/%d)\n", - __FUNCTION__, rc, rp); - return 0; + xen_be_printf(&netdev->xendev, 2, "%s: no rx buffers (%d/%d)\n", + __FUNCTION__, rc, rp); + return 0; } return 1; } @@ -252,34 +262,35 @@ static ssize_t net_rx_packet(VLANClientState *nc, const uint8_t *buf, size_t siz RING_IDX rc, rp; void *page; - if (netdev->xendev.be_state != XenbusStateConnected) - return -1; + if (netdev->xendev.be_state != XenbusStateConnected) { + return -1; + } rc = netdev->rx_ring.req_cons; rp = netdev->rx_ring.sring->req_prod; xen_rmb(); /* Ensure we see queued requests up to 'rp'. */ if (rc == rp || RING_REQUEST_CONS_OVERFLOW(&netdev->rx_ring, rc)) { - xen_be_printf(&netdev->xendev, 2, "no buffer, drop packet\n"); - return -1; + xen_be_printf(&netdev->xendev, 2, "no buffer, drop packet\n"); + return -1; } if (size > XC_PAGE_SIZE - NET_IP_ALIGN) { - xen_be_printf(&netdev->xendev, 0, "packet too big (%lu > %ld)", - (unsigned long)size, XC_PAGE_SIZE - NET_IP_ALIGN); - return -1; + xen_be_printf(&netdev->xendev, 0, "packet too big (%lu > %ld)", + (unsigned long)size, XC_PAGE_SIZE - NET_IP_ALIGN); + return -1; } memcpy(&rxreq, RING_GET_REQUEST(&netdev->rx_ring, rc), sizeof(rxreq)); netdev->rx_ring.req_cons = ++rc; page = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, - netdev->xendev.dom, - rxreq.gref, PROT_WRITE); + netdev->xendev.dom, + rxreq.gref, PROT_WRITE); if (page == NULL) { - xen_be_printf(&netdev->xendev, 0, "error: rx gref dereference failed (%d)\n", + xen_be_printf(&netdev->xendev, 0, "error: rx gref dereference failed (%d)\n", rxreq.gref); - net_rx_response(netdev, &rxreq, NETIF_RSP_ERROR, 0, 0, 0); - return -1; + net_rx_response(netdev, &rxreq, NETIF_RSP_ERROR, 0, 0, 0); + return -1; } memcpy(page + NET_IP_ALIGN, buf, size); xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1); @@ -302,15 +313,18 @@ static int net_init(struct XenDevice *xendev) struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); /* read xenstore entries */ - if (netdev->mac == NULL) - netdev->mac = xenstore_read_be_str(&netdev->xendev, "mac"); + if (netdev->mac == NULL) { + netdev->mac = xenstore_read_be_str(&netdev->xendev, "mac"); + } /* do we have all we need? */ - if (netdev->mac == NULL) - return -1; + if (netdev->mac == NULL) { + return -1; + } - if (net_parse_macaddr(netdev->conf.macaddr.a, netdev->mac) < 0) + if (net_parse_macaddr(netdev->conf.macaddr.a, netdev->mac) < 0) { return -1; + } netdev->conf.vlan = qemu_find_vlan(netdev->xendev.dev, 1); netdev->conf.peer = NULL; @@ -334,41 +348,46 @@ static int net_connect(struct XenDevice *xendev) int rx_copy; if (xenstore_read_fe_int(&netdev->xendev, "tx-ring-ref", - &netdev->tx_ring_ref) == -1) - return -1; + &netdev->tx_ring_ref) == -1) { + return -1; + } if (xenstore_read_fe_int(&netdev->xendev, "rx-ring-ref", - &netdev->rx_ring_ref) == -1) - return 1; + &netdev->rx_ring_ref) == -1) { + return 1; + } if (xenstore_read_fe_int(&netdev->xendev, "event-channel", - &netdev->xendev.remote_port) == -1) - return -1; + &netdev->xendev.remote_port) == -1) { + return -1; + } - if (xenstore_read_fe_int(&netdev->xendev, "request-rx-copy", &rx_copy) == -1) - rx_copy = 0; + if (xenstore_read_fe_int(&netdev->xendev, "request-rx-copy", &rx_copy) == -1) { + rx_copy = 0; + } if (rx_copy == 0) { - xen_be_printf(&netdev->xendev, 0, "frontend doesn't support rx-copy.\n"); - return -1; + xen_be_printf(&netdev->xendev, 0, "frontend doesn't support rx-copy.\n"); + return -1; } netdev->txs = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, - netdev->xendev.dom, - netdev->tx_ring_ref, - PROT_READ | PROT_WRITE); + netdev->xendev.dom, + netdev->tx_ring_ref, + PROT_READ | PROT_WRITE); netdev->rxs = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, - netdev->xendev.dom, - netdev->rx_ring_ref, - PROT_READ | PROT_WRITE); - if (!netdev->txs || !netdev->rxs) - return -1; + netdev->xendev.dom, + netdev->rx_ring_ref, + PROT_READ | PROT_WRITE); + if (!netdev->txs || !netdev->rxs) { + return -1; + } BACK_RING_INIT(&netdev->tx_ring, netdev->txs, XC_PAGE_SIZE); BACK_RING_INIT(&netdev->rx_ring, netdev->rxs, XC_PAGE_SIZE); xen_be_bind_evtchn(&netdev->xendev); xen_be_printf(&netdev->xendev, 1, "ok: tx-ring-ref %d, rx-ring-ref %d, " - "remote port %d, local port %d\n", - netdev->tx_ring_ref, netdev->rx_ring_ref, - netdev->xendev.remote_port, netdev->xendev.local_port); + "remote port %d, local port %d\n", + netdev->tx_ring_ref, netdev->rx_ring_ref, + netdev->xendev.remote_port, netdev->xendev.local_port); net_tx_packets(netdev); return 0; @@ -381,12 +400,12 @@ static void net_disconnect(struct XenDevice *xendev) xen_be_unbind_evtchn(&netdev->xendev); if (netdev->txs) { - xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->txs, 1); - netdev->txs = NULL; + xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->txs, 1); + netdev->txs = NULL; } if (netdev->rxs) { - xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->rxs, 1); - netdev->rxs = NULL; + xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->rxs, 1); + netdev->rxs = NULL; } if (netdev->nic) { qemu_del_vlan_client(&netdev->nic->nc); diff --git a/hw/xilinx_axidma.c b/hw/xilinx_axidma.c index e32534feaf..571a5b0661 100644 --- a/hw/xilinx_axidma.c +++ b/hw/xilinx_axidma.c @@ -134,10 +134,10 @@ static inline int stream_idle(struct AXIStream *s) static void stream_reset(struct AXIStream *s) { s->regs[R_DMASR] = DMASR_HALTED; /* starts up halted. */ - s->regs[R_DMACR] = 1 << 16; /* Starts with one in compl threshhold. */ + s->regs[R_DMACR] = 1 << 16; /* Starts with one in compl threshold. */ } -/* Mapp an offset addr into a channel index. */ +/* Map an offset addr into a channel index. */ static inline int streamid_from_addr(target_phys_addr_t addr) { int sid; |