aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/9pfs/9p-synth.c200
-rw-r--r--hw/acpi/Makefile.objs2
-rw-r--r--hw/acpi/aml-build.c22
-rw-r--r--hw/acpi/cpu.c561
-rw-r--r--hw/acpi/cpu_hotplug.c21
-rw-r--r--hw/acpi/ich9.c69
-rw-r--r--hw/acpi/ipmi.c105
-rw-r--r--hw/acpi/nvdimm.c400
-rw-r--r--hw/acpi/piix4.c71
-rw-r--r--hw/acpi/trace-events14
-rw-r--r--hw/arm/ast2400.c61
-rw-r--r--hw/arm/fsl-imx25.c8
-rw-r--r--hw/arm/fsl-imx31.c9
-rw-r--r--hw/arm/fsl-imx6.c8
-rw-r--r--hw/arm/palmetto-bmc.c33
-rw-r--r--hw/arm/sabrelite.c18
-rw-r--r--hw/arm/spitz.c12
-rw-r--r--hw/arm/tosa.c5
-rw-r--r--hw/arm/virt.c1
-rw-r--r--hw/arm/xilinx_zynq.c14
-rw-r--r--hw/arm/xlnx-ep108.c9
-rw-r--r--hw/arm/z2.c6
-rw-r--r--hw/audio/pcspk.c9
-rw-r--r--hw/block/dataplane/virtio-blk.c83
-rw-r--r--hw/block/dataplane/virtio-blk.h2
-rw-r--r--hw/block/m25p80.c472
-rw-r--r--hw/block/pflash_cfi01.c1
-rw-r--r--hw/block/pflash_cfi02.c1
-rw-r--r--hw/block/virtio-blk.c57
-rw-r--r--hw/char/cadence_uart.c16
-rw-r--r--hw/char/serial.c67
-rw-r--r--hw/core/Makefile.objs1
-rw-r--r--hw/core/register.c287
-rw-r--r--hw/display/ads7846.c5
-rw-r--r--hw/display/ssd0323.c5
-rw-r--r--hw/dma/Makefile.objs1
-rw-r--r--hw/dma/xlnx-zynq-devcfg.c400
-rw-r--r--hw/i2c/smbus_ich9.c1
-rw-r--r--hw/i386/acpi-build.c80
-rw-r--r--hw/i386/intel_iommu.c12
-rw-r--r--hw/i386/kvm/pci-assign.c18
-rw-r--r--hw/i386/pc.c73
-rw-r--r--hw/i386/pc_piix.c2
-rw-r--r--hw/i386/pc_q35.c30
-rw-r--r--hw/i386/pci-assign-load-rom.c3
-rw-r--r--hw/ide/ahci.c2
-rw-r--r--hw/ide/ahci.h405
-rw-r--r--hw/ide/internal.h635
-rw-r--r--hw/ide/macio.c2
-rw-r--r--hw/ide/pci.h76
-rw-r--r--hw/input/pckbd.c21
-rw-r--r--hw/intc/Makefile.objs1
-rw-r--r--hw/intc/arm_gicv3_cpuif.c2
-rw-r--r--hw/intc/armv7m_nvic.c8
-rw-r--r--hw/intc/xics.c548
-rw-r--r--hw/intc/xics_kvm.c63
-rw-r--r--hw/intc/xics_spapr.c434
-rw-r--r--hw/isa/isa-bus.c7
-rw-r--r--hw/isa/lpc_ich9.c90
-rw-r--r--hw/mem/nvdimm.c132
-rw-r--r--hw/mem/pc-dimm.c14
-rw-r--r--hw/microblaze/petalogix_ml605_mmu.c9
-rw-r--r--hw/misc/Makefile.objs1
-rw-r--r--hw/misc/aspeed_scu.c284
-rw-r--r--hw/misc/max111x.c12
-rw-r--r--hw/misc/trace-events3
-rw-r--r--hw/misc/vmport.c1
-rw-r--r--hw/net/cadence_gem.c13
-rw-r--r--hw/net/e1000.c18
-rw-r--r--hw/net/e1000e_core.c6
-rw-r--r--hw/net/e1000x_common.c2
-rw-r--r--hw/net/eepro100.c8
-rw-r--r--hw/net/mipsnet.c8
-rw-r--r--hw/net/rocker/rocker_tlv.h6
-rw-r--r--hw/net/rtl8139.c49
-rw-r--r--hw/net/virtio-net.c2
-rw-r--r--hw/net/vmware_utils.h55
-rw-r--r--hw/net/vmxnet3.c197
-rw-r--r--hw/pci-host/q35.c20
-rw-r--r--hw/ppc/Makefile.objs1
-rw-r--r--hw/ppc/e500.c2
-rw-r--r--hw/ppc/e500.h2
-rw-r--r--hw/ppc/ppc.c17
-rw-r--r--hw/ppc/ppce500_spin.c9
-rw-r--r--hw/ppc/spapr.c40
-rw-r--r--hw/ppc/spapr_cpu_core.c75
-rw-r--r--hw/ppc/spapr_events.c8
-rw-r--r--hw/ppc/spapr_hcall.c4
-rw-r--r--hw/ppc/spapr_iommu.c12
-rw-r--r--hw/ppc/spapr_pci.c94
-rw-r--r--hw/ppc/spapr_rtas_ddw.c295
-rw-r--r--hw/ppc/spapr_vio.c2
-rw-r--r--hw/ppc/trace-events4
-rw-r--r--hw/s390x/virtio-ccw.c133
-rw-r--r--hw/scsi/esp.c5
-rw-r--r--hw/scsi/virtio-scsi-dataplane.c9
-rw-r--r--hw/scsi/virtio-scsi.c5
-rw-r--r--hw/sd/ssi-sd.c9
-rw-r--r--hw/sh4/sh_pci.c4
-rw-r--r--hw/smbios/Makefile.objs1
-rw-r--r--hw/smbios/smbios.c72
-rw-r--r--hw/smbios/smbios_build.h87
-rw-r--r--hw/smbios/smbios_type_38.c117
-rw-r--r--hw/ssi/Makefile.objs1
-rw-r--r--hw/ssi/aspeed_smc.c470
-rw-r--r--hw/ssi/ssi.c6
-rw-r--r--hw/timer/mc146818rtc.c6
-rw-r--r--hw/vfio/Makefile.objs1
-rw-r--r--hw/vfio/common.c175
-rw-r--r--hw/vfio/pci-quirks.c8
-rw-r--r--hw/vfio/pci.c101
-rw-r--r--hw/vfio/pci.h1
-rw-r--r--hw/vfio/spapr.c210
-rw-r--r--hw/vfio/trace-events9
-rw-r--r--hw/virtio/vhost.c13
-rw-r--r--hw/virtio/virtio-bus.c132
-rw-r--r--hw/virtio/virtio-mmio.c128
-rw-r--r--hw/virtio/virtio-pci.c124
118 files changed, 6054 insertions, 2747 deletions
diff --git a/hw/9pfs/9p-synth.c b/hw/9pfs/9p-synth.c
index 73c8be816b..4b6d4e6a3f 100644
--- a/hw/9pfs/9p-synth.c
+++ b/hw/9pfs/9p-synth.c
@@ -21,19 +21,19 @@
#include "qemu/cutils.h"
/* Root node for synth file system */
-static V9fsSynthNode v9fs_synth_root = {
+static V9fsSynthNode synth_root = {
.name = "/",
.actual_attr = {
.mode = 0555 | S_IFDIR,
.nlink = 1,
},
- .attr = &v9fs_synth_root.actual_attr,
+ .attr = &synth_root.actual_attr,
};
-static QemuMutex v9fs_synth_mutex;
-static int v9fs_synth_node_count;
+static QemuMutex synth_mutex;
+static int synth_node_count;
/* set to 1 when the synth fs is ready */
-static int v9fs_synth_fs;
+static int synth_fs;
static V9fsSynthNode *v9fs_add_dir_node(V9fsSynthNode *parent, int mode,
const char *name,
@@ -69,16 +69,16 @@ int qemu_v9fs_synth_mkdir(V9fsSynthNode *parent, int mode,
int ret;
V9fsSynthNode *node, *tmp;
- if (!v9fs_synth_fs) {
+ if (!synth_fs) {
return EAGAIN;
}
if (!name || (strlen(name) >= NAME_MAX)) {
return EINVAL;
}
if (!parent) {
- parent = &v9fs_synth_root;
+ parent = &synth_root;
}
- qemu_mutex_lock(&v9fs_synth_mutex);
+ qemu_mutex_lock(&synth_mutex);
QLIST_FOREACH(tmp, &parent->child, sibling) {
if (!strcmp(tmp->name, name)) {
ret = EEXIST;
@@ -86,7 +86,7 @@ int qemu_v9fs_synth_mkdir(V9fsSynthNode *parent, int mode,
}
}
/* Add the name */
- node = v9fs_add_dir_node(parent, mode, name, NULL, v9fs_synth_node_count++);
+ node = v9fs_add_dir_node(parent, mode, name, NULL, synth_node_count++);
v9fs_add_dir_node(node, parent->attr->mode, "..",
parent->attr, parent->attr->inode);
v9fs_add_dir_node(node, node->attr->mode, ".",
@@ -94,7 +94,7 @@ int qemu_v9fs_synth_mkdir(V9fsSynthNode *parent, int mode,
*result = node;
ret = 0;
err_out:
- qemu_mutex_unlock(&v9fs_synth_mutex);
+ qemu_mutex_unlock(&synth_mutex);
return ret;
}
@@ -105,17 +105,17 @@ int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode,
int ret;
V9fsSynthNode *node, *tmp;
- if (!v9fs_synth_fs) {
+ if (!synth_fs) {
return EAGAIN;
}
if (!name || (strlen(name) >= NAME_MAX)) {
return EINVAL;
}
if (!parent) {
- parent = &v9fs_synth_root;
+ parent = &synth_root;
}
- qemu_mutex_lock(&v9fs_synth_mutex);
+ qemu_mutex_lock(&synth_mutex);
QLIST_FOREACH(tmp, &parent->child, sibling) {
if (!strcmp(tmp->name, name)) {
ret = EEXIST;
@@ -126,7 +126,7 @@ int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode,
mode = ((mode & 0777) | S_IFREG);
node = g_malloc0(sizeof(V9fsSynthNode));
node->attr = &node->actual_attr;
- node->attr->inode = v9fs_synth_node_count++;
+ node->attr->inode = synth_node_count++;
node->attr->nlink = 1;
node->attr->read = read;
node->attr->write = write;
@@ -136,11 +136,11 @@ int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode,
QLIST_INSERT_HEAD_RCU(&parent->child, node, sibling);
ret = 0;
err_out:
- qemu_mutex_unlock(&v9fs_synth_mutex);
+ qemu_mutex_unlock(&synth_mutex);
return ret;
}
-static void v9fs_synth_fill_statbuf(V9fsSynthNode *node, struct stat *stbuf)
+static void synth_fill_statbuf(V9fsSynthNode *node, struct stat *stbuf)
{
stbuf->st_dev = 0;
stbuf->st_ino = node->attr->inode;
@@ -157,24 +157,24 @@ static void v9fs_synth_fill_statbuf(V9fsSynthNode *node, struct stat *stbuf)
stbuf->st_ctime = 0;
}
-static int v9fs_synth_lstat(FsContext *fs_ctx,
+static int synth_lstat(FsContext *fs_ctx,
V9fsPath *fs_path, struct stat *stbuf)
{
V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data;
- v9fs_synth_fill_statbuf(node, stbuf);
+ synth_fill_statbuf(node, stbuf);
return 0;
}
-static int v9fs_synth_fstat(FsContext *fs_ctx, int fid_type,
+static int synth_fstat(FsContext *fs_ctx, int fid_type,
V9fsFidOpenState *fs, struct stat *stbuf)
{
V9fsSynthOpenState *synth_open = fs->private;
- v9fs_synth_fill_statbuf(synth_open->node, stbuf);
+ synth_fill_statbuf(synth_open->node, stbuf);
return 0;
}
-static int v9fs_synth_opendir(FsContext *ctx,
+static int synth_opendir(FsContext *ctx,
V9fsPath *fs_path, V9fsFidOpenState *fs)
{
V9fsSynthOpenState *synth_open;
@@ -187,7 +187,7 @@ static int v9fs_synth_opendir(FsContext *ctx,
return 0;
}
-static int v9fs_synth_closedir(FsContext *ctx, V9fsFidOpenState *fs)
+static int synth_closedir(FsContext *ctx, V9fsFidOpenState *fs)
{
V9fsSynthOpenState *synth_open = fs->private;
V9fsSynthNode *node = synth_open->node;
@@ -198,24 +198,24 @@ static int v9fs_synth_closedir(FsContext *ctx, V9fsFidOpenState *fs)
return 0;
}
-static off_t v9fs_synth_telldir(FsContext *ctx, V9fsFidOpenState *fs)
+static off_t synth_telldir(FsContext *ctx, V9fsFidOpenState *fs)
{
V9fsSynthOpenState *synth_open = fs->private;
return synth_open->offset;
}
-static void v9fs_synth_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
+static void synth_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
{
V9fsSynthOpenState *synth_open = fs->private;
synth_open->offset = off;
}
-static void v9fs_synth_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
+static void synth_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
{
- v9fs_synth_seekdir(ctx, fs, 0);
+ synth_seekdir(ctx, fs, 0);
}
-static void v9fs_synth_direntry(V9fsSynthNode *node,
+static void synth_direntry(V9fsSynthNode *node,
struct dirent *entry, off_t off)
{
strcpy(entry->d_name, node->name);
@@ -223,7 +223,7 @@ static void v9fs_synth_direntry(V9fsSynthNode *node,
entry->d_off = off + 1;
}
-static struct dirent *v9fs_synth_get_dentry(V9fsSynthNode *dir,
+static struct dirent *synth_get_dentry(V9fsSynthNode *dir,
struct dirent *entry, off_t off)
{
int i = 0;
@@ -242,23 +242,23 @@ static struct dirent *v9fs_synth_get_dentry(V9fsSynthNode *dir,
/* end of directory */
return NULL;
}
- v9fs_synth_direntry(node, entry, off);
+ synth_direntry(node, entry, off);
return entry;
}
-static struct dirent *v9fs_synth_readdir(FsContext *ctx, V9fsFidOpenState *fs)
+static struct dirent *synth_readdir(FsContext *ctx, V9fsFidOpenState *fs)
{
struct dirent *entry;
V9fsSynthOpenState *synth_open = fs->private;
V9fsSynthNode *node = synth_open->node;
- entry = v9fs_synth_get_dentry(node, &synth_open->dent, synth_open->offset);
+ entry = synth_get_dentry(node, &synth_open->dent, synth_open->offset);
if (entry) {
synth_open->offset++;
}
return entry;
}
-static int v9fs_synth_open(FsContext *ctx, V9fsPath *fs_path,
+static int synth_open(FsContext *ctx, V9fsPath *fs_path,
int flags, V9fsFidOpenState *fs)
{
V9fsSynthOpenState *synth_open;
@@ -271,7 +271,7 @@ static int v9fs_synth_open(FsContext *ctx, V9fsPath *fs_path,
return 0;
}
-static int v9fs_synth_open2(FsContext *fs_ctx, V9fsPath *dir_path,
+static int synth_open2(FsContext *fs_ctx, V9fsPath *dir_path,
const char *name, int flags,
FsCred *credp, V9fsFidOpenState *fs)
{
@@ -279,7 +279,7 @@ static int v9fs_synth_open2(FsContext *fs_ctx, V9fsPath *dir_path,
return -1;
}
-static int v9fs_synth_close(FsContext *ctx, V9fsFidOpenState *fs)
+static int synth_close(FsContext *ctx, V9fsFidOpenState *fs)
{
V9fsSynthOpenState *synth_open = fs->private;
V9fsSynthNode *node = synth_open->node;
@@ -290,7 +290,7 @@ static int v9fs_synth_close(FsContext *ctx, V9fsFidOpenState *fs)
return 0;
}
-static ssize_t v9fs_synth_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
+static ssize_t synth_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
const struct iovec *iov,
int iovcnt, off_t offset)
{
@@ -314,7 +314,7 @@ static ssize_t v9fs_synth_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
return count;
}
-static ssize_t v9fs_synth_preadv(FsContext *ctx, V9fsFidOpenState *fs,
+static ssize_t synth_preadv(FsContext *ctx, V9fsFidOpenState *fs,
const struct iovec *iov,
int iovcnt, off_t offset)
{
@@ -338,112 +338,112 @@ static ssize_t v9fs_synth_preadv(FsContext *ctx, V9fsFidOpenState *fs,
return count;
}
-static int v9fs_synth_truncate(FsContext *ctx, V9fsPath *path, off_t offset)
+static int synth_truncate(FsContext *ctx, V9fsPath *path, off_t offset)
{
errno = ENOSYS;
return -1;
}
-static int v9fs_synth_chmod(FsContext *fs_ctx, V9fsPath *path, FsCred *credp)
+static int synth_chmod(FsContext *fs_ctx, V9fsPath *path, FsCred *credp)
{
errno = EPERM;
return -1;
}
-static int v9fs_synth_mknod(FsContext *fs_ctx, V9fsPath *path,
+static int synth_mknod(FsContext *fs_ctx, V9fsPath *path,
const char *buf, FsCred *credp)
{
errno = EPERM;
return -1;
}
-static int v9fs_synth_mkdir(FsContext *fs_ctx, V9fsPath *path,
+static int synth_mkdir(FsContext *fs_ctx, V9fsPath *path,
const char *buf, FsCred *credp)
{
errno = EPERM;
return -1;
}
-static ssize_t v9fs_synth_readlink(FsContext *fs_ctx, V9fsPath *path,
+static ssize_t synth_readlink(FsContext *fs_ctx, V9fsPath *path,
char *buf, size_t bufsz)
{
errno = ENOSYS;
return -1;
}
-static int v9fs_synth_symlink(FsContext *fs_ctx, const char *oldpath,
+static int synth_symlink(FsContext *fs_ctx, const char *oldpath,
V9fsPath *newpath, const char *buf, FsCred *credp)
{
errno = EPERM;
return -1;
}
-static int v9fs_synth_link(FsContext *fs_ctx, V9fsPath *oldpath,
+static int synth_link(FsContext *fs_ctx, V9fsPath *oldpath,
V9fsPath *newpath, const char *buf)
{
errno = EPERM;
return -1;
}
-static int v9fs_synth_rename(FsContext *ctx, const char *oldpath,
+static int synth_rename(FsContext *ctx, const char *oldpath,
const char *newpath)
{
errno = EPERM;
return -1;
}
-static int v9fs_synth_chown(FsContext *fs_ctx, V9fsPath *path, FsCred *credp)
+static int synth_chown(FsContext *fs_ctx, V9fsPath *path, FsCred *credp)
{
errno = EPERM;
return -1;
}
-static int v9fs_synth_utimensat(FsContext *fs_ctx, V9fsPath *path,
+static int synth_utimensat(FsContext *fs_ctx, V9fsPath *path,
const struct timespec *buf)
{
errno = EPERM;
return 0;
}
-static int v9fs_synth_remove(FsContext *ctx, const char *path)
+static int synth_remove(FsContext *ctx, const char *path)
{
errno = EPERM;
return -1;
}
-static int v9fs_synth_fsync(FsContext *ctx, int fid_type,
+static int synth_fsync(FsContext *ctx, int fid_type,
V9fsFidOpenState *fs, int datasync)
{
errno = ENOSYS;
return 0;
}
-static int v9fs_synth_statfs(FsContext *s, V9fsPath *fs_path,
+static int synth_statfs(FsContext *s, V9fsPath *fs_path,
struct statfs *stbuf)
{
stbuf->f_type = 0xABCD;
stbuf->f_bsize = 512;
stbuf->f_blocks = 0;
- stbuf->f_files = v9fs_synth_node_count;
+ stbuf->f_files = synth_node_count;
stbuf->f_namelen = NAME_MAX;
return 0;
}
-static ssize_t v9fs_synth_lgetxattr(FsContext *ctx, V9fsPath *path,
+static ssize_t synth_lgetxattr(FsContext *ctx, V9fsPath *path,
const char *name, void *value, size_t size)
{
errno = ENOTSUP;
return -1;
}
-static ssize_t v9fs_synth_llistxattr(FsContext *ctx, V9fsPath *path,
+static ssize_t synth_llistxattr(FsContext *ctx, V9fsPath *path,
void *value, size_t size)
{
errno = ENOTSUP;
return -1;
}
-static int v9fs_synth_lsetxattr(FsContext *ctx, V9fsPath *path,
+static int synth_lsetxattr(FsContext *ctx, V9fsPath *path,
const char *name, void *value,
size_t size, int flags)
{
@@ -451,14 +451,14 @@ static int v9fs_synth_lsetxattr(FsContext *ctx, V9fsPath *path,
return -1;
}
-static int v9fs_synth_lremovexattr(FsContext *ctx,
+static int synth_lremovexattr(FsContext *ctx,
V9fsPath *path, const char *name)
{
errno = ENOTSUP;
return -1;
}
-static int v9fs_synth_name_to_path(FsContext *ctx, V9fsPath *dir_path,
+static int synth_name_to_path(FsContext *ctx, V9fsPath *dir_path,
const char *name, V9fsPath *target)
{
V9fsSynthNode *node;
@@ -471,7 +471,7 @@ static int v9fs_synth_name_to_path(FsContext *ctx, V9fsPath *dir_path,
}
if (!dir_path) {
- dir_node = &v9fs_synth_root;
+ dir_node = &synth_root;
} else {
dir_node = *(V9fsSynthNode **)dir_path->data;
}
@@ -500,7 +500,7 @@ out:
return 0;
}
-static int v9fs_synth_renameat(FsContext *ctx, V9fsPath *olddir,
+static int synth_renameat(FsContext *ctx, V9fsPath *olddir,
const char *old_name, V9fsPath *newdir,
const char *new_name)
{
@@ -508,62 +508,62 @@ static int v9fs_synth_renameat(FsContext *ctx, V9fsPath *olddir,
return -1;
}
-static int v9fs_synth_unlinkat(FsContext *ctx, V9fsPath *dir,
+static int synth_unlinkat(FsContext *ctx, V9fsPath *dir,
const char *name, int flags)
{
errno = EPERM;
return -1;
}
-static int v9fs_synth_init(FsContext *ctx)
+static int synth_init(FsContext *ctx)
{
- QLIST_INIT(&v9fs_synth_root.child);
- qemu_mutex_init(&v9fs_synth_mutex);
+ QLIST_INIT(&synth_root.child);
+ qemu_mutex_init(&synth_mutex);
/* Add "." and ".." entries for root */
- v9fs_add_dir_node(&v9fs_synth_root, v9fs_synth_root.attr->mode,
- "..", v9fs_synth_root.attr, v9fs_synth_root.attr->inode);
- v9fs_add_dir_node(&v9fs_synth_root, v9fs_synth_root.attr->mode,
- ".", v9fs_synth_root.attr, v9fs_synth_root.attr->inode);
+ v9fs_add_dir_node(&synth_root, synth_root.attr->mode,
+ "..", synth_root.attr, synth_root.attr->inode);
+ v9fs_add_dir_node(&synth_root, synth_root.attr->mode,
+ ".", synth_root.attr, synth_root.attr->inode);
/* Mark the subsystem is ready for use */
- v9fs_synth_fs = 1;
+ synth_fs = 1;
return 0;
}
FileOperations synth_ops = {
- .init = v9fs_synth_init,
- .lstat = v9fs_synth_lstat,
- .readlink = v9fs_synth_readlink,
- .close = v9fs_synth_close,
- .closedir = v9fs_synth_closedir,
- .open = v9fs_synth_open,
- .opendir = v9fs_synth_opendir,
- .rewinddir = v9fs_synth_rewinddir,
- .telldir = v9fs_synth_telldir,
- .readdir = v9fs_synth_readdir,
- .seekdir = v9fs_synth_seekdir,
- .preadv = v9fs_synth_preadv,
- .pwritev = v9fs_synth_pwritev,
- .chmod = v9fs_synth_chmod,
- .mknod = v9fs_synth_mknod,
- .mkdir = v9fs_synth_mkdir,
- .fstat = v9fs_synth_fstat,
- .open2 = v9fs_synth_open2,
- .symlink = v9fs_synth_symlink,
- .link = v9fs_synth_link,
- .truncate = v9fs_synth_truncate,
- .rename = v9fs_synth_rename,
- .chown = v9fs_synth_chown,
- .utimensat = v9fs_synth_utimensat,
- .remove = v9fs_synth_remove,
- .fsync = v9fs_synth_fsync,
- .statfs = v9fs_synth_statfs,
- .lgetxattr = v9fs_synth_lgetxattr,
- .llistxattr = v9fs_synth_llistxattr,
- .lsetxattr = v9fs_synth_lsetxattr,
- .lremovexattr = v9fs_synth_lremovexattr,
- .name_to_path = v9fs_synth_name_to_path,
- .renameat = v9fs_synth_renameat,
- .unlinkat = v9fs_synth_unlinkat,
+ .init = synth_init,
+ .lstat = synth_lstat,
+ .readlink = synth_readlink,
+ .close = synth_close,
+ .closedir = synth_closedir,
+ .open = synth_open,
+ .opendir = synth_opendir,
+ .rewinddir = synth_rewinddir,
+ .telldir = synth_telldir,
+ .readdir = synth_readdir,
+ .seekdir = synth_seekdir,
+ .preadv = synth_preadv,
+ .pwritev = synth_pwritev,
+ .chmod = synth_chmod,
+ .mknod = synth_mknod,
+ .mkdir = synth_mkdir,
+ .fstat = synth_fstat,
+ .open2 = synth_open2,
+ .symlink = synth_symlink,
+ .link = synth_link,
+ .truncate = synth_truncate,
+ .rename = synth_rename,
+ .chown = synth_chown,
+ .utimensat = synth_utimensat,
+ .remove = synth_remove,
+ .fsync = synth_fsync,
+ .statfs = synth_statfs,
+ .lgetxattr = synth_lgetxattr,
+ .llistxattr = synth_llistxattr,
+ .lsetxattr = synth_lsetxattr,
+ .lremovexattr = synth_lremovexattr,
+ .name_to_path = synth_name_to_path,
+ .renameat = synth_renameat,
+ .unlinkat = synth_unlinkat,
};
diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
index 66bd72702b..4b7da6639f 100644
--- a/hw/acpi/Makefile.objs
+++ b/hw/acpi/Makefile.objs
@@ -2,7 +2,9 @@ common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o pcihp.o
common-obj-$(CONFIG_ACPI_X86_ICH) += ich9.o tco.o
common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu_hotplug.o
common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o memory_hotplug_acpi_table.o
+common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu.o
obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o
common-obj-$(CONFIG_ACPI) += acpi_interface.o
common-obj-$(CONFIG_ACPI) += bios-linker-loader.o
common-obj-$(CONFIG_ACPI) += aml-build.o
+common-obj-$(call land,$(CONFIG_ACPI),$(CONFIG_IPMI)) += ipmi.o
diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c
index 874e473cac..db3e914fb4 100644
--- a/hw/acpi/aml-build.c
+++ b/hw/acpi/aml-build.c
@@ -660,6 +660,20 @@ Aml *aml_call4(const char *method, Aml *arg1, Aml *arg2, Aml *arg3, Aml *arg4)
return var;
}
+/* helper to call method with 5 arguments */
+Aml *aml_call5(const char *method, Aml *arg1, Aml *arg2, Aml *arg3, Aml *arg4,
+ Aml *arg5)
+{
+ Aml *var = aml_alloc();
+ build_append_namestring(var->buf, "%s", method);
+ aml_append(var, arg1);
+ aml_append(var, arg2);
+ aml_append(var, arg3);
+ aml_append(var, arg4);
+ aml_append(var, arg5);
+ return var;
+}
+
/*
* ACPI 5.0: 6.4.3.8.1 GPIO Connection Descriptor
* Type 1, Large Item Name 0xC
@@ -1481,6 +1495,14 @@ Aml *aml_concatenate(Aml *source1, Aml *source2, Aml *target)
target);
}
+/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefObjectType */
+Aml *aml_object_type(Aml *object)
+{
+ Aml *var = aml_opcode(0x8E /* ObjectTypeOp */);
+ aml_append(var, object);
+ return var;
+}
+
void
build_header(BIOSLinker *linker, GArray *table_data,
AcpiTableHeader *h, const char *sig, int len, uint8_t rev,
diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c
new file mode 100644
index 0000000000..c13b65c2c9
--- /dev/null
+++ b/hw/acpi/cpu.c
@@ -0,0 +1,561 @@
+#include "qemu/osdep.h"
+#include "hw/boards.h"
+#include "hw/acpi/cpu.h"
+#include "qapi/error.h"
+#include "qapi-event.h"
+#include "trace.h"
+
+#define ACPI_CPU_HOTPLUG_REG_LEN 12
+#define ACPI_CPU_SELECTOR_OFFSET_WR 0
+#define ACPI_CPU_FLAGS_OFFSET_RW 4
+#define ACPI_CPU_CMD_OFFSET_WR 5
+#define ACPI_CPU_CMD_DATA_OFFSET_RW 8
+
+enum {
+ CPHP_GET_NEXT_CPU_WITH_EVENT_CMD = 0,
+ CPHP_OST_EVENT_CMD = 1,
+ CPHP_OST_STATUS_CMD = 2,
+ CPHP_CMD_MAX
+};
+
+static ACPIOSTInfo *acpi_cpu_device_status(int idx, AcpiCpuStatus *cdev)
+{
+ ACPIOSTInfo *info = g_new0(ACPIOSTInfo, 1);
+
+ info->slot_type = ACPI_SLOT_TYPE_CPU;
+ info->slot = g_strdup_printf("%d", idx);
+ info->source = cdev->ost_event;
+ info->status = cdev->ost_status;
+ if (cdev->cpu) {
+ DeviceState *dev = DEVICE(cdev->cpu);
+ if (dev->id) {
+ info->device = g_strdup(dev->id);
+ info->has_device = true;
+ }
+ }
+ return info;
+}
+
+void acpi_cpu_ospm_status(CPUHotplugState *cpu_st, ACPIOSTInfoList ***list)
+{
+ int i;
+
+ for (i = 0; i < cpu_st->dev_count; i++) {
+ ACPIOSTInfoList *elem = g_new0(ACPIOSTInfoList, 1);
+ elem->value = acpi_cpu_device_status(i, &cpu_st->devs[i]);
+ elem->next = NULL;
+ **list = elem;
+ *list = &elem->next;
+ }
+}
+
+static uint64_t cpu_hotplug_rd(void *opaque, hwaddr addr, unsigned size)
+{
+ uint64_t val = 0;
+ CPUHotplugState *cpu_st = opaque;
+ AcpiCpuStatus *cdev;
+
+ if (cpu_st->selector >= cpu_st->dev_count) {
+ return val;
+ }
+
+ cdev = &cpu_st->devs[cpu_st->selector];
+ switch (addr) {
+ case ACPI_CPU_FLAGS_OFFSET_RW: /* pack and return is_* fields */
+ val |= cdev->cpu ? 1 : 0;
+ val |= cdev->is_inserting ? 2 : 0;
+ val |= cdev->is_removing ? 4 : 0;
+ trace_cpuhp_acpi_read_flags(cpu_st->selector, val);
+ break;
+ case ACPI_CPU_CMD_DATA_OFFSET_RW:
+ switch (cpu_st->command) {
+ case CPHP_GET_NEXT_CPU_WITH_EVENT_CMD:
+ val = cpu_st->selector;
+ break;
+ default:
+ break;
+ }
+ trace_cpuhp_acpi_read_cmd_data(cpu_st->selector, val);
+ break;
+ default:
+ break;
+ }
+ return val;
+}
+
+static void cpu_hotplug_wr(void *opaque, hwaddr addr, uint64_t data,
+ unsigned int size)
+{
+ CPUHotplugState *cpu_st = opaque;
+ AcpiCpuStatus *cdev;
+ ACPIOSTInfo *info;
+
+ assert(cpu_st->dev_count);
+
+ if (addr) {
+ if (cpu_st->selector >= cpu_st->dev_count) {
+ trace_cpuhp_acpi_invalid_idx_selected(cpu_st->selector);
+ return;
+ }
+ }
+
+ switch (addr) {
+ case ACPI_CPU_SELECTOR_OFFSET_WR: /* current CPU selector */
+ cpu_st->selector = data;
+ trace_cpuhp_acpi_write_idx(cpu_st->selector);
+ break;
+ case ACPI_CPU_FLAGS_OFFSET_RW: /* set is_* fields */
+ cdev = &cpu_st->devs[cpu_st->selector];
+ if (data & 2) { /* clear insert event */
+ cdev->is_inserting = false;
+ trace_cpuhp_acpi_clear_inserting_evt(cpu_st->selector);
+ } else if (data & 4) { /* clear remove event */
+ cdev->is_removing = false;
+ trace_cpuhp_acpi_clear_remove_evt(cpu_st->selector);
+ } else if (data & 8) {
+ DeviceState *dev = NULL;
+ HotplugHandler *hotplug_ctrl = NULL;
+
+ if (!cdev->cpu) {
+ trace_cpuhp_acpi_ejecting_invalid_cpu(cpu_st->selector);
+ break;
+ }
+
+ trace_cpuhp_acpi_ejecting_cpu(cpu_st->selector);
+ dev = DEVICE(cdev->cpu);
+ hotplug_ctrl = qdev_get_hotplug_handler(dev);
+ hotplug_handler_unplug(hotplug_ctrl, dev, NULL);
+ }
+ break;
+ case ACPI_CPU_CMD_OFFSET_WR:
+ trace_cpuhp_acpi_write_cmd(cpu_st->selector, data);
+ if (data < CPHP_CMD_MAX) {
+ cpu_st->command = data;
+ if (cpu_st->command == CPHP_GET_NEXT_CPU_WITH_EVENT_CMD) {
+ uint32_t iter = cpu_st->selector;
+
+ do {
+ cdev = &cpu_st->devs[iter];
+ if (cdev->is_inserting || cdev->is_removing) {
+ cpu_st->selector = iter;
+ trace_cpuhp_acpi_cpu_has_events(cpu_st->selector,
+ cdev->is_inserting, cdev->is_removing);
+ break;
+ }
+ iter = iter + 1 < cpu_st->dev_count ? iter + 1 : 0;
+ } while (iter != cpu_st->selector);
+ }
+ }
+ break;
+ case ACPI_CPU_CMD_DATA_OFFSET_RW:
+ switch (cpu_st->command) {
+ case CPHP_OST_EVENT_CMD: {
+ cdev = &cpu_st->devs[cpu_st->selector];
+ cdev->ost_event = data;
+ trace_cpuhp_acpi_write_ost_ev(cpu_st->selector, cdev->ost_event);
+ break;
+ }
+ case CPHP_OST_STATUS_CMD: {
+ cdev = &cpu_st->devs[cpu_st->selector];
+ cdev->ost_status = data;
+ info = acpi_cpu_device_status(cpu_st->selector, cdev);
+ qapi_event_send_acpi_device_ost(info, &error_abort);
+ qapi_free_ACPIOSTInfo(info);
+ trace_cpuhp_acpi_write_ost_status(cpu_st->selector,
+ cdev->ost_status);
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static const MemoryRegionOps cpu_hotplug_ops = {
+ .read = cpu_hotplug_rd,
+ .write = cpu_hotplug_wr,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ },
+};
+
+void cpu_hotplug_hw_init(MemoryRegion *as, Object *owner,
+ CPUHotplugState *state, hwaddr base_addr)
+{
+ MachineState *machine = MACHINE(qdev_get_machine());
+ MachineClass *mc = MACHINE_GET_CLASS(machine);
+ CPUArchIdList *id_list;
+ int i;
+
+ assert(mc->possible_cpu_arch_ids);
+ id_list = mc->possible_cpu_arch_ids(machine);
+ state->dev_count = id_list->len;
+ state->devs = g_new0(typeof(*state->devs), state->dev_count);
+ for (i = 0; i < id_list->len; i++) {
+ state->devs[i].cpu = id_list->cpus[i].cpu;
+ state->devs[i].arch_id = id_list->cpus[i].arch_id;
+ }
+ g_free(id_list);
+ memory_region_init_io(&state->ctrl_reg, owner, &cpu_hotplug_ops, state,
+ "acpi-mem-hotplug", ACPI_CPU_HOTPLUG_REG_LEN);
+ memory_region_add_subregion(as, base_addr, &state->ctrl_reg);
+}
+
+static AcpiCpuStatus *get_cpu_status(CPUHotplugState *cpu_st, DeviceState *dev)
+{
+ CPUClass *k = CPU_GET_CLASS(dev);
+ uint64_t cpu_arch_id = k->get_arch_id(CPU(dev));
+ int i;
+
+ for (i = 0; i < cpu_st->dev_count; i++) {
+ if (cpu_arch_id == cpu_st->devs[i].arch_id) {
+ return &cpu_st->devs[i];
+ }
+ }
+ return NULL;
+}
+
+void acpi_cpu_plug_cb(HotplugHandler *hotplug_dev,
+ CPUHotplugState *cpu_st, DeviceState *dev, Error **errp)
+{
+ AcpiCpuStatus *cdev;
+
+ cdev = get_cpu_status(cpu_st, dev);
+ if (!cdev) {
+ return;
+ }
+
+ cdev->cpu = CPU(dev);
+ if (dev->hotplugged) {
+ cdev->is_inserting = true;
+ acpi_send_event(DEVICE(hotplug_dev), ACPI_CPU_HOTPLUG_STATUS);
+ }
+}
+
+void acpi_cpu_unplug_request_cb(HotplugHandler *hotplug_dev,
+ CPUHotplugState *cpu_st,
+ DeviceState *dev, Error **errp)
+{
+ AcpiCpuStatus *cdev;
+
+ cdev = get_cpu_status(cpu_st, dev);
+ if (!cdev) {
+ return;
+ }
+
+ cdev->is_removing = true;
+ acpi_send_event(DEVICE(hotplug_dev), ACPI_CPU_HOTPLUG_STATUS);
+}
+
+void acpi_cpu_unplug_cb(CPUHotplugState *cpu_st,
+ DeviceState *dev, Error **errp)
+{
+ AcpiCpuStatus *cdev;
+
+ cdev = get_cpu_status(cpu_st, dev);
+ if (!cdev) {
+ return;
+ }
+
+ cdev->cpu = NULL;
+}
+
+static const VMStateDescription vmstate_cpuhp_sts = {
+ .name = "CPU hotplug device state",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_BOOL(is_inserting, AcpiCpuStatus),
+ VMSTATE_BOOL(is_removing, AcpiCpuStatus),
+ VMSTATE_UINT32(ost_event, AcpiCpuStatus),
+ VMSTATE_UINT32(ost_status, AcpiCpuStatus),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+const VMStateDescription vmstate_cpu_hotplug = {
+ .name = "CPU hotplug state",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(selector, CPUHotplugState),
+ VMSTATE_UINT8(command, CPUHotplugState),
+ VMSTATE_STRUCT_VARRAY_POINTER_UINT32(devs, CPUHotplugState, dev_count,
+ vmstate_cpuhp_sts, AcpiCpuStatus),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+#define CPU_NAME_FMT "C%.03X"
+#define CPUHP_RES_DEVICE "PRES"
+#define CPU_LOCK "CPLK"
+#define CPU_STS_METHOD "CSTA"
+#define CPU_SCAN_METHOD "CSCN"
+#define CPU_NOTIFY_METHOD "CTFY"
+#define CPU_EJECT_METHOD "CEJ0"
+#define CPU_OST_METHOD "COST"
+
+#define CPU_ENABLED "CPEN"
+#define CPU_SELECTOR "CSEL"
+#define CPU_COMMAND "CCMD"
+#define CPU_DATA "CDAT"
+#define CPU_INSERT_EVENT "CINS"
+#define CPU_REMOVE_EVENT "CRMV"
+#define CPU_EJECT_EVENT "CEJ0"
+
+void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts,
+ hwaddr io_base,
+ const char *res_root,
+ const char *event_handler_method)
+{
+ Aml *ifctx;
+ Aml *field;
+ Aml *method;
+ Aml *cpu_ctrl_dev;
+ Aml *cpus_dev;
+ Aml *zero = aml_int(0);
+ Aml *one = aml_int(1);
+ Aml *sb_scope = aml_scope("_SB");
+ MachineClass *mc = MACHINE_GET_CLASS(machine);
+ CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(machine);
+ char *cphp_res_path = g_strdup_printf("%s." CPUHP_RES_DEVICE, res_root);
+ Object *obj = object_resolve_path_type("", TYPE_ACPI_DEVICE_IF, NULL);
+ AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_GET_CLASS(obj);
+ AcpiDeviceIf *adev = ACPI_DEVICE_IF(obj);
+
+ cpu_ctrl_dev = aml_device("%s", cphp_res_path);
+ {
+ Aml *crs;
+
+ aml_append(cpu_ctrl_dev,
+ aml_name_decl("_HID", aml_eisaid("PNP0A06")));
+ aml_append(cpu_ctrl_dev,
+ aml_name_decl("_UID", aml_string("CPU Hotplug resources")));
+ aml_append(cpu_ctrl_dev, aml_mutex(CPU_LOCK, 0));
+
+ crs = aml_resource_template();
+ aml_append(crs, aml_io(AML_DECODE16, io_base, io_base, 1,
+ ACPI_CPU_HOTPLUG_REG_LEN));
+ aml_append(cpu_ctrl_dev, aml_name_decl("_CRS", crs));
+
+ /* declare CPU hotplug MMIO region with related access fields */
+ aml_append(cpu_ctrl_dev,
+ aml_operation_region("PRST", AML_SYSTEM_IO, aml_int(io_base),
+ ACPI_CPU_HOTPLUG_REG_LEN));
+
+ field = aml_field("PRST", AML_BYTE_ACC, AML_NOLOCK,
+ AML_WRITE_AS_ZEROS);
+ aml_append(field, aml_reserved_field(ACPI_CPU_FLAGS_OFFSET_RW * 8));
+ /* 1 if enabled, read only */
+ aml_append(field, aml_named_field(CPU_ENABLED, 1));
+ /* (read) 1 if has a insert event. (write) 1 to clear event */
+ aml_append(field, aml_named_field(CPU_INSERT_EVENT, 1));
+ /* (read) 1 if has a remove event. (write) 1 to clear event */
+ aml_append(field, aml_named_field(CPU_REMOVE_EVENT, 1));
+ /* initiates device eject, write only */
+ aml_append(field, aml_named_field(CPU_EJECT_EVENT, 1));
+ aml_append(field, aml_reserved_field(4));
+ aml_append(field, aml_named_field(CPU_COMMAND, 8));
+ aml_append(cpu_ctrl_dev, field);
+
+ field = aml_field("PRST", AML_DWORD_ACC, AML_NOLOCK, AML_PRESERVE);
+ /* CPU selector, write only */
+ aml_append(field, aml_named_field(CPU_SELECTOR, 32));
+ /* flags + cmd + 2byte align */
+ aml_append(field, aml_reserved_field(4 * 8));
+ aml_append(field, aml_named_field(CPU_DATA, 32));
+ aml_append(cpu_ctrl_dev, field);
+
+ if (opts.has_legacy_cphp) {
+ method = aml_method("_INI", 0, AML_SERIALIZED);
+ /* switch off legacy CPU hotplug HW and use new one,
+ * on reboot system is in new mode and writing 0
+ * in CPU_SELECTOR selects BSP, which is NOP at
+ * the time _INI is called */
+ aml_append(method, aml_store(zero, aml_name(CPU_SELECTOR)));
+ aml_append(cpu_ctrl_dev, method);
+ }
+ }
+ aml_append(sb_scope, cpu_ctrl_dev);
+
+ cpus_dev = aml_device("\\_SB.CPUS");
+ {
+ int i;
+ Aml *ctrl_lock = aml_name("%s.%s", cphp_res_path, CPU_LOCK);
+ Aml *cpu_selector = aml_name("%s.%s", cphp_res_path, CPU_SELECTOR);
+ Aml *is_enabled = aml_name("%s.%s", cphp_res_path, CPU_ENABLED);
+ Aml *cpu_cmd = aml_name("%s.%s", cphp_res_path, CPU_COMMAND);
+ Aml *cpu_data = aml_name("%s.%s", cphp_res_path, CPU_DATA);
+ Aml *ins_evt = aml_name("%s.%s", cphp_res_path, CPU_INSERT_EVENT);
+ Aml *rm_evt = aml_name("%s.%s", cphp_res_path, CPU_REMOVE_EVENT);
+ Aml *ej_evt = aml_name("%s.%s", cphp_res_path, CPU_EJECT_EVENT);
+
+ aml_append(cpus_dev, aml_name_decl("_HID", aml_string("ACPI0010")));
+ aml_append(cpus_dev, aml_name_decl("_CID", aml_eisaid("PNP0A05")));
+
+ method = aml_method(CPU_NOTIFY_METHOD, 2, AML_NOTSERIALIZED);
+ for (i = 0; i < arch_ids->len; i++) {
+ Aml *cpu = aml_name(CPU_NAME_FMT, i);
+ Aml *uid = aml_arg(0);
+ Aml *event = aml_arg(1);
+
+ ifctx = aml_if(aml_equal(uid, aml_int(i)));
+ {
+ aml_append(ifctx, aml_notify(cpu, event));
+ }
+ aml_append(method, ifctx);
+ }
+ aml_append(cpus_dev, method);
+
+ method = aml_method(CPU_STS_METHOD, 1, AML_SERIALIZED);
+ {
+ Aml *idx = aml_arg(0);
+ Aml *sta = aml_local(0);
+
+ aml_append(method, aml_acquire(ctrl_lock, 0xFFFF));
+ aml_append(method, aml_store(idx, cpu_selector));
+ aml_append(method, aml_store(zero, sta));
+ ifctx = aml_if(aml_equal(is_enabled, one));
+ {
+ aml_append(ifctx, aml_store(aml_int(0xF), sta));
+ }
+ aml_append(method, ifctx);
+ aml_append(method, aml_release(ctrl_lock));
+ aml_append(method, aml_return(sta));
+ }
+ aml_append(cpus_dev, method);
+
+ method = aml_method(CPU_EJECT_METHOD, 1, AML_SERIALIZED);
+ {
+ Aml *idx = aml_arg(0);
+
+ aml_append(method, aml_acquire(ctrl_lock, 0xFFFF));
+ aml_append(method, aml_store(idx, cpu_selector));
+ aml_append(method, aml_store(one, ej_evt));
+ aml_append(method, aml_release(ctrl_lock));
+ }
+ aml_append(cpus_dev, method);
+
+ method = aml_method(CPU_SCAN_METHOD, 0, AML_SERIALIZED);
+ {
+ Aml *else_ctx;
+ Aml *while_ctx;
+ Aml *has_event = aml_local(0);
+ Aml *dev_chk = aml_int(1);
+ Aml *eject_req = aml_int(3);
+ Aml *next_cpu_cmd = aml_int(CPHP_GET_NEXT_CPU_WITH_EVENT_CMD);
+
+ aml_append(method, aml_acquire(ctrl_lock, 0xFFFF));
+ aml_append(method, aml_store(one, has_event));
+ while_ctx = aml_while(aml_equal(has_event, one));
+ {
+ /* clear loop exit condition, ins_evt/rm_evt checks
+ * will set it to 1 while next_cpu_cmd returns a CPU
+ * with events */
+ aml_append(while_ctx, aml_store(zero, has_event));
+ aml_append(while_ctx, aml_store(next_cpu_cmd, cpu_cmd));
+ ifctx = aml_if(aml_equal(ins_evt, one));
+ {
+ aml_append(ifctx,
+ aml_call2(CPU_NOTIFY_METHOD, cpu_data, dev_chk));
+ aml_append(ifctx, aml_store(one, ins_evt));
+ aml_append(ifctx, aml_store(one, has_event));
+ }
+ aml_append(while_ctx, ifctx);
+ else_ctx = aml_else();
+ ifctx = aml_if(aml_equal(rm_evt, one));
+ {
+ aml_append(ifctx,
+ aml_call2(CPU_NOTIFY_METHOD, cpu_data, eject_req));
+ aml_append(ifctx, aml_store(one, rm_evt));
+ aml_append(ifctx, aml_store(one, has_event));
+ }
+ aml_append(else_ctx, ifctx);
+ aml_append(while_ctx, else_ctx);
+ }
+ aml_append(method, while_ctx);
+ aml_append(method, aml_release(ctrl_lock));
+ }
+ aml_append(cpus_dev, method);
+
+ method = aml_method(CPU_OST_METHOD, 4, AML_SERIALIZED);
+ {
+ Aml *uid = aml_arg(0);
+ Aml *ev_cmd = aml_int(CPHP_OST_EVENT_CMD);
+ Aml *st_cmd = aml_int(CPHP_OST_STATUS_CMD);
+
+ aml_append(method, aml_acquire(ctrl_lock, 0xFFFF));
+ aml_append(method, aml_store(uid, cpu_selector));
+ aml_append(method, aml_store(ev_cmd, cpu_cmd));
+ aml_append(method, aml_store(aml_arg(1), cpu_data));
+ aml_append(method, aml_store(st_cmd, cpu_cmd));
+ aml_append(method, aml_store(aml_arg(2), cpu_data));
+ aml_append(method, aml_release(ctrl_lock));
+ }
+ aml_append(cpus_dev, method);
+
+ /* build Processor object for each processor */
+ for (i = 0; i < arch_ids->len; i++) {
+ Aml *dev;
+ Aml *uid = aml_int(i);
+ GArray *madt_buf = g_array_new(0, 1, 1);
+ int arch_id = arch_ids->cpus[i].arch_id;
+
+ if (opts.apci_1_compatible && arch_id < 255) {
+ dev = aml_processor(i, 0, 0, CPU_NAME_FMT, i);
+ } else {
+ dev = aml_device(CPU_NAME_FMT, i);
+ aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0007")));
+ aml_append(dev, aml_name_decl("_UID", uid));
+ }
+
+ method = aml_method("_STA", 0, AML_SERIALIZED);
+ aml_append(method, aml_return(aml_call1(CPU_STS_METHOD, uid)));
+ aml_append(dev, method);
+
+ /* build _MAT object */
+ assert(adevc && adevc->madt_cpu);
+ adevc->madt_cpu(adev, i, arch_ids, madt_buf);
+ switch (madt_buf->data[0]) {
+ case ACPI_APIC_PROCESSOR: {
+ AcpiMadtProcessorApic *apic = (void *)madt_buf->data;
+ apic->flags = cpu_to_le32(1);
+ break;
+ }
+ default:
+ assert(0);
+ }
+ aml_append(dev, aml_name_decl("_MAT",
+ aml_buffer(madt_buf->len, (uint8_t *)madt_buf->data)));
+ g_array_free(madt_buf, true);
+
+ method = aml_method("_EJ0", 1, AML_NOTSERIALIZED);
+ aml_append(method, aml_call1(CPU_EJECT_METHOD, uid));
+ aml_append(dev, method);
+
+ method = aml_method("_OST", 3, AML_SERIALIZED);
+ aml_append(method,
+ aml_call4(CPU_OST_METHOD, uid, aml_arg(0),
+ aml_arg(1), aml_arg(2))
+ );
+ aml_append(dev, method);
+ aml_append(cpus_dev, dev);
+ }
+ }
+ aml_append(sb_scope, cpus_dev);
+ aml_append(table, sb_scope);
+
+ method = aml_method(event_handler_method, 0, AML_NOTSERIALIZED);
+ aml_append(method, aml_call0("\\_SB.CPUS." CPU_SCAN_METHOD));
+ aml_append(table, method);
+
+ g_free(cphp_res_path);
+ g_free(arch_ids);
+}
diff --git a/hw/acpi/cpu_hotplug.c b/hw/acpi/cpu_hotplug.c
index fe75bd9ac9..e19d902063 100644
--- a/hw/acpi/cpu_hotplug.c
+++ b/hw/acpi/cpu_hotplug.c
@@ -34,7 +34,15 @@ static uint64_t cpu_status_read(void *opaque, hwaddr addr, unsigned int size)
static void cpu_status_write(void *opaque, hwaddr addr, uint64_t data,
unsigned int size)
{
- /* TODO: implement VCPU removal on guest signal that CPU can be removed */
+ /* firmware never used to write in CPU present bitmap so use
+ this fact as means to switch QEMU into modern CPU hotplug
+ mode by writing 0 at the beginning of legacy CPU bitmap
+ */
+ if (addr == 0 && data == 0) {
+ AcpiCpuHotplug *cpus = opaque;
+ object_property_set_bool(cpus->device, false, "cpu-hotplug-legacy",
+ &error_abort);
+ }
}
static const MemoryRegionOps AcpiCpuHotplug_ops = {
@@ -83,6 +91,17 @@ void legacy_acpi_cpu_hotplug_init(MemoryRegion *parent, Object *owner,
memory_region_init_io(&gpe_cpu->io, owner, &AcpiCpuHotplug_ops,
gpe_cpu, "acpi-cpu-hotplug", ACPI_GPE_PROC_LEN);
memory_region_add_subregion(parent, base, &gpe_cpu->io);
+ gpe_cpu->device = owner;
+}
+
+void acpi_switch_to_modern_cphp(AcpiCpuHotplug *gpe_cpu,
+ CPUHotplugState *cpuhp_state,
+ uint16_t io_port)
+{
+ MemoryRegion *parent = pci_address_space_io(PCI_DEVICE(gpe_cpu->device));
+
+ memory_region_del_subregion(parent, &gpe_cpu->io);
+ cpu_hotplug_hw_init(parent, gpe_cpu->device, cpuhp_state, io_port);
}
void build_legacy_cpu_hotplug_aml(Aml *ctx, MachineState *machine,
diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c
index 853c9c4eb7..e5a3c18e52 100644
--- a/hw/acpi/ich9.c
+++ b/hw/acpi/ich9.c
@@ -189,6 +189,33 @@ static const VMStateDescription vmstate_tco_io_state = {
}
};
+static bool vmstate_test_use_cpuhp(void *opaque)
+{
+ ICH9LPCPMRegs *s = opaque;
+ return !s->cpu_hotplug_legacy;
+}
+
+static int vmstate_cpuhp_pre_load(void *opaque)
+{
+ ICH9LPCPMRegs *s = opaque;
+ Object *obj = OBJECT(s->gpe_cpu.device);
+ object_property_set_bool(obj, false, "cpu-hotplug-legacy", &error_abort);
+ return 0;
+}
+
+static const VMStateDescription vmstate_cpuhp_state = {
+ .name = "ich9_pm/cpuhp",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .needed = vmstate_test_use_cpuhp,
+ .pre_load = vmstate_cpuhp_pre_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_CPU_HOTPLUG(cpuhp_state, ICH9LPCPMRegs),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
const VMStateDescription vmstate_ich9_pm = {
.name = "ich9_pm",
.version_id = 1,
@@ -209,6 +236,7 @@ const VMStateDescription vmstate_ich9_pm = {
.subsections = (const VMStateDescription*[]) {
&vmstate_memhp_state,
&vmstate_tco_io_state,
+ &vmstate_cpuhp_state,
NULL
}
};
@@ -306,6 +334,26 @@ static void ich9_pm_set_memory_hotplug_support(Object *obj, bool value,
s->pm.acpi_memory_hotplug.is_enabled = value;
}
+static bool ich9_pm_get_cpu_hotplug_legacy(Object *obj, Error **errp)
+{
+ ICH9LPCState *s = ICH9_LPC_DEVICE(obj);
+
+ return s->pm.cpu_hotplug_legacy;
+}
+
+static void ich9_pm_set_cpu_hotplug_legacy(Object *obj, bool value,
+ Error **errp)
+{
+ ICH9LPCState *s = ICH9_LPC_DEVICE(obj);
+
+ assert(!value);
+ if (s->pm.cpu_hotplug_legacy && value == false) {
+ acpi_switch_to_modern_cphp(&s->pm.gpe_cpu, &s->pm.cpuhp_state,
+ ICH9_CPU_HOTPLUG_IO_BASE);
+ }
+ s->pm.cpu_hotplug_legacy = value;
+}
+
static void ich9_pm_get_disable_s3(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
@@ -397,6 +445,7 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp)
{
static const uint32_t gpe0_len = ICH9_PMIO_GPE0_LEN;
pm->acpi_memory_hotplug.is_enabled = true;
+ pm->cpu_hotplug_legacy = true;
pm->disable_s3 = 0;
pm->disable_s4 = 0;
pm->s4_val = 2;
@@ -412,6 +461,10 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp)
ich9_pm_get_memory_hotplug_support,
ich9_pm_set_memory_hotplug_support,
NULL);
+ object_property_add_bool(obj, "cpu-hotplug-legacy",
+ ich9_pm_get_cpu_hotplug_legacy,
+ ich9_pm_set_cpu_hotplug_legacy,
+ NULL);
object_property_add(obj, ACPI_PM_PROP_S3_DISABLED, "uint8",
ich9_pm_get_disable_s3,
ich9_pm_set_disable_s3,
@@ -440,7 +493,11 @@ void ich9_pm_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
acpi_memory_plug_cb(hotplug_dev, &lpc->pm.acpi_memory_hotplug,
dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
- legacy_acpi_cpu_plug_cb(hotplug_dev, &lpc->pm.gpe_cpu, dev, errp);
+ if (lpc->pm.cpu_hotplug_legacy) {
+ legacy_acpi_cpu_plug_cb(hotplug_dev, &lpc->pm.gpe_cpu, dev, errp);
+ } else {
+ acpi_cpu_plug_cb(hotplug_dev, &lpc->pm.cpuhp_state, dev, errp);
+ }
} else {
error_setg(errp, "acpi: device plug request for not supported device"
" type: %s", object_get_typename(OBJECT(dev)));
@@ -457,6 +514,10 @@ void ich9_pm_device_unplug_request_cb(HotplugHandler *hotplug_dev,
acpi_memory_unplug_request_cb(hotplug_dev,
&lpc->pm.acpi_memory_hotplug, dev,
errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU) &&
+ !lpc->pm.cpu_hotplug_legacy) {
+ acpi_cpu_unplug_request_cb(hotplug_dev, &lpc->pm.cpuhp_state,
+ dev, errp);
} else {
error_setg(errp, "acpi: device unplug request for not supported device"
" type: %s", object_get_typename(OBJECT(dev)));
@@ -471,6 +532,9 @@ void ich9_pm_device_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
if (lpc->pm.acpi_memory_hotplug.is_enabled &&
object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
acpi_memory_unplug_cb(&lpc->pm.acpi_memory_hotplug, dev, errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU) &&
+ !lpc->pm.cpu_hotplug_legacy) {
+ acpi_cpu_unplug_cb(&lpc->pm.cpuhp_state, dev, errp);
} else {
error_setg(errp, "acpi: device unplug for not supported device"
" type: %s", object_get_typename(OBJECT(dev)));
@@ -482,4 +546,7 @@ void ich9_pm_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list)
ICH9LPCState *s = ICH9_LPC_DEVICE(adev);
acpi_memory_ospm_status(&s->pm.acpi_memory_hotplug, list);
+ if (!s->pm.cpu_hotplug_legacy) {
+ acpi_cpu_ospm_status(&s->pm.cpuhp_state, list);
+ }
}
diff --git a/hw/acpi/ipmi.c b/hw/acpi/ipmi.c
new file mode 100644
index 0000000000..7e74ce4460
--- /dev/null
+++ b/hw/acpi/ipmi.c
@@ -0,0 +1,105 @@
+/*
+ * IPMI ACPI firmware handling
+ *
+ * Copyright (c) 2015,2016 Corey Minyard, MontaVista Software, LLC
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/ipmi/ipmi.h"
+#include "hw/acpi/aml-build.h"
+#include "hw/acpi/acpi.h"
+#include "hw/acpi/ipmi.h"
+
+static Aml *aml_ipmi_crs(IPMIFwInfo *info)
+{
+ Aml *crs = aml_resource_template();
+
+ /*
+ * The base address is fixed and cannot change. That may be different
+ * if someone does PCI, but we aren't there yet.
+ */
+ switch (info->memspace) {
+ case IPMI_MEMSPACE_IO:
+ aml_append(crs, aml_io(AML_DECODE16, info->base_address,
+ info->base_address + info->register_length - 1,
+ info->register_spacing, info->register_length));
+ break;
+ case IPMI_MEMSPACE_MEM32:
+ aml_append(crs,
+ aml_dword_memory(AML_POS_DECODE,
+ AML_MIN_FIXED, AML_MAX_FIXED,
+ AML_NON_CACHEABLE, AML_READ_WRITE,
+ 0xffffffff,
+ info->base_address,
+ info->base_address + info->register_length - 1,
+ info->register_spacing, info->register_length));
+ break;
+ case IPMI_MEMSPACE_MEM64:
+ aml_append(crs,
+ aml_qword_memory(AML_POS_DECODE,
+ AML_MIN_FIXED, AML_MAX_FIXED,
+ AML_NON_CACHEABLE, AML_READ_WRITE,
+ 0xffffffffffffffffULL,
+ info->base_address,
+ info->base_address + info->register_length - 1,
+ info->register_spacing, info->register_length));
+ break;
+ case IPMI_MEMSPACE_SMBUS:
+ aml_append(crs, aml_return(aml_int(info->base_address)));
+ break;
+ default:
+ abort();
+ }
+
+ if (info->interrupt_number) {
+ aml_append(crs, aml_irq_no_flags(info->interrupt_number));
+ }
+
+ return crs;
+}
+
+static Aml *aml_ipmi_device(IPMIFwInfo *info)
+{
+ Aml *dev;
+ uint16_t version = ((info->ipmi_spec_major_revision << 8)
+ | (info->ipmi_spec_minor_revision << 4));
+
+ assert(info->ipmi_spec_minor_revision <= 15);
+
+ dev = aml_device("MI%d", info->uuid);
+ aml_append(dev, aml_name_decl("_HID", aml_eisaid("IPI0001")));
+ aml_append(dev, aml_name_decl("_STR", aml_string("ipmi_%s",
+ info->interface_name)));
+ aml_append(dev, aml_name_decl("_UID", aml_int(info->uuid)));
+ aml_append(dev, aml_name_decl("_CRS", aml_ipmi_crs(info)));
+ aml_append(dev, aml_name_decl("_IFT", aml_int(info->interface_type)));
+ aml_append(dev, aml_name_decl("_SRV", aml_int(version)));
+
+ return dev;
+}
+
+void build_acpi_ipmi_devices(Aml *scope, BusState *bus)
+{
+
+ BusChild *kid;
+
+ QTAILQ_FOREACH(kid, &bus->children, sibling) {
+ IPMIInterface *ii;
+ IPMIInterfaceClass *iic;
+ IPMIFwInfo info;
+ Object *obj = object_dynamic_cast(OBJECT(kid->child),
+ TYPE_IPMI_INTERFACE);
+
+ if (!obj) {
+ continue;
+ }
+
+ ii = IPMI_INTERFACE(obj);
+ iic = IPMI_INTERFACE_GET_CLASS(obj);
+ iic->get_fwinfo(ii, &info);
+ aml_append(scope, aml_ipmi_device(&info));
+ }
+}
diff --git a/hw/acpi/nvdimm.c b/hw/acpi/nvdimm.c
index b4c22627df..e486128aa1 100644
--- a/hw/acpi/nvdimm.c
+++ b/hw/acpi/nvdimm.c
@@ -216,6 +216,26 @@ static uint32_t nvdimm_slot_to_dcr_index(int slot)
return nvdimm_slot_to_spa_index(slot) + 1;
}
+static NVDIMMDevice *nvdimm_get_device_by_handle(uint32_t handle)
+{
+ NVDIMMDevice *nvdimm = NULL;
+ GSList *list, *device_list = nvdimm_get_plugged_device_list();
+
+ for (list = device_list; list; list = list->next) {
+ NVDIMMDevice *nvd = list->data;
+ int slot = object_property_get_int(OBJECT(nvd), PC_DIMM_SLOT_PROP,
+ NULL);
+
+ if (nvdimm_slot_to_handle(slot) == handle) {
+ nvdimm = nvd;
+ break;
+ }
+ }
+
+ g_slist_free(device_list);
+ return nvdimm;
+}
+
/* ACPI 6.0: 5.2.25.1 System Physical Address Range Structure */
static void
nvdimm_build_structure_spa(GArray *structures, DeviceState *dev)
@@ -406,6 +426,282 @@ struct NvdimmDsmFuncNoPayloadOut {
} QEMU_PACKED;
typedef struct NvdimmDsmFuncNoPayloadOut NvdimmDsmFuncNoPayloadOut;
+struct NvdimmFuncGetLabelSizeOut {
+ /* the size of buffer filled by QEMU. */
+ uint32_t len;
+ uint32_t func_ret_status; /* return status code. */
+ uint32_t label_size; /* the size of label data area. */
+ /*
+ * Maximum size of the namespace label data length supported by
+ * the platform in Get/Set Namespace Label Data functions.
+ */
+ uint32_t max_xfer;
+} QEMU_PACKED;
+typedef struct NvdimmFuncGetLabelSizeOut NvdimmFuncGetLabelSizeOut;
+QEMU_BUILD_BUG_ON(sizeof(NvdimmFuncGetLabelSizeOut) > 4096);
+
+struct NvdimmFuncGetLabelDataIn {
+ uint32_t offset; /* the offset in the namespace label data area. */
+ uint32_t length; /* the size of data is to be read via the function. */
+} QEMU_PACKED;
+typedef struct NvdimmFuncGetLabelDataIn NvdimmFuncGetLabelDataIn;
+QEMU_BUILD_BUG_ON(sizeof(NvdimmFuncGetLabelDataIn) +
+ offsetof(NvdimmDsmIn, arg3) > 4096);
+
+struct NvdimmFuncGetLabelDataOut {
+ /* the size of buffer filled by QEMU. */
+ uint32_t len;
+ uint32_t func_ret_status; /* return status code. */
+ uint8_t out_buf[0]; /* the data got via Get Namesapce Label function. */
+} QEMU_PACKED;
+typedef struct NvdimmFuncGetLabelDataOut NvdimmFuncGetLabelDataOut;
+QEMU_BUILD_BUG_ON(sizeof(NvdimmFuncGetLabelDataOut) > 4096);
+
+struct NvdimmFuncSetLabelDataIn {
+ uint32_t offset; /* the offset in the namespace label data area. */
+ uint32_t length; /* the size of data is to be written via the function. */
+ uint8_t in_buf[0]; /* the data written to label data area. */
+} QEMU_PACKED;
+typedef struct NvdimmFuncSetLabelDataIn NvdimmFuncSetLabelDataIn;
+QEMU_BUILD_BUG_ON(sizeof(NvdimmFuncSetLabelDataIn) +
+ offsetof(NvdimmDsmIn, arg3) > 4096);
+
+static void
+nvdimm_dsm_function0(uint32_t supported_func, hwaddr dsm_mem_addr)
+{
+ NvdimmDsmFunc0Out func0 = {
+ .len = cpu_to_le32(sizeof(func0)),
+ .supported_func = cpu_to_le32(supported_func),
+ };
+ cpu_physical_memory_write(dsm_mem_addr, &func0, sizeof(func0));
+}
+
+static void
+nvdimm_dsm_no_payload(uint32_t func_ret_status, hwaddr dsm_mem_addr)
+{
+ NvdimmDsmFuncNoPayloadOut out = {
+ .len = cpu_to_le32(sizeof(out)),
+ .func_ret_status = cpu_to_le32(func_ret_status),
+ };
+ cpu_physical_memory_write(dsm_mem_addr, &out, sizeof(out));
+}
+
+static void nvdimm_dsm_root(NvdimmDsmIn *in, hwaddr dsm_mem_addr)
+{
+ /*
+ * function 0 is called to inquire which functions are supported by
+ * OSPM
+ */
+ if (!in->function) {
+ nvdimm_dsm_function0(0 /* No function supported other than
+ function 0 */, dsm_mem_addr);
+ return;
+ }
+
+ /* No function except function 0 is supported yet. */
+ nvdimm_dsm_no_payload(1 /* Not Supported */, dsm_mem_addr);
+}
+
+/*
+ * the max transfer size is the max size transferred by both a
+ * 'Get Namespace Label Data' function and a 'Set Namespace Label Data'
+ * function.
+ */
+static uint32_t nvdimm_get_max_xfer_label_size(void)
+{
+ uint32_t max_get_size, max_set_size, dsm_memory_size = 4096;
+
+ /*
+ * the max data ACPI can read one time which is transferred by
+ * the response of 'Get Namespace Label Data' function.
+ */
+ max_get_size = dsm_memory_size - sizeof(NvdimmFuncGetLabelDataOut);
+
+ /*
+ * the max data ACPI can write one time which is transferred by
+ * 'Set Namespace Label Data' function.
+ */
+ max_set_size = dsm_memory_size - offsetof(NvdimmDsmIn, arg3) -
+ sizeof(NvdimmFuncSetLabelDataIn);
+
+ return MIN(max_get_size, max_set_size);
+}
+
+/*
+ * DSM Spec Rev1 4.4 Get Namespace Label Size (Function Index 4).
+ *
+ * It gets the size of Namespace Label data area and the max data size
+ * that Get/Set Namespace Label Data functions can transfer.
+ */
+static void nvdimm_dsm_label_size(NVDIMMDevice *nvdimm, hwaddr dsm_mem_addr)
+{
+ NvdimmFuncGetLabelSizeOut label_size_out = {
+ .len = cpu_to_le32(sizeof(label_size_out)),
+ };
+ uint32_t label_size, mxfer;
+
+ label_size = nvdimm->label_size;
+ mxfer = nvdimm_get_max_xfer_label_size();
+
+ nvdimm_debug("label_size %#x, max_xfer %#x.\n", label_size, mxfer);
+
+ label_size_out.func_ret_status = cpu_to_le32(0 /* Success */);
+ label_size_out.label_size = cpu_to_le32(label_size);
+ label_size_out.max_xfer = cpu_to_le32(mxfer);
+
+ cpu_physical_memory_write(dsm_mem_addr, &label_size_out,
+ sizeof(label_size_out));
+}
+
+static uint32_t nvdimm_rw_label_data_check(NVDIMMDevice *nvdimm,
+ uint32_t offset, uint32_t length)
+{
+ uint32_t ret = 3 /* Invalid Input Parameters */;
+
+ if (offset + length < offset) {
+ nvdimm_debug("offset %#x + length %#x is overflow.\n", offset,
+ length);
+ return ret;
+ }
+
+ if (nvdimm->label_size < offset + length) {
+ nvdimm_debug("position %#x is beyond label data (len = %" PRIx64 ").\n",
+ offset + length, nvdimm->label_size);
+ return ret;
+ }
+
+ if (length > nvdimm_get_max_xfer_label_size()) {
+ nvdimm_debug("length (%#x) is larger than max_xfer (%#x).\n",
+ length, nvdimm_get_max_xfer_label_size());
+ return ret;
+ }
+
+ return 0 /* Success */;
+}
+
+/*
+ * DSM Spec Rev1 4.5 Get Namespace Label Data (Function Index 5).
+ */
+static void nvdimm_dsm_get_label_data(NVDIMMDevice *nvdimm, NvdimmDsmIn *in,
+ hwaddr dsm_mem_addr)
+{
+ NVDIMMClass *nvc = NVDIMM_GET_CLASS(nvdimm);
+ NvdimmFuncGetLabelDataIn *get_label_data;
+ NvdimmFuncGetLabelDataOut *get_label_data_out;
+ uint32_t status;
+ int size;
+
+ get_label_data = (NvdimmFuncGetLabelDataIn *)in->arg3;
+ le32_to_cpus(&get_label_data->offset);
+ le32_to_cpus(&get_label_data->length);
+
+ nvdimm_debug("Read Label Data: offset %#x length %#x.\n",
+ get_label_data->offset, get_label_data->length);
+
+ status = nvdimm_rw_label_data_check(nvdimm, get_label_data->offset,
+ get_label_data->length);
+ if (status != 0 /* Success */) {
+ nvdimm_dsm_no_payload(status, dsm_mem_addr);
+ return;
+ }
+
+ size = sizeof(*get_label_data_out) + get_label_data->length;
+ assert(size <= 4096);
+ get_label_data_out = g_malloc(size);
+
+ get_label_data_out->len = cpu_to_le32(size);
+ get_label_data_out->func_ret_status = cpu_to_le32(0 /* Success */);
+ nvc->read_label_data(nvdimm, get_label_data_out->out_buf,
+ get_label_data->length, get_label_data->offset);
+
+ cpu_physical_memory_write(dsm_mem_addr, get_label_data_out, size);
+ g_free(get_label_data_out);
+}
+
+/*
+ * DSM Spec Rev1 4.6 Set Namespace Label Data (Function Index 6).
+ */
+static void nvdimm_dsm_set_label_data(NVDIMMDevice *nvdimm, NvdimmDsmIn *in,
+ hwaddr dsm_mem_addr)
+{
+ NVDIMMClass *nvc = NVDIMM_GET_CLASS(nvdimm);
+ NvdimmFuncSetLabelDataIn *set_label_data;
+ uint32_t status;
+
+ set_label_data = (NvdimmFuncSetLabelDataIn *)in->arg3;
+
+ le32_to_cpus(&set_label_data->offset);
+ le32_to_cpus(&set_label_data->length);
+
+ nvdimm_debug("Write Label Data: offset %#x length %#x.\n",
+ set_label_data->offset, set_label_data->length);
+
+ status = nvdimm_rw_label_data_check(nvdimm, set_label_data->offset,
+ set_label_data->length);
+ if (status != 0 /* Success */) {
+ nvdimm_dsm_no_payload(status, dsm_mem_addr);
+ return;
+ }
+
+ assert(sizeof(*in) + sizeof(*set_label_data) + set_label_data->length <=
+ 4096);
+
+ nvc->write_label_data(nvdimm, set_label_data->in_buf,
+ set_label_data->length, set_label_data->offset);
+ nvdimm_dsm_no_payload(0 /* Success */, dsm_mem_addr);
+}
+
+static void nvdimm_dsm_device(NvdimmDsmIn *in, hwaddr dsm_mem_addr)
+{
+ NVDIMMDevice *nvdimm = nvdimm_get_device_by_handle(in->handle);
+
+ /* See the comments in nvdimm_dsm_root(). */
+ if (!in->function) {
+ uint32_t supported_func = 0;
+
+ if (nvdimm && nvdimm->label_size) {
+ supported_func |= 0x1 /* Bit 0 indicates whether there is
+ support for any functions other
+ than function 0. */ |
+ 1 << 4 /* Get Namespace Label Size */ |
+ 1 << 5 /* Get Namespace Label Data */ |
+ 1 << 6 /* Set Namespace Label Data */;
+ }
+ nvdimm_dsm_function0(supported_func, dsm_mem_addr);
+ return;
+ }
+
+ if (!nvdimm) {
+ nvdimm_dsm_no_payload(2 /* Non-Existing Memory Device */,
+ dsm_mem_addr);
+ return;
+ }
+
+ /* Encode DSM function according to DSM Spec Rev1. */
+ switch (in->function) {
+ case 4 /* Get Namespace Label Size */:
+ if (nvdimm->label_size) {
+ nvdimm_dsm_label_size(nvdimm, dsm_mem_addr);
+ return;
+ }
+ break;
+ case 5 /* Get Namespace Label Data */:
+ if (nvdimm->label_size) {
+ nvdimm_dsm_get_label_data(nvdimm, in, dsm_mem_addr);
+ return;
+ }
+ break;
+ case 0x6 /* Set Namespace Label Data */:
+ if (nvdimm->label_size) {
+ nvdimm_dsm_set_label_data(nvdimm, in, dsm_mem_addr);
+ return;
+ }
+ break;
+ }
+
+ nvdimm_dsm_no_payload(1 /* Not Supported */, dsm_mem_addr);
+}
+
static uint64_t
nvdimm_dsm_read(void *opaque, hwaddr addr, unsigned size)
{
@@ -436,26 +732,22 @@ nvdimm_dsm_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
nvdimm_debug("Revision %#x Handler %#x Function %#x.\n", in->revision,
in->handle, in->function);
- /*
- * function 0 is called to inquire which functions are supported by
- * OSPM
- */
- if (in->function == 0) {
- NvdimmDsmFunc0Out func0 = {
- .len = cpu_to_le32(sizeof(func0)),
- /* No function supported other than function 0 */
- .supported_func = cpu_to_le32(0),
- };
- cpu_physical_memory_write(dsm_mem_addr, &func0, sizeof func0);
- } else {
- /* No function except function 0 is supported yet. */
- NvdimmDsmFuncNoPayloadOut out = {
- .len = cpu_to_le32(sizeof(out)),
- .func_ret_status = cpu_to_le32(1) /* Not Supported */,
- };
- cpu_physical_memory_write(dsm_mem_addr, &out, sizeof(out));
+ if (in->revision != 0x1 /* Currently we only support DSM Spec Rev1. */) {
+ nvdimm_debug("Revision %#x is not supported, expect %#x.\n",
+ in->revision, 0x1);
+ nvdimm_dsm_no_payload(1 /* Not Supported */, dsm_mem_addr);
+ goto exit;
+ }
+
+ /* Handle 0 is reserved for NVDIMM Root Device. */
+ if (!in->handle) {
+ nvdimm_dsm_root(in, dsm_mem_addr);
+ goto exit;
}
+ nvdimm_dsm_device(in, dsm_mem_addr);
+
+exit:
g_free(in);
}
@@ -487,18 +779,39 @@ void nvdimm_init_acpi_state(AcpiNVDIMMState *state, MemoryRegion *io,
static void nvdimm_build_common_dsm(Aml *dev)
{
- Aml *method, *ifctx, *function, *dsm_mem, *unpatched, *result_size;
+ Aml *method, *ifctx, *function, *handle, *uuid, *dsm_mem, *result_size;
+ Aml *elsectx, *unsupport, *unpatched, *expected_uuid, *uuid_invalid;
+ Aml *pckg, *pckg_index, *pckg_buf;
uint8_t byte_list[1];
- method = aml_method(NVDIMM_COMMON_DSM, 4, AML_SERIALIZED);
+ method = aml_method(NVDIMM_COMMON_DSM, 5, AML_SERIALIZED);
+ uuid = aml_arg(0);
function = aml_arg(2);
+ handle = aml_arg(4);
dsm_mem = aml_name(NVDIMM_ACPI_MEM_ADDR);
/*
* do not support any method if DSM memory address has not been
* patched.
*/
- unpatched = aml_if(aml_equal(dsm_mem, aml_int(0x0)));
+ unpatched = aml_equal(dsm_mem, aml_int(0x0));
+
+ expected_uuid = aml_local(0);
+
+ ifctx = aml_if(aml_equal(handle, aml_int(0x0)));
+ aml_append(ifctx, aml_store(
+ aml_touuid("2F10E7A4-9E91-11E4-89D3-123B93F75CBA")
+ /* UUID for NVDIMM Root Device */, expected_uuid));
+ aml_append(method, ifctx);
+ elsectx = aml_else();
+ aml_append(elsectx, aml_store(
+ aml_touuid("4309AC30-0D11-11E4-9191-0800200C9A66")
+ /* UUID for NVDIMM Devices */, expected_uuid));
+ aml_append(method, elsectx);
+
+ uuid_invalid = aml_lnot(aml_equal(uuid, expected_uuid));
+
+ unsupport = aml_if(aml_or(unpatched, uuid_invalid, NULL));
/*
* function 0 is called to inquire what functions are supported by
@@ -507,24 +820,42 @@ static void nvdimm_build_common_dsm(Aml *dev)
ifctx = aml_if(aml_equal(function, aml_int(0)));
byte_list[0] = 0 /* No function Supported */;
aml_append(ifctx, aml_return(aml_buffer(1, byte_list)));
- aml_append(unpatched, ifctx);
+ aml_append(unsupport, ifctx);
/* No function is supported yet. */
byte_list[0] = 1 /* Not Supported */;
- aml_append(unpatched, aml_return(aml_buffer(1, byte_list)));
- aml_append(method, unpatched);
+ aml_append(unsupport, aml_return(aml_buffer(1, byte_list)));
+ aml_append(method, unsupport);
/*
* The HDLE indicates the DSM function is issued from which device,
- * it is not used at this time as no function is supported yet.
- * Currently we make it always be 0 for all the devices and will set
- * the appropriate value once real function is implemented.
+ * it reserves 0 for root device and is the handle for NVDIMM devices.
+ * See the comments in nvdimm_slot_to_handle().
*/
- aml_append(method, aml_store(aml_int(0x0), aml_name("HDLE")));
+ aml_append(method, aml_store(handle, aml_name("HDLE")));
aml_append(method, aml_store(aml_arg(1), aml_name("REVS")));
aml_append(method, aml_store(aml_arg(2), aml_name("FUNC")));
/*
+ * The fourth parameter (Arg3) of _DSM is a package which contains
+ * a buffer, the layout of the buffer is specified by UUID (Arg0),
+ * Revision ID (Arg1) and Function Index (Arg2) which are documented
+ * in the DSM Spec.
+ */
+ pckg = aml_arg(3);
+ ifctx = aml_if(aml_and(aml_equal(aml_object_type(pckg),
+ aml_int(4 /* Package */)) /* It is a Package? */,
+ aml_equal(aml_sizeof(pckg), aml_int(1)) /* 1 element? */,
+ NULL));
+
+ pckg_index = aml_local(2);
+ pckg_buf = aml_local(3);
+ aml_append(ifctx, aml_store(aml_index(pckg, aml_int(0)), pckg_index));
+ aml_append(ifctx, aml_store(aml_derefof(pckg_index), pckg_buf));
+ aml_append(ifctx, aml_store(pckg_buf, aml_name("ARG3")));
+ aml_append(method, ifctx);
+
+ /*
* tell QEMU about the real address of DSM memory, then QEMU
* gets the control and fills the result in DSM memory.
*/
@@ -542,13 +873,14 @@ static void nvdimm_build_common_dsm(Aml *dev)
aml_append(dev, method);
}
-static void nvdimm_build_device_dsm(Aml *dev)
+static void nvdimm_build_device_dsm(Aml *dev, uint32_t handle)
{
Aml *method;
method = aml_method("_DSM", 4, AML_NOTSERIALIZED);
- aml_append(method, aml_return(aml_call4(NVDIMM_COMMON_DSM, aml_arg(0),
- aml_arg(1), aml_arg(2), aml_arg(3))));
+ aml_append(method, aml_return(aml_call5(NVDIMM_COMMON_DSM, aml_arg(0),
+ aml_arg(1), aml_arg(2), aml_arg(3),
+ aml_int(handle))));
aml_append(dev, method);
}
@@ -573,7 +905,7 @@ static void nvdimm_build_nvdimm_devices(GSList *device_list, Aml *root_dev)
*/
aml_append(nvdimm_dev, aml_name_decl("_ADR", aml_int(handle)));
- nvdimm_build_device_dsm(nvdimm_dev);
+ nvdimm_build_device_dsm(nvdimm_dev, handle);
aml_append(root_dev, nvdimm_dev);
}
}
@@ -665,7 +997,9 @@ static void nvdimm_build_ssdt(GSList *device_list, GArray *table_offsets,
aml_append(dev, field);
nvdimm_build_common_dsm(dev);
- nvdimm_build_device_dsm(dev);
+
+ /* 0 is reserved for root device. */
+ nvdimm_build_device_dsm(dev, 0);
nvdimm_build_nvdimm_devices(device_list, dev);
diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c
index c48cb1b91a..2adc246b00 100644
--- a/hw/acpi/piix4.c
+++ b/hw/acpi/piix4.c
@@ -34,6 +34,7 @@
#include "hw/acpi/piix4.h"
#include "hw/acpi/pcihp.h"
#include "hw/acpi/cpu_hotplug.h"
+#include "hw/acpi/cpu.h"
#include "hw/hotplug.h"
#include "hw/mem/pc-dimm.h"
#include "hw/acpi/memory_hotplug.h"
@@ -86,7 +87,9 @@ typedef struct PIIX4PMState {
uint8_t disable_s4;
uint8_t s4_val;
+ bool cpu_hotplug_legacy;
AcpiCpuHotplug gpe_cpu;
+ CPUHotplugState cpuhp_state;
MemHotplugState acpi_memory_hotplug;
} PIIX4PMState;
@@ -273,6 +276,32 @@ static const VMStateDescription vmstate_memhp_state = {
}
};
+static bool vmstate_test_use_cpuhp(void *opaque)
+{
+ PIIX4PMState *s = opaque;
+ return !s->cpu_hotplug_legacy;
+}
+
+static int vmstate_cpuhp_pre_load(void *opaque)
+{
+ Object *obj = OBJECT(opaque);
+ object_property_set_bool(obj, false, "cpu-hotplug-legacy", &error_abort);
+ return 0;
+}
+
+static const VMStateDescription vmstate_cpuhp_state = {
+ .name = "piix4_pm/cpuhp",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .needed = vmstate_test_use_cpuhp,
+ .pre_load = vmstate_cpuhp_pre_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_CPU_HOTPLUG(cpuhp_state, PIIX4PMState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
/* qemu-kvm 1.2 uses version 3 but advertised as 2
* To support incoming qemu-kvm 1.2 migration, change version_id
* and minimum_version_id to 2 below (which breaks migration from
@@ -307,6 +336,7 @@ static const VMStateDescription vmstate_acpi = {
},
.subsections = (const VMStateDescription*[]) {
&vmstate_memhp_state,
+ &vmstate_cpuhp_state,
NULL
}
};
@@ -352,7 +382,11 @@ static void piix4_device_plug_cb(HotplugHandler *hotplug_dev,
} else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
acpi_pcihp_device_plug_cb(hotplug_dev, &s->acpi_pci_hotplug, dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
- legacy_acpi_cpu_plug_cb(hotplug_dev, &s->gpe_cpu, dev, errp);
+ if (s->cpu_hotplug_legacy) {
+ legacy_acpi_cpu_plug_cb(hotplug_dev, &s->gpe_cpu, dev, errp);
+ } else {
+ acpi_cpu_plug_cb(hotplug_dev, &s->cpuhp_state, dev, errp);
+ }
} else {
error_setg(errp, "acpi: device plug request for not supported device"
" type: %s", object_get_typename(OBJECT(dev)));
@@ -371,6 +405,9 @@ static void piix4_device_unplug_request_cb(HotplugHandler *hotplug_dev,
} else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
acpi_pcihp_device_unplug_cb(hotplug_dev, &s->acpi_pci_hotplug, dev,
errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU) &&
+ !s->cpu_hotplug_legacy) {
+ acpi_cpu_unplug_request_cb(hotplug_dev, &s->cpuhp_state, dev, errp);
} else {
error_setg(errp, "acpi: device unplug request for not supported device"
" type: %s", object_get_typename(OBJECT(dev)));
@@ -385,6 +422,9 @@ static void piix4_device_unplug_cb(HotplugHandler *hotplug_dev,
if (s->acpi_memory_hotplug.is_enabled &&
object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
acpi_memory_unplug_cb(&s->acpi_memory_hotplug, dev, errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU) &&
+ !s->cpu_hotplug_legacy) {
+ acpi_cpu_unplug_cb(&s->cpuhp_state, dev, errp);
} else {
error_setg(errp, "acpi: device unplug for not supported device"
" type: %s", object_get_typename(OBJECT(dev)));
@@ -560,6 +600,26 @@ static const MemoryRegionOps piix4_gpe_ops = {
.endianness = DEVICE_LITTLE_ENDIAN,
};
+
+static bool piix4_get_cpu_hotplug_legacy(Object *obj, Error **errp)
+{
+ PIIX4PMState *s = PIIX4_PM(obj);
+
+ return s->cpu_hotplug_legacy;
+}
+
+static void piix4_set_cpu_hotplug_legacy(Object *obj, bool value, Error **errp)
+{
+ PIIX4PMState *s = PIIX4_PM(obj);
+
+ assert(!value);
+ if (s->cpu_hotplug_legacy && value == false) {
+ acpi_switch_to_modern_cphp(&s->gpe_cpu, &s->cpuhp_state,
+ PIIX4_CPU_HOTPLUG_IO_BASE);
+ }
+ s->cpu_hotplug_legacy = value;
+}
+
static void piix4_acpi_system_hot_add_init(MemoryRegion *parent,
PCIBus *bus, PIIX4PMState *s)
{
@@ -570,6 +630,11 @@ static void piix4_acpi_system_hot_add_init(MemoryRegion *parent,
acpi_pcihp_init(OBJECT(s), &s->acpi_pci_hotplug, bus, parent,
s->use_acpi_pci_hotplug);
+ s->cpu_hotplug_legacy = true;
+ object_property_add_bool(OBJECT(s), "cpu-hotplug-legacy",
+ piix4_get_cpu_hotplug_legacy,
+ piix4_set_cpu_hotplug_legacy,
+ NULL);
legacy_acpi_cpu_hotplug_init(parent, OBJECT(s), &s->gpe_cpu,
PIIX4_CPU_HOTPLUG_IO_BASE);
@@ -583,6 +648,9 @@ static void piix4_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list)
PIIX4PMState *s = PIIX4_PM(adev);
acpi_memory_ospm_status(&s->acpi_memory_hotplug, list);
+ if (!s->cpu_hotplug_legacy) {
+ acpi_cpu_ospm_status(&s->cpuhp_state, list);
+ }
}
static void piix4_send_gpe(AcpiDeviceIf *adev, AcpiEventStatusBits ev)
@@ -631,6 +699,7 @@ static void piix4_pm_class_init(ObjectClass *klass, void *data)
hc->unplug = piix4_device_unplug_cb;
adevc->ospm_status = piix4_ospm_status;
adevc->send_event = piix4_send_gpe;
+ adevc->madt_cpu = pc_madt_cpu_entry;
}
static const TypeInfo piix4_pm_info = {
diff --git a/hw/acpi/trace-events b/hw/acpi/trace-events
index e95b2183ac..5aa3ba67c8 100644
--- a/hw/acpi/trace-events
+++ b/hw/acpi/trace-events
@@ -16,3 +16,17 @@ mhp_acpi_clear_insert_evt(uint32_t slot) "slot[0x%"PRIx32"] clear insert event"
mhp_acpi_clear_remove_evt(uint32_t slot) "slot[0x%"PRIx32"] clear remove event"
mhp_acpi_pc_dimm_deleted(uint32_t slot) "slot[0x%"PRIx32"] pc-dimm deleted"
mhp_acpi_pc_dimm_delete_failed(uint32_t slot) "slot[0x%"PRIx32"] pc-dimm delete failed"
+
+# hw/acpi/cpu.c
+cpuhp_acpi_invalid_idx_selected(uint32_t idx) "0x%"PRIx32
+cpuhp_acpi_read_flags(uint32_t idx, uint8_t flags) "idx[0x%"PRIx32"] flags: 0x%"PRIx8
+cpuhp_acpi_write_idx(uint32_t idx) "set active cpu idx: 0x%"PRIx32
+cpuhp_acpi_write_cmd(uint32_t idx, uint8_t cmd) "idx[0x%"PRIx32"] cmd: 0x%"PRIx8
+cpuhp_acpi_read_cmd_data(uint32_t idx, uint32_t data) "idx[0x%"PRIx32"] data: 0x%"PRIx32
+cpuhp_acpi_cpu_has_events(uint32_t idx, bool ins, bool rm) "idx[0x%"PRIx32"] inserting: %d, removing: %d"
+cpuhp_acpi_clear_inserting_evt(uint32_t idx) "idx[0x%"PRIx32"]"
+cpuhp_acpi_clear_remove_evt(uint32_t idx) "idx[0x%"PRIx32"]"
+cpuhp_acpi_ejecting_invalid_cpu(uint32_t idx) "0x%"PRIx32
+cpuhp_acpi_ejecting_cpu(uint32_t idx) "0x%"PRIx32
+cpuhp_acpi_write_ost_ev(uint32_t slot, uint32_t ev) "idx[0x%"PRIx32"] OST EVENT: 0x%"PRIx32
+cpuhp_acpi_write_ost_status(uint32_t slot, uint32_t st) "idx[0x%"PRIx32"] OST STATUS: 0x%"PRIx32
diff --git a/hw/arm/ast2400.c b/hw/arm/ast2400.c
index 4a9de0e10c..0555843620 100644
--- a/hw/arm/ast2400.c
+++ b/hw/arm/ast2400.c
@@ -23,10 +23,19 @@
#define AST2400_UART_5_BASE 0x00184000
#define AST2400_IOMEM_SIZE 0x00200000
#define AST2400_IOMEM_BASE 0x1E600000
+#define AST2400_SMC_BASE AST2400_IOMEM_BASE /* Legacy SMC */
+#define AST2400_FMC_BASE 0X1E620000
+#define AST2400_SPI_BASE 0X1E630000
#define AST2400_VIC_BASE 0x1E6C0000
+#define AST2400_SCU_BASE 0x1E6E2000
#define AST2400_TIMER_BASE 0x1E782000
#define AST2400_I2C_BASE 0x1E78A000
+#define AST2400_FMC_FLASH_BASE 0x20000000
+#define AST2400_SPI_FLASH_BASE 0x30000000
+
+#define AST2400_A0_SILICON_REV 0x02000303
+
static const int uart_irqs[] = { 9, 32, 33, 34, 10 };
static const int timer_irqs[] = { 16, 17, 18, 35, 36, 37, 38, 39, };
@@ -72,13 +81,31 @@ static void ast2400_init(Object *obj)
object_initialize(&s->i2c, sizeof(s->i2c), TYPE_ASPEED_I2C);
object_property_add_child(obj, "i2c", OBJECT(&s->i2c), NULL);
qdev_set_parent_bus(DEVICE(&s->i2c), sysbus_get_default());
+
+ object_initialize(&s->scu, sizeof(s->scu), TYPE_ASPEED_SCU);
+ object_property_add_child(obj, "scu", OBJECT(&s->scu), NULL);
+ qdev_set_parent_bus(DEVICE(&s->scu), sysbus_get_default());
+ qdev_prop_set_uint32(DEVICE(&s->scu), "silicon-rev",
+ AST2400_A0_SILICON_REV);
+ object_property_add_alias(obj, "hw-strap1", OBJECT(&s->scu),
+ "hw-strap1", &error_abort);
+ object_property_add_alias(obj, "hw-strap2", OBJECT(&s->scu),
+ "hw-strap2", &error_abort);
+
+ object_initialize(&s->smc, sizeof(s->smc), "aspeed.smc.fmc");
+ object_property_add_child(obj, "smc", OBJECT(&s->smc), NULL);
+ qdev_set_parent_bus(DEVICE(&s->smc), sysbus_get_default());
+
+ object_initialize(&s->spi, sizeof(s->spi), "aspeed.smc.spi");
+ object_property_add_child(obj, "spi", OBJECT(&s->spi), NULL);
+ qdev_set_parent_bus(DEVICE(&s->spi), sysbus_get_default());
}
static void ast2400_realize(DeviceState *dev, Error **errp)
{
int i;
AST2400State *s = AST2400(dev);
- Error *err = NULL;
+ Error *err = NULL, *local_err = NULL;
/* IO space */
memory_region_init_io(&s->iomem, NULL, &ast2400_io_ops, NULL,
@@ -110,6 +137,14 @@ static void ast2400_realize(DeviceState *dev, Error **errp)
sysbus_connect_irq(SYS_BUS_DEVICE(&s->timerctrl), i, irq);
}
+ /* SCU */
+ object_property_set_bool(OBJECT(&s->scu), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->scu), 0, AST2400_SCU_BASE);
+
/* UART - attach an 8250 to the IO space as our UART5 */
if (serial_hds[0]) {
qemu_irq uart5 = qdev_get_gpio_in(DEVICE(&s->vic), uart_irqs[4]);
@@ -126,6 +161,30 @@ static void ast2400_realize(DeviceState *dev, Error **errp)
sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c), 0, AST2400_I2C_BASE);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c), 0,
qdev_get_gpio_in(DEVICE(&s->vic), 12));
+
+ /* SMC */
+ object_property_set_int(OBJECT(&s->smc), 1, "num-cs", &err);
+ object_property_set_bool(OBJECT(&s->smc), true, "realized", &local_err);
+ error_propagate(&err, local_err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->smc), 0, AST2400_FMC_BASE);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->smc), 1, AST2400_FMC_FLASH_BASE);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->smc), 0,
+ qdev_get_gpio_in(DEVICE(&s->vic), 19));
+
+ /* SPI */
+ object_property_set_int(OBJECT(&s->spi), 1, "num-cs", &err);
+ object_property_set_bool(OBJECT(&s->spi), true, "realized", &local_err);
+ error_propagate(&err, local_err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi), 0, AST2400_SPI_BASE);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi), 1, AST2400_SPI_FLASH_BASE);
}
static void ast2400_class_init(ObjectClass *oc, void *data)
diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c
index 1cd749aa4b..1a53e51cf1 100644
--- a/hw/arm/fsl-imx25.c
+++ b/hw/arm/fsl-imx25.c
@@ -249,16 +249,16 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp)
}
/* initialize 2 x 16 KB ROM */
- memory_region_init_rom_device(&s->rom[0], NULL, NULL, NULL,
- "imx25.rom0", FSL_IMX25_ROM0_SIZE, &err);
+ memory_region_init_rom(&s->rom[0], NULL,
+ "imx25.rom0", FSL_IMX25_ROM0_SIZE, &err);
if (err) {
error_propagate(errp, err);
return;
}
memory_region_add_subregion(get_system_memory(), FSL_IMX25_ROM0_ADDR,
&s->rom[0]);
- memory_region_init_rom_device(&s->rom[1], NULL, NULL, NULL,
- "imx25.rom1", FSL_IMX25_ROM1_SIZE, &err);
+ memory_region_init_rom(&s->rom[1], NULL,
+ "imx25.rom1", FSL_IMX25_ROM1_SIZE, &err);
if (err) {
error_propagate(errp, err);
return;
diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c
index 31a3a87911..b283b71eb4 100644
--- a/hw/arm/fsl-imx31.c
+++ b/hw/arm/fsl-imx31.c
@@ -219,9 +219,8 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp)
}
/* On a real system, the first 16k is a `secure boot rom' */
- memory_region_init_rom_device(&s->secure_rom, NULL, NULL, NULL,
- "imx31.secure_rom",
- FSL_IMX31_SECURE_ROM_SIZE, &err);
+ memory_region_init_rom(&s->secure_rom, NULL, "imx31.secure_rom",
+ FSL_IMX31_SECURE_ROM_SIZE, &err);
if (err) {
error_propagate(errp, err);
return;
@@ -230,8 +229,8 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp)
&s->secure_rom);
/* There is also a 16k ROM */
- memory_region_init_rom_device(&s->rom, NULL, NULL, NULL, "imx31.rom",
- FSL_IMX31_ROM_SIZE, &err);
+ memory_region_init_rom(&s->rom, NULL, "imx31.rom",
+ FSL_IMX31_ROM_SIZE, &err);
if (err) {
error_propagate(errp, err);
return;
diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c
index 0c00e7a560..ed392a9e7f 100644
--- a/hw/arm/fsl-imx6.c
+++ b/hw/arm/fsl-imx6.c
@@ -399,8 +399,8 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp)
FSL_IMX6_ENET_MAC_1588_IRQ));
/* ROM memory */
- memory_region_init_rom_device(&s->rom, NULL, NULL, NULL, "imx6.rom",
- FSL_IMX6_ROM_SIZE, &err);
+ memory_region_init_rom(&s->rom, NULL, "imx6.rom",
+ FSL_IMX6_ROM_SIZE, &err);
if (err) {
error_propagate(errp, err);
return;
@@ -409,8 +409,8 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp)
&s->rom);
/* CAAM memory */
- memory_region_init_rom_device(&s->caam, NULL, NULL, NULL, "imx6.caam",
- FSL_IMX6_CAAM_MEM_SIZE, &err);
+ memory_region_init_rom(&s->caam, NULL, "imx6.caam",
+ FSL_IMX6_CAAM_MEM_SIZE, &err);
if (err) {
error_propagate(errp, err);
return;
diff --git a/hw/arm/palmetto-bmc.c b/hw/arm/palmetto-bmc.c
index a51d960510..54e29a865d 100644
--- a/hw/arm/palmetto-bmc.c
+++ b/hw/arm/palmetto-bmc.c
@@ -18,6 +18,8 @@
#include "hw/arm/ast2400.h"
#include "hw/boards.h"
#include "qemu/log.h"
+#include "sysemu/block-backend.h"
+#include "sysemu/blockdev.h"
static struct arm_boot_info palmetto_bmc_binfo = {
.loader_start = AST2400_SDRAM_BASE,
@@ -30,6 +32,32 @@ typedef struct PalmettoBMCState {
MemoryRegion ram;
} PalmettoBMCState;
+static void palmetto_bmc_init_flashes(AspeedSMCState *s, const char *flashtype,
+ Error **errp)
+{
+ int i ;
+
+ for (i = 0; i < s->num_cs; ++i) {
+ AspeedSMCFlash *fl = &s->flashes[i];
+ DriveInfo *dinfo = drive_get_next(IF_MTD);
+ qemu_irq cs_line;
+
+ /*
+ * FIXME: check that we are not using a flash module exceeding
+ * the controller segment size
+ */
+ fl->flash = ssi_create_slave_no_init(s->spi, flashtype);
+ if (dinfo) {
+ qdev_prop_set_drive(fl->flash, "drive", blk_by_legacy_dinfo(dinfo),
+ errp);
+ }
+ qdev_init_nofail(fl->flash);
+
+ cs_line = qdev_get_gpio_in_named(fl->flash, SSI_GPIO_CS, 0);
+ sysbus_connect_irq(SYS_BUS_DEVICE(s), i + 1, cs_line);
+ }
+}
+
static void palmetto_bmc_init(MachineState *machine)
{
PalmettoBMCState *bmc;
@@ -44,9 +72,14 @@ static void palmetto_bmc_init(MachineState *machine)
&bmc->ram);
object_property_add_const_link(OBJECT(&bmc->soc), "ram", OBJECT(&bmc->ram),
&error_abort);
+ object_property_set_int(OBJECT(&bmc->soc), 0x120CE416, "hw-strap1",
+ &error_abort);
object_property_set_bool(OBJECT(&bmc->soc), true, "realized",
&error_abort);
+ palmetto_bmc_init_flashes(&bmc->soc.smc, "n25q256a", &error_abort);
+ palmetto_bmc_init_flashes(&bmc->soc.spi, "mx25l25635e", &error_abort);
+
palmetto_bmc_binfo.kernel_filename = machine->kernel_filename;
palmetto_bmc_binfo.initrd_filename = machine->initrd_filename;
palmetto_bmc_binfo.kernel_cmdline = machine->kernel_cmdline;
diff --git a/hw/arm/sabrelite.c b/hw/arm/sabrelite.c
index 776c51e398..4e7ac8cc4f 100644
--- a/hw/arm/sabrelite.c
+++ b/hw/arm/sabrelite.c
@@ -86,13 +86,19 @@ static void sabrelite_init(MachineState *machine)
spi_bus = (SSIBus *)qdev_get_child_bus(DEVICE(spi_dev), "spi");
if (spi_bus) {
DeviceState *flash_dev;
-
- flash_dev = ssi_create_slave(spi_bus, "sst25vf016b");
- if (flash_dev) {
- qemu_irq cs_line = qdev_get_gpio_in_named(flash_dev,
- SSI_GPIO_CS, 0);
- sysbus_connect_irq(SYS_BUS_DEVICE(spi_dev), 1, cs_line);
+ qemu_irq cs_line;
+ DriveInfo *dinfo = drive_get_next(IF_MTD);
+
+ flash_dev = ssi_create_slave_no_init(spi_bus, "sst25vf016b");
+ if (dinfo) {
+ qdev_prop_set_drive(flash_dev, "drive",
+ blk_by_legacy_dinfo(dinfo),
+ &error_fatal);
}
+ qdev_init_nofail(flash_dev);
+
+ cs_line = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0);
+ sysbus_connect_irq(SYS_BUS_DEVICE(spi_dev), 1, cs_line);
}
}
}
diff --git a/hw/arm/spitz.c b/hw/arm/spitz.c
index ba40f8302b..41cc2eeeb1 100644
--- a/hw/arm/spitz.c
+++ b/hw/arm/spitz.c
@@ -598,15 +598,13 @@ static uint32_t spitz_lcdtg_transfer(SSISlave *dev, uint32_t value)
return 0;
}
-static int spitz_lcdtg_init(SSISlave *dev)
+static void spitz_lcdtg_realize(SSISlave *dev, Error **errp)
{
SpitzLCDTG *s = FROM_SSI_SLAVE(SpitzLCDTG, dev);
spitz_lcdtg = s;
s->bl_power = 0;
s->bl_intensity = 0x20;
-
- return 0;
}
/* SSP devices */
@@ -666,7 +664,7 @@ static void spitz_adc_temp_on(void *opaque, int line, int level)
max111x_set_input(max1111, MAX1111_BATT_TEMP, 0);
}
-static int corgi_ssp_init(SSISlave *d)
+static void corgi_ssp_realize(SSISlave *d, Error **errp)
{
DeviceState *dev = DEVICE(d);
CorgiSSPState *s = FROM_SSI_SLAVE(CorgiSSPState, d);
@@ -675,8 +673,6 @@ static int corgi_ssp_init(SSISlave *d)
s->bus[0] = ssi_create_bus(dev, "ssi0");
s->bus[1] = ssi_create_bus(dev, "ssi1");
s->bus[2] = ssi_create_bus(dev, "ssi2");
-
- return 0;
}
static void spitz_ssp_attach(PXA2xxState *cpu)
@@ -1121,7 +1117,7 @@ static void corgi_ssp_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
- k->init = corgi_ssp_init;
+ k->realize = corgi_ssp_realize;
k->transfer = corgi_ssp_transfer;
dc->vmsd = &vmstate_corgi_ssp_regs;
}
@@ -1150,7 +1146,7 @@ static void spitz_lcdtg_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
- k->init = spitz_lcdtg_init;
+ k->realize = spitz_lcdtg_realize;
k->transfer = spitz_lcdtg_transfer;
dc->vmsd = &vmstate_spitz_lcdtg_regs;
}
diff --git a/hw/arm/tosa.c b/hw/arm/tosa.c
index 4e9494f94c..2db66508b5 100644
--- a/hw/arm/tosa.c
+++ b/hw/arm/tosa.c
@@ -127,10 +127,9 @@ static uint32_t tosa_ssp_tansfer(SSISlave *dev, uint32_t value)
return 0;
}
-static int tosa_ssp_init(SSISlave *dev)
+static void tosa_ssp_realize(SSISlave *dev, Error **errp)
{
/* Nothing to do. */
- return 0;
}
#define TYPE_TOSA_DAC "tosa_dac"
@@ -283,7 +282,7 @@ static void tosa_ssp_class_init(ObjectClass *klass, void *data)
{
SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
- k->init = tosa_ssp_init;
+ k->realize = tosa_ssp_realize;
k->transfer = tosa_ssp_tansfer;
}
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index c5c125e920..6e098afd1f 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -1021,6 +1021,7 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic,
qemu_fdt_setprop_cell(vbi->fdt, nodename, "#size-cells", 2);
qemu_fdt_setprop_cells(vbi->fdt, nodename, "bus-range", 0,
nr_pcie_buses - 1);
+ qemu_fdt_setprop(vbi->fdt, nodename, "dma-coherent", NULL, 0);
if (vbi->v2m_phandle) {
qemu_fdt_setprop_cells(vbi->fdt, nodename, "msi-parent",
diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c
index aefebcfa6d..7dac20d67d 100644
--- a/hw/arm/xilinx_zynq.c
+++ b/hw/arm/xilinx_zynq.c
@@ -138,7 +138,13 @@ static inline void zynq_init_spi_flashes(uint32_t base_addr, qemu_irq irq,
spi = (SSIBus *)qdev_get_child_bus(dev, bus_name);
for (j = 0; j < num_ss; ++j) {
- flash_dev = ssi_create_slave(spi, "n25q128");
+ DriveInfo *dinfo = drive_get_next(IF_MTD);
+ flash_dev = ssi_create_slave_no_init(spi, "n25q128");
+ if (dinfo) {
+ qdev_prop_set_drive(flash_dev, "drive",
+ blk_by_legacy_dinfo(dinfo), &error_fatal);
+ }
+ qdev_init_nofail(flash_dev);
cs_line = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0);
sysbus_connect_irq(busdev, i * num_ss + j + 1, cs_line);
@@ -294,6 +300,12 @@ static void zynq_init(MachineState *machine)
sysbus_connect_irq(busdev, n + 1, pic[dma_irqs[n] - IRQ_OFFSET]);
}
+ dev = qdev_create(NULL, "xlnx.ps7-dev-cfg");
+ qdev_init_nofail(dev);
+ busdev = SYS_BUS_DEVICE(dev);
+ sysbus_connect_irq(busdev, 0, pic[40 - IRQ_OFFSET]);
+ sysbus_mmio_map(busdev, 0, 0xF8007000);
+
zynq_binfo.ram_size = ram_size;
zynq_binfo.kernel_filename = kernel_filename;
zynq_binfo.kernel_cmdline = kernel_cmdline;
diff --git a/hw/arm/xlnx-ep108.c b/hw/arm/xlnx-ep108.c
index 34b4641712..4ec590a25d 100644
--- a/hw/arm/xlnx-ep108.c
+++ b/hw/arm/xlnx-ep108.c
@@ -88,12 +88,19 @@ static void xlnx_ep108_init(MachineState *machine)
SSIBus *spi_bus;
DeviceState *flash_dev;
qemu_irq cs_line;
+ DriveInfo *dinfo = drive_get_next(IF_MTD);
gchar *bus_name = g_strdup_printf("spi%d", i);
spi_bus = (SSIBus *)qdev_get_child_bus(DEVICE(&s->soc), bus_name);
g_free(bus_name);
- flash_dev = ssi_create_slave(spi_bus, "sst25wf080");
+ flash_dev = ssi_create_slave_no_init(spi_bus, "sst25wf080");
+ if (dinfo) {
+ qdev_prop_set_drive(flash_dev, "drive", blk_by_legacy_dinfo(dinfo),
+ &error_fatal);
+ }
+ qdev_init_nofail(flash_dev);
+
cs_line = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->soc.spi[i]), 1, cs_line);
diff --git a/hw/arm/z2.c b/hw/arm/z2.c
index aea895a500..68a92f3184 100644
--- a/hw/arm/z2.c
+++ b/hw/arm/z2.c
@@ -151,14 +151,12 @@ static void z2_lcd_cs(void *opaque, int line, int level)
z2_lcd->selected = !level;
}
-static int zipit_lcd_init(SSISlave *dev)
+static void zipit_lcd_realize(SSISlave *dev, Error **errp)
{
ZipitLCD *z = FROM_SSI_SLAVE(ZipitLCD, dev);
z->selected = 0;
z->enabled = 0;
z->pos = 0;
-
- return 0;
}
static VMStateDescription vmstate_zipit_lcd_state = {
@@ -181,7 +179,7 @@ static void zipit_lcd_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
- k->init = zipit_lcd_init;
+ k->realize = zipit_lcd_realize;
k->transfer = zipit_lcd_transfer;
dc->vmsd = &vmstate_zipit_lcd_state;
}
diff --git a/hw/audio/pcspk.c b/hw/audio/pcspk.c
index d2599604d1..42a6f4885a 100644
--- a/hw/audio/pcspk.c
+++ b/hw/audio/pcspk.c
@@ -31,6 +31,7 @@
#include "qemu/timer.h"
#include "hw/timer/i8254.h"
#include "hw/audio/pcspk.h"
+#include "qapi/error.h"
#define PCSPK_BUF_LEN 1792
#define PCSPK_SAMPLE_RATE 32000
@@ -169,6 +170,11 @@ static void pcspk_initfn(Object *obj)
PCSpkState *s = PC_SPEAKER(obj);
memory_region_init_io(&s->ioport, OBJECT(s), &pcspk_io_ops, s, "pcspk", 1);
+
+ object_property_add_link(obj, "pit", TYPE_PIT_COMMON,
+ (Object **)&s->pit,
+ qdev_prop_allow_set_link_before_realize,
+ 0, &error_abort);
}
static void pcspk_realizefn(DeviceState *dev, Error **errp)
@@ -183,7 +189,6 @@ static void pcspk_realizefn(DeviceState *dev, Error **errp)
static Property pcspk_properties[] = {
DEFINE_PROP_UINT32("iobase", PCSpkState, iobase, -1),
- DEFINE_PROP_PTR("pit", PCSpkState, pit),
DEFINE_PROP_END_OF_LIST(),
};
@@ -194,7 +199,7 @@ static void pcspk_class_initfn(ObjectClass *klass, void *data)
dc->realize = pcspk_realizefn;
set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
dc->props = pcspk_properties;
- /* Reason: pointer property "pit", realize sets global pcspk_state */
+ /* Reason: realize sets global pcspk_state */
dc->cannot_instantiate_with_device_add_yet = true;
}
diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c
index 2073f9a270..54b9ac1da6 100644
--- a/hw/block/dataplane/virtio-blk.c
+++ b/hw/block/dataplane/virtio-blk.c
@@ -31,11 +31,9 @@ struct VirtIOBlockDataPlane {
bool stopping;
VirtIOBlkConf *conf;
-
VirtIODevice *vdev;
- VirtQueue *vq; /* virtqueue vring */
- EventNotifier *guest_notifier; /* irq */
QEMUBH *bh; /* bh for guest notification */
+ unsigned long *batch_notify_vqs;
/* Note that these EventNotifiers are assigned by value. This is
* fine as long as you do not call event_notifier_cleanup on them
@@ -47,20 +45,36 @@ struct VirtIOBlockDataPlane {
};
/* Raise an interrupt to signal guest, if necessary */
-void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s)
+void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s, VirtQueue *vq)
{
+ set_bit(virtio_get_queue_index(vq), s->batch_notify_vqs);
qemu_bh_schedule(s->bh);
}
static void notify_guest_bh(void *opaque)
{
VirtIOBlockDataPlane *s = opaque;
+ unsigned nvqs = s->conf->num_queues;
+ unsigned long bitmap[BITS_TO_LONGS(nvqs)];
+ unsigned j;
- if (!virtio_should_notify(s->vdev, s->vq)) {
- return;
- }
+ memcpy(bitmap, s->batch_notify_vqs, sizeof(bitmap));
+ memset(s->batch_notify_vqs, 0, sizeof(bitmap));
+
+ for (j = 0; j < nvqs; j += BITS_PER_LONG) {
+ unsigned long bits = bitmap[j];
- event_notifier_set(s->guest_notifier);
+ while (bits != 0) {
+ unsigned i = j + ctzl(bits);
+ VirtQueue *vq = virtio_get_queue(s->vdev, i);
+
+ if (virtio_should_notify(s->vdev, vq)) {
+ event_notifier_set(virtio_queue_get_guest_notifier(vq));
+ }
+
+ bits &= bits - 1; /* clear right-most bit */
+ }
+ }
}
/* Context: QEMU global mutex held */
@@ -79,7 +93,7 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
}
/* Don't try if transport does not support notifiers. */
- if (!k->set_guest_notifiers || !k->set_host_notifier) {
+ if (!k->set_guest_notifiers || !k->ioeventfd_started) {
error_setg(errp,
"device is incompatible with dataplane "
"(transport does not support notifiers)");
@@ -104,6 +118,7 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
}
s->ctx = iothread_get_aio_context(s->iothread);
s->bh = aio_bh_new(s->ctx, notify_guest_bh, s);
+ s->batch_notify_vqs = bitmap_new(conf->num_queues);
*dataplane = s;
}
@@ -116,6 +131,7 @@ void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s)
}
virtio_blk_data_plane_stop(s);
+ g_free(s->batch_notify_vqs);
qemu_bh_delete(s->bh);
object_unref(OBJECT(s->iothread));
g_free(s);
@@ -138,6 +154,8 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s)
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s->vdev)));
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
VirtIOBlock *vblk = VIRTIO_BLK(s->vdev);
+ unsigned i;
+ unsigned nvqs = s->conf->num_queues;
int r;
if (vblk->dataplane_started || s->starting) {
@@ -145,22 +163,25 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s)
}
s->starting = true;
- s->vq = virtio_get_queue(s->vdev, 0);
/* Set up guest notifier (irq) */
- r = k->set_guest_notifiers(qbus->parent, 1, true);
+ r = k->set_guest_notifiers(qbus->parent, nvqs, true);
if (r != 0) {
fprintf(stderr, "virtio-blk failed to set guest notifier (%d), "
"ensure -enable-kvm is set\n", r);
goto fail_guest_notifiers;
}
- s->guest_notifier = virtio_queue_get_guest_notifier(s->vq);
/* Set up virtqueue notify */
- r = k->set_host_notifier(qbus->parent, 0, true);
- if (r != 0) {
- fprintf(stderr, "virtio-blk failed to set host notifier (%d)\n", r);
- goto fail_host_notifier;
+ for (i = 0; i < nvqs; i++) {
+ r = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, true);
+ if (r != 0) {
+ fprintf(stderr, "virtio-blk failed to set host notifier (%d)\n", r);
+ while (i--) {
+ virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false);
+ }
+ goto fail_guest_notifiers;
+ }
}
s->starting = false;
@@ -170,17 +191,23 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s)
blk_set_aio_context(s->conf->conf.blk, s->ctx);
/* Kick right away to begin processing requests already in vring */
- event_notifier_set(virtio_queue_get_host_notifier(s->vq));
+ for (i = 0; i < nvqs; i++) {
+ VirtQueue *vq = virtio_get_queue(s->vdev, i);
+
+ event_notifier_set(virtio_queue_get_host_notifier(vq));
+ }
/* Get this show started by hooking up our callbacks */
aio_context_acquire(s->ctx);
- virtio_queue_aio_set_host_notifier_handler(s->vq, s->ctx,
- virtio_blk_data_plane_handle_output);
+ for (i = 0; i < nvqs; i++) {
+ VirtQueue *vq = virtio_get_queue(s->vdev, i);
+
+ virtio_queue_aio_set_host_notifier_handler(vq, s->ctx,
+ virtio_blk_data_plane_handle_output);
+ }
aio_context_release(s->ctx);
return;
- fail_host_notifier:
- k->set_guest_notifiers(qbus->parent, 1, false);
fail_guest_notifiers:
vblk->dataplane_disabled = true;
s->starting = false;
@@ -193,6 +220,8 @@ void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s)
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s->vdev)));
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
VirtIOBlock *vblk = VIRTIO_BLK(s->vdev);
+ unsigned i;
+ unsigned nvqs = s->conf->num_queues;
if (!vblk->dataplane_started || s->stopping) {
return;
@@ -210,17 +239,23 @@ void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s)
aio_context_acquire(s->ctx);
/* Stop notifications for new requests from guest */
- virtio_queue_aio_set_host_notifier_handler(s->vq, s->ctx, NULL);
+ for (i = 0; i < nvqs; i++) {
+ VirtQueue *vq = virtio_get_queue(s->vdev, i);
+
+ virtio_queue_aio_set_host_notifier_handler(vq, s->ctx, NULL);
+ }
/* Drain and switch bs back to the QEMU main loop */
blk_set_aio_context(s->conf->conf.blk, qemu_get_aio_context());
aio_context_release(s->ctx);
- k->set_host_notifier(qbus->parent, 0, false);
+ for (i = 0; i < nvqs; i++) {
+ virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false);
+ }
/* Clean up guest notifier (irq) */
- k->set_guest_notifiers(qbus->parent, 1, false);
+ k->set_guest_notifiers(qbus->parent, nvqs, false);
vblk->dataplane_started = false;
s->stopping = false;
diff --git a/hw/block/dataplane/virtio-blk.h b/hw/block/dataplane/virtio-blk.h
index 0714c11a2b..b1f0b95b32 100644
--- a/hw/block/dataplane/virtio-blk.h
+++ b/hw/block/dataplane/virtio-blk.h
@@ -26,6 +26,6 @@ void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s);
void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s);
void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s);
void virtio_blk_data_plane_drain(VirtIOBlockDataPlane *s);
-void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s);
+void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s, VirtQueue *vq);
#endif /* HW_DATAPLANE_VIRTIO_BLK_H */
diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
index 51d8596056..d9b27939dd 100644
--- a/hw/block/m25p80.c
+++ b/hw/block/m25p80.c
@@ -28,6 +28,7 @@
#include "hw/ssi/ssi.h"
#include "qemu/bitops.h"
#include "qemu/log.h"
+#include "qapi/error.h"
#ifndef M25P80_ERR_DEBUG
#define M25P80_ERR_DEBUG 0
@@ -53,12 +54,17 @@
/* 16 MiB max in 3 byte address mode */
#define MAX_3BYTES_SIZE 0x1000000
+#define SPI_NOR_MAX_ID_LEN 6
+
typedef struct FlashPartInfo {
const char *part_name;
- /* jedec code. (jedec >> 16) & 0xff is the 1st byte, >> 8 the 2nd etc */
- uint32_t jedec;
- /* extended jedec code */
- uint16_t ext_jedec;
+ /*
+ * This array stores the ID bytes.
+ * The first three bytes are the JEDIC ID.
+ * JEDEC ID zero means "no ID" (mostly older chips).
+ */
+ uint8_t id[SPI_NOR_MAX_ID_LEN];
+ uint8_t id_len;
/* there is confusion between manufacturers as to what a sector is. In this
* device model, a "sector" is the size that is erased by the ERASE_SECTOR
* command (opcode 0xd8).
@@ -70,11 +76,33 @@ typedef struct FlashPartInfo {
} FlashPartInfo;
/* adapted from linux */
-
-#define INFO(_part_name, _jedec, _ext_jedec, _sector_size, _n_sectors, _flags)\
- .part_name = (_part_name),\
- .jedec = (_jedec),\
- .ext_jedec = (_ext_jedec),\
+/* Used when the "_ext_id" is two bytes at most */
+#define INFO(_part_name, _jedec_id, _ext_id, _sector_size, _n_sectors, _flags)\
+ .part_name = _part_name,\
+ .id = {\
+ ((_jedec_id) >> 16) & 0xff,\
+ ((_jedec_id) >> 8) & 0xff,\
+ (_jedec_id) & 0xff,\
+ ((_ext_id) >> 8) & 0xff,\
+ (_ext_id) & 0xff,\
+ },\
+ .id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))),\
+ .sector_size = (_sector_size),\
+ .n_sectors = (_n_sectors),\
+ .page_size = 256,\
+ .flags = (_flags),
+
+#define INFO6(_part_name, _jedec_id, _ext_id, _sector_size, _n_sectors, _flags)\
+ .part_name = _part_name,\
+ .id = {\
+ ((_jedec_id) >> 16) & 0xff,\
+ ((_jedec_id) >> 8) & 0xff,\
+ (_jedec_id) & 0xff,\
+ ((_ext_id) >> 16) & 0xff,\
+ ((_ext_id) >> 8) & 0xff,\
+ (_ext_id) & 0xff,\
+ },\
+ .id_len = 6,\
.sector_size = (_sector_size),\
.n_sectors = (_n_sectors),\
.page_size = 256,\
@@ -102,12 +130,26 @@ typedef struct FlashPartInfo {
#define EVCFG_QUAD_IO_ENABLED (1 << 7)
#define NVCFG_4BYTE_ADDR_MASK (1 << 0)
#define NVCFG_LOWER_SEGMENT_MASK (1 << 1)
-#define CFG_UPPER_128MB_SEG_ENABLED 0x3
/* Numonyx (Micron) Flag Status Register macros */
#define FSR_4BYTE_ADDR_MODE_ENABLED 0x1
#define FSR_FLASH_READY (1 << 7)
+/* Spansion configuration registers macros. */
+#define SPANSION_QUAD_CFG_POS 0
+#define SPANSION_QUAD_CFG_LEN 1
+#define SPANSION_DUMMY_CLK_POS 0
+#define SPANSION_DUMMY_CLK_LEN 4
+#define SPANSION_ADDR_LEN_POS 7
+#define SPANSION_ADDR_LEN_LEN 1
+
+/*
+ * Spansion read mode command length in bytes,
+ * the mode is currently not supported.
+*/
+
+#define SPANSION_CONTINUOUS_READ_MODE_CMD_LEN 1
+
static const FlashPartInfo known_devices[] = {
/* Atmel -- some are (confusingly) marketed as "DataFlash" */
{ INFO("at25fs010", 0x1f6601, 0, 32 << 10, 4, ER_4K) },
@@ -158,6 +200,8 @@ static const FlashPartInfo known_devices[] = {
{ INFO("mx25l12855e", 0xc22618, 0, 64 << 10, 256, 0) },
{ INFO("mx25l25635e", 0xc22019, 0, 64 << 10, 512, 0) },
{ INFO("mx25l25655e", 0xc22619, 0, 64 << 10, 512, 0) },
+ { INFO("mx66u51235f", 0xc2253a, 0, 64 << 10, 1024, ER_4K | ER_32K) },
+ { INFO("mx66u1g45g", 0xc2253b, 0, 64 << 10, 2048, ER_4K | ER_32K) },
/* Micron */
{ INFO("n25q032a11", 0x20bb16, 0, 64 << 10, 64, ER_4K) },
@@ -168,6 +212,11 @@ static const FlashPartInfo known_devices[] = {
{ INFO("n25q128a13", 0x20ba18, 0, 64 << 10, 256, ER_4K) },
{ INFO("n25q256a11", 0x20bb19, 0, 64 << 10, 512, ER_4K) },
{ INFO("n25q256a13", 0x20ba19, 0, 64 << 10, 512, ER_4K) },
+ { INFO("n25q128", 0x20ba18, 0, 64 << 10, 256, 0) },
+ { INFO("n25q256a", 0x20ba19, 0, 64 << 10, 512, ER_4K) },
+ { INFO("n25q512a", 0x20ba20, 0, 64 << 10, 1024, ER_4K) },
+ { INFO("mt25ql01g", 0x20ba21, 0, 64 << 10, 2048, ER_4K) },
+ { INFO("mt25qu01g", 0x20bb21, 0, 64 << 10, 2048, ER_4K) },
/* Spansion -- single (large) sector size only, at least
* for the chips listed here (without boot sectors).
@@ -176,8 +225,8 @@ static const FlashPartInfo known_devices[] = {
{ INFO("s25sl064p", 0x010216, 0x4d00, 64 << 10, 128, ER_4K) },
{ INFO("s25fl256s0", 0x010219, 0x4d00, 256 << 10, 128, 0) },
{ INFO("s25fl256s1", 0x010219, 0x4d01, 64 << 10, 512, 0) },
- { INFO("s25fl512s", 0x010220, 0x4d00, 256 << 10, 256, 0) },
- { INFO("s70fl01gs", 0x010221, 0x4d00, 256 << 10, 256, 0) },
+ { INFO6("s25fl512s", 0x010220, 0x4d0080, 256 << 10, 256, 0) },
+ { INFO6("s70fl01gs", 0x010221, 0x4d0080, 256 << 10, 512, 0) },
{ INFO("s25sl12800", 0x012018, 0x0300, 256 << 10, 64, 0) },
{ INFO("s25sl12801", 0x012018, 0x0301, 64 << 10, 256, 0) },
{ INFO("s25fl129p0", 0x012018, 0x4d00, 256 << 10, 64, 0) },
@@ -190,6 +239,10 @@ static const FlashPartInfo known_devices[] = {
{ INFO("s25fl016k", 0xef4015, 0, 64 << 10, 32, ER_4K | ER_32K) },
{ INFO("s25fl064k", 0xef4017, 0, 64 << 10, 128, ER_4K | ER_32K) },
+ /* Spansion -- boot sectors support */
+ { INFO6("s25fs512s", 0x010220, 0x4d0081, 256 << 10, 256, 0) },
+ { INFO6("s70fs01gs", 0x010221, 0x4d0081, 256 << 10, 512, 0) },
+
/* SST -- large erase sizes are "overlays", "sectors" are 4<< 10 */
{ INFO("sst25vf040b", 0xbf258d, 0, 64 << 10, 8, ER_4K) },
{ INFO("sst25vf080b", 0xbf258e, 0, 64 << 10, 16, ER_4K) },
@@ -240,10 +293,6 @@ static const FlashPartInfo known_devices[] = {
{ INFO("w25q80", 0xef5014, 0, 64 << 10, 16, ER_4K) },
{ INFO("w25q80bl", 0xef4014, 0, 64 << 10, 16, ER_4K) },
{ INFO("w25q256", 0xef4019, 0, 64 << 10, 512, ER_4K) },
-
- { INFO("n25q128", 0x20ba18, 0, 64 << 10, 256, 0) },
- { INFO("n25q256a", 0x20ba19, 0, 64 << 10, 512, ER_4K) },
- { INFO("n25q512a", 0x20ba20, 0, 64 << 10, 1024, ER_4K) },
};
typedef enum {
@@ -255,6 +304,7 @@ typedef enum {
JEDEC_READ = 0x9f,
BULK_ERASE = 0xc7,
READ_FSR = 0x70,
+ RDCR = 0x15,
READ = 0x03,
READ4 = 0x13,
@@ -271,12 +321,14 @@ typedef enum {
PP = 0x02,
PP4 = 0x12,
+ PP4_4 = 0x3e,
DPP = 0xa2,
QPP = 0x32,
ERASE_4K = 0x20,
ERASE4_4K = 0x21,
ERASE_32K = 0x52,
+ ERASE4_32K = 0x5c,
ERASE_SECTOR = 0xd8,
ERASE4_SECTOR = 0xdc,
@@ -289,6 +341,13 @@ typedef enum {
RESET_ENABLE = 0x66,
RESET_MEMORY = 0x99,
+ /*
+ * Micron: 0x35 - enable QPI
+ * Spansion: 0x35 - read control register
+ */
+ RDCR_EQIO = 0x35,
+ RSTQIO = 0xf5,
+
RNVCR = 0xB5,
WNVCR = 0xB1,
@@ -304,9 +363,18 @@ typedef enum {
STATE_PAGE_PROGRAM,
STATE_READ,
STATE_COLLECTING_DATA,
+ STATE_COLLECTING_VAR_LEN_DATA,
STATE_READING_DATA,
} CMDState;
+typedef enum {
+ MAN_SPANSION,
+ MAN_MACRONIX,
+ MAN_NUMONYX,
+ MAN_WINBOND,
+ MAN_GENERIC,
+} Manufacturer;
+
typedef struct Flash {
SSISlave parent_obj;
@@ -322,13 +390,24 @@ typedef struct Flash {
uint32_t pos;
uint8_t needed_bytes;
uint8_t cmd_in_progress;
- uint64_t cur_addr;
+ uint32_t cur_addr;
uint32_t nonvolatile_cfg;
+ /* Configuration register for Macronix */
uint32_t volatile_cfg;
uint32_t enh_volatile_cfg;
+ /* Spansion cfg registers. */
+ uint8_t spansion_cr1nv;
+ uint8_t spansion_cr2nv;
+ uint8_t spansion_cr3nv;
+ uint8_t spansion_cr4nv;
+ uint8_t spansion_cr1v;
+ uint8_t spansion_cr2v;
+ uint8_t spansion_cr3v;
+ uint8_t spansion_cr4v;
bool write_enable;
bool four_bytes_address_mode;
bool reset_enable;
+ bool quad_enable;
uint8_t ear;
int64_t dirty_page;
@@ -350,8 +429,29 @@ typedef struct M25P80Class {
#define M25P80_GET_CLASS(obj) \
OBJECT_GET_CLASS(M25P80Class, (obj), TYPE_M25P80)
+static inline Manufacturer get_man(Flash *s)
+{
+ switch (s->pi->id[0]) {
+ case 0x20:
+ return MAN_NUMONYX;
+ case 0xEF:
+ return MAN_WINBOND;
+ case 0x01:
+ return MAN_SPANSION;
+ case 0xC2:
+ return MAN_MACRONIX;
+ default:
+ return MAN_GENERIC;
+ }
+}
+
static void blk_sync_complete(void *opaque, int ret)
{
+ QEMUIOVector *iov = opaque;
+
+ qemu_iovec_destroy(iov);
+ g_free(iov);
+
/* do nothing. Masters do not directly interact with the backing store,
* only the working copy so no mutexing required.
*/
@@ -359,31 +459,31 @@ static void blk_sync_complete(void *opaque, int ret)
static void flash_sync_page(Flash *s, int page)
{
- QEMUIOVector iov;
+ QEMUIOVector *iov = g_new(QEMUIOVector, 1);
if (!s->blk || blk_is_read_only(s->blk)) {
return;
}
- qemu_iovec_init(&iov, 1);
- qemu_iovec_add(&iov, s->storage + page * s->pi->page_size,
+ qemu_iovec_init(iov, 1);
+ qemu_iovec_add(iov, s->storage + page * s->pi->page_size,
s->pi->page_size);
- blk_aio_pwritev(s->blk, page * s->pi->page_size, &iov, 0,
- blk_sync_complete, NULL);
+ blk_aio_pwritev(s->blk, page * s->pi->page_size, iov, 0,
+ blk_sync_complete, iov);
}
static inline void flash_sync_area(Flash *s, int64_t off, int64_t len)
{
- QEMUIOVector iov;
+ QEMUIOVector *iov = g_new(QEMUIOVector, 1);
if (!s->blk || blk_is_read_only(s->blk)) {
return;
}
assert(!(len % BDRV_SECTOR_SIZE));
- qemu_iovec_init(&iov, 1);
- qemu_iovec_add(&iov, s->storage + off, len);
- blk_aio_pwritev(s->blk, off, &iov, 0, blk_sync_complete, NULL);
+ qemu_iovec_init(iov, 1);
+ qemu_iovec_add(iov, s->storage + off, len);
+ blk_aio_pwritev(s->blk, off, iov, 0, blk_sync_complete, iov);
}
static void flash_erase(Flash *s, int offset, FlashCMD cmd)
@@ -398,6 +498,7 @@ static void flash_erase(Flash *s, int offset, FlashCMD cmd)
capa_to_assert = ER_4K;
break;
case ERASE_32K:
+ case ERASE4_32K:
len = 32 << 10;
capa_to_assert = ER_32K;
break;
@@ -435,9 +536,9 @@ static inline void flash_sync_dirty(Flash *s, int64_t newpage)
}
static inline
-void flash_write8(Flash *s, uint64_t addr, uint8_t data)
+void flash_write8(Flash *s, uint32_t addr, uint8_t data)
{
- int64_t page = addr / s->pi->page_size;
+ uint32_t page = addr / s->pi->page_size;
uint8_t prev = s->storage[s->cur_addr];
if (!s->write_enable) {
@@ -445,7 +546,7 @@ void flash_write8(Flash *s, uint64_t addr, uint8_t data)
}
if ((prev ^ data) & data) {
- DB_PRINT_L(1, "programming zero to one! addr=%" PRIx64 " %" PRIx8
+ DB_PRINT_L(1, "programming zero to one! addr=%" PRIx32 " %" PRIx8
" -> %" PRIx8 "\n", addr, prev, data);
}
@@ -468,9 +569,11 @@ static inline int get_addr_length(Flash *s)
switch (s->cmd_in_progress) {
case PP4:
+ case PP4_4:
case READ4:
case QIOR4:
case ERASE4_4K:
+ case ERASE4_32K:
case ERASE4_SECTOR:
case FAST_READ4:
case DOR4:
@@ -484,18 +587,16 @@ static inline int get_addr_length(Flash *s)
static void complete_collecting_data(Flash *s)
{
- int i;
-
- s->cur_addr = 0;
+ int i, n;
- for (i = 0; i < get_addr_length(s); ++i) {
+ n = get_addr_length(s);
+ s->cur_addr = (n == 3 ? s->ear : 0);
+ for (i = 0; i < n; ++i) {
s->cur_addr <<= 8;
s->cur_addr |= s->data[i];
}
- if (get_addr_length(s) == 3) {
- s->cur_addr += (s->ear & 0x3) * MAX_3BYTES_SIZE;
- }
+ s->cur_addr &= s->size - 1;
s->state = STATE_IDLE;
@@ -504,6 +605,7 @@ static void complete_collecting_data(Flash *s)
case QPP:
case PP:
case PP4:
+ case PP4_4:
s->state = STATE_PAGE_PROGRAM;
break;
case READ:
@@ -523,11 +625,25 @@ static void complete_collecting_data(Flash *s)
case ERASE_4K:
case ERASE4_4K:
case ERASE_32K:
+ case ERASE4_32K:
case ERASE_SECTOR:
case ERASE4_SECTOR:
flash_erase(s, s->cur_addr, s->cmd_in_progress);
break;
case WRSR:
+ switch (get_man(s)) {
+ case MAN_SPANSION:
+ s->quad_enable = !!(s->data[1] & 0x02);
+ break;
+ case MAN_MACRONIX:
+ s->quad_enable = extract32(s->data[0], 6, 1);
+ if (s->len > 1) {
+ s->four_bytes_address_mode = extract32(s->data[1], 5, 1);
+ }
+ break;
+ default:
+ break;
+ }
if (s->write_enable) {
s->write_enable = false;
}
@@ -561,8 +677,10 @@ static void reset_memory(Flash *s)
s->state = STATE_IDLE;
s->write_enable = false;
s->reset_enable = false;
+ s->quad_enable = false;
- if (((s->pi->jedec >> 16) & 0xFF) == JEDEC_NUMONYX) {
+ switch (get_man(s)) {
+ case MAN_NUMONYX:
s->volatile_cfg = 0;
s->volatile_cfg |= VCFG_DUMMY;
s->volatile_cfg |= VCFG_WRAP_SEQUENTIAL;
@@ -592,16 +710,147 @@ static void reset_memory(Flash *s)
s->four_bytes_address_mode = true;
}
if (!(s->nonvolatile_cfg & NVCFG_LOWER_SEGMENT_MASK)) {
- s->ear = CFG_UPPER_128MB_SEG_ENABLED;
+ s->ear = s->size / MAX_3BYTES_SIZE - 1;
}
+ break;
+ case MAN_MACRONIX:
+ s->volatile_cfg = 0x7;
+ break;
+ case MAN_SPANSION:
+ s->spansion_cr1v = s->spansion_cr1nv;
+ s->spansion_cr2v = s->spansion_cr2nv;
+ s->spansion_cr3v = s->spansion_cr3nv;
+ s->spansion_cr4v = s->spansion_cr4nv;
+ s->quad_enable = extract32(s->spansion_cr1v,
+ SPANSION_QUAD_CFG_POS,
+ SPANSION_QUAD_CFG_LEN
+ );
+ s->four_bytes_address_mode = extract32(s->spansion_cr2v,
+ SPANSION_ADDR_LEN_POS,
+ SPANSION_ADDR_LEN_LEN
+ );
+ break;
+ default:
+ break;
}
DB_PRINT_L(0, "Reset done.\n");
}
+static void decode_fast_read_cmd(Flash *s)
+{
+ s->needed_bytes = get_addr_length(s);
+ switch (get_man(s)) {
+ /* Dummy cycles - modeled with bytes writes instead of bits */
+ case MAN_WINBOND:
+ s->needed_bytes += 8;
+ break;
+ case MAN_NUMONYX:
+ s->needed_bytes += extract32(s->volatile_cfg, 4, 4);
+ break;
+ case MAN_MACRONIX:
+ if (extract32(s->volatile_cfg, 6, 2) == 1) {
+ s->needed_bytes += 6;
+ } else {
+ s->needed_bytes += 8;
+ }
+ break;
+ case MAN_SPANSION:
+ s->needed_bytes += extract32(s->spansion_cr2v,
+ SPANSION_DUMMY_CLK_POS,
+ SPANSION_DUMMY_CLK_LEN
+ );
+ break;
+ default:
+ break;
+ }
+ s->pos = 0;
+ s->len = 0;
+ s->state = STATE_COLLECTING_DATA;
+}
+
+static void decode_dio_read_cmd(Flash *s)
+{
+ s->needed_bytes = get_addr_length(s);
+ /* Dummy cycles modeled with bytes writes instead of bits */
+ switch (get_man(s)) {
+ case MAN_WINBOND:
+ s->needed_bytes += 8;
+ break;
+ case MAN_SPANSION:
+ s->needed_bytes += SPANSION_CONTINUOUS_READ_MODE_CMD_LEN;
+ s->needed_bytes += extract32(s->spansion_cr2v,
+ SPANSION_DUMMY_CLK_POS,
+ SPANSION_DUMMY_CLK_LEN
+ );
+ break;
+ case MAN_NUMONYX:
+ s->needed_bytes += extract32(s->volatile_cfg, 4, 4);
+ break;
+ case MAN_MACRONIX:
+ switch (extract32(s->volatile_cfg, 6, 2)) {
+ case 1:
+ s->needed_bytes += 6;
+ break;
+ case 2:
+ s->needed_bytes += 8;
+ break;
+ default:
+ s->needed_bytes += 4;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ s->pos = 0;
+ s->len = 0;
+ s->state = STATE_COLLECTING_DATA;
+}
+
+static void decode_qio_read_cmd(Flash *s)
+{
+ s->needed_bytes = get_addr_length(s);
+ /* Dummy cycles modeled with bytes writes instead of bits */
+ switch (get_man(s)) {
+ case MAN_WINBOND:
+ s->needed_bytes += 8;
+ break;
+ case MAN_SPANSION:
+ s->needed_bytes += SPANSION_CONTINUOUS_READ_MODE_CMD_LEN;
+ s->needed_bytes += extract32(s->spansion_cr2v,
+ SPANSION_DUMMY_CLK_POS,
+ SPANSION_DUMMY_CLK_LEN
+ );
+ break;
+ case MAN_NUMONYX:
+ s->needed_bytes += extract32(s->volatile_cfg, 4, 4);
+ break;
+ case MAN_MACRONIX:
+ switch (extract32(s->volatile_cfg, 6, 2)) {
+ case 1:
+ s->needed_bytes += 4;
+ break;
+ case 2:
+ s->needed_bytes += 8;
+ break;
+ default:
+ s->needed_bytes += 6;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ s->pos = 0;
+ s->len = 0;
+ s->state = STATE_COLLECTING_DATA;
+}
+
static void decode_new_cmd(Flash *s, uint32_t value)
{
s->cmd_in_progress = value;
+ int i;
DB_PRINT_L(0, "decoded new command:%x\n", value);
if (value != RESET_MEMORY) {
@@ -613,6 +862,7 @@ static void decode_new_cmd(Flash *s, uint32_t value)
case ERASE_4K:
case ERASE4_4K:
case ERASE_32K:
+ case ERASE4_32K:
case ERASE_SECTOR:
case ERASE4_SECTOR:
case READ:
@@ -621,6 +871,7 @@ static void decode_new_cmd(Flash *s, uint32_t value)
case QPP:
case PP:
case PP4:
+ case PP4_4:
s->needed_bytes = get_addr_length(s);
s->pos = 0;
s->len = 0;
@@ -633,56 +884,35 @@ static void decode_new_cmd(Flash *s, uint32_t value)
case DOR4:
case QOR:
case QOR4:
- s->needed_bytes = get_addr_length(s);
- if (((s->pi->jedec >> 16) & 0xFF) == JEDEC_NUMONYX) {
- /* Dummy cycles modeled with bytes writes instead of bits */
- s->needed_bytes += extract32(s->volatile_cfg, 4, 4);
- }
- s->pos = 0;
- s->len = 0;
- s->state = STATE_COLLECTING_DATA;
+ decode_fast_read_cmd(s);
break;
case DIOR:
case DIOR4:
- switch ((s->pi->jedec >> 16) & 0xFF) {
- case JEDEC_WINBOND:
- case JEDEC_SPANSION:
- s->needed_bytes = 4;
- break;
- default:
- s->needed_bytes = get_addr_length(s);
- /* Dummy cycles modeled with bytes writes instead of bits */
- s->needed_bytes += extract32(s->volatile_cfg, 4, 4);
- }
- s->pos = 0;
- s->len = 0;
- s->state = STATE_COLLECTING_DATA;
+ decode_dio_read_cmd(s);
break;
case QIOR:
case QIOR4:
- switch ((s->pi->jedec >> 16) & 0xFF) {
- case JEDEC_WINBOND:
- case JEDEC_SPANSION:
- s->needed_bytes = 6;
- break;
- default:
- s->needed_bytes = get_addr_length(s);
- /* Dummy cycles modeled with bytes writes instead of bits */
- s->needed_bytes += extract32(s->volatile_cfg, 4, 4);
- }
- s->pos = 0;
- s->len = 0;
- s->state = STATE_COLLECTING_DATA;
+ decode_qio_read_cmd(s);
break;
case WRSR:
if (s->write_enable) {
- s->needed_bytes = 1;
+ switch (get_man(s)) {
+ case MAN_SPANSION:
+ s->needed_bytes = 2;
+ s->state = STATE_COLLECTING_DATA;
+ break;
+ case MAN_MACRONIX:
+ s->needed_bytes = 2;
+ s->state = STATE_COLLECTING_VAR_LEN_DATA;
+ break;
+ default:
+ s->needed_bytes = 1;
+ s->state = STATE_COLLECTING_DATA;
+ }
s->pos = 0;
- s->len = 0;
- s->state = STATE_COLLECTING_DATA;
}
break;
@@ -695,6 +925,9 @@ static void decode_new_cmd(Flash *s, uint32_t value)
case RDSR:
s->data[0] = (!!s->write_enable) << 1;
+ if (get_man(s) == MAN_MACRONIX) {
+ s->data[0] |= (!!s->quad_enable) << 6;
+ }
s->pos = 0;
s->len = 1;
s->state = STATE_READING_DATA;
@@ -712,17 +945,20 @@ static void decode_new_cmd(Flash *s, uint32_t value)
case JEDEC_READ:
DB_PRINT_L(0, "populated jedec code\n");
- s->data[0] = (s->pi->jedec >> 16) & 0xff;
- s->data[1] = (s->pi->jedec >> 8) & 0xff;
- s->data[2] = s->pi->jedec & 0xff;
- if (s->pi->ext_jedec) {
- s->data[3] = (s->pi->ext_jedec >> 8) & 0xff;
- s->data[4] = s->pi->ext_jedec & 0xff;
- s->len = 5;
- } else {
- s->len = 3;
+ for (i = 0; i < s->pi->id_len; i++) {
+ s->data[i] = s->pi->id[i];
}
+
+ s->len = s->pi->id_len;
+ s->pos = 0;
+ s->state = STATE_READING_DATA;
+ break;
+
+ case RDCR:
+ s->data[0] = s->volatile_cfg & 0xFF;
+ s->data[0] |= (!!s->four_bytes_address_mode) << 5;
s->pos = 0;
+ s->len = 1;
s->state = STATE_READING_DATA;
break;
@@ -765,7 +1001,7 @@ static void decode_new_cmd(Flash *s, uint32_t value)
s->state = STATE_READING_DATA;
break;
case WNVCR:
- if (s->write_enable) {
+ if (s->write_enable && get_man(s) == MAN_NUMONYX) {
s->needed_bytes = 2;
s->pos = 0;
s->len = 0;
@@ -808,6 +1044,24 @@ static void decode_new_cmd(Flash *s, uint32_t value)
reset_memory(s);
}
break;
+ case RDCR_EQIO:
+ switch (get_man(s)) {
+ case MAN_SPANSION:
+ s->data[0] = (!!s->quad_enable) << 1;
+ s->pos = 0;
+ s->len = 1;
+ s->state = STATE_READING_DATA;
+ break;
+ case MAN_MACRONIX:
+ s->quad_enable = true;
+ break;
+ default:
+ break;
+ }
+ break;
+ case RSTQIO:
+ s->quad_enable = false;
+ break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Unknown cmd %x\n", value);
break;
@@ -819,6 +1073,9 @@ static int m25p80_cs(SSISlave *ss, bool select)
Flash *s = M25P80(ss);
if (select) {
+ if (s->state == STATE_COLLECTING_VAR_LEN_DATA) {
+ complete_collecting_data(s);
+ }
s->len = 0;
s->pos = 0;
s->state = STATE_IDLE;
@@ -838,20 +1095,21 @@ static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx)
switch (s->state) {
case STATE_PAGE_PROGRAM:
- DB_PRINT_L(1, "page program cur_addr=%#" PRIx64 " data=%" PRIx8 "\n",
+ DB_PRINT_L(1, "page program cur_addr=%#" PRIx32 " data=%" PRIx8 "\n",
s->cur_addr, (uint8_t)tx);
flash_write8(s, s->cur_addr, (uint8_t)tx);
- s->cur_addr++;
+ s->cur_addr = (s->cur_addr + 1) & (s->size - 1);
break;
case STATE_READ:
r = s->storage[s->cur_addr];
- DB_PRINT_L(1, "READ 0x%" PRIx64 "=%" PRIx8 "\n", s->cur_addr,
+ DB_PRINT_L(1, "READ 0x%" PRIx32 "=%" PRIx8 "\n", s->cur_addr,
(uint8_t)r);
- s->cur_addr = (s->cur_addr + 1) % s->size;
+ s->cur_addr = (s->cur_addr + 1) & (s->size - 1);
break;
case STATE_COLLECTING_DATA:
+ case STATE_COLLECTING_VAR_LEN_DATA:
s->data[s->len] = (uint8_t)tx;
s->len++;
@@ -878,9 +1136,8 @@ static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx)
return r;
}
-static int m25p80_init(SSISlave *ss)
+static void m25p80_realize(SSISlave *ss, Error **errp)
{
- DriveInfo *dinfo;
Flash *s = M25P80(ss);
M25P80Class *mc = M25P80_GET_CLASS(s);
@@ -889,28 +1146,19 @@ static int m25p80_init(SSISlave *ss)
s->size = s->pi->sector_size * s->pi->n_sectors;
s->dirty_page = -1;
- /* FIXME use a qdev drive property instead of drive_get_next() */
- dinfo = drive_get_next(IF_MTD);
-
- if (dinfo) {
+ if (s->blk) {
DB_PRINT_L(0, "Binding to IF_MTD drive\n");
- s->blk = blk_by_legacy_dinfo(dinfo);
- blk_attach_dev_nofail(s->blk, s);
-
s->storage = blk_blockalign(s->blk, s->size);
- /* FIXME: Move to late init */
if (blk_pread(s->blk, 0, s->storage, s->size) != s->size) {
- fprintf(stderr, "Failed to initialize SPI flash!\n");
- return 1;
+ error_setg(errp, "failed to read the initial flash content");
+ return;
}
} else {
DB_PRINT_L(0, "No BDRV - binding to RAM\n");
s->storage = blk_blockalign(NULL, s->size);
memset(s->storage, 0xFF, s->size);
}
-
- return 0;
}
static void m25p80_reset(DeviceState *d)
@@ -926,13 +1174,19 @@ static void m25p80_pre_save(void *opaque)
}
static Property m25p80_properties[] = {
+ /* This is default value for Micron flash */
DEFINE_PROP_UINT32("nonvolatile-cfg", Flash, nonvolatile_cfg, 0x8FFF),
+ DEFINE_PROP_UINT8("spansion-cr1nv", Flash, spansion_cr1nv, 0x0),
+ DEFINE_PROP_UINT8("spansion-cr2nv", Flash, spansion_cr2nv, 0x8),
+ DEFINE_PROP_UINT8("spansion-cr3nv", Flash, spansion_cr3nv, 0x2),
+ DEFINE_PROP_UINT8("spansion-cr4nv", Flash, spansion_cr4nv, 0x10),
+ DEFINE_PROP_DRIVE("drive", Flash, blk),
DEFINE_PROP_END_OF_LIST(),
};
static const VMStateDescription vmstate_m25p80 = {
.name = "xilinx_spi",
- .version_id = 2,
+ .version_id = 3,
.minimum_version_id = 1,
.pre_save = m25p80_pre_save,
.fields = (VMStateField[]) {
@@ -942,7 +1196,8 @@ static const VMStateDescription vmstate_m25p80 = {
VMSTATE_UINT32(pos, Flash),
VMSTATE_UINT8(needed_bytes, Flash),
VMSTATE_UINT8(cmd_in_progress, Flash),
- VMSTATE_UINT64(cur_addr, Flash),
+ VMSTATE_UNUSED(4),
+ VMSTATE_UINT32(cur_addr, Flash),
VMSTATE_BOOL(write_enable, Flash),
VMSTATE_BOOL_V(reset_enable, Flash, 2),
VMSTATE_UINT8_V(ear, Flash, 2),
@@ -950,6 +1205,11 @@ static const VMStateDescription vmstate_m25p80 = {
VMSTATE_UINT32_V(nonvolatile_cfg, Flash, 2),
VMSTATE_UINT32_V(volatile_cfg, Flash, 2),
VMSTATE_UINT32_V(enh_volatile_cfg, Flash, 2),
+ VMSTATE_BOOL_V(quad_enable, Flash, 3),
+ VMSTATE_UINT8_V(spansion_cr1nv, Flash, 3),
+ VMSTATE_UINT8_V(spansion_cr2nv, Flash, 3),
+ VMSTATE_UINT8_V(spansion_cr3nv, Flash, 3),
+ VMSTATE_UINT8_V(spansion_cr4nv, Flash, 3),
VMSTATE_END_OF_LIST()
}
};
@@ -960,7 +1220,7 @@ static void m25p80_class_init(ObjectClass *klass, void *data)
SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
M25P80Class *mc = M25P80_CLASS(klass);
- k->init = m25p80_init;
+ k->realize = m25p80_realize;
k->transfer = m25p80_transfer8;
k->set_cs = m25p80_cs;
k->cs_polarity = SSI_CS_LOW;
diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c
index 31585e3aa1..62d7a5661d 100644
--- a/hw/block/pflash_cfi01.c
+++ b/hw/block/pflash_cfi01.c
@@ -65,7 +65,6 @@ do { \
#define DPRINTF(fmt, ...) do { } while (0)
#endif
-#define TYPE_CFI_PFLASH01 "cfi.pflash01"
#define CFI_PFLASH01(obj) OBJECT_CHECK(pflash_t, (obj), TYPE_CFI_PFLASH01)
#define PFLASH_BE 0
diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c
index 5f106102c5..4f6105cc58 100644
--- a/hw/block/pflash_cfi02.c
+++ b/hw/block/pflash_cfi02.c
@@ -57,7 +57,6 @@ do { \
#define PFLASH_LAZY_ROMD_THRESHOLD 42
-#define TYPE_CFI_PFLASH02 "cfi.pflash02"
#define CFI_PFLASH02(obj) OBJECT_CHECK(pflash_t, (obj), TYPE_CFI_PFLASH02)
struct pflash_t {
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
index 284e64667c..fb43bbaa46 100644
--- a/hw/block/virtio-blk.c
+++ b/hw/block/virtio-blk.c
@@ -29,9 +29,11 @@
#include "hw/virtio/virtio-bus.h"
#include "hw/virtio/virtio-access.h"
-void virtio_blk_init_request(VirtIOBlock *s, VirtIOBlockReq *req)
+void virtio_blk_init_request(VirtIOBlock *s, VirtQueue *vq,
+ VirtIOBlockReq *req)
{
req->dev = s;
+ req->vq = vq;
req->qiov.size = 0;
req->in_len = 0;
req->next = NULL;
@@ -53,11 +55,11 @@ static void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status)
trace_virtio_blk_req_complete(req, status);
stb_p(&req->in->status, status);
- virtqueue_push(s->vq, &req->elem, req->in_len);
+ virtqueue_push(req->vq, &req->elem, req->in_len);
if (s->dataplane_started && !s->dataplane_disabled) {
- virtio_blk_data_plane_notify(s->dataplane);
+ virtio_blk_data_plane_notify(s->dataplane, req->vq);
} else {
- virtio_notify(vdev, s->vq);
+ virtio_notify(vdev, req->vq);
}
}
@@ -187,12 +189,12 @@ out:
#endif
-static VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s)
+static VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s, VirtQueue *vq)
{
- VirtIOBlockReq *req = virtqueue_pop(s->vq, sizeof(VirtIOBlockReq));
+ VirtIOBlockReq *req = virtqueue_pop(vq, sizeof(VirtIOBlockReq));
if (req) {
- virtio_blk_init_request(s, req);
+ virtio_blk_init_request(s, vq, req);
}
return req;
}
@@ -583,7 +585,7 @@ void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq)
blk_io_plug(s->blk);
- while ((req = virtio_blk_get_request(s))) {
+ while ((req = virtio_blk_get_request(s, vq))) {
virtio_blk_handle_request(req, &mrb);
}
@@ -708,6 +710,7 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config)
blkcfg.physical_block_exp = get_physical_block_exp(conf);
blkcfg.alignment_offset = 0;
blkcfg.wce = blk_enable_write_cache(s->blk);
+ virtio_stw_p(vdev, &blkcfg.num_queues, s->conf.num_queues);
memcpy(config, &blkcfg, sizeof(struct virtio_blk_config));
}
@@ -751,6 +754,9 @@ static uint64_t virtio_blk_get_features(VirtIODevice *vdev, uint64_t features,
if (blk_is_read_only(s->blk)) {
virtio_add_feature(&features, VIRTIO_BLK_F_RO);
}
+ if (s->conf.num_queues > 1) {
+ virtio_add_feature(&features, VIRTIO_BLK_F_MQ);
+ }
return features;
}
@@ -795,11 +801,6 @@ static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status)
static void virtio_blk_save(QEMUFile *f, void *opaque)
{
VirtIODevice *vdev = VIRTIO_DEVICE(opaque);
- VirtIOBlock *s = VIRTIO_BLK(vdev);
-
- if (s->dataplane) {
- virtio_blk_data_plane_stop(s->dataplane);
- }
virtio_save(vdev, f);
}
@@ -811,6 +812,11 @@ static void virtio_blk_save_device(VirtIODevice *vdev, QEMUFile *f)
while (req) {
qemu_put_sbyte(f, 1);
+
+ if (s->conf.num_queues > 1) {
+ qemu_put_be32(f, virtio_get_queue_index(req->vq));
+ }
+
qemu_put_virtqueue_element(f, &req->elem);
req = req->next;
}
@@ -834,9 +840,22 @@ static int virtio_blk_load_device(VirtIODevice *vdev, QEMUFile *f,
VirtIOBlock *s = VIRTIO_BLK(vdev);
while (qemu_get_sbyte(f)) {
+ unsigned nvqs = s->conf.num_queues;
+ unsigned vq_idx = 0;
VirtIOBlockReq *req;
+
+ if (nvqs > 1) {
+ vq_idx = qemu_get_be32(f);
+
+ if (vq_idx >= nvqs) {
+ error_report("Invalid virtqueue index in request list: %#x",
+ vq_idx);
+ return -EINVAL;
+ }
+ }
+
req = qemu_get_virtqueue_element(f, sizeof(VirtIOBlockReq));
- virtio_blk_init_request(s, req);
+ virtio_blk_init_request(s, virtio_get_queue(vdev, vq_idx), req);
req->next = s->rq;
s->rq = req;
}
@@ -862,6 +881,7 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
VirtIOBlkConf *conf = &s->conf;
Error *err = NULL;
static int virtio_blk_id;
+ unsigned i;
if (!conf->conf.blk) {
error_setg(errp, "drive property not set");
@@ -871,6 +891,10 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
error_setg(errp, "Device needs media, but drive is empty");
return;
}
+ if (!conf->num_queues) {
+ error_setg(errp, "num-queues property must be larger than 0");
+ return;
+ }
blkconf_serial(&conf->conf, &conf->serial);
s->original_wce = blk_enable_write_cache(conf->conf.blk);
@@ -888,7 +912,9 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
s->rq = NULL;
s->sector_mask = (s->conf.conf.logical_block_size / BDRV_SECTOR_SIZE) - 1;
- s->vq = virtio_add_queue(vdev, 128, virtio_blk_handle_output);
+ for (i = 0; i < conf->num_queues; i++) {
+ virtio_add_queue(vdev, 128, virtio_blk_handle_output);
+ }
virtio_blk_data_plane_create(vdev, conf, &s->dataplane, &err);
if (err != NULL) {
error_propagate(errp, err);
@@ -941,6 +967,7 @@ static Property virtio_blk_properties[] = {
#endif
DEFINE_PROP_BIT("request-merging", VirtIOBlock, conf.request_merging, 0,
true),
+ DEFINE_PROP_UINT16("num-queues", VirtIOBlock, conf.num_queues, 1),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
index c856fc30b2..e3bc52f7df 100644
--- a/hw/char/cadence_uart.c
+++ b/hw/char/cadence_uart.c
@@ -288,13 +288,19 @@ static gboolean cadence_uart_xmit(GIOChannel *chan, GIOCondition cond,
}
ret = qemu_chr_fe_write(s->chr, s->tx_fifo, s->tx_count);
- s->tx_count -= ret;
- memmove(s->tx_fifo, s->tx_fifo + ret, s->tx_count);
+
+ if (ret >= 0) {
+ s->tx_count -= ret;
+ memmove(s->tx_fifo, s->tx_fifo + ret, s->tx_count);
+ }
if (s->tx_count) {
- int r = qemu_chr_fe_add_watch(s->chr, G_IO_OUT|G_IO_HUP,
- cadence_uart_xmit, s);
- assert(r);
+ guint r = qemu_chr_fe_add_watch(s->chr, G_IO_OUT|G_IO_HUP,
+ cadence_uart_xmit, s);
+ if (!r) {
+ s->tx_count = 0;
+ return FALSE;
+ }
}
uart_update_status(s);
diff --git a/hw/char/serial.c b/hw/char/serial.c
index 6d815b5c69..3442f47d36 100644
--- a/hw/char/serial.c
+++ b/hw/char/serial.c
@@ -106,6 +106,7 @@ do {} while (0)
#endif
static void serial_receive1(void *opaque, const uint8_t *buf, int size);
+static void serial_xmit(SerialState *s);
static inline void recv_fifo_put(SerialState *s, uint8_t chr)
{
@@ -223,13 +224,20 @@ static void serial_update_msl(SerialState *s)
}
}
-static gboolean serial_xmit(GIOChannel *chan, GIOCondition cond, void *opaque)
+static gboolean serial_watch_cb(GIOChannel *chan, GIOCondition cond,
+ void *opaque)
{
SerialState *s = opaque;
+ s->watch_tag = 0;
+ serial_xmit(s);
+ return FALSE;
+}
+static void serial_xmit(SerialState *s)
+{
do {
assert(!(s->lsr & UART_LSR_TEMT));
- if (s->tsr_retry <= 0) {
+ if (s->tsr_retry == 0) {
assert(!(s->lsr & UART_LSR_THRE));
if (s->fcr & UART_FCR_FE) {
@@ -251,17 +259,17 @@ static gboolean serial_xmit(GIOChannel *chan, GIOCondition cond, void *opaque)
if (s->mcr & UART_MCR_LOOP) {
/* in loopback mode, say that we just received a char */
serial_receive1(s, &s->tsr, 1);
- } else if (qemu_chr_fe_write(s->chr, &s->tsr, 1) != 1) {
- if (s->tsr_retry >= 0 && s->tsr_retry < MAX_XMIT_RETRY &&
- qemu_chr_fe_add_watch(s->chr, G_IO_OUT|G_IO_HUP,
- serial_xmit, s) > 0) {
+ } else if (qemu_chr_fe_write(s->chr, &s->tsr, 1) != 1 &&
+ s->tsr_retry < MAX_XMIT_RETRY) {
+ assert(s->watch_tag == 0);
+ s->watch_tag = qemu_chr_fe_add_watch(s->chr, G_IO_OUT|G_IO_HUP,
+ serial_watch_cb, s);
+ if (s->watch_tag > 0) {
s->tsr_retry++;
- return FALSE;
+ return;
}
- s->tsr_retry = 0;
- } else {
- s->tsr_retry = 0;
}
+ s->tsr_retry = 0;
/* Transmit another byte if it is already available. It is only
possible when FIFO is enabled and not empty. */
@@ -269,11 +277,8 @@ static gboolean serial_xmit(GIOChannel *chan, GIOCondition cond, void *opaque)
s->last_xmit_ts = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
s->lsr |= UART_LSR_TEMT;
-
- return FALSE;
}
-
/* Setter for FCR.
is_load flag means, that value is set while loading VM state
and interrupt should not be invoked */
@@ -330,8 +335,8 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val,
s->lsr &= ~UART_LSR_THRE;
s->lsr &= ~UART_LSR_TEMT;
serial_update_irq(s);
- if (s->tsr_retry <= 0) {
- serial_xmit(NULL, G_IO_OUT, s);
+ if (s->tsr_retry == 0) {
+ serial_xmit(s);
}
}
break;
@@ -639,6 +644,31 @@ static int serial_post_load(void *opaque, int version_id)
if (s->thr_ipending == -1) {
s->thr_ipending = ((s->iir & UART_IIR_ID) == UART_IIR_THRI);
}
+
+ if (s->tsr_retry > 0) {
+ /* tsr_retry > 0 implies LSR.TEMT = 0 (transmitter not empty). */
+ if (s->lsr & UART_LSR_TEMT) {
+ error_report("inconsistent state in serial device "
+ "(tsr empty, tsr_retry=%d", s->tsr_retry);
+ return -1;
+ }
+
+ if (s->tsr_retry > MAX_XMIT_RETRY) {
+ s->tsr_retry = MAX_XMIT_RETRY;
+ }
+
+ assert(s->watch_tag == 0);
+ s->watch_tag = qemu_chr_fe_add_watch(s->chr, G_IO_OUT|G_IO_HUP,
+ serial_watch_cb, s);
+ } else {
+ /* tsr_retry == 0 implies LSR.TEMT = 1 (transmitter empty). */
+ if (!(s->lsr & UART_LSR_TEMT)) {
+ error_report("inconsistent state in serial device "
+ "(tsr not empty, tsr_retry=0");
+ return -1;
+ }
+ }
+
s->last_break_enable = (s->lcr >> 6) & 1;
/* Initialize fcr via setter to perform essential side-effects */
serial_write_fcr(s, s->fcr_vmstate);
@@ -685,7 +715,7 @@ static const VMStateDescription vmstate_serial_tsr = {
.minimum_version_id = 1,
.needed = serial_tsr_needed,
.fields = (VMStateField[]) {
- VMSTATE_INT32(tsr_retry, SerialState),
+ VMSTATE_UINT32(tsr_retry, SerialState),
VMSTATE_UINT8(thr, SerialState),
VMSTATE_UINT8(tsr, SerialState),
VMSTATE_END_OF_LIST()
@@ -815,6 +845,11 @@ static void serial_reset(void *opaque)
{
SerialState *s = opaque;
+ if (s->watch_tag > 0) {
+ g_source_remove(s->watch_tag);
+ s->watch_tag = 0;
+ }
+
s->rbr = 0;
s->ier = 0;
s->iir = UART_IIR_NO_INT;
diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs
index 82a9ef84f8..cfd4840397 100644
--- a/hw/core/Makefile.objs
+++ b/hw/core/Makefile.objs
@@ -15,4 +15,5 @@ common-obj-$(CONFIG_SOFTMMU) += machine.o
common-obj-$(CONFIG_SOFTMMU) += null-machine.o
common-obj-$(CONFIG_SOFTMMU) += loader.o
common-obj-$(CONFIG_SOFTMMU) += qdev-properties-system.o
+common-obj-$(CONFIG_SOFTMMU) += register.o
common-obj-$(CONFIG_PLATFORM_BUS) += platform-bus.o
diff --git a/hw/core/register.c b/hw/core/register.c
new file mode 100644
index 0000000000..4bfbc508de
--- /dev/null
+++ b/hw/core/register.c
@@ -0,0 +1,287 @@
+/*
+ * Register Definition API
+ *
+ * Copyright (c) 2016 Xilinx Inc.
+ * Copyright (c) 2013 Peter Crosthwaite <peter.crosthwaite@xilinx.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/register.h"
+#include "hw/qdev.h"
+#include "qemu/log.h"
+
+static inline void register_write_val(RegisterInfo *reg, uint64_t val)
+{
+ g_assert(reg->data);
+
+ switch (reg->data_size) {
+ case 1:
+ *(uint8_t *)reg->data = val;
+ break;
+ case 2:
+ *(uint16_t *)reg->data = val;
+ break;
+ case 4:
+ *(uint32_t *)reg->data = val;
+ break;
+ case 8:
+ *(uint64_t *)reg->data = val;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static inline uint64_t register_read_val(RegisterInfo *reg)
+{
+ switch (reg->data_size) {
+ case 1:
+ return *(uint8_t *)reg->data;
+ case 2:
+ return *(uint16_t *)reg->data;
+ case 4:
+ return *(uint32_t *)reg->data;
+ case 8:
+ return *(uint64_t *)reg->data;
+ default:
+ g_assert_not_reached();
+ }
+ return 0; /* unreachable */
+}
+
+void register_write(RegisterInfo *reg, uint64_t val, uint64_t we,
+ const char *prefix, bool debug)
+{
+ uint64_t old_val, new_val, test, no_w_mask;
+ const RegisterAccessInfo *ac;
+
+ assert(reg);
+
+ ac = reg->access;
+
+ if (!ac || !ac->name) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: write to undefined device state "
+ "(written value: %#" PRIx64 ")\n", prefix, val);
+ return;
+ }
+
+ old_val = reg->data ? register_read_val(reg) : ac->reset;
+
+ test = (old_val ^ val) & ac->rsvd;
+ if (test) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: change of value in reserved bit"
+ "fields: %#" PRIx64 ")\n", prefix, test);
+ }
+
+ test = val & ac->unimp;
+ if (test) {
+ qemu_log_mask(LOG_UNIMP,
+ "%s:%s writing %#" PRIx64 " to unimplemented bits:" \
+ " %#" PRIx64 "",
+ prefix, reg->access->name, val, ac->unimp);
+ }
+
+ /* Create the no write mask based on the read only, write to clear and
+ * reserved bit masks.
+ */
+ no_w_mask = ac->ro | ac->w1c | ac->rsvd | ~we;
+ new_val = (val & ~no_w_mask) | (old_val & no_w_mask);
+ new_val &= ~(val & ac->w1c);
+
+ if (ac->pre_write) {
+ new_val = ac->pre_write(reg, new_val);
+ }
+
+ if (debug) {
+ qemu_log("%s:%s: write of value %#" PRIx64 "\n", prefix, ac->name,
+ new_val);
+ }
+
+ register_write_val(reg, new_val);
+
+ if (ac->post_write) {
+ ac->post_write(reg, new_val);
+ }
+}
+
+uint64_t register_read(RegisterInfo *reg, uint64_t re, const char* prefix,
+ bool debug)
+{
+ uint64_t ret;
+ const RegisterAccessInfo *ac;
+
+ assert(reg);
+
+ ac = reg->access;
+ if (!ac || !ac->name) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: read from undefined device state\n",
+ prefix);
+ return 0;
+ }
+
+ ret = reg->data ? register_read_val(reg) : ac->reset;
+
+ register_write_val(reg, ret & ~(ac->cor & re));
+
+ /* Mask based on the read enable size */
+ ret &= re;
+
+ if (ac->post_read) {
+ ret = ac->post_read(reg, ret);
+ }
+
+ if (debug) {
+ qemu_log("%s:%s: read of value %#" PRIx64 "\n", prefix,
+ ac->name, ret);
+ }
+
+ return ret;
+}
+
+void register_reset(RegisterInfo *reg)
+{
+ g_assert(reg);
+
+ if (!reg->data || !reg->access) {
+ return;
+ }
+
+ register_write_val(reg, reg->access->reset);
+}
+
+void register_init(RegisterInfo *reg)
+{
+ assert(reg);
+
+ if (!reg->data || !reg->access) {
+ return;
+ }
+
+ object_initialize((void *)reg, sizeof(*reg), TYPE_REGISTER);
+}
+
+void register_write_memory(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ RegisterInfoArray *reg_array = opaque;
+ RegisterInfo *reg = NULL;
+ uint64_t we;
+ int i;
+
+ for (i = 0; i < reg_array->num_elements; i++) {
+ if (reg_array->r[i]->access->addr == addr) {
+ reg = reg_array->r[i];
+ break;
+ }
+ }
+
+ if (!reg) {
+ qemu_log_mask(LOG_GUEST_ERROR, "Write to unimplemented register at " \
+ "address: %#" PRIx64 "\n", addr);
+ return;
+ }
+
+ /* Generate appropriate write enable mask */
+ if (reg->data_size < size) {
+ we = MAKE_64BIT_MASK(0, reg->data_size * 8);
+ } else {
+ we = MAKE_64BIT_MASK(0, size * 8);
+ }
+
+ register_write(reg, value, we, reg_array->prefix,
+ reg_array->debug);
+}
+
+uint64_t register_read_memory(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ RegisterInfoArray *reg_array = opaque;
+ RegisterInfo *reg = NULL;
+ uint64_t read_val;
+ int i;
+
+ for (i = 0; i < reg_array->num_elements; i++) {
+ if (reg_array->r[i]->access->addr == addr) {
+ reg = reg_array->r[i];
+ break;
+ }
+ }
+
+ if (!reg) {
+ qemu_log_mask(LOG_GUEST_ERROR, "Read to unimplemented register at " \
+ "address: %#" PRIx64 "\n", addr);
+ return 0;
+ }
+
+ read_val = register_read(reg, size * 8, reg_array->prefix,
+ reg_array->debug);
+
+ return extract64(read_val, 0, size * 8);
+}
+
+RegisterInfoArray *register_init_block32(DeviceState *owner,
+ const RegisterAccessInfo *rae,
+ int num, RegisterInfo *ri,
+ uint32_t *data,
+ const MemoryRegionOps *ops,
+ bool debug_enabled,
+ uint64_t memory_size)
+{
+ const char *device_prefix = object_get_typename(OBJECT(owner));
+ RegisterInfoArray *r_array = g_new0(RegisterInfoArray, 1);
+ int i;
+
+ r_array->r = g_new0(RegisterInfo *, num);
+ r_array->num_elements = num;
+ r_array->debug = debug_enabled;
+ r_array->prefix = device_prefix;
+
+ for (i = 0; i < num; i++) {
+ int index = rae[i].addr / 4;
+ RegisterInfo *r = &ri[index];
+
+ *r = (RegisterInfo) {
+ .data = &data[index],
+ .data_size = sizeof(uint32_t),
+ .access = &rae[i],
+ .opaque = owner,
+ };
+ register_init(r);
+
+ r_array->r[i] = r;
+ }
+
+ memory_region_init_io(&r_array->mem, OBJECT(owner), ops, r_array,
+ device_prefix, memory_size);
+
+ return r_array;
+}
+
+void register_finalize_block(RegisterInfoArray *r_array)
+{
+ object_unparent(OBJECT(&r_array->mem));
+ g_free(r_array->r);
+ g_free(r_array);
+}
+
+static const TypeInfo register_info = {
+ .name = TYPE_REGISTER,
+ .parent = TYPE_DEVICE,
+};
+
+static void register_register_types(void)
+{
+ type_register_static(&register_info);
+}
+
+type_init(register_register_types)
diff --git a/hw/display/ads7846.c b/hw/display/ads7846.c
index 05aa2d1e6b..166edade7d 100644
--- a/hw/display/ads7846.c
+++ b/hw/display/ads7846.c
@@ -133,7 +133,7 @@ static const VMStateDescription vmstate_ads7846 = {
}
};
-static int ads7846_init(SSISlave *d)
+static void ads7846_realize(SSISlave *d, Error **errp)
{
DeviceState *dev = DEVICE(d);
ADS7846State *s = FROM_SSI_SLAVE(ADS7846State, d);
@@ -152,14 +152,13 @@ static int ads7846_init(SSISlave *d)
ads7846_int_update(s);
vmstate_register(NULL, -1, &vmstate_ads7846, s);
- return 0;
}
static void ads7846_class_init(ObjectClass *klass, void *data)
{
SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
- k->init = ads7846_init;
+ k->realize = ads7846_realize;
k->transfer = ads7846_transfer;
}
diff --git a/hw/display/ssd0323.c b/hw/display/ssd0323.c
index 14c1bf339c..6d1faf44af 100644
--- a/hw/display/ssd0323.c
+++ b/hw/display/ssd0323.c
@@ -361,7 +361,7 @@ static const GraphicHwOps ssd0323_ops = {
.gfx_update = ssd0323_update_display,
};
-static int ssd0323_init(SSISlave *d)
+static void ssd0323_realize(SSISlave *d, Error **errp)
{
DeviceState *dev = DEVICE(d);
ssd0323_state *s = FROM_SSI_SLAVE(ssd0323_state, d);
@@ -375,14 +375,13 @@ static int ssd0323_init(SSISlave *d)
register_savevm(dev, "ssd0323_oled", -1, 1,
ssd0323_save, ssd0323_load, s);
- return 0;
}
static void ssd0323_class_init(ObjectClass *klass, void *data)
{
SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
- k->init = ssd0323_init;
+ k->realize = ssd0323_realize;
k->transfer = ssd0323_transfer;
k->cs_polarity = SSI_CS_HIGH;
}
diff --git a/hw/dma/Makefile.objs b/hw/dma/Makefile.objs
index 8b0823e593..087c8e6855 100644
--- a/hw/dma/Makefile.objs
+++ b/hw/dma/Makefile.objs
@@ -5,6 +5,7 @@ common-obj-$(CONFIG_PL330) += pl330.o
common-obj-$(CONFIG_I82374) += i82374.o
common-obj-$(CONFIG_I8257) += i8257.o
common-obj-$(CONFIG_XILINX_AXI) += xilinx_axidma.o
+common-obj-$(CONFIG_ZYNQ_DEVCFG) += xlnx-zynq-devcfg.o
common-obj-$(CONFIG_ETRAXFS) += etraxfs_dma.o
common-obj-$(CONFIG_STP2000) += sparc32_dma.o
common-obj-$(CONFIG_SUN4M) += sun4m_iommu.o
diff --git a/hw/dma/xlnx-zynq-devcfg.c b/hw/dma/xlnx-zynq-devcfg.c
new file mode 100644
index 0000000000..3b10523430
--- /dev/null
+++ b/hw/dma/xlnx-zynq-devcfg.c
@@ -0,0 +1,400 @@
+/*
+ * QEMU model of the Xilinx Zynq Devcfg Interface
+ *
+ * (C) 2011 PetaLogix Pty Ltd
+ * (C) 2014 Xilinx Inc.
+ * Written by Peter Crosthwaite <peter.crosthwaite@xilinx.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/dma/xlnx-zynq-devcfg.h"
+#include "qemu/bitops.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/dma.h"
+#include "qemu/log.h"
+
+#define FREQ_HZ 900000000
+
+#define BTT_MAX 0x400
+
+#ifndef XLNX_ZYNQ_DEVCFG_ERR_DEBUG
+#define XLNX_ZYNQ_DEVCFG_ERR_DEBUG 0
+#endif
+
+#define DB_PRINT(fmt, args...) do { \
+ if (XLNX_ZYNQ_DEVCFG_ERR_DEBUG) { \
+ qemu_log("%s: " fmt, __func__, ## args); \
+ } \
+} while (0);
+
+REG32(CTRL, 0x00)
+ FIELD(CTRL, FORCE_RST, 31, 1) /* Not supported, wr ignored */
+ FIELD(CTRL, PCAP_PR, 27, 1) /* Forced to 0 on bad unlock */
+ FIELD(CTRL, PCAP_MODE, 26, 1)
+ FIELD(CTRL, MULTIBOOT_EN, 24, 1)
+ FIELD(CTRL, USER_MODE, 15, 1)
+ FIELD(CTRL, PCFG_AES_FUSE, 12, 1)
+ FIELD(CTRL, PCFG_AES_EN, 9, 3)
+ FIELD(CTRL, SEU_EN, 8, 1)
+ FIELD(CTRL, SEC_EN, 7, 1)
+ FIELD(CTRL, SPNIDEN, 6, 1)
+ FIELD(CTRL, SPIDEN, 5, 1)
+ FIELD(CTRL, NIDEN, 4, 1)
+ FIELD(CTRL, DBGEN, 3, 1)
+ FIELD(CTRL, DAP_EN, 0, 3)
+
+REG32(LOCK, 0x04)
+#define AES_FUSE_LOCK 4
+#define AES_EN_LOCK 3
+#define SEU_LOCK 2
+#define SEC_LOCK 1
+#define DBG_LOCK 0
+
+/* mapping bits in R_LOCK to what they lock in R_CTRL */
+static const uint32_t lock_ctrl_map[] = {
+ [AES_FUSE_LOCK] = R_CTRL_PCFG_AES_FUSE_MASK,
+ [AES_EN_LOCK] = R_CTRL_PCFG_AES_EN_MASK,
+ [SEU_LOCK] = R_CTRL_SEU_EN_MASK,
+ [SEC_LOCK] = R_CTRL_SEC_EN_MASK,
+ [DBG_LOCK] = R_CTRL_SPNIDEN_MASK | R_CTRL_SPIDEN_MASK |
+ R_CTRL_NIDEN_MASK | R_CTRL_DBGEN_MASK |
+ R_CTRL_DAP_EN_MASK,
+};
+
+REG32(CFG, 0x08)
+ FIELD(CFG, RFIFO_TH, 10, 2)
+ FIELD(CFG, WFIFO_TH, 8, 2)
+ FIELD(CFG, RCLK_EDGE, 7, 1)
+ FIELD(CFG, WCLK_EDGE, 6, 1)
+ FIELD(CFG, DISABLE_SRC_INC, 5, 1)
+ FIELD(CFG, DISABLE_DST_INC, 4, 1)
+#define R_CFG_RESET 0x50B
+
+REG32(INT_STS, 0x0C)
+ FIELD(INT_STS, PSS_GTS_USR_B, 31, 1)
+ FIELD(INT_STS, PSS_FST_CFG_B, 30, 1)
+ FIELD(INT_STS, PSS_CFG_RESET_B, 27, 1)
+ FIELD(INT_STS, RX_FIFO_OV, 18, 1)
+ FIELD(INT_STS, WR_FIFO_LVL, 17, 1)
+ FIELD(INT_STS, RD_FIFO_LVL, 16, 1)
+ FIELD(INT_STS, DMA_CMD_ERR, 15, 1)
+ FIELD(INT_STS, DMA_Q_OV, 14, 1)
+ FIELD(INT_STS, DMA_DONE, 13, 1)
+ FIELD(INT_STS, DMA_P_DONE, 12, 1)
+ FIELD(INT_STS, P2D_LEN_ERR, 11, 1)
+ FIELD(INT_STS, PCFG_DONE, 2, 1)
+#define R_INT_STS_RSVD ((0x7 << 24) | (0x1 << 19) | (0xF < 7))
+
+REG32(INT_MASK, 0x10)
+
+REG32(STATUS, 0x14)
+ FIELD(STATUS, DMA_CMD_Q_F, 31, 1)
+ FIELD(STATUS, DMA_CMD_Q_E, 30, 1)
+ FIELD(STATUS, DMA_DONE_CNT, 28, 2)
+ FIELD(STATUS, RX_FIFO_LVL, 20, 5)
+ FIELD(STATUS, TX_FIFO_LVL, 12, 7)
+ FIELD(STATUS, PSS_GTS_USR_B, 11, 1)
+ FIELD(STATUS, PSS_FST_CFG_B, 10, 1)
+ FIELD(STATUS, PSS_CFG_RESET_B, 5, 1)
+
+REG32(DMA_SRC_ADDR, 0x18)
+REG32(DMA_DST_ADDR, 0x1C)
+REG32(DMA_SRC_LEN, 0x20)
+REG32(DMA_DST_LEN, 0x24)
+REG32(ROM_SHADOW, 0x28)
+REG32(SW_ID, 0x30)
+REG32(UNLOCK, 0x34)
+
+#define R_UNLOCK_MAGIC 0x757BDF0D
+
+REG32(MCTRL, 0x80)
+ FIELD(MCTRL, PS_VERSION, 28, 4)
+ FIELD(MCTRL, PCFG_POR_B, 8, 1)
+ FIELD(MCTRL, INT_PCAP_LPBK, 4, 1)
+ FIELD(MCTRL, QEMU, 3, 1)
+
+static void xlnx_zynq_devcfg_update_ixr(XlnxZynqDevcfg *s)
+{
+ qemu_set_irq(s->irq, ~s->regs[R_INT_MASK] & s->regs[R_INT_STS]);
+}
+
+static void xlnx_zynq_devcfg_reset(DeviceState *dev)
+{
+ XlnxZynqDevcfg *s = XLNX_ZYNQ_DEVCFG(dev);
+ int i;
+
+ for (i = 0; i < XLNX_ZYNQ_DEVCFG_R_MAX; ++i) {
+ register_reset(&s->regs_info[i]);
+ }
+}
+
+static void xlnx_zynq_devcfg_dma_go(XlnxZynqDevcfg *s)
+{
+ do {
+ uint8_t buf[BTT_MAX];
+ XlnxZynqDevcfgDMACmd *dmah = s->dma_cmd_fifo;
+ uint32_t btt = BTT_MAX;
+ bool loopback = s->regs[R_MCTRL] & R_MCTRL_INT_PCAP_LPBK_MASK;
+
+ btt = MIN(btt, dmah->src_len);
+ if (loopback) {
+ btt = MIN(btt, dmah->dest_len);
+ }
+ DB_PRINT("reading %x bytes from %x\n", btt, dmah->src_addr);
+ dma_memory_read(&address_space_memory, dmah->src_addr, buf, btt);
+ dmah->src_len -= btt;
+ dmah->src_addr += btt;
+ if (loopback && (dmah->src_len || dmah->dest_len)) {
+ DB_PRINT("writing %x bytes from %x\n", btt, dmah->dest_addr);
+ dma_memory_write(&address_space_memory, dmah->dest_addr, buf, btt);
+ dmah->dest_len -= btt;
+ dmah->dest_addr += btt;
+ }
+ if (!dmah->src_len && !dmah->dest_len) {
+ DB_PRINT("dma operation finished\n");
+ s->regs[R_INT_STS] |= R_INT_STS_DMA_DONE_MASK |
+ R_INT_STS_DMA_P_DONE_MASK;
+ s->dma_cmd_fifo_num--;
+ memmove(s->dma_cmd_fifo, &s->dma_cmd_fifo[1],
+ sizeof(s->dma_cmd_fifo) - sizeof(s->dma_cmd_fifo[0]));
+ }
+ xlnx_zynq_devcfg_update_ixr(s);
+ } while (s->dma_cmd_fifo_num);
+}
+
+static void r_ixr_post_write(RegisterInfo *reg, uint64_t val)
+{
+ XlnxZynqDevcfg *s = XLNX_ZYNQ_DEVCFG(reg->opaque);
+
+ xlnx_zynq_devcfg_update_ixr(s);
+}
+
+static uint64_t r_ctrl_pre_write(RegisterInfo *reg, uint64_t val)
+{
+ XlnxZynqDevcfg *s = XLNX_ZYNQ_DEVCFG(reg->opaque);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(lock_ctrl_map); ++i) {
+ if (s->regs[R_LOCK] & 1 << i) {
+ val &= ~lock_ctrl_map[i];
+ val |= lock_ctrl_map[i] & s->regs[R_CTRL];
+ }
+ }
+ return val;
+}
+
+static void r_ctrl_post_write(RegisterInfo *reg, uint64_t val)
+{
+ const char *device_prefix = object_get_typename(OBJECT(reg->opaque));
+ uint32_t aes_en = FIELD_EX32(val, CTRL, PCFG_AES_EN);
+
+ if (aes_en != 0 && aes_en != 7) {
+ qemu_log_mask(LOG_UNIMP, "%s: warning, aes-en bits inconsistent,"
+ "unimplemented security reset should happen!\n",
+ device_prefix);
+ }
+}
+
+static void r_unlock_post_write(RegisterInfo *reg, uint64_t val)
+{
+ XlnxZynqDevcfg *s = XLNX_ZYNQ_DEVCFG(reg->opaque);
+ const char *device_prefix = object_get_typename(OBJECT(s));
+
+ if (val == R_UNLOCK_MAGIC) {
+ DB_PRINT("successful unlock\n");
+ s->regs[R_CTRL] |= R_CTRL_PCAP_PR_MASK;
+ s->regs[R_CTRL] |= R_CTRL_PCFG_AES_EN_MASK;
+ memory_region_set_enabled(&s->iomem, true);
+ } else { /* bad unlock attempt */
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: failed unlock\n", device_prefix);
+ s->regs[R_CTRL] &= ~R_CTRL_PCAP_PR_MASK;
+ s->regs[R_CTRL] &= ~R_CTRL_PCFG_AES_EN_MASK;
+ /* core becomes inaccessible */
+ memory_region_set_enabled(&s->iomem, false);
+ }
+}
+
+static uint64_t r_lock_pre_write(RegisterInfo *reg, uint64_t val)
+{
+ XlnxZynqDevcfg *s = XLNX_ZYNQ_DEVCFG(reg->opaque);
+
+ /* once bits are locked they stay locked */
+ return s->regs[R_LOCK] | val;
+}
+
+static void r_dma_dst_len_post_write(RegisterInfo *reg, uint64_t val)
+{
+ XlnxZynqDevcfg *s = XLNX_ZYNQ_DEVCFG(reg->opaque);
+
+ s->dma_cmd_fifo[s->dma_cmd_fifo_num] = (XlnxZynqDevcfgDMACmd) {
+ .src_addr = s->regs[R_DMA_SRC_ADDR] & ~0x3UL,
+ .dest_addr = s->regs[R_DMA_DST_ADDR] & ~0x3UL,
+ .src_len = s->regs[R_DMA_SRC_LEN] << 2,
+ .dest_len = s->regs[R_DMA_DST_LEN] << 2,
+ };
+ s->dma_cmd_fifo_num++;
+ DB_PRINT("dma transfer started; %d total transfers pending\n",
+ s->dma_cmd_fifo_num);
+ xlnx_zynq_devcfg_dma_go(s);
+}
+
+static const RegisterAccessInfo xlnx_zynq_devcfg_regs_info[] = {
+ { .name = "CTRL", .addr = A_CTRL,
+ .reset = R_CTRL_PCAP_PR_MASK | R_CTRL_PCAP_MODE_MASK | 0x3 << 13,
+ .rsvd = 0x1 << 28 | 0x3ff << 13 | 0x3 << 13,
+ .pre_write = r_ctrl_pre_write,
+ .post_write = r_ctrl_post_write,
+ },
+ { .name = "LOCK", .addr = A_LOCK,
+ .rsvd = MAKE_64BIT_MASK(5, 64 - 5),
+ .pre_write = r_lock_pre_write,
+ },
+ { .name = "CFG", .addr = A_CFG,
+ .reset = R_CFG_RESET,
+ .rsvd = 0xfffff00f,
+ },
+ { .name = "INT_STS", .addr = A_INT_STS,
+ .w1c = ~R_INT_STS_RSVD,
+ .reset = R_INT_STS_PSS_GTS_USR_B_MASK |
+ R_INT_STS_PSS_CFG_RESET_B_MASK |
+ R_INT_STS_WR_FIFO_LVL_MASK,
+ .rsvd = R_INT_STS_RSVD,
+ .post_write = r_ixr_post_write,
+ },
+ { .name = "INT_MASK", .addr = A_INT_MASK,
+ .reset = ~0,
+ .rsvd = R_INT_STS_RSVD,
+ .post_write = r_ixr_post_write,
+ },
+ { .name = "STATUS", .addr = A_STATUS,
+ .reset = R_STATUS_DMA_CMD_Q_E_MASK |
+ R_STATUS_PSS_GTS_USR_B_MASK |
+ R_STATUS_PSS_CFG_RESET_B_MASK,
+ .ro = ~0,
+ },
+ { .name = "DMA_SRC_ADDR", .addr = A_DMA_SRC_ADDR, },
+ { .name = "DMA_DST_ADDR", .addr = A_DMA_DST_ADDR, },
+ { .name = "DMA_SRC_LEN", .addr = A_DMA_SRC_LEN,
+ .ro = MAKE_64BIT_MASK(27, 64 - 27) },
+ { .name = "DMA_DST_LEN", .addr = A_DMA_DST_LEN,
+ .ro = MAKE_64BIT_MASK(27, 64 - 27),
+ .post_write = r_dma_dst_len_post_write,
+ },
+ { .name = "ROM_SHADOW", .addr = A_ROM_SHADOW,
+ .rsvd = ~0ull,
+ },
+ { .name = "SW_ID", .addr = A_SW_ID, },
+ { .name = "UNLOCK", .addr = A_UNLOCK,
+ .post_write = r_unlock_post_write,
+ },
+ { .name = "MCTRL", .addr = R_MCTRL * 4,
+ /* Silicon 3.0 for version field, the mysterious reserved bit 23
+ * and QEMU platform identifier.
+ */
+ .reset = 0x2 << R_MCTRL_PS_VERSION_SHIFT | 1 << 23 | R_MCTRL_QEMU_MASK,
+ .ro = ~R_MCTRL_INT_PCAP_LPBK_MASK,
+ .rsvd = 0x00f00303,
+ },
+};
+
+static const MemoryRegionOps xlnx_zynq_devcfg_reg_ops = {
+ .read = register_read_memory,
+ .write = register_write_memory,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ }
+};
+
+static const VMStateDescription vmstate_xlnx_zynq_devcfg_dma_cmd = {
+ .name = "xlnx_zynq_devcfg_dma_cmd",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(src_addr, XlnxZynqDevcfgDMACmd),
+ VMSTATE_UINT32(dest_addr, XlnxZynqDevcfgDMACmd),
+ VMSTATE_UINT32(src_len, XlnxZynqDevcfgDMACmd),
+ VMSTATE_UINT32(dest_len, XlnxZynqDevcfgDMACmd),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_xlnx_zynq_devcfg = {
+ .name = "xlnx_zynq_devcfg",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_STRUCT_ARRAY(dma_cmd_fifo, XlnxZynqDevcfg,
+ XLNX_ZYNQ_DEVCFG_DMA_CMD_FIFO_LEN, 0,
+ vmstate_xlnx_zynq_devcfg_dma_cmd,
+ XlnxZynqDevcfgDMACmd),
+ VMSTATE_UINT8(dma_cmd_fifo_num, XlnxZynqDevcfg),
+ VMSTATE_UINT32_ARRAY(regs, XlnxZynqDevcfg, XLNX_ZYNQ_DEVCFG_R_MAX),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void xlnx_zynq_devcfg_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ XlnxZynqDevcfg *s = XLNX_ZYNQ_DEVCFG(obj);
+ RegisterInfoArray *reg_array;
+
+ sysbus_init_irq(sbd, &s->irq);
+
+ memory_region_init(&s->iomem, obj, "devcfg", XLNX_ZYNQ_DEVCFG_R_MAX * 4);
+ reg_array =
+ register_init_block32(DEVICE(obj), xlnx_zynq_devcfg_regs_info,
+ ARRAY_SIZE(xlnx_zynq_devcfg_regs_info),
+ s->regs_info, s->regs,
+ &xlnx_zynq_devcfg_reg_ops,
+ XLNX_ZYNQ_DEVCFG_ERR_DEBUG,
+ XLNX_ZYNQ_DEVCFG_R_MAX);
+ memory_region_add_subregion(&s->iomem,
+ A_CTRL,
+ &reg_array->mem);
+
+ sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static void xlnx_zynq_devcfg_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = xlnx_zynq_devcfg_reset;
+ dc->vmsd = &vmstate_xlnx_zynq_devcfg;
+}
+
+static const TypeInfo xlnx_zynq_devcfg_info = {
+ .name = TYPE_XLNX_ZYNQ_DEVCFG,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(XlnxZynqDevcfg),
+ .instance_init = xlnx_zynq_devcfg_init,
+ .class_init = xlnx_zynq_devcfg_class_init,
+};
+
+static void xlnx_zynq_devcfg_register_types(void)
+{
+ type_register_static(&xlnx_zynq_devcfg_info);
+}
+
+type_init(xlnx_zynq_devcfg_register_types)
diff --git a/hw/i2c/smbus_ich9.c b/hw/i2c/smbus_ich9.c
index 498f03e835..48fab22625 100644
--- a/hw/i2c/smbus_ich9.c
+++ b/hw/i2c/smbus_ich9.c
@@ -35,7 +35,6 @@
#include "hw/i386/ich9.h"
-#define TYPE_ICH9_SMB_DEVICE "ICH9 SMB"
#define ICH9_SMB_DEVICE(obj) \
OBJECT_CHECK(ICH9SMBState, (obj), TYPE_ICH9_SMB_DEVICE)
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index 8ca203211a..5a594be8ee 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -33,6 +33,7 @@
#include "hw/timer/hpet.h"
#include "hw/acpi/acpi-defs.h"
#include "hw/acpi/acpi.h"
+#include "hw/acpi/cpu.h"
#include "hw/nvram/fw_cfg.h"
#include "hw/acpi/bios-linker-loader.h"
#include "hw/loader.h"
@@ -43,6 +44,7 @@
#include "hw/acpi/tpm.h"
#include "sysemu/tpm_backend.h"
#include "hw/timer/mc146818rtc_regs.h"
+#include "sysemu/numa.h"
/* Supported chipsets: */
#include "hw/acpi/piix4.h"
@@ -58,6 +60,8 @@
#include "qapi/qmp/qint.h"
#include "qom/qom-qobject.h"
+#include "hw/acpi/ipmi.h"
+
/* These are used to size the ACPI tables for -M pc-i440fx-1.7 and
* -M pc-i440fx-2.0. Even if the actual amount of AML generated grows
* a little bit, there should be plenty of free space since the DSDT
@@ -327,12 +331,38 @@ build_fadt(GArray *table_data, BIOSLinker *linker, AcpiPmInfo *pm,
(void *)fadt, "FACP", sizeof(*fadt), 1, oem_id, oem_table_id);
}
+void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid,
+ CPUArchIdList *apic_ids, GArray *entry)
+{
+ int apic_id;
+ AcpiMadtProcessorApic *apic = acpi_data_push(entry, sizeof *apic);
+
+ apic_id = apic_ids->cpus[uid].arch_id;
+ apic->type = ACPI_APIC_PROCESSOR;
+ apic->length = sizeof(*apic);
+ apic->processor_id = uid;
+ apic->local_apic_id = apic_id;
+ if (apic_ids->cpus[uid].cpu != NULL) {
+ apic->flags = cpu_to_le32(1);
+ } else {
+ /* ACPI spec says that LAPIC entry for non present
+ * CPU may be omitted from MADT or it must be marked
+ * as disabled. However omitting non present CPU from
+ * MADT breaks hotplug on linux. So possible CPUs
+ * should be put in MADT but kept disabled.
+ */
+ apic->flags = cpu_to_le32(0);
+ }
+}
+
static void
build_madt(GArray *table_data, BIOSLinker *linker, PCMachineState *pcms)
{
MachineClass *mc = MACHINE_GET_CLASS(pcms);
CPUArchIdList *apic_ids = mc->possible_cpu_arch_ids(MACHINE(pcms));
int madt_start = table_data->len;
+ AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_GET_CLASS(pcms->acpi_dev);
+ AcpiDeviceIf *adev = ACPI_DEVICE_IF(pcms->acpi_dev);
AcpiMultipleApicTable *madt;
AcpiMadtIoApic *io_apic;
@@ -345,24 +375,7 @@ build_madt(GArray *table_data, BIOSLinker *linker, PCMachineState *pcms)
madt->flags = cpu_to_le32(1);
for (i = 0; i < apic_ids->len; i++) {
- AcpiMadtProcessorApic *apic = acpi_data_push(table_data, sizeof *apic);
- int apic_id = apic_ids->cpus[i].arch_id;
-
- apic->type = ACPI_APIC_PROCESSOR;
- apic->length = sizeof(*apic);
- apic->processor_id = i;
- apic->local_apic_id = apic_id;
- if (apic_ids->cpus[i].cpu != NULL) {
- apic->flags = cpu_to_le32(1);
- } else {
- /* ACPI spec says that LAPIC entry for non present
- * CPU may be omitted from MADT or it must be marked
- * as disabled. However omitting non present CPU from
- * MADT breaks hotplug on linux. So possible CPUs
- * should be put in MADT but kept disabled.
- */
- apic->flags = cpu_to_le32(0);
- }
+ adevc->madt_cpu(adev, i, apic_ids, table_data);
}
g_free(apic_ids);
@@ -1334,8 +1347,10 @@ static Aml *build_com_device_aml(uint8_t uid)
static void build_isa_devices_aml(Aml *table)
{
ISADevice *fdc = pc_find_fdc0();
+ bool ambiguous;
Aml *scope = aml_scope("_SB.PCI0.ISA");
+ Object *obj = object_resolve_path_type("", TYPE_ISA_BUS, &ambiguous);
aml_append(scope, build_rtc_device_aml());
aml_append(scope, build_kbd_device_aml());
@@ -1347,6 +1362,14 @@ static void build_isa_devices_aml(Aml *table)
aml_append(scope, build_com_device_aml(1));
aml_append(scope, build_com_device_aml(2));
+ if (ambiguous) {
+ error_report("Multiple ISA busses, unable to define IPMI ACPI data");
+ } else if (!obj) {
+ error_report("No ISA bus, unable to define IPMI ACPI data");
+ } else {
+ build_acpi_ipmi_devices(scope, BUS(obj));
+ }
+
aml_append(table, scope);
}
@@ -1874,6 +1897,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
GPtrArray *mem_ranges = g_ptr_array_new_with_free_func(crs_range_free);
GPtrArray *io_ranges = g_ptr_array_new_with_free_func(crs_range_free);
PCMachineState *pcms = PC_MACHINE(machine);
+ PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(machine);
uint32_t nr_mem = machine->ram_slots;
int root_bus_limit = 0xFF;
PCIBus *bus = NULL;
@@ -1929,7 +1953,15 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
build_q35_pci0_int(dsdt);
}
- build_legacy_cpu_hotplug_aml(dsdt, machine, pm->cpu_hp_io_base);
+ if (pcmc->legacy_cpu_hotplug) {
+ build_legacy_cpu_hotplug_aml(dsdt, machine, pm->cpu_hp_io_base);
+ } else {
+ CPUHotplugFeatures opts = {
+ .apci_1_compatible = true, .has_legacy_cphp = true
+ };
+ build_cpus_aml(dsdt, machine, opts, pm->cpu_hp_io_base,
+ "\\_SB.PCI0", "\\_GPE._E02");
+ }
build_memory_hotplug_aml(dsdt, nr_mem, pm->mem_hp_io_base,
pm->mem_hp_io_len);
@@ -2297,7 +2329,6 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
AcpiSratMemoryAffinity *numamem;
int i;
- uint64_t curnode;
int srat_start, numa_start, slots;
uint64_t mem_len, mem_base, next_base;
MachineClass *mc = MACHINE_GET_CLASS(machine);
@@ -2313,14 +2344,19 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
srat->reserved1 = cpu_to_le32(1);
for (i = 0; i < apic_ids->len; i++) {
+ int j;
int apic_id = apic_ids->cpus[i].arch_id;
core = acpi_data_push(table_data, sizeof *core);
core->type = ACPI_SRAT_PROCESSOR_APIC;
core->length = sizeof(*core);
core->local_apic_id = apic_id;
- curnode = pcms->node_cpu[apic_id];
- core->proximity_lo = curnode;
+ for (j = 0; j < nb_numa_nodes; j++) {
+ if (test_bit(i, numa_info[j].node_cpu)) {
+ core->proximity_lo = j;
+ break;
+ }
+ }
memset(core->proximity_hi, 0, 3);
core->local_sapic_eid = 0;
core->flags = cpu_to_le32(1);
diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
index 347718f938..5eba704477 100644
--- a/hw/i386/intel_iommu.c
+++ b/hw/i386/intel_iommu.c
@@ -24,6 +24,7 @@
#include "exec/address-spaces.h"
#include "intel_iommu_internal.h"
#include "hw/pci/pci.h"
+#include "hw/pci/pci_bus.h"
/*#define DEBUG_INTEL_IOMMU*/
#ifdef DEBUG_INTEL_IOMMU
@@ -1871,6 +1872,16 @@ static IOMMUTLBEntry vtd_iommu_translate(MemoryRegion *iommu, hwaddr addr,
return ret;
}
+static void vtd_iommu_notify_started(MemoryRegion *iommu)
+{
+ VTDAddressSpace *vtd_as = container_of(iommu, VTDAddressSpace, iommu);
+
+ hw_error("Device at bus %s addr %02x.%d requires iommu notifier which "
+ "is currently not supported by intel-iommu emulation",
+ vtd_as->bus->qbus.name, PCI_SLOT(vtd_as->devfn),
+ PCI_FUNC(vtd_as->devfn));
+}
+
static const VMStateDescription vtd_vmstate = {
.name = "iommu-intel",
.unmigratable = 1,
@@ -1938,6 +1949,7 @@ static void vtd_init(IntelIOMMUState *s)
memset(s->womask, 0, DMAR_REG_SIZE);
s->iommu_ops.translate = vtd_iommu_translate;
+ s->iommu_ops.notify_started = vtd_iommu_notify_started;
s->root = 0;
s->root_extended = false;
s->dmar_enabled = false;
diff --git a/hw/i386/kvm/pci-assign.c b/hw/i386/kvm/pci-assign.c
index f9c901471d..3623aa1965 100644
--- a/hw/i386/kvm/pci-assign.c
+++ b/hw/i386/kvm/pci-assign.c
@@ -36,8 +36,6 @@
#include "kvm_i386.h"
#include "hw/pci/pci-assign.h"
-#define MSIX_PAGE_SIZE 0x1000
-
/* From linux/ioport.h */
#define IORESOURCE_IO 0x00000100 /* Resource type */
#define IORESOURCE_MEM 0x00000200
@@ -122,6 +120,7 @@ typedef struct AssignedDevice {
int *msi_virq;
MSIXTableEntry *msix_table;
hwaddr msix_table_addr;
+ uint16_t msix_table_size;
uint16_t msix_max;
MemoryRegion mmio;
char *configfd_name;
@@ -1310,6 +1309,7 @@ static int assigned_device_pci_cap_init(PCIDevice *pci_dev, Error **errp)
bar_nr = msix_table_entry & PCI_MSIX_FLAGS_BIRMASK;
msix_table_entry &= ~PCI_MSIX_FLAGS_BIRMASK;
dev->msix_table_addr = pci_region[bar_nr].base_addr + msix_table_entry;
+ dev->msix_table_size = msix_max * sizeof(MSIXTableEntry);
dev->msix_max = msix_max;
}
@@ -1633,7 +1633,7 @@ static void assigned_dev_msix_reset(AssignedDevice *dev)
return;
}
- memset(dev->msix_table, 0, MSIX_PAGE_SIZE);
+ memset(dev->msix_table, 0, dev->msix_table_size);
for (i = 0, entry = dev->msix_table; i < dev->msix_max; i++, entry++) {
entry->ctrl = cpu_to_le32(0x1); /* Masked */
@@ -1642,8 +1642,8 @@ static void assigned_dev_msix_reset(AssignedDevice *dev)
static void assigned_dev_register_msix_mmio(AssignedDevice *dev, Error **errp)
{
- dev->msix_table = mmap(NULL, MSIX_PAGE_SIZE, PROT_READ|PROT_WRITE,
- MAP_ANONYMOUS|MAP_PRIVATE, 0, 0);
+ dev->msix_table = mmap(NULL, dev->msix_table_size, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
if (dev->msix_table == MAP_FAILED) {
error_setg_errno(errp, errno, "failed to allocate msix_table");
dev->msix_table = NULL;
@@ -1653,7 +1653,7 @@ static void assigned_dev_register_msix_mmio(AssignedDevice *dev, Error **errp)
assigned_dev_msix_reset(dev);
memory_region_init_io(&dev->mmio, OBJECT(dev), &assigned_dev_msix_mmio_ops,
- dev, "assigned-dev-msix", MSIX_PAGE_SIZE);
+ dev, "assigned-dev-msix", dev->msix_table_size);
}
static void assigned_dev_unregister_msix_mmio(AssignedDevice *dev)
@@ -1662,7 +1662,7 @@ static void assigned_dev_unregister_msix_mmio(AssignedDevice *dev)
return;
}
- if (munmap(dev->msix_table, MSIX_PAGE_SIZE) == -1) {
+ if (munmap(dev->msix_table, dev->msix_table_size) == -1) {
error_report("error unmapping msix_table! %s", strerror(errno));
}
dev->msix_table = NULL;
@@ -1891,8 +1891,4 @@ static void assigned_dev_load_option_rom(AssignedDevice *dev)
pci_assign_dev_load_option_rom(&dev->dev, OBJECT(dev), &size,
dev->host.domain, dev->host.bus,
dev->host.slot, dev->host.function);
-
- if (!size) {
- error_report("pci-assign: Invalid ROM.");
- }
}
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index 7198ed533c..44a8f3bcbd 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -505,7 +505,7 @@ typedef struct Port92State {
MemoryRegion io;
uint8_t outport;
- qemu_irq *a20_out;
+ qemu_irq a20_out;
} Port92State;
static void port92_write(void *opaque, hwaddr addr, uint64_t val,
@@ -516,7 +516,7 @@ static void port92_write(void *opaque, hwaddr addr, uint64_t val,
DPRINTF("port92: write 0x%02" PRIx64 "\n", val);
s->outport = val;
- qemu_set_irq(*s->a20_out, (val >> 1) & 1);
+ qemu_set_irq(s->a20_out, (val >> 1) & 1);
if ((val & 1) && !(oldval & 1)) {
qemu_system_reset_request();
}
@@ -535,9 +535,7 @@ static uint64_t port92_read(void *opaque, hwaddr addr,
static void port92_init(ISADevice *dev, qemu_irq *a20_out)
{
- Port92State *s = PORT92(dev);
-
- s->a20_out = a20_out;
+ qdev_connect_gpio_out_named(DEVICE(dev), PORT92_A20_LINE, 0, *a20_out);
}
static const VMStateDescription vmstate_port92_isa = {
@@ -574,6 +572,8 @@ static void port92_initfn(Object *obj)
memory_region_init_io(&s->io, OBJECT(s), &port92_ops, s, "port92", 1);
s->outport = 0;
+
+ qdev_init_gpio_out_named(DEVICE(obj), &s->a20_out, PORT92_A20_LINE, 1);
}
static void port92_realizefn(DeviceState *dev, Error **errp)
@@ -1179,7 +1179,7 @@ void pc_machine_done(Notifier *notifier, void *data)
void pc_guest_info_init(PCMachineState *pcms)
{
- int i, j;
+ int i;
pcms->apic_xrupt_override = kvm_allows_irq0_override();
pcms->numa_nodes = nb_numa_nodes;
@@ -1189,20 +1189,6 @@ void pc_guest_info_init(PCMachineState *pcms)
pcms->node_mem[i] = numa_info[i].node_mem;
}
- pcms->node_cpu = g_malloc0(pcms->apic_id_limit *
- sizeof *pcms->node_cpu);
-
- for (i = 0; i < max_cpus; i++) {
- unsigned int apic_id = x86_cpu_apic_id_from_index(i);
- assert(apic_id < pcms->apic_id_limit);
- for (j = 0; j < nb_numa_nodes; j++) {
- if (test_bit(i, numa_info[j].node_cpu)) {
- pcms->node_cpu[apic_id] = j;
- break;
- }
- }
- }
-
pcms->machine_done.notify = pc_machine_done;
qemu_add_machine_init_done_notifier(&pcms->machine_done);
}
@@ -1707,6 +1693,49 @@ static void pc_cpu_plug(HotplugHandler *hotplug_dev,
out:
error_propagate(errp, local_err);
}
+static void pc_cpu_unplug_request_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ HotplugHandlerClass *hhc;
+ Error *local_err = NULL;
+ PCMachineState *pcms = PC_MACHINE(hotplug_dev);
+
+ hhc = HOTPLUG_HANDLER_GET_CLASS(pcms->acpi_dev);
+ hhc->unplug_request(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err);
+
+ if (local_err) {
+ goto out;
+ }
+
+ out:
+ error_propagate(errp, local_err);
+
+}
+
+static void pc_cpu_unplug_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ HotplugHandlerClass *hhc;
+ Error *local_err = NULL;
+ PCMachineState *pcms = PC_MACHINE(hotplug_dev);
+
+ hhc = HOTPLUG_HANDLER_GET_CLASS(pcms->acpi_dev);
+ hhc->unplug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err);
+
+ if (local_err) {
+ goto out;
+ }
+
+ /*
+ * TODO: enable unplug once generic CPU remove bits land
+ * for now guest will be able to eject CPU ACPI wise but
+ * it will come back again on machine reset.
+ */
+ /* object_unparent(OBJECT(dev)); */
+
+ out:
+ error_propagate(errp, local_err);
+}
static void pc_machine_device_plug_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
@@ -1723,6 +1752,8 @@ static void pc_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev,
{
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
pc_dimm_unplug_request(hotplug_dev, dev, errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
+ pc_cpu_unplug_request_cb(hotplug_dev, dev, errp);
} else {
error_setg(errp, "acpi: device unplug request for not supported device"
" type: %s", object_get_typename(OBJECT(dev)));
@@ -1734,6 +1765,8 @@ static void pc_machine_device_unplug_cb(HotplugHandler *hotplug_dev,
{
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
pc_dimm_unplug(hotplug_dev, dev, errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
+ pc_cpu_unplug_cb(hotplug_dev, dev, errp);
} else {
error_setg(errp, "acpi: device unplug for not supported device"
" type: %s", object_get_typename(OBJECT(dev)));
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index 53bc968bd0..c7d70af253 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -445,9 +445,11 @@ DEFINE_I440FX_MACHINE(v2_7, "pc-i440fx-2.7", NULL,
static void pc_i440fx_2_6_machine_options(MachineClass *m)
{
+ PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
pc_i440fx_2_7_machine_options(m);
m->is_default = 0;
m->alias = NULL;
+ pcmc->legacy_cpu_hotplug = true;
SET_MACHINE_COMPAT(m, PC_COMPAT_2_6);
}
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
index e4b541f7b2..04b2684d37 100644
--- a/hw/i386/pc_q35.c
+++ b/hw/i386/pc_q35.c
@@ -60,6 +60,7 @@ static void pc_q35_init(MachineState *machine)
PCIHostState *phb;
PCIBus *host_bus;
PCIDevice *lpc;
+ DeviceState *lpc_dev;
BusState *idebus[MAX_SATA_PORTS];
ISADevice *rtc_state;
MemoryRegion *system_io = get_system_io();
@@ -159,12 +160,18 @@ static void pc_q35_init(MachineState *machine)
q35_host = Q35_HOST_DEVICE(qdev_create(NULL, TYPE_Q35_HOST_DEVICE));
object_property_add_child(qdev_get_machine(), "q35", OBJECT(q35_host), NULL);
- q35_host->mch.ram_memory = ram_memory;
- q35_host->mch.pci_address_space = pci_memory;
- q35_host->mch.system_memory = get_system_memory();
- q35_host->mch.address_space_io = system_io;
- q35_host->mch.below_4g_mem_size = pcms->below_4g_mem_size;
- q35_host->mch.above_4g_mem_size = pcms->above_4g_mem_size;
+ object_property_set_link(OBJECT(q35_host), OBJECT(ram_memory),
+ MCH_HOST_PROP_RAM_MEM, NULL);
+ object_property_set_link(OBJECT(q35_host), OBJECT(pci_memory),
+ MCH_HOST_PROP_PCI_MEM, NULL);
+ object_property_set_link(OBJECT(q35_host), OBJECT(get_system_memory()),
+ MCH_HOST_PROP_SYSTEM_MEM, NULL);
+ object_property_set_link(OBJECT(q35_host), OBJECT(system_io),
+ MCH_HOST_PROP_IO_MEM, NULL);
+ object_property_set_int(OBJECT(q35_host), pcms->below_4g_mem_size,
+ PCI_HOST_BELOW_4G_MEM_SIZE, NULL);
+ object_property_set_int(OBJECT(q35_host), pcms->above_4g_mem_size,
+ PCI_HOST_ABOVE_4G_MEM_SIZE, NULL);
/* pci */
qdev_init_nofail(DEVICE(q35_host));
phb = PCI_HOST_BRIDGE(q35_host);
@@ -184,16 +191,15 @@ static void pc_q35_init(MachineState *machine)
PC_MACHINE_ACPI_DEVICE_PROP, &error_abort);
ich9_lpc = ICH9_LPC_DEVICE(lpc);
- ich9_lpc->pic = gsi;
- ich9_lpc->ioapic = gsi_state->ioapic_irq;
+ lpc_dev = DEVICE(lpc);
+ for (i = 0; i < GSI_NUM_PINS; i++) {
+ qdev_connect_gpio_out_named(lpc_dev, ICH9_GPIO_GSI, i, gsi[i]);
+ }
pci_bus_irqs(host_bus, ich9_lpc_set_irq, ich9_lpc_map_irq, ich9_lpc,
ICH9_LPC_NB_PIRQS);
pci_bus_set_route_irq_fn(host_bus, ich9_route_intx_pin_to_irq);
isa_bus = ich9_lpc->isa_bus;
- /*end early*/
- isa_bus_irqs(isa_bus, gsi);
-
if (kvm_pic_in_kernel()) {
i8259 = kvm_i8259_init(isa_bus);
} else if (xen_enabled()) {
@@ -294,8 +300,10 @@ DEFINE_Q35_MACHINE(v2_7, "pc-q35-2.7", NULL,
static void pc_q35_2_6_machine_options(MachineClass *m)
{
+ PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
pc_q35_2_7_machine_options(m);
m->alias = NULL;
+ pcmc->legacy_cpu_hotplug = true;
SET_MACHINE_COMPAT(m, PC_COMPAT_2_6);
}
diff --git a/hw/i386/pci-assign-load-rom.c b/hw/i386/pci-assign-load-rom.c
index 4bbb08c955..0d8e4b2826 100644
--- a/hw/i386/pci-assign-load-rom.c
+++ b/hw/i386/pci-assign-load-rom.c
@@ -40,6 +40,9 @@ void *pci_assign_dev_load_option_rom(PCIDevice *dev, struct Object *owner,
domain, bus, slot, function);
if (stat(rom_file, &st)) {
+ if (errno != ENOENT) {
+ error_report("pci-assign: Invalid ROM.");
+ }
return NULL;
}
diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index 502d4f1c7b..b1a7b65a7b 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -30,7 +30,7 @@
#include "qemu/error-report.h"
#include "sysemu/block-backend.h"
#include "sysemu/dma.h"
-#include "internal.h"
+#include <hw/ide/internal.h>
#include <hw/ide/pci.h>
#include <hw/ide/ahci.h>
diff --git a/hw/ide/ahci.h b/hw/ide/ahci.h
deleted file mode 100644
index bc777ed5c2..0000000000
--- a/hw/ide/ahci.h
+++ /dev/null
@@ -1,405 +0,0 @@
-/*
- * QEMU AHCI Emulation
- *
- * Copyright (c) 2010 qiaochong@loongson.cn
- * Copyright (c) 2010 Roland Elek <elek.roland@gmail.com>
- * Copyright (c) 2010 Sebastian Herbszt <herbszt@gmx.de>
- * Copyright (c) 2010 Alexander Graf <agraf@suse.de>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#ifndef HW_IDE_AHCI_H
-#define HW_IDE_AHCI_H
-
-#include <hw/sysbus.h>
-
-#define AHCI_MEM_BAR_SIZE 0x1000
-#define AHCI_MAX_PORTS 32
-#define AHCI_MAX_SG 168 /* hardware max is 64K */
-#define AHCI_DMA_BOUNDARY 0xffffffff
-#define AHCI_USE_CLUSTERING 0
-#define AHCI_MAX_CMDS 32
-#define AHCI_CMD_SZ 32
-#define AHCI_CMD_SLOT_SZ (AHCI_MAX_CMDS * AHCI_CMD_SZ)
-#define AHCI_RX_FIS_SZ 256
-#define AHCI_CMD_TBL_CDB 0x40
-#define AHCI_CMD_TBL_HDR_SZ 0x80
-#define AHCI_CMD_TBL_SZ (AHCI_CMD_TBL_HDR_SZ + (AHCI_MAX_SG * 16))
-#define AHCI_CMD_TBL_AR_SZ (AHCI_CMD_TBL_SZ * AHCI_MAX_CMDS)
-#define AHCI_PORT_PRIV_DMA_SZ (AHCI_CMD_SLOT_SZ + AHCI_CMD_TBL_AR_SZ + \
- AHCI_RX_FIS_SZ)
-
-#define AHCI_IRQ_ON_SG (1U << 31)
-#define AHCI_CMD_ATAPI (1 << 5)
-#define AHCI_CMD_WRITE (1 << 6)
-#define AHCI_CMD_PREFETCH (1 << 7)
-#define AHCI_CMD_RESET (1 << 8)
-#define AHCI_CMD_CLR_BUSY (1 << 10)
-
-#define RX_FIS_D2H_REG 0x40 /* offset of D2H Register FIS data */
-#define RX_FIS_SDB 0x58 /* offset of SDB FIS data */
-#define RX_FIS_UNK 0x60 /* offset of Unknown FIS data */
-
-/* global controller registers */
-#define HOST_CAP 0x00 /* host capabilities */
-#define HOST_CTL 0x04 /* global host control */
-#define HOST_IRQ_STAT 0x08 /* interrupt status */
-#define HOST_PORTS_IMPL 0x0c /* bitmap of implemented ports */
-#define HOST_VERSION 0x10 /* AHCI spec. version compliancy */
-
-/* HOST_CTL bits */
-#define HOST_CTL_RESET (1 << 0) /* reset controller; self-clear */
-#define HOST_CTL_IRQ_EN (1 << 1) /* global IRQ enable */
-#define HOST_CTL_AHCI_EN (1U << 31) /* AHCI enabled */
-
-/* HOST_CAP bits */
-#define HOST_CAP_SSC (1 << 14) /* Slumber capable */
-#define HOST_CAP_AHCI (1 << 18) /* AHCI only */
-#define HOST_CAP_CLO (1 << 24) /* Command List Override support */
-#define HOST_CAP_SSS (1 << 27) /* Staggered Spin-up */
-#define HOST_CAP_NCQ (1 << 30) /* Native Command Queueing */
-#define HOST_CAP_64 (1U << 31) /* PCI DAC (64-bit DMA) support */
-
-/* registers for each SATA port */
-#define PORT_LST_ADDR 0x00 /* command list DMA addr */
-#define PORT_LST_ADDR_HI 0x04 /* command list DMA addr hi */
-#define PORT_FIS_ADDR 0x08 /* FIS rx buf addr */
-#define PORT_FIS_ADDR_HI 0x0c /* FIS rx buf addr hi */
-#define PORT_IRQ_STAT 0x10 /* interrupt status */
-#define PORT_IRQ_MASK 0x14 /* interrupt enable/disable mask */
-#define PORT_CMD 0x18 /* port command */
-#define PORT_TFDATA 0x20 /* taskfile data */
-#define PORT_SIG 0x24 /* device TF signature */
-#define PORT_SCR_STAT 0x28 /* SATA phy register: SStatus */
-#define PORT_SCR_CTL 0x2c /* SATA phy register: SControl */
-#define PORT_SCR_ERR 0x30 /* SATA phy register: SError */
-#define PORT_SCR_ACT 0x34 /* SATA phy register: SActive */
-#define PORT_CMD_ISSUE 0x38 /* command issue */
-#define PORT_RESERVED 0x3c /* reserved */
-
-/* PORT_IRQ_{STAT,MASK} bits */
-#define PORT_IRQ_COLD_PRES (1U << 31) /* cold presence detect */
-#define PORT_IRQ_TF_ERR (1 << 30) /* task file error */
-#define PORT_IRQ_HBUS_ERR (1 << 29) /* host bus fatal error */
-#define PORT_IRQ_HBUS_DATA_ERR (1 << 28) /* host bus data error */
-#define PORT_IRQ_IF_ERR (1 << 27) /* interface fatal error */
-#define PORT_IRQ_IF_NONFATAL (1 << 26) /* interface non-fatal error */
-#define PORT_IRQ_OVERFLOW (1 << 24) /* xfer exhausted available S/G */
-#define PORT_IRQ_BAD_PMP (1 << 23) /* incorrect port multiplier */
-
-#define PORT_IRQ_PHYRDY (1 << 22) /* PhyRdy changed */
-#define PORT_IRQ_DEV_ILCK (1 << 7) /* device interlock */
-#define PORT_IRQ_CONNECT (1 << 6) /* port connect change status */
-#define PORT_IRQ_SG_DONE (1 << 5) /* descriptor processed */
-#define PORT_IRQ_UNK_FIS (1 << 4) /* unknown FIS rx'd */
-#define PORT_IRQ_SDB_FIS (1 << 3) /* Set Device Bits FIS rx'd */
-#define PORT_IRQ_DMAS_FIS (1 << 2) /* DMA Setup FIS rx'd */
-#define PORT_IRQ_PIOS_FIS (1 << 1) /* PIO Setup FIS rx'd */
-#define PORT_IRQ_D2H_REG_FIS (1 << 0) /* D2H Register FIS rx'd */
-
-#define PORT_IRQ_FREEZE (PORT_IRQ_HBUS_ERR | PORT_IRQ_IF_ERR | \
- PORT_IRQ_CONNECT | PORT_IRQ_PHYRDY | \
- PORT_IRQ_UNK_FIS)
-#define PORT_IRQ_ERROR (PORT_IRQ_FREEZE | PORT_IRQ_TF_ERR | \
- PORT_IRQ_HBUS_DATA_ERR)
-#define DEF_PORT_IRQ (PORT_IRQ_ERROR | PORT_IRQ_SG_DONE | \
- PORT_IRQ_SDB_FIS | PORT_IRQ_DMAS_FIS | \
- PORT_IRQ_PIOS_FIS | PORT_IRQ_D2H_REG_FIS)
-
-/* PORT_CMD bits */
-#define PORT_CMD_ATAPI (1 << 24) /* Device is ATAPI */
-#define PORT_CMD_LIST_ON (1 << 15) /* cmd list DMA engine running */
-#define PORT_CMD_FIS_ON (1 << 14) /* FIS DMA engine running */
-#define PORT_CMD_FIS_RX (1 << 4) /* Enable FIS receive DMA engine */
-#define PORT_CMD_CLO (1 << 3) /* Command list override */
-#define PORT_CMD_POWER_ON (1 << 2) /* Power up device */
-#define PORT_CMD_SPIN_UP (1 << 1) /* Spin up device */
-#define PORT_CMD_START (1 << 0) /* Enable port DMA engine */
-
-#define PORT_CMD_ICC_MASK (0xfU << 28) /* i/f ICC state mask */
-#define PORT_CMD_ICC_ACTIVE (0x1 << 28) /* Put i/f in active state */
-#define PORT_CMD_ICC_PARTIAL (0x2 << 28) /* Put i/f in partial state */
-#define PORT_CMD_ICC_SLUMBER (0x6 << 28) /* Put i/f in slumber state */
-
-#define PORT_CMD_RO_MASK 0x007dffe0 /* Which CMD bits are read only? */
-
-/* ap->flags bits */
-#define AHCI_FLAG_NO_NCQ (1 << 24)
-#define AHCI_FLAG_IGN_IRQ_IF_ERR (1 << 25) /* ignore IRQ_IF_ERR */
-#define AHCI_FLAG_HONOR_PI (1 << 26) /* honor PORTS_IMPL */
-#define AHCI_FLAG_IGN_SERR_INTERNAL (1 << 27) /* ignore SERR_INTERNAL */
-#define AHCI_FLAG_32BIT_ONLY (1 << 28) /* force 32bit */
-
-#define ATA_SRST (1 << 2) /* software reset */
-
-#define STATE_RUN 0
-#define STATE_RESET 1
-
-#define SATA_SCR_SSTATUS_DET_NODEV 0x0
-#define SATA_SCR_SSTATUS_DET_DEV_PRESENT_PHY_UP 0x3
-
-#define SATA_SCR_SSTATUS_SPD_NODEV 0x00
-#define SATA_SCR_SSTATUS_SPD_GEN1 0x10
-
-#define SATA_SCR_SSTATUS_IPM_NODEV 0x000
-#define SATA_SCR_SSTATUS_IPM_ACTIVE 0X100
-
-#define AHCI_SCR_SCTL_DET 0xf
-
-#define SATA_FIS_TYPE_REGISTER_H2D 0x27
-#define SATA_FIS_REG_H2D_UPDATE_COMMAND_REGISTER 0x80
-#define SATA_FIS_TYPE_REGISTER_D2H 0x34
-#define SATA_FIS_TYPE_PIO_SETUP 0x5f
-#define SATA_FIS_TYPE_SDB 0xA1
-
-#define AHCI_CMD_HDR_CMD_FIS_LEN 0x1f
-#define AHCI_CMD_HDR_PRDT_LEN 16
-
-#define SATA_SIGNATURE_CDROM 0xeb140101
-#define SATA_SIGNATURE_DISK 0x00000101
-
-#define AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR 0x20
- /* Shouldn't this be 0x2c? */
-
-#define AHCI_PORT_REGS_START_ADDR 0x100
-#define AHCI_PORT_ADDR_OFFSET_MASK 0x7f
-#define AHCI_PORT_ADDR_OFFSET_LEN 0x80
-
-#define AHCI_NUM_COMMAND_SLOTS 31
-#define AHCI_SUPPORTED_SPEED 20
-#define AHCI_SUPPORTED_SPEED_GEN1 1
-#define AHCI_VERSION_1_0 0x10000
-
-#define AHCI_PROGMODE_MAJOR_REV_1 1
-
-#define AHCI_COMMAND_TABLE_ACMD 0x40
-
-#define AHCI_PRDT_SIZE_MASK 0x3fffff
-
-#define IDE_FEATURE_DMA 1
-
-#define READ_FPDMA_QUEUED 0x60
-#define WRITE_FPDMA_QUEUED 0x61
-#define NCQ_NON_DATA 0x63
-#define RECEIVE_FPDMA_QUEUED 0x65
-#define SEND_FPDMA_QUEUED 0x64
-
-#define NCQ_FIS_FUA_MASK 0x80
-#define NCQ_FIS_RARC_MASK 0x01
-
-#define RES_FIS_DSFIS 0x00
-#define RES_FIS_PSFIS 0x20
-#define RES_FIS_RFIS 0x40
-#define RES_FIS_SDBFIS 0x58
-#define RES_FIS_UFIS 0x60
-
-#define SATA_CAP_SIZE 0x8
-#define SATA_CAP_REV 0x2
-#define SATA_CAP_BAR 0x4
-
-typedef struct AHCIControlRegs {
- uint32_t cap;
- uint32_t ghc;
- uint32_t irqstatus;
- uint32_t impl;
- uint32_t version;
-} AHCIControlRegs;
-
-typedef struct AHCIPortRegs {
- uint32_t lst_addr;
- uint32_t lst_addr_hi;
- uint32_t fis_addr;
- uint32_t fis_addr_hi;
- uint32_t irq_stat;
- uint32_t irq_mask;
- uint32_t cmd;
- uint32_t unused0;
- uint32_t tfdata;
- uint32_t sig;
- uint32_t scr_stat;
- uint32_t scr_ctl;
- uint32_t scr_err;
- uint32_t scr_act;
- uint32_t cmd_issue;
- uint32_t reserved;
-} AHCIPortRegs;
-
-typedef struct AHCICmdHdr {
- uint16_t opts;
- uint16_t prdtl;
- uint32_t status;
- uint64_t tbl_addr;
- uint32_t reserved[4];
-} QEMU_PACKED AHCICmdHdr;
-
-typedef struct AHCI_SG {
- uint64_t addr;
- uint32_t reserved;
- uint32_t flags_size;
-} QEMU_PACKED AHCI_SG;
-
-typedef struct AHCIDevice AHCIDevice;
-
-typedef struct NCQTransferState {
- AHCIDevice *drive;
- BlockAIOCB *aiocb;
- AHCICmdHdr *cmdh;
- QEMUSGList sglist;
- BlockAcctCookie acct;
- uint32_t sector_count;
- uint64_t lba;
- uint8_t tag;
- uint8_t cmd;
- uint8_t slot;
- bool used;
- bool halt;
-} NCQTransferState;
-
-struct AHCIDevice {
- IDEDMA dma;
- IDEBus port;
- int port_no;
- uint32_t port_state;
- uint32_t finished;
- AHCIPortRegs port_regs;
- struct AHCIState *hba;
- QEMUBH *check_bh;
- uint8_t *lst;
- uint8_t *res_fis;
- bool done_atapi_packet;
- int32_t busy_slot;
- bool init_d2h_sent;
- AHCICmdHdr *cur_cmd;
- NCQTransferState ncq_tfs[AHCI_MAX_CMDS];
-};
-
-typedef struct AHCIState {
- DeviceState *container;
-
- AHCIDevice *dev;
- AHCIControlRegs control_regs;
- MemoryRegion mem;
- MemoryRegion idp; /* Index-Data Pair I/O port space */
- unsigned idp_offset; /* Offset of index in I/O port space */
- uint32_t idp_index; /* Current IDP index */
- int32_t ports;
- qemu_irq irq;
- AddressSpace *as;
-} AHCIState;
-
-typedef struct AHCIPCIState {
- /*< private >*/
- PCIDevice parent_obj;
- /*< public >*/
-
- AHCIState ahci;
-} AHCIPCIState;
-
-#define TYPE_ICH9_AHCI "ich9-ahci"
-
-#define ICH_AHCI(obj) \
- OBJECT_CHECK(AHCIPCIState, (obj), TYPE_ICH9_AHCI)
-
-extern const VMStateDescription vmstate_ahci;
-
-#define VMSTATE_AHCI(_field, _state) { \
- .name = (stringify(_field)), \
- .size = sizeof(AHCIState), \
- .vmsd = &vmstate_ahci, \
- .flags = VMS_STRUCT, \
- .offset = vmstate_offset_value(_state, _field, AHCIState), \
-}
-
-/**
- * NCQFrame is the same as a Register H2D FIS (described in SATA 3.2),
- * but some fields have been re-mapped and re-purposed, as seen in
- * SATA 3.2 section 13.6.4.1 ("READ FPDMA QUEUED")
- *
- * cmd_fis[3], feature 7:0, becomes sector count 7:0.
- * cmd_fis[7], device 7:0, uses bit 7 as the Force Unit Access bit.
- * cmd_fis[11], feature 15:8, becomes sector count 15:8.
- * cmd_fis[12], count 7:0, becomes the NCQ TAG (7:3) and RARC bit (0)
- * cmd_fis[13], count 15:8, becomes the priority value (7:6)
- * bytes 16-19 become an le32 "auxiliary" field.
- */
-typedef struct NCQFrame {
- uint8_t fis_type;
- uint8_t c;
- uint8_t command;
- uint8_t sector_count_low; /* (feature 7:0) */
- uint8_t lba0;
- uint8_t lba1;
- uint8_t lba2;
- uint8_t fua; /* (device 7:0) */
- uint8_t lba3;
- uint8_t lba4;
- uint8_t lba5;
- uint8_t sector_count_high; /* (feature 15:8) */
- uint8_t tag; /* (count 0:7) */
- uint8_t prio; /* (count 15:8) */
- uint8_t icc;
- uint8_t control;
- uint8_t aux0;
- uint8_t aux1;
- uint8_t aux2;
- uint8_t aux3;
-} QEMU_PACKED NCQFrame;
-
-typedef struct SDBFIS {
- uint8_t type;
- uint8_t flags;
- uint8_t status;
- uint8_t error;
- uint32_t payload;
-} QEMU_PACKED SDBFIS;
-
-void ahci_realize(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports);
-void ahci_init(AHCIState *s, DeviceState *qdev);
-void ahci_uninit(AHCIState *s);
-
-void ahci_reset(AHCIState *s);
-
-void ahci_ide_create_devs(PCIDevice *dev, DriveInfo **hd);
-
-#define TYPE_SYSBUS_AHCI "sysbus-ahci"
-#define SYSBUS_AHCI(obj) OBJECT_CHECK(SysbusAHCIState, (obj), TYPE_SYSBUS_AHCI)
-
-typedef struct SysbusAHCIState {
- /*< private >*/
- SysBusDevice parent_obj;
- /*< public >*/
-
- AHCIState ahci;
- uint32_t num_ports;
-} SysbusAHCIState;
-
-#define TYPE_ALLWINNER_AHCI "allwinner-ahci"
-#define ALLWINNER_AHCI(obj) OBJECT_CHECK(AllwinnerAHCIState, (obj), \
- TYPE_ALLWINNER_AHCI)
-
-#define ALLWINNER_AHCI_MMIO_OFF 0x80
-#define ALLWINNER_AHCI_MMIO_SIZE 0x80
-
-struct AllwinnerAHCIState {
- /*< private >*/
- SysbusAHCIState parent_obj;
- /*< public >*/
-
- MemoryRegion mmio;
- uint32_t regs[ALLWINNER_AHCI_MMIO_SIZE/4];
-};
-
-#endif /* HW_IDE_AHCI_H */
diff --git a/hw/ide/internal.h b/hw/ide/internal.h
deleted file mode 100644
index 773928af77..0000000000
--- a/hw/ide/internal.h
+++ /dev/null
@@ -1,635 +0,0 @@
-#ifndef HW_IDE_INTERNAL_H
-#define HW_IDE_INTERNAL_H
-
-/*
- * QEMU IDE Emulation -- internal header file
- * only files in hw/ide/ are supposed to include this file.
- * non-internal declarations are in hw/ide.h
- */
-#include <hw/ide.h>
-#include <hw/isa/isa.h>
-#include "sysemu/dma.h"
-#include "sysemu/sysemu.h"
-#include "hw/block/block.h"
-#include "block/scsi.h"
-
-/* debug IDE devices */
-//#define DEBUG_IDE
-//#define DEBUG_IDE_ATAPI
-//#define DEBUG_AIO
-#define USE_DMA_CDROM
-
-typedef struct IDEBus IDEBus;
-typedef struct IDEDevice IDEDevice;
-typedef struct IDEState IDEState;
-typedef struct IDEDMA IDEDMA;
-typedef struct IDEDMAOps IDEDMAOps;
-
-#define TYPE_IDE_BUS "IDE"
-#define IDE_BUS(obj) OBJECT_CHECK(IDEBus, (obj), TYPE_IDE_BUS)
-
-/* Bits of HD_STATUS */
-#define ERR_STAT 0x01
-#define INDEX_STAT 0x02
-#define ECC_STAT 0x04 /* Corrected error */
-#define DRQ_STAT 0x08
-#define SEEK_STAT 0x10
-#define SRV_STAT 0x10
-#define WRERR_STAT 0x20
-#define READY_STAT 0x40
-#define BUSY_STAT 0x80
-
-/* Bits for HD_ERROR */
-#define MARK_ERR 0x01 /* Bad address mark */
-#define TRK0_ERR 0x02 /* couldn't find track 0 */
-#define ABRT_ERR 0x04 /* Command aborted */
-#define MCR_ERR 0x08 /* media change request */
-#define ID_ERR 0x10 /* ID field not found */
-#define MC_ERR 0x20 /* media changed */
-#define ECC_ERR 0x40 /* Uncorrectable ECC error */
-#define BBD_ERR 0x80 /* pre-EIDE meaning: block marked bad */
-#define ICRC_ERR 0x80 /* new meaning: CRC error during transfer */
-
-/* Bits of HD_NSECTOR */
-#define CD 0x01
-#define IO 0x02
-#define REL 0x04
-#define TAG_MASK 0xf8
-
-#define IDE_CMD_RESET 0x04
-#define IDE_CMD_DISABLE_IRQ 0x02
-
-/* ACS-2 T13/2015-D Table B.2 Command codes */
-#define WIN_NOP 0x00
-/* reserved 0x01..0x02 */
-#define CFA_REQ_EXT_ERROR_CODE 0x03 /* CFA Request Extended Error Code */
-/* reserved 0x04..0x05 */
-#define WIN_DSM 0x06
-/* reserved 0x07 */
-#define WIN_DEVICE_RESET 0x08
-/* reserved 0x09..0x0a */
-/* REQUEST SENSE DATA EXT 0x0B */
-/* reserved 0x0C..0x0F */
-#define WIN_RECAL 0x10 /* obsolete since ATA4 */
-/* obsolete since ATA3, retired in ATA4 0x11..0x1F */
-#define WIN_READ 0x20 /* 28-Bit */
-#define WIN_READ_ONCE 0x21 /* 28-Bit w/o retries, obsolete since ATA5 */
-/* obsolete since ATA4 0x22..0x23 */
-#define WIN_READ_EXT 0x24 /* 48-Bit */
-#define WIN_READDMA_EXT 0x25 /* 48-Bit */
-#define WIN_READDMA_QUEUED_EXT 0x26 /* 48-Bit, obsolete since ACS2 */
-#define WIN_READ_NATIVE_MAX_EXT 0x27 /* 48-Bit */
-/* reserved 0x28 */
-#define WIN_MULTREAD_EXT 0x29 /* 48-Bit */
-/* READ STREAM DMA EXT 0x2A */
-/* READ STREAM EXT 0x2B */
-/* reserved 0x2C..0x2E */
-/* READ LOG EXT 0x2F */
-#define WIN_WRITE 0x30 /* 28-Bit */
-#define WIN_WRITE_ONCE 0x31 /* 28-Bit w/o retries, obsolete since ATA5 */
-/* obsolete since ATA4 0x32..0x33 */
-#define WIN_WRITE_EXT 0x34 /* 48-Bit */
-#define WIN_WRITEDMA_EXT 0x35 /* 48-Bit */
-#define WIN_WRITEDMA_QUEUED_EXT 0x36 /* 48-Bit */
-#define WIN_SET_MAX_EXT 0x37 /* 48-Bit, obsolete since ACS2 */
-#define WIN_SET_MAX_EXT 0x37 /* 48-Bit */
-#define CFA_WRITE_SECT_WO_ERASE 0x38 /* CFA Write Sectors without erase */
-#define WIN_MULTWRITE_EXT 0x39 /* 48-Bit */
-/* WRITE STREAM DMA EXT 0x3A */
-/* WRITE STREAM EXT 0x3B */
-#define WIN_WRITE_VERIFY 0x3C /* 28-Bit, obsolete since ATA4 */
-/* WRITE DMA FUA EXT 0x3D */
-/* obsolete since ACS2 0x3E */
-/* WRITE LOG EXT 0x3F */
-#define WIN_VERIFY 0x40 /* 28-Bit - Read Verify Sectors */
-#define WIN_VERIFY_ONCE 0x41 /* 28-Bit - w/o retries, obsolete since ATA5 */
-#define WIN_VERIFY_EXT 0x42 /* 48-Bit */
-/* reserved 0x43..0x44 */
-/* WRITE UNCORRECTABLE EXT 0x45 */
-/* reserved 0x46 */
-/* READ LOG DMA EXT 0x47 */
-/* reserved 0x48..0x4F */
-/* obsolete since ATA4 0x50 */
-/* CONFIGURE STREAM 0x51 */
-/* reserved 0x52..0x56 */
-/* WRITE LOG DMA EXT 0x57 */
-/* reserved 0x58..0x5A */
-/* TRUSTED NON DATA 0x5B */
-/* TRUSTED RECEIVE 0x5C */
-/* TRUSTED RECEIVE DMA 0x5D */
-/* TRUSTED SEND 0x5E */
-/* TRUSTED SEND DMA 0x5F */
-/* READ FPDMA QUEUED 0x60 */
-/* WRITE FPDMA QUEUED 0x61 */
-/* reserved 0x62->0x6F */
-#define WIN_SEEK 0x70 /* obsolete since ATA7 */
-/* reserved 0x71-0x7F */
-/* vendor specific 0x80-0x86 */
-#define CFA_TRANSLATE_SECTOR 0x87 /* CFA Translate Sector */
-/* vendor specific 0x88-0x8F */
-#define WIN_DIAGNOSE 0x90
-#define WIN_SPECIFY 0x91 /* set drive geometry translation, obsolete since ATA6 */
-#define WIN_DOWNLOAD_MICROCODE 0x92
-/* DOWNLOAD MICROCODE DMA 0x93 */
-#define WIN_STANDBYNOW2 0x94 /* retired in ATA4 */
-#define WIN_IDLEIMMEDIATE2 0x95 /* force drive to become "ready", retired in ATA4 */
-#define WIN_STANDBY2 0x96 /* retired in ATA4 */
-#define WIN_SETIDLE2 0x97 /* retired in ATA4 */
-#define WIN_CHECKPOWERMODE2 0x98 /* retired in ATA4 */
-#define WIN_SLEEPNOW2 0x99 /* retired in ATA4 */
-/* vendor specific 0x9A */
-/* reserved 0x9B..0x9F */
-#define WIN_PACKETCMD 0xA0 /* Send a packet command. */
-#define WIN_PIDENTIFY 0xA1 /* identify ATAPI device */
-#define WIN_QUEUED_SERVICE 0xA2 /* obsolete since ACS2 */
-/* reserved 0xA3..0xAF */
-#define WIN_SMART 0xB0 /* self-monitoring and reporting */
-/* Device Configuration Overlay 0xB1 */
-/* reserved 0xB2..0xB3 */
-/* Sanitize Device 0xB4 */
-/* reserved 0xB5 */
-/* NV Cache 0xB6 */
-/* reserved for CFA 0xB7..0xBB */
-#define CFA_ACCESS_METADATA_STORAGE 0xB8
-/* reserved 0xBC..0xBF */
-#define CFA_ERASE_SECTORS 0xC0 /* microdrives implement as NOP */
-/* vendor specific 0xC1..0xC3 */
-#define WIN_MULTREAD 0xC4 /* read sectors using multiple mode*/
-#define WIN_MULTWRITE 0xC5 /* write sectors using multiple mode */
-#define WIN_SETMULT 0xC6 /* enable/disable multiple mode */
-#define WIN_READDMA_QUEUED 0xC7 /* read sectors using Queued DMA transfers, obsolete since ACS2 */
-#define WIN_READDMA 0xC8 /* read sectors using DMA transfers */
-#define WIN_READDMA_ONCE 0xC9 /* 28-Bit - w/o retries, obsolete since ATA5 */
-#define WIN_WRITEDMA 0xCA /* write sectors using DMA transfers */
-#define WIN_WRITEDMA_ONCE 0xCB /* 28-Bit - w/o retries, obsolete since ATA5 */
-#define WIN_WRITEDMA_QUEUED 0xCC /* write sectors using Queued DMA transfers, obsolete since ACS2 */
-#define CFA_WRITE_MULTI_WO_ERASE 0xCD /* CFA Write multiple without erase */
-/* WRITE MULTIPLE FUA EXT 0xCE */
-/* reserved 0xCF..0xDO */
-/* CHECK MEDIA CARD TYPE 0xD1 */
-/* reserved for media card pass through 0xD2..0xD4 */
-/* reserved 0xD5..0xD9 */
-#define WIN_GETMEDIASTATUS 0xDA /* obsolete since ATA8 */
-/* obsolete since ATA3, retired in ATA4 0xDB..0xDD */
-#define WIN_DOORLOCK 0xDE /* lock door on removable drives, obsolete since ATA8 */
-#define WIN_DOORUNLOCK 0xDF /* unlock door on removable drives, obsolete since ATA8 */
-#define WIN_STANDBYNOW1 0xE0
-#define WIN_IDLEIMMEDIATE 0xE1 /* force drive to become "ready" */
-#define WIN_STANDBY 0xE2 /* Set device in Standby Mode */
-#define WIN_SETIDLE1 0xE3
-#define WIN_READ_BUFFER 0xE4 /* force read only 1 sector */
-#define WIN_CHECKPOWERMODE1 0xE5
-#define WIN_SLEEPNOW1 0xE6
-#define WIN_FLUSH_CACHE 0xE7
-#define WIN_WRITE_BUFFER 0xE8 /* force write only 1 sector */
-/* READ BUFFER DMA 0xE9 */
-#define WIN_FLUSH_CACHE_EXT 0xEA /* 48-Bit */
-/* WRITE BUFFER DMA 0xEB */
-#define WIN_IDENTIFY 0xEC /* ask drive to identify itself */
-#define WIN_MEDIAEJECT 0xED /* obsolete since ATA8 */
-/* obsolete since ATA4 0xEE */
-#define WIN_SETFEATURES 0xEF /* set special drive features */
-#define IBM_SENSE_CONDITION 0xF0 /* measure disk temperature, vendor specific */
-#define WIN_SECURITY_SET_PASS 0xF1
-#define WIN_SECURITY_UNLOCK 0xF2
-#define WIN_SECURITY_ERASE_PREPARE 0xF3
-#define WIN_SECURITY_ERASE_UNIT 0xF4
-#define WIN_SECURITY_FREEZE_LOCK 0xF5
-#define CFA_WEAR_LEVEL 0xF5 /* microdrives implement as NOP; not specified in T13! */
-#define WIN_SECURITY_DISABLE 0xF6
-/* vendor specific 0xF7 */
-#define WIN_READ_NATIVE_MAX 0xF8 /* return the native maximum address */
-#define WIN_SET_MAX 0xF9
-/* vendor specific 0xFA..0xFF */
-
-/* set to 1 set disable mult support */
-#define MAX_MULT_SECTORS 16
-
-#define IDE_DMA_BUF_SECTORS 256
-
-/* feature values for Data Set Management */
-#define DSM_TRIM 0x01
-
-#if (IDE_DMA_BUF_SECTORS < MAX_MULT_SECTORS)
-#error "IDE_DMA_BUF_SECTORS must be bigger or equal to MAX_MULT_SECTORS"
-#endif
-
-/* ATAPI defines */
-
-#define ATAPI_PACKET_SIZE 12
-
-/* The generic packet command opcodes for CD/DVD Logical Units,
- * From Table 57 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */
-#define GPCMD_BLANK 0xa1
-#define GPCMD_CLOSE_TRACK 0x5b
-#define GPCMD_FLUSH_CACHE 0x35
-#define GPCMD_FORMAT_UNIT 0x04
-#define GPCMD_GET_CONFIGURATION 0x46
-#define GPCMD_GET_EVENT_STATUS_NOTIFICATION 0x4a
-#define GPCMD_GET_PERFORMANCE 0xac
-#define GPCMD_INQUIRY 0x12
-#define GPCMD_LOAD_UNLOAD 0xa6
-#define GPCMD_MECHANISM_STATUS 0xbd
-#define GPCMD_MODE_SELECT_10 0x55
-#define GPCMD_MODE_SENSE_10 0x5a
-#define GPCMD_PAUSE_RESUME 0x4b
-#define GPCMD_PLAY_AUDIO_10 0x45
-#define GPCMD_PLAY_AUDIO_MSF 0x47
-#define GPCMD_PLAY_AUDIO_TI 0x48
-#define GPCMD_PLAY_CD 0xbc
-#define GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e
-#define GPCMD_READ_10 0x28
-#define GPCMD_READ_12 0xa8
-#define GPCMD_READ_CDVD_CAPACITY 0x25
-#define GPCMD_READ_CD 0xbe
-#define GPCMD_READ_CD_MSF 0xb9
-#define GPCMD_READ_DISC_INFO 0x51
-#define GPCMD_READ_DVD_STRUCTURE 0xad
-#define GPCMD_READ_FORMAT_CAPACITIES 0x23
-#define GPCMD_READ_HEADER 0x44
-#define GPCMD_READ_TRACK_RZONE_INFO 0x52
-#define GPCMD_READ_SUBCHANNEL 0x42
-#define GPCMD_READ_TOC_PMA_ATIP 0x43
-#define GPCMD_REPAIR_RZONE_TRACK 0x58
-#define GPCMD_REPORT_KEY 0xa4
-#define GPCMD_REQUEST_SENSE 0x03
-#define GPCMD_RESERVE_RZONE_TRACK 0x53
-#define GPCMD_SCAN 0xba
-#define GPCMD_SEEK 0x2b
-#define GPCMD_SEND_DVD_STRUCTURE 0xad
-#define GPCMD_SEND_EVENT 0xa2
-#define GPCMD_SEND_KEY 0xa3
-#define GPCMD_SEND_OPC 0x54
-#define GPCMD_SET_READ_AHEAD 0xa7
-#define GPCMD_SET_STREAMING 0xb6
-#define GPCMD_START_STOP_UNIT 0x1b
-#define GPCMD_STOP_PLAY_SCAN 0x4e
-#define GPCMD_TEST_UNIT_READY 0x00
-#define GPCMD_VERIFY_10 0x2f
-#define GPCMD_WRITE_10 0x2a
-#define GPCMD_WRITE_AND_VERIFY_10 0x2e
-/* This is listed as optional in ATAPI 2.6, but is (curiously)
- * missing from Mt. Fuji, Table 57. It _is_ mentioned in Mt. Fuji
- * Table 377 as an MMC command for SCSi devices though... Most ATAPI
- * drives support it. */
-#define GPCMD_SET_SPEED 0xbb
-/* This seems to be a SCSI specific CD-ROM opcode
- * to play data at track/index */
-#define GPCMD_PLAYAUDIO_TI 0x48
-/*
- * From MS Media Status Notification Support Specification. For
- * older drives only.
- */
-#define GPCMD_GET_MEDIA_STATUS 0xda
-#define GPCMD_MODE_SENSE_6 0x1a
-
-#define ATAPI_INT_REASON_CD 0x01 /* 0 = data transfer */
-#define ATAPI_INT_REASON_IO 0x02 /* 1 = transfer to the host */
-#define ATAPI_INT_REASON_REL 0x04
-#define ATAPI_INT_REASON_TAG 0xf8
-
-/* same constants as bochs */
-#define ASC_NO_SEEK_COMPLETE 0x02
-#define ASC_ILLEGAL_OPCODE 0x20
-#define ASC_LOGICAL_BLOCK_OOR 0x21
-#define ASC_INV_FIELD_IN_CMD_PACKET 0x24
-#define ASC_MEDIUM_MAY_HAVE_CHANGED 0x28
-#define ASC_INCOMPATIBLE_FORMAT 0x30
-#define ASC_MEDIUM_NOT_PRESENT 0x3a
-#define ASC_SAVING_PARAMETERS_NOT_SUPPORTED 0x39
-#define ASC_DATA_PHASE_ERROR 0x4b
-#define ASC_MEDIA_REMOVAL_PREVENTED 0x53
-
-#define CFA_NO_ERROR 0x00
-#define CFA_MISC_ERROR 0x09
-#define CFA_INVALID_COMMAND 0x20
-#define CFA_INVALID_ADDRESS 0x21
-#define CFA_ADDRESS_OVERFLOW 0x2f
-
-#define SMART_READ_DATA 0xd0
-#define SMART_READ_THRESH 0xd1
-#define SMART_ATTR_AUTOSAVE 0xd2
-#define SMART_SAVE_ATTR 0xd3
-#define SMART_EXECUTE_OFFLINE 0xd4
-#define SMART_READ_LOG 0xd5
-#define SMART_WRITE_LOG 0xd6
-#define SMART_ENABLE 0xd8
-#define SMART_DISABLE 0xd9
-#define SMART_STATUS 0xda
-
-typedef enum { IDE_HD, IDE_CD, IDE_CFATA } IDEDriveKind;
-
-typedef void EndTransferFunc(IDEState *);
-
-typedef void DMAStartFunc(IDEDMA *, IDEState *, BlockCompletionFunc *);
-typedef void DMAVoidFunc(IDEDMA *);
-typedef int DMAIntFunc(IDEDMA *, int);
-typedef int32_t DMAInt32Func(IDEDMA *, int32_t len);
-typedef void DMAu32Func(IDEDMA *, uint32_t);
-typedef void DMAStopFunc(IDEDMA *, bool);
-typedef void DMARestartFunc(void *, int, RunState);
-
-struct unreported_events {
- bool eject_request;
- bool new_media;
-};
-
-enum ide_dma_cmd {
- IDE_DMA_READ,
- IDE_DMA_WRITE,
- IDE_DMA_TRIM,
- IDE_DMA_ATAPI,
-};
-
-#define ide_cmd_is_read(s) \
- ((s)->dma_cmd == IDE_DMA_READ)
-
-typedef struct IDEBufferedRequest {
- QLIST_ENTRY(IDEBufferedRequest) list;
- struct iovec iov;
- QEMUIOVector qiov;
- QEMUIOVector *original_qiov;
- BlockCompletionFunc *original_cb;
- void *original_opaque;
- bool orphaned;
-} IDEBufferedRequest;
-
-/* NOTE: IDEState represents in fact one drive */
-struct IDEState {
- IDEBus *bus;
- uint8_t unit;
- /* ide config */
- IDEDriveKind drive_kind;
- int cylinders, heads, sectors, chs_trans;
- int64_t nb_sectors;
- int mult_sectors;
- int identify_set;
- uint8_t identify_data[512];
- int drive_serial;
- char drive_serial_str[21];
- char drive_model_str[41];
- uint64_t wwn;
- /* ide regs */
- uint8_t feature;
- uint8_t error;
- uint32_t nsector;
- uint8_t sector;
- uint8_t lcyl;
- uint8_t hcyl;
- /* other part of tf for lba48 support */
- uint8_t hob_feature;
- uint8_t hob_nsector;
- uint8_t hob_sector;
- uint8_t hob_lcyl;
- uint8_t hob_hcyl;
-
- uint8_t select;
- uint8_t status;
-
- /* set for lba48 access */
- uint8_t lba48;
- BlockBackend *blk;
- char version[9];
- /* ATAPI specific */
- struct unreported_events events;
- uint8_t sense_key;
- uint8_t asc;
- bool tray_open;
- bool tray_locked;
- uint8_t cdrom_changed;
- int packet_transfer_size;
- int elementary_transfer_size;
- int32_t io_buffer_index;
- int lba;
- int cd_sector_size;
- int atapi_dma; /* true if dma is requested for the packet cmd */
- BlockAcctCookie acct;
- BlockAIOCB *pio_aiocb;
- struct iovec iov;
- QEMUIOVector qiov;
- QLIST_HEAD(, IDEBufferedRequest) buffered_requests;
- /* ATA DMA state */
- uint64_t io_buffer_offset;
- int32_t io_buffer_size;
- QEMUSGList sg;
- /* PIO transfer handling */
- int req_nb_sectors; /* number of sectors per interrupt */
- EndTransferFunc *end_transfer_func;
- uint8_t *data_ptr;
- uint8_t *data_end;
- uint8_t *io_buffer;
- /* PIO save/restore */
- int32_t io_buffer_total_len;
- int32_t cur_io_buffer_offset;
- int32_t cur_io_buffer_len;
- uint8_t end_transfer_fn_idx;
- QEMUTimer *sector_write_timer; /* only used for win2k install hack */
- uint32_t irq_count; /* counts IRQs when using win2k install hack */
- /* CF-ATA extended error */
- uint8_t ext_error;
- /* CF-ATA metadata storage */
- uint32_t mdata_size;
- uint8_t *mdata_storage;
- int media_changed;
- enum ide_dma_cmd dma_cmd;
- /* SMART */
- uint8_t smart_enabled;
- uint8_t smart_autosave;
- int smart_errors;
- uint8_t smart_selftest_count;
- uint8_t *smart_selftest_data;
- /* AHCI */
- int ncq_queues;
-};
-
-struct IDEDMAOps {
- DMAStartFunc *start_dma;
- DMAVoidFunc *start_transfer;
- DMAInt32Func *prepare_buf;
- DMAu32Func *commit_buf;
- DMAIntFunc *rw_buf;
- DMAVoidFunc *restart;
- DMAVoidFunc *restart_dma;
- DMAStopFunc *set_inactive;
- DMAVoidFunc *cmd_done;
- DMAVoidFunc *reset;
-};
-
-struct IDEDMA {
- const struct IDEDMAOps *ops;
- struct iovec iov;
- QEMUIOVector qiov;
- BlockAIOCB *aiocb;
-};
-
-struct IDEBus {
- BusState qbus;
- IDEDevice *master;
- IDEDevice *slave;
- IDEState ifs[2];
- QEMUBH *bh;
-
- int bus_id;
- int max_units;
- IDEDMA *dma;
- uint8_t unit;
- uint8_t cmd;
- qemu_irq irq;
-
- int error_status;
- uint8_t retry_unit;
- int64_t retry_sector_num;
- uint32_t retry_nsector;
-};
-
-#define TYPE_IDE_DEVICE "ide-device"
-#define IDE_DEVICE(obj) \
- OBJECT_CHECK(IDEDevice, (obj), TYPE_IDE_DEVICE)
-#define IDE_DEVICE_CLASS(klass) \
- OBJECT_CLASS_CHECK(IDEDeviceClass, (klass), TYPE_IDE_DEVICE)
-#define IDE_DEVICE_GET_CLASS(obj) \
- OBJECT_GET_CLASS(IDEDeviceClass, (obj), TYPE_IDE_DEVICE)
-
-typedef struct IDEDeviceClass {
- DeviceClass parent_class;
- int (*init)(IDEDevice *dev);
-} IDEDeviceClass;
-
-struct IDEDevice {
- DeviceState qdev;
- uint32_t unit;
- BlockConf conf;
- int chs_trans;
- char *version;
- char *serial;
- char *model;
- uint64_t wwn;
-};
-
-/* These are used for the error_status field of IDEBus */
-#define IDE_RETRY_MASK 0xf8
-#define IDE_RETRY_DMA 0x08
-#define IDE_RETRY_PIO 0x10
-#define IDE_RETRY_ATAPI 0x20 /* reused IDE_RETRY_READ bit */
-#define IDE_RETRY_READ 0x20
-#define IDE_RETRY_FLUSH 0x40
-#define IDE_RETRY_TRIM 0x80
-#define IDE_RETRY_HBA 0x100
-
-#define IS_IDE_RETRY_DMA(_status) \
- ((_status) & IDE_RETRY_DMA)
-
-#define IS_IDE_RETRY_PIO(_status) \
- ((_status) & IDE_RETRY_PIO)
-
-/*
- * The method of the IDE_RETRY_ATAPI determination is to use a previously
- * impossible bit combination as a new status value.
- */
-#define IS_IDE_RETRY_ATAPI(_status) \
- (((_status) & IDE_RETRY_MASK) == IDE_RETRY_ATAPI)
-
-static inline uint8_t ide_dma_cmd_to_retry(uint8_t dma_cmd)
-{
- switch (dma_cmd) {
- case IDE_DMA_READ:
- return IDE_RETRY_DMA | IDE_RETRY_READ;
- case IDE_DMA_WRITE:
- return IDE_RETRY_DMA;
- case IDE_DMA_TRIM:
- return IDE_RETRY_DMA | IDE_RETRY_TRIM;
- case IDE_DMA_ATAPI:
- return IDE_RETRY_ATAPI;
- default:
- break;
- }
- return 0;
-}
-
-static inline IDEState *idebus_active_if(IDEBus *bus)
-{
- return bus->ifs + bus->unit;
-}
-
-static inline void ide_set_irq(IDEBus *bus)
-{
- if (!(bus->cmd & IDE_CMD_DISABLE_IRQ)) {
- qemu_irq_raise(bus->irq);
- }
-}
-
-/* hw/ide/core.c */
-extern const VMStateDescription vmstate_ide_bus;
-
-#define VMSTATE_IDE_BUS(_field, _state) \
- VMSTATE_STRUCT(_field, _state, 1, vmstate_ide_bus, IDEBus)
-
-#define VMSTATE_IDE_BUS_ARRAY(_field, _state, _num) \
- VMSTATE_STRUCT_ARRAY(_field, _state, _num, 1, vmstate_ide_bus, IDEBus)
-
-extern const VMStateDescription vmstate_ide_drive;
-
-#define VMSTATE_IDE_DRIVES(_field, _state) \
- VMSTATE_STRUCT_ARRAY(_field, _state, 2, 3, vmstate_ide_drive, IDEState)
-
-#define VMSTATE_IDE_DRIVE(_field, _state) \
- VMSTATE_STRUCT(_field, _state, 1, vmstate_ide_drive, IDEState)
-
-void ide_bus_reset(IDEBus *bus);
-int64_t ide_get_sector(IDEState *s);
-void ide_set_sector(IDEState *s, int64_t sector_num);
-
-void ide_start_dma(IDEState *s, BlockCompletionFunc *cb);
-void dma_buf_commit(IDEState *s, uint32_t tx_bytes);
-void ide_dma_error(IDEState *s);
-void ide_abort_command(IDEState *s);
-
-void ide_atapi_cmd_ok(IDEState *s);
-void ide_atapi_cmd_error(IDEState *s, int sense_key, int asc);
-void ide_atapi_dma_restart(IDEState *s);
-void ide_atapi_io_error(IDEState *s, int ret);
-
-void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val);
-uint32_t ide_ioport_read(void *opaque, uint32_t addr1);
-uint32_t ide_status_read(void *opaque, uint32_t addr);
-void ide_cmd_write(void *opaque, uint32_t addr, uint32_t val);
-void ide_data_writew(void *opaque, uint32_t addr, uint32_t val);
-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, BlockBackend *blk, IDEDriveKind kind,
- const char *version, const char *serial, const char *model,
- uint64_t wwn,
- uint32_t cylinders, uint32_t heads, uint32_t secs,
- int chs_trans);
-void ide_init2(IDEBus *bus, qemu_irq irq);
-void ide_init_ioport(IDEBus *bus, ISADevice *isa, int iobase, int iobase2);
-void ide_register_restart_cb(IDEBus *bus);
-
-void ide_exec_cmd(IDEBus *bus, uint32_t val);
-
-void ide_transfer_start(IDEState *s, uint8_t *buf, int size,
- EndTransferFunc *end_transfer_func);
-void ide_transfer_stop(IDEState *s);
-void ide_set_inactive(IDEState *s, bool more);
-BlockAIOCB *ide_issue_trim(
- int64_t offset, QEMUIOVector *qiov,
- BlockCompletionFunc *cb, void *cb_opaque, void *opaque);
-BlockAIOCB *ide_buffered_readv(IDEState *s, int64_t sector_num,
- QEMUIOVector *iov, int nb_sectors,
- BlockCompletionFunc *cb, void *opaque);
-void ide_cancel_dma_sync(IDEState *s);
-
-/* hw/ide/atapi.c */
-void ide_atapi_cmd(IDEState *s);
-void ide_atapi_cmd_reply_end(IDEState *s);
-
-/* hw/ide/qdev.c */
-void ide_bus_new(IDEBus *idebus, size_t idebus_size, DeviceState *dev,
- int bus_id, int max_units);
-IDEDevice *ide_create_drive(IDEBus *bus, int unit, DriveInfo *drive);
-
-int ide_handle_rw_error(IDEState *s, int error, int op);
-
-#endif /* HW_IDE_INTERNAL_H */
diff --git a/hw/ide/macio.c b/hw/ide/macio.c
index fa57352fc8..56cc50661f 100644
--- a/hw/ide/macio.c
+++ b/hw/ide/macio.c
@@ -406,7 +406,7 @@ static void pmac_ide_flush(DBDMA_io *io)
IDEState *s = idebus_active_if(&m->bus);
if (s->bus->dma->aiocb) {
- blk_drain_all();
+ blk_drain(s->blk);
}
}
diff --git a/hw/ide/pci.h b/hw/ide/pci.h
deleted file mode 100644
index 0f2d4b91a7..0000000000
--- a/hw/ide/pci.h
+++ /dev/null
@@ -1,76 +0,0 @@
-#ifndef HW_IDE_PCI_H
-#define HW_IDE_PCI_H
-
-#include <hw/ide/internal.h>
-
-#define BM_STATUS_DMAING 0x01
-#define BM_STATUS_ERROR 0x02
-#define BM_STATUS_INT 0x04
-
-#define BM_CMD_START 0x01
-#define BM_CMD_READ 0x08
-
-typedef struct BMDMAState {
- IDEDMA dma;
- uint8_t cmd;
- uint8_t status;
- uint32_t addr;
-
- IDEBus *bus;
- /* current transfer state */
- uint32_t cur_addr;
- uint32_t cur_prd_last;
- uint32_t cur_prd_addr;
- uint32_t cur_prd_len;
- BlockCompletionFunc *dma_cb;
- MemoryRegion addr_ioport;
- MemoryRegion extra_io;
- qemu_irq irq;
-
- /* Bit 0-2 and 7: BM status register
- * Bit 3-6: bus->error_status */
- uint8_t migration_compat_status;
- uint8_t migration_retry_unit;
- int64_t migration_retry_sector_num;
- uint32_t migration_retry_nsector;
-
- struct PCIIDEState *pci_dev;
-} BMDMAState;
-
-typedef struct CMD646BAR {
- MemoryRegion cmd;
- MemoryRegion data;
- IDEBus *bus;
- struct PCIIDEState *pci_dev;
-} CMD646BAR;
-
-#define TYPE_PCI_IDE "pci-ide"
-#define PCI_IDE(obj) OBJECT_CHECK(PCIIDEState, (obj), TYPE_PCI_IDE)
-
-typedef struct PCIIDEState {
- /*< private >*/
- PCIDevice parent_obj;
- /*< public >*/
-
- IDEBus bus[2];
- BMDMAState bmdma[2];
- uint32_t secondary; /* used only for cmd646 */
- MemoryRegion bmdma_bar;
- CMD646BAR cmd646_bar[2]; /* used only for cmd646 */
-} PCIIDEState;
-
-
-static inline IDEState *bmdma_active_if(BMDMAState *bmdma)
-{
- assert(bmdma->bus->retry_unit != (uint8_t)-1);
- return bmdma->bus->ifs + bmdma->bus->retry_unit;
-}
-
-
-void bmdma_init(IDEBus *bus, BMDMAState *bm, PCIIDEState *d);
-void bmdma_cmd_writeb(BMDMAState *bm, uint32_t val);
-extern MemoryRegionOps bmdma_addr_ioport_ops;
-void pci_ide_create_devs(PCIDevice *dev, DriveInfo **hd_table);
-
-extern const VMStateDescription vmstate_ide_pci;
-#endif
diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c
index 1d932ec19f..dc57e2c762 100644
--- a/hw/input/pckbd.c
+++ b/hw/input/pckbd.c
@@ -146,7 +146,7 @@ typedef struct KBDState {
qemu_irq irq_kbd;
qemu_irq irq_mouse;
- qemu_irq *a20_out;
+ qemu_irq a20_out;
hwaddr mask;
} KBDState;
@@ -224,9 +224,7 @@ static void outport_write(KBDState *s, uint32_t val)
{
DPRINTF("kbd: write outport=0x%02x\n", val);
s->outport = val;
- if (s->a20_out) {
- qemu_set_irq(*s->a20_out, (val >> 1) & 1);
- }
+ qemu_set_irq(s->a20_out, (val >> 1) & 1);
if (!(val & 1)) {
qemu_system_reset_request();
}
@@ -295,15 +293,11 @@ static void kbd_write_command(void *opaque, hwaddr addr,
kbd_queue(s, s->outport, 0);
break;
case KBD_CCMD_ENABLE_A20:
- if (s->a20_out) {
- qemu_irq_raise(*s->a20_out);
- }
+ qemu_irq_raise(s->a20_out);
s->outport |= KBD_OUT_A20;
break;
case KBD_CCMD_DISABLE_A20:
- if (s->a20_out) {
- qemu_irq_lower(*s->a20_out);
- }
+ qemu_irq_lower(s->a20_out);
s->outport &= ~KBD_OUT_A20;
break;
case KBD_CCMD_RESET:
@@ -507,10 +501,7 @@ void i8042_isa_mouse_fake_event(void *opaque)
void i8042_setup_a20_line(ISADevice *dev, qemu_irq *a20_out)
{
- ISAKBDState *isa = I8042(dev);
- KBDState *s = &isa->kbd;
-
- s->a20_out = a20_out;
+ qdev_connect_gpio_out_named(DEVICE(dev), I8042_A20_LINE, 0, *a20_out);
}
static const VMStateDescription vmstate_kbd_isa = {
@@ -552,6 +543,8 @@ static void i8042_initfn(Object *obj)
"i8042-data", 1);
memory_region_init_io(isa_s->io + 1, obj, &i8042_cmd_ops, s,
"i8042-cmd", 1);
+
+ qdev_init_gpio_out_named(DEVICE(obj), &s->a20_out, I8042_A20_LINE, 1);
}
static void i8042_realizefn(DeviceState *dev, Error **errp)
diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs
index c7bbf88edf..530df2eba6 100644
--- a/hw/intc/Makefile.objs
+++ b/hw/intc/Makefile.objs
@@ -30,6 +30,7 @@ obj-$(CONFIG_OPENPIC_KVM) += openpic_kvm.o
obj-$(CONFIG_RASPI) += bcm2835_ic.o bcm2836_control.o
obj-$(CONFIG_SH4) += sh_intc.o
obj-$(CONFIG_XICS) += xics.o
+obj-$(CONFIG_XICS_SPAPR) += xics_spapr.o
obj-$(CONFIG_XICS_KVM) += xics_kvm.o
obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
obj-$(CONFIG_S390_FLIC) += s390_flic.o
diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c
index 5b2972ea9c..4633172bec 100644
--- a/hw/intc/arm_gicv3_cpuif.c
+++ b/hw/intc/arm_gicv3_cpuif.c
@@ -975,6 +975,7 @@ static CPAccessResult gicv3_irqfiq_access(CPUARMState *env,
if (!is_a64(env) && !arm_is_el3_or_mon(env)) {
r = CP_ACCESS_TRAP_EL3;
}
+ break;
default:
g_assert_not_reached();
}
@@ -1006,6 +1007,7 @@ static CPAccessResult gicv3_fiq_access(CPUARMState *env,
if (!is_a64(env) && !arm_is_el3_or_mon(env)) {
r = CP_ACCESS_TRAP_EL3;
}
+ break;
default:
g_assert_not_reached();
}
diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c
index 890d5d7442..06d8db6bd6 100644
--- a/hw/intc/armv7m_nvic.c
+++ b/hw/intc/armv7m_nvic.c
@@ -187,11 +187,11 @@ static uint32_t nvic_readl(nvic_state *s, uint32_t offset)
case 0x1c: /* SysTick Calibration Value. */
return 10000;
case 0xd00: /* CPUID Base. */
- cpu = ARM_CPU(current_cpu);
+ cpu = ARM_CPU(qemu_get_cpu(0));
return cpu->midr;
case 0xd04: /* Interrupt Control State. */
/* VECTACTIVE */
- cpu = ARM_CPU(current_cpu);
+ cpu = ARM_CPU(qemu_get_cpu(0));
val = cpu->env.v7m.exception;
if (val == 1023) {
val = 0;
@@ -222,7 +222,7 @@ static uint32_t nvic_readl(nvic_state *s, uint32_t offset)
val |= (1 << 31);
return val;
case 0xd08: /* Vector Table Offset. */
- cpu = ARM_CPU(current_cpu);
+ cpu = ARM_CPU(qemu_get_cpu(0));
return cpu->env.v7m.vecbase;
case 0xd0c: /* Application Interrupt/Reset Control. */
return 0xfa050000;
@@ -349,7 +349,7 @@ static void nvic_writel(nvic_state *s, uint32_t offset, uint32_t value)
}
break;
case 0xd08: /* Vector Table Offset. */
- cpu = ARM_CPU(current_cpu);
+ cpu = ARM_CPU(qemu_get_cpu(0));
cpu->env.v7m.vecbase = value & 0xffffff80;
break;
case 0xd0c: /* Application Interrupt/Reset Control. */
diff --git a/hw/intc/xics.c b/hw/intc/xics.c
index cce7f3d112..cd48f42046 100644
--- a/hw/intc/xics.c
+++ b/hw/intc/xics.c
@@ -32,12 +32,11 @@
#include "hw/hw.h"
#include "trace.h"
#include "qemu/timer.h"
-#include "hw/ppc/spapr.h"
#include "hw/ppc/xics.h"
#include "qemu/error-report.h"
#include "qapi/visitor.h"
-static int get_cpu_index_by_dt_id(int cpu_dt_id)
+int xics_get_cpu_index_by_dt_id(int cpu_dt_id)
{
PowerPCCPU *cpu = ppc_get_vcpu_by_dt_id(cpu_dt_id);
@@ -48,31 +47,31 @@ static int get_cpu_index_by_dt_id(int cpu_dt_id)
return -1;
}
-void xics_cpu_destroy(XICSState *icp, PowerPCCPU *cpu)
+void xics_cpu_destroy(XICSState *xics, PowerPCCPU *cpu)
{
CPUState *cs = CPU(cpu);
- ICPState *ss = &icp->ss[cs->cpu_index];
+ ICPState *ss = &xics->ss[cs->cpu_index];
- assert(cs->cpu_index < icp->nr_servers);
+ assert(cs->cpu_index < xics->nr_servers);
assert(cs == ss->cs);
ss->output = NULL;
ss->cs = NULL;
}
-void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu)
+void xics_cpu_setup(XICSState *xics, PowerPCCPU *cpu)
{
CPUState *cs = CPU(cpu);
CPUPPCState *env = &cpu->env;
- ICPState *ss = &icp->ss[cs->cpu_index];
- XICSStateClass *info = XICS_COMMON_GET_CLASS(icp);
+ ICPState *ss = &xics->ss[cs->cpu_index];
+ XICSStateClass *info = XICS_COMMON_GET_CLASS(xics);
- assert(cs->cpu_index < icp->nr_servers);
+ assert(cs->cpu_index < xics->nr_servers);
ss->cs = cs;
if (info->cpu_setup) {
- info->cpu_setup(icp, cpu);
+ info->cpu_setup(xics, cpu);
}
switch (PPC_INPUT(env)) {
@@ -96,21 +95,21 @@ void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu)
*/
static void xics_common_reset(DeviceState *d)
{
- XICSState *icp = XICS_COMMON(d);
+ XICSState *xics = XICS_COMMON(d);
int i;
- for (i = 0; i < icp->nr_servers; i++) {
- device_reset(DEVICE(&icp->ss[i]));
+ for (i = 0; i < xics->nr_servers; i++) {
+ device_reset(DEVICE(&xics->ss[i]));
}
- device_reset(DEVICE(icp->ics));
+ device_reset(DEVICE(xics->ics));
}
static void xics_prop_get_nr_irqs(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
- XICSState *icp = XICS_COMMON(obj);
- int64_t value = icp->nr_irqs;
+ XICSState *xics = XICS_COMMON(obj);
+ int64_t value = xics->nr_irqs;
visit_type_int(v, name, &value, errp);
}
@@ -118,8 +117,8 @@ static void xics_prop_get_nr_irqs(Object *obj, Visitor *v, const char *name,
static void xics_prop_set_nr_irqs(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
- XICSState *icp = XICS_COMMON(obj);
- XICSStateClass *info = XICS_COMMON_GET_CLASS(icp);
+ XICSState *xics = XICS_COMMON(obj);
+ XICSStateClass *info = XICS_COMMON_GET_CLASS(xics);
Error *error = NULL;
int64_t value;
@@ -128,23 +127,23 @@ static void xics_prop_set_nr_irqs(Object *obj, Visitor *v, const char *name,
error_propagate(errp, error);
return;
}
- if (icp->nr_irqs) {
+ if (xics->nr_irqs) {
error_setg(errp, "Number of interrupts is already set to %u",
- icp->nr_irqs);
+ xics->nr_irqs);
return;
}
assert(info->set_nr_irqs);
- assert(icp->ics);
- info->set_nr_irqs(icp, value, errp);
+ assert(xics->ics);
+ info->set_nr_irqs(xics, value, errp);
}
static void xics_prop_get_nr_servers(Object *obj, Visitor *v,
const char *name, void *opaque,
Error **errp)
{
- XICSState *icp = XICS_COMMON(obj);
- int64_t value = icp->nr_servers;
+ XICSState *xics = XICS_COMMON(obj);
+ int64_t value = xics->nr_servers;
visit_type_int(v, name, &value, errp);
}
@@ -153,8 +152,8 @@ static void xics_prop_set_nr_servers(Object *obj, Visitor *v,
const char *name, void *opaque,
Error **errp)
{
- XICSState *icp = XICS_COMMON(obj);
- XICSStateClass *info = XICS_COMMON_GET_CLASS(icp);
+ XICSState *xics = XICS_COMMON(obj);
+ XICSStateClass *info = XICS_COMMON_GET_CLASS(xics);
Error *error = NULL;
int64_t value;
@@ -163,14 +162,14 @@ static void xics_prop_set_nr_servers(Object *obj, Visitor *v,
error_propagate(errp, error);
return;
}
- if (icp->nr_servers) {
+ if (xics->nr_servers) {
error_setg(errp, "Number of servers is already set to %u",
- icp->nr_servers);
+ xics->nr_servers);
return;
}
assert(info->set_nr_servers);
- info->set_nr_servers(icp, value, errp);
+ info->set_nr_servers(xics, value, errp);
}
static void xics_common_initfn(Object *obj)
@@ -213,9 +212,9 @@ static void ics_reject(ICSState *ics, int nr);
static void ics_resend(ICSState *ics);
static void ics_eoi(ICSState *ics, int nr);
-static void icp_check_ipi(XICSState *icp, int server)
+static void icp_check_ipi(XICSState *xics, int server)
{
- ICPState *ss = icp->ss + server;
+ ICPState *ss = xics->ss + server;
if (XISR(ss) && (ss->pending_priority <= ss->mfrr)) {
return;
@@ -224,7 +223,7 @@ static void icp_check_ipi(XICSState *icp, int server)
trace_xics_icp_check_ipi(server, ss->mfrr);
if (XISR(ss)) {
- ics_reject(icp->ics, XISR(ss));
+ ics_reject(xics->ics, XISR(ss));
}
ss->xirr = (ss->xirr & ~XISR_MASK) | XICS_IPI;
@@ -232,19 +231,19 @@ static void icp_check_ipi(XICSState *icp, int server)
qemu_irq_raise(ss->output);
}
-static void icp_resend(XICSState *icp, int server)
+static void icp_resend(XICSState *xics, int server)
{
- ICPState *ss = icp->ss + server;
+ ICPState *ss = xics->ss + server;
if (ss->mfrr < CPPR(ss)) {
- icp_check_ipi(icp, server);
+ icp_check_ipi(xics, server);
}
- ics_resend(icp->ics);
+ ics_resend(xics->ics);
}
-static void icp_set_cppr(XICSState *icp, int server, uint8_t cppr)
+void icp_set_cppr(XICSState *xics, int server, uint8_t cppr)
{
- ICPState *ss = icp->ss + server;
+ ICPState *ss = xics->ss + server;
uint8_t old_cppr;
uint32_t old_xisr;
@@ -257,26 +256,26 @@ static void icp_set_cppr(XICSState *icp, int server, uint8_t cppr)
ss->xirr &= ~XISR_MASK; /* Clear XISR */
ss->pending_priority = 0xff;
qemu_irq_lower(ss->output);
- ics_reject(icp->ics, old_xisr);
+ ics_reject(xics->ics, old_xisr);
}
} else {
if (!XISR(ss)) {
- icp_resend(icp, server);
+ icp_resend(xics, server);
}
}
}
-static void icp_set_mfrr(XICSState *icp, int server, uint8_t mfrr)
+void icp_set_mfrr(XICSState *xics, int server, uint8_t mfrr)
{
- ICPState *ss = icp->ss + server;
+ ICPState *ss = xics->ss + server;
ss->mfrr = mfrr;
if (mfrr < CPPR(ss)) {
- icp_check_ipi(icp, server);
+ icp_check_ipi(xics, server);
}
}
-static uint32_t icp_accept(ICPState *ss)
+uint32_t icp_accept(ICPState *ss)
{
uint32_t xirr = ss->xirr;
@@ -289,31 +288,39 @@ static uint32_t icp_accept(ICPState *ss)
return xirr;
}
-static void icp_eoi(XICSState *icp, int server, uint32_t xirr)
+uint32_t icp_ipoll(ICPState *ss, uint32_t *mfrr)
{
- ICPState *ss = icp->ss + server;
+ if (mfrr) {
+ *mfrr = ss->mfrr;
+ }
+ return ss->xirr;
+}
+
+void icp_eoi(XICSState *xics, int server, uint32_t xirr)
+{
+ ICPState *ss = xics->ss + server;
/* Send EOI -> ICS */
ss->xirr = (ss->xirr & ~CPPR_MASK) | (xirr & CPPR_MASK);
trace_xics_icp_eoi(server, xirr, ss->xirr);
- ics_eoi(icp->ics, xirr & XISR_MASK);
+ ics_eoi(xics->ics, xirr & XISR_MASK);
if (!XISR(ss)) {
- icp_resend(icp, server);
+ icp_resend(xics, server);
}
}
-static void icp_irq(XICSState *icp, int server, int nr, uint8_t priority)
+static void icp_irq(XICSState *xics, int server, int nr, uint8_t priority)
{
- ICPState *ss = icp->ss + server;
+ ICPState *ss = xics->ss + server;
trace_xics_icp_irq(server, nr, priority);
if ((priority >= CPPR(ss))
|| (XISR(ss) && (ss->pending_priority <= priority))) {
- ics_reject(icp->ics, nr);
+ ics_reject(xics->ics, nr);
} else {
if (XISR(ss)) {
- ics_reject(icp->ics, XISR(ss));
+ ics_reject(xics->ics, XISR(ss));
}
ss->xirr = (ss->xirr & ~XISR_MASK) | (nr & XISR_MASK);
ss->pending_priority = priority;
@@ -390,12 +397,6 @@ static const TypeInfo icp_info = {
/*
* ICS: Source layer
*/
-static int ics_valid_irq(ICSState *ics, uint32_t nr)
-{
- return (nr >= ics->offset)
- && (nr < (ics->offset + ics->nr_irqs));
-}
-
static void resend_msi(ICSState *ics, int srcno)
{
ICSIRQState *irq = ics->irqs + srcno;
@@ -404,7 +405,7 @@ static void resend_msi(ICSState *ics, int srcno)
if (irq->status & XICS_STATUS_REJECTED) {
irq->status &= ~XICS_STATUS_REJECTED;
if (irq->priority != 0xff) {
- icp_irq(ics->icp, irq->server, srcno + ics->offset,
+ icp_irq(ics->xics, irq->server, srcno + ics->offset,
irq->priority);
}
}
@@ -418,7 +419,7 @@ static void resend_lsi(ICSState *ics, int srcno)
&& (irq->status & XICS_STATUS_ASSERTED)
&& !(irq->status & XICS_STATUS_SENT)) {
irq->status |= XICS_STATUS_SENT;
- icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority);
+ icp_irq(ics->xics, irq->server, srcno + ics->offset, irq->priority);
}
}
@@ -433,7 +434,7 @@ static void set_irq_msi(ICSState *ics, int srcno, int val)
irq->status |= XICS_STATUS_MASKED_PENDING;
trace_xics_masked_pending();
} else {
- icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority);
+ icp_irq(ics->xics, irq->server, srcno + ics->offset, irq->priority);
}
}
}
@@ -472,7 +473,7 @@ static void write_xive_msi(ICSState *ics, int srcno)
}
irq->status &= ~XICS_STATUS_MASKED_PENDING;
- icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority);
+ icp_irq(ics->xics, irq->server, srcno + ics->offset, irq->priority);
}
static void write_xive_lsi(ICSState *ics, int srcno)
@@ -480,8 +481,8 @@ static void write_xive_lsi(ICSState *ics, int srcno)
resend_lsi(ics, srcno);
}
-static void ics_write_xive(ICSState *ics, int nr, int server,
- uint8_t priority, uint8_t saved_priority)
+void ics_write_xive(ICSState *ics, int nr, int server,
+ uint8_t priority, uint8_t saved_priority)
{
int srcno = nr - ics->offset;
ICSIRQState *irq = ics->irqs + srcno;
@@ -557,8 +558,8 @@ static int ics_post_load(ICSState *ics, int version_id)
{
int i;
- for (i = 0; i < ics->icp->nr_servers; i++) {
- icp_resend(ics->icp, i);
+ for (i = 0; i < ics->xics->nr_servers; i++) {
+ icp_resend(ics->xics, i);
}
return 0;
@@ -658,14 +659,14 @@ static const TypeInfo ics_info = {
/*
* Exported functions
*/
-static int xics_find_source(XICSState *icp, int irq)
+int xics_find_source(XICSState *xics, int irq)
{
int sources = 1;
int src;
/* FIXME: implement multiple sources */
for (src = 0; src < sources; ++src) {
- ICSState *ics = &icp->ics[src];
+ ICSState *ics = &xics->ics[src];
if (ics_valid_irq(ics, irq)) {
return src;
}
@@ -674,19 +675,19 @@ static int xics_find_source(XICSState *icp, int irq)
return -1;
}
-qemu_irq xics_get_qirq(XICSState *icp, int irq)
+qemu_irq xics_get_qirq(XICSState *xics, int irq)
{
- int src = xics_find_source(icp, irq);
+ int src = xics_find_source(xics, irq);
if (src >= 0) {
- ICSState *ics = &icp->ics[src];
+ ICSState *ics = &xics->ics[src];
return ics->qirqs[irq - ics->offset];
}
return NULL;
}
-static void ics_set_irq_type(ICSState *ics, int srcno, bool lsi)
+void ics_set_irq_type(ICSState *ics, int srcno, bool lsi)
{
assert(!(ics->irqs[srcno].flags & XICS_FLAGS_IRQ_MASK));
@@ -694,412 +695,9 @@ static void ics_set_irq_type(ICSState *ics, int srcno, bool lsi)
lsi ? XICS_FLAGS_IRQ_LSI : XICS_FLAGS_IRQ_MSI;
}
-void xics_set_irq_type(XICSState *icp, int irq, bool lsi)
-{
- int src = xics_find_source(icp, irq);
- ICSState *ics;
-
- assert(src >= 0);
-
- ics = &icp->ics[src];
- ics_set_irq_type(ics, irq - ics->offset, lsi);
-}
-
-#define ICS_IRQ_FREE(ics, srcno) \
- (!((ics)->irqs[(srcno)].flags & (XICS_FLAGS_IRQ_MASK)))
-
-static int ics_find_free_block(ICSState *ics, int num, int alignnum)
-{
- int first, i;
-
- for (first = 0; first < ics->nr_irqs; first += alignnum) {
- if (num > (ics->nr_irqs - first)) {
- return -1;
- }
- for (i = first; i < first + num; ++i) {
- if (!ICS_IRQ_FREE(ics, i)) {
- break;
- }
- }
- if (i == (first + num)) {
- return first;
- }
- }
-
- return -1;
-}
-
-int xics_alloc(XICSState *icp, int src, int irq_hint, bool lsi, Error **errp)
-{
- ICSState *ics = &icp->ics[src];
- int irq;
-
- if (irq_hint) {
- assert(src == xics_find_source(icp, irq_hint));
- if (!ICS_IRQ_FREE(ics, irq_hint - ics->offset)) {
- error_setg(errp, "can't allocate IRQ %d: already in use", irq_hint);
- return -1;
- }
- irq = irq_hint;
- } else {
- irq = ics_find_free_block(ics, 1, 1);
- if (irq < 0) {
- error_setg(errp, "can't allocate IRQ: no IRQ left");
- return -1;
- }
- irq += ics->offset;
- }
-
- ics_set_irq_type(ics, irq - ics->offset, lsi);
- trace_xics_alloc(src, irq);
-
- return irq;
-}
-
-/*
- * Allocate block of consecutive IRQs, and return the number of the first IRQ in the block.
- * If align==true, aligns the first IRQ number to num.
- */
-int xics_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align,
- Error **errp)
-{
- int i, first = -1;
- ICSState *ics = &icp->ics[src];
-
- assert(src == 0);
- /*
- * MSIMesage::data is used for storing VIRQ so
- * it has to be aligned to num to support multiple
- * MSI vectors. MSI-X is not affected by this.
- * The hint is used for the first IRQ, the rest should
- * be allocated continuously.
- */
- if (align) {
- assert((num == 1) || (num == 2) || (num == 4) ||
- (num == 8) || (num == 16) || (num == 32));
- first = ics_find_free_block(ics, num, num);
- } else {
- first = ics_find_free_block(ics, num, 1);
- }
- if (first < 0) {
- error_setg(errp, "can't find a free %d-IRQ block", num);
- return -1;
- }
-
- if (first >= 0) {
- for (i = first; i < first + num; ++i) {
- ics_set_irq_type(ics, i, lsi);
- }
- }
- first += ics->offset;
-
- trace_xics_alloc_block(src, first, num, lsi, align);
-
- return first;
-}
-
-static void ics_free(ICSState *ics, int srcno, int num)
-{
- int i;
-
- for (i = srcno; i < srcno + num; ++i) {
- if (ICS_IRQ_FREE(ics, i)) {
- trace_xics_ics_free_warn(ics - ics->icp->ics, i + ics->offset);
- }
- memset(&ics->irqs[i], 0, sizeof(ICSIRQState));
- }
-}
-
-void xics_free(XICSState *icp, int irq, int num)
-{
- int src = xics_find_source(icp, irq);
-
- if (src >= 0) {
- ICSState *ics = &icp->ics[src];
-
- /* FIXME: implement multiple sources */
- assert(src == 0);
-
- trace_xics_ics_free(ics - icp->ics, irq, num);
- ics_free(ics, irq - ics->offset, num);
- }
-}
-
-/*
- * Guest interfaces
- */
-
-static target_ulong h_cppr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
- target_ulong opcode, target_ulong *args)
-{
- CPUState *cs = CPU(cpu);
- target_ulong cppr = args[0];
-
- icp_set_cppr(spapr->icp, cs->cpu_index, cppr);
- return H_SUCCESS;
-}
-
-static target_ulong h_ipi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
- target_ulong opcode, target_ulong *args)
-{
- target_ulong server = get_cpu_index_by_dt_id(args[0]);
- target_ulong mfrr = args[1];
-
- if (server >= spapr->icp->nr_servers) {
- return H_PARAMETER;
- }
-
- icp_set_mfrr(spapr->icp, server, mfrr);
- return H_SUCCESS;
-}
-
-static target_ulong h_xirr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
- target_ulong opcode, target_ulong *args)
-{
- CPUState *cs = CPU(cpu);
- uint32_t xirr = icp_accept(spapr->icp->ss + cs->cpu_index);
-
- args[0] = xirr;
- return H_SUCCESS;
-}
-
-static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr,
- target_ulong opcode, target_ulong *args)
-{
- CPUState *cs = CPU(cpu);
- ICPState *ss = &spapr->icp->ss[cs->cpu_index];
- uint32_t xirr = icp_accept(ss);
-
- args[0] = xirr;
- args[1] = cpu_get_host_ticks();
- return H_SUCCESS;
-}
-
-static target_ulong h_eoi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
- target_ulong opcode, target_ulong *args)
-{
- CPUState *cs = CPU(cpu);
- target_ulong xirr = args[0];
-
- icp_eoi(spapr->icp, cs->cpu_index, xirr);
- return H_SUCCESS;
-}
-
-static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPRMachineState *spapr,
- target_ulong opcode, target_ulong *args)
-{
- CPUState *cs = CPU(cpu);
- ICPState *ss = &spapr->icp->ss[cs->cpu_index];
-
- args[0] = ss->xirr;
- args[1] = ss->mfrr;
-
- return H_SUCCESS;
-}
-
-static void rtas_set_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr,
- uint32_t token,
- uint32_t nargs, target_ulong args,
- uint32_t nret, target_ulong rets)
-{
- ICSState *ics = spapr->icp->ics;
- uint32_t nr, server, priority;
-
- if ((nargs != 3) || (nret != 1)) {
- rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
- return;
- }
-
- nr = rtas_ld(args, 0);
- server = get_cpu_index_by_dt_id(rtas_ld(args, 1));
- priority = rtas_ld(args, 2);
-
- if (!ics_valid_irq(ics, nr) || (server >= ics->icp->nr_servers)
- || (priority > 0xff)) {
- rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
- return;
- }
-
- ics_write_xive(ics, nr, server, priority, priority);
-
- rtas_st(rets, 0, RTAS_OUT_SUCCESS);
-}
-
-static void rtas_get_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr,
- uint32_t token,
- uint32_t nargs, target_ulong args,
- uint32_t nret, target_ulong rets)
-{
- ICSState *ics = spapr->icp->ics;
- uint32_t nr;
-
- if ((nargs != 1) || (nret != 3)) {
- rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
- return;
- }
-
- nr = rtas_ld(args, 0);
-
- if (!ics_valid_irq(ics, nr)) {
- rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
- return;
- }
-
- rtas_st(rets, 0, RTAS_OUT_SUCCESS);
- rtas_st(rets, 1, ics->irqs[nr - ics->offset].server);
- rtas_st(rets, 2, ics->irqs[nr - ics->offset].priority);
-}
-
-static void rtas_int_off(PowerPCCPU *cpu, sPAPRMachineState *spapr,
- uint32_t token,
- uint32_t nargs, target_ulong args,
- uint32_t nret, target_ulong rets)
-{
- ICSState *ics = spapr->icp->ics;
- uint32_t nr;
-
- if ((nargs != 1) || (nret != 1)) {
- rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
- return;
- }
-
- nr = rtas_ld(args, 0);
-
- if (!ics_valid_irq(ics, nr)) {
- rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
- return;
- }
-
- ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server, 0xff,
- ics->irqs[nr - ics->offset].priority);
-
- rtas_st(rets, 0, RTAS_OUT_SUCCESS);
-}
-
-static void rtas_int_on(PowerPCCPU *cpu, sPAPRMachineState *spapr,
- uint32_t token,
- uint32_t nargs, target_ulong args,
- uint32_t nret, target_ulong rets)
-{
- ICSState *ics = spapr->icp->ics;
- uint32_t nr;
-
- if ((nargs != 1) || (nret != 1)) {
- rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
- return;
- }
-
- nr = rtas_ld(args, 0);
-
- if (!ics_valid_irq(ics, nr)) {
- rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
- return;
- }
-
- ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server,
- ics->irqs[nr - ics->offset].saved_priority,
- ics->irqs[nr - ics->offset].saved_priority);
-
- rtas_st(rets, 0, RTAS_OUT_SUCCESS);
-}
-
-/*
- * XICS
- */
-
-static void xics_set_nr_irqs(XICSState *icp, uint32_t nr_irqs, Error **errp)
-{
- icp->nr_irqs = icp->ics->nr_irqs = nr_irqs;
-}
-
-static void xics_set_nr_servers(XICSState *icp, uint32_t nr_servers,
- Error **errp)
-{
- int i;
-
- icp->nr_servers = nr_servers;
-
- icp->ss = g_malloc0(icp->nr_servers*sizeof(ICPState));
- for (i = 0; i < icp->nr_servers; i++) {
- char buffer[32];
- object_initialize(&icp->ss[i], sizeof(icp->ss[i]), TYPE_ICP);
- snprintf(buffer, sizeof(buffer), "icp[%d]", i);
- object_property_add_child(OBJECT(icp), buffer, OBJECT(&icp->ss[i]),
- errp);
- }
-}
-
-static void xics_realize(DeviceState *dev, Error **errp)
-{
- XICSState *icp = XICS(dev);
- Error *error = NULL;
- int i;
-
- if (!icp->nr_servers) {
- error_setg(errp, "Number of servers needs to be greater 0");
- return;
- }
-
- /* Registration of global state belongs into realize */
- spapr_rtas_register(RTAS_IBM_SET_XIVE, "ibm,set-xive", rtas_set_xive);
- spapr_rtas_register(RTAS_IBM_GET_XIVE, "ibm,get-xive", rtas_get_xive);
- spapr_rtas_register(RTAS_IBM_INT_OFF, "ibm,int-off", rtas_int_off);
- spapr_rtas_register(RTAS_IBM_INT_ON, "ibm,int-on", rtas_int_on);
-
- spapr_register_hypercall(H_CPPR, h_cppr);
- spapr_register_hypercall(H_IPI, h_ipi);
- spapr_register_hypercall(H_XIRR, h_xirr);
- spapr_register_hypercall(H_XIRR_X, h_xirr_x);
- spapr_register_hypercall(H_EOI, h_eoi);
- spapr_register_hypercall(H_IPOLL, h_ipoll);
-
- object_property_set_bool(OBJECT(icp->ics), true, "realized", &error);
- if (error) {
- error_propagate(errp, error);
- return;
- }
-
- for (i = 0; i < icp->nr_servers; i++) {
- object_property_set_bool(OBJECT(&icp->ss[i]), true, "realized", &error);
- if (error) {
- error_propagate(errp, error);
- return;
- }
- }
-}
-
-static void xics_initfn(Object *obj)
-{
- XICSState *xics = XICS(obj);
-
- xics->ics = ICS(object_new(TYPE_ICS));
- object_property_add_child(obj, "ics", OBJECT(xics->ics), NULL);
- xics->ics->icp = xics;
-}
-
-static void xics_class_init(ObjectClass *oc, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(oc);
- XICSStateClass *xsc = XICS_CLASS(oc);
-
- dc->realize = xics_realize;
- xsc->set_nr_irqs = xics_set_nr_irqs;
- xsc->set_nr_servers = xics_set_nr_servers;
-}
-
-static const TypeInfo xics_info = {
- .name = TYPE_XICS,
- .parent = TYPE_XICS_COMMON,
- .instance_size = sizeof(XICSState),
- .class_size = sizeof(XICSStateClass),
- .class_init = xics_class_init,
- .instance_init = xics_initfn,
-};
-
static void xics_register_types(void)
{
type_register_static(&xics_common_info);
- type_register_static(&xics_info);
type_register_static(&ics_info);
type_register_static(&icp_info);
}
diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c
index b17d6a9f43..edbd62fd1b 100644
--- a/hw/intc/xics_kvm.c
+++ b/hw/intc/xics_kvm.c
@@ -145,7 +145,7 @@ static const TypeInfo icp_kvm_info = {
*/
static void ics_get_kvm_state(ICSState *ics)
{
- KVMXICSState *icpkvm = KVM_XICS(ics->icp);
+ KVMXICSState *xicskvm = XICS_SPAPR_KVM(ics->xics);
uint64_t state;
struct kvm_device_attr attr = {
.flags = 0,
@@ -160,7 +160,7 @@ static void ics_get_kvm_state(ICSState *ics)
attr.attr = i + ics->offset;
- ret = ioctl(icpkvm->kernel_xics_fd, KVM_GET_DEVICE_ATTR, &attr);
+ ret = ioctl(xicskvm->kernel_xics_fd, KVM_GET_DEVICE_ATTR, &attr);
if (ret != 0) {
error_report("Unable to retrieve KVM interrupt controller state"
" for IRQ %d: %s", i + ics->offset, strerror(errno));
@@ -204,7 +204,7 @@ static void ics_get_kvm_state(ICSState *ics)
static int ics_set_kvm_state(ICSState *ics, int version_id)
{
- KVMXICSState *icpkvm = KVM_XICS(ics->icp);
+ KVMXICSState *xicskvm = XICS_SPAPR_KVM(ics->xics);
uint64_t state;
struct kvm_device_attr attr = {
.flags = 0,
@@ -238,7 +238,7 @@ static int ics_set_kvm_state(ICSState *ics, int version_id)
}
}
- ret = ioctl(icpkvm->kernel_xics_fd, KVM_SET_DEVICE_ATTR, &attr);
+ ret = ioctl(xicskvm->kernel_xics_fd, KVM_SET_DEVICE_ATTR, &attr);
if (ret != 0) {
error_report("Unable to restore KVM interrupt controller state"
" for IRQs %d: %s", i + ics->offset, strerror(errno));
@@ -324,17 +324,17 @@ static const TypeInfo ics_kvm_info = {
/*
* XICS-KVM
*/
-static void xics_kvm_cpu_setup(XICSState *icp, PowerPCCPU *cpu)
+static void xics_kvm_cpu_setup(XICSState *xics, PowerPCCPU *cpu)
{
CPUState *cs;
ICPState *ss;
- KVMXICSState *icpkvm = KVM_XICS(icp);
+ KVMXICSState *xicskvm = XICS_SPAPR_KVM(xics);
cs = CPU(cpu);
- ss = &icp->ss[cs->cpu_index];
+ ss = &xics->ss[cs->cpu_index];
- assert(cs->cpu_index < icp->nr_servers);
- if (icpkvm->kernel_xics_fd == -1) {
+ assert(cs->cpu_index < xics->nr_servers);
+ if (xicskvm->kernel_xics_fd == -1) {
abort();
}
@@ -347,11 +347,12 @@ static void xics_kvm_cpu_setup(XICSState *icp, PowerPCCPU *cpu)
return;
}
- if (icpkvm->kernel_xics_fd != -1) {
+ if (xicskvm->kernel_xics_fd != -1) {
int ret;
ret = kvm_vcpu_enable_cap(cs, KVM_CAP_IRQ_XICS, 0,
- icpkvm->kernel_xics_fd, kvm_arch_vcpu_id(cs));
+ xicskvm->kernel_xics_fd,
+ kvm_arch_vcpu_id(cs));
if (ret < 0) {
error_report("Unable to connect CPU%ld to kernel XICS: %s",
kvm_arch_vcpu_id(cs), strerror(errno));
@@ -361,24 +362,25 @@ static void xics_kvm_cpu_setup(XICSState *icp, PowerPCCPU *cpu)
}
}
-static void xics_kvm_set_nr_irqs(XICSState *icp, uint32_t nr_irqs, Error **errp)
+static void xics_kvm_set_nr_irqs(XICSState *xics, uint32_t nr_irqs,
+ Error **errp)
{
- icp->nr_irqs = icp->ics->nr_irqs = nr_irqs;
+ xics->nr_irqs = xics->ics->nr_irqs = nr_irqs;
}
-static void xics_kvm_set_nr_servers(XICSState *icp, uint32_t nr_servers,
+static void xics_kvm_set_nr_servers(XICSState *xics, uint32_t nr_servers,
Error **errp)
{
int i;
- icp->nr_servers = nr_servers;
+ xics->nr_servers = nr_servers;
- icp->ss = g_malloc0(icp->nr_servers*sizeof(ICPState));
- for (i = 0; i < icp->nr_servers; i++) {
+ xics->ss = g_malloc0(xics->nr_servers * sizeof(ICPState));
+ for (i = 0; i < xics->nr_servers; i++) {
char buffer[32];
- object_initialize(&icp->ss[i], sizeof(icp->ss[i]), TYPE_KVM_ICP);
+ object_initialize(&xics->ss[i], sizeof(xics->ss[i]), TYPE_KVM_ICP);
snprintf(buffer, sizeof(buffer), "icp[%d]", i);
- object_property_add_child(OBJECT(icp), buffer, OBJECT(&icp->ss[i]),
+ object_property_add_child(OBJECT(xics), buffer, OBJECT(&xics->ss[i]),
errp);
}
}
@@ -394,8 +396,8 @@ static void rtas_dummy(PowerPCCPU *cpu, sPAPRMachineState *spapr,
static void xics_kvm_realize(DeviceState *dev, Error **errp)
{
- KVMXICSState *icpkvm = KVM_XICS(dev);
- XICSState *icp = XICS_COMMON(dev);
+ KVMXICSState *xicskvm = XICS_SPAPR_KVM(dev);
+ XICSState *xics = XICS_COMMON(dev);
int i, rc;
Error *error = NULL;
struct kvm_create_device xics_create_device = {
@@ -445,17 +447,18 @@ static void xics_kvm_realize(DeviceState *dev, Error **errp)
goto fail;
}
- icpkvm->kernel_xics_fd = xics_create_device.fd;
+ xicskvm->kernel_xics_fd = xics_create_device.fd;
- object_property_set_bool(OBJECT(icp->ics), true, "realized", &error);
+ object_property_set_bool(OBJECT(xics->ics), true, "realized", &error);
if (error) {
error_propagate(errp, error);
goto fail;
}
- assert(icp->nr_servers);
- for (i = 0; i < icp->nr_servers; i++) {
- object_property_set_bool(OBJECT(&icp->ss[i]), true, "realized", &error);
+ assert(xics->nr_servers);
+ for (i = 0; i < xics->nr_servers; i++) {
+ object_property_set_bool(OBJECT(&xics->ss[i]), true, "realized",
+ &error);
if (error) {
error_propagate(errp, error);
goto fail;
@@ -481,7 +484,7 @@ static void xics_kvm_initfn(Object *obj)
xics->ics = ICS(object_new(TYPE_KVM_ICS));
object_property_add_child(obj, "ics", OBJECT(xics->ics), NULL);
- xics->ics->icp = xics;
+ xics->ics->xics = xics;
}
static void xics_kvm_class_init(ObjectClass *oc, void *data)
@@ -495,8 +498,8 @@ static void xics_kvm_class_init(ObjectClass *oc, void *data)
xsc->set_nr_servers = xics_kvm_set_nr_servers;
}
-static const TypeInfo xics_kvm_info = {
- .name = TYPE_KVM_XICS,
+static const TypeInfo xics_spapr_kvm_info = {
+ .name = TYPE_XICS_SPAPR_KVM,
.parent = TYPE_XICS_COMMON,
.instance_size = sizeof(KVMXICSState),
.class_init = xics_kvm_class_init,
@@ -505,7 +508,7 @@ static const TypeInfo xics_kvm_info = {
static void xics_kvm_register_types(void)
{
- type_register_static(&xics_kvm_info);
+ type_register_static(&xics_spapr_kvm_info);
type_register_static(&ics_kvm_info);
type_register_static(&icp_kvm_info);
}
diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c
new file mode 100644
index 0000000000..618826dacf
--- /dev/null
+++ b/hw/intc/xics_spapr.c
@@ -0,0 +1,434 @@
+/*
+ * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator
+ *
+ * PAPR Virtualized Interrupt System, aka ICS/ICP aka xics
+ *
+ * Copyright (c) 2010,2011 David Gibson, IBM Corporation.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "hw/hw.h"
+#include "trace.h"
+#include "qemu/timer.h"
+#include "hw/ppc/spapr.h"
+#include "hw/ppc/xics.h"
+#include "qapi/visitor.h"
+#include "qapi/error.h"
+
+/*
+ * Guest interfaces
+ */
+
+static target_ulong h_cppr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ CPUState *cs = CPU(cpu);
+ target_ulong cppr = args[0];
+
+ icp_set_cppr(spapr->xics, cs->cpu_index, cppr);
+ return H_SUCCESS;
+}
+
+static target_ulong h_ipi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ target_ulong server = xics_get_cpu_index_by_dt_id(args[0]);
+ target_ulong mfrr = args[1];
+
+ if (server >= spapr->xics->nr_servers) {
+ return H_PARAMETER;
+ }
+
+ icp_set_mfrr(spapr->xics, server, mfrr);
+ return H_SUCCESS;
+}
+
+static target_ulong h_xirr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ CPUState *cs = CPU(cpu);
+ uint32_t xirr = icp_accept(spapr->xics->ss + cs->cpu_index);
+
+ args[0] = xirr;
+ return H_SUCCESS;
+}
+
+static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ CPUState *cs = CPU(cpu);
+ ICPState *ss = &spapr->xics->ss[cs->cpu_index];
+ uint32_t xirr = icp_accept(ss);
+
+ args[0] = xirr;
+ args[1] = cpu_get_host_ticks();
+ return H_SUCCESS;
+}
+
+static target_ulong h_eoi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ CPUState *cs = CPU(cpu);
+ target_ulong xirr = args[0];
+
+ icp_eoi(spapr->xics, cs->cpu_index, xirr);
+ return H_SUCCESS;
+}
+
+static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ CPUState *cs = CPU(cpu);
+ uint32_t mfrr;
+ uint32_t xirr = icp_ipoll(spapr->xics->ss + cs->cpu_index, &mfrr);
+
+ args[0] = xirr;
+ args[1] = mfrr;
+
+ return H_SUCCESS;
+}
+
+static void rtas_set_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+ uint32_t token,
+ uint32_t nargs, target_ulong args,
+ uint32_t nret, target_ulong rets)
+{
+ ICSState *ics = spapr->xics->ics;
+ uint32_t nr, server, priority;
+
+ if ((nargs != 3) || (nret != 1)) {
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+ return;
+ }
+
+ nr = rtas_ld(args, 0);
+ server = xics_get_cpu_index_by_dt_id(rtas_ld(args, 1));
+ priority = rtas_ld(args, 2);
+
+ if (!ics_valid_irq(ics, nr) || (server >= ics->xics->nr_servers)
+ || (priority > 0xff)) {
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+ return;
+ }
+
+ ics_write_xive(ics, nr, server, priority, priority);
+
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+}
+
+static void rtas_get_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+ uint32_t token,
+ uint32_t nargs, target_ulong args,
+ uint32_t nret, target_ulong rets)
+{
+ ICSState *ics = spapr->xics->ics;
+ uint32_t nr;
+
+ if ((nargs != 1) || (nret != 3)) {
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+ return;
+ }
+
+ nr = rtas_ld(args, 0);
+
+ if (!ics_valid_irq(ics, nr)) {
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+ return;
+ }
+
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+ rtas_st(rets, 1, ics->irqs[nr - ics->offset].server);
+ rtas_st(rets, 2, ics->irqs[nr - ics->offset].priority);
+}
+
+static void rtas_int_off(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+ uint32_t token,
+ uint32_t nargs, target_ulong args,
+ uint32_t nret, target_ulong rets)
+{
+ ICSState *ics = spapr->xics->ics;
+ uint32_t nr;
+
+ if ((nargs != 1) || (nret != 1)) {
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+ return;
+ }
+
+ nr = rtas_ld(args, 0);
+
+ if (!ics_valid_irq(ics, nr)) {
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+ return;
+ }
+
+ ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server, 0xff,
+ ics->irqs[nr - ics->offset].priority);
+
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+}
+
+static void rtas_int_on(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+ uint32_t token,
+ uint32_t nargs, target_ulong args,
+ uint32_t nret, target_ulong rets)
+{
+ ICSState *ics = spapr->xics->ics;
+ uint32_t nr;
+
+ if ((nargs != 1) || (nret != 1)) {
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+ return;
+ }
+
+ nr = rtas_ld(args, 0);
+
+ if (!ics_valid_irq(ics, nr)) {
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+ return;
+ }
+
+ ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server,
+ ics->irqs[nr - ics->offset].saved_priority,
+ ics->irqs[nr - ics->offset].saved_priority);
+
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+}
+
+static void xics_spapr_set_nr_irqs(XICSState *xics, uint32_t nr_irqs,
+ Error **errp)
+{
+ xics->nr_irqs = xics->ics->nr_irqs = nr_irqs;
+}
+
+static void xics_spapr_set_nr_servers(XICSState *xics, uint32_t nr_servers,
+ Error **errp)
+{
+ int i;
+
+ xics->nr_servers = nr_servers;
+
+ xics->ss = g_malloc0(xics->nr_servers * sizeof(ICPState));
+ for (i = 0; i < xics->nr_servers; i++) {
+ char buffer[32];
+ object_initialize(&xics->ss[i], sizeof(xics->ss[i]), TYPE_ICP);
+ snprintf(buffer, sizeof(buffer), "icp[%d]", i);
+ object_property_add_child(OBJECT(xics), buffer, OBJECT(&xics->ss[i]),
+ errp);
+ }
+}
+
+static void xics_spapr_realize(DeviceState *dev, Error **errp)
+{
+ XICSState *xics = XICS_SPAPR(dev);
+ Error *error = NULL;
+ int i;
+
+ if (!xics->nr_servers) {
+ error_setg(errp, "Number of servers needs to be greater 0");
+ return;
+ }
+
+ /* Registration of global state belongs into realize */
+ spapr_rtas_register(RTAS_IBM_SET_XIVE, "ibm,set-xive", rtas_set_xive);
+ spapr_rtas_register(RTAS_IBM_GET_XIVE, "ibm,get-xive", rtas_get_xive);
+ spapr_rtas_register(RTAS_IBM_INT_OFF, "ibm,int-off", rtas_int_off);
+ spapr_rtas_register(RTAS_IBM_INT_ON, "ibm,int-on", rtas_int_on);
+
+ spapr_register_hypercall(H_CPPR, h_cppr);
+ spapr_register_hypercall(H_IPI, h_ipi);
+ spapr_register_hypercall(H_XIRR, h_xirr);
+ spapr_register_hypercall(H_XIRR_X, h_xirr_x);
+ spapr_register_hypercall(H_EOI, h_eoi);
+ spapr_register_hypercall(H_IPOLL, h_ipoll);
+
+ object_property_set_bool(OBJECT(xics->ics), true, "realized", &error);
+ if (error) {
+ error_propagate(errp, error);
+ return;
+ }
+
+ for (i = 0; i < xics->nr_servers; i++) {
+ object_property_set_bool(OBJECT(&xics->ss[i]), true, "realized",
+ &error);
+ if (error) {
+ error_propagate(errp, error);
+ return;
+ }
+ }
+}
+
+static void xics_spapr_initfn(Object *obj)
+{
+ XICSState *xics = XICS_SPAPR(obj);
+
+ xics->ics = ICS(object_new(TYPE_ICS));
+ object_property_add_child(obj, "ics", OBJECT(xics->ics), NULL);
+ xics->ics->xics = xics;
+}
+
+static void xics_spapr_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ XICSStateClass *xsc = XICS_SPAPR_CLASS(oc);
+
+ dc->realize = xics_spapr_realize;
+ xsc->set_nr_irqs = xics_spapr_set_nr_irqs;
+ xsc->set_nr_servers = xics_spapr_set_nr_servers;
+}
+
+static const TypeInfo xics_spapr_info = {
+ .name = TYPE_XICS_SPAPR,
+ .parent = TYPE_XICS_COMMON,
+ .instance_size = sizeof(XICSState),
+ .class_size = sizeof(XICSStateClass),
+ .class_init = xics_spapr_class_init,
+ .instance_init = xics_spapr_initfn,
+};
+
+#define ICS_IRQ_FREE(ics, srcno) \
+ (!((ics)->irqs[(srcno)].flags & (XICS_FLAGS_IRQ_MASK)))
+
+static int ics_find_free_block(ICSState *ics, int num, int alignnum)
+{
+ int first, i;
+
+ for (first = 0; first < ics->nr_irqs; first += alignnum) {
+ if (num > (ics->nr_irqs - first)) {
+ return -1;
+ }
+ for (i = first; i < first + num; ++i) {
+ if (!ICS_IRQ_FREE(ics, i)) {
+ break;
+ }
+ }
+ if (i == (first + num)) {
+ return first;
+ }
+ }
+
+ return -1;
+}
+
+int xics_spapr_alloc(XICSState *xics, int src, int irq_hint, bool lsi,
+ Error **errp)
+{
+ ICSState *ics = &xics->ics[src];
+ int irq;
+
+ if (irq_hint) {
+ assert(src == xics_find_source(xics, irq_hint));
+ if (!ICS_IRQ_FREE(ics, irq_hint - ics->offset)) {
+ error_setg(errp, "can't allocate IRQ %d: already in use", irq_hint);
+ return -1;
+ }
+ irq = irq_hint;
+ } else {
+ irq = ics_find_free_block(ics, 1, 1);
+ if (irq < 0) {
+ error_setg(errp, "can't allocate IRQ: no IRQ left");
+ return -1;
+ }
+ irq += ics->offset;
+ }
+
+ ics_set_irq_type(ics, irq - ics->offset, lsi);
+ trace_xics_alloc(src, irq);
+
+ return irq;
+}
+
+/*
+ * Allocate block of consecutive IRQs, and return the number of the first IRQ in
+ * the block. If align==true, aligns the first IRQ number to num.
+ */
+int xics_spapr_alloc_block(XICSState *xics, int src, int num, bool lsi,
+ bool align, Error **errp)
+{
+ int i, first = -1;
+ ICSState *ics = &xics->ics[src];
+
+ assert(src == 0);
+ /*
+ * MSIMesage::data is used for storing VIRQ so
+ * it has to be aligned to num to support multiple
+ * MSI vectors. MSI-X is not affected by this.
+ * The hint is used for the first IRQ, the rest should
+ * be allocated continuously.
+ */
+ if (align) {
+ assert((num == 1) || (num == 2) || (num == 4) ||
+ (num == 8) || (num == 16) || (num == 32));
+ first = ics_find_free_block(ics, num, num);
+ } else {
+ first = ics_find_free_block(ics, num, 1);
+ }
+ if (first < 0) {
+ error_setg(errp, "can't find a free %d-IRQ block", num);
+ return -1;
+ }
+
+ if (first >= 0) {
+ for (i = first; i < first + num; ++i) {
+ ics_set_irq_type(ics, i, lsi);
+ }
+ }
+ first += ics->offset;
+
+ trace_xics_alloc_block(src, first, num, lsi, align);
+
+ return first;
+}
+
+static void ics_free(ICSState *ics, int srcno, int num)
+{
+ int i;
+
+ for (i = srcno; i < srcno + num; ++i) {
+ if (ICS_IRQ_FREE(ics, i)) {
+ trace_xics_ics_free_warn(ics - ics->xics->ics, i + ics->offset);
+ }
+ memset(&ics->irqs[i], 0, sizeof(ICSIRQState));
+ }
+}
+
+void xics_spapr_free(XICSState *xics, int irq, int num)
+{
+ int src = xics_find_source(xics, irq);
+
+ if (src >= 0) {
+ ICSState *ics = &xics->ics[src];
+
+ /* FIXME: implement multiple sources */
+ assert(src == 0);
+
+ trace_xics_ics_free(ics - xics->ics, irq, num);
+ ics_free(ics, irq - ics->offset, num);
+ }
+}
+
+static void xics_spapr_register_types(void)
+{
+ type_register_static(&xics_spapr_info);
+}
+
+type_init(xics_spapr_register_types)
diff --git a/hw/isa/isa-bus.c b/hw/isa/isa-bus.c
index 7aa115caf2..ce74db232a 100644
--- a/hw/isa/isa-bus.c
+++ b/hw/isa/isa-bus.c
@@ -97,6 +97,13 @@ void isa_init_irq(ISADevice *dev, qemu_irq *p, int isairq)
dev->nirqs++;
}
+void isa_connect_gpio_out(ISADevice *isadev, int gpioirq, int isairq)
+{
+ qemu_irq irq;
+ isa_init_irq(isadev, &irq, isairq);
+ qdev_connect_gpio_out(DEVICE(isadev), gpioirq, irq);
+}
+
void isa_bus_dma(ISABus *bus, IsaDma *dma8, IsaDma *dma16)
{
assert(bus && dma8 && dma16);
diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
index 213741bc21..10d1ee8b93 100644
--- a/hw/isa/lpc_ich9.c
+++ b/hw/isa/lpc_ich9.c
@@ -49,8 +49,6 @@
#include "sysemu/sysemu.h"
#include "qom/cpu.h"
-static int ich9_lpc_sci_irq(ICH9LPCState *lpc);
-
/*****************************************************************************/
/* ICH9 LPC PCI to ISA bridge */
@@ -204,41 +202,28 @@ static void ich9_lpc_pic_irq(ICH9LPCState *lpc, int pirq_num,
abort();
}
-/* pic_irq: i8254 irq 0-15 */
-static void ich9_lpc_update_pic(ICH9LPCState *lpc, int pic_irq)
+/* gsi: i8259+ioapic irq 0-15, otherwise assert */
+static void ich9_lpc_update_pic(ICH9LPCState *lpc, int gsi)
{
int i, pic_level;
+ assert(gsi < ICH9_LPC_PIC_NUM_PINS);
+
/* The pic level is the logical OR of all the PCI irqs mapped to it */
pic_level = 0;
for (i = 0; i < ICH9_LPC_NB_PIRQS; i++) {
int tmp_irq;
int tmp_dis;
ich9_lpc_pic_irq(lpc, i, &tmp_irq, &tmp_dis);
- if (!tmp_dis && pic_irq == tmp_irq) {
+ if (!tmp_dis && tmp_irq == gsi) {
pic_level |= pci_bus_get_irq_level(lpc->d.bus, i);
}
}
- if (pic_irq == ich9_lpc_sci_irq(lpc)) {
+ if (gsi == lpc->sci_gsi) {
pic_level |= lpc->sci_level;
}
- qemu_set_irq(lpc->pic[pic_irq], pic_level);
-}
-
-/* pirq: pirq[A-H] 0-7*/
-static void ich9_lpc_update_by_pirq(ICH9LPCState *lpc, int pirq)
-{
- int pic_irq;
- int pic_dis;
-
- ich9_lpc_pic_irq(lpc, pirq, &pic_irq, &pic_dis);
- assert(pic_irq < ICH9_LPC_PIC_NUM_PINS);
- if (pic_dis) {
- return;
- }
-
- ich9_lpc_update_pic(lpc, pic_irq);
+ qemu_set_irq(lpc->gsi[gsi], pic_level);
}
/* APIC mode: GSIx: PIRQ[A-H] -> GSI 16, ... no pirq shares same APIC pins. */
@@ -252,29 +237,32 @@ static int ich9_gsi_to_pirq(int gsi)
return gsi - ICH9_LPC_PIC_NUM_PINS;
}
+/* gsi: ioapic irq 16-23, otherwise assert */
static void ich9_lpc_update_apic(ICH9LPCState *lpc, int gsi)
{
int level = 0;
- if (gsi >= ICH9_LPC_PIC_NUM_PINS) {
- level |= pci_bus_get_irq_level(lpc->d.bus, ich9_gsi_to_pirq(gsi));
- }
- if (gsi == ich9_lpc_sci_irq(lpc)) {
+ assert(gsi >= ICH9_LPC_PIC_NUM_PINS);
+
+ level |= pci_bus_get_irq_level(lpc->d.bus, ich9_gsi_to_pirq(gsi));
+ if (gsi == lpc->sci_gsi) {
level |= lpc->sci_level;
}
- qemu_set_irq(lpc->ioapic[gsi], level);
+ qemu_set_irq(lpc->gsi[gsi], level);
}
void ich9_lpc_set_irq(void *opaque, int pirq, int level)
{
ICH9LPCState *lpc = opaque;
+ int pic_irq, pic_dis;
assert(0 <= pirq);
assert(pirq < ICH9_LPC_NB_PIRQS);
ich9_lpc_update_apic(lpc, ich9_pirq_to_gsi(pirq));
- ich9_lpc_update_by_pirq(lpc, pirq);
+ ich9_lpc_pic_irq(lpc, pirq, &pic_irq, &pic_dis);
+ ich9_lpc_update_pic(lpc, pic_irq);
}
/* return the pirq number (PIRQ[A-H]:0-7) corresponding to
@@ -360,13 +348,14 @@ static void ich9_set_sci(void *opaque, int irq_num, int level)
}
lpc->sci_level = level;
- irq = ich9_lpc_sci_irq(lpc);
+ irq = lpc->sci_gsi;
if (irq < 0) {
return;
}
- ich9_lpc_update_apic(lpc, irq);
- if (irq < ICH9_LPC_PIC_NUM_PINS) {
+ if (irq >= ICH9_LPC_PIC_NUM_PINS) {
+ ich9_lpc_update_apic(lpc, irq);
+ } else {
ich9_lpc_update_pic(lpc, irq);
}
}
@@ -403,12 +392,27 @@ static void ich9_apm_ctrl_changed(uint32_t val, void *arg)
/* config:PMBASE */
static void
-ich9_lpc_pmbase_update(ICH9LPCState *lpc)
+ich9_lpc_pmbase_sci_update(ICH9LPCState *lpc)
{
uint32_t pm_io_base = pci_get_long(lpc->d.config + ICH9_LPC_PMBASE);
- pm_io_base &= ICH9_LPC_PMBASE_BASE_ADDRESS_MASK;
+ uint8_t acpi_cntl = pci_get_long(lpc->d.config + ICH9_LPC_ACPI_CTRL);
+ uint8_t new_gsi;
+
+ if (acpi_cntl & ICH9_LPC_ACPI_CTRL_ACPI_EN) {
+ pm_io_base &= ICH9_LPC_PMBASE_BASE_ADDRESS_MASK;
+ } else {
+ pm_io_base = 0;
+ }
ich9_pm_iospace_update(&lpc->pm, pm_io_base);
+
+ new_gsi = ich9_lpc_sci_irq(lpc);
+ if (lpc->sci_level && new_gsi != lpc->sci_gsi) {
+ qemu_set_irq(lpc->pm.irq, 0);
+ lpc->sci_gsi = new_gsi;
+ qemu_set_irq(lpc->pm.irq, 1);
+ }
+ lpc->sci_gsi = new_gsi;
}
/* config:RCBA */
@@ -445,7 +449,7 @@ static int ich9_lpc_post_load(void *opaque, int version_id)
{
ICH9LPCState *lpc = opaque;
- ich9_lpc_pmbase_update(lpc);
+ ich9_lpc_pmbase_sci_update(lpc);
ich9_lpc_rcba_update(lpc, 0 /* disabled ICH9_LPC_RCBA_EN */);
ich9_lpc_pmcon_update(lpc);
return 0;
@@ -458,8 +462,9 @@ static void ich9_lpc_config_write(PCIDevice *d,
uint32_t rcba_old = pci_get_long(d->config + ICH9_LPC_RCBA);
pci_default_write_config(d, addr, val, len);
- if (ranges_overlap(addr, len, ICH9_LPC_PMBASE, 4)) {
- ich9_lpc_pmbase_update(lpc);
+ if (ranges_overlap(addr, len, ICH9_LPC_PMBASE, 4) ||
+ ranges_overlap(addr, len, ICH9_LPC_ACPI_CTRL, 1)) {
+ ich9_lpc_pmbase_sci_update(lpc);
}
if (ranges_overlap(addr, len, ICH9_LPC_RCBA, 4)) {
ich9_lpc_rcba_update(lpc, rcba_old);
@@ -497,7 +502,7 @@ static void ich9_lpc_reset(DeviceState *qdev)
ich9_cc_reset(lpc);
- ich9_lpc_pmbase_update(lpc);
+ ich9_lpc_pmbase_sci_update(lpc);
ich9_lpc_rcba_update(lpc, rcba_old);
lpc->sci_level = 0;
@@ -577,7 +582,7 @@ static void ich9_lpc_get_sci_int(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
ICH9LPCState *lpc = ICH9_LPC_DEVICE(obj);
- uint32_t value = ich9_lpc_sci_irq(lpc);
+ uint32_t value = lpc->sci_gsi;
visit_type_uint32(v, name, &value, errp);
}
@@ -608,6 +613,7 @@ static void ich9_lpc_initfn(Object *obj)
static void ich9_lpc_realize(PCIDevice *d, Error **errp)
{
ICH9LPCState *lpc = ICH9_LPC_DEVICE(d);
+ DeviceState *dev = DEVICE(d);
ISABus *isa_bus;
isa_bus = isa_bus_new(DEVICE(d), get_system_memory(), get_system_io(),
@@ -618,6 +624,9 @@ static void ich9_lpc_realize(PCIDevice *d, Error **errp)
pci_set_long(d->wmask + ICH9_LPC_PMBASE,
ICH9_LPC_PMBASE_BASE_ADDRESS_MASK);
+ pci_set_byte(d->wmask + ICH9_LPC_PMBASE,
+ ICH9_LPC_ACPI_CTRL_ACPI_EN |
+ ICH9_LPC_ACPI_CTRL_SCI_IRQ_SEL_MASK);
memory_region_init_io(&lpc->rcrb_mem, OBJECT(d), &rcrb_mmio_ops, lpc,
"lpc-rcrb-mmio", ICH9_CC_SIZE);
@@ -635,6 +644,10 @@ static void ich9_lpc_realize(PCIDevice *d, Error **errp)
memory_region_add_subregion_overlap(pci_address_space_io(d),
ICH9_RST_CNT_IOPORT, &lpc->rst_cnt_mem,
1);
+
+ qdev_init_gpio_out_named(dev, lpc->gsi, ICH9_GPIO_GSI, GSI_NUM_PINS);
+
+ isa_bus_irqs(isa_bus, lpc->gsi);
}
static bool ich9_rst_cnt_needed(void *opaque)
@@ -714,6 +727,7 @@ static void ich9_lpc_class_init(ObjectClass *klass, void *data)
hc->unplug = ich9_pm_device_unplug_cb;
adevc->ospm_status = ich9_pm_ospm_status;
adevc->send_event = ich9_send_gpe;
+ adevc->madt_cpu = pc_madt_cpu_entry;
}
static const TypeInfo ich9_lpc_info = {
diff --git a/hw/mem/nvdimm.c b/hw/mem/nvdimm.c
index 0a602f28ba..81896c0e84 100644
--- a/hw/mem/nvdimm.c
+++ b/hw/mem/nvdimm.c
@@ -23,20 +23,152 @@
*/
#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
#include "hw/mem/nvdimm.h"
+static void nvdimm_get_label_size(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ NVDIMMDevice *nvdimm = NVDIMM(obj);
+ uint64_t value = nvdimm->label_size;
+
+ visit_type_size(v, name, &value, errp);
+}
+
+static void nvdimm_set_label_size(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ NVDIMMDevice *nvdimm = NVDIMM(obj);
+ Error *local_err = NULL;
+ uint64_t value;
+
+ if (memory_region_size(&nvdimm->nvdimm_mr)) {
+ error_setg(&local_err, "cannot change property value");
+ goto out;
+ }
+
+ visit_type_size(v, name, &value, &local_err);
+ if (local_err) {
+ goto out;
+ }
+ if (value < MIN_NAMESPACE_LABEL_SIZE) {
+ error_setg(&local_err, "Property '%s.%s' (0x%" PRIx64 ") is required"
+ " at least 0x%lx", object_get_typename(obj),
+ name, value, MIN_NAMESPACE_LABEL_SIZE);
+ goto out;
+ }
+
+ nvdimm->label_size = value;
+out:
+ error_propagate(errp, local_err);
+}
+
+static void nvdimm_init(Object *obj)
+{
+ object_property_add(obj, "label-size", "int",
+ nvdimm_get_label_size, nvdimm_set_label_size, NULL,
+ NULL, NULL);
+}
+
+static MemoryRegion *nvdimm_get_memory_region(PCDIMMDevice *dimm)
+{
+ NVDIMMDevice *nvdimm = NVDIMM(dimm);
+
+ return &nvdimm->nvdimm_mr;
+}
+
+static void nvdimm_realize(PCDIMMDevice *dimm, Error **errp)
+{
+ MemoryRegion *mr = host_memory_backend_get_memory(dimm->hostmem, errp);
+ NVDIMMDevice *nvdimm = NVDIMM(dimm);
+ uint64_t align, pmem_size, size = memory_region_size(mr);
+
+ align = memory_region_get_alignment(mr);
+
+ pmem_size = size - nvdimm->label_size;
+ nvdimm->label_data = memory_region_get_ram_ptr(mr) + pmem_size;
+ pmem_size = QEMU_ALIGN_DOWN(pmem_size, align);
+
+ if (size <= nvdimm->label_size || !pmem_size) {
+ HostMemoryBackend *hostmem = dimm->hostmem;
+ char *path = object_get_canonical_path_component(OBJECT(hostmem));
+
+ error_setg(errp, "the size of memdev %s (0x%" PRIx64 ") is too "
+ "small to contain nvdimm label (0x%" PRIx64 ") and "
+ "aligned PMEM (0x%" PRIx64 ")",
+ path, memory_region_size(mr), nvdimm->label_size, align);
+ return;
+ }
+
+ memory_region_init_alias(&nvdimm->nvdimm_mr, OBJECT(dimm),
+ "nvdimm-memory", mr, 0, pmem_size);
+ nvdimm->nvdimm_mr.align = align;
+}
+
+/*
+ * the caller should check the input parameters before calling
+ * label read/write functions.
+ */
+static void nvdimm_validate_rw_label_data(NVDIMMDevice *nvdimm, uint64_t size,
+ uint64_t offset)
+{
+ assert((nvdimm->label_size >= size + offset) && (offset + size > offset));
+}
+
+static void nvdimm_read_label_data(NVDIMMDevice *nvdimm, void *buf,
+ uint64_t size, uint64_t offset)
+{
+ nvdimm_validate_rw_label_data(nvdimm, size, offset);
+
+ memcpy(buf, nvdimm->label_data + offset, size);
+}
+
+static void nvdimm_write_label_data(NVDIMMDevice *nvdimm, const void *buf,
+ uint64_t size, uint64_t offset)
+{
+ MemoryRegion *mr;
+ PCDIMMDevice *dimm = PC_DIMM(nvdimm);
+ uint64_t backend_offset;
+
+ nvdimm_validate_rw_label_data(nvdimm, size, offset);
+
+ memcpy(nvdimm->label_data + offset, buf, size);
+
+ mr = host_memory_backend_get_memory(dimm->hostmem, &error_abort);
+ backend_offset = memory_region_size(mr) - nvdimm->label_size + offset;
+ memory_region_set_dirty(mr, backend_offset, size);
+}
+
+static MemoryRegion *nvdimm_get_vmstate_memory_region(PCDIMMDevice *dimm)
+{
+ return host_memory_backend_get_memory(dimm->hostmem, &error_abort);
+}
+
static void nvdimm_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
+ PCDIMMDeviceClass *ddc = PC_DIMM_CLASS(oc);
+ NVDIMMClass *nvc = NVDIMM_CLASS(oc);
/* nvdimm hotplug has not been supported yet. */
dc->hotpluggable = false;
+
+ ddc->realize = nvdimm_realize;
+ ddc->get_memory_region = nvdimm_get_memory_region;
+ ddc->get_vmstate_memory_region = nvdimm_get_vmstate_memory_region;
+
+ nvc->read_label_data = nvdimm_read_label_data;
+ nvc->write_label_data = nvdimm_write_label_data;
}
static TypeInfo nvdimm_info = {
.name = TYPE_NVDIMM,
.parent = TYPE_PC_DIMM,
+ .class_size = sizeof(NVDIMMClass),
.class_init = nvdimm_class_init,
+ .instance_size = sizeof(NVDIMMDevice),
+ .instance_init = nvdimm_init,
};
static void nvdimm_register_types(void)
diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c
index 6de2275986..249193a543 100644
--- a/hw/mem/pc-dimm.c
+++ b/hw/mem/pc-dimm.c
@@ -40,6 +40,8 @@ void pc_dimm_memory_plug(DeviceState *dev, MemoryHotplugState *hpms,
int slot;
MachineState *machine = MACHINE(qdev_get_machine());
PCDIMMDevice *dimm = PC_DIMM(dev);
+ PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
+ MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm);
Error *local_err = NULL;
uint64_t existing_dimms_capacity = 0;
uint64_t addr;
@@ -105,7 +107,7 @@ void pc_dimm_memory_plug(DeviceState *dev, MemoryHotplugState *hpms,
}
memory_region_add_subregion(&hpms->mr, addr - hpms->base, mr);
- vmstate_register_ram(mr, dev);
+ vmstate_register_ram(vmstate_mr, dev);
numa_set_mem_node_id(addr, memory_region_size(mr), dimm->node);
out:
@@ -116,10 +118,12 @@ void pc_dimm_memory_unplug(DeviceState *dev, MemoryHotplugState *hpms,
MemoryRegion *mr)
{
PCDIMMDevice *dimm = PC_DIMM(dev);
+ PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
+ MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm);
numa_unset_mem_node_id(dimm->addr, memory_region_size(mr), dimm->node);
memory_region_del_subregion(&hpms->mr, mr);
- vmstate_unregister_ram(mr, dev);
+ vmstate_unregister_ram(vmstate_mr, dev);
}
static int pc_existing_dimms_capacity_internal(Object *obj, void *opaque)
@@ -424,6 +428,11 @@ static MemoryRegion *pc_dimm_get_memory_region(PCDIMMDevice *dimm)
return host_memory_backend_get_memory(dimm->hostmem, &error_abort);
}
+static MemoryRegion *pc_dimm_get_vmstate_memory_region(PCDIMMDevice *dimm)
+{
+ return host_memory_backend_get_memory(dimm->hostmem, &error_abort);
+}
+
static void pc_dimm_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
@@ -434,6 +443,7 @@ static void pc_dimm_class_init(ObjectClass *oc, void *data)
dc->desc = "DIMM memory module";
ddc->get_memory_region = pc_dimm_get_memory_region;
+ ddc->get_vmstate_memory_region = pc_dimm_get_vmstate_memory_region;
}
static TypeInfo pc_dimm_info = {
diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c
index 07527b677b..4968bdbb28 100644
--- a/hw/microblaze/petalogix_ml605_mmu.c
+++ b/hw/microblaze/petalogix_ml605_mmu.c
@@ -191,9 +191,16 @@ petalogix_ml605_init(MachineState *machine)
spi = (SSIBus *)qdev_get_child_bus(dev, "spi");
for (i = 0; i < NUM_SPI_FLASHES; i++) {
+ DriveInfo *dinfo = drive_get_next(IF_MTD);
qemu_irq cs_line;
- dev = ssi_create_slave(spi, "n25q128");
+ dev = ssi_create_slave_no_init(spi, "n25q128");
+ if (dinfo) {
+ qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(dinfo),
+ &error_fatal);
+ }
+ qdev_init_nofail(dev);
+
cs_line = qdev_get_gpio_in_named(dev, SSI_GPIO_CS, 0);
sysbus_connect_irq(busdev, i+1, cs_line);
}
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index ffb49c11ac..54020aa06c 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -52,3 +52,4 @@ obj-$(CONFIG_PVPANIC) += pvpanic.o
obj-$(CONFIG_EDU) += edu.o
obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o
obj-$(CONFIG_AUX) += aux.o
+obj-$(CONFIG_ASPEED_SOC) += aspeed_scu.o
diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c
new file mode 100644
index 0000000000..23f51752b0
--- /dev/null
+++ b/hw/misc/aspeed_scu.c
@@ -0,0 +1,284 @@
+/*
+ * ASPEED System Control Unit
+ *
+ * Andrew Jeffery <andrew@aj.id.au>
+ *
+ * Copyright 2016 IBM Corp.
+ *
+ * This code is licensed under the GPL version 2 or later. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/misc/aspeed_scu.h"
+#include "hw/qdev-properties.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "qemu/bitops.h"
+#include "qemu/log.h"
+#include "trace.h"
+
+#define TO_REG(offset) ((offset) >> 2)
+
+#define PROT_KEY TO_REG(0x00)
+#define SYS_RST_CTRL TO_REG(0x04)
+#define CLK_SEL TO_REG(0x08)
+#define CLK_STOP_CTRL TO_REG(0x0C)
+#define FREQ_CNTR_CTRL TO_REG(0x10)
+#define FREQ_CNTR_EVAL TO_REG(0x14)
+#define IRQ_CTRL TO_REG(0x18)
+#define D2PLL_PARAM TO_REG(0x1C)
+#define MPLL_PARAM TO_REG(0x20)
+#define HPLL_PARAM TO_REG(0x24)
+#define FREQ_CNTR_RANGE TO_REG(0x28)
+#define MISC_CTRL1 TO_REG(0x2C)
+#define PCI_CTRL1 TO_REG(0x30)
+#define PCI_CTRL2 TO_REG(0x34)
+#define PCI_CTRL3 TO_REG(0x38)
+#define SYS_RST_STATUS TO_REG(0x3C)
+#define SOC_SCRATCH1 TO_REG(0x40)
+#define SOC_SCRATCH2 TO_REG(0x44)
+#define MAC_CLK_DELAY TO_REG(0x48)
+#define MISC_CTRL2 TO_REG(0x4C)
+#define VGA_SCRATCH1 TO_REG(0x50)
+#define VGA_SCRATCH2 TO_REG(0x54)
+#define VGA_SCRATCH3 TO_REG(0x58)
+#define VGA_SCRATCH4 TO_REG(0x5C)
+#define VGA_SCRATCH5 TO_REG(0x60)
+#define VGA_SCRATCH6 TO_REG(0x64)
+#define VGA_SCRATCH7 TO_REG(0x68)
+#define VGA_SCRATCH8 TO_REG(0x6C)
+#define HW_STRAP1 TO_REG(0x70)
+#define RNG_CTRL TO_REG(0x74)
+#define RNG_DATA TO_REG(0x78)
+#define SILICON_REV TO_REG(0x7C)
+#define PINMUX_CTRL1 TO_REG(0x80)
+#define PINMUX_CTRL2 TO_REG(0x84)
+#define PINMUX_CTRL3 TO_REG(0x88)
+#define PINMUX_CTRL4 TO_REG(0x8C)
+#define PINMUX_CTRL5 TO_REG(0x90)
+#define PINMUX_CTRL6 TO_REG(0x94)
+#define WDT_RST_CTRL TO_REG(0x9C)
+#define PINMUX_CTRL7 TO_REG(0xA0)
+#define PINMUX_CTRL8 TO_REG(0xA4)
+#define PINMUX_CTRL9 TO_REG(0xA8)
+#define WAKEUP_EN TO_REG(0xC0)
+#define WAKEUP_CTRL TO_REG(0xC4)
+#define HW_STRAP2 TO_REG(0xD0)
+#define FREE_CNTR4 TO_REG(0xE0)
+#define FREE_CNTR4_EXT TO_REG(0xE4)
+#define CPU2_CTRL TO_REG(0x100)
+#define CPU2_BASE_SEG1 TO_REG(0x104)
+#define CPU2_BASE_SEG2 TO_REG(0x108)
+#define CPU2_BASE_SEG3 TO_REG(0x10C)
+#define CPU2_BASE_SEG4 TO_REG(0x110)
+#define CPU2_BASE_SEG5 TO_REG(0x114)
+#define CPU2_CACHE_CTRL TO_REG(0x118)
+#define UART_HPLL_CLK TO_REG(0x160)
+#define PCIE_CTRL TO_REG(0x180)
+#define BMC_MMIO_CTRL TO_REG(0x184)
+#define RELOC_DECODE_BASE1 TO_REG(0x188)
+#define RELOC_DECODE_BASE2 TO_REG(0x18C)
+#define MAILBOX_DECODE_BASE TO_REG(0x190)
+#define SRAM_DECODE_BASE1 TO_REG(0x194)
+#define SRAM_DECODE_BASE2 TO_REG(0x198)
+#define BMC_REV TO_REG(0x19C)
+#define BMC_DEV_ID TO_REG(0x1A4)
+
+#define PROT_KEY_UNLOCK 0x1688A8A8
+#define SCU_IO_REGION_SIZE 0x20000
+
+#define AST2400_A0_SILICON_REV 0x02000303U
+
+static const uint32_t ast2400_a0_resets[ASPEED_SCU_NR_REGS] = {
+ [SYS_RST_CTRL] = 0xFFCFFEDCU,
+ [CLK_SEL] = 0xF3F40000U,
+ [CLK_STOP_CTRL] = 0x19FC3E8BU,
+ [D2PLL_PARAM] = 0x00026108U,
+ [MPLL_PARAM] = 0x00030291U,
+ [HPLL_PARAM] = 0x00000291U,
+ [MISC_CTRL1] = 0x00000010U,
+ [PCI_CTRL1] = 0x20001A03U,
+ [PCI_CTRL2] = 0x20001A03U,
+ [PCI_CTRL3] = 0x04000030U,
+ [SYS_RST_STATUS] = 0x00000001U,
+ [SOC_SCRATCH1] = 0x000000C0U, /* SoC completed DRAM init */
+ [MISC_CTRL2] = 0x00000023U,
+ [RNG_CTRL] = 0x0000000EU,
+ [PINMUX_CTRL2] = 0x0000F000U,
+ [PINMUX_CTRL3] = 0x01000000U,
+ [PINMUX_CTRL4] = 0x000000FFU,
+ [PINMUX_CTRL5] = 0x0000A000U,
+ [WDT_RST_CTRL] = 0x003FFFF3U,
+ [PINMUX_CTRL8] = 0xFFFF0000U,
+ [PINMUX_CTRL9] = 0x000FFFFFU,
+ [FREE_CNTR4] = 0x000000FFU,
+ [FREE_CNTR4_EXT] = 0x000000FFU,
+ [CPU2_BASE_SEG1] = 0x80000000U,
+ [CPU2_BASE_SEG4] = 0x1E600000U,
+ [CPU2_BASE_SEG5] = 0xC0000000U,
+ [UART_HPLL_CLK] = 0x00001903U,
+ [PCIE_CTRL] = 0x0000007BU,
+ [BMC_DEV_ID] = 0x00002402U
+};
+
+static uint64_t aspeed_scu_read(void *opaque, hwaddr offset, unsigned size)
+{
+ AspeedSCUState *s = ASPEED_SCU(opaque);
+ int reg = TO_REG(offset);
+
+ if (reg >= ARRAY_SIZE(s->regs)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ return 0;
+ }
+
+ switch (reg) {
+ case WAKEUP_EN:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Read of write-only offset 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ break;
+ }
+
+ return s->regs[reg];
+}
+
+static void aspeed_scu_write(void *opaque, hwaddr offset, uint64_t data,
+ unsigned size)
+{
+ AspeedSCUState *s = ASPEED_SCU(opaque);
+ int reg = TO_REG(offset);
+
+ if (reg >= ARRAY_SIZE(s->regs)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ return;
+ }
+
+ if (reg > PROT_KEY && reg < CPU2_BASE_SEG1 &&
+ s->regs[PROT_KEY] != PROT_KEY_UNLOCK) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: SCU is locked!\n", __func__);
+ return;
+ }
+
+ trace_aspeed_scu_write(offset, size, data);
+
+ switch (reg) {
+ case FREQ_CNTR_EVAL:
+ case VGA_SCRATCH1 ... VGA_SCRATCH8:
+ case RNG_DATA:
+ case SILICON_REV:
+ case FREE_CNTR4:
+ case FREE_CNTR4_EXT:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Write to read-only offset 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ return;
+ }
+
+ s->regs[reg] = data;
+}
+
+static const MemoryRegionOps aspeed_scu_ops = {
+ .read = aspeed_scu_read,
+ .write = aspeed_scu_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .valid.unaligned = false,
+};
+
+static void aspeed_scu_reset(DeviceState *dev)
+{
+ AspeedSCUState *s = ASPEED_SCU(dev);
+ const uint32_t *reset;
+
+ switch (s->silicon_rev) {
+ case AST2400_A0_SILICON_REV:
+ reset = ast2400_a0_resets;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ memcpy(s->regs, reset, sizeof(s->regs));
+ s->regs[SILICON_REV] = s->silicon_rev;
+ s->regs[HW_STRAP1] = s->hw_strap1;
+ s->regs[HW_STRAP2] = s->hw_strap2;
+}
+
+static uint32_t aspeed_silicon_revs[] = { AST2400_A0_SILICON_REV, };
+
+static bool is_supported_silicon_rev(uint32_t silicon_rev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(aspeed_silicon_revs); i++) {
+ if (silicon_rev == aspeed_silicon_revs[i]) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void aspeed_scu_realize(DeviceState *dev, Error **errp)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ AspeedSCUState *s = ASPEED_SCU(dev);
+
+ if (!is_supported_silicon_rev(s->silicon_rev)) {
+ error_setg(errp, "Unknown silicon revision: 0x%" PRIx32,
+ s->silicon_rev);
+ return;
+ }
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_scu_ops, s,
+ TYPE_ASPEED_SCU, SCU_IO_REGION_SIZE);
+
+ sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static const VMStateDescription vmstate_aspeed_scu = {
+ .name = "aspeed.scu",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(regs, AspeedSCUState, ASPEED_SCU_NR_REGS),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property aspeed_scu_properties[] = {
+ DEFINE_PROP_UINT32("silicon-rev", AspeedSCUState, silicon_rev, 0),
+ DEFINE_PROP_UINT32("hw-strap1", AspeedSCUState, hw_strap1, 0),
+ DEFINE_PROP_UINT32("hw-strap2", AspeedSCUState, hw_strap1, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void aspeed_scu_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ dc->realize = aspeed_scu_realize;
+ dc->reset = aspeed_scu_reset;
+ dc->desc = "ASPEED System Control Unit";
+ dc->vmsd = &vmstate_aspeed_scu;
+ dc->props = aspeed_scu_properties;
+}
+
+static const TypeInfo aspeed_scu_info = {
+ .name = TYPE_ASPEED_SCU,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AspeedSCUState),
+ .class_init = aspeed_scu_class_init,
+};
+
+static void aspeed_scu_register_types(void)
+{
+ type_register_static(&aspeed_scu_info);
+}
+
+type_init(aspeed_scu_register_types);
diff --git a/hw/misc/max111x.c b/hw/misc/max111x.c
index 9014f0f705..2a277bdb86 100644
--- a/hw/misc/max111x.c
+++ b/hw/misc/max111x.c
@@ -147,14 +147,14 @@ static int max111x_init(SSISlave *d, int inputs)
return 0;
}
-static int max1110_init(SSISlave *dev)
+static void max1110_realize(SSISlave *dev, Error **errp)
{
- return max111x_init(dev, 8);
+ max111x_init(dev, 8);
}
-static int max1111_init(SSISlave *dev)
+static void max1111_realize(SSISlave *dev, Error **errp)
{
- return max111x_init(dev, 4);
+ max111x_init(dev, 4);
}
void max111x_set_input(DeviceState *dev, int line, uint8_t value)
@@ -183,7 +183,7 @@ static void max1110_class_init(ObjectClass *klass, void *data)
{
SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
- k->init = max1110_init;
+ k->realize = max1110_realize;
}
static const TypeInfo max1110_info = {
@@ -196,7 +196,7 @@ static void max1111_class_init(ObjectClass *klass, void *data)
{
SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
- k->init = max1111_init;
+ k->realize = max1111_realize;
}
static const TypeInfo max1111_info = {
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index 16b6701cbf..ea52a14d78 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -50,3 +50,6 @@ milkymist_pfpu_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x"
milkymist_pfpu_memory_write(uint32_t addr, uint32_t value) "addr %08x value %08x"
milkymist_pfpu_vectout(uint32_t a, uint32_t b, uint32_t dma_ptr) "a %08x b %08x dma_ptr %08x"
milkymist_pfpu_pulse_irq(void) "Pulse IRQ"
+
+# hw/misc/aspeed_scu.c
+aspeed_scu_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32
diff --git a/hw/misc/vmport.c b/hw/misc/vmport.c
index 6896789801..c763811a9f 100644
--- a/hw/misc/vmport.c
+++ b/hw/misc/vmport.c
@@ -36,7 +36,6 @@
#define VMPORT_ENTRIES 0x2c
#define VMPORT_MAGIC 0x564D5868
-#define TYPE_VMPORT "vmport"
#define VMPORT(obj) OBJECT_CHECK(VMPortState, (obj), TYPE_VMPORT)
typedef struct VMPortState
diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c
index 0346f3e335..8a4be1e667 100644
--- a/hw/net/cadence_gem.c
+++ b/hw/net/cadence_gem.c
@@ -274,6 +274,11 @@ static inline unsigned tx_desc_get_last(unsigned *desc)
return (desc[1] & DESC_1_TX_LAST) ? 1 : 0;
}
+static inline void tx_desc_set_last(unsigned *desc)
+{
+ desc[1] |= DESC_1_TX_LAST;
+}
+
static inline unsigned tx_desc_get_length(unsigned *desc)
{
return desc[1] & DESC_1_LENGTH;
@@ -664,6 +669,13 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size)
GEM_DMACFG_RBUFSZ_S) * GEM_DMACFG_RBUFSZ_MUL;
bytes_to_copy = size;
+ /* Hardware allows a zero value here but warns against it. To avoid QEMU
+ * indefinite loops we enforce a minimum value here
+ */
+ if (rxbufsize < GEM_DMACFG_RBUFSZ_MUL) {
+ rxbufsize = GEM_DMACFG_RBUFSZ_MUL;
+ }
+
/* Pad to minimum length. Assume FCS field is stripped, logic
* below will increment it to the real minimum of 64 when
* not FCS stripping
@@ -932,6 +944,7 @@ static void gem_transmit(CadenceGEMState *s)
/* read next descriptor */
if (tx_desc_get_wrap(desc)) {
+ tx_desc_set_last(desc);
packet_desc_addr = s->regs[GEM_TXQBASE];
} else {
packet_desc_addr += 8;
diff --git a/hw/net/e1000.c b/hw/net/e1000.c
index 1202371271..06ca7b2638 100644
--- a/hw/net/e1000.c
+++ b/hw/net/e1000.c
@@ -536,7 +536,7 @@ e1000_send_packet(E1000State *s, const uint8_t *buf, int size)
static void
xmit_seg(E1000State *s)
{
- uint16_t len, *sp;
+ uint16_t len;
unsigned int frames = s->tx.tso_frames, css, sofar;
struct e1000_tx *tp = &s->tx;
@@ -547,7 +547,7 @@ xmit_seg(E1000State *s)
if (tp->props.ip) { /* IPv4 */
stw_be_p(tp->data+css+2, tp->size - css);
stw_be_p(tp->data+css+4,
- be16_to_cpup((uint16_t *)(tp->data+css+4))+frames);
+ lduw_be_p(tp->data + css + 4) + frames);
} else { /* IPv6 */
stw_be_p(tp->data+css+4, tp->size - css);
}
@@ -567,8 +567,9 @@ xmit_seg(E1000State *s)
if (tp->props.sum_needed & E1000_TXD_POPTS_TXSM) {
unsigned int phsum;
// add pseudo-header length before checksum calculation
- sp = (uint16_t *)(tp->data + tp->props.tucso);
- phsum = be16_to_cpup(sp) + len;
+ void *sp = tp->data + tp->props.tucso;
+
+ phsum = lduw_be_p(sp) + len;
phsum = (phsum >> 16) + (phsum & 0xffff);
stw_be_p(sp, phsum);
}
@@ -759,9 +760,9 @@ receive_filter(E1000State *s, const uint8_t *buf, int size)
if (e1000x_is_vlan_packet(buf, le16_to_cpu(s->mac_reg[VET])) &&
e1000x_vlan_rx_filter_enabled(s->mac_reg)) {
- uint16_t vid = be16_to_cpup((uint16_t *)(buf + 14));
- uint32_t vfta = le32_to_cpup((uint32_t *)(s->mac_reg + VFTA) +
- ((vid >> 5) & 0x7f));
+ uint16_t vid = lduw_be_p(buf + 14);
+ uint32_t vfta = ldl_le_p((uint32_t*)(s->mac_reg + VFTA) +
+ ((vid >> 5) & 0x7f));
if ((vfta & (1 << (vid & 0x1f))) == 0)
return 0;
}
@@ -889,8 +890,7 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt)
if (e1000x_vlan_enabled(s->mac_reg) &&
e1000x_is_vlan_packet(filter_buf, le16_to_cpu(s->mac_reg[VET]))) {
- vlan_special = cpu_to_le16(be16_to_cpup((uint16_t *)(filter_buf
- + 14)));
+ vlan_special = cpu_to_le16(lduw_be_p(filter_buf + 14));
iov_ofs = 4;
if (filter_buf == iov->iov_base) {
memmove(filter_buf + 4, filter_buf, 12);
diff --git a/hw/net/e1000e_core.c b/hw/net/e1000e_core.c
index 4549acb120..6050d8b7f8 100644
--- a/hw/net/e1000e_core.c
+++ b/hw/net/e1000e_core.c
@@ -1019,9 +1019,9 @@ e1000e_receive_filter(E1000ECore *core, const uint8_t *buf, int size)
if (e1000x_is_vlan_packet(buf, core->vet) &&
e1000x_vlan_rx_filter_enabled(core->mac)) {
- uint16_t vid = be16_to_cpup((uint16_t *)(buf + 14));
- uint32_t vfta = le32_to_cpup((uint32_t *)(core->mac + VFTA) +
- ((vid >> 5) & 0x7f));
+ uint16_t vid = lduw_be_p(buf + 14);
+ uint32_t vfta = ldl_le_p((uint32_t *)(core->mac + VFTA) +
+ ((vid >> 5) & 0x7f));
if ((vfta & (1 << (vid & 0x1f))) == 0) {
trace_e1000e_rx_flt_vlan_mismatch(vid);
return false;
diff --git a/hw/net/e1000x_common.c b/hw/net/e1000x_common.c
index 94f85c98c8..eb0e097137 100644
--- a/hw/net/e1000x_common.c
+++ b/hw/net/e1000x_common.c
@@ -47,7 +47,7 @@ bool e1000x_rx_ready(PCIDevice *d, uint32_t *mac)
bool e1000x_is_vlan_packet(const uint8_t *buf, uint16_t vet)
{
- uint16_t eth_proto = be16_to_cpup((uint16_t *)(buf + 12));
+ uint16_t eth_proto = lduw_be_p(buf + 12);
bool res = (eth_proto == vet);
trace_e1000x_vlan_is_vlan_pkt(res, eth_proto, vet);
diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c
index 9b4b9b59d2..b10c419838 100644
--- a/hw/net/eepro100.c
+++ b/hw/net/eepro100.c
@@ -352,14 +352,14 @@ static unsigned e100_compute_mcast_idx(const uint8_t *ep)
static uint16_t e100_read_reg2(EEPRO100State *s, E100RegisterOffset addr)
{
assert(!((uintptr_t)&s->mem[addr] & 1));
- return le16_to_cpup((uint16_t *)&s->mem[addr]);
+ return lduw_le_p(&s->mem[addr]);
}
/* Read a 32 bit control/status (CSR) register. */
static uint32_t e100_read_reg4(EEPRO100State *s, E100RegisterOffset addr)
{
assert(!((uintptr_t)&s->mem[addr] & 3));
- return le32_to_cpup((uint32_t *)&s->mem[addr]);
+ return ldl_le_p(&s->mem[addr]);
}
/* Write a 16 bit control/status (CSR) register. */
@@ -367,7 +367,7 @@ static void e100_write_reg2(EEPRO100State *s, E100RegisterOffset addr,
uint16_t val)
{
assert(!((uintptr_t)&s->mem[addr] & 1));
- cpu_to_le16w((uint16_t *)&s->mem[addr], val);
+ stw_le_p(&s->mem[addr], val);
}
/* Read a 32 bit control/status (CSR) register. */
@@ -375,7 +375,7 @@ static void e100_write_reg4(EEPRO100State *s, E100RegisterOffset addr,
uint32_t val)
{
assert(!((uintptr_t)&s->mem[addr] & 3));
- cpu_to_le32w((uint32_t *)&s->mem[addr], val);
+ stl_le_p(&s->mem[addr], val);
}
#if defined(DEBUG_EEPRO100)
diff --git a/hw/net/mipsnet.c b/hw/net/mipsnet.c
index cf8b8236df..5115adcaea 100644
--- a/hw/net/mipsnet.c
+++ b/hw/net/mipsnet.c
@@ -183,10 +183,12 @@ static void mipsnet_ioport_write(void *opaque, hwaddr addr,
break;
case MIPSNET_TX_DATA_BUFFER:
s->tx_buffer[s->tx_written++] = val;
- if (s->tx_written == s->tx_count) {
+ if ((s->tx_written >= MAX_ETH_FRAME_SIZE)
+ || (s->tx_written == s->tx_count)) {
/* Send buffer. */
- trace_mipsnet_send(s->tx_count);
- qemu_send_packet(qemu_get_queue(s->nic), s->tx_buffer, s->tx_count);
+ trace_mipsnet_send(s->tx_written);
+ qemu_send_packet(qemu_get_queue(s->nic),
+ s->tx_buffer, s->tx_written);
s->tx_count = s->tx_written = 0;
s->intctl |= MIPSNET_INTCTL_TXDONE;
s->busy = 1;
diff --git a/hw/net/rocker/rocker_tlv.h b/hw/net/rocker/rocker_tlv.h
index e3c4ab6793..88561648f0 100644
--- a/hw/net/rocker/rocker_tlv.h
+++ b/hw/net/rocker/rocker_tlv.h
@@ -106,17 +106,17 @@ static inline uint64_t rocker_tlv_get_u64(const RockerTlv *tlv)
static inline uint16_t rocker_tlv_get_le16(const RockerTlv *tlv)
{
- return le16_to_cpup((uint16_t *) rocker_tlv_data(tlv));
+ return lduw_le_p(rocker_tlv_data(tlv));
}
static inline uint32_t rocker_tlv_get_le32(const RockerTlv *tlv)
{
- return le32_to_cpup((uint32_t *) rocker_tlv_data(tlv));
+ return ldl_le_p(rocker_tlv_data(tlv));
}
static inline uint64_t rocker_tlv_get_le64(const RockerTlv *tlv)
{
- return le64_to_cpup((uint64_t *) rocker_tlv_data(tlv));
+ return ldq_le_p(rocker_tlv_data(tlv));
}
static inline void rocker_tlv_parse(RockerTlv **tb, int maxtype,
diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c
index 562c1fded2..07297cb78f 100644
--- a/hw/net/rtl8139.c
+++ b/hw/net/rtl8139.c
@@ -1013,8 +1013,8 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t
uint32_t rx_space = rxdw0 & CP_RX_BUFFER_SIZE_MASK;
/* write VLAN info to descriptor variables. */
- if (s->CpCmd & CPlusRxVLAN && be16_to_cpup((uint16_t *)
- &buf[ETH_ALEN * 2]) == ETH_P_VLAN) {
+ if (s->CpCmd & CPlusRxVLAN &&
+ lduw_be_p(&buf[ETH_ALEN * 2]) == ETH_P_VLAN) {
dot1q_buf = &buf[ETH_ALEN * 2];
size -= VLAN_HLEN;
/* if too small buffer, use the tailroom added duing expansion */
@@ -1024,11 +1024,10 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t
rxdw1 &= ~CP_RX_VLAN_TAG_MASK;
/* BE + ~le_to_cpu()~ + cpu_to_le() = BE */
- rxdw1 |= CP_RX_TAVA | le16_to_cpup((uint16_t *)
- &dot1q_buf[ETHER_TYPE_LEN]);
+ rxdw1 |= CP_RX_TAVA | lduw_le_p(&dot1q_buf[ETHER_TYPE_LEN]);
DPRINTF("C+ Rx mode : extracted vlan tag with tci: ""%u\n",
- be16_to_cpup((uint16_t *)&dot1q_buf[ETHER_TYPE_LEN]));
+ lduw_be_p(&dot1q_buf[ETHER_TYPE_LEN]));
} else {
/* reset VLAN tag flag */
rxdw1 &= ~CP_RX_TAVA;
@@ -1352,29 +1351,6 @@ static void RTL8139TallyCounters_dma_write(RTL8139State *s, dma_addr_t tc_addr)
pci_dma_write(d, tc_addr + 62, (uint8_t *)&val16, 2);
}
-/* Loads values of tally counters from VM state file */
-
-static const VMStateDescription vmstate_tally_counters = {
- .name = "tally_counters",
- .version_id = 1,
- .minimum_version_id = 1,
- .fields = (VMStateField[]) {
- VMSTATE_UINT64(TxOk, RTL8139TallyCounters),
- VMSTATE_UINT64(RxOk, RTL8139TallyCounters),
- VMSTATE_UINT64(TxERR, RTL8139TallyCounters),
- VMSTATE_UINT32(RxERR, RTL8139TallyCounters),
- VMSTATE_UINT16(MissPkt, RTL8139TallyCounters),
- VMSTATE_UINT16(FAE, RTL8139TallyCounters),
- VMSTATE_UINT32(Tx1Col, RTL8139TallyCounters),
- VMSTATE_UINT32(TxMCol, RTL8139TallyCounters),
- VMSTATE_UINT64(RxOkPhy, RTL8139TallyCounters),
- VMSTATE_UINT64(RxOkBrd, RTL8139TallyCounters),
- VMSTATE_UINT16(TxAbt, RTL8139TallyCounters),
- VMSTATE_UINT16(TxUndrn, RTL8139TallyCounters),
- VMSTATE_END_OF_LIST()
- }
-};
-
static void rtl8139_ChipCmd_write(RTL8139State *s, uint32_t val)
{
DeviceState *d = DEVICE(s);
@@ -3222,7 +3198,7 @@ static void rtl8139_pre_save(void *opaque)
static const VMStateDescription vmstate_rtl8139 = {
.name = "rtl8139",
- .version_id = 4,
+ .version_id = 5,
.minimum_version_id = 3,
.post_load = rtl8139_post_load,
.pre_save = rtl8139_pre_save,
@@ -3293,8 +3269,19 @@ static const VMStateDescription vmstate_rtl8139 = {
VMSTATE_UINT32(TimerInt, RTL8139State),
VMSTATE_INT64(TCTR_base, RTL8139State),
- VMSTATE_STRUCT(tally_counters, RTL8139State, 0,
- vmstate_tally_counters, RTL8139TallyCounters),
+ VMSTATE_UINT64(tally_counters.TxOk, RTL8139State),
+ VMSTATE_UINT64(tally_counters.RxOk, RTL8139State),
+ VMSTATE_UINT64(tally_counters.TxERR, RTL8139State),
+ VMSTATE_UINT32(tally_counters.RxERR, RTL8139State),
+ VMSTATE_UINT16(tally_counters.MissPkt, RTL8139State),
+ VMSTATE_UINT16(tally_counters.FAE, RTL8139State),
+ VMSTATE_UINT32(tally_counters.Tx1Col, RTL8139State),
+ VMSTATE_UINT32(tally_counters.TxMCol, RTL8139State),
+ VMSTATE_UINT64(tally_counters.RxOkPhy, RTL8139State),
+ VMSTATE_UINT64(tally_counters.RxOkBrd, RTL8139State),
+ VMSTATE_UINT32_V(tally_counters.RxOkMul, RTL8139State, 5),
+ VMSTATE_UINT16(tally_counters.TxAbt, RTL8139State),
+ VMSTATE_UINT16(tally_counters.TxUndrn, RTL8139State),
VMSTATE_UINT32_V(cplus_enabled, RTL8139State, 4),
VMSTATE_END_OF_LIST()
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 5798f87d8e..7e6a60aa12 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -1051,7 +1051,7 @@ static int receive_filter(VirtIONet *n, const uint8_t *buf, int size)
ptr += n->host_hdr_len;
if (!memcmp(&ptr[12], vlan, sizeof(vlan))) {
- int vid = be16_to_cpup((uint16_t *)(ptr + 14)) & 0xfff;
+ int vid = lduw_be_p(ptr + 14) & 0xfff;
if (!(n->vlans[vid >> 5] & (1U << (vid & 0x1f))))
return 0;
}
diff --git a/hw/net/vmware_utils.h b/hw/net/vmware_utils.h
index c0dbb2ff41..550060170e 100644
--- a/hw/net/vmware_utils.h
+++ b/hw/net/vmware_utils.h
@@ -26,97 +26,104 @@
*
*/
static inline void
-vmw_shmem_read(hwaddr addr, void *buf, int len)
+vmw_shmem_read(PCIDevice *d, hwaddr addr, void *buf, int len)
{
VMW_SHPRN("SHMEM r: %" PRIx64 ", len: %d to %p", addr, len, buf);
- cpu_physical_memory_read(addr, buf, len);
+ pci_dma_read(d, addr, buf, len);
}
static inline void
-vmw_shmem_write(hwaddr addr, void *buf, int len)
+vmw_shmem_write(PCIDevice *d, hwaddr addr, void *buf, int len)
{
VMW_SHPRN("SHMEM w: %" PRIx64 ", len: %d to %p", addr, len, buf);
- cpu_physical_memory_write(addr, buf, len);
+ pci_dma_write(d, addr, buf, len);
}
static inline void
-vmw_shmem_rw(hwaddr addr, void *buf, int len, int is_write)
+vmw_shmem_rw(PCIDevice *d, hwaddr addr, void *buf, int len, int is_write)
{
VMW_SHPRN("SHMEM r/w: %" PRIx64 ", len: %d (to %p), is write: %d",
addr, len, buf, is_write);
- cpu_physical_memory_rw(addr, buf, len, is_write);
+ if (is_write)
+ pci_dma_write(d, addr, buf, len);
+ else
+ pci_dma_read(d, addr, buf, len);
}
static inline void
-vmw_shmem_set(hwaddr addr, uint8_t val, int len)
+vmw_shmem_set(PCIDevice *d, hwaddr addr, uint8_t val, int len)
{
int i;
VMW_SHPRN("SHMEM set: %" PRIx64 ", len: %d (value 0x%X)", addr, len, val);
for (i = 0; i < len; i++) {
- cpu_physical_memory_write(addr + i, &val, 1);
+ pci_dma_write(d, addr + i, &val, 1);
}
}
static inline uint32_t
-vmw_shmem_ld8(hwaddr addr)
+vmw_shmem_ld8(PCIDevice *d, hwaddr addr)
{
- uint8_t res = ldub_phys(&address_space_memory, addr);
+ uint8_t res;
+ pci_dma_read(d, addr, &res, 1);
VMW_SHPRN("SHMEM load8: %" PRIx64 " (value 0x%X)", addr, res);
return res;
}
static inline void
-vmw_shmem_st8(hwaddr addr, uint8_t value)
+vmw_shmem_st8(PCIDevice *d, hwaddr addr, uint8_t value)
{
VMW_SHPRN("SHMEM store8: %" PRIx64 " (value 0x%X)", addr, value);
- stb_phys(&address_space_memory, addr, value);
+ pci_dma_write(d, addr, &value, 1);
}
static inline uint32_t
-vmw_shmem_ld16(hwaddr addr)
+vmw_shmem_ld16(PCIDevice *d, hwaddr addr)
{
- uint16_t res = lduw_le_phys(&address_space_memory, addr);
+ uint16_t res;
+ pci_dma_read(d, addr, &res, 2);
VMW_SHPRN("SHMEM load16: %" PRIx64 " (value 0x%X)", addr, res);
return res;
}
static inline void
-vmw_shmem_st16(hwaddr addr, uint16_t value)
+vmw_shmem_st16(PCIDevice *d, hwaddr addr, uint16_t value)
{
VMW_SHPRN("SHMEM store16: %" PRIx64 " (value 0x%X)", addr, value);
- stw_le_phys(&address_space_memory, addr, value);
+ pci_dma_write(d, addr, &value, 2);
}
static inline uint32_t
-vmw_shmem_ld32(hwaddr addr)
+vmw_shmem_ld32(PCIDevice *d, hwaddr addr)
{
- uint32_t res = ldl_le_phys(&address_space_memory, addr);
+ uint32_t res;
+ pci_dma_read(d, addr, &res, 4);
VMW_SHPRN("SHMEM load32: %" PRIx64 " (value 0x%X)", addr, res);
return res;
}
static inline void
-vmw_shmem_st32(hwaddr addr, uint32_t value)
+vmw_shmem_st32(PCIDevice *d, hwaddr addr, uint32_t value)
{
VMW_SHPRN("SHMEM store32: %" PRIx64 " (value 0x%X)", addr, value);
- stl_le_phys(&address_space_memory, addr, value);
+ pci_dma_write(d, addr, &value, 4);
}
static inline uint64_t
-vmw_shmem_ld64(hwaddr addr)
+vmw_shmem_ld64(PCIDevice *d, hwaddr addr)
{
- uint64_t res = ldq_le_phys(&address_space_memory, addr);
+ uint64_t res;
+ pci_dma_read(d, addr, &res, 8);
VMW_SHPRN("SHMEM load64: %" PRIx64 " (value %" PRIx64 ")", addr, res);
return res;
}
static inline void
-vmw_shmem_st64(hwaddr addr, uint64_t value)
+vmw_shmem_st64(PCIDevice *d, hwaddr addr, uint64_t value)
{
VMW_SHPRN("SHMEM store64: %" PRIx64 " (value %" PRIx64 ")", addr, value);
- stq_le_phys(&address_space_memory, addr, value);
+ pci_dma_write(d, addr, &value, 8);
}
/* Macros for simplification of operations on array-style registers */
diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c
index 25cee9ff4b..b8e3b25053 100644
--- a/hw/net/vmxnet3.c
+++ b/hw/net/vmxnet3.c
@@ -74,54 +74,54 @@
#define VMXNET3_MAX_NMSIX_INTRS (1)
/* Macros for rings descriptors access */
-#define VMXNET3_READ_TX_QUEUE_DESCR8(dpa, field) \
- (vmw_shmem_ld8(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field)))
+#define VMXNET3_READ_TX_QUEUE_DESCR8(_d, dpa, field) \
+ (vmw_shmem_ld8(_d, dpa + offsetof(struct Vmxnet3_TxQueueDesc, field)))
-#define VMXNET3_WRITE_TX_QUEUE_DESCR8(dpa, field, value) \
- (vmw_shmem_st8(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field, value)))
+#define VMXNET3_WRITE_TX_QUEUE_DESCR8(_d, dpa, field, value) \
+ (vmw_shmem_st8(_d, dpa + offsetof(struct Vmxnet3_TxQueueDesc, field, value)))
-#define VMXNET3_READ_TX_QUEUE_DESCR32(dpa, field) \
- (vmw_shmem_ld32(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field)))
+#define VMXNET3_READ_TX_QUEUE_DESCR32(_d, dpa, field) \
+ (vmw_shmem_ld32(_d, dpa + offsetof(struct Vmxnet3_TxQueueDesc, field)))
-#define VMXNET3_WRITE_TX_QUEUE_DESCR32(dpa, field, value) \
- (vmw_shmem_st32(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field), value))
+#define VMXNET3_WRITE_TX_QUEUE_DESCR32(_d, dpa, field, value) \
+ (vmw_shmem_st32(_d, dpa + offsetof(struct Vmxnet3_TxQueueDesc, field), value))
-#define VMXNET3_READ_TX_QUEUE_DESCR64(dpa, field) \
- (vmw_shmem_ld64(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field)))
+#define VMXNET3_READ_TX_QUEUE_DESCR64(_d, dpa, field) \
+ (vmw_shmem_ld64(_d, dpa + offsetof(struct Vmxnet3_TxQueueDesc, field)))
-#define VMXNET3_WRITE_TX_QUEUE_DESCR64(dpa, field, value) \
- (vmw_shmem_st64(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field), value))
+#define VMXNET3_WRITE_TX_QUEUE_DESCR64(_d, dpa, field, value) \
+ (vmw_shmem_st64(_d, dpa + offsetof(struct Vmxnet3_TxQueueDesc, field), value))
-#define VMXNET3_READ_RX_QUEUE_DESCR64(dpa, field) \
- (vmw_shmem_ld64(dpa + offsetof(struct Vmxnet3_RxQueueDesc, field)))
+#define VMXNET3_READ_RX_QUEUE_DESCR64(_d, dpa, field) \
+ (vmw_shmem_ld64(_d, dpa + offsetof(struct Vmxnet3_RxQueueDesc, field)))
-#define VMXNET3_READ_RX_QUEUE_DESCR32(dpa, field) \
- (vmw_shmem_ld32(dpa + offsetof(struct Vmxnet3_RxQueueDesc, field)))
+#define VMXNET3_READ_RX_QUEUE_DESCR32(_d, dpa, field) \
+ (vmw_shmem_ld32(_d, dpa + offsetof(struct Vmxnet3_RxQueueDesc, field)))
-#define VMXNET3_WRITE_RX_QUEUE_DESCR64(dpa, field, value) \
- (vmw_shmem_st64(dpa + offsetof(struct Vmxnet3_RxQueueDesc, field), value))
+#define VMXNET3_WRITE_RX_QUEUE_DESCR64(_d, dpa, field, value) \
+ (vmw_shmem_st64(_d, dpa + offsetof(struct Vmxnet3_RxQueueDesc, field), value))
-#define VMXNET3_WRITE_RX_QUEUE_DESCR8(dpa, field, value) \
- (vmw_shmem_st8(dpa + offsetof(struct Vmxnet3_RxQueueDesc, field), value))
+#define VMXNET3_WRITE_RX_QUEUE_DESCR8(_d, dpa, field, value) \
+ (vmw_shmem_st8(_d, dpa + offsetof(struct Vmxnet3_RxQueueDesc, field), value))
/* Macros for guest driver shared area access */
-#define VMXNET3_READ_DRV_SHARED64(shpa, field) \
- (vmw_shmem_ld64(shpa + offsetof(struct Vmxnet3_DriverShared, field)))
+#define VMXNET3_READ_DRV_SHARED64(_d, shpa, field) \
+ (vmw_shmem_ld64(_d, shpa + offsetof(struct Vmxnet3_DriverShared, field)))
-#define VMXNET3_READ_DRV_SHARED32(shpa, field) \
- (vmw_shmem_ld32(shpa + offsetof(struct Vmxnet3_DriverShared, field)))
+#define VMXNET3_READ_DRV_SHARED32(_d, shpa, field) \
+ (vmw_shmem_ld32(_d, shpa + offsetof(struct Vmxnet3_DriverShared, field)))
-#define VMXNET3_WRITE_DRV_SHARED32(shpa, field, val) \
- (vmw_shmem_st32(shpa + offsetof(struct Vmxnet3_DriverShared, field), val))
+#define VMXNET3_WRITE_DRV_SHARED32(_d, shpa, field, val) \
+ (vmw_shmem_st32(_d, shpa + offsetof(struct Vmxnet3_DriverShared, field), val))
-#define VMXNET3_READ_DRV_SHARED16(shpa, field) \
- (vmw_shmem_ld16(shpa + offsetof(struct Vmxnet3_DriverShared, field)))
+#define VMXNET3_READ_DRV_SHARED16(_d, shpa, field) \
+ (vmw_shmem_ld16(_d, shpa + offsetof(struct Vmxnet3_DriverShared, field)))
-#define VMXNET3_READ_DRV_SHARED8(shpa, field) \
- (vmw_shmem_ld8(shpa + offsetof(struct Vmxnet3_DriverShared, field)))
+#define VMXNET3_READ_DRV_SHARED8(_d, shpa, field) \
+ (vmw_shmem_ld8(_d, shpa + offsetof(struct Vmxnet3_DriverShared, field)))
-#define VMXNET3_READ_DRV_SHARED(shpa, field, b, l) \
- (vmw_shmem_read(shpa + offsetof(struct Vmxnet3_DriverShared, field), b, l))
+#define VMXNET3_READ_DRV_SHARED(_d, shpa, field, b, l) \
+ (vmw_shmem_read(_d, shpa + offsetof(struct Vmxnet3_DriverShared, field), b, l))
#define VMXNET_FLAG_IS_SET(field, flag) (((field) & (flag)) == (flag))
@@ -147,7 +147,8 @@ typedef struct {
uint8_t gen;
} Vmxnet3Ring;
-static inline void vmxnet3_ring_init(Vmxnet3Ring *ring,
+static inline void vmxnet3_ring_init(PCIDevice *d,
+ Vmxnet3Ring *ring,
hwaddr pa,
size_t size,
size_t cell_size,
@@ -160,7 +161,7 @@ static inline void vmxnet3_ring_init(Vmxnet3Ring *ring,
ring->next = 0;
if (zero_region) {
- vmw_shmem_set(pa, 0, size * cell_size);
+ vmw_shmem_set(d, pa, 0, size * cell_size);
}
}
@@ -190,14 +191,16 @@ static inline hwaddr vmxnet3_ring_curr_cell_pa(Vmxnet3Ring *ring)
return ring->pa + ring->next * ring->cell_size;
}
-static inline void vmxnet3_ring_read_curr_cell(Vmxnet3Ring *ring, void *buff)
+static inline void vmxnet3_ring_read_curr_cell(PCIDevice *d, Vmxnet3Ring *ring,
+ void *buff)
{
- vmw_shmem_read(vmxnet3_ring_curr_cell_pa(ring), buff, ring->cell_size);
+ vmw_shmem_read(d, vmxnet3_ring_curr_cell_pa(ring), buff, ring->cell_size);
}
-static inline void vmxnet3_ring_write_curr_cell(Vmxnet3Ring *ring, void *buff)
+static inline void vmxnet3_ring_write_curr_cell(PCIDevice *d, Vmxnet3Ring *ring,
+ void *buff)
{
- vmw_shmem_write(vmxnet3_ring_curr_cell_pa(ring), buff, ring->cell_size);
+ vmw_shmem_write(d, vmxnet3_ring_curr_cell_pa(ring), buff, ring->cell_size);
}
static inline size_t vmxnet3_ring_curr_cell_idx(Vmxnet3Ring *ring)
@@ -456,9 +459,9 @@ vmxnet3_on_interrupt_mask_changed(VMXNET3State *s, int lidx, bool is_masked)
vmxnet3_update_interrupt_line_state(s, lidx);
}
-static bool vmxnet3_verify_driver_magic(hwaddr dshmem)
+static bool vmxnet3_verify_driver_magic(PCIDevice *d, hwaddr dshmem)
{
- return (VMXNET3_READ_DRV_SHARED32(dshmem, magic) == VMXNET3_REV1_MAGIC);
+ return (VMXNET3_READ_DRV_SHARED32(d, dshmem, magic) == VMXNET3_REV1_MAGIC);
}
#define VMXNET3_GET_BYTE(x, byte_num) (((x) >> (byte_num)*8) & 0xFF)
@@ -526,13 +529,14 @@ vmxnet3_dec_rx_completion_counter(VMXNET3State *s, int qidx)
static void vmxnet3_complete_packet(VMXNET3State *s, int qidx, uint32_t tx_ridx)
{
struct Vmxnet3_TxCompDesc txcq_descr;
+ PCIDevice *d = PCI_DEVICE(s);
VMXNET3_RING_DUMP(VMW_RIPRN, "TXC", qidx, &s->txq_descr[qidx].comp_ring);
txcq_descr.txdIdx = tx_ridx;
txcq_descr.gen = vmxnet3_ring_curr_gen(&s->txq_descr[qidx].comp_ring);
- vmxnet3_ring_write_curr_cell(&s->txq_descr[qidx].comp_ring, &txcq_descr);
+ vmxnet3_ring_write_curr_cell(d, &s->txq_descr[qidx].comp_ring, &txcq_descr);
/* Flush changes in TX descriptor before changing the counter value */
smp_wmb();
@@ -688,13 +692,14 @@ vmxnet3_pop_next_tx_descr(VMXNET3State *s,
uint32_t *descr_idx)
{
Vmxnet3Ring *ring = &s->txq_descr[qidx].tx_ring;
+ PCIDevice *d = PCI_DEVICE(s);
- vmxnet3_ring_read_curr_cell(ring, txd);
+ vmxnet3_ring_read_curr_cell(d, ring, txd);
if (txd->gen == vmxnet3_ring_curr_gen(ring)) {
/* Only read after generation field verification */
smp_rmb();
/* Re-read to be sure we got the latest version */
- vmxnet3_ring_read_curr_cell(ring, txd);
+ vmxnet3_ring_read_curr_cell(d, ring, txd);
VMXNET3_RING_DUMP(VMW_RIPRN, "TX", qidx, ring);
*descr_idx = vmxnet3_ring_curr_cell_idx(ring);
vmxnet3_inc_tx_consumption_counter(s, qidx);
@@ -782,9 +787,11 @@ static inline void
vmxnet3_read_next_rx_descr(VMXNET3State *s, int qidx, int ridx,
struct Vmxnet3_RxDesc *dbuf, uint32_t *didx)
{
+ PCIDevice *d = PCI_DEVICE(s);
+
Vmxnet3Ring *ring = &s->rxq_descr[qidx].rx_ring[ridx];
*didx = vmxnet3_ring_curr_cell_idx(ring);
- vmxnet3_ring_read_curr_cell(ring, dbuf);
+ vmxnet3_ring_read_curr_cell(d, ring, dbuf);
}
static inline uint8_t
@@ -802,9 +809,8 @@ vmxnet3_pop_rxc_descr(VMXNET3State *s, int qidx, uint32_t *descr_gen)
hwaddr daddr =
vmxnet3_ring_curr_cell_pa(&s->rxq_descr[qidx].comp_ring);
- pci_dma_read(PCI_DEVICE(s), daddr,
- &rxcd, sizeof(struct Vmxnet3_RxCompDesc));
-
+ pci_dma_read(PCI_DEVICE(s),
+ daddr, &rxcd, sizeof(struct Vmxnet3_RxCompDesc));
ring_gen = vmxnet3_ring_curr_gen(&s->rxq_descr[qidx].comp_ring);
if (rxcd.gen != ring_gen) {
@@ -1058,6 +1064,7 @@ static bool
vmxnet3_indicate_packet(VMXNET3State *s)
{
struct Vmxnet3_RxDesc rxd;
+ PCIDevice *d = PCI_DEVICE(s);
bool is_head = true;
uint32_t rxd_idx;
uint32_t rx_ridx = 0;
@@ -1091,7 +1098,7 @@ vmxnet3_indicate_packet(VMXNET3State *s)
}
chunk_size = MIN(bytes_left, rxd.len);
- vmxnet3_pci_dma_writev(PCI_DEVICE(s), data, bytes_copied,
+ vmxnet3_pci_dma_writev(d, data, bytes_copied,
le64_to_cpu(rxd.addr), chunk_size);
bytes_copied += chunk_size;
bytes_left -= chunk_size;
@@ -1099,7 +1106,7 @@ vmxnet3_indicate_packet(VMXNET3State *s)
vmxnet3_dump_rx_descr(&rxd);
if (ready_rxcd_pa != 0) {
- pci_dma_write(PCI_DEVICE(s), ready_rxcd_pa, &rxcd, sizeof(rxcd));
+ pci_dma_write(d, ready_rxcd_pa, &rxcd, sizeof(rxcd));
}
memset(&rxcd, 0, sizeof(struct Vmxnet3_RxCompDesc));
@@ -1131,7 +1138,7 @@ vmxnet3_indicate_packet(VMXNET3State *s)
rxcd.eop = 1;
rxcd.err = (bytes_left != 0);
- pci_dma_write(PCI_DEVICE(s), ready_rxcd_pa, &rxcd, sizeof(rxcd));
+ pci_dma_write(d, ready_rxcd_pa, &rxcd, sizeof(rxcd));
/* Flush RX descriptor changes */
smp_wmb();
@@ -1250,7 +1257,9 @@ static void vmxnet3_reset(VMXNET3State *s)
static void vmxnet3_update_rx_mode(VMXNET3State *s)
{
- s->rx_mode = VMXNET3_READ_DRV_SHARED32(s->drv_shmem,
+ PCIDevice *d = PCI_DEVICE(s);
+
+ s->rx_mode = VMXNET3_READ_DRV_SHARED32(d, s->drv_shmem,
devRead.rxFilterConf.rxMode);
VMW_CFPRN("RX mode: 0x%08X", s->rx_mode);
}
@@ -1258,9 +1267,10 @@ static void vmxnet3_update_rx_mode(VMXNET3State *s)
static void vmxnet3_update_vlan_filters(VMXNET3State *s)
{
int i;
+ PCIDevice *d = PCI_DEVICE(s);
/* Copy configuration from shared memory */
- VMXNET3_READ_DRV_SHARED(s->drv_shmem,
+ VMXNET3_READ_DRV_SHARED(d, s->drv_shmem,
devRead.rxFilterConf.vfTable,
s->vlan_table,
sizeof(s->vlan_table));
@@ -1281,8 +1291,10 @@ static void vmxnet3_update_vlan_filters(VMXNET3State *s)
static void vmxnet3_update_mcast_filters(VMXNET3State *s)
{
+ PCIDevice *d = PCI_DEVICE(s);
+
uint16_t list_bytes =
- VMXNET3_READ_DRV_SHARED16(s->drv_shmem,
+ VMXNET3_READ_DRV_SHARED16(d, s->drv_shmem,
devRead.rxFilterConf.mfTableLen);
s->mcast_list_len = list_bytes / sizeof(s->mcast_list[0]);
@@ -1299,10 +1311,10 @@ static void vmxnet3_update_mcast_filters(VMXNET3State *s)
} else {
int i;
hwaddr mcast_list_pa =
- VMXNET3_READ_DRV_SHARED64(s->drv_shmem,
+ VMXNET3_READ_DRV_SHARED64(d, s->drv_shmem,
devRead.rxFilterConf.mfTablePA);
- pci_dma_read(PCI_DEVICE(s), mcast_list_pa, s->mcast_list, list_bytes);
+ pci_dma_read(d, mcast_list_pa, s->mcast_list, list_bytes);
VMW_CFPRN("Current multicast list len is %d:", s->mcast_list_len);
for (i = 0; i < s->mcast_list_len; i++) {
@@ -1328,19 +1340,20 @@ static uint32_t vmxnet3_get_interrupt_config(VMXNET3State *s)
static void vmxnet3_fill_stats(VMXNET3State *s)
{
int i;
+ PCIDevice *d = PCI_DEVICE(s);
if (!s->device_active)
return;
for (i = 0; i < s->txq_num; i++) {
- pci_dma_write(PCI_DEVICE(s),
+ pci_dma_write(d,
s->txq_descr[i].tx_stats_pa,
&s->txq_descr[i].txq_stats,
sizeof(s->txq_descr[i].txq_stats));
}
for (i = 0; i < s->rxq_num; i++) {
- pci_dma_write(PCI_DEVICE(s),
+ pci_dma_write(d,
s->rxq_descr[i].rx_stats_pa,
&s->rxq_descr[i].rxq_stats,
sizeof(s->rxq_descr[i].rxq_stats));
@@ -1350,8 +1363,9 @@ static void vmxnet3_fill_stats(VMXNET3State *s)
static void vmxnet3_adjust_by_guest_type(VMXNET3State *s)
{
struct Vmxnet3_GOSInfo gos;
+ PCIDevice *d = PCI_DEVICE(s);
- VMXNET3_READ_DRV_SHARED(s->drv_shmem, devRead.misc.driverInfo.gos,
+ VMXNET3_READ_DRV_SHARED(d, s->drv_shmem, devRead.misc.driverInfo.gos,
&gos, sizeof(gos));
s->rx_packets_compound =
(gos.gosType == VMXNET3_GOS_TYPE_WIN) ? false : true;
@@ -1371,13 +1385,14 @@ vmxnet3_dump_conf_descr(const char *name,
static void vmxnet3_update_pm_state(VMXNET3State *s)
{
struct Vmxnet3_VariableLenConfDesc pm_descr;
+ PCIDevice *d = PCI_DEVICE(s);
pm_descr.confLen =
- VMXNET3_READ_DRV_SHARED32(s->drv_shmem, devRead.pmConfDesc.confLen);
+ VMXNET3_READ_DRV_SHARED32(d, s->drv_shmem, devRead.pmConfDesc.confLen);
pm_descr.confVer =
- VMXNET3_READ_DRV_SHARED32(s->drv_shmem, devRead.pmConfDesc.confVer);
+ VMXNET3_READ_DRV_SHARED32(d, s->drv_shmem, devRead.pmConfDesc.confVer);
pm_descr.confPA =
- VMXNET3_READ_DRV_SHARED64(s->drv_shmem, devRead.pmConfDesc.confPA);
+ VMXNET3_READ_DRV_SHARED64(d, s->drv_shmem, devRead.pmConfDesc.confPA);
vmxnet3_dump_conf_descr("PM State", &pm_descr);
}
@@ -1386,8 +1401,9 @@ static void vmxnet3_update_features(VMXNET3State *s)
{
uint32_t guest_features;
int rxcso_supported;
+ PCIDevice *d = PCI_DEVICE(s);
- guest_features = VMXNET3_READ_DRV_SHARED32(s->drv_shmem,
+ guest_features = VMXNET3_READ_DRV_SHARED32(d, s->drv_shmem,
devRead.misc.uptFeatures);
rxcso_supported = VMXNET_FLAG_IS_SET(guest_features, UPT1_F_RXCSUM);
@@ -1462,12 +1478,13 @@ static void vmxnet3_activate_device(VMXNET3State *s)
{
int i;
static const uint32_t VMXNET3_DEF_TX_THRESHOLD = 1;
+ PCIDevice *d = PCI_DEVICE(s);
hwaddr qdescr_table_pa;
uint64_t pa;
uint32_t size;
/* Verify configuration consistency */
- if (!vmxnet3_verify_driver_magic(s->drv_shmem)) {
+ if (!vmxnet3_verify_driver_magic(d, s->drv_shmem)) {
VMW_ERPRN("Device configuration received from driver is invalid");
return;
}
@@ -1483,11 +1500,11 @@ static void vmxnet3_activate_device(VMXNET3State *s)
vmxnet3_update_pm_state(s);
vmxnet3_setup_rx_filtering(s);
/* Cache fields from shared memory */
- s->mtu = VMXNET3_READ_DRV_SHARED32(s->drv_shmem, devRead.misc.mtu);
+ s->mtu = VMXNET3_READ_DRV_SHARED32(d, s->drv_shmem, devRead.misc.mtu);
VMW_CFPRN("MTU is %u", s->mtu);
s->max_rx_frags =
- VMXNET3_READ_DRV_SHARED16(s->drv_shmem, devRead.misc.maxNumRxSG);
+ VMXNET3_READ_DRV_SHARED16(d, s->drv_shmem, devRead.misc.maxNumRxSG);
if (s->max_rx_frags == 0) {
s->max_rx_frags = 1;
@@ -1496,24 +1513,24 @@ static void vmxnet3_activate_device(VMXNET3State *s)
VMW_CFPRN("Max RX fragments is %u", s->max_rx_frags);
s->event_int_idx =
- VMXNET3_READ_DRV_SHARED8(s->drv_shmem, devRead.intrConf.eventIntrIdx);
+ VMXNET3_READ_DRV_SHARED8(d, s->drv_shmem, devRead.intrConf.eventIntrIdx);
assert(vmxnet3_verify_intx(s, s->event_int_idx));
VMW_CFPRN("Events interrupt line is %u", s->event_int_idx);
s->auto_int_masking =
- VMXNET3_READ_DRV_SHARED8(s->drv_shmem, devRead.intrConf.autoMask);
+ VMXNET3_READ_DRV_SHARED8(d, s->drv_shmem, devRead.intrConf.autoMask);
VMW_CFPRN("Automatic interrupt masking is %d", (int)s->auto_int_masking);
s->txq_num =
- VMXNET3_READ_DRV_SHARED8(s->drv_shmem, devRead.misc.numTxQueues);
+ VMXNET3_READ_DRV_SHARED8(d, s->drv_shmem, devRead.misc.numTxQueues);
s->rxq_num =
- VMXNET3_READ_DRV_SHARED8(s->drv_shmem, devRead.misc.numRxQueues);
+ VMXNET3_READ_DRV_SHARED8(d, s->drv_shmem, devRead.misc.numRxQueues);
VMW_CFPRN("Number of TX/RX queues %u/%u", s->txq_num, s->rxq_num);
vmxnet3_validate_queues(s);
qdescr_table_pa =
- VMXNET3_READ_DRV_SHARED64(s->drv_shmem, devRead.misc.queueDescPA);
+ VMXNET3_READ_DRV_SHARED64(d, s->drv_shmem, devRead.misc.queueDescPA);
VMW_CFPRN("TX queues descriptors table is at 0x%" PRIx64, qdescr_table_pa);
/*
@@ -1529,25 +1546,25 @@ static void vmxnet3_activate_device(VMXNET3State *s)
/* Read interrupt number for this TX queue */
s->txq_descr[i].intr_idx =
- VMXNET3_READ_TX_QUEUE_DESCR8(qdescr_pa, conf.intrIdx);
+ VMXNET3_READ_TX_QUEUE_DESCR8(d, qdescr_pa, conf.intrIdx);
assert(vmxnet3_verify_intx(s, s->txq_descr[i].intr_idx));
VMW_CFPRN("TX Queue %d interrupt: %d", i, s->txq_descr[i].intr_idx);
/* Read rings memory locations for TX queues */
- pa = VMXNET3_READ_TX_QUEUE_DESCR64(qdescr_pa, conf.txRingBasePA);
- size = VMXNET3_READ_TX_QUEUE_DESCR32(qdescr_pa, conf.txRingSize);
+ pa = VMXNET3_READ_TX_QUEUE_DESCR64(d, qdescr_pa, conf.txRingBasePA);
+ size = VMXNET3_READ_TX_QUEUE_DESCR32(d, qdescr_pa, conf.txRingSize);
- vmxnet3_ring_init(&s->txq_descr[i].tx_ring, pa, size,
+ vmxnet3_ring_init(d, &s->txq_descr[i].tx_ring, pa, size,
sizeof(struct Vmxnet3_TxDesc), false);
VMXNET3_RING_DUMP(VMW_CFPRN, "TX", i, &s->txq_descr[i].tx_ring);
s->max_tx_frags += size;
/* TXC ring */
- pa = VMXNET3_READ_TX_QUEUE_DESCR64(qdescr_pa, conf.compRingBasePA);
- size = VMXNET3_READ_TX_QUEUE_DESCR32(qdescr_pa, conf.compRingSize);
- vmxnet3_ring_init(&s->txq_descr[i].comp_ring, pa, size,
+ pa = VMXNET3_READ_TX_QUEUE_DESCR64(d, qdescr_pa, conf.compRingBasePA);
+ size = VMXNET3_READ_TX_QUEUE_DESCR32(d, qdescr_pa, conf.compRingSize);
+ vmxnet3_ring_init(d, &s->txq_descr[i].comp_ring, pa, size,
sizeof(struct Vmxnet3_TxCompDesc), true);
VMXNET3_RING_DUMP(VMW_CFPRN, "TXC", i, &s->txq_descr[i].comp_ring);
@@ -1558,7 +1575,7 @@ static void vmxnet3_activate_device(VMXNET3State *s)
sizeof(s->txq_descr[i].txq_stats));
/* Fill device-managed parameters for queues */
- VMXNET3_WRITE_TX_QUEUE_DESCR32(qdescr_pa,
+ VMXNET3_WRITE_TX_QUEUE_DESCR32(d, qdescr_pa,
ctrl.txThreshold,
VMXNET3_DEF_TX_THRESHOLD);
}
@@ -1578,7 +1595,7 @@ static void vmxnet3_activate_device(VMXNET3State *s)
/* Read interrupt number for this RX queue */
s->rxq_descr[i].intr_idx =
- VMXNET3_READ_TX_QUEUE_DESCR8(qd_pa, conf.intrIdx);
+ VMXNET3_READ_TX_QUEUE_DESCR8(d, qd_pa, conf.intrIdx);
assert(vmxnet3_verify_intx(s, s->rxq_descr[i].intr_idx));
VMW_CFPRN("RX Queue %d interrupt: %d", i, s->rxq_descr[i].intr_idx);
@@ -1586,18 +1603,18 @@ static void vmxnet3_activate_device(VMXNET3State *s)
/* Read rings memory locations */
for (j = 0; j < VMXNET3_RX_RINGS_PER_QUEUE; j++) {
/* RX rings */
- pa = VMXNET3_READ_RX_QUEUE_DESCR64(qd_pa, conf.rxRingBasePA[j]);
- size = VMXNET3_READ_RX_QUEUE_DESCR32(qd_pa, conf.rxRingSize[j]);
- vmxnet3_ring_init(&s->rxq_descr[i].rx_ring[j], pa, size,
+ pa = VMXNET3_READ_RX_QUEUE_DESCR64(d, qd_pa, conf.rxRingBasePA[j]);
+ size = VMXNET3_READ_RX_QUEUE_DESCR32(d, qd_pa, conf.rxRingSize[j]);
+ vmxnet3_ring_init(d, &s->rxq_descr[i].rx_ring[j], pa, size,
sizeof(struct Vmxnet3_RxDesc), false);
VMW_CFPRN("RX queue %d:%d: Base: %" PRIx64 ", Size: %d",
i, j, pa, size);
}
/* RXC ring */
- pa = VMXNET3_READ_RX_QUEUE_DESCR64(qd_pa, conf.compRingBasePA);
- size = VMXNET3_READ_RX_QUEUE_DESCR32(qd_pa, conf.compRingSize);
- vmxnet3_ring_init(&s->rxq_descr[i].comp_ring, pa, size,
+ pa = VMXNET3_READ_RX_QUEUE_DESCR64(d, qd_pa, conf.compRingBasePA);
+ size = VMXNET3_READ_RX_QUEUE_DESCR32(d, qd_pa, conf.compRingSize);
+ vmxnet3_ring_init(d, &s->rxq_descr[i].comp_ring, pa, size,
sizeof(struct Vmxnet3_RxCompDesc), true);
VMW_CFPRN("RXC queue %d: Base: %" PRIx64 ", Size: %d", i, pa, size);
@@ -1764,19 +1781,21 @@ static uint64_t vmxnet3_get_command_status(VMXNET3State *s)
static void vmxnet3_set_events(VMXNET3State *s, uint32_t val)
{
uint32_t events;
+ PCIDevice *d = PCI_DEVICE(s);
VMW_CBPRN("Setting events: 0x%x", val);
- events = VMXNET3_READ_DRV_SHARED32(s->drv_shmem, ecr) | val;
- VMXNET3_WRITE_DRV_SHARED32(s->drv_shmem, ecr, events);
+ events = VMXNET3_READ_DRV_SHARED32(d, s->drv_shmem, ecr) | val;
+ VMXNET3_WRITE_DRV_SHARED32(d, s->drv_shmem, ecr, events);
}
static void vmxnet3_ack_events(VMXNET3State *s, uint32_t val)
{
+ PCIDevice *d = PCI_DEVICE(s);
uint32_t events;
VMW_CBPRN("Clearing events: 0x%x", val);
- events = VMXNET3_READ_DRV_SHARED32(s->drv_shmem, ecr) & ~val;
- VMXNET3_WRITE_DRV_SHARED32(s->drv_shmem, ecr, events);
+ events = VMXNET3_READ_DRV_SHARED32(d, s->drv_shmem, ecr) & ~val;
+ VMXNET3_WRITE_DRV_SHARED32(d, s->drv_shmem, ecr, events);
}
static void
diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c
index 70f897e3a9..03be05dc0d 100644
--- a/hw/pci-host/q35.c
+++ b/hw/pci-host/q35.c
@@ -127,6 +127,10 @@ static Property mch_props[] = {
DEFINE_PROP_SIZE(PCI_HOST_PROP_PCI_HOLE64_SIZE, Q35PCIHost,
mch.pci_hole64_size, DEFAULT_PCI_HOLE64_SIZE),
DEFINE_PROP_UINT32("short_root_bus", Q35PCIHost, mch.short_root_bus, 0),
+ DEFINE_PROP_SIZE(PCI_HOST_BELOW_4G_MEM_SIZE, Q35PCIHost,
+ mch.below_4g_mem_size, 0),
+ DEFINE_PROP_SIZE(PCI_HOST_ABOVE_4G_MEM_SIZE, Q35PCIHost,
+ mch.above_4g_mem_size, 0),
DEFINE_PROP_END_OF_LIST(),
};
@@ -177,6 +181,22 @@ static void q35_host_initfn(Object *obj)
q35_host_get_mmcfg_size,
NULL, NULL, NULL, NULL);
+ object_property_add_link(obj, MCH_HOST_PROP_RAM_MEM, TYPE_MEMORY_REGION,
+ (Object **) &s->mch.ram_memory,
+ qdev_prop_allow_set_link_before_realize, 0, NULL);
+
+ object_property_add_link(obj, MCH_HOST_PROP_PCI_MEM, TYPE_MEMORY_REGION,
+ (Object **) &s->mch.pci_address_space,
+ qdev_prop_allow_set_link_before_realize, 0, NULL);
+
+ object_property_add_link(obj, MCH_HOST_PROP_SYSTEM_MEM, TYPE_MEMORY_REGION,
+ (Object **) &s->mch.system_memory,
+ qdev_prop_allow_set_link_before_realize, 0, NULL);
+
+ object_property_add_link(obj, MCH_HOST_PROP_IO_MEM, TYPE_MEMORY_REGION,
+ (Object **) &s->mch.address_space_io,
+ qdev_prop_allow_set_link_before_realize, 0, NULL);
+
/* Leave enough space for the biggest MCFG BAR */
/* TODO: this matches current bios behaviour, but
* it's not a power of two, which means an MTRR
diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
index 5cc6608e50..91a3420f47 100644
--- a/hw/ppc/Makefile.objs
+++ b/hw/ppc/Makefile.objs
@@ -8,6 +8,7 @@ obj-$(CONFIG_PSERIES) += spapr_cpu_core.o
ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
obj-y += spapr_pci_vfio.o
endif
+obj-$(CONFIG_PSERIES) += spapr_rtas_ddw.o
# PowerPC 4xx boards
obj-y += ppc405_boards.o ppc4xx_devs.o ppc405_uc.o ppc440_bamboo.o
obj-y += ppc4xx_pci.o
diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c
index ee1c60b820..0cd534df55 100644
--- a/hw/ppc/e500.c
+++ b/hw/ppc/e500.c
@@ -601,7 +601,7 @@ static int ppce500_prep_device_tree(MachineState *machine,
}
/* Create -kernel TLB entries for BookE. */
-static inline hwaddr booke206_page_size_to_tlb(uint64_t size)
+hwaddr booke206_page_size_to_tlb(uint64_t size)
{
return 63 - clz64(size >> 10);
}
diff --git a/hw/ppc/e500.h b/hw/ppc/e500.h
index ef224ea5e6..70ba1d8f4f 100644
--- a/hw/ppc/e500.h
+++ b/hw/ppc/e500.h
@@ -26,4 +26,6 @@ typedef struct PPCE500Params {
void ppce500_init(MachineState *machine, PPCE500Params *params);
+hwaddr booke206_page_size_to_tlb(uint64_t size);
+
#endif
diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c
index 1bcf740f0e..e4252528a6 100644
--- a/hw/ppc/ppc.c
+++ b/hw/ppc/ppc.c
@@ -699,9 +699,18 @@ static inline void cpu_ppc_decr_lower(PowerPCCPU *cpu)
static inline void cpu_ppc_hdecr_excp(PowerPCCPU *cpu)
{
+ CPUPPCState *env = &cpu->env;
+
/* Raise it */
- LOG_TB("raise decrementer exception\n");
- ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 1);
+ LOG_TB("raise hv decrementer exception\n");
+
+ /* The architecture specifies that we don't deliver HDEC
+ * interrupts in a PM state. Not only they don't cause a
+ * wakeup but they also get effectively discarded.
+ */
+ if (!env->in_pm_state) {
+ ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 1);
+ }
}
static inline void cpu_ppc_hdecr_lower(PowerPCCPU *cpu)
@@ -928,9 +937,7 @@ clk_setup_cb cpu_ppc_tb_init (CPUPPCState *env, uint32_t freq)
}
/* Create new timer */
tb_env->decr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_ppc_decr_cb, cpu);
- if (0) {
- /* XXX: find a suitable condition to enable the hypervisor decrementer
- */
+ if (env->has_hv_mode) {
tb_env->hdecr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_ppc_hdecr_cb,
cpu);
} else {
diff --git a/hw/ppc/ppce500_spin.c b/hw/ppc/ppce500_spin.c
index 76bd78bfd7..22c584eb8d 100644
--- a/hw/ppc/ppce500_spin.c
+++ b/hw/ppc/ppce500_spin.c
@@ -32,6 +32,7 @@
#include "sysemu/sysemu.h"
#include "hw/sysbus.h"
#include "sysemu/kvm.h"
+#include "e500.h"
#define MAX_CPUS 32
@@ -72,12 +73,6 @@ static void spin_reset(void *opaque)
}
}
-/* Create -kernel TLB entries for BookE, linearly spanning 256MB. */
-static inline hwaddr booke206_page_size_to_tlb(uint64_t size)
-{
- return ctz32(size >> 10) >> 1;
-}
-
static void mmubooke_create_initial_mapping(CPUPPCState *env,
target_ulong va,
hwaddr pa,
@@ -104,7 +99,7 @@ static void spin_kick(void *data)
hwaddr map_start;
cpu_synchronize_state(cpu);
- stl_p(&curspin->pir, env->spr[SPR_PIR]);
+ stl_p(&curspin->pir, env->spr[SPR_BOOKE_PIR]);
env->nip = ldq_p(&curspin->addr) & (map_size - 1);
env->gpr[3] = ldq_p(&curspin->r3);
env->gpr[4] = 0;
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index 778fa255a9..7f33a1b2b5 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -116,15 +116,16 @@ static XICSState *try_create_xics(const char *type, int nr_servers,
static XICSState *xics_system_init(MachineState *machine,
int nr_servers, int nr_irqs, Error **errp)
{
- XICSState *icp = NULL;
+ XICSState *xics = NULL;
if (kvm_enabled()) {
Error *err = NULL;
if (machine_kernel_irqchip_allowed(machine)) {
- icp = try_create_xics(TYPE_KVM_XICS, nr_servers, nr_irqs, &err);
+ xics = try_create_xics(TYPE_XICS_SPAPR_KVM, nr_servers, nr_irqs,
+ &err);
}
- if (machine_kernel_irqchip_required(machine) && !icp) {
+ if (machine_kernel_irqchip_required(machine) && !xics) {
error_reportf_err(err,
"kernel_irqchip requested but unavailable: ");
} else {
@@ -132,11 +133,11 @@ static XICSState *xics_system_init(MachineState *machine,
}
}
- if (!icp) {
- icp = try_create_xics(TYPE_XICS, nr_servers, nr_irqs, errp);
+ if (!xics) {
+ xics = try_create_xics(TYPE_XICS_SPAPR, nr_servers, nr_irqs, errp);
}
- return icp;
+ return xics;
}
static int spapr_fixup_cpu_smt_dt(void *fdt, int offset, PowerPCCPU *cpu,
@@ -339,6 +340,9 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base,
add_str(hypertas, "hcall-splpar");
add_str(hypertas, "hcall-bulk");
add_str(hypertas, "hcall-set-mode");
+ add_str(hypertas, "hcall-sprg0");
+ add_str(hypertas, "hcall-copy");
+ add_str(hypertas, "hcall-debug");
add_str(qemu_hypertas, "hcall-memop1");
fdt = g_malloc0(FDT_MAX_SIZE);
@@ -1767,6 +1771,13 @@ static void ppc_spapr_init(MachineState *machine)
spapr->vrma_adjust = 1;
spapr->rma_size = MIN(spapr->rma_size, 0x10000000);
}
+
+ /* Actually we don't support unbounded RMA anymore since we
+ * added proper emulation of HV mode. The max we can get is
+ * 16G which also happens to be what we configure for PAPR
+ * mode so make sure we don't do anything bigger than that
+ */
+ spapr->rma_size = MIN(spapr->rma_size, 0x400000000ull);
}
if (spapr->rma_size > node0_size) {
@@ -1779,9 +1790,9 @@ static void ppc_spapr_init(MachineState *machine)
load_limit = MIN(spapr->rma_size, RTAS_MAX_ADDR) - FW_OVERHEAD;
/* Set up Interrupt Controller before we create the VCPUs */
- spapr->icp = xics_system_init(machine,
- DIV_ROUND_UP(max_cpus * smt, smp_threads),
- XICS_IRQS, &error_fatal);
+ spapr->xics = xics_system_init(machine,
+ DIV_ROUND_UP(max_cpus * smt, smp_threads),
+ XICS_IRQS_SPAPR, &error_fatal);
if (smc->dr_lmb_enabled) {
spapr_validate_node_memory(machine, &error_fatal);
@@ -2367,8 +2378,8 @@ static HotpluggableCPUList *spapr_query_hotpluggable_cpus(MachineState *machine)
cpu_item->type = spapr_get_cpu_core_type(machine->cpu_model);
cpu_item->vcpus_count = smp_threads;
- cpu_props->has_core = true;
- cpu_props->core = i * smt;
+ cpu_props->has_core_id = true;
+ cpu_props->core_id = i * smt;
/* TODO: add 'has_node/node' here to describe
to which node core belongs */
@@ -2485,7 +2496,12 @@ DEFINE_SPAPR_MACHINE(2_7, "2.7", true);
* pseries-2.6
*/
#define SPAPR_COMPAT_2_6 \
- HW_COMPAT_2_6
+ HW_COMPAT_2_6 \
+ { \
+ .driver = TYPE_SPAPR_PCI_HOST_BRIDGE,\
+ .property = "ddw",\
+ .value = stringify(off),\
+ },
static void spapr_machine_2_6_instance_options(MachineState *machine)
{
diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c
index 3a5da09b99..70b6b0b5ee 100644
--- a/hw/ppc/spapr_cpu_core.c
+++ b/hw/ppc/spapr_cpu_core.c
@@ -42,7 +42,7 @@ static void spapr_cpu_destroy(PowerPCCPU *cpu)
{
sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
- xics_cpu_destroy(spapr->icp, cpu);
+ xics_cpu_destroy(spapr->xics, cpu);
qemu_unregister_reset(spapr_cpu_reset, cpu);
}
@@ -76,7 +76,7 @@ void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, Error **errp)
}
}
- xics_cpu_setup(spapr->icp, cpu);
+ xics_cpu_setup(spapr->xics, cpu);
qemu_register_reset(spapr_cpu_reset, cpu);
spapr_cpu_reset(cpu);
@@ -102,7 +102,6 @@ static void spapr_core_release(DeviceState *dev, void *opaque)
const char *typename = object_class_get_name(sc->cpu_class);
size_t size = object_type_get_instance_size(typename);
sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
- sPAPRCPUCore *core = SPAPR_CPU_CORE(OBJECT(dev));
CPUCore *cc = CPU_CORE(dev);
int smt = kvmppc_smt_threads();
int i;
@@ -120,7 +119,7 @@ static void spapr_core_release(DeviceState *dev, void *opaque)
spapr->cores[cc->core_id / smt] = NULL;
- g_free(core->threads);
+ g_free(sc->threads);
object_unparent(OBJECT(dev));
}
@@ -260,23 +259,24 @@ out:
error_propagate(errp, local_err);
}
-static int spapr_cpu_core_realize_child(Object *child, void *opaque)
+static void spapr_cpu_core_realize_child(Object *child, Error **errp)
{
- Error **errp = opaque;
+ Error *local_err = NULL;
sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
CPUState *cs = CPU(child);
PowerPCCPU *cpu = POWERPC_CPU(cs);
- object_property_set_bool(child, true, "realized", errp);
- if (*errp) {
- return 1;
+ object_property_set_bool(child, true, "realized", &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
}
- spapr_cpu_init(spapr, cpu, errp);
- if (*errp) {
- return 1;
+ spapr_cpu_init(spapr, cpu, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
}
- return 0;
}
static void spapr_cpu_core_realize(DeviceState *dev, Error **errp)
@@ -286,13 +286,13 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp)
const char *typename = object_class_get_name(sc->cpu_class);
size_t size = object_type_get_instance_size(typename);
Error *local_err = NULL;
- Object *obj;
- int i;
+ void *obj;
+ int i, j;
sc->threads = g_malloc0(size * cc->nr_threads);
for (i = 0; i < cc->nr_threads; i++) {
char id[32];
- void *obj = sc->threads + i * size;
+ obj = sc->threads + i * size;
object_initialize(obj, size, typename);
snprintf(id, sizeof(id), "thread[%d]", i);
@@ -300,19 +300,23 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp)
if (local_err) {
goto err;
}
+ object_unref(obj);
}
- object_child_foreach(OBJECT(dev), spapr_cpu_core_realize_child, &local_err);
- if (local_err) {
- goto err;
- } else {
- return;
+
+ for (j = 0; j < cc->nr_threads; j++) {
+ obj = sc->threads + j * size;
+
+ spapr_cpu_core_realize_child(obj, &local_err);
+ if (local_err) {
+ goto err;
+ }
}
+ return;
err:
- while (i >= 0) {
+ while (--i >= 0) {
obj = sc->threads + i * size;
object_unparent(obj);
- i--;
}
g_free(sc->threads);
error_propagate(errp, local_err);
@@ -326,7 +330,6 @@ static void spapr_cpu_core_class_init(ObjectClass *oc, void *data)
/*
* instance_init routines from different flavours of sPAPR CPU cores.
- * TODO: Add support for 'host' core type.
*/
#define SPAPR_CPU_CORE_INITFN(_type, _fname) \
static void glue(glue(spapr_cpu_core_, _fname), _initfn(Object *obj)) \
@@ -339,10 +342,15 @@ static void glue(glue(spapr_cpu_core_, _fname), _initfn(Object *obj)) \
core->cpu_class = oc; \
}
+SPAPR_CPU_CORE_INITFN(970mp_v1.0, 970MP_v10);
+SPAPR_CPU_CORE_INITFN(970mp_v1.1, 970MP_v11);
+SPAPR_CPU_CORE_INITFN(970_v2.2, 970);
+SPAPR_CPU_CORE_INITFN(POWER5+_v2.1, POWER5plus);
SPAPR_CPU_CORE_INITFN(POWER7_v2.3, POWER7);
SPAPR_CPU_CORE_INITFN(POWER7+_v2.1, POWER7plus);
SPAPR_CPU_CORE_INITFN(POWER8_v2.0, POWER8);
SPAPR_CPU_CORE_INITFN(POWER8E_v2.1, POWER8E);
+SPAPR_CPU_CORE_INITFN(POWER8NVL_v1.0, POWER8NVL);
typedef struct SPAPRCoreInfo {
const char *name;
@@ -350,6 +358,21 @@ typedef struct SPAPRCoreInfo {
} SPAPRCoreInfo;
static const SPAPRCoreInfo spapr_cores[] = {
+ /* 970 and aliaes */
+ { .name = "970_v2.2", .initfn = spapr_cpu_core_970_initfn },
+ { .name = "970", .initfn = spapr_cpu_core_970_initfn },
+
+ /* 970MP variants and aliases */
+ { .name = "970MP_v1.0", .initfn = spapr_cpu_core_970MP_v10_initfn },
+ { .name = "970mp_v1.0", .initfn = spapr_cpu_core_970MP_v10_initfn },
+ { .name = "970MP_v1.1", .initfn = spapr_cpu_core_970MP_v11_initfn },
+ { .name = "970mp_v1.1", .initfn = spapr_cpu_core_970MP_v11_initfn },
+ { .name = "970mp", .initfn = spapr_cpu_core_970MP_v11_initfn },
+
+ /* POWER5 and aliases */
+ { .name = "POWER5+_v2.1", .initfn = spapr_cpu_core_POWER5plus_initfn },
+ { .name = "POWER5+", .initfn = spapr_cpu_core_POWER5plus_initfn },
+
/* POWER7 and aliases */
{ .name = "POWER7_v2.3", .initfn = spapr_cpu_core_POWER7_initfn },
{ .name = "POWER7", .initfn = spapr_cpu_core_POWER7_initfn },
@@ -367,6 +390,10 @@ static const SPAPRCoreInfo spapr_cores[] = {
{ .name = "POWER8E_v2.1", .initfn = spapr_cpu_core_POWER8E_initfn },
{ .name = "POWER8E", .initfn = spapr_cpu_core_POWER8E_initfn },
+ /* POWER8NVL and aliases */
+ { .name = "POWER8NVL_v1.0", .initfn = spapr_cpu_core_POWER8NVL_initfn },
+ { .name = "POWER8NVL", .initfn = spapr_cpu_core_POWER8NVL_initfn },
+
{ .name = NULL }
};
diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c
index af8099220e..b0668b34a9 100644
--- a/hw/ppc/spapr_events.c
+++ b/hw/ppc/spapr_events.c
@@ -386,7 +386,7 @@ static void spapr_powerdown_req(Notifier *n, void *opaque)
rtas_event_log_queue(RTAS_LOG_TYPE_EPOW, new_epow, true);
- qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq));
+ qemu_irq_pulse(xics_get_qirq(spapr->xics, spapr->check_exception_irq));
}
static void spapr_hotplug_set_signalled(uint32_t drc_index)
@@ -468,7 +468,7 @@ static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action,
rtas_event_log_queue(RTAS_LOG_TYPE_HOTPLUG, new_hp, true);
- qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq));
+ qemu_irq_pulse(xics_get_qirq(spapr->xics, spapr->check_exception_irq));
}
void spapr_hotplug_req_add_by_index(sPAPRDRConnector *drc)
@@ -551,7 +551,7 @@ static void check_exception(PowerPCCPU *cpu, sPAPRMachineState *spapr,
* interrupts.
*/
if (rtas_event_log_contains(mask, true)) {
- qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq));
+ qemu_irq_pulse(xics_get_qirq(spapr->xics, spapr->check_exception_irq));
}
return;
@@ -603,7 +603,7 @@ out_no_events:
void spapr_events_init(sPAPRMachineState *spapr)
{
QTAILQ_INIT(&spapr->pending_events);
- spapr->check_exception_irq = xics_alloc(spapr->icp, 0, 0, false,
+ spapr->check_exception_irq = xics_spapr_alloc(spapr->xics, 0, 0, false,
&error_fatal);
spapr->epow_notifier.notify = spapr_powerdown_req;
qemu_register_powerdown_notifier(&spapr->epow_notifier);
diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
index e011ed4b66..73af112e1d 100644
--- a/hw/ppc/spapr_hcall.c
+++ b/hw/ppc/spapr_hcall.c
@@ -83,12 +83,12 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong pte_index = args[1];
target_ulong pteh = args[2];
target_ulong ptel = args[3];
- unsigned apshift, spshift;
+ unsigned apshift;
target_ulong raddr;
target_ulong index;
uint64_t token;
- apshift = ppc_hash64_hpte_page_shift_noslb(cpu, pteh, ptel, &spshift);
+ apshift = ppc_hash64_hpte_page_shift_noslb(cpu, pteh, ptel);
if (!apshift) {
/* Bad page size encoding */
return H_PARAMETER;
diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c
index e230bacae1..d57b05d5c0 100644
--- a/hw/ppc/spapr_iommu.c
+++ b/hw/ppc/spapr_iommu.c
@@ -156,6 +156,16 @@ static uint64_t spapr_tce_get_min_page_size(MemoryRegion *iommu)
return 1ULL << tcet->page_shift;
}
+static void spapr_tce_notify_started(MemoryRegion *iommu)
+{
+ spapr_tce_set_need_vfio(container_of(iommu, sPAPRTCETable, iommu), true);
+}
+
+static void spapr_tce_notify_stopped(MemoryRegion *iommu)
+{
+ spapr_tce_set_need_vfio(container_of(iommu, sPAPRTCETable, iommu), false);
+}
+
static int spapr_tce_table_post_load(void *opaque, int version_id)
{
sPAPRTCETable *tcet = SPAPR_TCE_TABLE(opaque);
@@ -236,6 +246,8 @@ static const VMStateDescription vmstate_spapr_tce_table = {
static MemoryRegionIOMMUOps spapr_iommu_ops = {
.translate = spapr_tce_translate_iommu,
.get_min_page_size = spapr_tce_get_min_page_size,
+ .notify_started = spapr_tce_notify_started,
+ .notify_stopped = spapr_tce_notify_stopped,
};
static int spapr_tce_table_realize(DeviceState *dev)
diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c
index 9f28fb3829..949c44fec8 100644
--- a/hw/ppc/spapr_pci.c
+++ b/hw/ppc/spapr_pci.c
@@ -35,6 +35,7 @@
#include "hw/ppc/spapr.h"
#include "hw/pci-host/spapr.h"
#include "exec/address-spaces.h"
+#include "exec/ram_addr.h"
#include <libfdt.h>
#include "trace.h"
#include "qemu/error-report.h"
@@ -45,6 +46,7 @@
#include "hw/ppc/spapr_drc.h"
#include "sysemu/device_tree.h"
#include "sysemu/kvm.h"
+#include "sysemu/hostmem.h"
#include "hw/vfio/vfio.h"
@@ -322,7 +324,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
return;
}
- xics_free(spapr->icp, msi->first_irq, msi->num);
+ xics_spapr_free(spapr->xics, msi->first_irq, msi->num);
if (msi_present(pdev)) {
spapr_msi_setmsg(pdev, 0, false, 0, 0);
}
@@ -360,7 +362,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
}
/* Allocate MSIs */
- irq = xics_alloc_block(spapr->icp, 0, req_num, false,
+ irq = xics_spapr_alloc_block(spapr->xics, 0, req_num, false,
ret_intr_type == RTAS_TYPE_MSI, &err);
if (err) {
error_reportf_err(err, "Can't allocate MSIs for device %x: ",
@@ -371,7 +373,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
/* Release previous MSIs */
if (msi) {
- xics_free(spapr->icp, msi->first_irq, msi->num);
+ xics_spapr_free(spapr->xics, msi->first_irq, msi->num);
g_hash_table_remove(phb->msi, &config_addr);
}
@@ -733,7 +735,7 @@ static void spapr_msi_write(void *opaque, hwaddr addr,
trace_spapr_pci_msi_write(addr, data, irq);
- qemu_irq_pulse(xics_get_qirq(spapr->icp, irq));
+ qemu_irq_pulse(xics_get_qirq(spapr->xics, irq));
}
static const MemoryRegionOps spapr_msi_ops = {
@@ -1087,12 +1089,6 @@ static void spapr_phb_add_pci_device(sPAPRDRConnector *drc,
void *fdt = NULL;
int fdt_start_offset = 0, fdt_size;
- if (object_dynamic_cast(OBJECT(pdev), "vfio-pci")) {
- sPAPRTCETable *tcet = spapr_tce_find_by_liobn(phb->dma_liobn);
-
- spapr_tce_set_need_vfio(tcet, true);
- }
-
fdt = create_device_tree(&fdt_size);
fdt_start_offset = spapr_create_pci_child_dt(phb, pdev, fdt, 0);
if (!fdt_start_offset) {
@@ -1310,11 +1306,14 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
PCIBus *bus;
uint64_t msi_window_size = 4096;
sPAPRTCETable *tcet;
+ const unsigned windows_supported =
+ sphb->ddw_enabled ? SPAPR_PCI_DMA_MAX_WINDOWS : 1;
if (sphb->index != (uint32_t)-1) {
hwaddr windows_base;
- if ((sphb->buid != (uint64_t)-1) || (sphb->dma_liobn != (uint32_t)-1)
+ if ((sphb->buid != (uint64_t)-1) || (sphb->dma_liobn[0] != (uint32_t)-1)
+ || (sphb->dma_liobn[1] != (uint32_t)-1 && windows_supported == 2)
|| (sphb->mem_win_addr != (hwaddr)-1)
|| (sphb->io_win_addr != (hwaddr)-1)) {
error_setg(errp, "Either \"index\" or other parameters must"
@@ -1329,7 +1328,9 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
}
sphb->buid = SPAPR_PCI_BASE_BUID + sphb->index;
- sphb->dma_liobn = SPAPR_PCI_LIOBN(sphb->index, 0);
+ for (i = 0; i < windows_supported; ++i) {
+ sphb->dma_liobn[i] = SPAPR_PCI_LIOBN(sphb->index, i);
+ }
windows_base = SPAPR_PCI_WINDOW_BASE
+ sphb->index * SPAPR_PCI_WINDOW_SPACING;
@@ -1342,8 +1343,9 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
return;
}
- if (sphb->dma_liobn == (uint32_t)-1) {
- error_setg(errp, "LIOBN not specified for PHB");
+ if ((sphb->dma_liobn[0] == (uint32_t)-1) ||
+ ((sphb->dma_liobn[1] == (uint32_t)-1) && (windows_supported > 1))) {
+ error_setg(errp, "LIOBN(s) not specified for PHB");
return;
}
@@ -1442,7 +1444,8 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
uint32_t irq;
Error *local_err = NULL;
- irq = xics_alloc_block(spapr->icp, 0, 1, true, false, &local_err);
+ irq = xics_spapr_alloc_block(spapr->xics, 0, 1, true, false,
+ &local_err);
if (local_err) {
error_propagate(errp, local_err);
error_prepend(errp, "can't allocate LSIs: ");
@@ -1461,16 +1464,18 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
}
}
- tcet = spapr_tce_new_table(DEVICE(sphb), sphb->dma_liobn);
- if (!tcet) {
- error_setg(errp, "Unable to create TCE table for %s",
- sphb->dtbusname);
- return;
+ /* DMA setup */
+ for (i = 0; i < windows_supported; ++i) {
+ tcet = spapr_tce_new_table(DEVICE(sphb), sphb->dma_liobn[i]);
+ if (!tcet) {
+ error_setg(errp, "Creating window#%d failed for %s",
+ i, sphb->dtbusname);
+ return;
+ }
+ memory_region_add_subregion_overlap(&sphb->iommu_root, 0,
+ spapr_tce_get_iommu(tcet), 0);
}
- memory_region_add_subregion_overlap(&sphb->iommu_root, 0,
- spapr_tce_get_iommu(tcet), 0);
-
sphb->msi = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free);
}
@@ -1487,13 +1492,19 @@ static int spapr_phb_children_reset(Object *child, void *opaque)
void spapr_phb_dma_reset(sPAPRPHBState *sphb)
{
- sPAPRTCETable *tcet = spapr_tce_find_by_liobn(sphb->dma_liobn);
+ int i;
+ sPAPRTCETable *tcet;
- if (tcet && tcet->nb_table) {
- spapr_tce_table_disable(tcet);
+ for (i = 0; i < SPAPR_PCI_DMA_MAX_WINDOWS; ++i) {
+ tcet = spapr_tce_find_by_liobn(sphb->dma_liobn[i]);
+
+ if (tcet && tcet->nb_table) {
+ spapr_tce_table_disable(tcet);
+ }
}
/* Register default 32bit DMA window */
+ tcet = spapr_tce_find_by_liobn(sphb->dma_liobn[0]);
spapr_tce_table_enable(tcet, SPAPR_TCE_PAGE_SHIFT, sphb->dma_win_addr,
sphb->dma_win_size >> SPAPR_TCE_PAGE_SHIFT);
}
@@ -1515,7 +1526,8 @@ static void spapr_phb_reset(DeviceState *qdev)
static Property spapr_phb_properties[] = {
DEFINE_PROP_UINT32("index", sPAPRPHBState, index, -1),
DEFINE_PROP_UINT64("buid", sPAPRPHBState, buid, -1),
- DEFINE_PROP_UINT32("liobn", sPAPRPHBState, dma_liobn, -1),
+ DEFINE_PROP_UINT32("liobn", sPAPRPHBState, dma_liobn[0], -1),
+ DEFINE_PROP_UINT32("liobn64", sPAPRPHBState, dma_liobn[1], -1),
DEFINE_PROP_UINT64("mem_win_addr", sPAPRPHBState, mem_win_addr, -1),
DEFINE_PROP_UINT64("mem_win_size", sPAPRPHBState, mem_win_size,
SPAPR_PCI_MMIO_WIN_SIZE),
@@ -1527,6 +1539,11 @@ static Property spapr_phb_properties[] = {
/* Default DMA window is 0..1GB */
DEFINE_PROP_UINT64("dma_win_addr", sPAPRPHBState, dma_win_addr, 0),
DEFINE_PROP_UINT64("dma_win_size", sPAPRPHBState, dma_win_size, 0x40000000),
+ DEFINE_PROP_UINT64("dma64_win_addr", sPAPRPHBState, dma64_win_addr,
+ 0x800000000000000ULL),
+ DEFINE_PROP_BOOL("ddw", sPAPRPHBState, ddw_enabled, true),
+ DEFINE_PROP_UINT64("pgsz", sPAPRPHBState, page_size_mask,
+ (1ULL << 12) | (1ULL << 16)),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1603,7 +1620,7 @@ static const VMStateDescription vmstate_spapr_pci = {
.post_load = spapr_pci_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINT64_EQUAL(buid, sPAPRPHBState),
- VMSTATE_UINT32_EQUAL(dma_liobn, sPAPRPHBState),
+ VMSTATE_UINT32_EQUAL(dma_liobn[0], sPAPRPHBState),
VMSTATE_UINT64_EQUAL(mem_win_addr, sPAPRPHBState),
VMSTATE_UINT64_EQUAL(mem_win_size, sPAPRPHBState),
VMSTATE_UINT64_EQUAL(io_win_addr, sPAPRPHBState),
@@ -1779,6 +1796,15 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb,
uint32_t interrupt_map_mask[] = {
cpu_to_be32(b_ddddd(-1)|b_fff(0)), 0x0, 0x0, cpu_to_be32(-1)};
uint32_t interrupt_map[PCI_SLOT_MAX * PCI_NUM_PINS][7];
+ uint32_t ddw_applicable[] = {
+ cpu_to_be32(RTAS_IBM_QUERY_PE_DMA_WINDOW),
+ cpu_to_be32(RTAS_IBM_CREATE_PE_DMA_WINDOW),
+ cpu_to_be32(RTAS_IBM_REMOVE_PE_DMA_WINDOW)
+ };
+ uint32_t ddw_extensions[] = {
+ cpu_to_be32(1),
+ cpu_to_be32(RTAS_IBM_RESET_PE_DMA_WINDOW)
+ };
sPAPRTCETable *tcet;
PCIBus *bus = PCI_HOST_BRIDGE(phb)->bus;
sPAPRFDT s_fdt;
@@ -1801,7 +1827,15 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb,
_FDT(fdt_setprop(fdt, bus_off, "ranges", &ranges, sizeof_ranges));
_FDT(fdt_setprop(fdt, bus_off, "reg", &bus_reg, sizeof(bus_reg)));
_FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pci-config-space-type", 0x1));
- _FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pe-total-#msi", XICS_IRQS));
+ _FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pe-total-#msi", XICS_IRQS_SPAPR));
+
+ /* Dynamic DMA window */
+ if (phb->ddw_enabled) {
+ _FDT(fdt_setprop(fdt, bus_off, "ibm,ddw-applicable", &ddw_applicable,
+ sizeof(ddw_applicable)));
+ _FDT(fdt_setprop(fdt, bus_off, "ibm,ddw-extensions",
+ &ddw_extensions, sizeof(ddw_extensions)));
+ }
/* Build the interrupt-map, this must matches what is done
* in pci_spapr_map_irq
@@ -1826,7 +1860,7 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb,
_FDT(fdt_setprop(fdt, bus_off, "interrupt-map", &interrupt_map,
sizeof(interrupt_map)));
- tcet = spapr_tce_find_by_liobn(phb->dma_liobn);
+ tcet = spapr_tce_find_by_liobn(phb->dma_liobn[0]);
if (!tcet) {
return -1;
}
diff --git a/hw/ppc/spapr_rtas_ddw.c b/hw/ppc/spapr_rtas_ddw.c
new file mode 100644
index 0000000000..177dcffc9b
--- /dev/null
+++ b/hw/ppc/spapr_rtas_ddw.c
@@ -0,0 +1,295 @@
+/*
+ * QEMU sPAPR Dynamic DMA windows support
+ *
+ * Copyright (c) 2015 Alexey Kardashevskiy, IBM Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "qemu/error-report.h"
+#include "hw/ppc/spapr.h"
+#include "hw/pci-host/spapr.h"
+#include "trace.h"
+
+static int spapr_phb_get_active_win_num_cb(Object *child, void *opaque)
+{
+ sPAPRTCETable *tcet;
+
+ tcet = (sPAPRTCETable *) object_dynamic_cast(child, TYPE_SPAPR_TCE_TABLE);
+ if (tcet && tcet->nb_table) {
+ ++*(unsigned *)opaque;
+ }
+ return 0;
+}
+
+static unsigned spapr_phb_get_active_win_num(sPAPRPHBState *sphb)
+{
+ unsigned ret = 0;
+
+ object_child_foreach(OBJECT(sphb), spapr_phb_get_active_win_num_cb, &ret);
+
+ return ret;
+}
+
+static int spapr_phb_get_free_liobn_cb(Object *child, void *opaque)
+{
+ sPAPRTCETable *tcet;
+
+ tcet = (sPAPRTCETable *) object_dynamic_cast(child, TYPE_SPAPR_TCE_TABLE);
+ if (tcet && !tcet->nb_table) {
+ *(uint32_t *)opaque = tcet->liobn;
+ return 1;
+ }
+ return 0;
+}
+
+static unsigned spapr_phb_get_free_liobn(sPAPRPHBState *sphb)
+{
+ uint32_t liobn = 0;
+
+ object_child_foreach(OBJECT(sphb), spapr_phb_get_free_liobn_cb, &liobn);
+
+ return liobn;
+}
+
+static uint32_t spapr_page_mask_to_query_mask(uint64_t page_mask)
+{
+ int i;
+ uint32_t mask = 0;
+ const struct { int shift; uint32_t mask; } masks[] = {
+ { 12, RTAS_DDW_PGSIZE_4K },
+ { 16, RTAS_DDW_PGSIZE_64K },
+ { 24, RTAS_DDW_PGSIZE_16M },
+ { 25, RTAS_DDW_PGSIZE_32M },
+ { 26, RTAS_DDW_PGSIZE_64M },
+ { 27, RTAS_DDW_PGSIZE_128M },
+ { 28, RTAS_DDW_PGSIZE_256M },
+ { 34, RTAS_DDW_PGSIZE_16G },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(masks); ++i) {
+ if (page_mask & (1ULL << masks[i].shift)) {
+ mask |= masks[i].mask;
+ }
+ }
+
+ return mask;
+}
+
+static void rtas_ibm_query_pe_dma_window(PowerPCCPU *cpu,
+ sPAPRMachineState *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args,
+ uint32_t nret, target_ulong rets)
+{
+ sPAPRPHBState *sphb;
+ uint64_t buid, max_window_size;
+ uint32_t avail, addr, pgmask = 0;
+ MachineState *machine = MACHINE(spapr);
+
+ if ((nargs != 3) || (nret != 5)) {
+ goto param_error_exit;
+ }
+
+ buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+ addr = rtas_ld(args, 0);
+ sphb = spapr_pci_find_phb(spapr, buid);
+ if (!sphb || !sphb->ddw_enabled) {
+ goto param_error_exit;
+ }
+
+ /* Translate page mask to LoPAPR format */
+ pgmask = spapr_page_mask_to_query_mask(sphb->page_size_mask);
+
+ /*
+ * This is "Largest contiguous block of TCEs allocated specifically
+ * for (that is, are reserved for) this PE".
+ * Return the maximum number as maximum supported RAM size was in 4K pages.
+ */
+ if (machine->ram_size == machine->maxram_size) {
+ max_window_size = machine->ram_size;
+ } else {
+ MemoryHotplugState *hpms = &spapr->hotplug_memory;
+
+ max_window_size = hpms->base + memory_region_size(&hpms->mr);
+ }
+
+ avail = SPAPR_PCI_DMA_MAX_WINDOWS - spapr_phb_get_active_win_num(sphb);
+
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+ rtas_st(rets, 1, avail);
+ rtas_st(rets, 2, max_window_size >> SPAPR_TCE_PAGE_SHIFT);
+ rtas_st(rets, 3, pgmask);
+ rtas_st(rets, 4, 0); /* DMA migration mask, not supported */
+
+ trace_spapr_iommu_ddw_query(buid, addr, avail, max_window_size, pgmask);
+ return;
+
+param_error_exit:
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+}
+
+static void rtas_ibm_create_pe_dma_window(PowerPCCPU *cpu,
+ sPAPRMachineState *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args,
+ uint32_t nret, target_ulong rets)
+{
+ sPAPRPHBState *sphb;
+ sPAPRTCETable *tcet = NULL;
+ uint32_t addr, page_shift, window_shift, liobn;
+ uint64_t buid, win_addr;
+ int windows;
+
+ if ((nargs != 5) || (nret != 4)) {
+ goto param_error_exit;
+ }
+
+ buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+ addr = rtas_ld(args, 0);
+ sphb = spapr_pci_find_phb(spapr, buid);
+ if (!sphb || !sphb->ddw_enabled) {
+ goto param_error_exit;
+ }
+
+ page_shift = rtas_ld(args, 3);
+ window_shift = rtas_ld(args, 4);
+ liobn = spapr_phb_get_free_liobn(sphb);
+ windows = spapr_phb_get_active_win_num(sphb);
+
+ if (!(sphb->page_size_mask & (1ULL << page_shift)) ||
+ (window_shift < page_shift)) {
+ goto param_error_exit;
+ }
+
+ if (!liobn || !sphb->ddw_enabled || windows == SPAPR_PCI_DMA_MAX_WINDOWS) {
+ goto hw_error_exit;
+ }
+
+ tcet = spapr_tce_find_by_liobn(liobn);
+ if (!tcet) {
+ goto hw_error_exit;
+ }
+
+ win_addr = (windows == 0) ? sphb->dma_win_addr : sphb->dma64_win_addr;
+ spapr_tce_table_enable(tcet, page_shift, win_addr,
+ 1ULL << (window_shift - page_shift));
+ if (!tcet->nb_table) {
+ goto hw_error_exit;
+ }
+
+ trace_spapr_iommu_ddw_create(buid, addr, 1ULL << page_shift,
+ 1ULL << window_shift, tcet->bus_offset, liobn);
+
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+ rtas_st(rets, 1, liobn);
+ rtas_st(rets, 2, tcet->bus_offset >> 32);
+ rtas_st(rets, 3, tcet->bus_offset & ((uint32_t) -1));
+
+ return;
+
+hw_error_exit:
+ rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
+ return;
+
+param_error_exit:
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+}
+
+static void rtas_ibm_remove_pe_dma_window(PowerPCCPU *cpu,
+ sPAPRMachineState *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args,
+ uint32_t nret, target_ulong rets)
+{
+ sPAPRPHBState *sphb;
+ sPAPRTCETable *tcet;
+ uint32_t liobn;
+
+ if ((nargs != 1) || (nret != 1)) {
+ goto param_error_exit;
+ }
+
+ liobn = rtas_ld(args, 0);
+ tcet = spapr_tce_find_by_liobn(liobn);
+ if (!tcet) {
+ goto param_error_exit;
+ }
+
+ sphb = SPAPR_PCI_HOST_BRIDGE(OBJECT(tcet)->parent);
+ if (!sphb || !sphb->ddw_enabled || !tcet->nb_table) {
+ goto param_error_exit;
+ }
+
+ spapr_tce_table_disable(tcet);
+ trace_spapr_iommu_ddw_remove(liobn);
+
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+ return;
+
+param_error_exit:
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+}
+
+static void rtas_ibm_reset_pe_dma_window(PowerPCCPU *cpu,
+ sPAPRMachineState *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args,
+ uint32_t nret, target_ulong rets)
+{
+ sPAPRPHBState *sphb;
+ uint64_t buid;
+ uint32_t addr;
+
+ if ((nargs != 3) || (nret != 1)) {
+ goto param_error_exit;
+ }
+
+ buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+ addr = rtas_ld(args, 0);
+ sphb = spapr_pci_find_phb(spapr, buid);
+ if (!sphb || !sphb->ddw_enabled) {
+ goto param_error_exit;
+ }
+
+ spapr_phb_dma_reset(sphb);
+ trace_spapr_iommu_ddw_reset(buid, addr);
+
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+
+ return;
+
+param_error_exit:
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+}
+
+static void spapr_rtas_ddw_init(void)
+{
+ spapr_rtas_register(RTAS_IBM_QUERY_PE_DMA_WINDOW,
+ "ibm,query-pe-dma-window",
+ rtas_ibm_query_pe_dma_window);
+ spapr_rtas_register(RTAS_IBM_CREATE_PE_DMA_WINDOW,
+ "ibm,create-pe-dma-window",
+ rtas_ibm_create_pe_dma_window);
+ spapr_rtas_register(RTAS_IBM_REMOVE_PE_DMA_WINDOW,
+ "ibm,remove-pe-dma-window",
+ rtas_ibm_remove_pe_dma_window);
+ spapr_rtas_register(RTAS_IBM_RESET_PE_DMA_WINDOW,
+ "ibm,reset-pe-dma-window",
+ rtas_ibm_reset_pe_dma_window);
+}
+
+type_init(spapr_rtas_ddw_init)
diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c
index ae40db8fd2..f93244d7c1 100644
--- a/hw/ppc/spapr_vio.c
+++ b/hw/ppc/spapr_vio.c
@@ -463,7 +463,7 @@ static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp)
dev->qdev.id = id;
}
- dev->irq = xics_alloc(spapr->icp, 0, dev->irq, false, &local_err);
+ dev->irq = xics_spapr_alloc(spapr->xics, 0, dev->irq, false, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events
index 6da713547f..900679bc9d 100644
--- a/hw/ppc/trace-events
+++ b/hw/ppc/trace-events
@@ -30,6 +30,10 @@ spapr_iommu_xlate(uint64_t liobn, uint64_t ioba, uint64_t tce, unsigned perm, un
spapr_iommu_new_table(uint64_t liobn, void *table, int fd) "liobn=%"PRIx64" table=%p fd=%d"
spapr_iommu_pre_save(uint64_t liobn, uint32_t nb, uint64_t offs, uint32_t ps) "liobn=%"PRIx64" %"PRIx32" bus_offset=%"PRIx64" ps=%"PRIu32
spapr_iommu_post_load(uint64_t liobn, uint32_t pre_nb, uint32_t post_nb, uint64_t offs, uint32_t ps) "liobn=%"PRIx64" %"PRIx32" => %"PRIx32" bus_offset=%"PRIx64" ps=%"PRIu32
+spapr_iommu_ddw_query(uint64_t buid, uint32_t cfgaddr, unsigned wa, uint64_t win_size, uint32_t pgmask) "buid=%"PRIx64" addr=%"PRIx32", %u windows available, max window size=%"PRIx64", mask=%"PRIx32
+spapr_iommu_ddw_create(uint64_t buid, uint32_t cfgaddr, uint64_t pg_size, uint64_t req_size, uint64_t start, uint32_t liobn) "buid=%"PRIx64" addr=%"PRIx32", page size=0x%"PRIx64", requested=0x%"PRIx64", start addr=%"PRIx64", liobn=%"PRIx32
+spapr_iommu_ddw_remove(uint32_t liobn) "liobn=%"PRIx32
+spapr_iommu_ddw_reset(uint64_t buid, uint32_t cfgaddr) "buid=%"PRIx64" addr=%"PRIx32
# hw/ppc/ppc.c
ppc_tb_adjust(uint64_t offs1, uint64_t offs2, int64_t diff, int64_t seconds) "adjusted from 0x%"PRIx64" to 0x%"PRIx64", diff %"PRId64" (%"PRId64"s)"
diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
index 1625e6b38b..8b709e362e 100644
--- a/hw/s390x/virtio-ccw.c
+++ b/hw/s390x/virtio-ccw.c
@@ -69,92 +69,58 @@ VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch)
return vdev;
}
-static int virtio_ccw_set_guest2host_notifier(VirtioCcwDevice *dev, int n,
- bool assign, bool set_handler)
+static void virtio_ccw_start_ioeventfd(VirtioCcwDevice *dev)
{
- VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
- VirtQueue *vq = virtio_get_queue(vdev, n);
- EventNotifier *notifier = virtio_queue_get_host_notifier(vq);
- int r = 0;
- SubchDev *sch = dev->sch;
- uint32_t sch_id = (css_build_subchannel_id(sch) << 16) | sch->schid;
+ virtio_bus_start_ioeventfd(&dev->bus);
+}
- if (assign) {
- r = event_notifier_init(notifier, 1);
- if (r < 0) {
- error_report("%s: unable to init event notifier: %d", __func__, r);
- return r;
- }
- virtio_queue_set_host_notifier_fd_handler(vq, true, set_handler);
- r = s390_assign_subch_ioeventfd(notifier, sch_id, n, assign);
- if (r < 0) {
- error_report("%s: unable to assign ioeventfd: %d", __func__, r);
- virtio_queue_set_host_notifier_fd_handler(vq, false, false);
- event_notifier_cleanup(notifier);
- return r;
- }
- } else {
- virtio_queue_set_host_notifier_fd_handler(vq, false, false);
- s390_assign_subch_ioeventfd(notifier, sch_id, n, assign);
- event_notifier_cleanup(notifier);
- }
- return r;
+static void virtio_ccw_stop_ioeventfd(VirtioCcwDevice *dev)
+{
+ virtio_bus_stop_ioeventfd(&dev->bus);
}
-static void virtio_ccw_start_ioeventfd(VirtioCcwDevice *dev)
+static bool virtio_ccw_ioeventfd_started(DeviceState *d)
{
- VirtIODevice *vdev;
- int n, r;
+ VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
- if (!(dev->flags & VIRTIO_CCW_FLAG_USE_IOEVENTFD) ||
- dev->ioeventfd_disabled ||
- dev->ioeventfd_started) {
- return;
- }
- vdev = virtio_bus_get_device(&dev->bus);
- for (n = 0; n < VIRTIO_CCW_QUEUE_MAX; n++) {
- if (!virtio_queue_get_num(vdev, n)) {
- continue;
- }
- r = virtio_ccw_set_guest2host_notifier(dev, n, true, true);
- if (r < 0) {
- goto assign_error;
- }
- }
- dev->ioeventfd_started = true;
- return;
+ return dev->ioeventfd_started;
+}
- assign_error:
- while (--n >= 0) {
- if (!virtio_queue_get_num(vdev, n)) {
- continue;
- }
- r = virtio_ccw_set_guest2host_notifier(dev, n, false, false);
- assert(r >= 0);
+static void virtio_ccw_ioeventfd_set_started(DeviceState *d, bool started,
+ bool err)
+{
+ VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
+
+ dev->ioeventfd_started = started;
+ if (err) {
+ /* Disable ioeventfd for this device. */
+ dev->flags &= ~VIRTIO_CCW_FLAG_USE_IOEVENTFD;
}
- dev->ioeventfd_started = false;
- /* Disable ioeventfd for this device. */
- dev->flags &= ~VIRTIO_CCW_FLAG_USE_IOEVENTFD;
- error_report("%s: failed. Fallback to userspace (slower).", __func__);
}
-static void virtio_ccw_stop_ioeventfd(VirtioCcwDevice *dev)
+static bool virtio_ccw_ioeventfd_disabled(DeviceState *d)
{
- VirtIODevice *vdev;
- int n, r;
+ VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
- if (!dev->ioeventfd_started) {
- return;
- }
- vdev = virtio_bus_get_device(&dev->bus);
- for (n = 0; n < VIRTIO_CCW_QUEUE_MAX; n++) {
- if (!virtio_queue_get_num(vdev, n)) {
- continue;
- }
- r = virtio_ccw_set_guest2host_notifier(dev, n, false, false);
- assert(r >= 0);
- }
- dev->ioeventfd_started = false;
+ return dev->ioeventfd_disabled ||
+ !(dev->flags & VIRTIO_CCW_FLAG_USE_IOEVENTFD);
+}
+
+static void virtio_ccw_ioeventfd_set_disabled(DeviceState *d, bool disabled)
+{
+ VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
+
+ dev->ioeventfd_disabled = disabled;
+}
+
+static int virtio_ccw_ioeventfd_assign(DeviceState *d, EventNotifier *notifier,
+ int n, bool assign)
+{
+ VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
+ SubchDev *sch = dev->sch;
+ uint32_t sch_id = (css_build_subchannel_id(sch) << 16) | sch->schid;
+
+ return s390_assign_subch_ioeventfd(notifier, sch_id, n, assign);
}
VirtualCssBus *virtual_css_bus_init(void)
@@ -1157,19 +1123,6 @@ static bool virtio_ccw_query_guest_notifiers(DeviceState *d)
return !!(dev->sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ENA);
}
-static int virtio_ccw_set_host_notifier(DeviceState *d, int n, bool assign)
-{
- VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
-
- /* Stop using the generic ioeventfd, we are doing eventfd handling
- * ourselves below */
- dev->ioeventfd_disabled = assign;
- if (assign) {
- virtio_ccw_stop_ioeventfd(dev);
- }
- return virtio_ccw_set_guest2host_notifier(dev, n, assign, false);
-}
-
static int virtio_ccw_get_mappings(VirtioCcwDevice *dev)
{
int r;
@@ -1798,7 +1751,6 @@ static void virtio_ccw_bus_class_init(ObjectClass *klass, void *data)
k->notify = virtio_ccw_notify;
k->vmstate_change = virtio_ccw_vmstate_change;
k->query_guest_notifiers = virtio_ccw_query_guest_notifiers;
- k->set_host_notifier = virtio_ccw_set_host_notifier;
k->set_guest_notifiers = virtio_ccw_set_guest_notifiers;
k->save_queue = virtio_ccw_save_queue;
k->load_queue = virtio_ccw_load_queue;
@@ -1807,6 +1759,11 @@ static void virtio_ccw_bus_class_init(ObjectClass *klass, void *data)
k->device_plugged = virtio_ccw_device_plugged;
k->post_plugged = virtio_ccw_post_plugged;
k->device_unplugged = virtio_ccw_device_unplugged;
+ k->ioeventfd_started = virtio_ccw_ioeventfd_started;
+ k->ioeventfd_set_started = virtio_ccw_ioeventfd_set_started;
+ k->ioeventfd_disabled = virtio_ccw_ioeventfd_disabled;
+ k->ioeventfd_set_disabled = virtio_ccw_ioeventfd_set_disabled;
+ k->ioeventfd_assign = virtio_ccw_ioeventfd_assign;
}
static const TypeInfo virtio_ccw_bus_info = {
diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c
index baa0a2cfdf..1f2f2d33dd 100644
--- a/hw/scsi/esp.c
+++ b/hw/scsi/esp.c
@@ -574,7 +574,7 @@ static bool esp_mem_accepts(void *opaque, hwaddr addr,
const VMStateDescription vmstate_esp = {
.name ="esp",
- .version_id = 3,
+ .version_id = 4,
.minimum_version_id = 3,
.fields = (VMStateField[]) {
VMSTATE_BUFFER(rregs, ESPState),
@@ -585,7 +585,8 @@ const VMStateDescription vmstate_esp = {
VMSTATE_BUFFER(ti_buf, ESPState),
VMSTATE_UINT32(status, ESPState),
VMSTATE_UINT32(dma, ESPState),
- VMSTATE_BUFFER(cmdbuf, ESPState),
+ VMSTATE_PARTIAL_BUFFER(cmdbuf, ESPState, 16),
+ VMSTATE_BUFFER_START_MIDDLE_V(cmdbuf, ESPState, 16, 4),
VMSTATE_UINT32(cmdlen, ESPState),
VMSTATE_UINT32(do_cmd, ESPState),
VMSTATE_UINT32(dma_left, ESPState),
diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c
index 1a49f1e4b7..18ced31493 100644
--- a/hw/scsi/virtio-scsi-dataplane.c
+++ b/hw/scsi/virtio-scsi-dataplane.c
@@ -31,7 +31,7 @@ void virtio_scsi_set_iothread(VirtIOSCSI *s, IOThread *iothread)
s->ctx = iothread_get_aio_context(vs->conf.iothread);
/* Don't try if transport does not support notifiers. */
- if (!k->set_guest_notifiers || !k->set_host_notifier) {
+ if (!k->set_guest_notifiers || !k->ioeventfd_started) {
fprintf(stderr, "virtio-scsi: Failed to set iothread "
"(transport does not support notifiers)");
exit(1);
@@ -69,11 +69,10 @@ static int virtio_scsi_vring_init(VirtIOSCSI *s, VirtQueue *vq, int n,
void (*fn)(VirtIODevice *vdev, VirtQueue *vq))
{
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s)));
- VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
int rc;
/* Set up virtqueue notify */
- rc = k->set_host_notifier(qbus->parent, n, true);
+ rc = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), n, true);
if (rc != 0) {
fprintf(stderr, "virtio-scsi: Failed to set host notifier (%d)\n",
rc);
@@ -159,7 +158,7 @@ fail_vrings:
virtio_scsi_clear_aio(s);
aio_context_release(s->ctx);
for (i = 0; i < vs->conf.num_queues + 2; i++) {
- k->set_host_notifier(qbus->parent, i, false);
+ virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false);
}
k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false);
fail_guest_notifiers:
@@ -198,7 +197,7 @@ void virtio_scsi_dataplane_stop(VirtIOSCSI *s)
aio_context_release(s->ctx);
for (i = 0; i < vs->conf.num_queues + 2; i++) {
- k->set_host_notifier(qbus->parent, i, false);
+ virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false);
}
/* Clean up guest notifier (irq) */
diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c
index 71d09d3ef3..e8179d6616 100644
--- a/hw/scsi/virtio-scsi.c
+++ b/hw/scsi/virtio-scsi.c
@@ -666,11 +666,6 @@ static void virtio_scsi_reset(VirtIODevice *vdev)
static void virtio_scsi_save(QEMUFile *f, void *opaque)
{
VirtIODevice *vdev = VIRTIO_DEVICE(opaque);
- VirtIOSCSI *s = VIRTIO_SCSI(vdev);
-
- if (s->dataplane_started) {
- virtio_scsi_dataplane_stop(s);
- }
virtio_save(vdev, f);
}
diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c
index 075e4ed5df..3ff0886dd5 100644
--- a/hw/sd/ssi-sd.c
+++ b/hw/sd/ssi-sd.c
@@ -15,6 +15,7 @@
#include "sysemu/blockdev.h"
#include "hw/ssi/ssi.h"
#include "hw/sd/sd.h"
+#include "qapi/error.h"
//#define DEBUG_SSI_SD 1
@@ -249,7 +250,7 @@ static int ssi_sd_load(QEMUFile *f, void *opaque, int version_id)
return 0;
}
-static int ssi_sd_init(SSISlave *d)
+static void ssi_sd_realize(SSISlave *d, Error **errp)
{
DeviceState *dev = DEVICE(d);
ssi_sd_state *s = FROM_SSI_SLAVE(ssi_sd_state, d);
@@ -260,17 +261,17 @@ static int ssi_sd_init(SSISlave *d)
dinfo = drive_get_next(IF_SD);
s->sd = sd_init(dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, true);
if (s->sd == NULL) {
- return -1;
+ error_setg(errp, "Device initialization failed.");
+ return;
}
register_savevm(dev, "ssi_sd", -1, 1, ssi_sd_save, ssi_sd_load, s);
- return 0;
}
static void ssi_sd_class_init(ObjectClass *klass, void *data)
{
SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
- k->init = ssi_sd_init;
+ k->realize = ssi_sd_realize;
k->transfer = ssi_sd_transfer;
k->cs_polarity = SSI_CS_LOW;
}
diff --git a/hw/sh4/sh_pci.c b/hw/sh4/sh_pci.c
index e820a32307..1747628f3d 100644
--- a/hw/sh4/sh_pci.c
+++ b/hw/sh4/sh_pci.c
@@ -55,7 +55,7 @@ static void sh_pci_reg_write (void *p, hwaddr addr, uint64_t val,
switch(addr) {
case 0 ... 0xfc:
- cpu_to_le32w((uint32_t*)(pcic->dev->config + addr), val);
+ stl_le_p(pcic->dev->config + addr, val);
break;
case 0x1c0:
pcic->par = val;
@@ -85,7 +85,7 @@ static uint64_t sh_pci_reg_read (void *p, hwaddr addr,
switch(addr) {
case 0 ... 0xfc:
- return le32_to_cpup((uint32_t*)(pcic->dev->config + addr));
+ return ldl_le_p(pcic->dev->config + addr);
case 0x1c0:
return pcic->par;
case 0x1c4:
diff --git a/hw/smbios/Makefile.objs b/hw/smbios/Makefile.objs
index f69a92f967..c3d3753602 100644
--- a/hw/smbios/Makefile.objs
+++ b/hw/smbios/Makefile.objs
@@ -1 +1,2 @@
common-obj-$(CONFIG_SMBIOS) += smbios.o
+common-obj-$(call land,$(CONFIG_SMBIOS),$(CONFIG_IPMI)) += smbios_type_38.o
diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c
index cb8a111102..74c7102929 100644
--- a/hw/smbios/smbios.c
+++ b/hw/smbios/smbios.c
@@ -24,6 +24,8 @@
#include "hw/smbios/smbios.h"
#include "hw/loader.h"
#include "exec/cpu-common.h"
+#include "smbios_build.h"
+#include "hw/smbios/ipmi.h"
/* legacy structures and constants for <= 2.0 machines */
struct smbios_header {
@@ -53,10 +55,10 @@ static bool smbios_uuid_encoded = true;
/* end: legacy structures & constants for <= 2.0 machines */
-static uint8_t *smbios_tables;
-static size_t smbios_tables_len;
-static unsigned smbios_table_max;
-static unsigned smbios_table_cnt;
+uint8_t *smbios_tables;
+size_t smbios_tables_len;
+unsigned smbios_table_max;
+unsigned smbios_table_cnt;
static SmbiosEntryPointType smbios_ep_type = SMBIOS_ENTRY_POINT_21;
static SmbiosEntryPoint ep;
@@ -429,7 +431,7 @@ uint8_t *smbios_get_table_legacy(size_t *length)
/* end: legacy setup functions for <= 2.0 machines */
-static bool smbios_skip_table(uint8_t type, bool required_table)
+bool smbios_skip_table(uint8_t type, bool required_table)
{
if (test_bit(type, have_binfile_bitmap)) {
return true; /* user provided their own binary blob(s) */
@@ -443,65 +445,6 @@ static bool smbios_skip_table(uint8_t type, bool required_table)
return true;
}
-#define SMBIOS_BUILD_TABLE_PRE(tbl_type, tbl_handle, tbl_required) \
- struct smbios_type_##tbl_type *t; \
- size_t t_off; /* table offset into smbios_tables */ \
- int str_index = 0; \
- do { \
- /* should we skip building this table ? */ \
- if (smbios_skip_table(tbl_type, tbl_required)) { \
- return; \
- } \
- \
- /* use offset of table t within smbios_tables */ \
- /* (pointer must be updated after each realloc) */ \
- t_off = smbios_tables_len; \
- smbios_tables_len += sizeof(*t); \
- smbios_tables = g_realloc(smbios_tables, smbios_tables_len); \
- t = (struct smbios_type_##tbl_type *)(smbios_tables + t_off); \
- \
- t->header.type = tbl_type; \
- t->header.length = sizeof(*t); \
- t->header.handle = cpu_to_le16(tbl_handle); \
- } while (0)
-
-#define SMBIOS_TABLE_SET_STR(tbl_type, field, value) \
- do { \
- int len = (value != NULL) ? strlen(value) + 1 : 0; \
- if (len > 1) { \
- smbios_tables = g_realloc(smbios_tables, \
- smbios_tables_len + len); \
- memcpy(smbios_tables + smbios_tables_len, value, len); \
- smbios_tables_len += len; \
- /* update pointer post-realloc */ \
- t = (struct smbios_type_##tbl_type *)(smbios_tables + t_off); \
- t->field = ++str_index; \
- } else { \
- t->field = 0; \
- } \
- } while (0)
-
-#define SMBIOS_BUILD_TABLE_POST \
- do { \
- size_t term_cnt, t_size; \
- \
- /* add '\0' terminator (add two if no strings defined) */ \
- term_cnt = (str_index == 0) ? 2 : 1; \
- smbios_tables = g_realloc(smbios_tables, \
- smbios_tables_len + term_cnt); \
- memset(smbios_tables + smbios_tables_len, 0, term_cnt); \
- smbios_tables_len += term_cnt; \
- \
- /* update smbios max. element size */ \
- t_size = smbios_tables_len - t_off; \
- if (t_size > smbios_table_max) { \
- smbios_table_max = t_size; \
- } \
- \
- /* update smbios element count */ \
- smbios_table_cnt++; \
- } while (0)
-
static void smbios_build_type_0_table(void)
{
SMBIOS_BUILD_TABLE_PRE(0, 0x000, false); /* optional, leave up to BIOS */
@@ -906,6 +849,7 @@ void smbios_get_tables(const struct smbios_phys_mem_area *mem_array,
}
smbios_build_type_32_table();
+ smbios_build_type_38_table();
smbios_build_type_127_table();
smbios_validate_table();
diff --git a/hw/smbios/smbios_build.h b/hw/smbios/smbios_build.h
new file mode 100644
index 0000000000..68b8b72e09
--- /dev/null
+++ b/hw/smbios/smbios_build.h
@@ -0,0 +1,87 @@
+/*
+ * SMBIOS Support
+ *
+ * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * Authors:
+ * Alex Williamson <alex.williamson@hp.com>
+ * Markus Armbruster <armbru@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#ifndef QEMU_SMBIOS_BUILD_H
+#define QEMU_SMBIOS_BUILD_H
+
+bool smbios_skip_table(uint8_t type, bool required_table);
+
+extern uint8_t *smbios_tables;
+extern size_t smbios_tables_len;
+extern unsigned smbios_table_max;
+extern unsigned smbios_table_cnt;
+
+#define SMBIOS_BUILD_TABLE_PRE(tbl_type, tbl_handle, tbl_required) \
+ struct smbios_type_##tbl_type *t; \
+ size_t t_off; /* table offset into smbios_tables */ \
+ int str_index = 0; \
+ do { \
+ /* should we skip building this table ? */ \
+ if (smbios_skip_table(tbl_type, tbl_required)) { \
+ return; \
+ } \
+ \
+ /* use offset of table t within smbios_tables */ \
+ /* (pointer must be updated after each realloc) */ \
+ t_off = smbios_tables_len; \
+ smbios_tables_len += sizeof(*t); \
+ smbios_tables = g_realloc(smbios_tables, smbios_tables_len); \
+ t = (struct smbios_type_##tbl_type *)(smbios_tables + t_off); \
+ \
+ t->header.type = tbl_type; \
+ t->header.length = sizeof(*t); \
+ t->header.handle = cpu_to_le16(tbl_handle); \
+ } while (0)
+
+#define SMBIOS_TABLE_SET_STR(tbl_type, field, value) \
+ do { \
+ int len = (value != NULL) ? strlen(value) + 1 : 0; \
+ if (len > 1) { \
+ smbios_tables = g_realloc(smbios_tables, \
+ smbios_tables_len + len); \
+ memcpy(smbios_tables + smbios_tables_len, value, len); \
+ smbios_tables_len += len; \
+ /* update pointer post-realloc */ \
+ t = (struct smbios_type_##tbl_type *)(smbios_tables + t_off); \
+ t->field = ++str_index; \
+ } else { \
+ t->field = 0; \
+ } \
+ } while (0)
+
+#define SMBIOS_BUILD_TABLE_POST \
+ do { \
+ size_t term_cnt, t_size; \
+ \
+ /* add '\0' terminator (add two if no strings defined) */ \
+ term_cnt = (str_index == 0) ? 2 : 1; \
+ smbios_tables = g_realloc(smbios_tables, \
+ smbios_tables_len + term_cnt); \
+ memset(smbios_tables + smbios_tables_len, 0, term_cnt); \
+ smbios_tables_len += term_cnt; \
+ \
+ /* update smbios max. element size */ \
+ t_size = smbios_tables_len - t_off; \
+ if (t_size > smbios_table_max) { \
+ smbios_table_max = t_size; \
+ } \
+ \
+ /* update smbios element count */ \
+ smbios_table_cnt++; \
+ } while (0)
+
+#endif /* QEMU_SMBIOS_BUILD_H */
diff --git a/hw/smbios/smbios_type_38.c b/hw/smbios/smbios_type_38.c
new file mode 100644
index 0000000000..56e8609c00
--- /dev/null
+++ b/hw/smbios/smbios_type_38.c
@@ -0,0 +1,117 @@
+/*
+ * IPMI SMBIOS firmware handling
+ *
+ * Copyright (c) 2015,2016 Corey Minyard, MontaVista Software, LLC
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/ipmi/ipmi.h"
+#include "hw/smbios/ipmi.h"
+#include "hw/smbios/smbios.h"
+#include "qemu/error-report.h"
+#include "smbios_build.h"
+
+/* SMBIOS type 38 - IPMI */
+struct smbios_type_38 {
+ struct smbios_structure_header header;
+ uint8_t interface_type;
+ uint8_t ipmi_spec_revision;
+ uint8_t i2c_slave_address;
+ uint8_t nv_storage_device_address;
+ uint64_t base_address;
+ uint8_t base_address_modifier;
+ uint8_t interrupt_number;
+} QEMU_PACKED;
+
+static void smbios_build_one_type_38(IPMIFwInfo *info)
+{
+ uint64_t baseaddr = info->base_address;
+ SMBIOS_BUILD_TABLE_PRE(38, 0x3000, true);
+
+ t->interface_type = info->interface_type;
+ t->ipmi_spec_revision = ((info->ipmi_spec_major_revision << 4)
+ | info->ipmi_spec_minor_revision);
+ t->i2c_slave_address = info->i2c_slave_address;
+ t->nv_storage_device_address = 0;
+
+ assert(info->ipmi_spec_minor_revision <= 15);
+ assert(info->ipmi_spec_major_revision <= 15);
+
+ /* or 1 to set it to I/O space */
+ switch (info->memspace) {
+ case IPMI_MEMSPACE_IO:
+ baseaddr |= 1;
+ break;
+ case IPMI_MEMSPACE_MEM32:
+ case IPMI_MEMSPACE_MEM64:
+ break;
+ case IPMI_MEMSPACE_SMBUS:
+ baseaddr <<= 1;
+ break;
+ }
+
+ t->base_address = cpu_to_le64(baseaddr);
+
+ t->base_address_modifier = 0;
+ if (info->irq_type == IPMI_LEVEL_IRQ) {
+ t->base_address_modifier |= 1;
+ }
+ switch (info->register_spacing) {
+ case 1:
+ break;
+ case 4:
+ t->base_address_modifier |= 1 << 6;
+ break;
+ case 16:
+ t->base_address_modifier |= 2 << 6;
+ break;
+ default:
+ error_report("IPMI register spacing %d is not compatible with"
+ " SMBIOS, ignoring this entry.", info->register_spacing);
+ return;
+ }
+ t->interrupt_number = info->interrupt_number;
+
+ SMBIOS_BUILD_TABLE_POST;
+}
+
+static void smbios_add_ipmi_devices(BusState *bus)
+{
+ BusChild *kid;
+
+ QTAILQ_FOREACH(kid, &bus->children, sibling) {
+ DeviceState *dev = kid->child;
+ Object *obj = object_dynamic_cast(OBJECT(dev), TYPE_IPMI_INTERFACE);
+ BusState *childbus;
+
+ if (obj) {
+ IPMIInterface *ii;
+ IPMIInterfaceClass *iic;
+ IPMIFwInfo info;
+
+ ii = IPMI_INTERFACE(obj);
+ iic = IPMI_INTERFACE_GET_CLASS(obj);
+ memset(&info, 0, sizeof(info));
+ iic->get_fwinfo(ii, &info);
+ smbios_build_one_type_38(&info);
+ continue;
+ }
+
+ QLIST_FOREACH(childbus, &dev->child_bus, sibling) {
+ smbios_add_ipmi_devices(childbus);
+ }
+ }
+}
+
+void smbios_build_type_38_table(void)
+{
+ BusState *bus;
+
+ bus = sysbus_get_default();
+ if (bus) {
+ smbios_add_ipmi_devices(bus);
+ }
+}
diff --git a/hw/ssi/Makefile.objs b/hw/ssi/Makefile.objs
index fcbb79ef01..c79a8dcd86 100644
--- a/hw/ssi/Makefile.objs
+++ b/hw/ssi/Makefile.objs
@@ -2,6 +2,7 @@ common-obj-$(CONFIG_PL022) += pl022.o
common-obj-$(CONFIG_SSI) += ssi.o
common-obj-$(CONFIG_XILINX_SPI) += xilinx_spi.o
common-obj-$(CONFIG_XILINX_SPIPS) += xilinx_spips.o
+common-obj-$(CONFIG_ASPEED_SOC) += aspeed_smc.o
obj-$(CONFIG_OMAP) += omap_spi.o
obj-$(CONFIG_IMX) += imx_spi.o
diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
new file mode 100644
index 0000000000..a371e302d4
--- /dev/null
+++ b/hw/ssi/aspeed_smc.c
@@ -0,0 +1,470 @@
+/*
+ * ASPEED AST2400 SMC Controller (SPI Flash Only)
+ *
+ * Copyright (C) 2016 IBM Corp.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/sysbus.h"
+#include "sysemu/sysemu.h"
+#include "qemu/log.h"
+#include "include/qemu/error-report.h"
+#include "exec/address-spaces.h"
+
+#include "hw/ssi/aspeed_smc.h"
+
+/* CE Type Setting Register */
+#define R_CONF (0x00 / 4)
+#define CONF_LEGACY_DISABLE (1 << 31)
+#define CONF_ENABLE_W4 20
+#define CONF_ENABLE_W3 19
+#define CONF_ENABLE_W2 18
+#define CONF_ENABLE_W1 17
+#define CONF_ENABLE_W0 16
+#define CONF_FLASH_TYPE4 9
+#define CONF_FLASH_TYPE3 7
+#define CONF_FLASH_TYPE2 5
+#define CONF_FLASH_TYPE1 3
+#define CONF_FLASH_TYPE0 1
+
+/* CE Control Register */
+#define R_CE_CTRL (0x04 / 4)
+#define CTRL_EXTENDED4 4 /* 32 bit addressing for SPI */
+#define CTRL_EXTENDED3 3 /* 32 bit addressing for SPI */
+#define CTRL_EXTENDED2 2 /* 32 bit addressing for SPI */
+#define CTRL_EXTENDED1 1 /* 32 bit addressing for SPI */
+#define CTRL_EXTENDED0 0 /* 32 bit addressing for SPI */
+
+/* Interrupt Control and Status Register */
+#define R_INTR_CTRL (0x08 / 4)
+#define INTR_CTRL_DMA_STATUS (1 << 11)
+#define INTR_CTRL_CMD_ABORT_STATUS (1 << 10)
+#define INTR_CTRL_WRITE_PROTECT_STATUS (1 << 9)
+#define INTR_CTRL_DMA_EN (1 << 3)
+#define INTR_CTRL_CMD_ABORT_EN (1 << 2)
+#define INTR_CTRL_WRITE_PROTECT_EN (1 << 1)
+
+/* CEx Control Register */
+#define R_CTRL0 (0x10 / 4)
+#define CTRL_CMD_SHIFT 16
+#define CTRL_CMD_MASK 0xff
+#define CTRL_CE_STOP_ACTIVE (1 << 2)
+#define CTRL_CMD_MODE_MASK 0x3
+#define CTRL_READMODE 0x0
+#define CTRL_FREADMODE 0x1
+#define CTRL_WRITEMODE 0x2
+#define CTRL_USERMODE 0x3
+#define R_CTRL1 (0x14 / 4)
+#define R_CTRL2 (0x18 / 4)
+#define R_CTRL3 (0x1C / 4)
+#define R_CTRL4 (0x20 / 4)
+
+/* CEx Segment Address Register */
+#define R_SEG_ADDR0 (0x30 / 4)
+#define SEG_SIZE_SHIFT 24 /* 8MB units */
+#define SEG_SIZE_MASK 0x7f
+#define SEG_START_SHIFT 16 /* address bit [A29-A23] */
+#define SEG_START_MASK 0x7f
+#define R_SEG_ADDR1 (0x34 / 4)
+#define R_SEG_ADDR2 (0x38 / 4)
+#define R_SEG_ADDR3 (0x3C / 4)
+#define R_SEG_ADDR4 (0x40 / 4)
+
+/* Misc Control Register #1 */
+#define R_MISC_CTRL1 (0x50 / 4)
+
+/* Misc Control Register #2 */
+#define R_MISC_CTRL2 (0x54 / 4)
+
+/* DMA Control/Status Register */
+#define R_DMA_CTRL (0x80 / 4)
+#define DMA_CTRL_DELAY_MASK 0xf
+#define DMA_CTRL_DELAY_SHIFT 8
+#define DMA_CTRL_FREQ_MASK 0xf
+#define DMA_CTRL_FREQ_SHIFT 4
+#define DMA_CTRL_MODE (1 << 3)
+#define DMA_CTRL_CKSUM (1 << 2)
+#define DMA_CTRL_DIR (1 << 1)
+#define DMA_CTRL_EN (1 << 0)
+
+/* DMA Flash Side Address */
+#define R_DMA_FLASH_ADDR (0x84 / 4)
+
+/* DMA DRAM Side Address */
+#define R_DMA_DRAM_ADDR (0x88 / 4)
+
+/* DMA Length Register */
+#define R_DMA_LEN (0x8C / 4)
+
+/* Checksum Calculation Result */
+#define R_DMA_CHECKSUM (0x90 / 4)
+
+/* Misc Control Register #2 */
+#define R_TIMINGS (0x94 / 4)
+
+/* SPI controller registers and bits */
+#define R_SPI_CONF (0x00 / 4)
+#define SPI_CONF_ENABLE_W0 0
+#define R_SPI_CTRL0 (0x4 / 4)
+#define R_SPI_MISC_CTRL (0x10 / 4)
+#define R_SPI_TIMINGS (0x14 / 4)
+
+/*
+ * Default segments mapping addresses and size for each slave per
+ * controller. These can be changed when board is initialized with the
+ * Segment Address Registers but they don't seem do be used on the
+ * field.
+ */
+static const AspeedSegments aspeed_segments_legacy[] = {
+ { 0x10000000, 32 * 1024 * 1024 },
+};
+
+static const AspeedSegments aspeed_segments_fmc[] = {
+ { 0x20000000, 64 * 1024 * 1024 },
+ { 0x24000000, 32 * 1024 * 1024 },
+ { 0x26000000, 32 * 1024 * 1024 },
+ { 0x28000000, 32 * 1024 * 1024 },
+ { 0x2A000000, 32 * 1024 * 1024 }
+};
+
+static const AspeedSegments aspeed_segments_spi[] = {
+ { 0x30000000, 64 * 1024 * 1024 },
+};
+
+static const AspeedSMCController controllers[] = {
+ { "aspeed.smc.smc", R_CONF, R_CE_CTRL, R_CTRL0, R_TIMINGS,
+ CONF_ENABLE_W0, 5, aspeed_segments_legacy, 0x6000000 },
+ { "aspeed.smc.fmc", R_CONF, R_CE_CTRL, R_CTRL0, R_TIMINGS,
+ CONF_ENABLE_W0, 5, aspeed_segments_fmc, 0x10000000 },
+ { "aspeed.smc.spi", R_SPI_CONF, 0xff, R_SPI_CTRL0, R_SPI_TIMINGS,
+ SPI_CONF_ENABLE_W0, 1, aspeed_segments_spi, 0x10000000 },
+};
+
+static uint64_t aspeed_smc_flash_default_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: To 0x%" HWADDR_PRIx " of size %u"
+ PRIx64 "\n", __func__, addr, size);
+ return 0;
+}
+
+static void aspeed_smc_flash_default_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: To 0x%" HWADDR_PRIx " of size %u: 0x%"
+ PRIx64 "\n", __func__, addr, size, data);
+}
+
+static const MemoryRegionOps aspeed_smc_flash_default_ops = {
+ .read = aspeed_smc_flash_default_read,
+ .write = aspeed_smc_flash_default_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ },
+};
+
+static inline int aspeed_smc_flash_mode(const AspeedSMCState *s, int cs)
+{
+ return s->regs[s->r_ctrl0 + cs] & CTRL_CMD_MODE_MASK;
+}
+
+static inline bool aspeed_smc_is_usermode(const AspeedSMCState *s, int cs)
+{
+ return aspeed_smc_flash_mode(s, cs) == CTRL_USERMODE;
+}
+
+static inline bool aspeed_smc_is_writable(const AspeedSMCState *s, int cs)
+{
+ return s->regs[s->r_conf] & (1 << (s->conf_enable_w0 + cs));
+}
+
+static uint64_t aspeed_smc_flash_read(void *opaque, hwaddr addr, unsigned size)
+{
+ AspeedSMCFlash *fl = opaque;
+ const AspeedSMCState *s = fl->controller;
+ uint64_t ret = 0;
+ int i;
+
+ if (aspeed_smc_is_usermode(s, fl->id)) {
+ for (i = 0; i < size; i++) {
+ ret |= ssi_transfer(s->spi, 0x0) << (8 * i);
+ }
+ } else {
+ qemu_log_mask(LOG_UNIMP, "%s: usermode not implemented\n",
+ __func__);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static void aspeed_smc_flash_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned size)
+{
+ AspeedSMCFlash *fl = opaque;
+ const AspeedSMCState *s = fl->controller;
+ int i;
+
+ if (!aspeed_smc_is_writable(s, fl->id)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: flash is not writable at 0x%"
+ HWADDR_PRIx "\n", __func__, addr);
+ return;
+ }
+
+ if (!aspeed_smc_is_usermode(s, fl->id)) {
+ qemu_log_mask(LOG_UNIMP, "%s: usermode not implemented\n",
+ __func__);
+ return;
+ }
+
+ for (i = 0; i < size; i++) {
+ ssi_transfer(s->spi, (data >> (8 * i)) & 0xff);
+ }
+}
+
+static const MemoryRegionOps aspeed_smc_flash_ops = {
+ .read = aspeed_smc_flash_read,
+ .write = aspeed_smc_flash_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ },
+};
+
+static bool aspeed_smc_is_ce_stop_active(const AspeedSMCState *s, int cs)
+{
+ return s->regs[s->r_ctrl0 + cs] & CTRL_CE_STOP_ACTIVE;
+}
+
+static void aspeed_smc_update_cs(const AspeedSMCState *s)
+{
+ int i;
+
+ for (i = 0; i < s->num_cs; ++i) {
+ qemu_set_irq(s->cs_lines[i], aspeed_smc_is_ce_stop_active(s, i));
+ }
+}
+
+static void aspeed_smc_reset(DeviceState *d)
+{
+ AspeedSMCState *s = ASPEED_SMC(d);
+ int i;
+
+ memset(s->regs, 0, sizeof s->regs);
+
+ /* Unselect all slaves */
+ for (i = 0; i < s->num_cs; ++i) {
+ s->regs[s->r_ctrl0 + i] |= CTRL_CE_STOP_ACTIVE;
+ }
+
+ aspeed_smc_update_cs(s);
+}
+
+static bool aspeed_smc_is_implemented(AspeedSMCState *s, hwaddr addr)
+{
+ return (addr == s->r_conf || addr == s->r_timings || addr == s->r_ce_ctrl ||
+ (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + s->num_cs));
+}
+
+static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size)
+{
+ AspeedSMCState *s = ASPEED_SMC(opaque);
+
+ addr >>= 2;
+
+ if (addr >= ARRAY_SIZE(s->regs)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Out-of-bounds read at 0x%" HWADDR_PRIx "\n",
+ __func__, addr);
+ return 0;
+ }
+
+ if (!aspeed_smc_is_implemented(s, addr)) {
+ qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n",
+ __func__, addr);
+ return 0;
+ }
+
+ return s->regs[addr];
+}
+
+static void aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned int size)
+{
+ AspeedSMCState *s = ASPEED_SMC(opaque);
+ uint32_t value = data;
+
+ addr >>= 2;
+
+ if (addr >= ARRAY_SIZE(s->regs)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Out-of-bounds write at 0x%" HWADDR_PRIx "\n",
+ __func__, addr);
+ return;
+ }
+
+ if (!aspeed_smc_is_implemented(s, addr)) {
+ qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n",
+ __func__, addr);
+ return;
+ }
+
+ /*
+ * Not much to do apart from storing the value and set the cs
+ * lines if the register is a controlling one.
+ */
+ s->regs[addr] = value;
+ if (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + s->num_cs) {
+ aspeed_smc_update_cs(s);
+ }
+}
+
+static const MemoryRegionOps aspeed_smc_ops = {
+ .read = aspeed_smc_read,
+ .write = aspeed_smc_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid.unaligned = true,
+};
+
+static void aspeed_smc_realize(DeviceState *dev, Error **errp)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ AspeedSMCState *s = ASPEED_SMC(dev);
+ AspeedSMCClass *mc = ASPEED_SMC_GET_CLASS(s);
+ int i;
+ char name[32];
+ hwaddr offset = 0;
+
+ s->ctrl = mc->ctrl;
+
+ /* keep a copy under AspeedSMCState to speed up accesses */
+ s->r_conf = s->ctrl->r_conf;
+ s->r_ce_ctrl = s->ctrl->r_ce_ctrl;
+ s->r_ctrl0 = s->ctrl->r_ctrl0;
+ s->r_timings = s->ctrl->r_timings;
+ s->conf_enable_w0 = s->ctrl->conf_enable_w0;
+
+ /* Enforce some real HW limits */
+ if (s->num_cs > s->ctrl->max_slaves) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: num_cs cannot exceed: %d\n",
+ __func__, s->ctrl->max_slaves);
+ s->num_cs = s->ctrl->max_slaves;
+ }
+
+ s->spi = ssi_create_bus(dev, "spi");
+
+ /* Setup cs_lines for slaves */
+ sysbus_init_irq(sbd, &s->irq);
+ s->cs_lines = g_new0(qemu_irq, s->num_cs);
+ ssi_auto_connect_slaves(dev, s->cs_lines, s->spi);
+
+ for (i = 0; i < s->num_cs; ++i) {
+ sysbus_init_irq(sbd, &s->cs_lines[i]);
+ }
+
+ aspeed_smc_reset(dev);
+
+ memory_region_init_io(&s->mmio, OBJECT(s), &aspeed_smc_ops, s,
+ s->ctrl->name, ASPEED_SMC_R_MAX * 4);
+ sysbus_init_mmio(sbd, &s->mmio);
+
+ /*
+ * Memory region where flash modules are remapped
+ */
+ snprintf(name, sizeof(name), "%s.flash", s->ctrl->name);
+
+ memory_region_init_io(&s->mmio_flash, OBJECT(s),
+ &aspeed_smc_flash_default_ops, s, name,
+ s->ctrl->mapping_window_size);
+ sysbus_init_mmio(sbd, &s->mmio_flash);
+
+ s->flashes = g_new0(AspeedSMCFlash, s->num_cs);
+
+ for (i = 0; i < s->num_cs; ++i) {
+ AspeedSMCFlash *fl = &s->flashes[i];
+
+ snprintf(name, sizeof(name), "%s.%d", s->ctrl->name, i);
+
+ fl->id = i;
+ fl->controller = s;
+ fl->size = s->ctrl->segments[i].size;
+ memory_region_init_io(&fl->mmio, OBJECT(s), &aspeed_smc_flash_ops,
+ fl, name, fl->size);
+ memory_region_add_subregion(&s->mmio_flash, offset, &fl->mmio);
+ offset += fl->size;
+ }
+}
+
+static const VMStateDescription vmstate_aspeed_smc = {
+ .name = "aspeed.smc",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(regs, AspeedSMCState, ASPEED_SMC_R_MAX),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property aspeed_smc_properties[] = {
+ DEFINE_PROP_UINT32("num-cs", AspeedSMCState, num_cs, 1),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void aspeed_smc_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ AspeedSMCClass *mc = ASPEED_SMC_CLASS(klass);
+
+ dc->realize = aspeed_smc_realize;
+ dc->reset = aspeed_smc_reset;
+ dc->props = aspeed_smc_properties;
+ dc->vmsd = &vmstate_aspeed_smc;
+ mc->ctrl = data;
+}
+
+static const TypeInfo aspeed_smc_info = {
+ .name = TYPE_ASPEED_SMC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AspeedSMCState),
+ .class_size = sizeof(AspeedSMCClass),
+ .abstract = true,
+};
+
+static void aspeed_smc_register_types(void)
+{
+ int i;
+
+ type_register_static(&aspeed_smc_info);
+ for (i = 0; i < ARRAY_SIZE(controllers); ++i) {
+ TypeInfo ti = {
+ .name = controllers[i].name,
+ .parent = TYPE_ASPEED_SMC,
+ .class_init = aspeed_smc_class_init,
+ .class_data = (void *)&controllers[i],
+ };
+ type_register(&ti);
+ }
+}
+
+type_init(aspeed_smc_register_types)
diff --git a/hw/ssi/ssi.c b/hw/ssi/ssi.c
index 9791c0d947..7eaaf565fd 100644
--- a/hw/ssi/ssi.c
+++ b/hw/ssi/ssi.c
@@ -54,7 +54,7 @@ static uint32_t ssi_transfer_raw_default(SSISlave *dev, uint32_t val)
return 0;
}
-static int ssi_slave_init(DeviceState *dev)
+static void ssi_slave_realize(DeviceState *dev, Error **errp)
{
SSISlave *s = SSI_SLAVE(dev);
SSISlaveClass *ssc = SSI_SLAVE_GET_CLASS(s);
@@ -64,7 +64,7 @@ static int ssi_slave_init(DeviceState *dev)
qdev_init_gpio_in_named(dev, ssi_cs_default, SSI_GPIO_CS, 1);
}
- return ssc->init(s);
+ ssc->realize(s, errp);
}
static void ssi_slave_class_init(ObjectClass *klass, void *data)
@@ -72,7 +72,7 @@ static void ssi_slave_class_init(ObjectClass *klass, void *data)
SSISlaveClass *ssc = SSI_SLAVE_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
- dc->init = ssi_slave_init;
+ dc->realize = ssi_slave_realize;
dc->bus_type = TYPE_SSI_BUS;
if (!ssc->transfer_raw) {
ssc->transfer_raw = ssi_transfer_raw_default;
diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c
index f4e333eb8f..ea625f25ce 100644
--- a/hw/timer/mc146818rtc.c
+++ b/hw/timer/mc146818rtc.c
@@ -906,6 +906,8 @@ static void rtc_realizefn(DeviceState *dev, Error **errp)
object_property_add_alias(qdev_get_machine(), "rtc-time",
OBJECT(s), "date", NULL);
+
+ qdev_init_gpio_out(dev, &s->irq, 1);
}
ISADevice *rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq)
@@ -920,9 +922,9 @@ ISADevice *rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq)
qdev_prop_set_int32(dev, "base_year", base_year);
qdev_init_nofail(dev);
if (intercept_irq) {
- s->irq = intercept_irq;
+ qdev_connect_gpio_out(dev, 0, intercept_irq);
} else {
- isa_init_irq(isadev, &s->irq, RTC_ISA_IRQ);
+ isa_connect_gpio_out(isadev, 0, RTC_ISA_IRQ);
}
QLIST_INSERT_HEAD(&rtc_devices, s, link);
diff --git a/hw/vfio/Makefile.objs b/hw/vfio/Makefile.objs
index ceddbb8f99..c25e32b029 100644
--- a/hw/vfio/Makefile.objs
+++ b/hw/vfio/Makefile.objs
@@ -4,4 +4,5 @@ obj-$(CONFIG_PCI) += pci.o pci-quirks.o
obj-$(CONFIG_SOFTMMU) += platform.o
obj-$(CONFIG_SOFTMMU) += calxeda-xgmac.o
obj-$(CONFIG_SOFTMMU) += amd-xgbe.o
+obj-$(CONFIG_SOFTMMU) += spapr.o
endif
diff --git a/hw/vfio/common.c b/hw/vfio/common.c
index 27cc1596f9..f3c0522e7e 100644
--- a/hw/vfio/common.c
+++ b/hw/vfio/common.c
@@ -28,6 +28,7 @@
#include "exec/memory.h"
#include "hw/hw.h"
#include "qemu/error-report.h"
+#include "qemu/range.h"
#include "sysemu/kvm.h"
#ifdef CONFIG_KVM
#include "linux/kvm.h"
@@ -241,6 +242,44 @@ static int vfio_dma_map(VFIOContainer *container, hwaddr iova,
return -errno;
}
+static void vfio_host_win_add(VFIOContainer *container,
+ hwaddr min_iova, hwaddr max_iova,
+ uint64_t iova_pgsizes)
+{
+ VFIOHostDMAWindow *hostwin;
+
+ QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) {
+ if (ranges_overlap(hostwin->min_iova,
+ hostwin->max_iova - hostwin->min_iova + 1,
+ min_iova,
+ max_iova - min_iova + 1)) {
+ hw_error("%s: Overlapped IOMMU are not enabled", __func__);
+ }
+ }
+
+ hostwin = g_malloc0(sizeof(*hostwin));
+
+ hostwin->min_iova = min_iova;
+ hostwin->max_iova = max_iova;
+ hostwin->iova_pgsizes = iova_pgsizes;
+ QLIST_INSERT_HEAD(&container->hostwin_list, hostwin, hostwin_next);
+}
+
+static int vfio_host_win_del(VFIOContainer *container, hwaddr min_iova,
+ hwaddr max_iova)
+{
+ VFIOHostDMAWindow *hostwin;
+
+ QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) {
+ if (hostwin->min_iova == min_iova && hostwin->max_iova == max_iova) {
+ QLIST_REMOVE(hostwin, hostwin_next);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
static bool vfio_listener_skipped_section(MemoryRegionSection *section)
{
return (!memory_region_is_ram(section->mr) &&
@@ -329,6 +368,8 @@ static void vfio_listener_region_add(MemoryListener *listener,
Int128 llend, llsize;
void *vaddr;
int ret;
+ VFIOHostDMAWindow *hostwin;
+ bool hostwin_found;
if (vfio_listener_skipped_section(section)) {
trace_vfio_listener_region_add_skip(
@@ -354,7 +395,40 @@ static void vfio_listener_region_add(MemoryListener *listener,
}
end = int128_get64(int128_sub(llend, int128_one()));
- if ((iova < container->min_iova) || (end > container->max_iova)) {
+ if (container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU) {
+ VFIOHostDMAWindow *hostwin;
+ hwaddr pgsize = 0;
+
+ /* For now intersections are not allowed, we may relax this later */
+ QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) {
+ if (ranges_overlap(hostwin->min_iova,
+ hostwin->max_iova - hostwin->min_iova + 1,
+ section->offset_within_address_space,
+ int128_get64(section->size))) {
+ ret = -1;
+ goto fail;
+ }
+ }
+
+ ret = vfio_spapr_create_window(container, section, &pgsize);
+ if (ret) {
+ goto fail;
+ }
+
+ vfio_host_win_add(container, section->offset_within_address_space,
+ section->offset_within_address_space +
+ int128_get64(section->size) - 1, pgsize);
+ }
+
+ hostwin_found = false;
+ QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) {
+ if (hostwin->min_iova <= iova && end <= hostwin->max_iova) {
+ hostwin_found = true;
+ break;
+ }
+ }
+
+ if (!hostwin_found) {
error_report("vfio: IOMMU container %p can't map guest IOVA region"
" 0x%"HWADDR_PRIx"..0x%"HWADDR_PRIx,
container, iova, end);
@@ -369,10 +443,6 @@ static void vfio_listener_region_add(MemoryListener *listener,
trace_vfio_listener_region_add_iommu(iova, end);
/*
- * FIXME: We should do some checking to see if the
- * capabilities of the host VFIO IOMMU are adequate to model
- * the guest IOMMU
- *
* FIXME: For VFIO iommu types which have KVM acceleration to
* avoid bouncing all map/unmaps through qemu this way, this
* would be the right place to wire that up (tell the KVM
@@ -455,7 +525,8 @@ static void vfio_listener_region_del(MemoryListener *listener,
QLIST_FOREACH(giommu, &container->giommu_list, giommu_next) {
if (giommu->iommu == section->mr) {
- memory_region_unregister_iommu_notifier(&giommu->n);
+ memory_region_unregister_iommu_notifier(giommu->iommu,
+ &giommu->n);
QLIST_REMOVE(giommu, giommu_next);
g_free(giommu);
break;
@@ -492,6 +563,18 @@ static void vfio_listener_region_del(MemoryListener *listener,
"0x%"HWADDR_PRIx") = %d (%m)",
container, iova, int128_get64(llsize), ret);
}
+
+ if (container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU) {
+ vfio_spapr_remove_window(container,
+ section->offset_within_address_space);
+ if (vfio_host_win_del(container,
+ section->offset_within_address_space,
+ section->offset_within_address_space +
+ int128_get64(section->size) - 1) < 0) {
+ hw_error("%s: Cannot delete missing window at %"HWADDR_PRIx,
+ __func__, section->offset_within_address_space);
+ }
+ }
}
static const MemoryListener vfio_memory_listener = {
@@ -502,6 +585,9 @@ static const MemoryListener vfio_memory_listener = {
static void vfio_listener_release(VFIOContainer *container)
{
memory_listener_unregister(&container->listener);
+ if (container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU) {
+ memory_listener_unregister(&container->prereg_listener);
+ }
}
static struct vfio_info_cap_header *
@@ -860,8 +946,8 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as)
goto free_container_exit;
}
- ret = ioctl(fd, VFIO_SET_IOMMU,
- v2 ? VFIO_TYPE1v2_IOMMU : VFIO_TYPE1_IOMMU);
+ container->iommu_type = v2 ? VFIO_TYPE1v2_IOMMU : VFIO_TYPE1_IOMMU;
+ ret = ioctl(fd, VFIO_SET_IOMMU, container->iommu_type);
if (ret) {
error_report("vfio: failed to set iommu for container: %m");
ret = -errno;
@@ -875,19 +961,18 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as)
* existing Type1 IOMMUs generally support any IOVA we're
* going to actually try in practice.
*/
- container->min_iova = 0;
- container->max_iova = (hwaddr)-1;
-
- /* Assume just 4K IOVA page size */
- container->iova_pgsizes = 0x1000;
info.argsz = sizeof(info);
ret = ioctl(fd, VFIO_IOMMU_GET_INFO, &info);
/* Ignore errors */
- if ((ret == 0) && (info.flags & VFIO_IOMMU_INFO_PGSIZES)) {
- container->iova_pgsizes = info.iova_pgsizes;
+ if (ret || !(info.flags & VFIO_IOMMU_INFO_PGSIZES)) {
+ /* Assume 4k IOVA page size */
+ info.iova_pgsizes = 4096;
}
- } else if (ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_SPAPR_TCE_IOMMU)) {
+ vfio_host_win_add(container, 0, (hwaddr)-1, info.iova_pgsizes);
+ } else if (ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_SPAPR_TCE_IOMMU) ||
+ ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_SPAPR_TCE_v2_IOMMU)) {
struct vfio_iommu_spapr_tce_info info;
+ bool v2 = !!ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_SPAPR_TCE_v2_IOMMU);
ret = ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &fd);
if (ret) {
@@ -895,7 +980,9 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as)
ret = -errno;
goto free_container_exit;
}
- ret = ioctl(fd, VFIO_SET_IOMMU, VFIO_SPAPR_TCE_IOMMU);
+ container->iommu_type =
+ v2 ? VFIO_SPAPR_TCE_v2_IOMMU : VFIO_SPAPR_TCE_IOMMU;
+ ret = ioctl(fd, VFIO_SET_IOMMU, container->iommu_type);
if (ret) {
error_report("vfio: failed to set iommu for container: %m");
ret = -errno;
@@ -907,30 +994,54 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as)
* when container fd is closed so we do not call it explicitly
* in this file.
*/
- ret = ioctl(fd, VFIO_IOMMU_ENABLE);
- if (ret) {
- error_report("vfio: failed to enable container: %m");
- ret = -errno;
- goto free_container_exit;
+ if (!v2) {
+ ret = ioctl(fd, VFIO_IOMMU_ENABLE);
+ if (ret) {
+ error_report("vfio: failed to enable container: %m");
+ ret = -errno;
+ goto free_container_exit;
+ }
+ } else {
+ container->prereg_listener = vfio_prereg_listener;
+
+ memory_listener_register(&container->prereg_listener,
+ &address_space_memory);
+ if (container->error) {
+ memory_listener_unregister(&container->prereg_listener);
+ error_report("vfio: RAM memory listener initialization failed for container");
+ goto free_container_exit;
+ }
}
- /*
- * This only considers the host IOMMU's 32-bit window. At
- * some point we need to add support for the optional 64-bit
- * window and dynamic windows
- */
info.argsz = sizeof(info);
ret = ioctl(fd, VFIO_IOMMU_SPAPR_TCE_GET_INFO, &info);
if (ret) {
error_report("vfio: VFIO_IOMMU_SPAPR_TCE_GET_INFO failed: %m");
ret = -errno;
+ if (v2) {
+ memory_listener_unregister(&container->prereg_listener);
+ }
goto free_container_exit;
}
- container->min_iova = info.dma32_window_start;
- container->max_iova = container->min_iova + info.dma32_window_size - 1;
- /* Assume just 4K IOVA pages for now */
- container->iova_pgsizes = 0x1000;
+ if (v2) {
+ /*
+ * There is a default window in just created container.
+ * To make region_add/del simpler, we better remove this
+ * window now and let those iommu_listener callbacks
+ * create/remove them when needed.
+ */
+ ret = vfio_spapr_remove_window(container, info.dma32_window_start);
+ if (ret) {
+ goto free_container_exit;
+ }
+ } else {
+ /* The default table uses 4K pages */
+ vfio_host_win_add(container, info.dma32_window_start,
+ info.dma32_window_start +
+ info.dma32_window_size - 1,
+ 0x1000);
+ }
} else {
error_report("vfio: No available IOMMU models");
ret = -EINVAL;
@@ -991,7 +1102,7 @@ static void vfio_disconnect_container(VFIOGroup *group)
QLIST_REMOVE(container, next);
QLIST_FOREACH_SAFE(giommu, &container->giommu_list, giommu_next, tmp) {
- memory_region_unregister_iommu_notifier(&giommu->n);
+ memory_region_unregister_iommu_notifier(giommu->iommu, &giommu->n);
QLIST_REMOVE(giommu, giommu_next);
g_free(giommu);
}
diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c
index 35d32b78f4..bec694c8d8 100644
--- a/hw/vfio/pci-quirks.c
+++ b/hw/vfio/pci-quirks.c
@@ -318,7 +318,7 @@ static void vfio_probe_ati_bar4_quirk(VFIOPCIDevice *vdev, int nr)
/* This windows doesn't seem to be used except by legacy VGA code */
if (!vfio_pci_is(vdev, PCI_VENDOR_ID_ATI, PCI_ANY_ID) ||
- !vdev->has_vga || nr != 4) {
+ !vdev->vga || nr != 4) {
return;
}
@@ -366,7 +366,7 @@ static void vfio_probe_ati_bar2_quirk(VFIOPCIDevice *vdev, int nr)
/* Only enable on newer devices where BAR2 is 64bit */
if (!vfio_pci_is(vdev, PCI_VENDOR_ID_ATI, PCI_ANY_ID) ||
- !vdev->has_vga || nr != 2 || !vdev->bars[2].mem64) {
+ !vdev->vga || nr != 2 || !vdev->bars[2].mem64) {
return;
}
@@ -660,7 +660,7 @@ static void vfio_probe_nvidia_bar5_quirk(VFIOPCIDevice *vdev, int nr)
VFIOConfigWindowQuirk *window;
if (!vfio_pci_is(vdev, PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID) ||
- !vdev->has_vga || nr != 5) {
+ !vdev->vga || nr != 5) {
return;
}
@@ -776,7 +776,7 @@ static void vfio_probe_nvidia_bar0_quirk(VFIOPCIDevice *vdev, int nr)
QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
/* The 0x1800 offset mirror only seems to get used by legacy VGA */
- if (vdev->has_vga) {
+ if (vdev->vga) {
quirk = g_malloc0(sizeof(*quirk));
mirror = quirk->data = g_malloc0(sizeof(*mirror));
mirror->mem = quirk->mem = g_new0(MemoryRegion, 1);
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index 53b87b76ea..f2c679e47c 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -1502,6 +1502,21 @@ static uint8_t vfio_std_cap_max_size(PCIDevice *pdev, uint8_t pos)
return next - pos;
}
+
+static uint16_t vfio_ext_cap_max_size(const uint8_t *config, uint16_t pos)
+{
+ uint16_t tmp, next = PCIE_CONFIG_SPACE_SIZE;
+
+ for (tmp = PCI_CONFIG_SPACE_SIZE; tmp;
+ tmp = PCI_EXT_CAP_NEXT(pci_get_long(config + tmp))) {
+ if (tmp > pos && tmp < next) {
+ next = tmp;
+ }
+ }
+
+ return next - pos;
+}
+
static void vfio_set_word_bits(uint8_t *buf, uint16_t val, uint16_t mask)
{
pci_set_word(buf, (pci_get_word(buf) & ~mask) | val);
@@ -1749,16 +1764,100 @@ static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos)
return 0;
}
+static int vfio_add_ext_cap(VFIOPCIDevice *vdev)
+{
+ PCIDevice *pdev = &vdev->pdev;
+ uint32_t header;
+ uint16_t cap_id, next, size;
+ uint8_t cap_ver;
+ uint8_t *config;
+
+ /* Only add extended caps if we have them and the guest can see them */
+ if (!pci_is_express(pdev) || !pci_bus_is_express(pdev->bus) ||
+ !pci_get_long(pdev->config + PCI_CONFIG_SPACE_SIZE)) {
+ return 0;
+ }
+
+ /*
+ * pcie_add_capability always inserts the new capability at the tail
+ * of the chain. Therefore to end up with a chain that matches the
+ * physical device, we cache the config space to avoid overwriting
+ * the original config space when we parse the extended capabilities.
+ */
+ config = g_memdup(pdev->config, vdev->config_size);
+
+ /*
+ * Extended capabilities are chained with each pointing to the next, so we
+ * can drop anything other than the head of the chain simply by modifying
+ * the previous next pointer. For the head of the chain, we can modify the
+ * capability ID to something that cannot match a valid capability. ID
+ * 0 is reserved for this since absence of capabilities is indicated by
+ * 0 for the ID, version, AND next pointer. However, pcie_add_capability()
+ * uses ID 0 as reserved for list management and will incorrectly match and
+ * assert if we attempt to pre-load the head of the chain with with this
+ * ID. Use ID 0xFFFF temporarily since it is also seems to be reserved in
+ * part for identifying absence of capabilities in a root complex register
+ * block. If the ID still exists after adding capabilities, switch back to
+ * zero. We'll mark this entire first dword as emulated for this purpose.
+ */
+ pci_set_long(pdev->config + PCI_CONFIG_SPACE_SIZE,
+ PCI_EXT_CAP(0xFFFF, 0, 0));
+ pci_set_long(pdev->wmask + PCI_CONFIG_SPACE_SIZE, 0);
+ pci_set_long(vdev->emulated_config_bits + PCI_CONFIG_SPACE_SIZE, ~0);
+
+ for (next = PCI_CONFIG_SPACE_SIZE; next;
+ next = PCI_EXT_CAP_NEXT(pci_get_long(config + next))) {
+ header = pci_get_long(config + next);
+ cap_id = PCI_EXT_CAP_ID(header);
+ cap_ver = PCI_EXT_CAP_VER(header);
+
+ /*
+ * If it becomes important to configure extended capabilities to their
+ * actual size, use this as the default when it's something we don't
+ * recognize. Since QEMU doesn't actually handle many of the config
+ * accesses, exact size doesn't seem worthwhile.
+ */
+ size = vfio_ext_cap_max_size(config, next);
+
+ /* Use emulated next pointer to allow dropping extended caps */
+ pci_long_test_and_set_mask(vdev->emulated_config_bits + next,
+ PCI_EXT_CAP_NEXT_MASK);
+
+ switch (cap_id) {
+ case PCI_EXT_CAP_ID_SRIOV: /* Read-only VF BARs confuse OVMF */
+ trace_vfio_add_ext_cap_dropped(vdev->vbasedev.name, cap_id, next);
+ break;
+ default:
+ pcie_add_capability(pdev, cap_id, cap_ver, next, size);
+ }
+
+ }
+
+ /* Cleanup chain head ID if necessary */
+ if (pci_get_word(pdev->config + PCI_CONFIG_SPACE_SIZE) == 0xFFFF) {
+ pci_set_word(pdev->config + PCI_CONFIG_SPACE_SIZE, 0);
+ }
+
+ g_free(config);
+ return 0;
+}
+
static int vfio_add_capabilities(VFIOPCIDevice *vdev)
{
PCIDevice *pdev = &vdev->pdev;
+ int ret;
if (!(pdev->config[PCI_STATUS] & PCI_STATUS_CAP_LIST) ||
!pdev->config[PCI_CAPABILITY_LIST]) {
return 0; /* Nothing to add */
}
- return vfio_add_std_cap(vdev, pdev->config[PCI_CAPABILITY_LIST]);
+ ret = vfio_add_std_cap(vdev, pdev->config[PCI_CAPABILITY_LIST]);
+ if (ret) {
+ return ret;
+ }
+
+ return vfio_add_ext_cap(vdev);
}
static void vfio_pci_pre_reset(VFIOPCIDevice *vdev)
diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h
index b3eb0d838e..7d482d9d21 100644
--- a/hw/vfio/pci.h
+++ b/hw/vfio/pci.h
@@ -135,7 +135,6 @@ typedef struct VFIOPCIDevice {
int32_t bootindex;
uint32_t igd_gms;
uint8_t pm_cap;
- bool has_vga;
bool pci_aer;
bool req_enabled;
bool has_flr;
diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c
new file mode 100644
index 0000000000..0af342332c
--- /dev/null
+++ b/hw/vfio/spapr.c
@@ -0,0 +1,210 @@
+/*
+ * DMA memory preregistration
+ *
+ * Authors:
+ * Alexey Kardashevskiy <aik@ozlabs.ru>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include <sys/ioctl.h>
+#include <linux/vfio.h>
+
+#include "hw/vfio/vfio-common.h"
+#include "hw/hw.h"
+#include "qemu/error-report.h"
+#include "trace.h"
+
+static bool vfio_prereg_listener_skipped_section(MemoryRegionSection *section)
+{
+ if (memory_region_is_iommu(section->mr)) {
+ hw_error("Cannot possibly preregister IOMMU memory");
+ }
+
+ return !memory_region_is_ram(section->mr) ||
+ memory_region_is_skip_dump(section->mr);
+}
+
+static void *vfio_prereg_gpa_to_vaddr(MemoryRegionSection *section, hwaddr gpa)
+{
+ return memory_region_get_ram_ptr(section->mr) +
+ section->offset_within_region +
+ (gpa - section->offset_within_address_space);
+}
+
+static void vfio_prereg_listener_region_add(MemoryListener *listener,
+ MemoryRegionSection *section)
+{
+ VFIOContainer *container = container_of(listener, VFIOContainer,
+ prereg_listener);
+ const hwaddr gpa = section->offset_within_address_space;
+ hwaddr end;
+ int ret;
+ hwaddr page_mask = qemu_real_host_page_mask;
+ struct vfio_iommu_spapr_register_memory reg = {
+ .argsz = sizeof(reg),
+ .flags = 0,
+ };
+
+ if (vfio_prereg_listener_skipped_section(section)) {
+ trace_vfio_prereg_listener_region_add_skip(
+ section->offset_within_address_space,
+ section->offset_within_address_space +
+ int128_get64(int128_sub(section->size, int128_one())));
+ return;
+ }
+
+ if (unlikely((section->offset_within_address_space & ~page_mask) ||
+ (section->offset_within_region & ~page_mask) ||
+ (int128_get64(section->size) & ~page_mask))) {
+ error_report("%s received unaligned region", __func__);
+ return;
+ }
+
+ end = section->offset_within_address_space + int128_get64(section->size);
+ if (gpa >= end) {
+ return;
+ }
+
+ memory_region_ref(section->mr);
+
+ reg.vaddr = (uintptr_t) vfio_prereg_gpa_to_vaddr(section, gpa);
+ reg.size = end - gpa;
+
+ ret = ioctl(container->fd, VFIO_IOMMU_SPAPR_REGISTER_MEMORY, &reg);
+ trace_vfio_prereg_register(reg.vaddr, reg.size, ret ? -errno : 0);
+ if (ret) {
+ /*
+ * On the initfn path, store the first error in the container so we
+ * can gracefully fail. Runtime, there's not much we can do other
+ * than throw a hardware error.
+ */
+ if (!container->initialized) {
+ if (!container->error) {
+ container->error = ret;
+ }
+ } else {
+ hw_error("vfio: Memory registering failed, unable to continue");
+ }
+ }
+}
+
+static void vfio_prereg_listener_region_del(MemoryListener *listener,
+ MemoryRegionSection *section)
+{
+ VFIOContainer *container = container_of(listener, VFIOContainer,
+ prereg_listener);
+ const hwaddr gpa = section->offset_within_address_space;
+ hwaddr end;
+ int ret;
+ hwaddr page_mask = qemu_real_host_page_mask;
+ struct vfio_iommu_spapr_register_memory reg = {
+ .argsz = sizeof(reg),
+ .flags = 0,
+ };
+
+ if (vfio_prereg_listener_skipped_section(section)) {
+ trace_vfio_prereg_listener_region_del_skip(
+ section->offset_within_address_space,
+ section->offset_within_address_space +
+ int128_get64(int128_sub(section->size, int128_one())));
+ return;
+ }
+
+ if (unlikely((section->offset_within_address_space & ~page_mask) ||
+ (section->offset_within_region & ~page_mask) ||
+ (int128_get64(section->size) & ~page_mask))) {
+ error_report("%s received unaligned region", __func__);
+ return;
+ }
+
+ end = section->offset_within_address_space + int128_get64(section->size);
+ if (gpa >= end) {
+ return;
+ }
+
+ reg.vaddr = (uintptr_t) vfio_prereg_gpa_to_vaddr(section, gpa);
+ reg.size = end - gpa;
+
+ ret = ioctl(container->fd, VFIO_IOMMU_SPAPR_UNREGISTER_MEMORY, &reg);
+ trace_vfio_prereg_unregister(reg.vaddr, reg.size, ret ? -errno : 0);
+}
+
+const MemoryListener vfio_prereg_listener = {
+ .region_add = vfio_prereg_listener_region_add,
+ .region_del = vfio_prereg_listener_region_del,
+};
+
+int vfio_spapr_create_window(VFIOContainer *container,
+ MemoryRegionSection *section,
+ hwaddr *pgsize)
+{
+ int ret;
+ unsigned pagesize = memory_region_iommu_get_min_page_size(section->mr);
+ unsigned entries, pages;
+ struct vfio_iommu_spapr_tce_create create = { .argsz = sizeof(create) };
+
+ /*
+ * FIXME: For VFIO iommu types which have KVM acceleration to
+ * avoid bouncing all map/unmaps through qemu this way, this
+ * would be the right place to wire that up (tell the KVM
+ * device emulation the VFIO iommu handles to use).
+ */
+ create.window_size = int128_get64(section->size);
+ create.page_shift = ctz64(pagesize);
+ /*
+ * SPAPR host supports multilevel TCE tables, there is some
+ * heuristic to decide how many levels we want for our table:
+ * 0..64 = 1; 65..4096 = 2; 4097..262144 = 3; 262145.. = 4
+ */
+ entries = create.window_size >> create.page_shift;
+ pages = MAX((entries * sizeof(uint64_t)) / getpagesize(), 1);
+ pages = MAX(pow2ceil(pages) - 1, 1); /* Round up */
+ create.levels = ctz64(pages) / 6 + 1;
+
+ ret = ioctl(container->fd, VFIO_IOMMU_SPAPR_TCE_CREATE, &create);
+ if (ret) {
+ error_report("Failed to create a window, ret = %d (%m)", ret);
+ return -errno;
+ }
+
+ if (create.start_addr != section->offset_within_address_space) {
+ vfio_spapr_remove_window(container, create.start_addr);
+
+ error_report("Host doesn't support DMA window at %"HWADDR_PRIx", must be %"PRIx64,
+ section->offset_within_address_space,
+ (uint64_t)create.start_addr);
+ ioctl(container->fd, VFIO_IOMMU_SPAPR_TCE_REMOVE, &remove);
+ return -EINVAL;
+ }
+ trace_vfio_spapr_create_window(create.page_shift,
+ create.window_size,
+ create.start_addr);
+ *pgsize = pagesize;
+
+ return 0;
+}
+
+int vfio_spapr_remove_window(VFIOContainer *container,
+ hwaddr offset_within_address_space)
+{
+ struct vfio_iommu_spapr_tce_remove remove = {
+ .argsz = sizeof(remove),
+ .start_addr = offset_within_address_space,
+ };
+ int ret;
+
+ ret = ioctl(container->fd, VFIO_IOMMU_SPAPR_TCE_REMOVE, &remove);
+ if (ret) {
+ error_report("Failed to remove window at %"PRIx64,
+ (uint64_t)remove.start_addr);
+ return -errno;
+ }
+
+ trace_vfio_spapr_remove_window(offset_within_address_space);
+
+ return 0;
+}
diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events
index 9da0ff928b..4bb7690c46 100644
--- a/hw/vfio/trace-events
+++ b/hw/vfio/trace-events
@@ -37,6 +37,7 @@ vfio_pci_hot_reset_result(const char *name, const char *result) "%s hot reset: %
vfio_populate_device_config(const char *name, unsigned long size, unsigned long offset, unsigned long flags) "Device %s config:\n size: 0x%lx, offset: 0x%lx, flags: 0x%lx"
vfio_populate_device_get_irq_info_failure(void) "VFIO_DEVICE_GET_IRQ_INFO failure: %m"
vfio_initfn(const char *name, int group_id) " (%s) group %d"
+vfio_add_ext_cap_dropped(const char *name, uint16_t cap, uint16_t offset) "%s %x@%x"
vfio_pci_reset(const char *name) " (%s)"
vfio_pci_reset_flr(const char *name) "%s FLR/VFIO_DEVICE_RESET"
vfio_pci_reset_pm(const char *name) "%s PCI PM Reset"
@@ -114,3 +115,11 @@ vfio_platform_populate_interrupts(int pin, int count, int flags) "- IRQ index %d
vfio_intp_interrupt_set_pending(int index) "irq %d is set PENDING"
vfio_platform_start_level_irqfd_injection(int index, int fd, int resamplefd) "IRQ index=%d, fd = %d, resamplefd = %d"
vfio_platform_start_edge_irqfd_injection(int index, int fd) "IRQ index=%d, fd = %d"
+
+# hw/vfio/spapr.c
+vfio_prereg_listener_region_add_skip(uint64_t start, uint64_t end) "%"PRIx64" - %"PRIx64
+vfio_prereg_listener_region_del_skip(uint64_t start, uint64_t end) "%"PRIx64" - %"PRIx64
+vfio_prereg_register(uint64_t va, uint64_t size, int ret) "va=%"PRIx64" size=%"PRIx64" ret=%d"
+vfio_prereg_unregister(uint64_t va, uint64_t size, int ret) "va=%"PRIx64" size=%"PRIx64" ret=%d"
+vfio_spapr_create_window(int ps, uint64_t ws, uint64_t off) "pageshift=0x%x winsize=0x%"PRIx64" offset=0x%"PRIx64
+vfio_spapr_remove_window(uint64_t off) "offset=%"PRIx64
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
index 81cc5b0ae3..a01394d5ac 100644
--- a/hw/virtio/vhost.c
+++ b/hw/virtio/vhost.c
@@ -1110,14 +1110,15 @@ int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev)
VirtioBusState *vbus = VIRTIO_BUS(qbus);
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus);
int i, r, e;
- if (!k->set_host_notifier) {
+ if (!k->ioeventfd_started) {
fprintf(stderr, "binding does not support host notifiers\n");
r = -ENOSYS;
goto fail;
}
for (i = 0; i < hdev->nvqs; ++i) {
- r = k->set_host_notifier(qbus->parent, hdev->vq_index + i, true);
+ r = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), hdev->vq_index + i,
+ true);
if (r < 0) {
fprintf(stderr, "vhost VQ %d notifier binding failed: %d\n", i, -r);
goto fail_vq;
@@ -1127,7 +1128,8 @@ int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev)
return 0;
fail_vq:
while (--i >= 0) {
- e = k->set_host_notifier(qbus->parent, hdev->vq_index + i, false);
+ e = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), hdev->vq_index + i,
+ false);
if (e < 0) {
fprintf(stderr, "vhost VQ %d notifier cleanup error: %d\n", i, -r);
fflush(stderr);
@@ -1146,12 +1148,11 @@ fail:
void vhost_dev_disable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev)
{
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
- VirtioBusState *vbus = VIRTIO_BUS(qbus);
- VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus);
int i, r;
for (i = 0; i < hdev->nvqs; ++i) {
- r = k->set_host_notifier(qbus->parent, hdev->vq_index + i, false);
+ r = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), hdev->vq_index + i,
+ false);
if (r < 0) {
fprintf(stderr, "vhost VQ %d notifier cleanup failed: %d\n", i, -r);
fflush(stderr);
diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c
index 574f0e23f8..131376027b 100644
--- a/hw/virtio/virtio-bus.c
+++ b/hw/virtio/virtio-bus.c
@@ -146,6 +146,138 @@ void virtio_bus_set_vdev_config(VirtioBusState *bus, uint8_t *config)
}
}
+/*
+ * This function handles both assigning the ioeventfd handler and
+ * registering it with the kernel.
+ * assign: register/deregister ioeventfd with the kernel
+ * set_handler: use the generic ioeventfd handler
+ */
+static int set_host_notifier_internal(DeviceState *proxy, VirtioBusState *bus,
+ int n, bool assign, bool set_handler)
+{
+ VirtIODevice *vdev = virtio_bus_get_device(bus);
+ VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(bus);
+ VirtQueue *vq = virtio_get_queue(vdev, n);
+ EventNotifier *notifier = virtio_queue_get_host_notifier(vq);
+ int r = 0;
+
+ if (assign) {
+ r = event_notifier_init(notifier, 1);
+ if (r < 0) {
+ error_report("%s: unable to init event notifier: %d", __func__, r);
+ return r;
+ }
+ virtio_queue_set_host_notifier_fd_handler(vq, true, set_handler);
+ r = k->ioeventfd_assign(proxy, notifier, n, assign);
+ if (r < 0) {
+ error_report("%s: unable to assign ioeventfd: %d", __func__, r);
+ virtio_queue_set_host_notifier_fd_handler(vq, false, false);
+ event_notifier_cleanup(notifier);
+ return r;
+ }
+ } else {
+ virtio_queue_set_host_notifier_fd_handler(vq, false, false);
+ k->ioeventfd_assign(proxy, notifier, n, assign);
+ event_notifier_cleanup(notifier);
+ }
+ return r;
+}
+
+void virtio_bus_start_ioeventfd(VirtioBusState *bus)
+{
+ VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(bus);
+ DeviceState *proxy = DEVICE(BUS(bus)->parent);
+ VirtIODevice *vdev;
+ int n, r;
+
+ if (!k->ioeventfd_started || k->ioeventfd_started(proxy)) {
+ return;
+ }
+ if (k->ioeventfd_disabled(proxy)) {
+ return;
+ }
+ vdev = virtio_bus_get_device(bus);
+ for (n = 0; n < VIRTIO_QUEUE_MAX; n++) {
+ if (!virtio_queue_get_num(vdev, n)) {
+ continue;
+ }
+ r = set_host_notifier_internal(proxy, bus, n, true, true);
+ if (r < 0) {
+ goto assign_error;
+ }
+ }
+ k->ioeventfd_set_started(proxy, true, false);
+ return;
+
+assign_error:
+ while (--n >= 0) {
+ if (!virtio_queue_get_num(vdev, n)) {
+ continue;
+ }
+
+ r = set_host_notifier_internal(proxy, bus, n, false, false);
+ assert(r >= 0);
+ }
+ k->ioeventfd_set_started(proxy, false, true);
+ error_report("%s: failed. Fallback to userspace (slower).", __func__);
+}
+
+void virtio_bus_stop_ioeventfd(VirtioBusState *bus)
+{
+ VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(bus);
+ DeviceState *proxy = DEVICE(BUS(bus)->parent);
+ VirtIODevice *vdev;
+ int n, r;
+
+ if (!k->ioeventfd_started || !k->ioeventfd_started(proxy)) {
+ return;
+ }
+ vdev = virtio_bus_get_device(bus);
+ for (n = 0; n < VIRTIO_QUEUE_MAX; n++) {
+ if (!virtio_queue_get_num(vdev, n)) {
+ continue;
+ }
+ r = set_host_notifier_internal(proxy, bus, n, false, false);
+ assert(r >= 0);
+ }
+ k->ioeventfd_set_started(proxy, false, false);
+}
+
+/*
+ * This function switches from/to the generic ioeventfd handler.
+ * assign==false means 'use generic ioeventfd handler'.
+ */
+int virtio_bus_set_host_notifier(VirtioBusState *bus, int n, bool assign)
+{
+ VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(bus);
+ DeviceState *proxy = DEVICE(BUS(bus)->parent);
+ VirtIODevice *vdev = virtio_bus_get_device(bus);
+ VirtQueue *vq = virtio_get_queue(vdev, n);
+
+ if (!k->ioeventfd_started) {
+ return -ENOSYS;
+ }
+ if (assign) {
+ /*
+ * Stop using the generic ioeventfd, we are doing eventfd handling
+ * ourselves below
+ */
+ k->ioeventfd_set_disabled(proxy, true);
+ }
+ /*
+ * Just switch the handler, don't deassign the ioeventfd.
+ * Otherwise, there's a window where we don't have an
+ * ioeventfd and we may end up with a notification where
+ * we don't expect one.
+ */
+ virtio_queue_set_host_notifier_fd_handler(vq, assign, !assign);
+ if (!assign) {
+ /* Use generic ioeventfd handler again. */
+ k->ioeventfd_set_disabled(proxy, false);
+ }
+ return 0;
+}
+
static char *virtio_bus_get_dev_path(DeviceState *dev)
{
BusState *bus = qdev_get_parent_bus(dev);
diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c
index d4cd91f8c4..eb84b74532 100644
--- a/hw/virtio/virtio-mmio.c
+++ b/hw/virtio/virtio-mmio.c
@@ -93,90 +93,59 @@ typedef struct {
bool ioeventfd_started;
} VirtIOMMIOProxy;
-static int virtio_mmio_set_host_notifier_internal(VirtIOMMIOProxy *proxy,
- int n, bool assign,
- bool set_handler)
+static bool virtio_mmio_ioeventfd_started(DeviceState *d)
{
- VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
- VirtQueue *vq = virtio_get_queue(vdev, n);
- EventNotifier *notifier = virtio_queue_get_host_notifier(vq);
- int r = 0;
+ VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d);
- if (assign) {
- r = event_notifier_init(notifier, 1);
- if (r < 0) {
- error_report("%s: unable to init event notifier: %d",
- __func__, r);
- return r;
- }
- virtio_queue_set_host_notifier_fd_handler(vq, true, set_handler);
- memory_region_add_eventfd(&proxy->iomem, VIRTIO_MMIO_QUEUENOTIFY, 4,
- true, n, notifier);
- } else {
- memory_region_del_eventfd(&proxy->iomem, VIRTIO_MMIO_QUEUENOTIFY, 4,
- true, n, notifier);
- virtio_queue_set_host_notifier_fd_handler(vq, false, false);
- event_notifier_cleanup(notifier);
- }
- return r;
+ return proxy->ioeventfd_started;
}
-static void virtio_mmio_start_ioeventfd(VirtIOMMIOProxy *proxy)
+static void virtio_mmio_ioeventfd_set_started(DeviceState *d, bool started,
+ bool err)
{
- VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
- int n, r;
+ VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d);
- if (!kvm_eventfds_enabled() ||
- proxy->ioeventfd_disabled ||
- proxy->ioeventfd_started) {
- return;
- }
+ proxy->ioeventfd_started = started;
+}
- for (n = 0; n < VIRTIO_QUEUE_MAX; n++) {
- if (!virtio_queue_get_num(vdev, n)) {
- continue;
- }
+static bool virtio_mmio_ioeventfd_disabled(DeviceState *d)
+{
+ VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d);
- r = virtio_mmio_set_host_notifier_internal(proxy, n, true, true);
- if (r < 0) {
- goto assign_error;
- }
- }
- proxy->ioeventfd_started = true;
- return;
+ return !kvm_eventfds_enabled() || proxy->ioeventfd_disabled;
+}
-assign_error:
- while (--n >= 0) {
- if (!virtio_queue_get_num(vdev, n)) {
- continue;
- }
+static void virtio_mmio_ioeventfd_set_disabled(DeviceState *d, bool disabled)
+{
+ VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d);
- r = virtio_mmio_set_host_notifier_internal(proxy, n, false, false);
- assert(r >= 0);
- }
- proxy->ioeventfd_started = false;
- error_report("%s: failed. Fallback to a userspace (slower).", __func__);
+ proxy->ioeventfd_disabled = disabled;
}
-static void virtio_mmio_stop_ioeventfd(VirtIOMMIOProxy *proxy)
+static int virtio_mmio_ioeventfd_assign(DeviceState *d,
+ EventNotifier *notifier,
+ int n, bool assign)
{
- int r;
- int n;
- VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+ VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d);
- if (!proxy->ioeventfd_started) {
- return;
+ if (assign) {
+ memory_region_add_eventfd(&proxy->iomem, VIRTIO_MMIO_QUEUENOTIFY, 4,
+ true, n, notifier);
+ } else {
+ memory_region_del_eventfd(&proxy->iomem, VIRTIO_MMIO_QUEUENOTIFY, 4,
+ true, n, notifier);
}
+ return 0;
+}
- for (n = 0; n < VIRTIO_QUEUE_MAX; n++) {
- if (!virtio_queue_get_num(vdev, n)) {
- continue;
- }
+static void virtio_mmio_start_ioeventfd(VirtIOMMIOProxy *proxy)
+{
+ virtio_bus_start_ioeventfd(&proxy->bus);
+}
- r = virtio_mmio_set_host_notifier_internal(proxy, n, false, false);
- assert(r >= 0);
- }
- proxy->ioeventfd_started = false;
+static void virtio_mmio_stop_ioeventfd(VirtIOMMIOProxy *proxy)
+{
+ virtio_bus_stop_ioeventfd(&proxy->bus);
}
static uint64_t virtio_mmio_read(void *opaque, hwaddr offset, unsigned size)
@@ -498,25 +467,6 @@ assign_error:
return r;
}
-static int virtio_mmio_set_host_notifier(DeviceState *opaque, int n,
- bool assign)
-{
- VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque);
-
- /* Stop using ioeventfd for virtqueue kick if the device starts using host
- * notifiers. This makes it easy to avoid stepping on each others' toes.
- */
- proxy->ioeventfd_disabled = assign;
- if (assign) {
- virtio_mmio_stop_ioeventfd(proxy);
- }
- /* We don't need to start here: it's not needed because backend
- * currently only stops on status change away from ok,
- * reset, vmstop and such. If we do add code to start here,
- * need to check vmstate, device state etc. */
- return virtio_mmio_set_host_notifier_internal(proxy, n, assign, false);
-}
-
/* virtio-mmio device */
static void virtio_mmio_realizefn(DeviceState *d, Error **errp)
@@ -558,8 +508,12 @@ static void virtio_mmio_bus_class_init(ObjectClass *klass, void *data)
k->notify = virtio_mmio_update_irq;
k->save_config = virtio_mmio_save_config;
k->load_config = virtio_mmio_load_config;
- k->set_host_notifier = virtio_mmio_set_host_notifier;
k->set_guest_notifiers = virtio_mmio_set_guest_notifiers;
+ k->ioeventfd_started = virtio_mmio_ioeventfd_started;
+ k->ioeventfd_set_started = virtio_mmio_ioeventfd_set_started;
+ k->ioeventfd_disabled = virtio_mmio_ioeventfd_disabled;
+ k->ioeventfd_set_disabled = virtio_mmio_ioeventfd_set_disabled;
+ k->ioeventfd_assign = virtio_mmio_ioeventfd_assign;
k->has_variable_vring_alignment = true;
bus_class->max_dev = 1;
}
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index 1a0278304b..2b34b43060 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -262,14 +262,44 @@ static int virtio_pci_load_queue(DeviceState *d, int n, QEMUFile *f)
return 0;
}
+static bool virtio_pci_ioeventfd_started(DeviceState *d)
+{
+ VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+
+ return proxy->ioeventfd_started;
+}
+
+static void virtio_pci_ioeventfd_set_started(DeviceState *d, bool started,
+ bool err)
+{
+ VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+
+ proxy->ioeventfd_started = started;
+}
+
+static bool virtio_pci_ioeventfd_disabled(DeviceState *d)
+{
+ VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+
+ return proxy->ioeventfd_disabled ||
+ !(proxy->flags & VIRTIO_PCI_FLAG_USE_IOEVENTFD);
+}
+
+static void virtio_pci_ioeventfd_set_disabled(DeviceState *d, bool disabled)
+{
+ VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+
+ proxy->ioeventfd_disabled = disabled;
+}
+
#define QEMU_VIRTIO_PCI_QUEUE_MEM_MULT 0x1000
-static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy,
- int n, bool assign, bool set_handler)
+static int virtio_pci_ioeventfd_assign(DeviceState *d, EventNotifier *notifier,
+ int n, bool assign)
{
+ VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
VirtQueue *vq = virtio_get_queue(vdev, n);
- EventNotifier *notifier = virtio_queue_get_host_notifier(vq);
bool legacy = !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_LEGACY);
bool modern = !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_MODERN);
bool fast_mmio = kvm_ioeventfd_any_length_enabled();
@@ -280,16 +310,8 @@ static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy,
hwaddr modern_addr = QEMU_VIRTIO_PCI_QUEUE_MEM_MULT *
virtio_get_queue_index(vq);
hwaddr legacy_addr = VIRTIO_PCI_QUEUE_NOTIFY;
- int r = 0;
if (assign) {
- r = event_notifier_init(notifier, 1);
- if (r < 0) {
- error_report("%s: unable to init event notifier: %d",
- __func__, r);
- return r;
- }
- virtio_queue_set_host_notifier_fd_handler(vq, true, set_handler);
if (modern) {
if (fast_mmio) {
memory_region_add_eventfd(modern_mr, modern_addr, 0,
@@ -325,68 +347,18 @@ static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy,
memory_region_del_eventfd(legacy_mr, legacy_addr, 2,
true, n, notifier);
}
- virtio_queue_set_host_notifier_fd_handler(vq, false, false);
- event_notifier_cleanup(notifier);
}
- return r;
+ return 0;
}
static void virtio_pci_start_ioeventfd(VirtIOPCIProxy *proxy)
{
- VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
- int n, r;
-
- if (!(proxy->flags & VIRTIO_PCI_FLAG_USE_IOEVENTFD) ||
- proxy->ioeventfd_disabled ||
- proxy->ioeventfd_started) {
- return;
- }
-
- for (n = 0; n < VIRTIO_QUEUE_MAX; n++) {
- if (!virtio_queue_get_num(vdev, n)) {
- continue;
- }
-
- r = virtio_pci_set_host_notifier_internal(proxy, n, true, true);
- if (r < 0) {
- goto assign_error;
- }
- }
- proxy->ioeventfd_started = true;
- return;
-
-assign_error:
- while (--n >= 0) {
- if (!virtio_queue_get_num(vdev, n)) {
- continue;
- }
-
- r = virtio_pci_set_host_notifier_internal(proxy, n, false, false);
- assert(r >= 0);
- }
- proxy->ioeventfd_started = false;
- error_report("%s: failed. Fallback to a userspace (slower).", __func__);
+ virtio_bus_start_ioeventfd(&proxy->bus);
}
static void virtio_pci_stop_ioeventfd(VirtIOPCIProxy *proxy)
{
- VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
- int r;
- int n;
-
- if (!proxy->ioeventfd_started) {
- return;
- }
-
- for (n = 0; n < VIRTIO_QUEUE_MAX; n++) {
- if (!virtio_queue_get_num(vdev, n)) {
- continue;
- }
-
- r = virtio_pci_set_host_notifier_internal(proxy, n, false, false);
- assert(r >= 0);
- }
- proxy->ioeventfd_started = false;
+ virtio_bus_stop_ioeventfd(&proxy->bus);
}
static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
@@ -1110,24 +1082,6 @@ assign_error:
return r;
}
-static int virtio_pci_set_host_notifier(DeviceState *d, int n, bool assign)
-{
- VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
-
- /* Stop using ioeventfd for virtqueue kick if the device starts using host
- * notifiers. This makes it easy to avoid stepping on each others' toes.
- */
- proxy->ioeventfd_disabled = assign;
- if (assign) {
- virtio_pci_stop_ioeventfd(proxy);
- }
- /* We don't need to start here: it's not needed because backend
- * currently only stops on status change away from ok,
- * reset, vmstop and such. If we do add code to start here,
- * need to check vmstate, device state etc. */
- return virtio_pci_set_host_notifier_internal(proxy, n, assign, false);
-}
-
static void virtio_pci_vmstate_change(DeviceState *d, bool running)
{
VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
@@ -2488,12 +2442,16 @@ static void virtio_pci_bus_class_init(ObjectClass *klass, void *data)
k->load_extra_state = virtio_pci_load_extra_state;
k->has_extra_state = virtio_pci_has_extra_state;
k->query_guest_notifiers = virtio_pci_query_guest_notifiers;
- k->set_host_notifier = virtio_pci_set_host_notifier;
k->set_guest_notifiers = virtio_pci_set_guest_notifiers;
k->vmstate_change = virtio_pci_vmstate_change;
k->device_plugged = virtio_pci_device_plugged;
k->device_unplugged = virtio_pci_device_unplugged;
k->query_nvectors = virtio_pci_query_nvectors;
+ k->ioeventfd_started = virtio_pci_ioeventfd_started;
+ k->ioeventfd_set_started = virtio_pci_ioeventfd_set_started;
+ k->ioeventfd_disabled = virtio_pci_ioeventfd_disabled;
+ k->ioeventfd_set_disabled = virtio_pci_ioeventfd_set_disabled;
+ k->ioeventfd_assign = virtio_pci_ioeventfd_assign;
}
static const TypeInfo virtio_pci_bus_info = {