aboutsummaryrefslogtreecommitdiffstats
path: root/hw
diff options
context:
space:
mode:
authorMichael S. Tsirkin <mst@redhat.com>2011-06-14 17:51:11 +0300
committerMichael S. Tsirkin <mst@redhat.com>2011-06-15 18:27:15 +0300
commitbefeac45d4d9afb587eca9a27d975db4a7950960 (patch)
treeaab24c856a3ea944e287d7f2591bb4bab6a56eb4 /hw
parent35f754620615138aaae0ef72602f84c88fd8de0f (diff)
parent0b862cedf36d927818c50584ddd611b0370673df (diff)
Merge remote-tracking branch 'origin/master' into pci
Conflicts: hw/virtio-pci.c
Diffstat (limited to 'hw')
-rw-r--r--hw/9pfs/virtio-9p-debug.c5
-rw-r--r--hw/9pfs/virtio-9p-device.c173
-rw-r--r--hw/9pfs/virtio-9p-local.c138
-rw-r--r--hw/9pfs/virtio-9p-posix-acl.c22
-rw-r--r--hw/9pfs/virtio-9p-xattr-user.c11
-rw-r--r--hw/9pfs/virtio-9p-xattr.c7
-rw-r--r--hw/9pfs/virtio-9p-xattr.h9
-rw-r--r--hw/9pfs/virtio-9p.c165
-rw-r--r--hw/9pfs/virtio-9p.h7
-rw-r--r--hw/ac97.c6
-rw-r--r--hw/acpi_piix4.c4
-rw-r--r--hw/alpha_palcode.c1048
-rw-r--r--hw/bitbang_i2c.c5
-rw-r--r--hw/boards.h1
-rw-r--r--hw/bt-hid.c6
-rw-r--r--hw/bt.h2
-rw-r--r--hw/eepro100.c2
-rw-r--r--hw/eeprom93xx.c10
-rw-r--r--hw/esp.c119
-rw-r--r--hw/ide/ahci.c35
-rw-r--r--hw/ide/core.c17
-rw-r--r--hw/ide/ich.c6
-rw-r--r--hw/ide/internal.h2
-rw-r--r--hw/ide/pci.c8
-rw-r--r--hw/ide/qdev.c81
-rw-r--r--hw/lan9118.c2
-rw-r--r--hw/lsi53c895a.c214
-rw-r--r--hw/msi.c2
-rw-r--r--hw/msix.c2
-rw-r--r--hw/mst_fpga.c2
-rw-r--r--hw/multiboot.c2
-rw-r--r--hw/pc.c37
-rw-r--r--hw/pc.h11
-rw-r--r--hw/pc_piix.c71
-rw-r--r--hw/pci.c8
-rw-r--r--hw/pci.h2
-rw-r--r--hw/pci_ids.h1
-rw-r--r--hw/pci_regs.h2
-rw-r--r--hw/pcie.c2
-rw-r--r--hw/pcie.h2
-rw-r--r--hw/pcie_aer.c2
-rw-r--r--hw/pflash_cfi02.c4
-rw-r--r--hw/piix_pci.c51
-rw-r--r--hw/pl031.c2
-rw-r--r--hw/pl061.c4
-rw-r--r--hw/ppc.c12
-rw-r--r--hw/ppc4xx_devs.c2
-rw-r--r--hw/ppce500.h22
-rw-r--r--hw/ppce500_mpc8544ds.c113
-rw-r--r--hw/ppce500_pci.c136
-rw-r--r--hw/qxl-render.c25
-rw-r--r--hw/qxl.c31
-rw-r--r--hw/realview.c1
-rw-r--r--hw/rtl8139.c44
-rw-r--r--hw/s390-virtio-bus.c3
-rw-r--r--hw/s390-virtio-bus.h2
-rw-r--r--hw/s390-virtio.c20
-rw-r--r--hw/scsi-bus.c224
-rw-r--r--hw/scsi-disk.c408
-rw-r--r--hw/scsi-generic.c223
-rw-r--r--hw/scsi.h91
-rw-r--r--hw/sd.c11
-rw-r--r--hw/sga.c56
-rw-r--r--hw/sh7750_regs.h6
-rw-r--r--hw/spapr.c9
-rw-r--r--hw/spapr_hcall.c11
-rw-r--r--hw/spapr_llan.c3
-rw-r--r--hw/spapr_rtas.c3
-rw-r--r--hw/spapr_vio.h2
-rw-r--r--hw/spapr_vscsi.c187
-rw-r--r--hw/ssd0303.c2
-rw-r--r--hw/sun4m_iommu.c2
-rw-r--r--hw/syborg_serial.c2
-rw-r--r--hw/usb-bt.c6
-rw-r--r--hw/usb-ccid.c4
-rw-r--r--hw/usb-desc.c56
-rw-r--r--hw/usb-desc.h24
-rw-r--r--hw/usb-ehci.c2037
-rw-r--r--hw/usb-hid.c9
-rw-r--r--hw/usb-hub.c23
-rw-r--r--hw/usb-msd.c143
-rw-r--r--hw/usb-musb.c81
-rw-r--r--hw/usb-net.c6
-rw-r--r--hw/usb-ohci.c13
-rw-r--r--hw/usb-serial.c7
-rw-r--r--hw/usb-uhci.c88
-rw-r--r--hw/usb-wacom.c7
-rw-r--r--hw/usb.c107
-rw-r--r--hw/usb.h45
-rw-r--r--hw/vga-isa.c1
-rw-r--r--hw/virtio-console.c47
-rw-r--r--hw/virtio-pci.c60
-rw-r--r--hw/virtio-pci.h45
-rw-r--r--hw/virtio-serial-bus.c83
-rw-r--r--hw/virtio-serial.h11
-rw-r--r--hw/xen.h41
-rw-r--r--hw/xen_backend.c421
-rw-r--r--hw/xen_backend.h6
-rw-r--r--hw/xen_common.h106
-rw-r--r--hw/xen_devconfig.c2
-rw-r--r--hw/xen_disk.c496
-rw-r--r--hw/xen_domainbuild.c3
-rw-r--r--hw/xen_machine_pv.c1
-rw-r--r--hw/xen_nic.c265
-rw-r--r--hw/xilinx_axidma.c4
105 files changed, 5163 insertions, 3048 deletions
diff --git a/hw/9pfs/virtio-9p-debug.c b/hw/9pfs/virtio-9p-debug.c
index 6b18842fd..4636ad51f 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 000000000..f235236ea
--- /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 0a015de9a..77904c37b 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 575abe86b..f5b392e18 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 bba13ce64..5044a3e5a 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 03c3d3f6b..bde0b7fb4 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 2bbae2dcb..247e414eb 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 b5fc52b3e..4890df6f7 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 622928fce..2bfbe622a 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
diff --git a/hw/ac97.c b/hw/ac97.c
index c26a86da4..0b59896fb 100644
--- a/hw/ac97.c
+++ b/hw/ac97.c
@@ -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 232008dd9..350558b85 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 033b54201..000000000
--- 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 4ee99a18b..2937b5c4a 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 6f0f0d792..716fd7b1a 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 abdfd35e8..09120af07 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? */
diff --git a/hw/bt.h b/hw/bt.h
index 4a702adef..379725469 100644
--- a/hw/bt.h
+++ b/hw/bt.h
@@ -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 84b98c092..9b6f4a5cd 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 660b28f22..7b21f98e2 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 {
diff --git a/hw/esp.c b/hw/esp.c
index fa9d2a270..6d3f5d239 100644
--- a/hw/esp.c
+++ b/hw/esp.c
@@ -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 c6e0c7767..1f008a3dd 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 90f553b69..95beb175b 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 976cc9244..054e0734e 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 aa198b6b1..c2b35ec5e 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 65cb56c38..a4726adbe 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 2bb5c2734..3f9dc89c6 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 2dc8d1854..4c42fe94c 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 6b9c904ab..3b75467da 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);
}
diff --git a/hw/msi.c b/hw/msi.c
index 941b339c6..e8c56079a 100644
--- a/hw/msi.c
+++ b/hw/msi.c
@@ -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));
}
diff --git a/hw/msix.c b/hw/msix.c
index 28559b6d6..03d7becaa 100644
--- a/hw/msix.c
+++ b/hw/msix.c
@@ -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 a04355cc7..4e47574b6 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 394ed0136..6e6cfb953 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);
diff --git a/hw/pc.c b/hw/pc.c
index 6939c0456..a3e8539dc 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -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);
diff --git a/hw/pc.h b/hw/pc.h
index feb8a7a68..0dcbee7ca 100644
--- a/hw/pc.h
+++ b/hw/pc.h
@@ -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 1aba09f8f..90861257d 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);
diff --git a/hw/pci.c b/hw/pci.c
index 0e97a028f..ba0598ba1 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -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 */
diff --git a/hw/pci.h b/hw/pci.h
index ce214f4e5..c220745c9 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -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 ea3418cef..d9457ed3f 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 63e895686..e8840964a 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 */
diff --git a/hw/pcie.c b/hw/pcie.c
index 9de614904..39607bf31 100644
--- a/hw/pcie.c
+++ b/hw/pcie.c
@@ -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
diff --git a/hw/pcie.h b/hw/pcie.h
index bc909e279..a213fbaee 100644
--- a/hw/pcie.h
+++ b/hw/pcie.h
@@ -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 b87201026..be019c7c0 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 14bbc34e1..725cd1e78 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 b927f01bd..e0da0bdf9 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 8c2f9d0bc..017a313fd 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 2e181f8c2..372dfc2da 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 */
diff --git a/hw/ppc.c b/hw/ppc.c
index 18733289d..915771944 100644
--- a/hw/ppc.c
+++ b/hw/ppc.c
@@ -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 7f9ed1713..68bdfaacc 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 24d49bb87..000000000
--- 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 e111dda5f..6b57fbf59 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 83a20e462..069af9691 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 58965e017..131606659 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;
}
}
diff --git a/hw/qxl.c b/hw/qxl.c
index 63cffc384..16316f2bf 100644
--- a/hw/qxl.c
+++ b/hw/qxl.c
@@ -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 96fb9da24..82f3d82d4 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 34e3a9eec..5214b8cb7 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 bb49e393e..d4a12f753 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 edf6d0457..0c412d08e 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 698ff6f34..3eba7ea1e 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 ceeb4ecb9..ad6a730be 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 b05e6547d..a8c7372d3 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 9be1cca4c..8e59c7ee8 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(),
diff --git a/hw/scsi.h b/hw/scsi.h
index d3b5d56cd..c1dca35b8 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -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
diff --git a/hw/sd.c b/hw/sd.c
index f44a97092..cedfb2024 100644
--- a/hw/sd.c
+++ b/hw/sd.c
@@ -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 000000000..7ef750adf
--- /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 5a23a2ca2..6ec13ab6f 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 1782cc0a9..109b77459 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 f88e1d208..43c441dc7 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 ff3a78f72..c18efc7ee 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 16b65422b..00c8ce5a1 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 841b04351..603a8c43a 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 992833450..1c901ef6e 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 108c0683c..b39e2596f 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 bba69eef9..7f5dad535 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 df2950fe8..2ef71758b 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 22e684504..baae4876e 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 079b4a255..5b6878bea 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 62591f20a..e4a4680fe 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 ac734ab08..9d7ed599c 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 000000000..f63519ecf
--- /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 89c293c46..53b261c3b 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 3dd31ba31..6e2a35839 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 947fd3f83..c59797b27 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 15bc549a8..6037193db 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 bf51bb389..9be709f7c 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 7ff23228c..a67556af4 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 6763d5204..59cb0fb2f 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 536c24c82..872a99557 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 16be7a20c..9d348e170 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;
}
diff --git a/hw/usb.c b/hw/usb.c
index 82a6217a0..4a39cbcc7 100644
--- a/hw/usb.c
+++ b/hw/usb.c
@@ -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;
}
diff --git a/hw/usb.h b/hw/usb.h
index d3d755db7..98824009b 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -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 fde0d56fd..245841f18 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 de539c4ea..b076331d3 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 b86c44162..b3e7ba5d1 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 000000000..b5189172d
--- /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 f10d48fdb..9a1210498 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 5eb948e3f..36e9d222e 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.
diff --git a/hw/xen.h b/hw/xen.h
index 780dcf713..d435ca0ce 100644
--- a/hw/xen.h
+++ b/hw/xen.h
@@ -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 a2e408fa0..d881fa2f7 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 1b428e3bf..6401c85a7 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 8a55b44f0..a1958a0af 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 8d50216c0..3a9215566 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 558bf8ae2..0c298afa8 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 4093587df..a6a12e593 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 0d7f73ed8..7985d11d5 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 08055b83f..ff86491cf 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 e32534fea..571a5b066 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;