diff options
Diffstat (limited to 'hw')
560 files changed, 36675 insertions, 9573 deletions
diff --git a/hw/9pfs/9p-handle.c b/hw/9pfs/9p-handle.c index 894041488..3d77594f9 100644 --- a/hw/9pfs/9p-handle.c +++ b/hw/9pfs/9p-handle.c @@ -112,7 +112,7 @@ static int handle_close(FsContext *ctx, V9fsFidOpenState *fs) static int handle_closedir(FsContext *ctx, V9fsFidOpenState *fs) { - return closedir(fs->dir); + return closedir(fs->dir.stream); } static int handle_open(FsContext *ctx, V9fsPath *fs_path, @@ -132,8 +132,8 @@ static int handle_opendir(FsContext *ctx, if (ret < 0) { return -1; } - fs->dir = fdopendir(ret); - if (!fs->dir) { + fs->dir.stream = fdopendir(ret); + if (!fs->dir.stream) { return -1; } return 0; @@ -141,24 +141,22 @@ static int handle_opendir(FsContext *ctx, static void handle_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) { - rewinddir(fs->dir); + rewinddir(fs->dir.stream); } static off_t handle_telldir(FsContext *ctx, V9fsFidOpenState *fs) { - return telldir(fs->dir); + return telldir(fs->dir.stream); } -static int handle_readdir_r(FsContext *ctx, V9fsFidOpenState *fs, - struct dirent *entry, - struct dirent **result) +static struct dirent *handle_readdir(FsContext *ctx, V9fsFidOpenState *fs) { - return readdir_r(fs->dir, entry, result); + return readdir(fs->dir.stream); } static void handle_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) { - seekdir(fs->dir, off); + seekdir(fs->dir.stream, off); } static ssize_t handle_preadv(FsContext *ctx, V9fsFidOpenState *fs, @@ -262,7 +260,7 @@ static int handle_fstat(FsContext *fs_ctx, int fid_type, int fd; if (fid_type == P9_FID_DIR) { - fd = dirfd(fs->dir); + fd = dirfd(fs->dir.stream); } else { fd = fs->fd; } @@ -409,7 +407,7 @@ static int handle_fsync(FsContext *ctx, int fid_type, int fd; if (fid_type == P9_FID_DIR) { - fd = dirfd(fs->dir); + fd = dirfd(fs->dir.stream); } else { fd = fs->fd; } @@ -681,7 +679,7 @@ FileOperations handle_ops = { .opendir = handle_opendir, .rewinddir = handle_rewinddir, .telldir = handle_telldir, - .readdir_r = handle_readdir_r, + .readdir = handle_readdir, .seekdir = handle_seekdir, .preadv = handle_preadv, .pwritev = handle_pwritev, diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c index 16f45f485..3f271fcbd 100644 --- a/hw/9pfs/9p-local.c +++ b/hw/9pfs/9p-local.c @@ -348,7 +348,7 @@ static int local_close(FsContext *ctx, V9fsFidOpenState *fs) static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs) { - return closedir(fs->dir); + return closedir(fs->dir.stream); } static int local_open(FsContext *ctx, V9fsPath *fs_path, @@ -370,9 +370,9 @@ static int local_opendir(FsContext *ctx, char *path = fs_path->data; buffer = rpath(ctx, path); - fs->dir = opendir(buffer); + fs->dir.stream = opendir(buffer); g_free(buffer); - if (!fs->dir) { + if (!fs->dir.stream) { return -1; } return 0; @@ -380,38 +380,40 @@ static int local_opendir(FsContext *ctx, static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) { - rewinddir(fs->dir); + rewinddir(fs->dir.stream); } static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs) { - return telldir(fs->dir); + return telldir(fs->dir.stream); } -static int local_readdir_r(FsContext *ctx, V9fsFidOpenState *fs, - struct dirent *entry, - struct dirent **result) +static struct dirent *local_readdir(FsContext *ctx, V9fsFidOpenState *fs) { - int ret; + struct dirent *entry; again: - ret = readdir_r(fs->dir, entry, result); + entry = readdir(fs->dir.stream); + if (!entry) { + return NULL; + } + if (ctx->export_flags & V9FS_SM_MAPPED) { entry->d_type = DT_UNKNOWN; } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { - if (!ret && *result != NULL && - !strcmp(entry->d_name, VIRTFS_META_DIR)) { + if (!strcmp(entry->d_name, VIRTFS_META_DIR)) { /* skp the meta data directory */ goto again; } entry->d_type = DT_UNKNOWN; } - return ret; + + return entry; } static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) { - seekdir(fs->dir, off); + seekdir(fs->dir.stream, off); } static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs, @@ -610,7 +612,7 @@ static int local_fstat(FsContext *fs_ctx, int fid_type, int err, fd; if (fid_type == P9_FID_DIR) { - fd = dirfd(fs->dir); + fd = dirfd(fs->dir.stream); } else { fd = fs->fd; } @@ -998,7 +1000,7 @@ static int local_fsync(FsContext *ctx, int fid_type, int fd; if (fid_type == P9_FID_DIR) { - fd = dirfd(fs->dir); + fd = dirfd(fs->dir.stream); } else { fd = fs->fd; } @@ -1254,7 +1256,7 @@ FileOperations local_ops = { .opendir = local_opendir, .rewinddir = local_rewinddir, .telldir = local_telldir, - .readdir_r = local_readdir_r, + .readdir = local_readdir, .seekdir = local_seekdir, .preadv = local_preadv, .pwritev = local_pwritev, diff --git a/hw/9pfs/9p-proxy.c b/hw/9pfs/9p-proxy.c index 00a4eb2a7..f265501ea 100644 --- a/hw/9pfs/9p-proxy.c +++ b/hw/9pfs/9p-proxy.c @@ -633,7 +633,7 @@ static int proxy_close(FsContext *ctx, V9fsFidOpenState *fs) static int proxy_closedir(FsContext *ctx, V9fsFidOpenState *fs) { - return closedir(fs->dir); + return closedir(fs->dir.stream); } static int proxy_open(FsContext *ctx, V9fsPath *fs_path, @@ -652,14 +652,14 @@ static int proxy_opendir(FsContext *ctx, { int serrno, fd; - fs->dir = NULL; + fs->dir.stream = NULL; fd = v9fs_request(ctx->private, T_OPEN, NULL, "sd", fs_path, O_DIRECTORY); if (fd < 0) { errno = -fd; return -1; } - fs->dir = fdopendir(fd); - if (!fs->dir) { + fs->dir.stream = fdopendir(fd); + if (!fs->dir.stream) { serrno = errno; close(fd); errno = serrno; @@ -670,24 +670,22 @@ static int proxy_opendir(FsContext *ctx, static void proxy_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) { - rewinddir(fs->dir); + rewinddir(fs->dir.stream); } static off_t proxy_telldir(FsContext *ctx, V9fsFidOpenState *fs) { - return telldir(fs->dir); + return telldir(fs->dir.stream); } -static int proxy_readdir_r(FsContext *ctx, V9fsFidOpenState *fs, - struct dirent *entry, - struct dirent **result) +static struct dirent *proxy_readdir(FsContext *ctx, V9fsFidOpenState *fs) { - return readdir_r(fs->dir, entry, result); + return readdir(fs->dir.stream); } static void proxy_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) { - seekdir(fs->dir, off); + seekdir(fs->dir.stream, off); } static ssize_t proxy_preadv(FsContext *ctx, V9fsFidOpenState *fs, @@ -791,7 +789,7 @@ static int proxy_fstat(FsContext *fs_ctx, int fid_type, int fd; if (fid_type == P9_FID_DIR) { - fd = dirfd(fs->dir); + fd = dirfd(fs->dir.stream); } else { fd = fs->fd; } @@ -936,7 +934,7 @@ static int proxy_fsync(FsContext *ctx, int fid_type, int fd; if (fid_type == P9_FID_DIR) { - fd = dirfd(fs->dir); + fd = dirfd(fs->dir.stream); } else { fd = fs->fd; } @@ -1192,7 +1190,7 @@ FileOperations proxy_ops = { .opendir = proxy_opendir, .rewinddir = proxy_rewinddir, .telldir = proxy_telldir, - .readdir_r = proxy_readdir_r, + .readdir = proxy_readdir, .seekdir = proxy_seekdir, .preadv = proxy_preadv, .pwritev = proxy_pwritev, diff --git a/hw/9pfs/9p-proxy.h b/hw/9pfs/9p-proxy.h index ba9ca203d..b84301d00 100644 --- a/hw/9pfs/9p-proxy.h +++ b/hw/9pfs/9p-proxy.h @@ -9,8 +9,9 @@ * This work is licensed under the terms of the GNU GPL, version 2. See * the COPYING file in the top-level directory. */ -#ifndef _QEMU_9P_PROXY_H -#define _QEMU_9P_PROXY_H + +#ifndef QEMU_9P_PROXY_H +#define QEMU_9P_PROXY_H #define PROXY_MAX_IO_SZ (64 * 1024) #define V9FS_FD_VALID INT_MAX diff --git a/hw/9pfs/9p-synth.c b/hw/9pfs/9p-synth.c index f1475dfd6..4b6d4e6a3 100644 --- a/hw/9pfs/9p-synth.c +++ b/hw/9pfs/9p-synth.c @@ -1,5 +1,5 @@ /* - * Virtio 9p synthetic file system support + * 9p synthetic file system support * * Copyright IBM, Corp. 2011 * @@ -13,9 +13,7 @@ */ #include "qemu/osdep.h" -#include "hw/virtio/virtio.h" #include "9p.h" -#include "9p-xattr.h" #include "fsdev/qemu-fsdev.h" #include "9p-synth.h" #include "qemu/rcu.h" @@ -23,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, @@ -71,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; @@ -88,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, ".", @@ -96,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; } @@ -107,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; @@ -128,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; @@ -138,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; @@ -159,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; @@ -189,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; @@ -200,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); @@ -225,8 +223,8 @@ static void v9fs_synth_direntry(V9fsSynthNode *node, entry->d_off = off + 1; } -static int v9fs_synth_get_dentry(V9fsSynthNode *dir, struct dirent *entry, - struct dirent **result, off_t off) +static struct dirent *synth_get_dentry(V9fsSynthNode *dir, + struct dirent *entry, off_t off) { int i = 0; V9fsSynthNode *node; @@ -242,28 +240,25 @@ static int v9fs_synth_get_dentry(V9fsSynthNode *dir, struct dirent *entry, rcu_read_unlock(); if (!node) { /* end of directory */ - *result = NULL; - return 0; + return NULL; } - v9fs_synth_direntry(node, entry, off); - *result = entry; - return 0; + synth_direntry(node, entry, off); + return entry; } -static int v9fs_synth_readdir_r(FsContext *ctx, V9fsFidOpenState *fs, - struct dirent *entry, struct dirent **result) +static struct dirent *synth_readdir(FsContext *ctx, V9fsFidOpenState *fs) { - int ret; + struct dirent *entry; V9fsSynthOpenState *synth_open = fs->private; V9fsSynthNode *node = synth_open->node; - ret = v9fs_synth_get_dentry(node, entry, result, synth_open->offset); - if (!ret && *result != NULL) { + entry = synth_get_dentry(node, &synth_open->dent, synth_open->offset); + if (entry) { synth_open->offset++; } - return ret; + 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; @@ -276,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) { @@ -284,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; @@ -295,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) { @@ -319,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) { @@ -343,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) { @@ -456,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; @@ -476,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; } @@ -505,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) { @@ -513,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_r = v9fs_synth_readdir_r, - .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/9pfs/9p-synth.h b/hw/9pfs/9p-synth.h index 82962512a..6bcb44ace 100644 --- a/hw/9pfs/9p-synth.h +++ b/hw/9pfs/9p-synth.h @@ -10,9 +10,9 @@ * the COPYING file in the top-level directory. * */ -#ifndef HW_9PFS_SYNTH_H -#define HW_9PFS_SYNTH_H 1 +#ifndef QEMU_9P_SYNTH_H +#define QEMU_9P_SYNTH_H typedef struct V9fsSynthNode V9fsSynthNode; typedef ssize_t (*v9fs_synth_read)(void *buf, int len, off_t offset, @@ -40,6 +40,7 @@ struct V9fsSynthNode { typedef struct V9fsSynthOpenState { off_t offset; V9fsSynthNode *node; + struct dirent dent; } V9fsSynthOpenState; extern int qemu_v9fs_synth_mkdir(V9fsSynthNode *parent, int mode, diff --git a/hw/9pfs/9p-xattr.h b/hw/9pfs/9p-xattr.h index 4d39a2026..a853ea641 100644 --- a/hw/9pfs/9p-xattr.h +++ b/hw/9pfs/9p-xattr.h @@ -10,8 +10,9 @@ * the COPYING file in the top-level directory. * */ -#ifndef _QEMU_9P_XATTR_H -#define _QEMU_9P_XATTR_H + +#ifndef QEMU_9P_XATTR_H +#define QEMU_9P_XATTR_H #include "qemu/xattr.h" diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index f5e30125f..dfe293d11 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -13,7 +13,6 @@ #include "qemu/osdep.h" #include "hw/virtio/virtio.h" -#include "hw/i386/pc.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/iov.h" @@ -232,7 +231,7 @@ static int v9fs_reopen_fid(V9fsPDU *pdu, V9fsFidState *f) } while (err == -EINTR && !pdu->cancelled); } } else if (f->fid_type == P9_FID_DIR) { - if (f->fs.dir == NULL) { + if (f->fs.dir.stream == NULL) { do { err = v9fs_co_opendir(pdu, f); } while (err == -EINTR && !pdu->cancelled); @@ -301,6 +300,9 @@ static V9fsFidState *alloc_fid(V9fsState *s, int32_t fid) f->next = s->fid_list; s->fid_list = f; + v9fs_readdir_init(&f->fs.dir); + v9fs_readdir_init(&f->fs_reclaim.dir); + return f; } @@ -346,7 +348,7 @@ static int free_fid(V9fsPDU *pdu, V9fsFidState *fidp) retval = v9fs_co_close(pdu, &fidp->fs); } } else if (fidp->fid_type == P9_FID_DIR) { - if (fidp->fs.dir != NULL) { + if (fidp->fs.dir.stream != NULL) { retval = v9fs_co_closedir(pdu, &fidp->fs); } } else if (fidp->fid_type == P9_FID_XATTR) { @@ -444,7 +446,7 @@ void v9fs_reclaim_fd(V9fsPDU *pdu) reclaim_count++; } } else if (f->fid_type == P9_FID_DIR) { - if (f->fs.dir != NULL) { + if (f->fs.dir.stream != NULL) { /* * Up the reference count so that * a clunk request won't free this fid @@ -452,8 +454,8 @@ void v9fs_reclaim_fd(V9fsPDU *pdu) f->ref++; f->rclm_lst = reclaim_list; reclaim_list = f; - f->fs_reclaim.dir = f->fs.dir; - f->fs.dir = NULL; + f->fs_reclaim.dir.stream = f->fs.dir.stream; + f->fs.dir.stream = NULL; reclaim_count++; } } @@ -1008,6 +1010,7 @@ static void v9fs_attach(void *opaque) goto out; } err += offset; + memcpy(&s->root_qid, &qid, sizeof(qid)); trace_v9fs_attach_return(pdu->tag, pdu->id, qid.type, qid.version, qid.path); /* @@ -1254,6 +1257,19 @@ static int v9fs_walk_marshal(V9fsPDU *pdu, uint16_t nwnames, V9fsQID *qids) return offset; } +static bool name_is_illegal(const char *name) +{ + return !*name || strchr(name, '/') != NULL; +} + +static bool not_same_qid(const V9fsQID *qid1, const V9fsQID *qid2) +{ + return + qid1->type != qid2->type || + qid1->version != qid2->version || + qid1->path != qid2->path; +} + static void v9fs_walk(void *opaque) { int name_idx; @@ -1269,6 +1285,7 @@ static void v9fs_walk(void *opaque) V9fsFidState *newfidp = NULL; V9fsPDU *pdu = opaque; V9fsState *s = pdu->s; + V9fsQID qid; err = pdu_unmarshal(pdu, offset, "ddw", &fid, &newfid, &nwnames); if (err < 0) { @@ -1287,6 +1304,10 @@ static void v9fs_walk(void *opaque) if (err < 0) { goto out_nofid; } + if (name_is_illegal(wnames[i].data)) { + err = -ENOENT; + goto out_nofid; + } offset += err; } } else if (nwnames > P9_MAXWELEM) { @@ -1298,6 +1319,12 @@ static void v9fs_walk(void *opaque) err = -ENOENT; goto out_nofid; } + + err = fid_to_qid(pdu, fidp, &qid); + if (err < 0) { + goto out; + } + v9fs_path_init(&dpath); v9fs_path_init(&path); /* @@ -1307,16 +1334,22 @@ static void v9fs_walk(void *opaque) v9fs_path_copy(&dpath, &fidp->path); v9fs_path_copy(&path, &fidp->path); for (name_idx = 0; name_idx < nwnames; name_idx++) { - err = v9fs_co_name_to_path(pdu, &dpath, wnames[name_idx].data, &path); - if (err < 0) { - goto out; - } - err = v9fs_co_lstat(pdu, &path, &stbuf); - if (err < 0) { - goto out; + if (not_same_qid(&pdu->s->root_qid, &qid) || + strcmp("..", wnames[name_idx].data)) { + err = v9fs_co_name_to_path(pdu, &dpath, wnames[name_idx].data, + &path); + if (err < 0) { + goto out; + } + + err = v9fs_co_lstat(pdu, &path, &stbuf); + if (err < 0) { + goto out; + } + stat_to_qid(&stbuf, &qid); + v9fs_path_copy(&dpath, &path); } - stat_to_qid(&stbuf, &qids[name_idx]); - v9fs_path_copy(&dpath, &path); + memcpy(&qids[name_idx], &qid, sizeof(qid)); } if (fid == newfid) { BUG_ON(fidp->fid_type != P9_FID_NONE); @@ -1481,6 +1514,16 @@ static void v9fs_lcreate(void *opaque) } trace_v9fs_lcreate(pdu->tag, pdu->id, dfid, flags, mode, gid); + if (name_is_illegal(name.data)) { + err = -ENOENT; + goto out_nofid; + } + + if (!strcmp(".", name.data) || !strcmp("..", name.data)) { + err = -EEXIST; + goto out_nofid; + } + fidp = get_fid(pdu, dfid); if (fidp == NULL) { err = -ENOENT; @@ -1625,7 +1668,7 @@ static int v9fs_do_readdir_with_stat(V9fsPDU *pdu, int32_t count = 0; struct stat stbuf; off_t saved_dir_pos; - struct dirent *dent, *result; + struct dirent *dent; /* save the directory position */ saved_dir_pos = v9fs_co_telldir(pdu, fidp); @@ -1633,34 +1676,37 @@ static int v9fs_do_readdir_with_stat(V9fsPDU *pdu, return saved_dir_pos; } - dent = g_malloc(sizeof(struct dirent)); - while (1) { v9fs_path_init(&path); - err = v9fs_co_readdir_r(pdu, fidp, dent, &result); - if (err || !result) { + + v9fs_readdir_lock(&fidp->fs.dir); + + err = v9fs_co_readdir(pdu, fidp, &dent); + if (err || !dent) { break; } err = v9fs_co_name_to_path(pdu, &fidp->path, dent->d_name, &path); if (err < 0) { - goto out; + break; } err = v9fs_co_lstat(pdu, &path, &stbuf); if (err < 0) { - goto out; + break; } err = stat_to_v9stat(pdu, &path, &stbuf, &v9stat); if (err < 0) { - goto out; + break; } /* 11 = 7 + 4 (7 = start offset, 4 = space for storing count) */ len = pdu_marshal(pdu, 11 + count, "S", &v9stat); + + v9fs_readdir_unlock(&fidp->fs.dir); + if ((len != (v9stat.size + 2)) || ((count + len) > max_count)) { /* Ran out of buffer. Set dir back to old position and return */ v9fs_co_seekdir(pdu, fidp, saved_dir_pos); v9fs_stat_free(&v9stat); v9fs_path_free(&path); - g_free(dent); return count; } count += len; @@ -1668,8 +1714,9 @@ static int v9fs_do_readdir_with_stat(V9fsPDU *pdu, v9fs_path_free(&path); saved_dir_pos = dent->d_off; } -out: - g_free(dent); + + v9fs_readdir_unlock(&fidp->fs.dir); + v9fs_path_free(&path); if (err < 0) { return err; @@ -1805,7 +1852,7 @@ static int v9fs_do_readdir(V9fsPDU *pdu, int len, err = 0; int32_t count = 0; off_t saved_dir_pos; - struct dirent *dent, *result; + struct dirent *dent; /* save the directory position */ saved_dir_pos = v9fs_co_telldir(pdu, fidp); @@ -1813,20 +1860,21 @@ static int v9fs_do_readdir(V9fsPDU *pdu, return saved_dir_pos; } - dent = g_malloc(sizeof(struct dirent)); - while (1) { - err = v9fs_co_readdir_r(pdu, fidp, dent, &result); - if (err || !result) { + v9fs_readdir_lock(&fidp->fs.dir); + + err = v9fs_co_readdir(pdu, fidp, &dent); + if (err || !dent) { break; } v9fs_string_init(&name); v9fs_string_sprintf(&name, "%s", dent->d_name); if ((count + v9fs_readdir_data_size(&name)) > max_count) { + v9fs_readdir_unlock(&fidp->fs.dir); + /* Ran out of buffer. Set dir back to old position and return */ v9fs_co_seekdir(pdu, fidp, saved_dir_pos); v9fs_string_free(&name); - g_free(dent); return count; } /* @@ -1844,17 +1892,21 @@ static int v9fs_do_readdir(V9fsPDU *pdu, len = pdu_marshal(pdu, 11 + count, "Qqbs", &qid, dent->d_off, dent->d_type, &name); + + v9fs_readdir_unlock(&fidp->fs.dir); + if (len < 0) { v9fs_co_seekdir(pdu, fidp, saved_dir_pos); v9fs_string_free(&name); - g_free(dent); return len; } count += len; v9fs_string_free(&name); saved_dir_pos = dent->d_off; } - g_free(dent); + + v9fs_readdir_unlock(&fidp->fs.dir); + if (err < 0) { return err; } @@ -1884,7 +1936,7 @@ static void v9fs_readdir(void *opaque) retval = -EINVAL; goto out_nofid; } - if (!fidp->fs.dir) { + if (!fidp->fs.dir.stream) { retval = -EINVAL; goto out; } @@ -2066,6 +2118,16 @@ static void v9fs_create(void *opaque) } trace_v9fs_create(pdu->tag, pdu->id, fid, name.data, perm, mode); + if (name_is_illegal(name.data)) { + err = -ENOENT; + goto out_nofid; + } + + if (!strcmp(".", name.data) || !strcmp("..", name.data)) { + err = -EEXIST; + goto out_nofid; + } + fidp = get_fid(pdu, fid); if (fidp == NULL) { err = -EINVAL; @@ -2231,6 +2293,16 @@ static void v9fs_symlink(void *opaque) } trace_v9fs_symlink(pdu->tag, pdu->id, dfid, name.data, symname.data, gid); + if (name_is_illegal(name.data)) { + err = -ENOENT; + goto out_nofid; + } + + if (!strcmp(".", name.data) || !strcmp("..", name.data)) { + err = -EEXIST; + goto out_nofid; + } + dfidp = get_fid(pdu, dfid); if (dfidp == NULL) { err = -EINVAL; @@ -2305,6 +2377,16 @@ static void v9fs_link(void *opaque) } trace_v9fs_link(pdu->tag, pdu->id, dfid, oldfid, name.data); + if (name_is_illegal(name.data)) { + err = -ENOENT; + goto out_nofid; + } + + if (!strcmp(".", name.data) || !strcmp("..", name.data)) { + err = -EEXIST; + goto out_nofid; + } + dfidp = get_fid(pdu, dfid); if (dfidp == NULL) { err = -ENOENT; @@ -2387,6 +2469,22 @@ static void v9fs_unlinkat(void *opaque) if (err < 0) { goto out_nofid; } + + if (name_is_illegal(name.data)) { + err = -ENOENT; + goto out_nofid; + } + + if (!strcmp(".", name.data)) { + err = -EINVAL; + goto out_nofid; + } + + if (!strcmp("..", name.data)) { + err = -ENOTEMPTY; + goto out_nofid; + } + dfidp = get_fid(pdu, dfid); if (dfidp == NULL) { err = -EINVAL; @@ -2493,6 +2591,17 @@ static void v9fs_rename(void *opaque) if (err < 0) { goto out_nofid; } + + if (name_is_illegal(name.data)) { + err = -ENOENT; + goto out_nofid; + } + + if (!strcmp(".", name.data) || !strcmp("..", name.data)) { + err = -EISDIR; + goto out_nofid; + } + fidp = get_fid(pdu, fid); if (fidp == NULL) { err = -ENOENT; @@ -2605,6 +2714,17 @@ static void v9fs_renameat(void *opaque) goto out_err; } + if (name_is_illegal(old_name.data) || name_is_illegal(new_name.data)) { + err = -ENOENT; + goto out_err; + } + + if (!strcmp(".", old_name.data) || !strcmp("..", old_name.data) || + !strcmp(".", new_name.data) || !strcmp("..", new_name.data)) { + err = -EISDIR; + goto out_err; + } + v9fs_path_write_lock(s); err = v9fs_complete_renameat(pdu, olddirfid, &old_name, newdirfid, &new_name); @@ -2815,6 +2935,16 @@ static void v9fs_mknod(void *opaque) } trace_v9fs_mknod(pdu->tag, pdu->id, fid, mode, major, minor); + if (name_is_illegal(name.data)) { + err = -ENOENT; + goto out_nofid; + } + + if (!strcmp(".", name.data) || !strcmp("..", name.data)) { + err = -EEXIST; + goto out_nofid; + } + fidp = get_fid(pdu, fid); if (fidp == NULL) { err = -ENOENT; @@ -2966,6 +3096,16 @@ static void v9fs_mkdir(void *opaque) } trace_v9fs_mkdir(pdu->tag, pdu->id, fid, name.data, mode, gid); + if (name_is_illegal(name.data)) { + err = -ENOENT; + goto out_nofid; + } + + if (!strcmp(".", name.data) || !strcmp("..", name.data)) { + err = -EEXIST; + goto out_nofid; + } + fidp = get_fid(pdu, fid); if (fidp == NULL) { err = -ENOENT; @@ -3267,8 +3407,8 @@ void pdu_submit(V9fsPDU *pdu) if (is_ro_export(&s->ctx) && !is_read_only_op(pdu)) { handler = v9fs_fs_ro; } - co = qemu_coroutine_create(handler); - qemu_coroutine_enter(co, pdu); + co = qemu_coroutine_create(handler, pdu); + qemu_coroutine_enter(co); } /* Returns 0 on success, 1 on failure. */ diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h index 1a19418a8..a38603398 100644 --- a/hw/9pfs/9p.h +++ b/hw/9pfs/9p.h @@ -1,12 +1,9 @@ -#ifndef _QEMU_9P_H -#define _QEMU_9P_H +#ifndef QEMU_9P_H +#define QEMU_9P_H #include <dirent.h> #include <utime.h> #include <sys/resource.h> -#include <glib.h> -#include "standard-headers/linux/virtio_9p.h" -#include "hw/virtio/virtio.h" #include "fsdev/file-op-9p.h" #include "fsdev/9p-iov-marshal.h" #include "qemu/thread.h" @@ -169,13 +166,33 @@ typedef struct V9fsXattr int flags; } V9fsXattr; +typedef struct V9fsDir { + DIR *stream; + QemuMutex readdir_mutex; +} V9fsDir; + +static inline void v9fs_readdir_lock(V9fsDir *dir) +{ + qemu_mutex_lock(&dir->readdir_mutex); +} + +static inline void v9fs_readdir_unlock(V9fsDir *dir) +{ + qemu_mutex_unlock(&dir->readdir_mutex); +} + +static inline void v9fs_readdir_init(V9fsDir *dir) +{ + qemu_mutex_init(&dir->readdir_mutex); +} + /* * Filled by fs driver on open and other * calls. */ union V9fsFidOpenState { int fd; - DIR *dir; + V9fsDir dir; V9fsXattr xattr; /* * private pointer for fs drivers, that @@ -219,6 +236,7 @@ typedef struct V9fsState int32_t root_fid; Error *migration_blocker; V9fsConf fsconf; + V9fsQID root_qid; } V9fsState; /* 9p2000.L open flags */ diff --git a/hw/9pfs/codir.c b/hw/9pfs/codir.c index 91df7f7a7..d91f9ad6e 100644 --- a/hw/9pfs/codir.c +++ b/hw/9pfs/codir.c @@ -1,6 +1,5 @@ - /* - * Virtio 9p backend + * 9p backend * * Copyright IBM, Corp. 2011 * @@ -18,8 +17,7 @@ #include "qemu/coroutine.h" #include "coth.h" -int v9fs_co_readdir_r(V9fsPDU *pdu, V9fsFidState *fidp, struct dirent *dent, - struct dirent **result) +int v9fs_co_readdir(V9fsPDU *pdu, V9fsFidState *fidp, struct dirent **dent) { int err; V9fsState *s = pdu->s; @@ -29,11 +27,14 @@ int v9fs_co_readdir_r(V9fsPDU *pdu, V9fsFidState *fidp, struct dirent *dent, } v9fs_co_run_in_worker( { + struct dirent *entry; + errno = 0; - err = s->ops->readdir_r(&s->ctx, &fidp->fs, dent, result); - if (!*result && errno) { + entry = s->ops->readdir(&s->ctx, &fidp->fs); + if (!entry && errno) { err = -errno; } else { + *dent = entry; err = 0; } }); diff --git a/hw/9pfs/cofile.c b/hw/9pfs/cofile.c index 293483e0c..10343c0a9 100644 --- a/hw/9pfs/cofile.c +++ b/hw/9pfs/cofile.c @@ -1,6 +1,5 @@ - /* - * Virtio 9p backend + * 9p backend * * Copyright IBM, Corp. 2011 * diff --git a/hw/9pfs/cofs.c b/hw/9pfs/cofs.c index 18c81cb3d..70f584fcb 100644 --- a/hw/9pfs/cofs.c +++ b/hw/9pfs/cofs.c @@ -1,6 +1,5 @@ - /* - * Virtio 9p backend + * 9p backend * * Copyright IBM, Corp. 2011 * diff --git a/hw/9pfs/coth.c b/hw/9pfs/coth.c index 464293ef2..89018de6b 100644 --- a/hw/9pfs/coth.c +++ b/hw/9pfs/coth.c @@ -16,21 +16,20 @@ #include "qemu-common.h" #include "block/thread-pool.h" #include "qemu/coroutine.h" -#include "qemu/main-loop.h" #include "coth.h" /* Called from QEMU I/O thread. */ static void coroutine_enter_cb(void *opaque, int ret) { Coroutine *co = opaque; - qemu_coroutine_enter(co, NULL); + qemu_coroutine_enter(co); } /* Called from worker thread. */ static int coroutine_enter_func(void *arg) { Coroutine *co = arg; - qemu_coroutine_enter(co, NULL); + qemu_coroutine_enter(co); return 0; } diff --git a/hw/9pfs/coth.h b/hw/9pfs/coth.h index 209fc6a9a..3c7424e42 100644 --- a/hw/9pfs/coth.h +++ b/hw/9pfs/coth.h @@ -12,12 +12,13 @@ * */ -#ifndef _QEMU_9P_COTH_H -#define _QEMU_9P_COTH_H +#ifndef QEMU_9P_COTH_H +#define QEMU_9P_COTH_H #include "qemu/thread.h" #include "qemu/coroutine.h" -#include "virtio-9p.h" +#include "qemu/main-loop.h" +#include "9p.h" /* * we want to use bottom half because we want to make sure the below @@ -47,10 +48,8 @@ } while (0) extern void co_run_in_worker_bh(void *); -extern int v9fs_init_worker_threads(void); extern int v9fs_co_readlink(V9fsPDU *, V9fsPath *, V9fsString *); -extern int v9fs_co_readdir_r(V9fsPDU *, V9fsFidState *, - struct dirent *, struct dirent **result); +extern int v9fs_co_readdir(V9fsPDU *, V9fsFidState *, struct dirent **); extern off_t v9fs_co_telldir(V9fsPDU *, V9fsFidState *); extern void v9fs_co_seekdir(V9fsPDU *, V9fsFidState *, off_t); extern void v9fs_co_rewinddir(V9fsPDU *, V9fsFidState *); diff --git a/hw/9pfs/coxattr.c b/hw/9pfs/coxattr.c index 6ad96ea9f..133c4ead3 100644 --- a/hw/9pfs/coxattr.c +++ b/hw/9pfs/coxattr.c @@ -1,6 +1,5 @@ - /* - * Virtio 9p backend + * 9p backend * * Copyright IBM, Corp. 2011 * diff --git a/hw/9pfs/trace-events b/hw/9pfs/trace-events new file mode 100644 index 000000000..48d3d8abe --- /dev/null +++ b/hw/9pfs/trace-events @@ -0,0 +1,47 @@ +# See docs/tracing.txt for syntax documentation. + +# hw/9pfs/virtio-9p.c +v9fs_rerror(uint16_t tag, uint8_t id, int err) "tag %d id %d err %d" +v9fs_version(uint16_t tag, uint8_t id, int32_t msize, char* version) "tag %d id %d msize %d version %s" +v9fs_version_return(uint16_t tag, uint8_t id, int32_t msize, char* version) "tag %d id %d msize %d version %s" +v9fs_attach(uint16_t tag, uint8_t id, int32_t fid, int32_t afid, char* uname, char* aname) "tag %u id %u fid %d afid %d uname %s aname %s" +v9fs_attach_return(uint16_t tag, uint8_t id, int8_t type, int32_t version, int64_t path) "tag %d id %d type %d version %d path %"PRId64 +v9fs_stat(uint16_t tag, uint8_t id, int32_t fid) "tag %d id %d fid %d" +v9fs_stat_return(uint16_t tag, uint8_t id, int32_t mode, int32_t atime, int32_t mtime, int64_t length) "tag %d id %d stat={mode %d atime %d mtime %d length %"PRId64"}" +v9fs_getattr(uint16_t tag, uint8_t id, int32_t fid, uint64_t request_mask) "tag %d id %d fid %d request_mask %"PRIu64 +v9fs_getattr_return(uint16_t tag, uint8_t id, uint64_t result_mask, uint32_t mode, uint32_t uid, uint32_t gid) "tag %d id %d getattr={result_mask %"PRId64" mode %u uid %u gid %u}" +v9fs_walk(uint16_t tag, uint8_t id, int32_t fid, int32_t newfid, uint16_t nwnames) "tag %d id %d fid %d newfid %d nwnames %d" +v9fs_walk_return(uint16_t tag, uint8_t id, uint16_t nwnames, void* qids) "tag %d id %d nwnames %d qids %p" +v9fs_open(uint16_t tag, uint8_t id, int32_t fid, int32_t mode) "tag %d id %d fid %d mode %d" +v9fs_open_return(uint16_t tag, uint8_t id, int8_t type, int32_t version, int64_t path, int iounit) "tag %d id %d qid={type %d version %d path %"PRId64"} iounit %d" +v9fs_lcreate(uint16_t tag, uint8_t id, int32_t dfid, int32_t flags, int32_t mode, uint32_t gid) "tag %d id %d dfid %d flags %d mode %d gid %u" +v9fs_lcreate_return(uint16_t tag, uint8_t id, int8_t type, int32_t version, int64_t path, int32_t iounit) "tag %d id %d qid={type %d version %d path %"PRId64"} iounit %d" +v9fs_fsync(uint16_t tag, uint8_t id, int32_t fid, int datasync) "tag %d id %d fid %d datasync %d" +v9fs_clunk(uint16_t tag, uint8_t id, int32_t fid) "tag %d id %d fid %d" +v9fs_read(uint16_t tag, uint8_t id, int32_t fid, uint64_t off, uint32_t max_count) "tag %d id %d fid %d off %"PRIu64" max_count %u" +v9fs_read_return(uint16_t tag, uint8_t id, int32_t count, ssize_t err) "tag %d id %d count %d err %zd" +v9fs_readdir(uint16_t tag, uint8_t id, int32_t fid, uint64_t offset, uint32_t max_count) "tag %d id %d fid %d offset %"PRIu64" max_count %u" +v9fs_readdir_return(uint16_t tag, uint8_t id, uint32_t count, ssize_t retval) "tag %d id %d count %u retval %zd" +v9fs_write(uint16_t tag, uint8_t id, int32_t fid, uint64_t off, uint32_t count, int cnt) "tag %d id %d fid %d off %"PRIu64" count %u cnt %d" +v9fs_write_return(uint16_t tag, uint8_t id, int32_t total, ssize_t err) "tag %d id %d total %d err %zd" +v9fs_create(uint16_t tag, uint8_t id, int32_t fid, char* name, int32_t perm, int8_t mode) "tag %d id %d fid %d name %s perm %d mode %d" +v9fs_create_return(uint16_t tag, uint8_t id, int8_t type, int32_t version, int64_t path, int iounit) "tag %d id %d qid={type %d version %d path %"PRId64"} iounit %d" +v9fs_symlink(uint16_t tag, uint8_t id, int32_t fid, char* name, char* symname, uint32_t gid) "tag %d id %d fid %d name %s symname %s gid %u" +v9fs_symlink_return(uint16_t tag, uint8_t id, int8_t type, int32_t version, int64_t path) "tag %d id %d qid={type %d version %d path %"PRId64"}" +v9fs_flush(uint16_t tag, uint8_t id, int16_t flush_tag) "tag %d id %d flush_tag %d" +v9fs_link(uint16_t tag, uint8_t id, int32_t dfid, int32_t oldfid, char* name) "tag %d id %d dfid %d oldfid %d name %s" +v9fs_remove(uint16_t tag, uint8_t id, int32_t fid) "tag %d id %d fid %d" +v9fs_wstat(uint16_t tag, uint8_t id, int32_t fid, int32_t mode, int32_t atime, int32_t mtime) "tag %u id %u fid %d stat={mode %d atime %d mtime %d}" +v9fs_mknod(uint16_t tag, uint8_t id, int32_t fid, int mode, int major, int minor) "tag %d id %d fid %d mode %d major %d minor %d" +v9fs_mknod_return(uint16_t tag, uint8_t id, int8_t type, int32_t version, int64_t path) "tag %d id %d qid={type %d version %d path %"PRId64"}" +v9fs_lock(uint16_t tag, uint8_t id, int32_t fid, uint8_t type, uint64_t start, uint64_t length) "tag %d id %d fid %d type %d start %"PRIu64" length %"PRIu64 +v9fs_lock_return(uint16_t tag, uint8_t id, int8_t status) "tag %d id %d status %d" +v9fs_getlock(uint16_t tag, uint8_t id, int32_t fid, uint8_t type, uint64_t start, uint64_t length)"tag %d id %d fid %d type %d start %"PRIu64" length %"PRIu64 +v9fs_getlock_return(uint16_t tag, uint8_t id, uint8_t type, uint64_t start, uint64_t length, uint32_t proc_id) "tag %d id %d type %d start %"PRIu64" length %"PRIu64" proc_id %u" +v9fs_mkdir(uint16_t tag, uint8_t id, int32_t fid, char* name, int mode, uint32_t gid) "tag %u id %u fid %d name %s mode %d gid %u" +v9fs_mkdir_return(uint16_t tag, uint8_t id, int8_t type, int32_t version, int64_t path, int err) "tag %u id %u qid={type %d version %d path %"PRId64"} err %d" +v9fs_xattrwalk(uint16_t tag, uint8_t id, int32_t fid, int32_t newfid, char* name) "tag %d id %d fid %d newfid %d name %s" +v9fs_xattrwalk_return(uint16_t tag, uint8_t id, int64_t size) "tag %d id %d size %"PRId64 +v9fs_xattrcreate(uint16_t tag, uint8_t id, int32_t fid, char* name, int64_t size, int flags) "tag %d id %d fid %d name %s size %"PRId64" flags %d" +v9fs_readlink(uint16_t tag, uint8_t id, int32_t fid) "tag %d id %d fid %d" +v9fs_readlink_return(uint16_t tag, uint8_t id, char* target) "tag %d id %d name %s" diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c index a38850ee8..009b43f6d 100644 --- a/hw/9pfs/virtio-9p-device.c +++ b/hw/9pfs/virtio-9p-device.c @@ -13,11 +13,9 @@ #include "qemu/osdep.h" #include "hw/virtio/virtio.h" -#include "hw/i386/pc.h" #include "qemu/sockets.h" #include "virtio-9p.h" #include "fsdev/qemu-fsdev.h" -#include "9p-xattr.h" #include "coth.h" #include "hw/virtio/virtio-access.h" #include "qemu/iov.h" @@ -99,14 +97,9 @@ static void virtio_9p_get_config(VirtIODevice *vdev, uint8_t *config) g_free(cfg); } -static void virtio_9p_save(QEMUFile *f, void *opaque) +static int virtio_9p_load(QEMUFile *f, void *opaque, size_t size) { - virtio_save(VIRTIO_DEVICE(opaque), f); -} - -static int virtio_9p_load(QEMUFile *f, void *opaque, int version_id) -{ - return virtio_load(VIRTIO_DEVICE(opaque), f, version_id); + return virtio_load(VIRTIO_DEVICE(opaque), f, 1); } static void virtio_9p_device_realize(DeviceState *dev, Error **errp) @@ -122,7 +115,6 @@ static void virtio_9p_device_realize(DeviceState *dev, Error **errp) v->config_size = sizeof(struct virtio_9p_config) + strlen(s->fsconf.tag); virtio_init(vdev, "virtio-9p", VIRTIO_ID_9P, v->config_size); v->vq = virtio_add_queue(vdev, MAX_REQ, handle_9p_output); - register_savevm(dev, "virtio-9p", -1, 1, virtio_9p_save, virtio_9p_load, v); out: return; @@ -135,7 +127,6 @@ static void virtio_9p_device_unrealize(DeviceState *dev, Error **errp) V9fsState *s = &v->state; virtio_cleanup(vdev); - unregister_savevm(dev, "virtio-9p", v); v9fs_device_unrealize_common(s, errp); } @@ -177,6 +168,8 @@ void virtio_init_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov, /* virtio-9p device */ +VMSTATE_VIRTIO_DEVICE(9p, 1, virtio_9p_load, virtio_vmstate_save); + static Property virtio_9p_properties[] = { DEFINE_PROP_STRING("mount_tag", V9fsVirtioState, state.fsconf.tag), DEFINE_PROP_STRING("fsdev", V9fsVirtioState, state.fsconf.fsdev_id), @@ -189,6 +182,7 @@ static void virtio_9p_class_init(ObjectClass *klass, void *data) VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); dc->props = virtio_9p_properties; + dc->vmsd = &vmstate_virtio_9p; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); vdc->realize = virtio_9p_device_realize; vdc->unrealize = virtio_9p_device_unrealize; diff --git a/hw/9pfs/virtio-9p.h b/hw/9pfs/virtio-9p.h index 7f6d88553..7586b792d 100644 --- a/hw/9pfs/virtio-9p.h +++ b/hw/9pfs/virtio-9p.h @@ -1,5 +1,5 @@ -#ifndef _QEMU_VIRTIO_9P_H -#define _QEMU_VIRTIO_9P_H +#ifndef QEMU_VIRTIO_9P_H +#define QEMU_VIRTIO_9P_H #include "standard-headers/linux/virtio_9p.h" #include "hw/virtio/virtio.h" diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs index faee86c5c..4b7da6639 100644 --- a/hw/acpi/Makefile.objs +++ b/hw/acpi/Makefile.objs @@ -1,8 +1,10 @@ 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 cpu_hotplug_acpi_table.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/acpi_interface.c b/hw/acpi/acpi_interface.c index d82131326..6583917b8 100644 --- a/hw/acpi/acpi_interface.c +++ b/hw/acpi/acpi_interface.c @@ -2,6 +2,15 @@ #include "hw/acpi/acpi_dev_interface.h" #include "qemu/module.h" +void acpi_send_event(DeviceState *dev, AcpiEventStatusBits event) +{ + AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_GET_CLASS(dev); + if (adevc->send_event) { + AcpiDeviceIf *adev = ACPI_DEVICE_IF(dev); + adevc->send_event(adev, event); + } +} + static void register_types(void) { static const TypeInfo acpi_dev_if_info = { diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index ab89ca638..db3e914fb 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -24,7 +24,6 @@ #include "hw/acpi/aml-build.h" #include "qemu/bswap.h" #include "qemu/bitops.h" -#include "hw/acpi/bios-linker-loader.h" static GArray *build_alloc_array(void) { @@ -325,12 +324,9 @@ static void aml_free(gpointer data, gpointer user_data) Aml *init_aml_allocator(void) { - Aml *var; - assert(!alloc_list); alloc_list = g_ptr_array_new(); - var = aml_alloc(); - return var; + return aml_alloc(); } void free_aml_allocator(void) @@ -406,6 +402,15 @@ Aml *aml_return(Aml *val) return var; } +/* ACPI 1.0b: 16.2.6.3 Debug Objects Encoding: DebugObj */ +Aml *aml_debug(void) +{ + Aml *var = aml_alloc(); + build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */ + build_append_byte(var->buf, 0x31); /* DebugOp */ + return var; +} + /* * ACPI 1.0b: 16.2.3 Data Objects Encoding: * encodes: ByteConst, WordConst, DWordConst, QWordConst, ZeroOp, OneOp @@ -443,12 +448,10 @@ Aml *aml_name_decl(const char *name, Aml *val) /* ACPI 1.0b: 16.2.6.1 Arg Objects Encoding */ Aml *aml_arg(int pos) { - Aml *var; uint8_t op = 0x68 /* ARG0 op */ + pos; assert(pos <= 6); - var = aml_opcode(op); - return var; + return aml_opcode(op); } /* ACPI 2.0a: 17.2.4.4 Type 2 Opcodes Encoding: DefToInteger */ @@ -657,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 @@ -1074,12 +1091,10 @@ Aml *aml_string(const char *name_format, ...) /* ACPI 1.0b: 16.2.6.2 Local Objects Encoding */ Aml *aml_local(int num) { - Aml *var; uint8_t op = 0x60 /* Local0Op */ + num; assert(num <= 7); - var = aml_opcode(op); - return var; + return aml_opcode(op); } /* ACPI 2.0a: 17.2.2 Data Objects Encoding: DefVarPackage */ @@ -1407,6 +1422,14 @@ Aml *aml_unicode(const char *str) return var; } +/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefRefOf */ +Aml *aml_refof(Aml *arg) +{ + Aml *var = aml_opcode(0x71 /* RefOfOp */); + aml_append(var, arg); + return var; +} + /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefDerefOf */ Aml *aml_derefof(Aml *arg) { @@ -1472,11 +1495,21 @@ 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(GArray *linker, GArray *table_data, +build_header(BIOSLinker *linker, GArray *table_data, AcpiTableHeader *h, const char *sig, int len, uint8_t rev, const char *oem_id, const char *oem_table_id) { + unsigned tbl_offset = (char *)h - table_data->data; + unsigned checksum_offset = (char *)&h->checksum - table_data->data; memcpy(&h->signature, sig, 4); h->length = cpu_to_le32(len); h->revision = rev; @@ -1497,10 +1530,9 @@ build_header(GArray *linker, GArray *table_data, h->oem_revision = cpu_to_le32(1); memcpy(h->asl_compiler_id, ACPI_BUILD_APPNAME4, 4); h->asl_compiler_revision = cpu_to_le32(1); - h->checksum = 0; /* Checksum to be filled in by Guest linker */ bios_linker_loader_add_checksum(linker, ACPI_BUILD_TABLE_FILE, - table_data, h, len, &h->checksum); + tbl_offset, len, checksum_offset); } void *acpi_data_push(GArray *table_data, unsigned size) @@ -1518,7 +1550,7 @@ unsigned acpi_data_len(GArray *table) void acpi_add_table(GArray *table_offsets, GArray *table_data) { - uint32_t offset = cpu_to_le32(table_data->len); + uint32_t offset = table_data->len; g_array_append_val(table_offsets, offset); } @@ -1532,8 +1564,7 @@ void acpi_build_tables_init(AcpiBuildTables *tables) void acpi_build_tables_cleanup(AcpiBuildTables *tables, bool mfre) { - void *linker_data = bios_linker_loader_cleanup(tables->linker); - g_free(linker_data); + bios_linker_loader_cleanup(tables->linker); g_array_free(tables->rsdp, true); g_array_free(tables->table_data, true); g_array_free(tables->tcpalog, mfre); @@ -1541,25 +1572,38 @@ void acpi_build_tables_cleanup(AcpiBuildTables *tables, bool mfre) /* Build rsdt table */ void -build_rsdt(GArray *table_data, GArray *linker, GArray *table_offsets, +build_rsdt(GArray *table_data, BIOSLinker *linker, GArray *table_offsets, const char *oem_id, const char *oem_table_id) { - AcpiRsdtDescriptorRev1 *rsdt; - size_t rsdt_len; int i; - const int table_data_len = (sizeof(uint32_t) * table_offsets->len); + unsigned rsdt_entries_offset; + AcpiRsdtDescriptorRev1 *rsdt; + const unsigned table_data_len = (sizeof(uint32_t) * table_offsets->len); + const unsigned rsdt_entry_size = sizeof(rsdt->table_offset_entry[0]); + const size_t rsdt_len = sizeof(*rsdt) + table_data_len; - rsdt_len = sizeof(*rsdt) + table_data_len; rsdt = acpi_data_push(table_data, rsdt_len); - memcpy(rsdt->table_offset_entry, table_offsets->data, table_data_len); + rsdt_entries_offset = (char *)rsdt->table_offset_entry - table_data->data; for (i = 0; i < table_offsets->len; ++i) { + uint32_t ref_tbl_offset = g_array_index(table_offsets, uint32_t, i); + uint32_t rsdt_entry_offset = rsdt_entries_offset + rsdt_entry_size * i; + /* rsdt->table_offset_entry to be filled by Guest linker */ bios_linker_loader_add_pointer(linker, - ACPI_BUILD_TABLE_FILE, - ACPI_BUILD_TABLE_FILE, - table_data, &rsdt->table_offset_entry[i], - sizeof(uint32_t)); + ACPI_BUILD_TABLE_FILE, rsdt_entry_offset, rsdt_entry_size, + ACPI_BUILD_TABLE_FILE, ref_tbl_offset); } build_header(linker, table_data, (void *)rsdt, "RSDT", rsdt_len, 1, oem_id, oem_table_id); } + +void build_srat_memory(AcpiSratMemoryAffinity *numamem, uint64_t base, + uint64_t len, int node, MemoryAffinityFlags flags) +{ + numamem->type = ACPI_SRAT_MEMORY; + numamem->length = sizeof(*numamem); + numamem->proximity = cpu_to_le32(node); + numamem->flags = cpu_to_le32(flags); + numamem->base_addr = cpu_to_le64(base); + numamem->range_length = cpu_to_le64(len); +} diff --git a/hw/acpi/bios-linker-loader.c b/hw/acpi/bios-linker-loader.c index 5153ab151..d963ebe24 100644 --- a/hw/acpi/bios-linker-loader.c +++ b/hw/acpi/bios-linker-loader.c @@ -96,134 +96,170 @@ enum { }; /* - * bios_linker_loader_init: allocate a new linker file blob array. + * BiosLinkerFileEntry: + * + * An internal type used for book-keeping file entries + */ +typedef struct BiosLinkerFileEntry { + char *name; /* file name */ + GArray *blob; /* data accosiated with @name */ +} BiosLinkerFileEntry; + +/* + * bios_linker_loader_init: allocate a new linker object instance. * * After initialization, linker commands can be added, and will - * be stored in the array. + * be stored in the linker.cmd_blob array. */ -GArray *bios_linker_loader_init(void) +BIOSLinker *bios_linker_loader_init(void) { - return g_array_new(false, true /* clear */, 1); + BIOSLinker *linker = g_new(BIOSLinker, 1); + + linker->cmd_blob = g_array_new(false, true /* clear */, 1); + linker->file_list = g_array_new(false, true /* clear */, + sizeof(BiosLinkerFileEntry)); + return linker; } -/* Free linker wrapper and return the linker array. */ -void *bios_linker_loader_cleanup(GArray *linker) +/* Free linker wrapper */ +void bios_linker_loader_cleanup(BIOSLinker *linker) { - return g_array_free(linker, false); + int i; + BiosLinkerFileEntry *entry; + + g_array_free(linker->cmd_blob, true); + + for (i = 0; i < linker->file_list->len; i++) { + entry = &g_array_index(linker->file_list, BiosLinkerFileEntry, i); + g_free(entry->name); + } + g_array_free(linker->file_list, true); + g_free(linker); +} + +static const BiosLinkerFileEntry * +bios_linker_find_file(const BIOSLinker *linker, const char *name) +{ + int i; + BiosLinkerFileEntry *entry; + + for (i = 0; i < linker->file_list->len; i++) { + entry = &g_array_index(linker->file_list, BiosLinkerFileEntry, i); + if (!strcmp(entry->name, name)) { + return entry; + } + } + return NULL; } /* * bios_linker_loader_alloc: ask guest to load file into guest memory. * - * @linker: linker file blob array - * @file: file to be loaded + * @linker: linker object instance + * @file_name: name of the file blob to be loaded + * @file_blob: pointer to blob corresponding to @file_name * @alloc_align: required minimal alignment in bytes. Must be a power of 2. * @alloc_fseg: request allocation in FSEG zone (useful for the RSDP ACPI table) * * Note: this command must precede any other linker command using this file. */ -void bios_linker_loader_alloc(GArray *linker, - const char *file, +void bios_linker_loader_alloc(BIOSLinker *linker, + const char *file_name, + GArray *file_blob, uint32_t alloc_align, bool alloc_fseg) { BiosLinkerLoaderEntry entry; + BiosLinkerFileEntry file = { g_strdup(file_name), file_blob}; assert(!(alloc_align & (alloc_align - 1))); + assert(!bios_linker_find_file(linker, file_name)); + g_array_append_val(linker->file_list, file); + memset(&entry, 0, sizeof entry); - strncpy(entry.alloc.file, file, sizeof entry.alloc.file - 1); + strncpy(entry.alloc.file, file_name, sizeof entry.alloc.file - 1); entry.command = cpu_to_le32(BIOS_LINKER_LOADER_COMMAND_ALLOCATE); entry.alloc.align = cpu_to_le32(alloc_align); entry.alloc.zone = alloc_fseg ? BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG : BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH; /* Alloc entries must come first, so prepend them */ - g_array_prepend_vals(linker, &entry, sizeof entry); + g_array_prepend_vals(linker->cmd_blob, &entry, sizeof entry); } /* - * bios_linker_loader_add_checksum: ask guest to add checksum of file data - * into (same) file at the specified pointer. + * bios_linker_loader_add_checksum: ask guest to add checksum of ACPI + * table in the specified file at the specified offset. * * Checksum calculation simply sums -X for each byte X in the range * using 8-bit math (i.e. ACPI checksum). * - * @linker: linker file blob array + * @linker: linker object instance * @file: file that includes the checksum to be calculated * and the data to be checksummed - * @table: @file blob contents - * @start, @size: range of data to checksum - * @checksum: location of the checksum to be patched within file blob - * - * Notes: - * - checksum byte initial value must have been pushed into @table - * and reside at address @checksum. - * - @size bytes must have been pushed into @table and reside at address - * @start. - * - Guest calculates checksum of specified range of data, result is added to - * initial value at @checksum into copy of @file in Guest memory. - * - Range might include the checksum itself. - * - To avoid confusion, caller must always put 0x0 at @checksum. - * - @file must be loaded into Guest memory using bios_linker_loader_alloc + * @start_offset, @size: range of data in the file to checksum, + * relative to the start of file blob + * @checksum_offset: location of the checksum to be patched within file blob, + * relative to the start of file blob */ -void bios_linker_loader_add_checksum(GArray *linker, const char *file, - GArray *table, - void *start, unsigned size, - uint8_t *checksum) +void bios_linker_loader_add_checksum(BIOSLinker *linker, const char *file_name, + unsigned start_offset, unsigned size, + unsigned checksum_offset) { BiosLinkerLoaderEntry entry; - ptrdiff_t checksum_offset = (gchar *)checksum - table->data; - ptrdiff_t start_offset = (gchar *)start - table->data; + const BiosLinkerFileEntry *file = bios_linker_find_file(linker, file_name); - assert(checksum_offset >= 0); - assert(start_offset >= 0); - assert(checksum_offset + 1 <= table->len); - assert(start_offset + size <= table->len); - assert(*checksum == 0x0); + assert(file); + assert(start_offset < file->blob->len); + assert(start_offset + size <= file->blob->len); + assert(checksum_offset >= start_offset); + assert(checksum_offset + 1 <= start_offset + size); + *(file->blob->data + checksum_offset) = 0; memset(&entry, 0, sizeof entry); - strncpy(entry.cksum.file, file, sizeof entry.cksum.file - 1); + strncpy(entry.cksum.file, file_name, sizeof entry.cksum.file - 1); entry.command = cpu_to_le32(BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM); entry.cksum.offset = cpu_to_le32(checksum_offset); entry.cksum.start = cpu_to_le32(start_offset); entry.cksum.length = cpu_to_le32(size); - g_array_append_vals(linker, &entry, sizeof entry); + g_array_append_vals(linker->cmd_blob, &entry, sizeof entry); } /* - * bios_linker_loader_add_pointer: ask guest to add address of source file - * into destination file at the specified pointer. + * bios_linker_loader_add_pointer: ask guest to patch address in + * destination file with a pointer to source file * - * @linker: linker file blob array + * @linker: linker object instance * @dest_file: destination file that must be changed + * @dst_patched_offset: location within destination file blob to be patched + * with the pointer to @src_file+@src_offset (i.e. source + * blob allocated in guest memory + @src_offset), in bytes + * @dst_patched_offset_size: size of the pointer to be patched + * at @dst_patched_offset in @dest_file blob, in bytes * @src_file: source file who's address must be taken - * @table: @dest_file blob contents array - * @pointer: location of the pointer to be patched within destination file blob - * @pointer_size: size of pointer to be patched, in bytes - * - * Notes: - * - @pointer_size bytes must have been pushed into @table - * and reside at address @pointer. - * - Guest address is added to initial value at @pointer - * into copy of @dest_file in Guest memory. - * e.g. to get start of src_file in guest memory, put 0x0 there - * to get address of a field at offset 0x10 in src_file, put 0x10 there - * - Both @dest_file and @src_file must be - * loaded into Guest memory using bios_linker_loader_alloc + * @src_offset: location within source file blob to which + * @dest_file+@dst_patched_offset will point to after + * firmware's executed ADD_POINTER command */ -void bios_linker_loader_add_pointer(GArray *linker, +void bios_linker_loader_add_pointer(BIOSLinker *linker, const char *dest_file, + uint32_t dst_patched_offset, + uint8_t dst_patched_size, const char *src_file, - GArray *table, void *pointer, - uint8_t pointer_size) + uint32_t src_offset) { + uint64_t le_src_offset; BiosLinkerLoaderEntry entry; - ptrdiff_t offset = (gchar *)pointer - table->data; + const BiosLinkerFileEntry *dst_file = + bios_linker_find_file(linker, dest_file); + const BiosLinkerFileEntry *source_file = + bios_linker_find_file(linker, src_file); - assert(offset >= 0); - assert(offset + pointer_size <= table->len); + assert(dst_patched_offset < dst_file->blob->len); + assert(dst_patched_offset + dst_patched_size <= dst_file->blob->len); + assert(src_offset < source_file->blob->len); memset(&entry, 0, sizeof entry); strncpy(entry.pointer.dest_file, dest_file, @@ -231,10 +267,14 @@ void bios_linker_loader_add_pointer(GArray *linker, strncpy(entry.pointer.src_file, src_file, sizeof entry.pointer.src_file - 1); entry.command = cpu_to_le32(BIOS_LINKER_LOADER_COMMAND_ADD_POINTER); - entry.pointer.offset = cpu_to_le32(offset); - entry.pointer.size = pointer_size; - assert(pointer_size == 1 || pointer_size == 2 || - pointer_size == 4 || pointer_size == 8); + entry.pointer.offset = cpu_to_le32(dst_patched_offset); + entry.pointer.size = dst_patched_size; + assert(dst_patched_size == 1 || dst_patched_size == 2 || + dst_patched_size == 4 || dst_patched_size == 8); + + le_src_offset = cpu_to_le64(src_offset); + memcpy(dst_file->blob->data + dst_patched_offset, + &le_src_offset, dst_patched_size); - g_array_append_vals(linker, &entry, sizeof entry); + g_array_append_vals(linker->cmd_blob, &entry, sizeof entry); } diff --git a/hw/acpi/core.c b/hw/acpi/core.c index 6a2f45214..e890a5d67 100644 --- a/hw/acpi/core.c +++ b/hw/acpi/core.c @@ -239,11 +239,11 @@ void acpi_table_add(const QemuOpts *opts, Error **errp) char unsigned *blob = NULL; { - OptsVisitor *ov; + Visitor *v; - ov = opts_visitor_new(opts); - visit_type_AcpiTableOptions(opts_get_visitor(ov), NULL, &hdrs, &err); - opts_visitor_cleanup(ov); + v = opts_visitor_new(opts); + visit_type_AcpiTableOptions(v, NULL, &hdrs, &err); + visit_free(v); } if (err) { @@ -491,6 +491,12 @@ void acpi_pm_tmr_update(ACPIREGS *ar, bool enable) } } +static inline int64_t acpi_pm_tmr_get_clock(void) +{ + return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), PM_TIMER_FREQUENCY, + NANOSECONDS_PER_SECOND); +} + void acpi_pm_tmr_calc_overflow_time(ACPIREGS *ar) { int64_t d = acpi_pm_tmr_get_clock(); @@ -692,7 +698,7 @@ uint32_t acpi_gpe_ioport_readb(ACPIREGS *ar, uint32_t addr) } void acpi_send_gpe_event(ACPIREGS *ar, qemu_irq irq, - AcpiGPEStatusBits status) + AcpiEventStatusBits status) { ar->gpe.sts[0] |= status; acpi_update_sci(ar, irq); diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c new file mode 100644 index 000000000..c13b65c2c --- /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 4d86743fd..e19d90206 100644 --- a/hw/acpi/cpu_hotplug.c +++ b/hw/acpi/cpu_hotplug.c @@ -14,6 +14,14 @@ #include "hw/acpi/cpu_hotplug.h" #include "qapi/error.h" #include "qom/cpu.h" +#include "hw/i386/pc.h" + +#define CPU_EJECT_METHOD "CPEJ" +#define CPU_MAT_METHOD "CPMA" +#define CPU_ON_BITMAP "CPON" +#define CPU_STATUS_METHOD "CPST" +#define CPU_STATUS_MAP "PRS" +#define CPU_SCAN_METHOD "PRSC" static uint64_t cpu_status_read(void *opaque, hwaddr addr, unsigned int size) { @@ -26,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 = { @@ -54,19 +70,18 @@ static void acpi_set_cpu_present_bit(AcpiCpuHotplug *g, CPUState *cpu, g->sts[cpu_id / 8] |= (1 << (cpu_id % 8)); } -void acpi_cpu_plug_cb(ACPIREGS *ar, qemu_irq irq, - AcpiCpuHotplug *g, DeviceState *dev, Error **errp) +void legacy_acpi_cpu_plug_cb(HotplugHandler *hotplug_dev, + AcpiCpuHotplug *g, DeviceState *dev, Error **errp) { acpi_set_cpu_present_bit(g, CPU(dev), errp); if (*errp != NULL) { return; } - - acpi_send_gpe_event(ar, irq, ACPI_CPU_HOTPLUG_STATUS); + acpi_send_event(DEVICE(hotplug_dev), ACPI_CPU_HOTPLUG_STATUS); } -void acpi_cpu_hotplug_init(MemoryRegion *parent, Object *owner, - AcpiCpuHotplug *gpe_cpu, uint16_t base) +void legacy_acpi_cpu_hotplug_init(MemoryRegion *parent, Object *owner, + AcpiCpuHotplug *gpe_cpu, uint16_t base) { CPUState *cpu; @@ -76,4 +91,242 @@ void 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, + uint16_t io_base) +{ + Aml *dev; + Aml *crs; + Aml *pkg; + Aml *field; + Aml *method; + Aml *if_ctx; + Aml *else_ctx; + int i, apic_idx; + Aml *sb_scope = aml_scope("_SB"); + uint8_t madt_tmpl[8] = {0x00, 0x08, 0x00, 0x00, 0x00, 0, 0, 0}; + Aml *cpu_id = aml_arg(1); + Aml *apic_id = aml_arg(0); + Aml *cpu_on = aml_local(0); + Aml *madt = aml_local(1); + Aml *cpus_map = aml_name(CPU_ON_BITMAP); + Aml *zero = aml_int(0); + Aml *one = aml_int(1); + MachineClass *mc = MACHINE_GET_CLASS(machine); + CPUArchIdList *apic_ids = mc->possible_cpu_arch_ids(machine); + PCMachineState *pcms = PC_MACHINE(machine); + + /* + * _MAT method - creates an madt apic buffer + * apic_id = Arg0 = Local APIC ID + * cpu_id = Arg1 = Processor ID + * cpu_on = Local0 = CPON flag for this cpu + * madt = Local1 = Buffer (in madt apic form) to return + */ + method = aml_method(CPU_MAT_METHOD, 2, AML_NOTSERIALIZED); + aml_append(method, + aml_store(aml_derefof(aml_index(cpus_map, apic_id)), cpu_on)); + aml_append(method, + aml_store(aml_buffer(sizeof(madt_tmpl), madt_tmpl), madt)); + /* Update the processor id, lapic id, and enable/disable status */ + aml_append(method, aml_store(cpu_id, aml_index(madt, aml_int(2)))); + aml_append(method, aml_store(apic_id, aml_index(madt, aml_int(3)))); + aml_append(method, aml_store(cpu_on, aml_index(madt, aml_int(4)))); + aml_append(method, aml_return(madt)); + aml_append(sb_scope, method); + + /* + * _STA method - return ON status of cpu + * apic_id = Arg0 = Local APIC ID + * cpu_on = Local0 = CPON flag for this cpu + */ + method = aml_method(CPU_STATUS_METHOD, 1, AML_NOTSERIALIZED); + aml_append(method, + aml_store(aml_derefof(aml_index(cpus_map, apic_id)), cpu_on)); + if_ctx = aml_if(cpu_on); + { + aml_append(if_ctx, aml_return(aml_int(0xF))); + } + aml_append(method, if_ctx); + else_ctx = aml_else(); + { + aml_append(else_ctx, aml_return(zero)); + } + aml_append(method, else_ctx); + aml_append(sb_scope, method); + + method = aml_method(CPU_EJECT_METHOD, 2, AML_NOTSERIALIZED); + aml_append(method, aml_sleep(200)); + aml_append(sb_scope, method); + + method = aml_method(CPU_SCAN_METHOD, 0, AML_NOTSERIALIZED); + { + Aml *while_ctx, *if_ctx2, *else_ctx2; + Aml *bus_check_evt = aml_int(1); + Aml *remove_evt = aml_int(3); + Aml *status_map = aml_local(5); /* Local5 = active cpu bitmap */ + Aml *byte = aml_local(2); /* Local2 = last read byte from bitmap */ + Aml *idx = aml_local(0); /* Processor ID / APIC ID iterator */ + Aml *is_cpu_on = aml_local(1); /* Local1 = CPON flag for cpu */ + Aml *status = aml_local(3); /* Local3 = active state for cpu */ + + aml_append(method, aml_store(aml_name(CPU_STATUS_MAP), status_map)); + aml_append(method, aml_store(zero, byte)); + aml_append(method, aml_store(zero, idx)); + + /* While (idx < SizeOf(CPON)) */ + while_ctx = aml_while(aml_lless(idx, aml_sizeof(cpus_map))); + aml_append(while_ctx, + aml_store(aml_derefof(aml_index(cpus_map, idx)), is_cpu_on)); + + if_ctx = aml_if(aml_and(idx, aml_int(0x07), NULL)); + { + /* Shift down previously read bitmap byte */ + aml_append(if_ctx, aml_shiftright(byte, one, byte)); + } + aml_append(while_ctx, if_ctx); + + else_ctx = aml_else(); + { + /* Read next byte from cpu bitmap */ + aml_append(else_ctx, aml_store(aml_derefof(aml_index(status_map, + aml_shiftright(idx, aml_int(3), NULL))), byte)); + } + aml_append(while_ctx, else_ctx); + + aml_append(while_ctx, aml_store(aml_and(byte, one, NULL), status)); + if_ctx = aml_if(aml_lnot(aml_equal(is_cpu_on, status))); + { + /* State change - update CPON with new state */ + aml_append(if_ctx, aml_store(status, aml_index(cpus_map, idx))); + if_ctx2 = aml_if(aml_equal(status, one)); + { + aml_append(if_ctx2, + aml_call2(AML_NOTIFY_METHOD, idx, bus_check_evt)); + } + aml_append(if_ctx, if_ctx2); + else_ctx2 = aml_else(); + { + aml_append(else_ctx2, + aml_call2(AML_NOTIFY_METHOD, idx, remove_evt)); + } + } + aml_append(if_ctx, else_ctx2); + aml_append(while_ctx, if_ctx); + + aml_append(while_ctx, aml_increment(idx)); /* go to next cpu */ + aml_append(method, while_ctx); + } + aml_append(sb_scope, method); + + /* The current AML generator can cover the APIC ID range [0..255], + * inclusive, for VCPU hotplug. */ + QEMU_BUILD_BUG_ON(ACPI_CPU_HOTPLUG_ID_LIMIT > 256); + g_assert(pcms->apic_id_limit <= ACPI_CPU_HOTPLUG_ID_LIMIT); + + /* create PCI0.PRES device and its _CRS to reserve CPU hotplug MMIO */ + dev = aml_device("PCI0." stringify(CPU_HOTPLUG_RESOURCE_DEVICE)); + aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A06"))); + aml_append(dev, + aml_name_decl("_UID", aml_string("CPU Hotplug resources")) + ); + /* device present, functioning, decoding, not shown in UI */ + aml_append(dev, aml_name_decl("_STA", aml_int(0xB))); + crs = aml_resource_template(); + aml_append(crs, + aml_io(AML_DECODE16, io_base, io_base, 1, ACPI_GPE_PROC_LEN) + ); + aml_append(dev, aml_name_decl("_CRS", crs)); + aml_append(sb_scope, dev); + /* declare CPU hotplug MMIO region and PRS field to access it */ + aml_append(sb_scope, aml_operation_region( + "PRST", AML_SYSTEM_IO, aml_int(io_base), ACPI_GPE_PROC_LEN)); + field = aml_field("PRST", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE); + aml_append(field, aml_named_field("PRS", 256)); + aml_append(sb_scope, field); + + /* build Processor object for each processor */ + for (i = 0; i < apic_ids->len; i++) { + int apic_id = apic_ids->cpus[i].arch_id; + + assert(apic_id < ACPI_CPU_HOTPLUG_ID_LIMIT); + + dev = aml_processor(i, 0, 0, "CP%.02X", apic_id); + + method = aml_method("_MAT", 0, AML_NOTSERIALIZED); + aml_append(method, + aml_return(aml_call2(CPU_MAT_METHOD, aml_int(apic_id), aml_int(i)) + )); + aml_append(dev, method); + + method = aml_method("_STA", 0, AML_NOTSERIALIZED); + aml_append(method, + aml_return(aml_call1(CPU_STATUS_METHOD, aml_int(apic_id)))); + aml_append(dev, method); + + method = aml_method("_EJ0", 1, AML_NOTSERIALIZED); + aml_append(method, + aml_return(aml_call2(CPU_EJECT_METHOD, aml_int(apic_id), + aml_arg(0))) + ); + aml_append(dev, method); + + aml_append(sb_scope, dev); + } + + /* build this code: + * Method(NTFY, 2) {If (LEqual(Arg0, 0x00)) {Notify(CP00, Arg1)} ...} + */ + /* Arg0 = APIC ID */ + method = aml_method(AML_NOTIFY_METHOD, 2, AML_NOTSERIALIZED); + for (i = 0; i < apic_ids->len; i++) { + int apic_id = apic_ids->cpus[i].arch_id; + + if_ctx = aml_if(aml_equal(aml_arg(0), aml_int(apic_id))); + aml_append(if_ctx, + aml_notify(aml_name("CP%.02X", apic_id), aml_arg(1)) + ); + aml_append(method, if_ctx); + } + aml_append(sb_scope, method); + + /* build "Name(CPON, Package() { One, One, ..., Zero, Zero, ... })" + * + * Note: The ability to create variable-sized packages was first + * introduced in ACPI 2.0. ACPI 1.0 only allowed fixed-size packages + * ith up to 255 elements. Windows guests up to win2k8 fail when + * VarPackageOp is used. + */ + pkg = pcms->apic_id_limit <= 255 ? aml_package(pcms->apic_id_limit) : + aml_varpackage(pcms->apic_id_limit); + + for (i = 0, apic_idx = 0; i < apic_ids->len; i++) { + int apic_id = apic_ids->cpus[i].arch_id; + + for (; apic_idx < apic_id; apic_idx++) { + aml_append(pkg, aml_int(0)); + } + aml_append(pkg, aml_int(apic_ids->cpus[i].cpu ? 1 : 0)); + apic_idx = apic_id + 1; + } + aml_append(sb_scope, aml_name_decl(CPU_ON_BITMAP, pkg)); + g_free(apic_ids); + + aml_append(ctx, sb_scope); + + method = aml_method("\\_GPE._E02", 0, AML_NOTSERIALIZED); + aml_append(method, aml_call0("\\_SB." CPU_SCAN_METHOD)); + aml_append(ctx, method); } diff --git a/hw/acpi/cpu_hotplug_acpi_table.c b/hw/acpi/cpu_hotplug_acpi_table.c deleted file mode 100644 index 97bb1092a..000000000 --- a/hw/acpi/cpu_hotplug_acpi_table.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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 "hw/acpi/cpu_hotplug.h" - -void build_cpu_hotplug_aml(Aml *ctx) -{ - Aml *method; - Aml *if_ctx; - Aml *else_ctx; - Aml *sb_scope = aml_scope("_SB"); - uint8_t madt_tmpl[8] = {0x00, 0x08, 0x00, 0x00, 0x00, 0, 0, 0}; - Aml *cpu_id = aml_arg(0); - Aml *cpu_on = aml_local(0); - Aml *madt = aml_local(1); - Aml *cpus_map = aml_name(CPU_ON_BITMAP); - Aml *zero = aml_int(0); - Aml *one = aml_int(1); - - /* - * _MAT method - creates an madt apic buffer - * cpu_id = Arg0 = Processor ID = Local APIC ID - * cpu_on = Local0 = CPON flag for this cpu - * madt = Local1 = Buffer (in madt apic form) to return - */ - method = aml_method(CPU_MAT_METHOD, 1, AML_NOTSERIALIZED); - aml_append(method, - aml_store(aml_derefof(aml_index(cpus_map, cpu_id)), cpu_on)); - aml_append(method, - aml_store(aml_buffer(sizeof(madt_tmpl), madt_tmpl), madt)); - /* Update the processor id, lapic id, and enable/disable status */ - aml_append(method, aml_store(cpu_id, aml_index(madt, aml_int(2)))); - aml_append(method, aml_store(cpu_id, aml_index(madt, aml_int(3)))); - aml_append(method, aml_store(cpu_on, aml_index(madt, aml_int(4)))); - aml_append(method, aml_return(madt)); - aml_append(sb_scope, method); - - /* - * _STA method - return ON status of cpu - * cpu_id = Arg0 = Processor ID = Local APIC ID - * cpu_on = Local0 = CPON flag for this cpu - */ - method = aml_method(CPU_STATUS_METHOD, 1, AML_NOTSERIALIZED); - aml_append(method, - aml_store(aml_derefof(aml_index(cpus_map, cpu_id)), cpu_on)); - if_ctx = aml_if(cpu_on); - { - aml_append(if_ctx, aml_return(aml_int(0xF))); - } - aml_append(method, if_ctx); - else_ctx = aml_else(); - { - aml_append(else_ctx, aml_return(zero)); - } - aml_append(method, else_ctx); - aml_append(sb_scope, method); - - method = aml_method(CPU_EJECT_METHOD, 2, AML_NOTSERIALIZED); - aml_append(method, aml_sleep(200)); - aml_append(sb_scope, method); - - method = aml_method(CPU_SCAN_METHOD, 0, AML_NOTSERIALIZED); - { - Aml *while_ctx, *if_ctx2, *else_ctx2; - Aml *bus_check_evt = aml_int(1); - Aml *remove_evt = aml_int(3); - Aml *status_map = aml_local(5); /* Local5 = active cpu bitmap */ - Aml *byte = aml_local(2); /* Local2 = last read byte from bitmap */ - Aml *idx = aml_local(0); /* Processor ID / APIC ID iterator */ - Aml *is_cpu_on = aml_local(1); /* Local1 = CPON flag for cpu */ - Aml *status = aml_local(3); /* Local3 = active state for cpu */ - - aml_append(method, aml_store(aml_name(CPU_STATUS_MAP), status_map)); - aml_append(method, aml_store(zero, byte)); - aml_append(method, aml_store(zero, idx)); - - /* While (idx < SizeOf(CPON)) */ - while_ctx = aml_while(aml_lless(idx, aml_sizeof(cpus_map))); - aml_append(while_ctx, - aml_store(aml_derefof(aml_index(cpus_map, idx)), is_cpu_on)); - - if_ctx = aml_if(aml_and(idx, aml_int(0x07), NULL)); - { - /* Shift down previously read bitmap byte */ - aml_append(if_ctx, aml_shiftright(byte, one, byte)); - } - aml_append(while_ctx, if_ctx); - - else_ctx = aml_else(); - { - /* Read next byte from cpu bitmap */ - aml_append(else_ctx, aml_store(aml_derefof(aml_index(status_map, - aml_shiftright(idx, aml_int(3), NULL))), byte)); - } - aml_append(while_ctx, else_ctx); - - aml_append(while_ctx, aml_store(aml_and(byte, one, NULL), status)); - if_ctx = aml_if(aml_lnot(aml_equal(is_cpu_on, status))); - { - /* State change - update CPON with new state */ - aml_append(if_ctx, aml_store(status, aml_index(cpus_map, idx))); - if_ctx2 = aml_if(aml_equal(status, one)); - { - aml_append(if_ctx2, - aml_call2(AML_NOTIFY_METHOD, idx, bus_check_evt)); - } - aml_append(if_ctx, if_ctx2); - else_ctx2 = aml_else(); - { - aml_append(else_ctx2, - aml_call2(AML_NOTIFY_METHOD, idx, remove_evt)); - } - } - aml_append(if_ctx, else_ctx2); - aml_append(while_ctx, if_ctx); - - aml_append(while_ctx, aml_increment(idx)); /* go to next cpu */ - aml_append(method, while_ctx); - } - aml_append(sb_scope, method); - - aml_append(ctx, sb_scope); -} diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index 27e978f5f..e5a3c18e5 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 } }; @@ -273,8 +301,8 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, pm->powerdown_notifier.notify = pm_powerdown_req; qemu_register_powerdown_notifier(&pm->powerdown_notifier); - acpi_cpu_hotplug_init(pci_address_space_io(lpc_pci), OBJECT(lpc_pci), - &pm->gpe_cpu, ICH9_CPU_HOTPLUG_IO_BASE); + legacy_acpi_cpu_hotplug_init(pci_address_space_io(lpc_pci), + OBJECT(lpc_pci), &pm->gpe_cpu, ICH9_CPU_HOTPLUG_IO_BASE); if (pm->acpi_memory_hotplug.is_enabled) { acpi_memory_hotplug_init(pci_address_space_io(lpc_pci), OBJECT(lpc_pci), @@ -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, @@ -430,39 +483,58 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp) NULL); } -void ich9_pm_device_plug_cb(ICH9LPCPMRegs *pm, DeviceState *dev, Error **errp) +void ich9_pm_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) { - if (pm->acpi_memory_hotplug.is_enabled && + ICH9LPCState *lpc = ICH9_LPC_DEVICE(hotplug_dev); + + if (lpc->pm.acpi_memory_hotplug.is_enabled && object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { - acpi_memory_plug_cb(&pm->acpi_regs, pm->irq, &pm->acpi_memory_hotplug, + acpi_memory_plug_cb(hotplug_dev, &lpc->pm.acpi_memory_hotplug, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { - acpi_cpu_plug_cb(&pm->acpi_regs, pm->irq, &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))); } } -void ich9_pm_device_unplug_request_cb(ICH9LPCPMRegs *pm, DeviceState *dev, - Error **errp) +void ich9_pm_device_unplug_request_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { - if (pm->acpi_memory_hotplug.is_enabled && + ICH9LPCState *lpc = ICH9_LPC_DEVICE(hotplug_dev); + + if (lpc->pm.acpi_memory_hotplug.is_enabled && object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { - acpi_memory_unplug_request_cb(&pm->acpi_regs, pm->irq, - &pm->acpi_memory_hotplug, dev, errp); + 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))); } } -void ich9_pm_device_unplug_cb(ICH9LPCPMRegs *pm, DeviceState *dev, +void ich9_pm_device_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - if (pm->acpi_memory_hotplug.is_enabled && + ICH9LPCState *lpc = ICH9_LPC_DEVICE(hotplug_dev); + + if (lpc->pm.acpi_memory_hotplug.is_enabled && object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { - acpi_memory_unplug_cb(&pm->acpi_memory_hotplug, dev, errp); + 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))); @@ -474,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 000000000..7e74ce446 --- /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/memory_hotplug.c b/hw/acpi/memory_hotplug.c index f65a3a21e..ec4e64b36 100644 --- a/hw/acpi/memory_hotplug.c +++ b/hw/acpi/memory_hotplug.c @@ -228,7 +228,7 @@ acpi_memory_slot_status(MemHotplugState *mem_st, return &mem_st->devs[slot]; } -void acpi_memory_plug_cb(ACPIREGS *ar, qemu_irq irq, MemHotplugState *mem_st, +void acpi_memory_plug_cb(HotplugHandler *hotplug_dev, MemHotplugState *mem_st, DeviceState *dev, Error **errp) { MemStatus *mdev; @@ -247,13 +247,11 @@ void acpi_memory_plug_cb(ACPIREGS *ar, qemu_irq irq, MemHotplugState *mem_st, mdev->is_enabled = true; if (dev->hotplugged) { mdev->is_inserting = true; - - /* do ACPI magic */ - acpi_send_gpe_event(ar, irq, ACPI_MEMORY_HOTPLUG_STATUS); + acpi_send_event(DEVICE(hotplug_dev), ACPI_MEMORY_HOTPLUG_STATUS); } } -void acpi_memory_unplug_request_cb(ACPIREGS *ar, qemu_irq irq, +void acpi_memory_unplug_request_cb(HotplugHandler *hotplug_dev, MemHotplugState *mem_st, DeviceState *dev, Error **errp) { @@ -265,9 +263,7 @@ void acpi_memory_unplug_request_cb(ACPIREGS *ar, qemu_irq irq, } mdev->is_removing = true; - - /* Do ACPI magic */ - acpi_send_gpe_event(ar, irq, ACPI_MEMORY_HOTPLUG_STATUS); + acpi_send_event(DEVICE(hotplug_dev), ACPI_MEMORY_HOTPLUG_STATUS); } void acpi_memory_unplug_cb(MemHotplugState *mem_st, diff --git a/hw/acpi/nvdimm.c b/hw/acpi/nvdimm.c index 9531340e5..e486128aa 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) @@ -353,7 +373,7 @@ static GArray *nvdimm_build_device_structure(GSList *device_list) } static void nvdimm_build_nfit(GSList *device_list, GArray *table_offsets, - GArray *table_data, GArray *linker) + GArray *table_data, BIOSLinker *linker) { GArray *structures = nvdimm_build_device_structure(device_list); unsigned int header; @@ -378,17 +398,19 @@ struct NvdimmDsmIn { uint32_t function; /* the remaining size in the page is used by arg3. */ union { - uint8_t arg3[0]; + uint8_t arg3[4084]; }; } QEMU_PACKED; typedef struct NvdimmDsmIn NvdimmDsmIn; +QEMU_BUILD_BUG_ON(sizeof(NvdimmDsmIn) != 4096); struct NvdimmDsmOut { /* the size of buffer filled by QEMU. */ uint32_t len; - uint8_t data[0]; + uint8_t data[4092]; } QEMU_PACKED; typedef struct NvdimmDsmOut NvdimmDsmOut; +QEMU_BUILD_BUG_ON(sizeof(NvdimmDsmOut) != 4096); struct NvdimmDsmFunc0Out { /* the size of buffer filled by QEMU. */ @@ -404,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) { @@ -424,8 +722,8 @@ nvdimm_dsm_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) * can change its content while we are doing DSM emulation. Avoid * this by copying DSM memory to QEMU local memory. */ - in = g_malloc(TARGET_PAGE_SIZE); - cpu_physical_memory_read(dsm_mem_addr, in, TARGET_PAGE_SIZE); + in = g_new(NvdimmDsmIn, 1); + cpu_physical_memory_read(dsm_mem_addr, in, sizeof(*in)); le32_to_cpus(&in->revision); le32_to_cpus(&in->function); @@ -434,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); } @@ -475,7 +769,7 @@ void nvdimm_init_acpi_state(AcpiNVDIMMState *state, MemoryRegion *io, memory_region_add_subregion(io, NVDIMM_ACPI_IO_BASE, &state->io_mr); state->dsm_mem = g_array_new(false, true /* clear */, 1); - acpi_data_push(state->dsm_mem, TARGET_PAGE_SIZE); + acpi_data_push(state->dsm_mem, sizeof(NvdimmDsmIn)); fw_cfg_add_file(fw_cfg, NVDIMM_DSM_MEM_FILE, state->dsm_mem->data, state->dsm_mem->len); } @@ -485,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 @@ -505,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. */ @@ -540,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); } @@ -571,13 +905,14 @@ 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); } } static void nvdimm_build_ssdt(GSList *device_list, GArray *table_offsets, - GArray *table_data, GArray *linker) + GArray *table_data, BIOSLinker *linker, + GArray *dsm_dma_arrea) { Aml *ssdt, *sb_scope, *dev, *field; int mem_addr_offset, nvdimm_ssdt; @@ -608,7 +943,7 @@ static void nvdimm_build_ssdt(GSList *device_list, GArray *table_offsets, aml_append(dev, aml_operation_region("NPIO", AML_SYSTEM_IO, aml_int(NVDIMM_ACPI_IO_BASE), NVDIMM_ACPI_IO_LEN)); aml_append(dev, aml_operation_region("NRAM", AML_SYSTEM_MEMORY, - aml_name(NVDIMM_ACPI_MEM_ADDR), TARGET_PAGE_SIZE)); + aml_name(NVDIMM_ACPI_MEM_ADDR), sizeof(NvdimmDsmIn))); /* * DSM notifier: @@ -642,8 +977,7 @@ static void nvdimm_build_ssdt(GSList *device_list, GArray *table_offsets, aml_append(field, aml_named_field("FUNC", sizeof(typeof_field(NvdimmDsmIn, function)) * BITS_PER_BYTE)); aml_append(field, aml_named_field("ARG3", - (TARGET_PAGE_SIZE - offsetof(NvdimmDsmIn, arg3)) * - BITS_PER_BYTE)); + (sizeof(NvdimmDsmIn) - offsetof(NvdimmDsmIn, arg3)) * BITS_PER_BYTE)); aml_append(dev, field); /* @@ -659,12 +993,13 @@ static void nvdimm_build_ssdt(GSList *device_list, GArray *table_offsets, aml_append(field, aml_named_field("RLEN", sizeof(typeof_field(NvdimmDsmOut, len)) * BITS_PER_BYTE)); aml_append(field, aml_named_field("ODAT", - (TARGET_PAGE_SIZE - offsetof(NvdimmDsmOut, data)) * - BITS_PER_BYTE)); + (sizeof(NvdimmDsmOut) - offsetof(NvdimmDsmOut, data)) * BITS_PER_BYTE)); 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); @@ -678,12 +1013,12 @@ static void nvdimm_build_ssdt(GSList *device_list, GArray *table_offsets, mem_addr_offset = build_append_named_dword(table_data, NVDIMM_ACPI_MEM_ADDR); - bios_linker_loader_alloc(linker, NVDIMM_DSM_MEM_FILE, TARGET_PAGE_SIZE, - false /* high memory */); - bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE, - NVDIMM_DSM_MEM_FILE, table_data, - table_data->data + mem_addr_offset, - sizeof(uint32_t)); + bios_linker_loader_alloc(linker, + NVDIMM_DSM_MEM_FILE, dsm_dma_arrea, + sizeof(NvdimmDsmIn), false /* high memory */); + bios_linker_loader_add_pointer(linker, + ACPI_BUILD_TABLE_FILE, mem_addr_offset, sizeof(uint32_t), + NVDIMM_DSM_MEM_FILE, 0); build_header(linker, table_data, (void *)(table_data->data + nvdimm_ssdt), "SSDT", table_data->len - nvdimm_ssdt, 1, NULL, "NVDIMM"); @@ -691,7 +1026,7 @@ static void nvdimm_build_ssdt(GSList *device_list, GArray *table_offsets, } void nvdimm_build_acpi(GArray *table_offsets, GArray *table_data, - GArray *linker) + BIOSLinker *linker, GArray *dsm_dma_arrea) { GSList *device_list; @@ -701,6 +1036,7 @@ void nvdimm_build_acpi(GArray *table_offsets, GArray *table_data, return; } nvdimm_build_nfit(device_list, table_offsets, table_data, linker); - nvdimm_build_ssdt(device_list, table_offsets, table_data, linker); + nvdimm_build_ssdt(device_list, table_offsets, table_data, linker, + dsm_dma_arrea); g_slist_free(device_list); } diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index 71f4c4e14..d957d1e30 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c @@ -182,7 +182,7 @@ void acpi_pcihp_reset(AcpiPciHpState *s) acpi_pcihp_update(s); } -void acpi_pcihp_device_plug_cb(ACPIREGS *ar, qemu_irq irq, AcpiPciHpState *s, +void acpi_pcihp_device_plug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s, DeviceState *dev, Error **errp) { PCIDevice *pdev = PCI_DEVICE(dev); @@ -202,11 +202,10 @@ void acpi_pcihp_device_plug_cb(ACPIREGS *ar, qemu_irq irq, AcpiPciHpState *s, } s->acpi_pcihp_pci_status[bsel].up |= (1U << slot); - - acpi_send_gpe_event(ar, irq, ACPI_PCI_HOTPLUG_STATUS); + acpi_send_event(DEVICE(hotplug_dev), ACPI_PCI_HOTPLUG_STATUS); } -void acpi_pcihp_device_unplug_cb(ACPIREGS *ar, qemu_irq irq, AcpiPciHpState *s, +void acpi_pcihp_device_unplug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s, DeviceState *dev, Error **errp) { PCIDevice *pdev = PCI_DEVICE(dev); @@ -219,8 +218,7 @@ void acpi_pcihp_device_unplug_cb(ACPIREGS *ar, qemu_irq irq, AcpiPciHpState *s, } s->acpi_pcihp_pci_status[bsel].down |= (1U << slot); - - acpi_send_gpe_event(ar, irq, ACPI_PCI_HOTPLUG_STATUS); + acpi_send_event(DEVICE(hotplug_dev), ACPI_PCI_HOTPLUG_STATUS); } static uint64_t pci_read(void *opaque, hwaddr addr, unsigned int size) diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 16abdf162..2adc246b0 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -34,11 +34,13 @@ #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" #include "hw/acpi/acpi_dev_interface.h" #include "hw/xen/xen.h" +#include "qom/cpu.h" //#define DEBUG @@ -85,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; @@ -272,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 @@ -306,6 +336,7 @@ static const VMStateDescription vmstate_acpi = { }, .subsections = (const VMStateDescription*[]) { &vmstate_memhp_state, + &vmstate_cpuhp_state, NULL } }; @@ -347,12 +378,15 @@ static void piix4_device_plug_cb(HotplugHandler *hotplug_dev, if (s->acpi_memory_hotplug.is_enabled && object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { - acpi_memory_plug_cb(&s->ar, s->irq, &s->acpi_memory_hotplug, dev, errp); + acpi_memory_plug_cb(hotplug_dev, &s->acpi_memory_hotplug, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { - acpi_pcihp_device_plug_cb(&s->ar, s->irq, &s->acpi_pci_hotplug, dev, - errp); + acpi_pcihp_device_plug_cb(hotplug_dev, &s->acpi_pci_hotplug, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { - acpi_cpu_plug_cb(&s->ar, s->irq, &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))); @@ -366,11 +400,14 @@ static void piix4_device_unplug_request_cb(HotplugHandler *hotplug_dev, if (s->acpi_memory_hotplug.is_enabled && object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { - acpi_memory_unplug_request_cb(&s->ar, s->irq, &s->acpi_memory_hotplug, + acpi_memory_unplug_request_cb(hotplug_dev, &s->acpi_memory_hotplug, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { - acpi_pcihp_device_unplug_cb(&s->ar, s->irq, &s->acpi_pci_hotplug, dev, + 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,8 +630,13 @@ 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); - acpi_cpu_hotplug_init(parent, OBJECT(s), &s->gpe_cpu, - PIIX4_CPU_HOTPLUG_IO_BASE); + 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); if (s->acpi_memory_hotplug.is_enabled) { acpi_memory_hotplug_init(parent, OBJECT(s), &s->acpi_memory_hotplug); @@ -583,6 +648,16 @@ 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) +{ + PIIX4PMState *s = PIIX4_PM(adev); + + acpi_send_gpe_event(&s->ar, s->irq, ev); } static Property piix4_pm_properties[] = { @@ -623,6 +698,8 @@ static void piix4_pm_class_init(ObjectClass *klass, void *data) hc->unplug_request = piix4_device_unplug_request_cb; 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 new file mode 100644 index 000000000..c379607a3 --- /dev/null +++ b/hw/acpi/trace-events @@ -0,0 +1,32 @@ +# See docs/tracing.txt for syntax documentation. + +# hw/acpi/memory_hotplug.c +mhp_acpi_invalid_slot_selected(uint32_t slot) "0x%"PRIx32 +mhp_acpi_ejecting_invalid_slot(uint32_t slot) "0x%"PRIx32 +mhp_acpi_read_addr_lo(uint32_t slot, uint32_t addr) "slot[0x%"PRIx32"] addr lo: 0x%"PRIx32 +mhp_acpi_read_addr_hi(uint32_t slot, uint32_t addr) "slot[0x%"PRIx32"] addr hi: 0x%"PRIx32 +mhp_acpi_read_size_lo(uint32_t slot, uint32_t size) "slot[0x%"PRIx32"] size lo: 0x%"PRIx32 +mhp_acpi_read_size_hi(uint32_t slot, uint32_t size) "slot[0x%"PRIx32"] size hi: 0x%"PRIx32 +mhp_acpi_read_pxm(uint32_t slot, uint32_t pxm) "slot[0x%"PRIx32"] proximity: 0x%"PRIx32 +mhp_acpi_read_flags(uint32_t slot, uint32_t flags) "slot[0x%"PRIx32"] flags: 0x%"PRIx32 +mhp_acpi_write_slot(uint32_t slot) "set active slot: 0x%"PRIx32 +mhp_acpi_write_ost_ev(uint32_t slot, uint32_t ev) "slot[0x%"PRIx32"] OST EVENT: 0x%"PRIx32 +mhp_acpi_write_ost_status(uint32_t slot, uint32_t st) "slot[0x%"PRIx32"] OST STATUS: 0x%"PRIx32 +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/alpha/alpha_sys.h b/hw/alpha/alpha_sys.h index e11025b4b..ed911f22a 100644 --- a/hw/alpha/alpha_sys.h +++ b/hw/alpha/alpha_sys.h @@ -1,8 +1,9 @@ /* Alpha cores and system support chips. */ -#ifndef HW_ALPHA_H -#define HW_ALPHA_H 1 +#ifndef HW_ALPHA_SYS_H +#define HW_ALPHA_SYS_H +#include "target-alpha/cpu-qom.h" #include "hw/pci/pci.h" #include "hw/pci/pci_host.h" #include "hw/ide.h" diff --git a/hw/alpha/pci.c b/hw/alpha/pci.c index 5baa0eaf1..8dde637bf 100644 --- a/hw/alpha/pci.c +++ b/hw/alpha/pci.c @@ -8,7 +8,6 @@ #include "qemu/osdep.h" #include "qemu-common.h" -#include "cpu.h" #include "alpha_sys.h" #include "qemu/log.h" #include "sysemu/sysemu.h" diff --git a/hw/alpha/trace-events b/hw/alpha/trace-events new file mode 100644 index 000000000..e44ff01a0 --- /dev/null +++ b/hw/alpha/trace-events @@ -0,0 +1,4 @@ +# See docs/tracing.txt for syntax documentation. + +# hw/alpha/pci.c +alpha_pci_iack_write(void) "" diff --git a/hw/alpha/typhoon.c b/hw/alpha/typhoon.c index 97721b535..883db13f9 100644 --- a/hw/alpha/typhoon.c +++ b/hw/alpha/typhoon.c @@ -824,7 +824,6 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, int i; dev = qdev_create(NULL, TYPE_TYPHOON_PCI_HOST_BRIDGE); - qdev_init_nofail(dev); s = TYPHOON_PCI_HOST_BRIDGE(dev); phb = PCI_HOST_BRIDGE(dev); @@ -889,6 +888,7 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, &s->pchip.reg_mem, &s->pchip.reg_io, 0, 64, TYPE_PCI_BUS); phb->bus = b; + qdev_init_nofail(dev); /* Host memory as seen from the PCI side, via the IOMMU. */ memory_region_init_iommu(&s->pchip.iommu, OBJECT(s), &typhoon_iommu_ops, diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 954c9fe15..12764ef2b 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -16,4 +16,5 @@ obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp.o xlnx-ep108.o obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o +obj-$(CONFIG_FSL_IMX6) += fsl-imx6.o sabrelite.o obj-$(CONFIG_ASPEED_SOC) += ast2400.o palmetto-bmc.o diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index bb2a22d96..49d30782c 100644 --- a/hw/arm/armv7m.c +++ b/hw/arm/armv7m.c @@ -132,14 +132,14 @@ typedef struct { uint32_t base; } BitBandState; -static int bitband_init(SysBusDevice *dev) +static void bitband_init(Object *obj) { - BitBandState *s = BITBAND(dev); + BitBandState *s = BITBAND(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); - memory_region_init_io(&s->iomem, OBJECT(s), &bitband_ops, &s->base, + memory_region_init_io(&s->iomem, obj, &bitband_ops, &s->base, "bitband", 0x02000000); sysbus_init_mmio(dev, &s->iomem); - return 0; } static void armv7m_bitband_init(void) @@ -244,9 +244,7 @@ static Property bitband_properties[] = { static void bitband_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = bitband_init; dc->props = bitband_properties; } @@ -254,6 +252,7 @@ static const TypeInfo bitband_info = { .name = TYPE_BITBAND, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(BitBandState), + .instance_init = bitband_init, .class_init = bitband_class_init, }; diff --git a/hw/arm/ast2400.c b/hw/arm/ast2400.c index 03f993863..326fdb36e 100644 --- a/hw/arm/ast2400.c +++ b/hw/arm/ast2400.c @@ -17,12 +17,22 @@ #include "exec/address-spaces.h" #include "hw/arm/ast2400.h" #include "hw/char/serial.h" +#include "qemu/log.h" +#include "hw/i2c/aspeed_i2c.h" #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 static const int uart_irqs[] = { 9, 32, 33, 34, 10 }; static const int timer_irqs[] = { 16, 17, 18, 35, 36, 37, 38, 39, }; @@ -65,13 +75,35 @@ static void ast2400_init(Object *obj) object_initialize(&s->timerctrl, sizeof(s->timerctrl), TYPE_ASPEED_TIMER); object_property_add_child(obj, "timerctrl", OBJECT(&s->timerctrl), NULL); qdev_set_parent_bus(DEVICE(&s->timerctrl), sysbus_get_default()); + + 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, @@ -103,12 +135,54 @@ 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]); serial_mm_init(&s->iomem, AST2400_UART_5_BASE, 2, uart5, 38400, serial_hds[0], DEVICE_LITTLE_ENDIAN); } + + /* I2C */ + object_property_set_bool(OBJECT(&s->i2c), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + 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/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c index 234d51843..2e641a398 100644 --- a/hw/arm/bcm2835_peripherals.c +++ b/hw/arm/bcm2835_peripherals.c @@ -14,6 +14,7 @@ #include "hw/misc/bcm2835_mbox_defs.h" #include "hw/arm/raspi_platform.h" #include "sysemu/char.h" +#include "sysemu/sysemu.h" /* Peripheral base address on the VC (GPU) system bus */ #define BCM2835_VC_PERI_BASE 0x7e000000 @@ -106,7 +107,6 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) MemoryRegion *ram; Error *err = NULL; uint32_t ram_size, vcram_size; - CharDriverState *chr; int n; obj = object_property_get_link(OBJECT(dev), "ram", &err); @@ -147,6 +147,7 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) sysbus_pass_irq(SYS_BUS_DEVICE(s), SYS_BUS_DEVICE(&s->ic)); /* UART0 */ + qdev_prop_set_chr(DEVICE(s->uart0), "chardev", serial_hds[0]); object_property_set_bool(OBJECT(s->uart0), true, "realized", &err); if (err) { error_propagate(errp, err); @@ -158,17 +159,8 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(s->uart0, 0, qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ, INTERRUPT_UART)); - /* AUX / UART1 */ - /* TODO: don't call qemu_char_get_next_serial() here, instead set - * chardev properties for each uart at the board level, once pl011 - * (uart0) has been updated to avoid qemu_char_get_next_serial() - */ - chr = qemu_char_get_next_serial(); - if (chr == NULL) { - chr = qemu_chr_new("bcm2835.uart1", "null", NULL); - } - qdev_prop_set_chr(DEVICE(&s->aux), "chardev", chr); + qdev_prop_set_chr(DEVICE(&s->aux), "chardev", serial_hds[1]); object_property_set_bool(OBJECT(&s->aux), true, "realized", &err); if (err) { @@ -292,8 +284,6 @@ static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = bcm2835_peripherals_realize; - /* Reason: realize() method uses qemu_char_get_next_serial() */ - dc->cannot_instantiate_with_device_add_yet = true; } static const TypeInfo bcm2835_peripherals_type_info = { diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 587694557..1b913a43c 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -14,6 +14,7 @@ #include "hw/arm/linux-boot-if.h" #include "sysemu/kvm.h" #include "sysemu/sysemu.h" +#include "sysemu/numa.h" #include "hw/boards.h" #include "hw/loader.h" #include "elf.h" @@ -405,6 +406,9 @@ static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo, void *fdt = NULL; int size, rc; uint32_t acells, scells; + char *nodename; + unsigned int i; + hwaddr mem_base, mem_len; if (binfo->dtb_filename) { char *filename; @@ -456,12 +460,39 @@ static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo, goto fail; } - rc = qemu_fdt_setprop_sized_cells(fdt, "/memory", "reg", - acells, binfo->loader_start, - scells, binfo->ram_size); - if (rc < 0) { - fprintf(stderr, "couldn't set /memory/reg\n"); - goto fail; + if (nb_numa_nodes > 0) { + /* + * Turn the /memory node created before into a NOP node, then create + * /memory@addr nodes for all numa nodes respectively. + */ + qemu_fdt_nop_node(fdt, "/memory"); + mem_base = binfo->loader_start; + for (i = 0; i < nb_numa_nodes; i++) { + mem_len = numa_info[i].node_mem; + nodename = g_strdup_printf("/memory@%" PRIx64, mem_base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory"); + rc = qemu_fdt_setprop_sized_cells(fdt, nodename, "reg", + acells, mem_base, + scells, mem_len); + if (rc < 0) { + fprintf(stderr, "couldn't set %s/reg for node %d\n", nodename, + i); + goto fail; + } + + qemu_fdt_setprop_cell(fdt, nodename, "numa-node-id", i); + mem_base += mem_len; + g_free(nodename); + } + } else { + rc = qemu_fdt_setprop_sized_cells(fdt, "/memory", "reg", + acells, binfo->loader_start, + scells, binfo->ram_size); + if (rc < 0) { + fprintf(stderr, "couldn't set /memory/reg\n"); + goto fail; + } } if (binfo->kernel_cmdline && *binfo->kernel_cmdline) { diff --git a/hw/arm/collie.c b/hw/arm/collie.c index 8bb308a42..2e6953128 100644 --- a/hw/arm/collie.c +++ b/hw/arm/collie.c @@ -18,6 +18,7 @@ #include "hw/block/flash.h" #include "sysemu/block-backend.h" #include "exec/address-spaces.h" +#include "qom/cpu.h" static struct arm_boot_info collie_binfo = { .loader_start = SA_SDCS0, diff --git a/hw/arm/digic.c b/hw/arm/digic.c index e0f973032..d60ea395f 100644 --- a/hw/arm/digic.c +++ b/hw/arm/digic.c @@ -23,6 +23,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/arm/digic.h" +#include "sysemu/sysemu.h" #define DIGIC4_TIMER_BASE(n) (0xc0210000 + (n) * 0x100) @@ -84,6 +85,7 @@ static void digic_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(sbd, 0, DIGIC4_TIMER_BASE(i)); } + qdev_prop_set_chr(DEVICE(&s->uart), "chardev", serial_hds[0]); object_property_set_bool(OBJECT(&s->uart), true, "realized", &err); if (err != NULL) { error_propagate(errp, err); diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c index 2f878b935..b4e358db6 100644 --- a/hw/arm/fsl-imx25.c +++ b/hw/arm/fsl-imx25.c @@ -51,7 +51,7 @@ static void fsl_imx25_init(Object *obj) } for (i = 0; i < FSL_IMX25_NUM_GPTS; i++) { - object_initialize(&s->gpt[i], sizeof(s->gpt[i]), TYPE_IMX_GPT); + object_initialize(&s->gpt[i], sizeof(s->gpt[i]), TYPE_IMX25_GPT); qdev_set_parent_bus(DEVICE(&s->gpt[i]), sysbus_get_default()); } @@ -191,6 +191,7 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp) } qdev_set_nic_properties(DEVICE(&s->fec), &nd_table[0]); + object_property_set_bool(OBJECT(&s->fec), true, "realized", &err); if (err) { error_propagate(errp, err); @@ -248,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 31a3a8791..fe204ace6 100644 --- a/hw/arm/fsl-imx31.c +++ b/hw/arm/fsl-imx31.c @@ -47,7 +47,7 @@ static void fsl_imx31_init(Object *obj) qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus_get_default()); } - object_initialize(&s->gpt, sizeof(s->gpt), TYPE_IMX_GPT); + object_initialize(&s->gpt, sizeof(s->gpt), TYPE_IMX31_GPT); qdev_set_parent_bus(DEVICE(&s->gpt), sysbus_get_default()); for (i = 0; i < FSL_IMX31_NUM_EPITS; i++) { @@ -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 new file mode 100644 index 000000000..6a1bf263a --- /dev/null +++ b/hw/arm/fsl-imx6.c @@ -0,0 +1,466 @@ +/* + * Copyright (c) 2015 Jean-Christophe Dubois <jcd@tribudubois.net> + * + * i.MX6 SOC emulation. + * + * Based on hw/arm/fsl-imx31.c + * + * 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 "qapi/error.h" +#include "qemu-common.h" +#include "hw/arm/fsl-imx6.h" +#include "sysemu/sysemu.h" +#include "sysemu/char.h" +#include "qemu/error-report.h" + +#define NAME_SIZE 20 + +static void fsl_imx6_init(Object *obj) +{ + FslIMX6State *s = FSL_IMX6(obj); + char name[NAME_SIZE]; + int i; + + if (smp_cpus > FSL_IMX6_NUM_CPUS) { + error_report("%s: Only %d CPUs are supported (%d requested)", + TYPE_FSL_IMX6, FSL_IMX6_NUM_CPUS, smp_cpus); + exit(1); + } + + for (i = 0; i < smp_cpus; i++) { + object_initialize(&s->cpu[i], sizeof(s->cpu[i]), + "cortex-a9-" TYPE_ARM_CPU); + snprintf(name, NAME_SIZE, "cpu%d", i); + object_property_add_child(obj, name, OBJECT(&s->cpu[i]), NULL); + } + + object_initialize(&s->a9mpcore, sizeof(s->a9mpcore), TYPE_A9MPCORE_PRIV); + qdev_set_parent_bus(DEVICE(&s->a9mpcore), sysbus_get_default()); + object_property_add_child(obj, "a9mpcore", OBJECT(&s->a9mpcore), NULL); + + object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX6_CCM); + qdev_set_parent_bus(DEVICE(&s->ccm), sysbus_get_default()); + object_property_add_child(obj, "ccm", OBJECT(&s->ccm), NULL); + + object_initialize(&s->src, sizeof(s->src), TYPE_IMX6_SRC); + qdev_set_parent_bus(DEVICE(&s->src), sysbus_get_default()); + object_property_add_child(obj, "src", OBJECT(&s->src), NULL); + + for (i = 0; i < FSL_IMX6_NUM_UARTS; i++) { + object_initialize(&s->uart[i], sizeof(s->uart[i]), TYPE_IMX_SERIAL); + qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus_get_default()); + snprintf(name, NAME_SIZE, "uart%d", i + 1); + object_property_add_child(obj, name, OBJECT(&s->uart[i]), NULL); + } + + object_initialize(&s->gpt, sizeof(s->gpt), TYPE_IMX6_GPT); + qdev_set_parent_bus(DEVICE(&s->gpt), sysbus_get_default()); + object_property_add_child(obj, "gpt", OBJECT(&s->gpt), NULL); + + for (i = 0; i < FSL_IMX6_NUM_EPITS; i++) { + object_initialize(&s->epit[i], sizeof(s->epit[i]), TYPE_IMX_EPIT); + qdev_set_parent_bus(DEVICE(&s->epit[i]), sysbus_get_default()); + snprintf(name, NAME_SIZE, "epit%d", i + 1); + object_property_add_child(obj, name, OBJECT(&s->epit[i]), NULL); + } + + for (i = 0; i < FSL_IMX6_NUM_I2CS; i++) { + object_initialize(&s->i2c[i], sizeof(s->i2c[i]), TYPE_IMX_I2C); + qdev_set_parent_bus(DEVICE(&s->i2c[i]), sysbus_get_default()); + snprintf(name, NAME_SIZE, "i2c%d", i + 1); + object_property_add_child(obj, name, OBJECT(&s->i2c[i]), NULL); + } + + for (i = 0; i < FSL_IMX6_NUM_GPIOS; i++) { + object_initialize(&s->gpio[i], sizeof(s->gpio[i]), TYPE_IMX_GPIO); + qdev_set_parent_bus(DEVICE(&s->gpio[i]), sysbus_get_default()); + snprintf(name, NAME_SIZE, "gpio%d", i + 1); + object_property_add_child(obj, name, OBJECT(&s->gpio[i]), NULL); + } + + for (i = 0; i < FSL_IMX6_NUM_ESDHCS; i++) { + object_initialize(&s->esdhc[i], sizeof(s->esdhc[i]), TYPE_SYSBUS_SDHCI); + qdev_set_parent_bus(DEVICE(&s->esdhc[i]), sysbus_get_default()); + snprintf(name, NAME_SIZE, "sdhc%d", i + 1); + object_property_add_child(obj, name, OBJECT(&s->esdhc[i]), NULL); + } + + for (i = 0; i < FSL_IMX6_NUM_ECSPIS; i++) { + object_initialize(&s->spi[i], sizeof(s->spi[i]), TYPE_IMX_SPI); + qdev_set_parent_bus(DEVICE(&s->spi[i]), sysbus_get_default()); + snprintf(name, NAME_SIZE, "spi%d", i + 1); + object_property_add_child(obj, name, OBJECT(&s->spi[i]), NULL); + } + + object_initialize(&s->eth, sizeof(s->eth), TYPE_IMX_ENET); + qdev_set_parent_bus(DEVICE(&s->eth), sysbus_get_default()); + object_property_add_child(obj, "eth", OBJECT(&s->eth), NULL); +} + +static void fsl_imx6_realize(DeviceState *dev, Error **errp) +{ + FslIMX6State *s = FSL_IMX6(dev); + uint16_t i; + Error *err = NULL; + + for (i = 0; i < smp_cpus; i++) { + + /* On uniprocessor, the CBAR is set to 0 */ + if (smp_cpus > 1) { + object_property_set_int(OBJECT(&s->cpu[i]), FSL_IMX6_A9MPCORE_ADDR, + "reset-cbar", &error_abort); + } + + /* All CPU but CPU 0 start in power off mode */ + if (i) { + object_property_set_bool(OBJECT(&s->cpu[i]), true, + "start-powered-off", &error_abort); + } + + object_property_set_bool(OBJECT(&s->cpu[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + } + + object_property_set_int(OBJECT(&s->a9mpcore), smp_cpus, "num-cpu", + &error_abort); + + object_property_set_int(OBJECT(&s->a9mpcore), + FSL_IMX6_MAX_IRQ + GIC_INTERNAL, "num-irq", + &error_abort); + + object_property_set_bool(OBJECT(&s->a9mpcore), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->a9mpcore), 0, FSL_IMX6_A9MPCORE_ADDR); + + for (i = 0; i < smp_cpus; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&s->a9mpcore), i, + qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->a9mpcore), i + smp_cpus, + qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_FIQ)); + } + + object_property_set_bool(OBJECT(&s->ccm), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccm), 0, FSL_IMX6_CCM_ADDR); + + object_property_set_bool(OBJECT(&s->src), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->src), 0, FSL_IMX6_SRC_ADDR); + + /* Initialize all UARTs */ + for (i = 0; i < FSL_IMX6_NUM_UARTS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } serial_table[FSL_IMX6_NUM_UARTS] = { + { FSL_IMX6_UART1_ADDR, FSL_IMX6_UART1_IRQ }, + { FSL_IMX6_UART2_ADDR, FSL_IMX6_UART2_IRQ }, + { FSL_IMX6_UART3_ADDR, FSL_IMX6_UART3_IRQ }, + { FSL_IMX6_UART4_ADDR, FSL_IMX6_UART4_IRQ }, + { FSL_IMX6_UART5_ADDR, FSL_IMX6_UART5_IRQ }, + }; + + if (i < MAX_SERIAL_PORTS) { + CharDriverState *chr; + + chr = serial_hds[i]; + + if (!chr) { + char *label = g_strdup_printf("imx6.uart%d", i + 1); + chr = qemu_chr_new(label, "null", NULL); + g_free(label); + serial_hds[i] = chr; + } + + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", chr); + } + + object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, serial_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, + qdev_get_gpio_in(DEVICE(&s->a9mpcore), + serial_table[i].irq)); + } + + s->gpt.ccm = IMX_CCM(&s->ccm); + + object_property_set_bool(OBJECT(&s->gpt), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpt), 0, FSL_IMX6_GPT_ADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpt), 0, + qdev_get_gpio_in(DEVICE(&s->a9mpcore), + FSL_IMX6_GPT_IRQ)); + + /* Initialize all EPIT timers */ + for (i = 0; i < FSL_IMX6_NUM_EPITS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } epit_table[FSL_IMX6_NUM_EPITS] = { + { FSL_IMX6_EPIT1_ADDR, FSL_IMX6_EPIT1_IRQ }, + { FSL_IMX6_EPIT2_ADDR, FSL_IMX6_EPIT2_IRQ }, + }; + + s->epit[i].ccm = IMX_CCM(&s->ccm); + + object_property_set_bool(OBJECT(&s->epit[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->epit[i]), 0, epit_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->epit[i]), 0, + qdev_get_gpio_in(DEVICE(&s->a9mpcore), + epit_table[i].irq)); + } + + /* Initialize all I2C */ + for (i = 0; i < FSL_IMX6_NUM_I2CS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } i2c_table[FSL_IMX6_NUM_I2CS] = { + { FSL_IMX6_I2C1_ADDR, FSL_IMX6_I2C1_IRQ }, + { FSL_IMX6_I2C2_ADDR, FSL_IMX6_I2C2_IRQ }, + { FSL_IMX6_I2C3_ADDR, FSL_IMX6_I2C3_IRQ } + }; + + object_property_set_bool(OBJECT(&s->i2c[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c[i]), 0, i2c_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[i]), 0, + qdev_get_gpio_in(DEVICE(&s->a9mpcore), + i2c_table[i].irq)); + } + + /* Initialize all GPIOs */ + for (i = 0; i < FSL_IMX6_NUM_GPIOS; i++) { + static const struct { + hwaddr addr; + unsigned int irq_low; + unsigned int irq_high; + } gpio_table[FSL_IMX6_NUM_GPIOS] = { + { + FSL_IMX6_GPIO1_ADDR, + FSL_IMX6_GPIO1_LOW_IRQ, + FSL_IMX6_GPIO1_HIGH_IRQ + }, + { + FSL_IMX6_GPIO2_ADDR, + FSL_IMX6_GPIO2_LOW_IRQ, + FSL_IMX6_GPIO2_HIGH_IRQ + }, + { + FSL_IMX6_GPIO3_ADDR, + FSL_IMX6_GPIO3_LOW_IRQ, + FSL_IMX6_GPIO3_HIGH_IRQ + }, + { + FSL_IMX6_GPIO4_ADDR, + FSL_IMX6_GPIO4_LOW_IRQ, + FSL_IMX6_GPIO4_HIGH_IRQ + }, + { + FSL_IMX6_GPIO5_ADDR, + FSL_IMX6_GPIO5_LOW_IRQ, + FSL_IMX6_GPIO5_HIGH_IRQ + }, + { + FSL_IMX6_GPIO6_ADDR, + FSL_IMX6_GPIO6_LOW_IRQ, + FSL_IMX6_GPIO6_HIGH_IRQ + }, + { + FSL_IMX6_GPIO7_ADDR, + FSL_IMX6_GPIO7_LOW_IRQ, + FSL_IMX6_GPIO7_HIGH_IRQ + }, + }; + + object_property_set_bool(OBJECT(&s->gpio[i]), true, "has-edge-sel", + &error_abort); + object_property_set_bool(OBJECT(&s->gpio[i]), true, "has-upper-pin-irq", + &error_abort); + object_property_set_bool(OBJECT(&s->gpio[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, gpio_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 0, + qdev_get_gpio_in(DEVICE(&s->a9mpcore), + gpio_table[i].irq_low)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 1, + qdev_get_gpio_in(DEVICE(&s->a9mpcore), + gpio_table[i].irq_high)); + } + + /* Initialize all SDHC */ + for (i = 0; i < FSL_IMX6_NUM_ESDHCS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } esdhc_table[FSL_IMX6_NUM_ESDHCS] = { + { FSL_IMX6_uSDHC1_ADDR, FSL_IMX6_uSDHC1_IRQ }, + { FSL_IMX6_uSDHC2_ADDR, FSL_IMX6_uSDHC2_IRQ }, + { FSL_IMX6_uSDHC3_ADDR, FSL_IMX6_uSDHC3_IRQ }, + { FSL_IMX6_uSDHC4_ADDR, FSL_IMX6_uSDHC4_IRQ }, + }; + + object_property_set_bool(OBJECT(&s->esdhc[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->esdhc[i]), 0, esdhc_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->esdhc[i]), 0, + qdev_get_gpio_in(DEVICE(&s->a9mpcore), + esdhc_table[i].irq)); + } + + /* Initialize all ECSPI */ + for (i = 0; i < FSL_IMX6_NUM_ECSPIS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } spi_table[FSL_IMX6_NUM_ECSPIS] = { + { FSL_IMX6_eCSPI1_ADDR, FSL_IMX6_ECSPI1_IRQ }, + { FSL_IMX6_eCSPI2_ADDR, FSL_IMX6_ECSPI2_IRQ }, + { FSL_IMX6_eCSPI3_ADDR, FSL_IMX6_ECSPI3_IRQ }, + { FSL_IMX6_eCSPI4_ADDR, FSL_IMX6_ECSPI4_IRQ }, + { FSL_IMX6_eCSPI5_ADDR, FSL_IMX6_ECSPI5_IRQ }, + }; + + /* Initialize the SPI */ + object_property_set_bool(OBJECT(&s->spi[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, spi_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi[i]), 0, + qdev_get_gpio_in(DEVICE(&s->a9mpcore), + spi_table[i].irq)); + } + + object_property_set_bool(OBJECT(&s->eth), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->eth), 0, FSL_IMX6_ENET_ADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->eth), 0, + qdev_get_gpio_in(DEVICE(&s->a9mpcore), + FSL_IMX6_ENET_MAC_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->eth), 1, + qdev_get_gpio_in(DEVICE(&s->a9mpcore), + FSL_IMX6_ENET_MAC_1588_IRQ)); + + /* ROM memory */ + memory_region_init_rom(&s->rom, NULL, "imx6.rom", + FSL_IMX6_ROM_SIZE, &err); + if (err) { + error_propagate(errp, err); + return; + } + memory_region_add_subregion(get_system_memory(), FSL_IMX6_ROM_ADDR, + &s->rom); + + /* CAAM memory */ + memory_region_init_rom(&s->caam, NULL, "imx6.caam", + FSL_IMX6_CAAM_MEM_SIZE, &err); + if (err) { + error_propagate(errp, err); + return; + } + memory_region_add_subregion(get_system_memory(), FSL_IMX6_CAAM_MEM_ADDR, + &s->caam); + + /* OCRAM memory */ + memory_region_init_ram(&s->ocram, NULL, "imx6.ocram", FSL_IMX6_OCRAM_SIZE, + &err); + if (err) { + error_propagate(errp, err); + return; + } + memory_region_add_subregion(get_system_memory(), FSL_IMX6_OCRAM_ADDR, + &s->ocram); + vmstate_register_ram_global(&s->ocram); + + /* internal OCRAM (256 KB) is aliased over 1 MB */ + memory_region_init_alias(&s->ocram_alias, NULL, "imx6.ocram_alias", + &s->ocram, 0, FSL_IMX6_OCRAM_ALIAS_SIZE); + memory_region_add_subregion(get_system_memory(), FSL_IMX6_OCRAM_ALIAS_ADDR, + &s->ocram_alias); +} + +static void fsl_imx6_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = fsl_imx6_realize; + + /* + * Reason: creates an ARM CPU, thus use after free(), see + * arm_cpu_class_init() + */ + dc->cannot_destroy_with_object_finalize_yet = true; + dc->desc = "i.MX6 SOC"; +} + +static const TypeInfo fsl_imx6_type_info = { + .name = TYPE_FSL_IMX6, + .parent = TYPE_DEVICE, + .instance_size = sizeof(FslIMX6State), + .instance_init = fsl_imx6_init, + .class_init = fsl_imx6_class_init, +}; + +static void fsl_imx6_register_types(void) +{ + type_register_static(&fsl_imx6_type_info); +} + +type_init(fsl_imx6_register_types) diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c index d9930c0d3..80e5fd458 100644 --- a/hw/arm/highbank.c +++ b/hw/arm/highbank.c @@ -30,6 +30,7 @@ #include "sysemu/block-backend.h" #include "exec/address-spaces.h" #include "qemu/error-report.h" +#include "hw/char/pl011.h" #define SMP_BOOT_ADDR 0x100 #define SMP_BOOT_REG 0x40 @@ -168,23 +169,20 @@ static void highbank_regs_reset(DeviceState *dev) s->regs[0x43] = 0x05F40121; } -static int highbank_regs_init(SysBusDevice *dev) +static void highbank_regs_init(Object *obj) { - HighbankRegsState *s = HIGHBANK_REGISTERS(dev); + HighbankRegsState *s = HIGHBANK_REGISTERS(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); - memory_region_init_io(&s->iomem, OBJECT(s), &hb_mem_ops, s->regs, + memory_region_init_io(&s->iomem, obj, &hb_mem_ops, s->regs, "highbank_regs", 0x1000); sysbus_init_mmio(dev, &s->iomem); - - return 0; } static void highbank_regs_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - sbc->init = highbank_regs_init; dc->desc = "Calxeda Highbank registers"; dc->vmsd = &vmstate_highbank_regs; dc->reset = highbank_regs_reset; @@ -194,6 +192,7 @@ static const TypeInfo highbank_regs_info = { .name = TYPE_HIGHBANK_REGISTERS, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(HighbankRegsState), + .instance_init = highbank_regs_init, .class_init = highbank_regs_class_init, }; @@ -328,7 +327,7 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) busdev = SYS_BUS_DEVICE(dev); sysbus_mmio_map(busdev, 0, 0xfff34000); sysbus_connect_irq(busdev, 0, pic[18]); - sysbus_create_simple("pl011", 0xfff36000, pic[20]); + pl011_create(0xfff36000, pic[20], serial_hds[0]); dev = qdev_create(NULL, "highbank-regs"); qdev_init_nofail(dev); diff --git a/hw/arm/integratorcp.c b/hw/arm/integratorcp.c index e31bca6e7..96dc15002 100644 --- a/hw/arm/integratorcp.c +++ b/hw/arm/integratorcp.c @@ -20,6 +20,7 @@ #include "exec/address-spaces.h" #include "sysemu/sysemu.h" #include "qemu/error-report.h" +#include "hw/char/pl011.h" #define TYPE_INTEGRATOR_CM "integrator_core" #define INTEGRATOR_CM(obj) \ @@ -242,9 +243,10 @@ static const MemoryRegionOps integratorcm_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int integratorcm_init(SysBusDevice *dev) +static void integratorcm_init(Object *obj) { - IntegratorCMState *s = INTEGRATOR_CM(dev); + IntegratorCMState *s = INTEGRATOR_CM(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); s->cm_osc = 0x01000048; /* ??? What should the high bits of this value be? */ @@ -269,17 +271,16 @@ static int integratorcm_init(SysBusDevice *dev) s->cm_init = 0x00000112; s->cm_refcnt_offset = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 24, 1000); - memory_region_init_ram(&s->flash, OBJECT(s), "integrator.flash", 0x100000, + memory_region_init_ram(&s->flash, obj, "integrator.flash", 0x100000, &error_fatal); vmstate_register_ram_global(&s->flash); - memory_region_init_io(&s->iomem, OBJECT(s), &integratorcm_ops, s, + memory_region_init_io(&s->iomem, obj, &integratorcm_ops, s, "integratorcm", 0x00800000); sysbus_init_mmio(dev, &s->iomem); integratorcm_do_remap(s); /* ??? Save/restore. */ - return 0; } /* Integrator/CP hardware emulation. */ @@ -394,18 +395,18 @@ static const MemoryRegionOps icp_pic_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int icp_pic_init(SysBusDevice *sbd) +static void icp_pic_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - icp_pic_state *s = INTEGRATOR_PIC(dev); + DeviceState *dev = DEVICE(obj); + icp_pic_state *s = INTEGRATOR_PIC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); qdev_init_gpio_in(dev, icp_pic_set_irq, 32); sysbus_init_irq(sbd, &s->parent_irq); sysbus_init_irq(sbd, &s->parent_fiq); - memory_region_init_io(&s->iomem, OBJECT(s), &icp_pic_ops, s, + memory_region_init_io(&s->iomem, obj, &icp_pic_ops, s, "icp-pic", 0x00800000); sysbus_init_mmio(sbd, &s->iomem); - return 0; } /* CP control registers. */ @@ -588,8 +589,8 @@ static void integratorcp_init(MachineState *machine) sysbus_create_varargs("integrator_pit", 0x13000000, pic[5], pic[6], pic[7], NULL); sysbus_create_simple("pl031", 0x15000000, pic[8]); - sysbus_create_simple("pl011", 0x16000000, pic[1]); - sysbus_create_simple("pl011", 0x17000000, pic[2]); + pl011_create(0x16000000, pic[1], serial_hds[0]); + pl011_create(0x17000000, pic[2], serial_hds[1]); icp = sysbus_create_simple(TYPE_ICP_CONTROL_REGS, 0xcb000000, qdev_get_gpio_in(sic, 3)); sysbus_create_simple("pl050_keyboard", 0x18000000, pic[3]); @@ -630,9 +631,7 @@ static Property core_properties[] = { static void core_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = integratorcm_init; dc->props = core_properties; } @@ -640,21 +639,15 @@ static const TypeInfo core_info = { .name = TYPE_INTEGRATOR_CM, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(IntegratorCMState), + .instance_init = integratorcm_init, .class_init = core_class_init, }; -static void icp_pic_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - - sdc->init = icp_pic_init; -} - static const TypeInfo icp_pic_info = { .name = TYPE_INTEGRATOR_PIC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(icp_pic_state), - .class_init = icp_pic_class_init, + .instance_init = icp_pic_init, }; static const TypeInfo icp_ctrl_regs_info = { diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index 7a4cc07dd..cc50ace13 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -378,7 +378,7 @@ static void eth_cleanup(NetClientState *nc) } static NetClientInfo net_mv88w8618_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, + .type = NET_CLIENT_DRIVER_NIC, .size = sizeof(NICState), .receive = eth_receive, .cleanup = eth_cleanup, diff --git a/hw/arm/nseries.c b/hw/arm/nseries.c index 538250555..fea911e3e 100644 --- a/hw/arm/nseries.c +++ b/hw/arm/nseries.c @@ -20,7 +20,9 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "cpu.h" #include "qemu/cutils.h" +#include "qemu/bswap.h" #include "sysemu/sysemu.h" #include "hw/arm/omap.h" #include "hw/arm/arm.h" @@ -35,6 +37,7 @@ #include "hw/loader.h" #include "sysemu/block-backend.h" #include "hw/sysbus.h" +#include "qemu/log.h" #include "exec/address-spaces.h" /* Nokia N8x0 support */ @@ -1348,7 +1351,7 @@ static void n8x0_init(MachineState *machine, n8x0_dss_setup(s); n8x0_cbus_setup(s); n8x0_uart_setup(s); - if (usb_enabled()) { + if (machine_usb(machine)) { n8x0_usb_setup(s); } @@ -1364,7 +1367,7 @@ static void n8x0_init(MachineState *machine, if (option_rom[0].name && (machine->boot_order[0] == 'n' || !machine->kernel_filename)) { - uint8_t nolo_tags[0x10000]; + uint8_t *nolo_tags = g_new(uint8_t, 0x10000); /* No, wait, better start at the ROM. */ s->mpu->cpu->env.regs[15] = OMAP2_Q2_BASE + 0x400000; @@ -1383,6 +1386,7 @@ static void n8x0_init(MachineState *machine, n800_setup_nolo_tags(nolo_tags); cpu_physical_memory_write(OMAP2_SRAM_BASE, nolo_tags, 0x10000); + g_free(nolo_tags); } } diff --git a/hw/arm/palmetto-bmc.c b/hw/arm/palmetto-bmc.c index 89ebd92b9..54e29a865 100644 --- a/hw/arm/palmetto-bmc.c +++ b/hw/arm/palmetto-bmc.c @@ -17,6 +17,9 @@ #include "hw/arm/arm.h" #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, @@ -29,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; @@ -43,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/pxa2xx.c b/hw/arm/pxa2xx.c index 1a8c36033..cb5570468 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -1107,9 +1107,10 @@ static const MemoryRegionOps pxa2xx_rtc_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int pxa2xx_rtc_init(SysBusDevice *dev) +static void pxa2xx_rtc_init(Object *obj) { - PXA2xxRTCState *s = PXA2XX_RTC(dev); + PXA2xxRTCState *s = PXA2XX_RTC(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); struct tm tm; int wom; @@ -1138,11 +1139,9 @@ static int pxa2xx_rtc_init(SysBusDevice *dev) sysbus_init_irq(dev, &s->rtc_irq); - memory_region_init_io(&s->iomem, OBJECT(s), &pxa2xx_rtc_ops, s, + memory_region_init_io(&s->iomem, obj, &pxa2xx_rtc_ops, s, "pxa2xx-rtc", 0x10000); sysbus_init_mmio(dev, &s->iomem); - - return 0; } static void pxa2xx_rtc_pre_save(void *opaque) @@ -1195,9 +1194,7 @@ static const VMStateDescription vmstate_pxa2xx_rtc_regs = { static void pxa2xx_rtc_sysbus_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = pxa2xx_rtc_init; dc->desc = "PXA2xx RTC Controller"; dc->vmsd = &vmstate_pxa2xx_rtc_regs; } @@ -1206,6 +1203,7 @@ static const TypeInfo pxa2xx_rtc_sysbus_info = { .name = TYPE_PXA2XX_RTC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PXA2xxRTCState), + .instance_init = pxa2xx_rtc_init, .class_init = pxa2xx_rtc_sysbus_class_init, }; @@ -1501,19 +1499,18 @@ PXA2xxI2CState *pxa2xx_i2c_init(hwaddr base, return s; } -static int pxa2xx_i2c_initfn(SysBusDevice *sbd) +static void pxa2xx_i2c_initfn(Object *obj) { - DeviceState *dev = DEVICE(sbd); - PXA2xxI2CState *s = PXA2XX_I2C(dev); + DeviceState *dev = DEVICE(obj); + PXA2xxI2CState *s = PXA2XX_I2C(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); s->bus = i2c_init_bus(dev, "i2c"); - memory_region_init_io(&s->iomem, OBJECT(s), &pxa2xx_i2c_ops, s, + memory_region_init_io(&s->iomem, obj, &pxa2xx_i2c_ops, s, "pxa2xx-i2c", s->region_size); sysbus_init_mmio(sbd, &s->iomem); sysbus_init_irq(sbd, &s->irq); - - return 0; } I2CBus *pxa2xx_i2c_bus(PXA2xxI2CState *s) @@ -1530,9 +1527,7 @@ static Property pxa2xx_i2c_properties[] = { static void pxa2xx_i2c_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = pxa2xx_i2c_initfn; dc->desc = "PXA2xx I2C Bus Controller"; dc->vmsd = &vmstate_pxa2xx_i2c; dc->props = pxa2xx_i2c_properties; @@ -1542,6 +1537,7 @@ static const TypeInfo pxa2xx_i2c_info = { .name = TYPE_PXA2XX_I2C, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PXA2xxI2CState), + .instance_init = pxa2xx_i2c_initfn, .class_init = pxa2xx_i2c_class_init, }; @@ -2169,10 +2165,8 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space, s->ssp[i] = (SSIBus *)qdev_get_child_bus(dev, "ssi"); } - if (usb_enabled()) { - sysbus_create_simple("sysbus-ohci", 0x4c000000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_USBH1)); - } + sysbus_create_simple("sysbus-ohci", 0x4c000000, + qdev_get_gpio_in(s->pic, PXA2XX_PIC_USBH1)); s->pcmcia[0] = pxa2xx_pcmcia_init(address_space, 0x20000000); s->pcmcia[1] = pxa2xx_pcmcia_init(address_space, 0x30000000); @@ -2302,10 +2296,8 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size) s->ssp[i] = (SSIBus *)qdev_get_child_bus(dev, "ssi"); } - if (usb_enabled()) { - sysbus_create_simple("sysbus-ohci", 0x4c000000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_USBH1)); - } + sysbus_create_simple("sysbus-ohci", 0x4c000000, + qdev_get_gpio_in(s->pic, PXA2XX_PIC_USBH1)); s->pcmcia[0] = pxa2xx_pcmcia_init(address_space, 0x20000000); s->pcmcia[1] = pxa2xx_pcmcia_init(address_space, 0x30000000); diff --git a/hw/arm/pxa2xx_gpio.c b/hw/arm/pxa2xx_gpio.c index 67e7e7094..576a8eb91 100644 --- a/hw/arm/pxa2xx_gpio.c +++ b/hw/arm/pxa2xx_gpio.c @@ -8,9 +8,11 @@ */ #include "qemu/osdep.h" +#include "cpu.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "hw/arm/pxa.h" +#include "qemu/log.h" #define PXA2XX_GPIO_BANKS 4 diff --git a/hw/arm/pxa2xx_pic.c b/hw/arm/pxa2xx_pic.c index 7e51532cd..b516ced8c 100644 --- a/hw/arm/pxa2xx_pic.c +++ b/hw/arm/pxa2xx_pic.c @@ -310,17 +310,10 @@ static VMStateDescription vmstate_pxa2xx_pic_regs = { }, }; -static int pxa2xx_pic_initfn(SysBusDevice *dev) -{ - return 0; -} - static void pxa2xx_pic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = pxa2xx_pic_initfn; dc->desc = "PXA2xx PIC"; dc->vmsd = &vmstate_pxa2xx_pic_regs; } diff --git a/hw/arm/realview.c b/hw/arm/realview.c index 3222b360e..8eafccaf1 100644 --- a/hw/arm/realview.c +++ b/hw/arm/realview.c @@ -23,6 +23,7 @@ #include "sysemu/block-backend.h" #include "exec/address-spaces.h" #include "qemu/error-report.h" +#include "hw/char/pl011.h" #define SMP_BOOT_ADDR 0xe0000000 #define SMP_BOOTREG_ADDR 0x10000030 @@ -202,10 +203,10 @@ static void realview_init(MachineState *machine, sysbus_create_simple("pl050_keyboard", 0x10006000, pic[20]); sysbus_create_simple("pl050_mouse", 0x10007000, pic[21]); - sysbus_create_simple("pl011", 0x10009000, pic[12]); - sysbus_create_simple("pl011", 0x1000a000, pic[13]); - sysbus_create_simple("pl011", 0x1000b000, pic[14]); - sysbus_create_simple("pl011", 0x1000c000, pic[15]); + pl011_create(0x10009000, pic[12], serial_hds[0]); + pl011_create(0x1000a000, pic[13], serial_hds[1]); + pl011_create(0x1000b000, pic[14], serial_hds[2]); + pl011_create(0x1000c000, pic[15], serial_hds[3]); /* DMA controller is optional, apparently. */ sysbus_create_simple("pl081", 0x10030000, pic[24]); @@ -253,7 +254,7 @@ static void realview_init(MachineState *machine, sysbus_connect_irq(busdev, 2, pic[50]); sysbus_connect_irq(busdev, 3, pic[51]); pci_bus = (PCIBus *)qdev_get_child_bus(dev, "pci"); - if (usb_enabled()) { + if (machine_usb(machine)) { pci_create_simple(pci_bus, -1, "pci-ohci"); } n = drive_get_max_bus(IF_SCSI); diff --git a/hw/arm/sabrelite.c b/hw/arm/sabrelite.c new file mode 100644 index 000000000..4e7ac8cc4 --- /dev/null +++ b/hw/arm/sabrelite.c @@ -0,0 +1,127 @@ +/* + * SABRELITE Board System emulation. + * + * Copyright (c) 2015 Jean-Christophe Dubois <jcd@tribudubois.net> + * + * This code is licensed under the GPL, version 2 or later. + * See the file `COPYING' in the top level directory. + * + * It (partially) emulates a sabrelite board, with a Freescale + * i.MX6 SoC + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "hw/arm/fsl-imx6.h" +#include "hw/boards.h" +#include "sysemu/sysemu.h" +#include "qemu/error-report.h" +#include "sysemu/qtest.h" + +typedef struct IMX6Sabrelite { + FslIMX6State soc; + MemoryRegion ram; +} IMX6Sabrelite; + +static struct arm_boot_info sabrelite_binfo = { + /* DDR memory start */ + .loader_start = FSL_IMX6_MMDC_ADDR, + /* No board ID, we boot from DT tree */ + .board_id = -1, +}; + +/* No need to do any particular setup for secondary boot */ +static void sabrelite_write_secondary(ARMCPU *cpu, + const struct arm_boot_info *info) +{ +} + +/* Secondary cores are reset through SRC device */ +static void sabrelite_reset_secondary(ARMCPU *cpu, + const struct arm_boot_info *info) +{ +} + +static void sabrelite_init(MachineState *machine) +{ + IMX6Sabrelite *s = g_new0(IMX6Sabrelite, 1); + Error *err = NULL; + + /* Check the amount of memory is compatible with the SOC */ + if (machine->ram_size > FSL_IMX6_MMDC_SIZE) { + error_report("RAM size " RAM_ADDR_FMT " above max supported (%08x)", + machine->ram_size, FSL_IMX6_MMDC_SIZE); + exit(1); + } + + object_initialize(&s->soc, sizeof(s->soc), TYPE_FSL_IMX6); + object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc), + &error_abort); + + object_property_set_bool(OBJECT(&s->soc), true, "realized", &err); + if (err != NULL) { + error_report("%s", error_get_pretty(err)); + exit(1); + } + + memory_region_allocate_system_memory(&s->ram, NULL, "sabrelite.ram", + machine->ram_size); + memory_region_add_subregion(get_system_memory(), FSL_IMX6_MMDC_ADDR, + &s->ram); + + { + /* + * TODO: Ideally we would expose the chip select and spi bus on the + * SoC object using alias properties; then we would not need to + * directly access the underlying spi device object. + */ + /* Add the sst25vf016b NOR FLASH memory to first SPI */ + Object *spi_dev; + + spi_dev = object_resolve_path_component(OBJECT(&s->soc), "spi1"); + if (spi_dev) { + SSIBus *spi_bus; + + spi_bus = (SSIBus *)qdev_get_child_bus(DEVICE(spi_dev), "spi"); + if (spi_bus) { + DeviceState *flash_dev; + 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); + } + } + } + + sabrelite_binfo.ram_size = machine->ram_size; + sabrelite_binfo.kernel_filename = machine->kernel_filename; + sabrelite_binfo.kernel_cmdline = machine->kernel_cmdline; + sabrelite_binfo.initrd_filename = machine->initrd_filename; + sabrelite_binfo.nb_cpus = smp_cpus; + sabrelite_binfo.secure_boot = true; + sabrelite_binfo.write_secondary_boot = sabrelite_write_secondary; + sabrelite_binfo.secondary_cpu_reset_hook = sabrelite_reset_secondary; + + if (!qtest_enabled()) { + arm_load_kernel(&s->soc.cpu[0], &sabrelite_binfo); + } +} + +static void sabrelite_machine_init(MachineClass *mc) +{ + mc->desc = "Freescale i.MX6 Quad SABRE Lite Board (Cortex A9)"; + mc->init = sabrelite_init; + mc->max_cpus = FSL_IMX6_NUM_CPUS; +} + +DEFINE_MACHINE("sabrelite", sabrelite_machine_init) diff --git a/hw/arm/spitz.c b/hw/arm/spitz.c index bf61d63b5..41cc2eeeb 100644 --- a/hw/arm/spitz.c +++ b/hw/arm/spitz.c @@ -164,9 +164,10 @@ static void sl_flash_register(PXA2xxState *cpu, int size) sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, FLASH_BASE); } -static int sl_nand_init(SysBusDevice *dev) +static void sl_nand_init(Object *obj) { - SLNANDState *s = SL_NAND(dev); + SLNANDState *s = SL_NAND(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); DriveInfo *nand; s->ctl = 0; @@ -175,10 +176,8 @@ static int sl_nand_init(SysBusDevice *dev) s->nand = nand_init(nand ? blk_by_legacy_dinfo(nand) : NULL, s->manf_id, s->chip_id); - memory_region_init_io(&s->iomem, OBJECT(s), &sl_ops, s, "sl", 0x40); + memory_region_init_io(&s->iomem, obj, &sl_ops, s, "sl", 0x40); sysbus_init_mmio(dev, &s->iomem); - - return 0; } /* Spitz Keyboard */ @@ -501,10 +500,10 @@ static void spitz_keyboard_register(PXA2xxState *cpu) qemu_add_kbd_event_handler(spitz_keyboard_handler, s); } -static int spitz_keyboard_init(SysBusDevice *sbd) +static void spitz_keyboard_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - SpitzKeyboardState *s = SPITZ_KEYBOARD(dev); + DeviceState *dev = DEVICE(obj); + SpitzKeyboardState *s = SPITZ_KEYBOARD(obj); int i, j; for (i = 0; i < 0x80; i ++) @@ -519,8 +518,6 @@ static int spitz_keyboard_init(SysBusDevice *sbd) s->kbdtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, spitz_keyboard_tick, s); qdev_init_gpio_in(dev, spitz_keyboard_strobe, SPITZ_KEY_STROBE_NUM); qdev_init_gpio_out(dev, s->sense, SPITZ_KEY_SENSE_NUM); - - return 0; } /* LCD backlight controller */ @@ -601,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 */ @@ -669,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); @@ -678,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) @@ -1065,9 +1058,7 @@ static Property sl_nand_properties[] = { static void sl_nand_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = sl_nand_init; dc->vmsd = &vmstate_sl_nand_info; dc->props = sl_nand_properties; /* Reason: init() method uses drive_get() */ @@ -1078,6 +1069,7 @@ static const TypeInfo sl_nand_info = { .name = TYPE_SL_NAND, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SLNANDState), + .instance_init = sl_nand_init, .class_init = sl_nand_class_init, }; @@ -1097,9 +1089,7 @@ static VMStateDescription vmstate_spitz_kbd = { static void spitz_keyboard_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = spitz_keyboard_init; dc->vmsd = &vmstate_spitz_kbd; } @@ -1107,6 +1097,7 @@ static const TypeInfo spitz_keyboard_info = { .name = TYPE_SPITZ_KEYBOARD, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SpitzKeyboardState), + .instance_init = spitz_keyboard_init, .class_init = spitz_keyboard_class_init, }; @@ -1126,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; } @@ -1155,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/stellaris.c b/hw/arm/stellaris.c index c1766f856..794a3ada7 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -17,8 +17,10 @@ #include "hw/i2c/i2c.h" #include "net/net.h" #include "hw/boards.h" +#include "qemu/log.h" #include "exec/address-spaces.h" #include "sysemu/sysemu.h" +#include "hw/char/pl011.h" #define GPIO_A 0 #define GPIO_B 1 @@ -316,23 +318,22 @@ static const VMStateDescription vmstate_stellaris_gptm = { } }; -static int stellaris_gptm_init(SysBusDevice *sbd) +static void stellaris_gptm_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - gptm_state *s = STELLARIS_GPTM(dev); + DeviceState *dev = DEVICE(obj); + gptm_state *s = STELLARIS_GPTM(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); sysbus_init_irq(sbd, &s->irq); qdev_init_gpio_out(dev, &s->trigger, 1); - memory_region_init_io(&s->iomem, OBJECT(s), &gptm_ops, s, + memory_region_init_io(&s->iomem, obj, &gptm_ops, s, "gptm", 0x1000); sysbus_init_mmio(sbd, &s->iomem); s->opaque[0] = s->opaque[1] = s; s->timer[0] = timer_new_ns(QEMU_CLOCK_VIRTUAL, gptm_tick, &s->opaque[0]); s->timer[1] = timer_new_ns(QEMU_CLOCK_VIRTUAL, gptm_tick, &s->opaque[1]); - vmstate_register(dev, -1, &vmstate_stellaris_gptm, s); - return 0; } @@ -873,23 +874,22 @@ static const VMStateDescription vmstate_stellaris_i2c = { } }; -static int stellaris_i2c_init(SysBusDevice *sbd) +static void stellaris_i2c_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - stellaris_i2c_state *s = STELLARIS_I2C(dev); + DeviceState *dev = DEVICE(obj); + stellaris_i2c_state *s = STELLARIS_I2C(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); I2CBus *bus; sysbus_init_irq(sbd, &s->irq); bus = i2c_init_bus(dev, "i2c"); s->bus = bus; - memory_region_init_io(&s->iomem, OBJECT(s), &stellaris_i2c_ops, s, + memory_region_init_io(&s->iomem, obj, &stellaris_i2c_ops, s, "i2c", 0x1000); sysbus_init_mmio(sbd, &s->iomem); /* ??? For now we only implement the master interface. */ stellaris_i2c_reset(s); - vmstate_register(dev, -1, &vmstate_stellaris_i2c, s); - return 0; } /* Analogue to Digital Converter. This is only partially implemented, @@ -1160,23 +1160,22 @@ static const VMStateDescription vmstate_stellaris_adc = { } }; -static int stellaris_adc_init(SysBusDevice *sbd) +static void stellaris_adc_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - stellaris_adc_state *s = STELLARIS_ADC(dev); + DeviceState *dev = DEVICE(obj); + stellaris_adc_state *s = STELLARIS_ADC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); int n; for (n = 0; n < 4; n++) { sysbus_init_irq(sbd, &s->irq[n]); } - memory_region_init_io(&s->iomem, OBJECT(s), &stellaris_adc_ops, s, + memory_region_init_io(&s->iomem, obj, &stellaris_adc_ops, s, "adc", 0x1000); sysbus_init_mmio(sbd, &s->iomem); stellaris_adc_reset(s); qdev_init_gpio_in(dev, stellaris_adc_trigger, 1); - vmstate_register(dev, -1, &vmstate_stellaris_adc, s); - return 0; } static @@ -1305,8 +1304,9 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model, for (i = 0; i < 4; i++) { if (board->dc2 & (1 << i)) { - sysbus_create_simple("pl011_luminary", 0x4000c000 + i * 0x1000, - qdev_get_gpio_in(nvic, uart_irq[i])); + pl011_luminary_create(0x4000c000 + i * 0x1000, + qdev_get_gpio_in(nvic, uart_irq[i]), + serial_hds[i]); } } if (board->dc2 & (1 << 4)) { @@ -1425,43 +1425,46 @@ type_init(stellaris_machine_init) static void stellaris_i2c_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); - sdc->init = stellaris_i2c_init; + dc->vmsd = &vmstate_stellaris_i2c; } static const TypeInfo stellaris_i2c_info = { .name = TYPE_STELLARIS_I2C, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(stellaris_i2c_state), + .instance_init = stellaris_i2c_init, .class_init = stellaris_i2c_class_init, }; static void stellaris_gptm_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); - sdc->init = stellaris_gptm_init; + dc->vmsd = &vmstate_stellaris_gptm; } static const TypeInfo stellaris_gptm_info = { .name = TYPE_STELLARIS_GPTM, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(gptm_state), + .instance_init = stellaris_gptm_init, .class_init = stellaris_gptm_class_init, }; static void stellaris_adc_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); - sdc->init = stellaris_adc_init; + dc->vmsd = &vmstate_stellaris_adc; } static const TypeInfo stellaris_adc_info = { .name = TYPE_STELLARIS_ADC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(stellaris_adc_state), + .instance_init = stellaris_adc_init, .class_init = stellaris_adc_class_init, }; diff --git a/hw/arm/stm32f205_soc.c b/hw/arm/stm32f205_soc.c index a5ea1e237..de26b8caf 100644 --- a/hw/arm/stm32f205_soc.c +++ b/hw/arm/stm32f205_soc.c @@ -25,7 +25,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu-common.h" -#include "cpu.h" #include "hw/arm/arm.h" #include "exec/address-spaces.h" #include "hw/arm/stm32f205_soc.h" @@ -108,6 +107,7 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp) /* Attach UART (uses USART registers) and USART controllers */ for (i = 0; i < STM_NUM_USARTS; i++) { usartdev = DEVICE(&(s->usart[i])); + qdev_prop_set_chr(usartdev, "chardev", i < MAX_SERIAL_PORTS ? serial_hds[i] : NULL); object_property_set_bool(OBJECT(&s->usart[i]), true, "realized", &err); if (err != NULL) { error_propagate(errp, err); diff --git a/hw/arm/strongarm.c b/hw/arm/strongarm.c index 1eeb1ab39..f1b2c6c96 100644 --- a/hw/arm/strongarm.c +++ b/hw/arm/strongarm.c @@ -38,6 +38,7 @@ #include "sysemu/sysemu.h" #include "hw/ssi/ssi.h" #include "qemu/cutils.h" +#include "qemu/log.h" //#define DEBUG @@ -179,19 +180,18 @@ static const MemoryRegionOps strongarm_pic_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int strongarm_pic_initfn(SysBusDevice *sbd) +static void strongarm_pic_initfn(Object *obj) { - DeviceState *dev = DEVICE(sbd); - StrongARMPICState *s = STRONGARM_PIC(dev); + DeviceState *dev = DEVICE(obj); + StrongARMPICState *s = STRONGARM_PIC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); qdev_init_gpio_in(dev, strongarm_pic_set_irq, SA_PIC_SRCS); - memory_region_init_io(&s->iomem, OBJECT(s), &strongarm_pic_ops, s, + memory_region_init_io(&s->iomem, obj, &strongarm_pic_ops, s, "pic", 0x1000); sysbus_init_mmio(sbd, &s->iomem); sysbus_init_irq(sbd, &s->irq); sysbus_init_irq(sbd, &s->fiq); - - return 0; } static int strongarm_pic_post_load(void *opaque, int version_id) @@ -217,9 +217,7 @@ static VMStateDescription vmstate_strongarm_pic_regs = { static void strongarm_pic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = strongarm_pic_initfn; dc->desc = "StrongARM PIC"; dc->vmsd = &vmstate_strongarm_pic_regs; } @@ -228,6 +226,7 @@ static const TypeInfo strongarm_pic_info = { .name = TYPE_STRONGARM_PIC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(StrongARMPICState), + .instance_init = strongarm_pic_initfn, .class_init = strongarm_pic_class_init, }; @@ -381,9 +380,10 @@ static const MemoryRegionOps strongarm_rtc_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int strongarm_rtc_init(SysBusDevice *dev) +static void strongarm_rtc_init(Object *obj) { - StrongARMRTCState *s = STRONGARM_RTC(dev); + StrongARMRTCState *s = STRONGARM_RTC(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); struct tm tm; s->rttr = 0x0; @@ -400,11 +400,9 @@ static int strongarm_rtc_init(SysBusDevice *dev) sysbus_init_irq(dev, &s->rtc_irq); sysbus_init_irq(dev, &s->rtc_hz_irq); - memory_region_init_io(&s->iomem, OBJECT(s), &strongarm_rtc_ops, s, + memory_region_init_io(&s->iomem, obj, &strongarm_rtc_ops, s, "rtc", 0x10000); sysbus_init_mmio(dev, &s->iomem); - - return 0; } static void strongarm_rtc_pre_save(void *opaque) @@ -443,9 +441,7 @@ static const VMStateDescription vmstate_strongarm_rtc_regs = { static void strongarm_rtc_sysbus_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = strongarm_rtc_init; dc->desc = "StrongARM RTC Controller"; dc->vmsd = &vmstate_strongarm_rtc_regs; } @@ -454,6 +450,7 @@ static const TypeInfo strongarm_rtc_sysbus_info = { .name = TYPE_STRONGARM_RTC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(StrongARMRTCState), + .instance_init = strongarm_rtc_init, .class_init = strongarm_rtc_sysbus_class_init, }; @@ -646,16 +643,17 @@ static DeviceState *strongarm_gpio_init(hwaddr base, return dev; } -static int strongarm_gpio_initfn(SysBusDevice *sbd) +static void strongarm_gpio_initfn(Object *obj) { - DeviceState *dev = DEVICE(sbd); - StrongARMGPIOInfo *s = STRONGARM_GPIO(dev); + DeviceState *dev = DEVICE(obj); + StrongARMGPIOInfo *s = STRONGARM_GPIO(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); int i; qdev_init_gpio_in(dev, strongarm_gpio_set, 28); qdev_init_gpio_out(dev, s->handler, 28); - memory_region_init_io(&s->iomem, OBJECT(s), &strongarm_gpio_ops, s, + memory_region_init_io(&s->iomem, obj, &strongarm_gpio_ops, s, "gpio", 0x1000); sysbus_init_mmio(sbd, &s->iomem); @@ -663,8 +661,6 @@ static int strongarm_gpio_initfn(SysBusDevice *sbd) sysbus_init_irq(sbd, &s->irqs[i]); } sysbus_init_irq(sbd, &s->irqX); - - return 0; } static const VMStateDescription vmstate_strongarm_gpio_regs = { @@ -687,9 +683,7 @@ static const VMStateDescription vmstate_strongarm_gpio_regs = { static void strongarm_gpio_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = strongarm_gpio_initfn; dc->desc = "StrongARM GPIO controller"; dc->vmsd = &vmstate_strongarm_gpio_regs; } @@ -698,6 +692,7 @@ static const TypeInfo strongarm_gpio_info = { .name = TYPE_STRONGARM_GPIO, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(StrongARMGPIOInfo), + .instance_init = strongarm_gpio_initfn, .class_init = strongarm_gpio_class_init, }; @@ -824,20 +819,19 @@ static const MemoryRegionOps strongarm_ppc_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int strongarm_ppc_init(SysBusDevice *sbd) +static void strongarm_ppc_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - StrongARMPPCInfo *s = STRONGARM_PPC(dev); + DeviceState *dev = DEVICE(obj); + StrongARMPPCInfo *s = STRONGARM_PPC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); qdev_init_gpio_in(dev, strongarm_ppc_set, 22); qdev_init_gpio_out(dev, s->handler, 22); - memory_region_init_io(&s->iomem, OBJECT(s), &strongarm_ppc_ops, s, + memory_region_init_io(&s->iomem, obj, &strongarm_ppc_ops, s, "ppc", 0x1000); sysbus_init_mmio(sbd, &s->iomem); - - return 0; } static const VMStateDescription vmstate_strongarm_ppc_regs = { @@ -859,9 +853,7 @@ static const VMStateDescription vmstate_strongarm_ppc_regs = { static void strongarm_ppc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = strongarm_ppc_init; dc->desc = "StrongARM PPC controller"; dc->vmsd = &vmstate_strongarm_ppc_regs; } @@ -870,6 +862,7 @@ static const TypeInfo strongarm_ppc_info = { .name = TYPE_STRONGARM_PPC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(StrongARMPPCInfo), + .instance_init = strongarm_ppc_init, .class_init = strongarm_ppc_class_init, }; @@ -1231,11 +1224,12 @@ static const MemoryRegionOps strongarm_uart_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int strongarm_uart_init(SysBusDevice *dev) +static void strongarm_uart_init(Object *obj) { - StrongARMUARTState *s = STRONGARM_UART(dev); + StrongARMUARTState *s = STRONGARM_UART(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); - memory_region_init_io(&s->iomem, OBJECT(s), &strongarm_uart_ops, s, + memory_region_init_io(&s->iomem, obj, &strongarm_uart_ops, s, "uart", 0x10000); sysbus_init_mmio(dev, &s->iomem); sysbus_init_irq(dev, &s->irq); @@ -1250,8 +1244,6 @@ static int strongarm_uart_init(SysBusDevice *dev) strongarm_uart_event, s); } - - return 0; } static void strongarm_uart_reset(DeviceState *dev) @@ -1321,9 +1313,7 @@ static Property strongarm_uart_properties[] = { static void strongarm_uart_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = strongarm_uart_init; dc->desc = "StrongARM UART controller"; dc->reset = strongarm_uart_reset; dc->vmsd = &vmstate_strongarm_uart_regs; @@ -1334,6 +1324,7 @@ static const TypeInfo strongarm_uart_info = { .name = TYPE_STRONGARM_UART, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(StrongARMUARTState), + .instance_init = strongarm_uart_init, .class_init = strongarm_uart_class_init, }; diff --git a/hw/arm/strongarm.h b/hw/arm/strongarm.h index 2893f9444..1470eac4f 100644 --- a/hw/arm/strongarm.h +++ b/hw/arm/strongarm.h @@ -1,7 +1,8 @@ -#ifndef _STRONGARM_H -#define _STRONGARM_H +#ifndef STRONGARM_H +#define STRONGARM_H #include "exec/memory.h" +#include "target-arm/cpu-qom.h" #define SA_CS0 0x00000000 #define SA_CS1 0x08000000 diff --git a/hw/arm/tosa.c b/hw/arm/tosa.c index 4e9494f94..2db66508b 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/trace-events b/hw/arm/trace-events new file mode 100644 index 000000000..d5f33a2a0 --- /dev/null +++ b/hw/arm/trace-events @@ -0,0 +1,4 @@ +# See docs/tracing.txt for syntax documentation. + +# hw/arm/virt-acpi-build.c +virt_acpi_setup(void) "No fw cfg or ACPI disabled. Bailing out." diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c index e5a80c2d2..8ae5392bc 100644 --- a/hw/arm/versatilepb.c +++ b/hw/arm/versatilepb.c @@ -23,6 +23,7 @@ #include "exec/address-spaces.h" #include "hw/block/flash.h" #include "qemu/error-report.h" +#include "hw/char/pl011.h" #define VERSATILE_FLASH_ADDR 0x34000000 #define VERSATILE_FLASH_SIZE (64 * 1024 * 1024) @@ -153,10 +154,11 @@ static const MemoryRegionOps vpb_sic_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int vpb_sic_init(SysBusDevice *sbd) +static void vpb_sic_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - vpb_sic_state *s = VERSATILE_PB_SIC(dev); + DeviceState *dev = DEVICE(obj); + vpb_sic_state *s = VERSATILE_PB_SIC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); int i; qdev_init_gpio_in(dev, vpb_sic_set_irq, 32); @@ -164,10 +166,9 @@ static int vpb_sic_init(SysBusDevice *sbd) sysbus_init_irq(sbd, &s->parent[i]); } s->irq = 31; - memory_region_init_io(&s->iomem, OBJECT(s), &vpb_sic_ops, s, + memory_region_init_io(&s->iomem, obj, &vpb_sic_ops, s, "vpb-sic", 0x1000); sysbus_init_mmio(sbd, &s->iomem); - return 0; } /* Board init. */ @@ -275,7 +276,7 @@ static void versatile_init(MachineState *machine, int board_id) pci_nic_init_nofail(nd, pci_bus, "rtl8139", NULL); } } - if (usb_enabled()) { + if (machine_usb(machine)) { pci_create_simple(pci_bus, -1, "pci-ohci"); } n = drive_get_max_bus(IF_SCSI); @@ -284,10 +285,10 @@ static void versatile_init(MachineState *machine, int board_id) n--; } - sysbus_create_simple("pl011", 0x101f1000, pic[12]); - sysbus_create_simple("pl011", 0x101f2000, pic[13]); - sysbus_create_simple("pl011", 0x101f3000, pic[14]); - sysbus_create_simple("pl011", 0x10009000, sic[6]); + pl011_create(0x101f1000, pic[12], serial_hds[0]); + pl011_create(0x101f2000, pic[13], serial_hds[1]); + pl011_create(0x101f3000, pic[14], serial_hds[2]); + pl011_create(0x10009000, sic[6], serial_hds[3]); sysbus_create_simple("pl080", 0x10130000, pic[17]); sysbus_create_simple("sp804", 0x101e2000, pic[4]); @@ -427,9 +428,7 @@ type_init(versatile_machine_init) static void vpb_sic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = vpb_sic_init; dc->vmsd = &vmstate_vpb_sic; } @@ -437,6 +436,7 @@ static const TypeInfo vpb_sic_info = { .name = TYPE_VERSATILE_PB_SIC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(vpb_sic_state), + .instance_init = vpb_sic_init, .class_init = vpb_sic_class_init, }; diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index 70b3e701e..58760f40c 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -39,6 +39,7 @@ #include "sysemu/device_tree.h" #include "qemu/error-report.h" #include <libfdt.h> +#include "hw/char/pl011.h" #define VEXPRESS_BOARD_ID 0x8e0 #define VEXPRESS_FLASH_SIZE (64 * 1024 * 1024) @@ -631,10 +632,10 @@ static void vexpress_common_init(MachineState *machine) sysbus_create_simple("pl050_keyboard", map[VE_KMI0], pic[12]); sysbus_create_simple("pl050_mouse", map[VE_KMI1], pic[13]); - sysbus_create_simple("pl011", map[VE_UART0], pic[5]); - sysbus_create_simple("pl011", map[VE_UART1], pic[6]); - sysbus_create_simple("pl011", map[VE_UART2], pic[7]); - sysbus_create_simple("pl011", map[VE_UART3], pic[8]); + pl011_create(map[VE_UART0], pic[5], serial_hds[0]); + pl011_create(map[VE_UART1], pic[6], serial_hds[1]); + pl011_create(map[VE_UART2], pic[7], serial_hds[2]); + pl011_create(map[VE_UART3], pic[8], serial_hds[3]); sysbus_create_simple("sp804", map[VE_TIMER01], pic[2]); sysbus_create_simple("sp804", map[VE_TIMER23], pic[3]); diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index f51fe396c..28fc59c66 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -43,6 +43,7 @@ #include "hw/acpi/aml-build.h" #include "hw/pci/pcie_host.h" #include "hw/pci/pci.h" +#include "sysemu/numa.h" #define ARM_SPI_BASE 32 #define ACPI_POWER_BUTTON_DEVICE "PWRB" @@ -230,7 +231,8 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, aml_append(rbuf, aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, AML_NON_CACHEABLE, AML_READ_WRITE, 0x0000, - base_mmio_high, base_mmio_high, 0x0000, + base_mmio_high, + base_mmio_high + size_mmio_high - 1, 0x0000, size_mmio_high)); } @@ -352,11 +354,14 @@ static void acpi_dsdt_add_power_button(Aml *scope) /* RSDP */ static GArray * -build_rsdp(GArray *rsdp_table, GArray *linker, unsigned rsdt) +build_rsdp(GArray *rsdp_table, BIOSLinker *linker, unsigned rsdt_tbl_offset) { AcpiRsdpDescriptor *rsdp = acpi_data_push(rsdp_table, sizeof *rsdp); + unsigned rsdt_pa_size = sizeof(rsdp->rsdt_physical_address); + unsigned rsdt_pa_offset = + (char *)&rsdp->rsdt_physical_address - rsdp_table->data; - bios_linker_loader_alloc(linker, ACPI_BUILD_RSDP_FILE, 16, + bios_linker_loader_alloc(linker, ACPI_BUILD_RSDP_FILE, rsdp_table, 16, true /* fseg memory */); memcpy(&rsdp->signature, "RSD PTR ", sizeof(rsdp->signature)); @@ -364,24 +369,21 @@ build_rsdp(GArray *rsdp_table, GArray *linker, unsigned rsdt) rsdp->length = cpu_to_le32(sizeof(*rsdp)); rsdp->revision = 0x02; - /* Point to RSDT */ - rsdp->rsdt_physical_address = cpu_to_le32(rsdt); /* Address to be filled by Guest linker */ - bios_linker_loader_add_pointer(linker, ACPI_BUILD_RSDP_FILE, - ACPI_BUILD_TABLE_FILE, - rsdp_table, &rsdp->rsdt_physical_address, - sizeof rsdp->rsdt_physical_address); - rsdp->checksum = 0; + bios_linker_loader_add_pointer(linker, + ACPI_BUILD_RSDP_FILE, rsdt_pa_offset, rsdt_pa_size, + ACPI_BUILD_TABLE_FILE, rsdt_tbl_offset); + /* Checksum to be filled by Guest linker */ bios_linker_loader_add_checksum(linker, ACPI_BUILD_RSDP_FILE, - rsdp_table, rsdp, sizeof *rsdp, - &rsdp->checksum); + (char *)rsdp - rsdp_table->data, sizeof *rsdp, + (char *)&rsdp->checksum - rsdp_table->data); return rsdp_table; } static void -build_spcr(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) +build_spcr(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info) { AcpiSerialPortConsoleRedirection *spcr; const MemMapEntry *uart_memmap = &guest_info->memmap[VIRT_UART]; @@ -414,7 +416,52 @@ build_spcr(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) } static void -build_mcfg(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) +build_srat(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info) +{ + AcpiSystemResourceAffinityTable *srat; + AcpiSratProcessorGiccAffinity *core; + AcpiSratMemoryAffinity *numamem; + int i, j, srat_start; + uint64_t mem_base; + uint32_t *cpu_node = g_malloc0(guest_info->smp_cpus * sizeof(uint32_t)); + + for (i = 0; i < guest_info->smp_cpus; i++) { + for (j = 0; j < nb_numa_nodes; j++) { + if (test_bit(i, numa_info[j].node_cpu)) { + cpu_node[i] = j; + break; + } + } + } + + srat_start = table_data->len; + srat = acpi_data_push(table_data, sizeof(*srat)); + srat->reserved1 = cpu_to_le32(1); + + for (i = 0; i < guest_info->smp_cpus; ++i) { + core = acpi_data_push(table_data, sizeof(*core)); + core->type = ACPI_SRAT_PROCESSOR_GICC; + core->length = sizeof(*core); + core->proximity = cpu_to_le32(cpu_node[i]); + core->acpi_processor_uid = cpu_to_le32(i); + core->flags = cpu_to_le32(1); + } + g_free(cpu_node); + + mem_base = guest_info->memmap[VIRT_MEM].base; + for (i = 0; i < nb_numa_nodes; ++i) { + numamem = acpi_data_push(table_data, sizeof(*numamem)); + build_srat_memory(numamem, mem_base, numa_info[i].node_mem, i, + MEM_AFFINITY_ENABLED); + mem_base += numa_info[i].node_mem; + } + + build_header(linker, table_data, (void *)srat, "SRAT", + table_data->len - srat_start, 3, NULL, NULL); +} + +static void +build_mcfg(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info) { AcpiTableMcfg *mcfg; const MemMapEntry *memmap = guest_info->memmap; @@ -434,7 +481,7 @@ build_mcfg(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) /* GTDT */ static void -build_gtdt(GArray *table_data, GArray *linker) +build_gtdt(GArray *table_data, BIOSLinker *linker) { int gtdt_start = table_data->len; AcpiGenericTimerTable *gtdt; @@ -460,7 +507,7 @@ build_gtdt(GArray *table_data, GArray *linker) /* MADT */ static void -build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) +build_madt(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info) { int madt_start = table_data->len; const MemMapEntry *memmap = guest_info->memmap; @@ -476,6 +523,7 @@ build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) gicd->type = ACPI_APIC_GENERIC_DISTRIBUTOR; gicd->length = sizeof(*gicd); gicd->base_address = memmap[VIRT_GIC_DIST].base; + gicd->version = guest_info->gic_version; for (i = 0; i < guest_info->smp_cpus; i++) { AcpiMadtGenericInterrupt *gicc = acpi_data_push(table_data, @@ -491,6 +539,10 @@ build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) gicc->arm_mpidr = armcpu->mp_affinity; gicc->uid = i; gicc->flags = cpu_to_le32(ACPI_GICC_ENABLED); + + if (armcpu->has_pmu) { + gicc->performance_interrupt = cpu_to_le32(PPI(VIRTUAL_PMU_IRQ)); + } } if (guest_info->gic_version == 3) { @@ -519,9 +571,10 @@ build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) /* FADT */ static void -build_fadt(GArray *table_data, GArray *linker, unsigned dsdt) +build_fadt(GArray *table_data, BIOSLinker *linker, unsigned dsdt_tbl_offset) { AcpiFadtDescriptorRev5_1 *fadt = acpi_data_push(table_data, sizeof(*fadt)); + unsigned dsdt_entry_offset = (char *)&fadt->dsdt - table_data->data; /* Hardware Reduced = 1 and use PSCI 0.2+ and with HVC */ fadt->flags = cpu_to_le32(1 << ACPI_FADT_F_HW_REDUCED_ACPI); @@ -531,12 +584,10 @@ build_fadt(GArray *table_data, GArray *linker, unsigned dsdt) /* ACPI v5.1 (fadt->revision.fadt->minor_revision) */ fadt->minor_revision = 0x1; - fadt->dsdt = cpu_to_le32(dsdt); /* DSDT address to be filled by Guest linker */ - bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE, - ACPI_BUILD_TABLE_FILE, - table_data, &fadt->dsdt, - sizeof fadt->dsdt); + bios_linker_loader_add_pointer(linker, + ACPI_BUILD_TABLE_FILE, dsdt_entry_offset, sizeof(fadt->dsdt), + ACPI_BUILD_TABLE_FILE, dsdt_tbl_offset); build_header(linker, table_data, (void *)fadt, "FACP", sizeof(*fadt), 5, NULL, NULL); @@ -544,7 +595,7 @@ build_fadt(GArray *table_data, GArray *linker, unsigned dsdt) /* DSDT */ static void -build_dsdt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) +build_dsdt(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info) { Aml *scope, *dsdt; const MemMapEntry *memmap = guest_info->memmap; @@ -604,7 +655,8 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables) table_offsets = g_array_new(false, true /* clear */, sizeof(uint32_t)); - bios_linker_loader_alloc(tables->linker, ACPI_BUILD_TABLE_FILE, + bios_linker_loader_alloc(tables->linker, + ACPI_BUILD_TABLE_FILE, tables_blob, 64, false /* high memory */); /* @@ -638,6 +690,11 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables) acpi_add_table(table_offsets, tables_blob); build_spcr(tables_blob, tables->linker, guest_info); + if (nb_numa_nodes > 0) { + acpi_add_table(table_offsets, tables_blob); + build_srat(tables_blob, tables->linker, guest_info); + } + /* RSDT is pointed to by RSDP */ rsdt = tables_blob->len; build_rsdt(tables_blob, tables->linker, table_offsets, NULL, NULL); @@ -678,7 +735,7 @@ static void virt_acpi_build_update(void *build_opaque) acpi_ram_update(build_state->table_mr, tables.table_data); acpi_ram_update(build_state->rsdp_mr, tables.rsdp); - acpi_ram_update(build_state->linker_mr, tables.linker); + acpi_ram_update(build_state->linker_mr, tables.linker->cmd_blob); acpi_build_tables_cleanup(&tables, true); @@ -736,7 +793,8 @@ void virt_acpi_setup(VirtGuestInfo *guest_info) assert(build_state->table_mr != NULL); build_state->linker_mr = - acpi_add_rom_blob(build_state, tables.linker, "etc/table-loader", 0); + acpi_add_rom_blob(build_state, tables.linker->cmd_blob, + "etc/table-loader", 0); fw_cfg_add_file(guest_info->fw_cfg, ACPI_BUILD_TPMLOG_FILE, tables.tcpalog->data, acpi_data_len(tables.tcpalog)); diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 56d35c771..a193b5a95 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -38,9 +38,11 @@ #include "net/net.h" #include "sysemu/block-backend.h" #include "sysemu/device_tree.h" +#include "sysemu/numa.h" #include "sysemu/sysemu.h" #include "sysemu/kvm.h" #include "hw/boards.h" +#include "hw/compat.h" #include "hw/loader.h" #include "exec/address-spaces.h" #include "qemu/bitops.h" @@ -50,7 +52,8 @@ #include "hw/arm/sysbus-fdt.h" #include "hw/platform-bus.h" #include "hw/arm/fdt.h" -#include "hw/intc/arm_gic_common.h" +#include "hw/intc/arm_gic.h" +#include "hw/intc/arm_gicv3_common.h" #include "kvm_arm.h" #include "hw/smbios/smbios.h" #include "qapi/visitor.h" @@ -80,6 +83,7 @@ typedef struct VirtBoardInfo { typedef struct { MachineClass parent; VirtBoardInfo *daughterboard; + bool disallow_affinity_adjustment; } VirtMachineClass; typedef struct { @@ -97,6 +101,36 @@ typedef struct { #define VIRT_MACHINE_CLASS(klass) \ OBJECT_CLASS_CHECK(VirtMachineClass, klass, TYPE_VIRT_MACHINE) + +#define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \ + static void virt_##major##_##minor##_class_init(ObjectClass *oc, \ + void *data) \ + { \ + MachineClass *mc = MACHINE_CLASS(oc); \ + virt_machine_##major##_##minor##_options(mc); \ + mc->desc = "QEMU " # major "." # minor " ARM Virtual Machine"; \ + if (latest) { \ + mc->alias = "virt"; \ + } \ + } \ + static const TypeInfo machvirt_##major##_##minor##_info = { \ + .name = MACHINE_TYPE_NAME("virt-" # major "." # minor), \ + .parent = TYPE_VIRT_MACHINE, \ + .instance_init = virt_##major##_##minor##_instance_init, \ + .class_init = virt_##major##_##minor##_class_init, \ + }; \ + static void machvirt_machine_##major##_##minor##_init(void) \ + { \ + type_register_static(&machvirt_##major##_##minor##_info); \ + } \ + type_init(machvirt_machine_##major##_##minor##_init); + +#define DEFINE_VIRT_MACHINE_AS_LATEST(major, minor) \ + DEFINE_VIRT_MACHINE_LATEST(major, minor, true) +#define DEFINE_VIRT_MACHINE(major, minor) \ + DEFINE_VIRT_MACHINE_LATEST(major, minor, false) + + /* RAM limit in GB. Since VIRT_MEM starts at the 1GB mark, this means * RAM can go up to the 256GB mark, leaving 256GB of the physical * address space unallocated and free for future use between 256G and 512G. @@ -329,6 +363,7 @@ static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi) { int cpu; int addr_cells = 1; + unsigned int i; /* * From Documentation/devicetree/bindings/arm/cpus.txt @@ -378,6 +413,12 @@ static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi) armcpu->mp_affinity); } + for (i = 0; i < nb_numa_nodes; i++) { + if (test_bit(cpu, numa_info[i].node_cpu)) { + qemu_fdt_setprop_cell(vbi->fdt, nodename, "numa-node-id", i); + } + } + g_free(nodename); } } @@ -428,6 +469,37 @@ static void fdt_add_gic_node(VirtBoardInfo *vbi, int type) qemu_fdt_setprop_cell(vbi->fdt, "/intc", "phandle", vbi->gic_phandle); } +static void fdt_add_pmu_nodes(const VirtBoardInfo *vbi, int gictype) +{ + CPUState *cpu; + ARMCPU *armcpu; + uint32_t irqflags = GIC_FDT_IRQ_FLAGS_LEVEL_HI; + + CPU_FOREACH(cpu) { + armcpu = ARM_CPU(cpu); + if (!armcpu->has_pmu || + !kvm_arm_pmu_create(cpu, PPI(VIRTUAL_PMU_IRQ))) { + return; + } + } + + if (gictype == 2) { + irqflags = deposit32(irqflags, GIC_FDT_IRQ_PPI_CPU_START, + GIC_FDT_IRQ_PPI_CPU_WIDTH, + (1 << vbi->smp_cpus) - 1); + } + + armcpu = ARM_CPU(qemu_get_cpu(0)); + qemu_fdt_add_subnode(vbi->fdt, "/pmu"); + if (arm_feature(&armcpu->env, ARM_FEATURE_V8)) { + const char compat[] = "arm,armv8-pmuv3"; + qemu_fdt_setprop(vbi->fdt, "/pmu", "compatible", + compat, sizeof(compat)); + qemu_fdt_setprop_cells(vbi->fdt, "/pmu", "interrupts", + GIC_FDT_IRQ_TYPE_PPI, VIRTUAL_PMU_IRQ, irqflags); + } +} + static void create_v2m(VirtBoardInfo *vbi, qemu_irq *pic) { int i; @@ -517,7 +589,7 @@ static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic, int type, bool secure) } static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic, int uart, - MemoryRegion *mem) + MemoryRegion *mem, CharDriverState *chr) { char *nodename; hwaddr base = vbi->memmap[uart].base; @@ -528,6 +600,7 @@ static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic, int uart, DeviceState *dev = qdev_create(NULL, "pl011"); SysBusDevice *s = SYS_BUS_DEVICE(dev); + qdev_prop_set_chr(dev, "chardev", chr); qdev_init_nofail(dev); memory_region_add_subregion(mem, base, sysbus_mmio_get_region(s, 0)); @@ -950,6 +1023,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", @@ -1093,6 +1167,7 @@ void virt_guest_info_machine_done(Notifier *notifier, void *data) static void machvirt_init(MachineState *machine) { VirtMachineState *vms = VIRT_MACHINE(machine); + VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(machine); qemu_irq pic[NUM_IRQS]; MemoryRegion *sysmem = get_system_memory(); MemoryRegion *secure_sysmem = NULL; @@ -1104,7 +1179,12 @@ static void machvirt_init(MachineState *machine) VirtGuestInfoState *guest_info_state = g_malloc0(sizeof *guest_info_state); VirtGuestInfo *guest_info = &guest_info_state->info; char **cpustr; + ObjectClass *oc; + const char *typename; + CPUClass *cc; + Error *err = NULL; bool firmware_loaded = bios_name || drive_get(IF_PFLASH, 0, 0); + uint8_t clustersz; if (!cpu_model) { cpu_model = "cortex-a15"; @@ -1114,10 +1194,14 @@ static void machvirt_init(MachineState *machine) * KVM is not available yet */ if (!gic_version) { + if (!kvm_enabled()) { + error_report("gic-version=host requires KVM"); + exit(1); + } + gic_version = kvm_arm_vgic_probe(); if (!gic_version) { error_report("Unable to determine GIC version supported by host"); - error_printf("KVM acceleration is probably not supported\n"); exit(1); } } @@ -1146,8 +1230,10 @@ static void machvirt_init(MachineState *machine) */ if (gic_version == 3) { virt_max_cpus = vbi->memmap[VIRT_GIC_REDIST].size / 0x20000; + clustersz = GICV3_TARGETLIST_BITS; } else { virt_max_cpus = GIC_NCPU; + clustersz = GIC_TARGETLIST_BITS; } if (max_cpus > virt_max_cpus) { @@ -1183,25 +1269,37 @@ static void machvirt_init(MachineState *machine) create_fdt(vbi); - for (n = 0; n < smp_cpus; n++) { - ObjectClass *oc = cpu_class_by_name(TYPE_ARM_CPU, cpustr[0]); - CPUClass *cc = CPU_CLASS(oc); - Object *cpuobj; - Error *err = NULL; - char *cpuopts = g_strdup(cpustr[1]); - - if (!oc) { - error_report("Unable to find CPU definition"); - exit(1); - } - cpuobj = object_new(object_class_get_name(oc)); + oc = cpu_class_by_name(TYPE_ARM_CPU, cpustr[0]); + if (!oc) { + error_report("Unable to find CPU definition"); + exit(1); + } + typename = object_class_get_name(oc); - /* Handle any CPU options specified by the user */ - cc->parse_features(CPU(cpuobj), cpuopts, &err); - g_free(cpuopts); - if (err) { - error_report_err(err); - exit(1); + /* convert -smp CPU options specified by the user into global props */ + cc = CPU_CLASS(oc); + cc->parse_features(typename, cpustr[1], &err); + g_strfreev(cpustr); + if (err) { + error_report_err(err); + exit(1); + } + + for (n = 0; n < smp_cpus; n++) { + Object *cpuobj = object_new(typename); + if (!vmc->disallow_affinity_adjustment) { + /* Adjust MPIDR like 64-bit KVM hosts, which incorporate the + * GIC's target-list limitations. 32-bit KVM hosts currently + * always create clusters of 4 CPUs, but that is expected to + * change when they gain support for gicv3. When KVM is enabled + * it will override the changes we make here, therefore our + * purposes are to make TCG consistent (with 64-bit KVM hosts) + * and to improve SGI efficiency. + */ + uint8_t aff1 = n / clustersz; + uint8_t aff0 = n % clustersz; + object_property_set_int(cpuobj, (aff1 << ARM_AFF1_SHIFT) | aff0, + "mp-affinity", NULL); } if (!vms->secure) { @@ -1233,7 +1331,6 @@ static void machvirt_init(MachineState *machine) object_property_set_bool(cpuobj, true, "realized", NULL); } - g_strfreev(cpustr); fdt_add_timer_nodes(vbi, gic_version); fdt_add_cpu_nodes(vbi); fdt_add_psci_node(vbi); @@ -1246,11 +1343,13 @@ static void machvirt_init(MachineState *machine) create_gic(vbi, pic, gic_version, vms->secure); - create_uart(vbi, pic, VIRT_UART, sysmem); + fdt_add_pmu_nodes(vbi, gic_version); + + create_uart(vbi, pic, VIRT_UART, sysmem, serial_hds[0]); if (vms->secure) { create_secure_ram(vbi, secure_sysmem); - create_uart(vbi, pic, VIRT_SECURE_UART, secure_sysmem); + create_uart(vbi, pic, VIRT_SECURE_UART, secure_sysmem, serial_hds[1]); } create_rtc(vbi, pic); @@ -1374,7 +1473,13 @@ static const TypeInfo virt_machine_info = { .class_init = virt_machine_class_init, }; -static void virt_2_6_instance_init(Object *obj) +static void machvirt_machine_init(void) +{ + type_register_static(&virt_machine_info); +} +type_init(machvirt_machine_init); + +static void virt_2_7_instance_init(Object *obj) { VirtMachineState *vms = VIRT_MACHINE(obj); @@ -1407,29 +1512,25 @@ static void virt_2_6_instance_init(Object *obj) "Valid values are 2, 3 and host", NULL); } -static void virt_2_6_class_init(ObjectClass *oc, void *data) +static void virt_machine_2_7_options(MachineClass *mc) { - MachineClass *mc = MACHINE_CLASS(oc); - static GlobalProperty compat_props[] = { - { /* end of list */ } - }; - - mc->desc = "QEMU 2.6 ARM Virtual Machine"; - mc->alias = "virt"; - mc->compat_props = compat_props; } +DEFINE_VIRT_MACHINE_AS_LATEST(2, 7) -static const TypeInfo machvirt_info = { - .name = MACHINE_TYPE_NAME("virt-2.6"), - .parent = TYPE_VIRT_MACHINE, - .instance_init = virt_2_6_instance_init, - .class_init = virt_2_6_class_init, -}; +#define VIRT_COMPAT_2_6 \ + HW_COMPAT_2_6 -static void machvirt_machine_init(void) +static void virt_2_6_instance_init(Object *obj) { - type_register_static(&virt_machine_info); - type_register_static(&machvirt_info); + virt_2_7_instance_init(obj); } -type_init(machvirt_machine_init); +static void virt_machine_2_6_options(MachineClass *mc) +{ + VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc)); + + virt_machine_2_7_options(mc); + SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_6); + vmc->disallow_affinity_adjustment = true; +} +DEFINE_VIRT_MACHINE(2, 6) diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index 98b17c9ae..7dac20d67 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -32,6 +32,7 @@ #include "hw/ssi/ssi.h" #include "qemu/error-report.h" #include "hw/sd/sd.h" +#include "hw/char/cadence_uart.h" #define NUM_SPI_FLASHES 4 #define NUM_QSPI_FLASHES 2 @@ -137,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); @@ -235,8 +242,8 @@ static void zynq_init(MachineState *machine) sysbus_create_simple("xlnx,ps7-usb", 0xE0002000, pic[53-IRQ_OFFSET]); sysbus_create_simple("xlnx,ps7-usb", 0xE0003000, pic[76-IRQ_OFFSET]); - sysbus_create_simple("cadence_uart", 0xE0000000, pic[59-IRQ_OFFSET]); - sysbus_create_simple("cadence_uart", 0xE0001000, pic[82-IRQ_OFFSET]); + cadence_uart_create(0xE0000000, pic[59 - IRQ_OFFSET], serial_hds[0]); + cadence_uart_create(0xE0001000, pic[82 - IRQ_OFFSET], serial_hds[1]); sysbus_create_varargs("cadence_ttc", 0xF8001000, pic[42-IRQ_OFFSET], pic[43-IRQ_OFFSET], pic[44-IRQ_OFFSET], NULL); @@ -293,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 5f480182b..4ec590a25 100644 --- a/hw/arm/xlnx-ep108.c +++ b/hw/arm/xlnx-ep108.c @@ -23,6 +23,7 @@ #include "hw/boards.h" #include "qemu/error-report.h" #include "exec/address-spaces.h" +#include "qemu/log.h" typedef struct XlnxEP108 { XlnxZynqMPState soc; @@ -87,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); @@ -113,3 +121,11 @@ static void xlnx_ep108_machine_init(MachineClass *mc) } DEFINE_MACHINE("xlnx-ep108", xlnx_ep108_machine_init) + +static void xlnx_zcu102_machine_init(MachineClass *mc) +{ + mc->desc = "Xilinx ZynqMP ZCU102 board"; + mc->init = xlnx_ep108_init; +} + +DEFINE_MACHINE("xlnx-zcu102", xlnx_zcu102_machine_init) diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index 4d504da64..23c719986 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -22,6 +22,8 @@ #include "hw/arm/xlnx-zynqmp.h" #include "hw/intc/arm_gic_common.h" #include "exec/address-spaces.h" +#include "sysemu/kvm.h" +#include "kvm_arm.h" #define GIC_NUM_SPI_INTR 160 @@ -36,6 +38,12 @@ #define SATA_ADDR 0xFD0C0000 #define SATA_NUM_PORTS 2 +#define DP_ADDR 0xfd4a0000 +#define DP_IRQ 113 + +#define DPDMA_ADDR 0xfd4c0000 +#define DPDMA_IRQ 116 + static const uint64_t gem_addr[XLNX_ZYNQMP_NUM_GEMS] = { 0xFF0B0000, 0xFF0C0000, 0xFF0D0000, 0xFF0E0000, }; @@ -83,6 +91,41 @@ static inline int arm_gic_ppi_index(int cpu_nr, int ppi_index) return GIC_NUM_SPI_INTR + cpu_nr * GIC_INTERNAL + ppi_index; } +static void xlnx_zynqmp_create_rpu(XlnxZynqMPState *s, const char *boot_cpu, + Error **errp) +{ + Error *err = NULL; + int i; + + for (i = 0; i < XLNX_ZYNQMP_NUM_RPU_CPUS; i++) { + char *name; + + object_initialize(&s->rpu_cpu[i], sizeof(s->rpu_cpu[i]), + "cortex-r5-" TYPE_ARM_CPU); + object_property_add_child(OBJECT(s), "rpu-cpu[*]", + OBJECT(&s->rpu_cpu[i]), &error_abort); + + name = object_get_canonical_path_component(OBJECT(&s->rpu_cpu[i])); + if (strcmp(name, boot_cpu)) { + /* Secondary CPUs start in PSCI powered-down state */ + object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true, + "start-powered-off", &error_abort); + } else { + s->boot_cpu_ptr = &s->rpu_cpu[i]; + } + g_free(name); + + object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true, "reset-hivecs", + &error_abort); + object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true, "realized", + &err); + if (err) { + error_propagate(errp, err); + return; + } + } +} + static void xlnx_zynqmp_init(Object *obj) { XlnxZynqMPState *s = XLNX_ZYNQMP(obj); @@ -95,19 +138,12 @@ static void xlnx_zynqmp_init(Object *obj) &error_abort); } - for (i = 0; i < XLNX_ZYNQMP_NUM_RPU_CPUS; i++) { - object_initialize(&s->rpu_cpu[i], sizeof(s->rpu_cpu[i]), - "cortex-r5-" TYPE_ARM_CPU); - object_property_add_child(obj, "rpu-cpu[*]", OBJECT(&s->rpu_cpu[i]), - &error_abort); - } - object_property_add_link(obj, "ddr-ram", TYPE_MEMORY_REGION, (Object **)&s->ddr_ram, qdev_prop_allow_set_link_before_realize, OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort); - object_initialize(&s->gic, sizeof(s->gic), TYPE_ARM_GIC); + object_initialize(&s->gic, sizeof(s->gic), gic_class_name()); qdev_set_parent_bus(DEVICE(&s->gic), sysbus_get_default()); for (i = 0; i < XLNX_ZYNQMP_NUM_GEMS; i++) { @@ -135,6 +171,12 @@ static void xlnx_zynqmp_init(Object *obj) TYPE_XILINX_SPIPS); qdev_set_parent_bus(DEVICE(&s->spi[i]), sysbus_get_default()); } + + object_initialize(&s->dp, sizeof(s->dp), TYPE_XLNX_DP); + qdev_set_parent_bus(DEVICE(&s->dp), sysbus_get_default()); + + object_initialize(&s->dpdma, sizeof(s->dpdma), TYPE_XLNX_DPDMA); + qdev_set_parent_bus(DEVICE(&s->dpdma), sysbus_get_default()); } static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) @@ -196,11 +238,42 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) qdev_prop_set_uint32(DEVICE(&s->gic), "num-irq", GIC_NUM_SPI_INTR + 32); qdev_prop_set_uint32(DEVICE(&s->gic), "revision", 2); qdev_prop_set_uint32(DEVICE(&s->gic), "num-cpu", XLNX_ZYNQMP_NUM_APU_CPUS); + + /* Realize APUs before realizing the GIC. KVM requires this. */ + for (i = 0; i < XLNX_ZYNQMP_NUM_APU_CPUS; i++) { + char *name; + + object_property_set_int(OBJECT(&s->apu_cpu[i]), QEMU_PSCI_CONDUIT_SMC, + "psci-conduit", &error_abort); + + name = object_get_canonical_path_component(OBJECT(&s->apu_cpu[i])); + if (strcmp(name, boot_cpu)) { + /* Secondary CPUs start in PSCI powered-down state */ + object_property_set_bool(OBJECT(&s->apu_cpu[i]), true, + "start-powered-off", &error_abort); + } else { + s->boot_cpu_ptr = &s->apu_cpu[i]; + } + g_free(name); + + object_property_set_bool(OBJECT(&s->apu_cpu[i]), + s->secure, "has_el3", NULL); + object_property_set_int(OBJECT(&s->apu_cpu[i]), GIC_BASE_ADDR, + "reset-cbar", &error_abort); + object_property_set_bool(OBJECT(&s->apu_cpu[i]), true, "realized", + &err); + if (err) { + error_propagate(errp, err); + return; + } + } + object_property_set_bool(OBJECT(&s->gic), true, "realized", &err); if (err) { error_propagate(errp, err); return; } + assert(ARRAY_SIZE(xlnx_zynqmp_gic_regions) == XLNX_ZYNQMP_GIC_REGIONS); for (i = 0; i < XLNX_ZYNQMP_GIC_REGIONS; i++) { SysBusDevice *gic = SYS_BUS_DEVICE(&s->gic); @@ -223,29 +296,6 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) for (i = 0; i < XLNX_ZYNQMP_NUM_APU_CPUS; i++) { qemu_irq irq; - char *name; - - object_property_set_int(OBJECT(&s->apu_cpu[i]), QEMU_PSCI_CONDUIT_SMC, - "psci-conduit", &error_abort); - - name = object_get_canonical_path_component(OBJECT(&s->apu_cpu[i])); - if (strcmp(name, boot_cpu)) { - /* Secondary CPUs start in PSCI powered-down state */ - object_property_set_bool(OBJECT(&s->apu_cpu[i]), true, - "start-powered-off", &error_abort); - } else { - s->boot_cpu_ptr = &s->apu_cpu[i]; - } - g_free(name); - - object_property_set_int(OBJECT(&s->apu_cpu[i]), GIC_BASE_ADDR, - "reset-cbar", &error_abort); - object_property_set_bool(OBJECT(&s->apu_cpu[i]), true, "realized", - &err); - if (err) { - error_propagate(errp, err); - return; - } sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i, qdev_get_gpio_in(DEVICE(&s->apu_cpu[i]), @@ -258,23 +308,8 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) qdev_connect_gpio_out(DEVICE(&s->apu_cpu[i]), 1, irq); } - for (i = 0; i < XLNX_ZYNQMP_NUM_RPU_CPUS; i++) { - char *name; - - name = object_get_canonical_path_component(OBJECT(&s->rpu_cpu[i])); - if (strcmp(name, boot_cpu)) { - /* Secondary CPUs start in PSCI powered-down state */ - object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true, - "start-powered-off", &error_abort); - } else { - s->boot_cpu_ptr = &s->rpu_cpu[i]; - } - g_free(name); - - object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true, "reset-hivecs", - &error_abort); - object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true, "realized", - &err); + if (s->has_rpu) { + xlnx_zynqmp_create_rpu(s, boot_cpu, &err); if (err) { error_propagate(errp, err); return; @@ -308,6 +343,7 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) } for (i = 0; i < XLNX_ZYNQMP_NUM_UARTS; i++) { + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hds[i]); object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", &err); if (err) { error_propagate(errp, err); @@ -364,12 +400,32 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) object_property_add_alias(OBJECT(s), bus_name, OBJECT(&s->spi[i]), "spi0", &error_abort); - g_free(bus_name); + g_free(bus_name); + } + + object_property_set_bool(OBJECT(&s->dp), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->dp), 0, DP_ADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->dp), 0, gic_spi[DP_IRQ]); + + object_property_set_bool(OBJECT(&s->dpdma), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; } + object_property_set_link(OBJECT(&s->dp), OBJECT(&s->dpdma), "dpdma", + &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->dpdma), 0, DPDMA_ADDR); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->dpdma), 0, gic_spi[DPDMA_IRQ]); } static Property xlnx_zynqmp_props[] = { DEFINE_PROP_STRING("boot-cpu", XlnxZynqMPState, boot_cpu), + DEFINE_PROP_BOOL("secure", XlnxZynqMPState, secure, false), + DEFINE_PROP_BOOL("has_rpu", XlnxZynqMPState, has_rpu, false), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/arm/z2.c b/hw/arm/z2.c index aea895a50..68a92f318 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/cs4231.c b/hw/audio/cs4231.c index caf97c169..30690f96a 100644 --- a/hw/audio/cs4231.c +++ b/hw/audio/cs4231.c @@ -145,16 +145,15 @@ static const VMStateDescription vmstate_cs4231 = { } }; -static int cs4231_init1(SysBusDevice *dev) +static void cs4231_init(Object *obj) { - CSState *s = CS4231(dev); + CSState *s = CS4231(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); - memory_region_init_io(&s->iomem, OBJECT(s), &cs_mem_ops, s, "cs4321", + memory_region_init_io(&s->iomem, obj, &cs_mem_ops, s, "cs4321", CS_SIZE); sysbus_init_mmio(dev, &s->iomem); sysbus_init_irq(dev, &s->irq); - - return 0; } static Property cs4231_properties[] = { @@ -164,9 +163,7 @@ static Property cs4231_properties[] = { static void cs4231_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = cs4231_init1; dc->reset = cs_reset; dc->vmsd = &vmstate_cs4231; dc->props = cs4231_properties; @@ -176,6 +173,7 @@ static const TypeInfo cs4231_info = { .name = TYPE_CS4231, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(CSState), + .instance_init = cs4231_init, .class_init = cs4231_class_init, }; diff --git a/hw/audio/fmopl.h b/hw/audio/fmopl.h index 24ba5f480..fdda7f9f5 100644 --- a/hw/audio/fmopl.h +++ b/hw/audio/fmopl.h @@ -1,5 +1,5 @@ -#ifndef __FMOPL_H_ -#define __FMOPL_H_ +#ifndef FMOPL_H +#define FMOPL_H /* --- select emulation chips --- */ #define BUILD_YM3812 (HAS_YM3812) diff --git a/hw/audio/gus.c b/hw/audio/gus.c index 9dd6947be..6c0264677 100644 --- a/hw/audio/gus.c +++ b/hw/audio/gus.c @@ -144,7 +144,7 @@ static void GUS_callback (void *opaque, int free) s->left = samples; reset: - gus_irqgen (&s->emu, muldiv64 (net, 1000000, s->freq)); + gus_irqgen (&s->emu, (uint64_t)net * 1000000 / s->freq); } int GUS_irqrequest (GUSEmuState *emu, int hwirq, int n) diff --git a/hw/audio/gusemu.h b/hw/audio/gusemu.h index b7f075126..9aec7bf8e 100644 --- a/hw/audio/gusemu.h +++ b/hw/audio/gusemu.h @@ -101,4 +101,4 @@ void gus_irqgen(GUSEmuState *state, unsigned int elapsed_time); /* lower values won´t provide any benefit at all, higher values can cause audible timing delays */ /* note: masked timers are also calculated by this function, thus it might be needed even without any IRQs in use! */ -#endif /* gusemu.h */ +#endif /* GUSEMU_H */ diff --git a/hw/audio/gustate.h b/hw/audio/gustate.h index ece903abb..d16297110 100644 --- a/hw/audio/gustate.h +++ b/hw/audio/gustate.h @@ -129,4 +129,4 @@ #define gusdataend (VSRegsEnd+4) -#endif /* gustate.h */ +#endif /* GUSTATE_H */ diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c index d372d4ab9..cd95340cd 100644 --- a/hw/audio/intel-hda.c +++ b/hw/audio/intel-hda.c @@ -26,6 +26,7 @@ #include "intel-hda.h" #include "intel-hda-defs.h" #include "sysemu/dma.h" +#include "qapi/error.h" /* --------------------------------------------------------------------- */ /* hda bus */ @@ -50,25 +51,28 @@ void hda_codec_bus_init(DeviceState *dev, HDACodecBus *bus, size_t bus_size, bus->xfer = xfer; } -static int hda_codec_dev_init(DeviceState *qdev) +static void hda_codec_dev_realize(DeviceState *qdev, Error **errp) { - HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, qdev->parent_bus); - HDACodecDevice *dev = DO_UPCAST(HDACodecDevice, qdev, qdev); + HDACodecBus *bus = HDA_BUS(qdev->parent_bus); + HDACodecDevice *dev = HDA_CODEC_DEVICE(qdev); HDACodecDeviceClass *cdc = HDA_CODEC_DEVICE_GET_CLASS(dev); if (dev->cad == -1) { dev->cad = bus->next_cad; } if (dev->cad >= 15) { - return -1; + error_setg(errp, "HDA audio codec address is full"); + return; } bus->next_cad = dev->cad + 1; - return cdc->init(dev); + if (cdc->init(dev) != 0) { + error_setg(errp, "HDA audio init failed"); + } } static int hda_codec_dev_exit(DeviceState *qdev) { - HDACodecDevice *dev = DO_UPCAST(HDACodecDevice, qdev, qdev); + HDACodecDevice *dev = HDA_CODEC_DEVICE(qdev); HDACodecDeviceClass *cdc = HDA_CODEC_DEVICE_GET_CLASS(dev); if (cdc->exit) { @@ -84,7 +88,7 @@ HDACodecDevice *hda_codec_find(HDACodecBus *bus, uint32_t cad) QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) { DeviceState *qdev = kid->child; - cdev = DO_UPCAST(HDACodecDevice, qdev, qdev); + cdev = HDA_CODEC_DEVICE(qdev); if (cdev->cad == cad) { return cdev; } @@ -94,14 +98,14 @@ HDACodecDevice *hda_codec_find(HDACodecBus *bus, uint32_t cad) void hda_codec_response(HDACodecDevice *dev, bool solicited, uint32_t response) { - HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, dev->qdev.parent_bus); + HDACodecBus *bus = HDA_BUS(dev->qdev.parent_bus); bus->response(dev, solicited, response); } bool hda_codec_xfer(HDACodecDevice *dev, uint32_t stnr, bool output, uint8_t *buf, uint32_t len) { - HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, dev->qdev.parent_bus); + HDACodecBus *bus = HDA_BUS(dev->qdev.parent_bus); return bus->xfer(dev, stnr, output, buf, len); } @@ -187,7 +191,7 @@ struct IntelHDAState { /* properties */ uint32_t debug; - uint32_t msi; + OnOffAuto msi; bool old_msi_addr; }; @@ -215,10 +219,7 @@ static void intel_hda_reset(DeviceState *dev); static hwaddr intel_hda_addr(uint32_t lbase, uint32_t ubase) { - hwaddr addr; - - addr = ((uint64_t)ubase << 32) | lbase; - return addr; + return ((uint64_t)ubase << 32) | lbase; } static void intel_hda_update_int_sts(IntelHDAState *d) @@ -255,7 +256,7 @@ static void intel_hda_update_int_sts(IntelHDAState *d) static void intel_hda_update_irq(IntelHDAState *d) { - int msi = d->msi && msi_enabled(&d->pci); + bool msi = msi_enabled(&d->pci); int level; intel_hda_update_int_sts(d); @@ -337,7 +338,7 @@ static void intel_hda_corb_run(IntelHDAState *d) static void intel_hda_response(HDACodecDevice *dev, bool solicited, uint32_t response) { - HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, dev->qdev.parent_bus); + HDACodecBus *bus = HDA_BUS(dev->qdev.parent_bus); IntelHDAState *d = container_of(bus, IntelHDAState, codecs); hwaddr addr; uint32_t wp, ex; @@ -386,7 +387,7 @@ static void intel_hda_response(HDACodecDevice *dev, bool solicited, uint32_t res static bool intel_hda_xfer(HDACodecDevice *dev, uint32_t stnr, bool output, uint8_t *buf, uint32_t len) { - HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, dev->qdev.parent_bus); + HDACodecBus *bus = HDA_BUS(dev->qdev.parent_bus); IntelHDAState *d = container_of(bus, IntelHDAState, codecs); hwaddr addr; uint32_t s, copy, left; @@ -493,7 +494,7 @@ static void intel_hda_notify_codecs(IntelHDAState *d, uint32_t stream, bool runn DeviceState *qdev = kid->child; HDACodecDeviceClass *cdc; - cdev = DO_UPCAST(HDACodecDevice, qdev, qdev); + cdev = HDA_CODEC_DEVICE(qdev); cdc = HDA_CODEC_DEVICE_GET_CLASS(cdev); if (cdc->stream) { cdc->stream(cdev, stream, running, output); @@ -1120,7 +1121,7 @@ static void intel_hda_reset(DeviceState *dev) /* reset codecs */ QTAILQ_FOREACH(kid, &d->codecs.qbus.children, sibling) { DeviceState *qdev = kid->child; - cdev = DO_UPCAST(HDACodecDevice, qdev, qdev); + cdev = HDA_CODEC_DEVICE(qdev); device_reset(DEVICE(cdev)); d->state_sts |= (1 << cdev->cad); } @@ -1131,6 +1132,8 @@ static void intel_hda_realize(PCIDevice *pci, Error **errp) { IntelHDAState *d = INTEL_HDA(pci); uint8_t *conf = d->pci.config; + Error *err = NULL; + int ret; d->name = object_get_typename(OBJECT(d)); @@ -1139,12 +1142,27 @@ static void intel_hda_realize(PCIDevice *pci, Error **errp) /* HDCTL off 0x40 bit 0 selects signaling mode (1-HDA, 0 - Ac97) 18.1.19 */ conf[0x40] = 0x01; + if (d->msi != ON_OFF_AUTO_OFF) { + ret = msi_init(&d->pci, d->old_msi_addr ? 0x50 : 0x60, + 1, true, false, &err); + /* Any error other than -ENOTSUP(board's MSI support is broken) + * is a programming error */ + assert(!ret || ret == -ENOTSUP); + if (ret && d->msi == ON_OFF_AUTO_ON) { + /* Can't satisfy user's explicit msi=on request, fail */ + error_append_hint(&err, "You have to use msi=auto (default) or " + "msi=off with this machine type.\n"); + error_propagate(errp, err); + return; + } + assert(!err || d->msi == ON_OFF_AUTO_AUTO); + /* With msi=auto, we fall back to MSI off silently */ + error_free(err); + } + memory_region_init_io(&d->mmio, OBJECT(d), &intel_hda_mmio_ops, d, "intel-hda", 0x4000); pci_register_bar(&d->pci, 0, 0, &d->mmio); - if (d->msi) { - msi_init(&d->pci, d->old_msi_addr ? 0x50 : 0x60, 1, true, false); - } hda_codec_bus_init(DEVICE(pci), &d->codecs, sizeof(d->codecs), intel_hda_response, intel_hda_xfer); @@ -1234,7 +1252,7 @@ static const VMStateDescription vmstate_intel_hda = { static Property intel_hda_properties[] = { DEFINE_PROP_UINT32("debug", IntelHDAState, debug, 0), - DEFINE_PROP_UINT32("msi", IntelHDAState, msi, 1), + DEFINE_PROP_ON_OFF_AUTO("msi", IntelHDAState, msi, ON_OFF_AUTO_AUTO), DEFINE_PROP_BOOL("old_msi_addr", IntelHDAState, old_msi_addr, false), DEFINE_PROP_END_OF_LIST(), }; @@ -1298,7 +1316,7 @@ static const TypeInfo intel_hda_info_ich9 = { static void hda_codec_device_class_init(ObjectClass *klass, void *data) { DeviceClass *k = DEVICE_CLASS(klass); - k->init = hda_codec_dev_init; + k->realize = hda_codec_dev_realize; k->exit = hda_codec_dev_exit; set_bit(DEVICE_CATEGORY_SOUND, k->categories); k->bus_type = TYPE_HDA_BUS; diff --git a/hw/audio/lm4549.h b/hw/audio/lm4549.h index 812a7a444..74c3ee893 100644 --- a/hw/audio/lm4549.h +++ b/hw/audio/lm4549.h @@ -40,4 +40,4 @@ uint32_t lm4549_read(lm4549_state *s, hwaddr offset); void lm4549_write(lm4549_state *s, hwaddr offset, uint32_t value); uint32_t lm4549_write_samples(lm4549_state *s, uint32_t left, uint32_t right); -#endif /* #ifndef HW_LM4549_H */ +#endif /* HW_LM4549_H */ diff --git a/hw/audio/milkymist-ac97.c b/hw/audio/milkymist-ac97.c index 6a3b53674..bc8db71ae 100644 --- a/hw/audio/milkymist-ac97.c +++ b/hw/audio/milkymist-ac97.c @@ -18,7 +18,7 @@ * * * Specification available at: - * http://www.milkymist.org/socdoc/ac97.pdf + * http://milkymist.walle.cc/socdoc/ac97.pdf */ #include "qemu/osdep.h" @@ -284,16 +284,26 @@ static int ac97_post_load(void *opaque, int version_id) return 0; } -static int milkymist_ac97_init(SysBusDevice *dev) +static void milkymist_ac97_init(Object *obj) { - MilkymistAC97State *s = MILKYMIST_AC97(dev); + MilkymistAC97State *s = MILKYMIST_AC97(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); - struct audsettings as; sysbus_init_irq(dev, &s->crrequest_irq); sysbus_init_irq(dev, &s->crreply_irq); sysbus_init_irq(dev, &s->dmar_irq); sysbus_init_irq(dev, &s->dmaw_irq); + memory_region_init_io(&s->regs_region, obj, &ac97_mmio_ops, s, + "milkymist-ac97", R_MAX * 4); + sysbus_init_mmio(dev, &s->regs_region); +} + +static void milkymist_ac97_realize(DeviceState *dev, Error **errp) +{ + MilkymistAC97State *s = MILKYMIST_AC97(dev); + struct audsettings as; + AUD_register_card("Milkymist AC'97", &s->card); as.freq = 48000; @@ -305,12 +315,6 @@ static int milkymist_ac97_init(SysBusDevice *dev) "mm_ac97.in", s, ac97_in_cb, &as); s->voice_out = AUD_open_out(&s->card, s->voice_out, "mm_ac97.out", s, ac97_out_cb, &as); - - memory_region_init_io(&s->regs_region, OBJECT(s), &ac97_mmio_ops, s, - "milkymist-ac97", R_MAX * 4); - sysbus_init_mmio(dev, &s->regs_region); - - return 0; } static const VMStateDescription vmstate_milkymist_ac97 = { @@ -327,9 +331,8 @@ static const VMStateDescription vmstate_milkymist_ac97 = { static void milkymist_ac97_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = milkymist_ac97_init; + dc->realize = milkymist_ac97_realize; dc->reset = milkymist_ac97_reset; dc->vmsd = &vmstate_milkymist_ac97; } @@ -338,6 +341,7 @@ static const TypeInfo milkymist_ac97_info = { .name = TYPE_MILKYMIST_AC97, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(MilkymistAC97State), + .instance_init = milkymist_ac97_init, .class_init = milkymist_ac97_class_init, }; diff --git a/hw/audio/pcspk.c b/hw/audio/pcspk.c index f9afc8eda..42a6f4885 100644 --- a/hw/audio/pcspk.c +++ b/hw/audio/pcspk.c @@ -31,11 +31,12 @@ #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 #define PCSPK_MAX_FREQ (PCSPK_SAMPLE_RATE >> 1) -#define PCSPK_MIN_COUNT ((PIT_FREQ + PCSPK_MAX_FREQ - 1) / PCSPK_MAX_FREQ) +#define PCSPK_MIN_COUNT DIV_ROUND_UP(PIT_FREQ, PCSPK_MAX_FREQ) #define PC_SPEAKER(obj) OBJECT_CHECK(PCSpkState, (obj), TYPE_PC_SPEAKER) @@ -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/audio/pl041.c b/hw/audio/pl041.c index 4717bc9b9..6e9c10401 100644 --- a/hw/audio/pl041.c +++ b/hw/audio/pl041.c @@ -22,6 +22,7 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" +#include "qemu/log.h" #include "pl041.h" #include "lm4549.h" diff --git a/hw/audio/pl041.h b/hw/audio/pl041.h index 427ab6d6f..515db4756 100644 --- a/hw/audio/pl041.h +++ b/hw/audio/pl041.h @@ -132,4 +132,4 @@ enum { #define RXTOFEC3 (1 << 11) #define RXTOFEC4 (1 << 12) -#endif /* #ifndef HW_PL041_H */ +#endif /* HW_PL041_H */ diff --git a/hw/audio/trace-events b/hw/audio/trace-events new file mode 100644 index 000000000..3210386e8 --- /dev/null +++ b/hw/audio/trace-events @@ -0,0 +1,19 @@ +# See docs/tracing.txt for syntax documentation. + +# hw/audio/cs4231.c +cs4231_mem_readl_dreg(uint32_t reg, uint32_t ret) "read dreg %d: 0x%02x" +cs4231_mem_readl_reg(uint32_t reg, uint32_t ret) "read reg %d: 0x%08x" +cs4231_mem_writel_reg(uint32_t reg, uint32_t old, uint32_t val) "write reg %d: 0x%08x -> 0x%08x" +cs4231_mem_writel_dreg(uint32_t reg, uint32_t old, uint32_t val) "write dreg %d: 0x%02x -> 0x%02x" + +# hw/audio/milkymist-ac97.c +milkymist_ac97_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x" +milkymist_ac97_memory_write(uint32_t addr, uint32_t value) "addr %08x value %08x" +milkymist_ac97_pulse_irq_crrequest(void) "Pulse IRQ CR request" +milkymist_ac97_pulse_irq_crreply(void) "Pulse IRQ CR reply" +milkymist_ac97_pulse_irq_dmaw(void) "Pulse IRQ DMA write" +milkymist_ac97_pulse_irq_dmar(void) "Pulse IRQ DMA read" +milkymist_ac97_in_cb(int avail, uint32_t remaining) "avail %d remaining %u" +milkymist_ac97_in_cb_transferred(int transferred) "transferred %d" +milkymist_ac97_out_cb(int free, uint32_t remaining) "free %d remaining %u" +milkymist_ac97_out_cb_transferred(int transferred) "transferred %d" diff --git a/hw/block/block.c b/hw/block/block.c index 97a59d4fa..8dc9d84a3 100644 --- a/hw/block/block.c +++ b/hw/block/block.c @@ -51,6 +51,34 @@ void blkconf_blocksizes(BlockConf *conf) } } +void blkconf_apply_backend_options(BlockConf *conf) +{ + BlockBackend *blk = conf->blk; + BlockdevOnError rerror, werror; + bool wce; + + switch (conf->wce) { + case ON_OFF_AUTO_ON: wce = true; break; + case ON_OFF_AUTO_OFF: wce = false; break; + case ON_OFF_AUTO_AUTO: wce = blk_enable_write_cache(blk); break; + default: + abort(); + } + + rerror = conf->rerror; + if (rerror == BLOCKDEV_ON_ERROR_AUTO) { + rerror = blk_get_on_error(blk, true); + } + + werror = conf->werror; + if (werror == BLOCKDEV_ON_ERROR_AUTO) { + werror = blk_get_on_error(blk, false); + } + + blk_set_enable_write_cache(blk, wce); + blk_set_on_error(blk, rerror, werror); +} + void blkconf_geometry(BlockConf *conf, int *ptrans, unsigned cyls_max, unsigned heads_max, unsigned secs_max, Error **errp) diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c index 3cb97c9a2..704a76360 100644 --- a/hw/block/dataplane/virtio-blk.c +++ b/hw/block/dataplane/virtio-blk.c @@ -31,13 +31,9 @@ struct VirtIOBlockDataPlane { bool stopping; VirtIOBlkConf *conf; - VirtIODevice *vdev; - VirtQueue *vq; /* virtqueue vring */ - EventNotifier *guest_notifier; /* irq */ QEMUBH *bh; /* bh for guest notification */ - - Notifier insert_notifier, remove_notifier; + 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 @@ -46,74 +42,39 @@ struct VirtIOBlockDataPlane { */ IOThread *iothread; AioContext *ctx; - - /* Operation blocker on BDS */ - Error *blocker; }; /* 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; - } - - event_notifier_set(s->guest_notifier); -} + memcpy(bitmap, s->batch_notify_vqs, sizeof(bitmap)); + memset(s->batch_notify_vqs, 0, sizeof(bitmap)); -static void data_plane_set_up_op_blockers(VirtIOBlockDataPlane *s) -{ - assert(!s->blocker); - error_setg(&s->blocker, "block device is in use by data plane"); - blk_op_block_all(s->conf->conf.blk, s->blocker); - blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_RESIZE, s->blocker); - blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_DRIVE_DEL, s->blocker); - blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_BACKUP_SOURCE, s->blocker); - blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_CHANGE, s->blocker); - blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_COMMIT_SOURCE, s->blocker); - blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_COMMIT_TARGET, s->blocker); - blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_EJECT, s->blocker); - blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, - s->blocker); - blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, - s->blocker); - blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, - s->blocker); - blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_MIRROR_SOURCE, s->blocker); - blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_STREAM, s->blocker); - blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_REPLACE, s->blocker); -} + for (j = 0; j < nvqs; j += BITS_PER_LONG) { + unsigned long bits = bitmap[j]; -static void data_plane_remove_op_blockers(VirtIOBlockDataPlane *s) -{ - if (s->blocker) { - blk_op_unblock_all(s->conf->conf.blk, s->blocker); - error_free(s->blocker); - s->blocker = NULL; - } -} + while (bits != 0) { + unsigned i = j + ctzl(bits); + VirtQueue *vq = virtio_get_queue(s->vdev, i); -static void data_plane_blk_insert_notifier(Notifier *n, void *data) -{ - VirtIOBlockDataPlane *s = container_of(n, VirtIOBlockDataPlane, - insert_notifier); - assert(s->conf->conf.blk == data); - data_plane_set_up_op_blockers(s); -} + if (virtio_should_notify(s->vdev, vq)) { + event_notifier_set(virtio_queue_get_guest_notifier(vq)); + } -static void data_plane_blk_remove_notifier(Notifier *n, void *data) -{ - VirtIOBlockDataPlane *s = container_of(n, VirtIOBlockDataPlane, - remove_notifier); - assert(s->conf->conf.blk == data); - data_plane_remove_op_blockers(s); + bits &= bits - 1; /* clear right-most bit */ + } + } } /* Context: QEMU global mutex held */ @@ -132,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)"); @@ -151,19 +112,11 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, s->vdev = vdev; s->conf = conf; - if (conf->iothread) { - s->iothread = conf->iothread; - object_ref(OBJECT(s->iothread)); - } + s->iothread = conf->iothread; + object_ref(OBJECT(s->iothread)); s->ctx = iothread_get_aio_context(s->iothread); s->bh = aio_bh_new(s->ctx, notify_guest_bh, s); - - s->insert_notifier.notify = data_plane_blk_insert_notifier; - s->remove_notifier.notify = data_plane_blk_remove_notifier; - blk_add_insert_bs_notifier(conf->conf.blk, &s->insert_notifier); - blk_add_remove_bs_notifier(conf->conf.blk, &s->remove_notifier); - - data_plane_set_up_op_blockers(s); + s->batch_notify_vqs = bitmap_new(conf->num_queues); *dataplane = s; } @@ -176,9 +129,7 @@ void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s) } virtio_blk_data_plane_stop(s); - data_plane_remove_op_blockers(s); - notifier_remove(&s->insert_notifier); - notifier_remove(&s->remove_notifier); + g_free(s->batch_notify_vqs); qemu_bh_delete(s->bh); object_unref(OBJECT(s->iothread)); g_free(s); @@ -201,6 +152,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) { @@ -208,22 +161,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; @@ -233,17 +189,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; @@ -256,6 +218,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; @@ -273,17 +237,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 0714c11a2..b1f0b95b3 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/fdc.c b/hw/block/fdc.c index 372227569..f73af7db4 100644 --- a/hw/block/fdc.c +++ b/hw/block/fdc.c @@ -223,6 +223,13 @@ static int fd_sector(FDrive *drv) NUM_SIDES(drv)); } +/* Returns current position, in bytes, for given drive */ +static int fd_offset(FDrive *drv) +{ + g_assert(fd_sector(drv) < INT_MAX >> BDRV_SECTOR_BITS); + return fd_sector(drv) << BDRV_SECTOR_BITS; +} + /* Seek to a new position: * returns 0 if already on right track * returns 1 if track changed @@ -1629,8 +1636,8 @@ static int fdctrl_transfer_handler (void *opaque, int nchan, if (fdctrl->data_dir != FD_DIR_WRITE || len < FD_SECTOR_LEN || rel_pos != 0) { /* READ & SCAN commands and realign to a sector for WRITE */ - if (blk_read(cur_drv->blk, fd_sector(cur_drv), - fdctrl->fifo, 1) < 0) { + if (blk_pread(cur_drv->blk, fd_offset(cur_drv), + fdctrl->fifo, BDRV_SECTOR_SIZE) < 0) { FLOPPY_DPRINTF("Floppy: error getting sector %d\n", fd_sector(cur_drv)); /* Sure, image size is too small... */ @@ -1657,8 +1664,8 @@ static int fdctrl_transfer_handler (void *opaque, int nchan, k->read_memory(fdctrl->dma, nchan, fdctrl->fifo + rel_pos, fdctrl->data_pos, len); - if (blk_write(cur_drv->blk, fd_sector(cur_drv), - fdctrl->fifo, 1) < 0) { + if (blk_pwrite(cur_drv->blk, fd_offset(cur_drv), + fdctrl->fifo, BDRV_SECTOR_SIZE, 0) < 0) { FLOPPY_DPRINTF("error writing sector %d\n", fd_sector(cur_drv)); fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00); @@ -1741,7 +1748,8 @@ static uint32_t fdctrl_read_data(FDCtrl *fdctrl) fd_sector(cur_drv)); return 0; } - if (blk_read(cur_drv->blk, fd_sector(cur_drv), fdctrl->fifo, 1) + if (blk_pread(cur_drv->blk, fd_offset(cur_drv), fdctrl->fifo, + BDRV_SECTOR_SIZE) < 0) { FLOPPY_DPRINTF("error getting sector %d\n", fd_sector(cur_drv)); @@ -1820,7 +1828,8 @@ static void fdctrl_format_sector(FDCtrl *fdctrl) } memset(fdctrl->fifo, 0, FD_SECTOR_LEN); if (cur_drv->blk == NULL || - blk_write(cur_drv->blk, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) { + blk_pwrite(cur_drv->blk, fd_offset(cur_drv), fdctrl->fifo, + BDRV_SECTOR_SIZE, 0) < 0) { FLOPPY_DPRINTF("error formatting sector %d\n", fd_sector(cur_drv)); fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00); } else { @@ -2243,8 +2252,8 @@ static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value) if (pos == FD_SECTOR_LEN - 1 || fdctrl->data_pos == fdctrl->data_len) { cur_drv = get_cur_drv(fdctrl); - if (blk_write(cur_drv->blk, fd_sector(cur_drv), fdctrl->fifo, 1) - < 0) { + if (blk_pwrite(cur_drv->blk, fd_offset(cur_drv), fdctrl->fifo, + BDRV_SECTOR_SIZE, 0) < 0) { FLOPPY_DPRINTF("error writing sector %d\n", fd_sector(cur_drv)); break; diff --git a/hw/block/hd-geometry.c b/hw/block/hd-geometry.c index 6d02192db..57ad5012a 100644 --- a/hw/block/hd-geometry.c +++ b/hw/block/hd-geometry.c @@ -32,6 +32,7 @@ #include "qemu/osdep.h" #include "sysemu/block-backend.h" +#include "qemu/bswap.h" #include "hw/block/block.h" #include "trace.h" @@ -66,7 +67,7 @@ static int guess_disk_lchs(BlockBackend *blk, * but also in async I/O mode. So the I/O throttling function has to * be disabled temporarily here, not permanently. */ - if (blk_read_unthrottled(blk, 0, buf, 1) < 0) { + if (blk_pread_unthrottled(blk, 0, buf, BDRV_SECTOR_SIZE) < 0) { return -1; } /* test msdos magic */ diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index 906b71257..9828ee61d 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -27,6 +27,8 @@ #include "sysemu/blockdev.h" #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 @@ -52,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). @@ -69,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,\ @@ -101,12 +130,27 @@ 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 +#define WINBOND_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) }, @@ -157,6 +201,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) }, @@ -167,6 +213,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). @@ -175,8 +226,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) }, @@ -189,6 +240,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) }, @@ -239,10 +294,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 { @@ -254,6 +305,7 @@ typedef enum { JEDEC_READ = 0x9f, BULK_ERASE = 0xc7, READ_FSR = 0x70, + RDCR = 0x15, READ = 0x03, READ4 = 0x13, @@ -270,12 +322,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, @@ -288,6 +342,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, @@ -303,9 +364,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; @@ -321,13 +391,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; @@ -349,8 +430,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. */ @@ -358,39 +460,33 @@ static void blk_sync_complete(void *opaque, int ret) static void flash_sync_page(Flash *s, int page) { - int blk_sector, nb_sectors; - QEMUIOVector iov; + QEMUIOVector *iov; if (!s->blk || blk_is_read_only(s->blk)) { return; } - blk_sector = (page * s->pi->page_size) / BDRV_SECTOR_SIZE; - nb_sectors = DIV_ROUND_UP(s->pi->page_size, BDRV_SECTOR_SIZE); - qemu_iovec_init(&iov, 1); - qemu_iovec_add(&iov, s->storage + blk_sector * BDRV_SECTOR_SIZE, - nb_sectors * BDRV_SECTOR_SIZE); - blk_aio_writev(s->blk, blk_sector, &iov, nb_sectors, blk_sync_complete, - NULL); + iov = g_new(QEMUIOVector, 1); + 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, iov); } static inline void flash_sync_area(Flash *s, int64_t off, int64_t len) { - int64_t start, end, nb_sectors; - QEMUIOVector iov; + QEMUIOVector *iov; if (!s->blk || blk_is_read_only(s->blk)) { return; } assert(!(len % BDRV_SECTOR_SIZE)); - start = off / BDRV_SECTOR_SIZE; - end = (off + len) / BDRV_SECTOR_SIZE; - nb_sectors = end - start; - qemu_iovec_init(&iov, 1); - qemu_iovec_add(&iov, s->storage + (start * BDRV_SECTOR_SIZE), - nb_sectors * BDRV_SECTOR_SIZE); - blk_aio_writev(s->blk, start, &iov, nb_sectors, blk_sync_complete, NULL); + iov = g_new(QEMUIOVector, 1); + 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) @@ -405,6 +501,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; @@ -442,9 +539,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) { @@ -452,7 +549,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); } @@ -475,9 +572,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: @@ -491,18 +590,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; @@ -511,6 +608,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: @@ -530,11 +628,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; } @@ -568,8 +680,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; @@ -599,16 +713,148 @@ 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 += WINBOND_CONTINUOUS_READ_MODE_CMD_LEN; + 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 += WINBOND_CONTINUOUS_READ_MODE_CMD_LEN; + s->needed_bytes += 4; + 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) { @@ -620,6 +866,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: @@ -628,6 +875,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; @@ -640,56 +888,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; @@ -702,6 +929,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; @@ -719,17 +949,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; @@ -772,7 +1005,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; @@ -815,6 +1048,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; @@ -826,6 +1077,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; @@ -845,20 +1099,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++; @@ -885,9 +1140,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); @@ -896,29 +1150,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_read(s->blk, 0, s->storage, - DIV_ROUND_UP(s->size, BDRV_SECTOR_SIZE))) { - fprintf(stderr, "Failed to initialize SPI flash!\n"); - return 1; + if (blk_pread(s->blk, 0, s->storage, s->size) != s->size) { + 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) @@ -934,13 +1178,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[]) { @@ -950,7 +1200,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), @@ -958,6 +1209,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() } }; @@ -968,7 +1224,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/nand.c b/hw/block/nand.c index 29c659681..c69e6755d 100644 --- a/hw/block/nand.c +++ b/hw/block/nand.c @@ -663,7 +663,8 @@ static void glue(nand_blk_write_, PAGE_SIZE)(NANDFlashState *s) sector = SECTOR(s->addr); off = (s->addr & PAGE_MASK) + s->offset; soff = SECTOR_OFFSET(s->addr); - if (blk_read(s->blk, sector, iobuf, PAGE_SECTORS) < 0) { + if (blk_pread(s->blk, sector << BDRV_SECTOR_BITS, iobuf, + PAGE_SECTORS << BDRV_SECTOR_BITS) < 0) { printf("%s: read error in sector %" PRIu64 "\n", __func__, sector); return; } @@ -675,21 +676,24 @@ static void glue(nand_blk_write_, PAGE_SIZE)(NANDFlashState *s) MIN(OOB_SIZE, off + s->iolen - PAGE_SIZE)); } - if (blk_write(s->blk, sector, iobuf, PAGE_SECTORS) < 0) { + if (blk_pwrite(s->blk, sector << BDRV_SECTOR_BITS, iobuf, + PAGE_SECTORS << BDRV_SECTOR_BITS, 0) < 0) { printf("%s: write error in sector %" PRIu64 "\n", __func__, sector); } } else { off = PAGE_START(s->addr) + (s->addr & PAGE_MASK) + s->offset; sector = off >> 9; soff = off & 0x1ff; - if (blk_read(s->blk, sector, iobuf, PAGE_SECTORS + 2) < 0) { + if (blk_pread(s->blk, sector << BDRV_SECTOR_BITS, iobuf, + (PAGE_SECTORS + 2) << BDRV_SECTOR_BITS) < 0) { printf("%s: read error in sector %" PRIu64 "\n", __func__, sector); return; } mem_and(iobuf + soff, s->io, s->iolen); - if (blk_write(s->blk, sector, iobuf, PAGE_SECTORS + 2) < 0) { + if (blk_pwrite(s->blk, sector << BDRV_SECTOR_BITS, iobuf, + (PAGE_SECTORS + 2) << BDRV_SECTOR_BITS, 0) < 0) { printf("%s: write error in sector %" PRIu64 "\n", __func__, sector); } } @@ -716,17 +720,20 @@ static void glue(nand_blk_erase_, PAGE_SIZE)(NANDFlashState *s) i = SECTOR(addr); page = SECTOR(addr + (1 << (ADDR_SHIFT + s->erase_shift))); for (; i < page; i ++) - if (blk_write(s->blk, i, iobuf, 1) < 0) { + if (blk_pwrite(s->blk, i << BDRV_SECTOR_BITS, iobuf, + BDRV_SECTOR_SIZE, 0) < 0) { printf("%s: write error in sector %" PRIu64 "\n", __func__, i); } } else { addr = PAGE_START(addr); page = addr >> 9; - if (blk_read(s->blk, page, iobuf, 1) < 0) { + if (blk_pread(s->blk, page << BDRV_SECTOR_BITS, iobuf, + BDRV_SECTOR_SIZE) < 0) { printf("%s: read error in sector %" PRIu64 "\n", __func__, page); } memset(iobuf + (addr & 0x1ff), 0xff, (~addr & 0x1ff) + 1); - if (blk_write(s->blk, page, iobuf, 1) < 0) { + if (blk_pwrite(s->blk, page << BDRV_SECTOR_BITS, iobuf, + BDRV_SECTOR_SIZE, 0) < 0) { printf("%s: write error in sector %" PRIu64 "\n", __func__, page); } @@ -734,18 +741,20 @@ static void glue(nand_blk_erase_, PAGE_SIZE)(NANDFlashState *s) i = (addr & ~0x1ff) + 0x200; for (addr += ((PAGE_SIZE + OOB_SIZE) << s->erase_shift) - 0x200; i < addr; i += 0x200) { - if (blk_write(s->blk, i >> 9, iobuf, 1) < 0) { + if (blk_pwrite(s->blk, i, iobuf, BDRV_SECTOR_SIZE, 0) < 0) { printf("%s: write error in sector %" PRIu64 "\n", __func__, i >> 9); } } page = i >> 9; - if (blk_read(s->blk, page, iobuf, 1) < 0) { + if (blk_pread(s->blk, page << BDRV_SECTOR_BITS, iobuf, + BDRV_SECTOR_SIZE) < 0) { printf("%s: read error in sector %" PRIu64 "\n", __func__, page); } memset(iobuf, 0xff, ((addr - 1) & 0x1ff) + 1); - if (blk_write(s->blk, page, iobuf, 1) < 0) { + if (blk_pwrite(s->blk, page << BDRV_SECTOR_BITS, iobuf, + BDRV_SECTOR_SIZE, 0) < 0) { printf("%s: write error in sector %" PRIu64 "\n", __func__, page); } } @@ -760,7 +769,8 @@ static void glue(nand_blk_load_, PAGE_SIZE)(NANDFlashState *s, if (s->blk) { if (s->mem_oob) { - if (blk_read(s->blk, SECTOR(addr), s->io, PAGE_SECTORS) < 0) { + if (blk_pread(s->blk, SECTOR(addr) << BDRV_SECTOR_BITS, s->io, + PAGE_SECTORS << BDRV_SECTOR_BITS) < 0) { printf("%s: read error in sector %" PRIu64 "\n", __func__, SECTOR(addr)); } @@ -769,8 +779,8 @@ static void glue(nand_blk_load_, PAGE_SIZE)(NANDFlashState *s, OOB_SIZE); s->ioaddr = s->io + SECTOR_OFFSET(s->addr) + offset; } else { - if (blk_read(s->blk, PAGE_START(addr) >> 9, - s->io, (PAGE_SECTORS + 2)) < 0) { + if (blk_pread(s->blk, PAGE_START(addr), s->io, + (PAGE_SECTORS + 2) << BDRV_SECTOR_BITS) < 0) { printf("%s: read error in sector %" PRIu64 "\n", __func__, PAGE_START(addr) >> 9); } diff --git a/hw/block/nvme.c b/hw/block/nvme.c index 173988ee8..cef3bb42f 100644 --- a/hw/block/nvme.c +++ b/hw/block/nvme.c @@ -21,10 +21,10 @@ */ #include "qemu/osdep.h" -#include <hw/block/block.h> -#include <hw/hw.h> -#include <hw/pci/msix.h> -#include <hw/pci/pci.h> +#include "hw/block/block.h" +#include "hw/hw.h" +#include "hw/pci/msix.h" +#include "hw/pci/pci.h" #include "sysemu/sysemu.h" #include "qapi/error.h" #include "qapi/visitor.h" @@ -239,7 +239,7 @@ static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd, uint8_t lba_index = NVME_ID_NS_FLBAS_INDEX(ns->id_ns.flbas); uint8_t data_shift = ns->id_ns.lbaf[lba_index].ds; uint64_t data_size = (uint64_t)nlb << data_shift; - uint64_t aio_slba = slba << (data_shift - BDRV_SECTOR_BITS); + uint64_t data_offset = slba << data_shift; int is_write = rw->opcode == NVME_CMD_WRITE ? 1 : 0; enum BlockAcctType acct = is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ; @@ -258,8 +258,8 @@ static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd, req->has_sg = true; dma_acct_start(n->conf.blk, &req->acct, &req->qsg, acct); req->aiocb = is_write ? - dma_blk_write(n->conf.blk, &req->qsg, aio_slba, nvme_rw_cb, req) : - dma_blk_read(n->conf.blk, &req->qsg, aio_slba, nvme_rw_cb, req); + dma_blk_write(n->conf.blk, &req->qsg, data_offset, nvme_rw_cb, req) : + dma_blk_read(n->conf.blk, &req->qsg, data_offset, nvme_rw_cb, req); return NVME_NO_COMPLETE; } @@ -469,19 +469,22 @@ static uint16_t nvme_create_cq(NvmeCtrl *n, NvmeCmd *cmd) return NVME_SUCCESS; } -static uint16_t nvme_identify(NvmeCtrl *n, NvmeCmd *cmd) +static uint16_t nvme_identify_ctrl(NvmeCtrl *n, NvmeIdentify *c) +{ + uint64_t prp1 = le64_to_cpu(c->prp1); + uint64_t prp2 = le64_to_cpu(c->prp2); + + return nvme_dma_read_prp(n, (uint8_t *)&n->id_ctrl, sizeof(n->id_ctrl), + prp1, prp2); +} + +static uint16_t nvme_identify_ns(NvmeCtrl *n, NvmeIdentify *c) { NvmeNamespace *ns; - NvmeIdentify *c = (NvmeIdentify *)cmd; - uint32_t cns = le32_to_cpu(c->cns); uint32_t nsid = le32_to_cpu(c->nsid); uint64_t prp1 = le64_to_cpu(c->prp1); uint64_t prp2 = le64_to_cpu(c->prp2); - if (cns) { - return nvme_dma_read_prp(n, (uint8_t *)&n->id_ctrl, sizeof(n->id_ctrl), - prp1, prp2); - } if (nsid == 0 || nsid > n->num_namespaces) { return NVME_INVALID_NSID | NVME_DNR; } @@ -491,6 +494,48 @@ static uint16_t nvme_identify(NvmeCtrl *n, NvmeCmd *cmd) prp1, prp2); } +static uint16_t nvme_identify_nslist(NvmeCtrl *n, NvmeIdentify *c) +{ + static const int data_len = 4096; + uint32_t min_nsid = le32_to_cpu(c->nsid); + uint64_t prp1 = le64_to_cpu(c->prp1); + uint64_t prp2 = le64_to_cpu(c->prp2); + uint32_t *list; + uint16_t ret; + int i, j = 0; + + list = g_malloc0(data_len); + for (i = 0; i < n->num_namespaces; i++) { + if (i < min_nsid) { + continue; + } + list[j++] = cpu_to_le32(i + 1); + if (j == data_len / sizeof(uint32_t)) { + break; + } + } + ret = nvme_dma_read_prp(n, (uint8_t *)list, data_len, prp1, prp2); + g_free(list); + return ret; +} + + +static uint16_t nvme_identify(NvmeCtrl *n, NvmeCmd *cmd) +{ + NvmeIdentify *c = (NvmeIdentify *)cmd; + + switch (le32_to_cpu(c->cns)) { + case 0x00: + return nvme_identify_ns(n, c); + case 0x01: + return nvme_identify_ctrl(n, c); + case 0x02: + return nvme_identify_nslist(n, c); + default: + return NVME_INVALID_FIELD | NVME_DNR; + } +} + static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) { uint32_t dw10 = le32_to_cpu(cmd->cdw10); @@ -803,6 +848,7 @@ static int nvme_init(PCIDevice *pci_dev) return -1; } blkconf_blocksizes(&n->conf); + blkconf_apply_backend_options(&n->conf); pci_conf = pci_dev->config; pci_conf[PCI_INTERRUPT_PIN] = 1; @@ -908,7 +954,7 @@ static void nvme_class_init(ObjectClass *oc, void *data) pc->class_id = PCI_CLASS_STORAGE_EXPRESS; pc->vendor_id = PCI_VENDOR_ID_INTEL; pc->device_id = 0x5845; - pc->revision = 1; + pc->revision = 2; pc->is_express = 1; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); diff --git a/hw/block/onenand.c b/hw/block/onenand.c index 883f4b1fa..8d8422739 100644 --- a/hw/block/onenand.c +++ b/hw/block/onenand.c @@ -224,7 +224,8 @@ static void onenand_reset(OneNANDState *s, int cold) /* Lock the whole flash */ memset(s->blockwp, ONEN_LOCK_LOCKED, s->blocks); - if (s->blk_cur && blk_read(s->blk_cur, 0, s->boot[0], 8) < 0) { + if (s->blk_cur && blk_pread(s->blk_cur, 0, s->boot[0], + 8 << BDRV_SECTOR_BITS) < 0) { hw_error("%s: Loading the BootRAM failed.\n", __func__); } } @@ -240,8 +241,11 @@ static void onenand_system_reset(DeviceState *dev) static inline int onenand_load_main(OneNANDState *s, int sec, int secn, void *dest) { + assert(UINT32_MAX >> BDRV_SECTOR_BITS > sec); + assert(UINT32_MAX >> BDRV_SECTOR_BITS > secn); if (s->blk_cur) { - return blk_read(s->blk_cur, sec, dest, secn) < 0; + return blk_pread(s->blk_cur, sec << BDRV_SECTOR_BITS, dest, + secn << BDRV_SECTOR_BITS) < 0; } else if (sec + secn > s->secs_cur) { return 1; } @@ -257,19 +261,22 @@ static inline int onenand_prog_main(OneNANDState *s, int sec, int secn, int result = 0; if (secn > 0) { - uint32_t size = (uint32_t)secn * 512; + uint32_t size = secn << BDRV_SECTOR_BITS; + uint32_t offset = sec << BDRV_SECTOR_BITS; + assert(UINT32_MAX >> BDRV_SECTOR_BITS > sec); + assert(UINT32_MAX >> BDRV_SECTOR_BITS > secn); const uint8_t *sp = (const uint8_t *)src; uint8_t *dp = 0; if (s->blk_cur) { dp = g_malloc(size); - if (!dp || blk_read(s->blk_cur, sec, dp, secn) < 0) { + if (!dp || blk_pread(s->blk_cur, offset, dp, size) < 0) { result = 1; } } else { if (sec + secn > s->secs_cur) { result = 1; } else { - dp = (uint8_t *)s->current + (sec << 9); + dp = (uint8_t *)s->current + offset; } } if (!result) { @@ -278,7 +285,7 @@ static inline int onenand_prog_main(OneNANDState *s, int sec, int secn, dp[i] &= sp[i]; } if (s->blk_cur) { - result = blk_write(s->blk_cur, sec, dp, secn) < 0; + result = blk_pwrite(s->blk_cur, offset, dp, size, 0) < 0; } } if (dp && s->blk_cur) { @@ -295,7 +302,8 @@ static inline int onenand_load_spare(OneNANDState *s, int sec, int secn, uint8_t buf[512]; if (s->blk_cur) { - if (blk_read(s->blk_cur, s->secs_cur + (sec >> 5), buf, 1) < 0) { + uint32_t offset = (s->secs_cur + (sec >> 5)) << BDRV_SECTOR_BITS; + if (blk_pread(s->blk_cur, offset, buf, BDRV_SECTOR_SIZE) < 0) { return 1; } memcpy(dest, buf + ((sec & 31) << 4), secn << 4); @@ -304,7 +312,7 @@ static inline int onenand_load_spare(OneNANDState *s, int sec, int secn, } else { memcpy(dest, s->current + (s->secs_cur << 9) + (sec << 4), secn << 4); } - + return 0; } @@ -315,10 +323,12 @@ static inline int onenand_prog_spare(OneNANDState *s, int sec, int secn, if (secn > 0) { const uint8_t *sp = (const uint8_t *)src; uint8_t *dp = 0, *dpp = 0; + uint32_t offset = (s->secs_cur + (sec >> 5)) << BDRV_SECTOR_BITS; + assert(UINT32_MAX >> BDRV_SECTOR_BITS > s->secs_cur + (sec >> 5)); if (s->blk_cur) { dp = g_malloc(512); if (!dp - || blk_read(s->blk_cur, s->secs_cur + (sec >> 5), dp, 1) < 0) { + || blk_pread(s->blk_cur, offset, dp, BDRV_SECTOR_SIZE) < 0) { result = 1; } else { dpp = dp + ((sec & 31) << 4); @@ -336,8 +346,8 @@ static inline int onenand_prog_spare(OneNANDState *s, int sec, int secn, dpp[i] &= sp[i]; } if (s->blk_cur) { - result = blk_write(s->blk_cur, s->secs_cur + (sec >> 5), - dp, 1) < 0; + result = blk_pwrite(s->blk_cur, offset, dp, + BDRV_SECTOR_SIZE, 0) < 0; } } g_free(dp); @@ -355,14 +365,17 @@ static inline int onenand_erase(OneNANDState *s, int sec, int num) for (; num > 0; num--, sec++) { if (s->blk_cur) { int erasesec = s->secs_cur + (sec >> 5); - if (blk_write(s->blk_cur, sec, blankbuf, 1) < 0) { + if (blk_pwrite(s->blk_cur, sec << BDRV_SECTOR_BITS, blankbuf, + BDRV_SECTOR_SIZE, 0) < 0) { goto fail; } - if (blk_read(s->blk_cur, erasesec, tmpbuf, 1) < 0) { + if (blk_pread(s->blk_cur, erasesec << BDRV_SECTOR_BITS, tmpbuf, + BDRV_SECTOR_SIZE) < 0) { goto fail; } memcpy(tmpbuf + ((sec & 31) << 4), blankbuf, 1 << 4); - if (blk_write(s->blk_cur, erasesec, tmpbuf, 1) < 0) { + if (blk_pwrite(s->blk_cur, erasesec << BDRV_SECTOR_BITS, tmpbuf, + BDRV_SECTOR_SIZE, 0) < 0) { goto fail; } } else { diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index 106a77523..62d7a5661 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -45,6 +45,7 @@ #include "qemu/bitops.h" #include "exec/address-spaces.h" #include "qemu/host-utils.h" +#include "qemu/log.h" #include "hw/sysbus.h" #include "sysemu/sysemu.h" @@ -64,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 @@ -413,11 +413,11 @@ static void pflash_update(pflash_t *pfl, int offset, int offset_end; if (pfl->blk) { offset_end = offset + size; - /* round to sectors */ - offset = offset >> 9; - offset_end = (offset_end + 511) >> 9; - blk_write(pfl->blk, offset, pfl->storage + (offset << 9), - offset_end - offset); + /* widen to sector boundaries */ + offset = QEMU_ALIGN_DOWN(offset, BDRV_SECTOR_SIZE); + offset_end = QEMU_ALIGN_UP(offset_end, BDRV_SECTOR_SIZE); + blk_pwrite(pfl->blk, offset, pfl->storage + offset, + offset_end - offset, 0); } } @@ -739,7 +739,7 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp) if (pfl->blk) { /* read the initial flash content */ - ret = blk_read(pfl->blk, 0, pfl->storage, total_len >> 9); + ret = blk_pread(pfl->blk, 0, pfl->storage, total_len); if (ret < 0) { vmstate_unregister_ram(&pfl->mem, DEVICE(pfl)); diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c index b13172c6e..4f6105cc5 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 { @@ -253,11 +252,11 @@ static void pflash_update(pflash_t *pfl, int offset, int offset_end; if (pfl->blk) { offset_end = offset + size; - /* round to sectors */ - offset = offset >> 9; - offset_end = (offset_end + 511) >> 9; - blk_write(pfl->blk, offset, pfl->storage + (offset << 9), - offset_end - offset); + /* widen to sector boundaries */ + offset = QEMU_ALIGN_DOWN(offset, BDRV_SECTOR_SIZE); + offset_end = QEMU_ALIGN_UP(offset_end, BDRV_SECTOR_SIZE); + blk_pwrite(pfl->blk, offset, pfl->storage + offset, + offset_end - offset, 0); } } @@ -622,7 +621,7 @@ static void pflash_cfi02_realize(DeviceState *dev, Error **errp) pfl->chip_len = chip_len; if (pfl->blk) { /* read the initial flash content */ - ret = blk_read(pfl->blk, 0, pfl->storage, chip_len >> 9); + ret = blk_pread(pfl->blk, 0, pfl->storage, chip_len); if (ret < 0) { vmstate_unregister_ram(&pfl->orig_mem, DEVICE(pfl)); error_setg(errp, "failed to read the initial flash content"); diff --git a/hw/block/tc58128.c b/hw/block/tc58128.c index 7909d5041..1d9f7ee00 100644 --- a/hw/block/tc58128.c +++ b/hw/block/tc58128.c @@ -45,7 +45,7 @@ static void init_dev(tc58128_dev * dev, const char *filename) } } else { /* Build first block with number of blocks */ - blocks = (ret + 528 * 32 - 1) / (528 * 32); + blocks = DIV_ROUND_UP(ret, 528 * 32); dev->flash_contents[0] = blocks & 0xff; dev->flash_contents[1] = (blocks >> 8) & 0xff; dev->flash_contents[2] = (blocks >> 16) & 0xff; diff --git a/hw/block/trace-events b/hw/block/trace-events new file mode 100644 index 000000000..d0dd94ff0 --- /dev/null +++ b/hw/block/trace-events @@ -0,0 +1,17 @@ +# See docs/tracing.txt for syntax documentation. + +# hw/block/virtio-blk.c +virtio_blk_req_complete(void *req, int status) "req %p status %d" +virtio_blk_rw_complete(void *req, int ret) "req %p ret %d" +virtio_blk_handle_write(void *req, uint64_t sector, size_t nsectors) "req %p sector %"PRIu64" nsectors %zu" +virtio_blk_handle_read(void *req, uint64_t sector, size_t nsectors) "req %p sector %"PRIu64" nsectors %zu" +virtio_blk_submit_multireq(void *mrb, int start, int num_reqs, uint64_t offset, size_t size, bool is_write) "mrb %p start %d num_reqs %d offset %"PRIu64" size %zu is_write %d" + +# hw/block/dataplane/virtio-blk.c +virtio_blk_data_plane_start(void *s) "dataplane %p" +virtio_blk_data_plane_stop(void *s) "dataplane %p" +virtio_blk_data_plane_process_request(void *s, unsigned int out_num, unsigned int in_num, unsigned int head) "dataplane %p out_num %u in_num %u head %u" + +# hw/block/hd-geometry.c +hd_geometry_lchs_guess(void *blk, int cyls, int heads, int secs) "blk %p LCHS %d %d %d" +hd_geometry_guess(void *blk, uint32_t cyls, uint32_t heads, uint32_t secs, int trans) "blk %p CHS %u %u %u trans %d" diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 3f88f8cf5..331d7667e 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; } @@ -322,7 +324,6 @@ static inline void submit_requests(BlockBackend *blk, MultiReqBuffer *mrb, { QEMUIOVector *qiov = &mrb->reqs[start]->qiov; int64_t sector_num = mrb->reqs[start]->sector_num; - int nb_sectors = mrb->reqs[start]->qiov.size / BDRV_SECTOR_SIZE; bool is_write = mrb->is_write; if (num_reqs > 1) { @@ -331,7 +332,7 @@ static inline void submit_requests(BlockBackend *blk, MultiReqBuffer *mrb, int tmp_niov = qiov->niov; /* mrb->reqs[start]->qiov was initialized from external so we can't - * modifiy it here. We need to initialize it locally and then add the + * modify it here. We need to initialize it locally and then add the * external iovecs. */ qemu_iovec_init(qiov, niov); @@ -343,23 +344,22 @@ static inline void submit_requests(BlockBackend *blk, MultiReqBuffer *mrb, qemu_iovec_concat(qiov, &mrb->reqs[i]->qiov, 0, mrb->reqs[i]->qiov.size); mrb->reqs[i - 1]->mr_next = mrb->reqs[i]; - nb_sectors += mrb->reqs[i]->qiov.size / BDRV_SECTOR_SIZE; } - assert(nb_sectors == qiov->size / BDRV_SECTOR_SIZE); - trace_virtio_blk_submit_multireq(mrb, start, num_reqs, sector_num, - nb_sectors, is_write); + trace_virtio_blk_submit_multireq(mrb, start, num_reqs, + sector_num << BDRV_SECTOR_BITS, + qiov->size, is_write); block_acct_merge_done(blk_get_stats(blk), is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ, num_reqs - 1); } if (is_write) { - blk_aio_writev(blk, sector_num, qiov, nb_sectors, - virtio_blk_rw_complete, mrb->reqs[start]); + blk_aio_pwritev(blk, sector_num << BDRV_SECTOR_BITS, qiov, 0, + virtio_blk_rw_complete, mrb->reqs[start]); } else { - blk_aio_readv(blk, sector_num, qiov, nb_sectors, - virtio_blk_rw_complete, mrb->reqs[start]); + blk_aio_preadv(blk, sector_num << BDRV_SECTOR_BITS, qiov, 0, + virtio_blk_rw_complete, mrb->reqs[start]); } } @@ -384,7 +384,7 @@ static int multireq_compare(const void *a, const void *b) void virtio_blk_submit_multireq(BlockBackend *blk, MultiReqBuffer *mrb) { int i = 0, start = 0, num_reqs = 0, niov = 0, nb_sectors = 0; - int max_xfer_len = 0; + uint32_t max_transfer; int64_t sector_num = 0; if (mrb->num_reqs == 1) { @@ -393,8 +393,7 @@ void virtio_blk_submit_multireq(BlockBackend *blk, MultiReqBuffer *mrb) return; } - max_xfer_len = blk_get_max_transfer_length(mrb->reqs[0]->dev->blk); - max_xfer_len = MIN_NON_ZERO(max_xfer_len, BDRV_REQUEST_MAX_SECTORS); + max_transfer = blk_get_max_transfer(mrb->reqs[0]->dev->blk); qsort(mrb->reqs, mrb->num_reqs, sizeof(*mrb->reqs), &multireq_compare); @@ -410,8 +409,9 @@ void virtio_blk_submit_multireq(BlockBackend *blk, MultiReqBuffer *mrb) */ if (sector_num + nb_sectors != req->sector_num || niov > blk_get_max_iov(blk) - req->qiov.niov || - req->qiov.size / BDRV_SECTOR_SIZE > max_xfer_len || - nb_sectors > max_xfer_len - req->qiov.size / BDRV_SECTOR_SIZE) { + req->qiov.size > max_transfer || + nb_sectors > (max_transfer - + req->qiov.size) / BDRV_SECTOR_SIZE) { submit_requests(blk, mrb, start, num_reqs, niov); num_reqs = 0; } @@ -585,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); } @@ -654,15 +654,20 @@ static void virtio_blk_reset(VirtIODevice *vdev) { VirtIOBlock *s = VIRTIO_BLK(vdev); AioContext *ctx; + VirtIOBlockReq *req; - /* - * This should cancel pending requests, but can't do nicely until there - * are per-device request lists. - */ ctx = blk_get_aio_context(s->blk); aio_context_acquire(ctx); blk_drain(s->blk); + /* We drop queued requests after blk_drain() because blk_drain() itself can + * produce them. */ + while (s->rq) { + req = s->rq; + s->rq = req->next; + virtio_blk_free_request(req); + } + if (s->dataplane) { virtio_blk_data_plane_stop(s->dataplane); } @@ -710,6 +715,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)); } @@ -753,6 +759,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; } @@ -794,14 +803,9 @@ static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status) } } -static void virtio_blk_save(QEMUFile *f, void *opaque) +static void virtio_blk_save(QEMUFile *f, void *opaque, size_t size) { VirtIODevice *vdev = VIRTIO_DEVICE(opaque); - VirtIOBlock *s = VIRTIO_BLK(vdev); - - if (s->dataplane) { - virtio_blk_data_plane_stop(s->dataplane); - } virtio_save(vdev, f); } @@ -813,21 +817,23 @@ 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; } qemu_put_sbyte(f, 0); } -static int virtio_blk_load(QEMUFile *f, void *opaque, int version_id) +static int virtio_blk_load(QEMUFile *f, void *opaque, size_t size) { VirtIOBlock *s = opaque; VirtIODevice *vdev = VIRTIO_DEVICE(s); - if (version_id != 2) - return -EINVAL; - - return virtio_load(vdev, f, version_id); + return virtio_load(vdev, f, 2); } static int virtio_blk_load_device(VirtIODevice *vdev, QEMUFile *f, @@ -836,9 +842,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; } @@ -863,7 +882,7 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) VirtIOBlock *s = VIRTIO_BLK(dev); 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"); @@ -873,8 +892,13 @@ 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); + blkconf_apply_backend_options(&conf->conf); s->original_wce = blk_enable_write_cache(conf->conf.blk); blkconf_geometry(&conf->conf, NULL, 65535, 255, 255, &err); if (err) { @@ -890,7 +914,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_aio(vdev, 128, virtio_blk_handle_output); + } virtio_blk_data_plane_create(vdev, conf, &s->dataplane, &err); if (err != NULL) { error_propagate(errp, err); @@ -899,8 +925,6 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) } s->change = qemu_add_vm_change_state_handler(virtio_blk_dma_restart_cb, s); - register_savevm(dev, "virtio-blk", virtio_blk_id++, 2, - virtio_blk_save, virtio_blk_load, s); blk_set_dev_ops(s->blk, &virtio_block_ops, s); blk_set_guest_block_size(s->blk, s->conf.conf.logical_block_size); @@ -915,7 +939,6 @@ static void virtio_blk_device_unrealize(DeviceState *dev, Error **errp) virtio_blk_data_plane_destroy(s->dataplane); s->dataplane = NULL; qemu_del_vm_change_state_handler(s->change); - unregister_savevm(dev, "virtio-blk", s); blockdev_mark_auto_del(s->blk); virtio_cleanup(vdev); } @@ -933,8 +956,11 @@ static void virtio_blk_instance_init(Object *obj) DEVICE(obj), NULL); } +VMSTATE_VIRTIO_DEVICE(blk, 2, virtio_blk_load, virtio_blk_save); + static Property virtio_blk_properties[] = { DEFINE_BLOCK_PROPERTIES(VirtIOBlock, conf.conf), + DEFINE_BLOCK_ERROR_PROPERTIES(VirtIOBlock, conf.conf), DEFINE_BLOCK_CHS_PROPERTIES(VirtIOBlock, conf.conf), DEFINE_PROP_STRING("serial", VirtIOBlock, conf.serial), DEFINE_PROP_BIT("config-wce", VirtIOBlock, conf.config_wce, 0, true), @@ -943,6 +969,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(), }; @@ -952,6 +979,7 @@ static void virtio_blk_class_init(ObjectClass *klass, void *data) VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); dc->props = virtio_blk_properties; + dc->vmsd = &vmstate_virtio_blk; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); vdc->realize = virtio_blk_device_realize; vdc->unrealize = virtio_blk_device_unrealize; diff --git a/hw/block/xen_blkif.h b/hw/block/xen_blkif.h index c68487cb3..3300b6fc0 100644 --- a/hw/block/xen_blkif.h +++ b/hw/block/xen_blkif.h @@ -1,35 +1,45 @@ -#ifndef __XEN_BLKIF_H__ -#define __XEN_BLKIF_H__ +#ifndef XEN_BLKIF_H +#define XEN_BLKIF_H #include <xen/io/ring.h> #include <xen/io/blkif.h> #include <xen/io/protocols.h> -/* Not a real protocol. Used to generate ring structs which contain +/* + * Not a real protocol. Used to generate ring structs which contain * the elements common to all protocols only. This way we get a * compiler-checkable way to use common struct elements, so we can - * avoid using switch(protocol) in a number of places. */ + * avoid using switch(protocol) in a number of places. + */ struct blkif_common_request { - char dummy; + char dummy; }; struct blkif_common_response { - char dummy; + char dummy; }; /* i386 protocol version */ #pragma pack(push, 4) struct blkif_x86_32_request { - uint8_t operation; /* BLKIF_OP_??? */ - uint8_t nr_segments; /* number of segments */ - blkif_vdev_t handle; /* only for read/write requests */ - uint64_t id; /* private guest value, echoed in resp */ - blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */ - struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + uint8_t operation; /* BLKIF_OP_??? */ + uint8_t nr_segments; /* number of segments */ + blkif_vdev_t handle; /* only for read/write requests */ + uint64_t id; /* private guest value, echoed in resp */ + blkif_sector_t sector_number; /* start sector idx on disk (r/w only) */ + struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST]; +}; +struct blkif_x86_32_request_discard { + uint8_t operation; /* BLKIF_OP_DISCARD */ + uint8_t flag; /* nr_segments in request struct */ + blkif_vdev_t handle; /* only for read/write requests */ + uint64_t id; /* private guest value, echoed in resp */ + blkif_sector_t sector_number; /* start sector idx on disk (r/w only) */ + uint64_t nr_sectors; /* # of contiguous sectors to discard */ }; struct blkif_x86_32_response { - uint64_t id; /* copied from request */ - uint8_t operation; /* copied from request */ - int16_t status; /* BLKIF_RSP_??? */ + uint64_t id; /* copied from request */ + uint8_t operation; /* copied from request */ + int16_t status; /* BLKIF_RSP_??? */ }; typedef struct blkif_x86_32_request blkif_x86_32_request_t; typedef struct blkif_x86_32_response blkif_x86_32_response_t; @@ -37,83 +47,100 @@ typedef struct blkif_x86_32_response blkif_x86_32_response_t; /* x86_64 protocol version */ struct blkif_x86_64_request { - uint8_t operation; /* BLKIF_OP_??? */ - uint8_t nr_segments; /* number of segments */ - blkif_vdev_t handle; /* only for read/write requests */ - uint64_t __attribute__((__aligned__(8))) id; - blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */ - struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + uint8_t operation; /* BLKIF_OP_??? */ + uint8_t nr_segments; /* number of segments */ + blkif_vdev_t handle; /* only for read/write requests */ + uint64_t __attribute__((__aligned__(8))) id; + blkif_sector_t sector_number; /* start sector idx on disk (r/w only) */ + struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST]; +}; +struct blkif_x86_64_request_discard { + uint8_t operation; /* BLKIF_OP_DISCARD */ + uint8_t flag; /* nr_segments in request struct */ + blkif_vdev_t handle; /* only for read/write requests */ + uint64_t __attribute__((__aligned__(8))) id; + blkif_sector_t sector_number; /* start sector idx on disk (r/w only) */ + uint64_t nr_sectors; /* # of contiguous sectors to discard */ }; struct blkif_x86_64_response { - uint64_t __attribute__((__aligned__(8))) id; - uint8_t operation; /* copied from request */ - int16_t status; /* BLKIF_RSP_??? */ + uint64_t __attribute__((__aligned__(8))) id; + uint8_t operation; /* copied from request */ + int16_t status; /* BLKIF_RSP_??? */ }; typedef struct blkif_x86_64_request blkif_x86_64_request_t; typedef struct blkif_x86_64_response blkif_x86_64_response_t; -DEFINE_RING_TYPES(blkif_common, struct blkif_common_request, struct blkif_common_response); -DEFINE_RING_TYPES(blkif_x86_32, struct blkif_x86_32_request, struct blkif_x86_32_response); -DEFINE_RING_TYPES(blkif_x86_64, struct blkif_x86_64_request, struct blkif_x86_64_response); +DEFINE_RING_TYPES(blkif_common, struct blkif_common_request, + struct blkif_common_response); +DEFINE_RING_TYPES(blkif_x86_32, struct blkif_x86_32_request, + struct blkif_x86_32_response); +DEFINE_RING_TYPES(blkif_x86_64, struct blkif_x86_64_request, + struct blkif_x86_64_response); union blkif_back_rings { - blkif_back_ring_t native; - blkif_common_back_ring_t common; - blkif_x86_32_back_ring_t x86_32_part; - blkif_x86_64_back_ring_t x86_64_part; + blkif_back_ring_t native; + blkif_common_back_ring_t common; + blkif_x86_32_back_ring_t x86_32_part; + blkif_x86_64_back_ring_t x86_64_part; }; typedef union blkif_back_rings blkif_back_rings_t; enum blkif_protocol { - BLKIF_PROTOCOL_NATIVE = 1, - BLKIF_PROTOCOL_X86_32 = 2, - BLKIF_PROTOCOL_X86_64 = 3, + BLKIF_PROTOCOL_NATIVE = 1, + BLKIF_PROTOCOL_X86_32 = 2, + BLKIF_PROTOCOL_X86_64 = 3, }; -static inline void blkif_get_x86_32_req(blkif_request_t *dst, blkif_x86_32_request_t *src) +static inline void blkif_get_x86_32_req(blkif_request_t *dst, + blkif_x86_32_request_t *src) { - int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST; + int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST; - dst->operation = src->operation; - dst->nr_segments = src->nr_segments; - dst->handle = src->handle; - dst->id = src->id; - dst->sector_number = src->sector_number; - if (src->operation == BLKIF_OP_DISCARD) { - struct blkif_request_discard *s = (void *)src; - struct blkif_request_discard *d = (void *)dst; - d->nr_sectors = s->nr_sectors; - return; - } - /* prevent the compiler from optimizing the code and using src->nr_segments instead */ - barrier(); - if (n > dst->nr_segments) - n = dst->nr_segments; - for (i = 0; i < n; i++) - dst->seg[i] = src->seg[i]; + dst->operation = src->operation; + dst->nr_segments = src->nr_segments; + dst->handle = src->handle; + dst->id = src->id; + dst->sector_number = src->sector_number; + /* Prevent the compiler from using src->... instead. */ + barrier(); + if (dst->operation == BLKIF_OP_DISCARD) { + struct blkif_x86_32_request_discard *s = (void *)src; + struct blkif_request_discard *d = (void *)dst; + d->nr_sectors = s->nr_sectors; + return; + } + if (n > dst->nr_segments) { + n = dst->nr_segments; + } + for (i = 0; i < n; i++) { + dst->seg[i] = src->seg[i]; + } } -static inline void blkif_get_x86_64_req(blkif_request_t *dst, blkif_x86_64_request_t *src) +static inline void blkif_get_x86_64_req(blkif_request_t *dst, + blkif_x86_64_request_t *src) { - int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST; + int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST; - dst->operation = src->operation; - dst->nr_segments = src->nr_segments; - dst->handle = src->handle; - dst->id = src->id; - dst->sector_number = src->sector_number; - if (src->operation == BLKIF_OP_DISCARD) { - struct blkif_request_discard *s = (void *)src; - struct blkif_request_discard *d = (void *)dst; - d->nr_sectors = s->nr_sectors; - return; - } - /* prevent the compiler from optimizing the code and using src->nr_segments instead */ - barrier(); - if (n > dst->nr_segments) - n = dst->nr_segments; - for (i = 0; i < n; i++) - dst->seg[i] = src->seg[i]; + dst->operation = src->operation; + dst->nr_segments = src->nr_segments; + dst->handle = src->handle; + dst->id = src->id; + dst->sector_number = src->sector_number; + /* Prevent the compiler from using src->... instead. */ + barrier(); + if (dst->operation == BLKIF_OP_DISCARD) { + struct blkif_x86_64_request_discard *s = (void *)src; + struct blkif_request_discard *d = (void *)dst; + d->nr_sectors = s->nr_sectors; + return; + } + if (n > dst->nr_segments) { + n = dst->nr_segments; + } + for (i = 0; i < n; i++) { + dst->seg[i] = src->seg[i]; + } } -#endif /* __XEN_BLKIF_H__ */ +#endif /* XEN_BLKIF_H */ diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c index d4ce380fe..3b8ad33fc 100644 --- a/hw/block/xen_disk.c +++ b/hw/block/xen_disk.c @@ -21,7 +21,6 @@ #include "qemu/osdep.h" #include <sys/ioctl.h> -#include <sys/mman.h> #include <sys/uio.h> #include "hw/hw.h" @@ -554,9 +553,8 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq) block_acct_start(blk_get_stats(blkdev->blk), &ioreq->acct, ioreq->v.size, BLOCK_ACCT_READ); ioreq->aio_inflight++; - blk_aio_readv(blkdev->blk, ioreq->start / BLOCK_SIZE, - &ioreq->v, ioreq->v.size / BLOCK_SIZE, - qemu_aio_complete, ioreq); + blk_aio_preadv(blkdev->blk, ioreq->start, &ioreq->v, 0, + qemu_aio_complete, ioreq); break; case BLKIF_OP_WRITE: case BLKIF_OP_FLUSH_DISKCACHE: @@ -569,17 +567,17 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq) ioreq->req.operation == BLKIF_OP_WRITE ? BLOCK_ACCT_WRITE : BLOCK_ACCT_FLUSH); ioreq->aio_inflight++; - blk_aio_writev(blkdev->blk, ioreq->start / BLOCK_SIZE, - &ioreq->v, ioreq->v.size / BLOCK_SIZE, - qemu_aio_complete, ioreq); + blk_aio_pwritev(blkdev->blk, ioreq->start, &ioreq->v, 0, + qemu_aio_complete, ioreq); break; case BLKIF_OP_DISCARD: { struct blkif_request_discard *discard_req = (void *)&ioreq->req; ioreq->aio_inflight++; - blk_aio_discard(blkdev->blk, - discard_req->sector_number, discard_req->nr_sectors, - qemu_aio_complete, ioreq); + blk_aio_pdiscard(blkdev->blk, + discard_req->sector_number << BDRV_SECTOR_BITS, + discard_req->nr_sectors << BDRV_SECTOR_BITS, + qemu_aio_complete, ioreq); break; } default: @@ -681,6 +679,8 @@ static int blk_get_request(struct XenBlkDev *blkdev, struct ioreq *ioreq, RING_I RING_GET_REQUEST(&blkdev->rings.x86_64_part, rc)); break; } + /* Prevent the compiler from accessing the on-ring fields instead. */ + barrier(); return 0; } diff --git a/hw/bt/hci-csr.c b/hw/bt/hci-csr.c index 2e970b656..d688372ca 100644 --- a/hw/bt/hci-csr.c +++ b/hw/bt/hci-csr.c @@ -22,6 +22,7 @@ #include "qemu-common.h" #include "sysemu/char.h" #include "qemu/timer.h" +#include "qemu/bswap.h" #include "hw/irq.h" #include "sysemu/bt.h" #include "hw/bt.h" @@ -38,9 +39,14 @@ struct csrhci_s { int out_size; uint8_t outfifo[FIFO_LEN * 2]; uint8_t inpkt[FIFO_LEN]; + enum { + CSR_HDR_LEN, + CSR_DATA_LEN, + CSR_DATA + } in_state; int in_len; int in_hdr; - int in_data; + int in_needed; QEMUTimer *out_tm; int64_t baud_delay; @@ -295,38 +301,60 @@ static int csrhci_data_len(const uint8_t *pkt) exit(-1); } +static void csrhci_ready_for_next_inpkt(struct csrhci_s *s) +{ + s->in_state = CSR_HDR_LEN; + s->in_len = 0; + s->in_needed = 2; + s->in_hdr = INT_MAX; +} + static int csrhci_write(struct CharDriverState *chr, const uint8_t *buf, int len) { struct csrhci_s *s = (struct csrhci_s *) chr->opaque; - int plen = s->in_len; + int total = 0; if (!s->enable) return 0; - s->in_len += len; - memcpy(s->inpkt + plen, buf, len); + for (;;) { + int cnt = MIN(len, s->in_needed - s->in_len); + if (cnt) { + memcpy(s->inpkt + s->in_len, buf, cnt); + s->in_len += cnt; + buf += cnt; + len -= cnt; + total += cnt; + } + + if (s->in_len < s->in_needed) { + break; + } - while (1) { - if (s->in_len >= 2 && plen < 2) + if (s->in_state == CSR_HDR_LEN) { s->in_hdr = csrhci_header_len(s->inpkt) + 1; + assert(s->in_hdr >= s->in_needed); + s->in_needed = s->in_hdr; + s->in_state = CSR_DATA_LEN; + continue; + } - if (s->in_len >= s->in_hdr && plen < s->in_hdr) - s->in_data = csrhci_data_len(s->inpkt) + s->in_hdr; + if (s->in_state == CSR_DATA_LEN) { + s->in_needed += csrhci_data_len(s->inpkt); + /* hci_acl_hdr could specify more than 4096 bytes, so assert. */ + assert(s->in_needed <= sizeof(s->inpkt)); + s->in_state = CSR_DATA; + continue; + } - if (s->in_len >= s->in_data) { + if (s->in_state == CSR_DATA) { csrhci_in_packet(s, s->inpkt); - - memmove(s->inpkt, s->inpkt + s->in_len, s->in_len - s->in_data); - s->in_len -= s->in_data; - s->in_hdr = INT_MAX; - s->in_data = INT_MAX; - plen = 0; - } else - break; + csrhci_ready_for_next_inpkt(s); + } } - return len; + return total; } static void csrhci_out_hci_packet_event(void *opaque, @@ -388,11 +416,9 @@ static void csrhci_reset(struct csrhci_s *s) { s->out_len = 0; s->out_size = FIFO_LEN; - s->in_len = 0; + csrhci_ready_for_next_inpkt(s); s->baud_delay = NANOSECONDS_PER_SECOND; s->enable = 0; - s->in_hdr = INT_MAX; - s->in_data = INT_MAX; s->modem_state = 0; /* After a while... (but sooner than 10ms) */ diff --git a/hw/bt/hci.c b/hw/bt/hci.c index 7d5220509..351123fab 100644 --- a/hw/bt/hci.c +++ b/hw/bt/hci.c @@ -426,11 +426,7 @@ static void bt_submit_raw_acl(struct bt_piconet_s *net, int length, uint8_t *dat * be continuously allocated. We do it though, to preserve similar * behaviour between hosts. Some things, like the BD_ADDR cannot be * preserved though (for example if a real hci is used). */ -#ifdef HOST_WORDS_BIGENDIAN -# define HNDL(raw) bswap16(raw) -#else -# define HNDL(raw) (raw) -#endif +#define HNDL(raw) cpu_to_le16(raw) static const uint8_t bt_event_reserved_mask[8] = { 0xff, 0x9f, 0xfb, 0xff, 0x07, 0x18, 0x00, 0x00, @@ -1504,8 +1500,8 @@ static void bt_submit_hci(struct HCIInfo *info, return; #define PARAM(cmd, param) (((cmd##_cp *) data)->param) -#define PARAM16(cmd, param) le16_to_cpup(&PARAM(cmd, param)) -#define PARAMHANDLE(cmd) HNDL(PARAM(cmd, handle)) +#define PARAM16(cmd, param) lduw_le_p(&PARAM(cmd, param)) +#define PARAMHANDLE(cmd) PARAM16(cmd, handle) #define LENGTH_CHECK(cmd) if (length < sizeof(cmd##_cp)) goto short_hci /* Note: the supported commands bitmask in bt_hci_read_local_commands_rp * needs to be updated every time a command is implemented here! */ diff --git a/hw/bt/l2cap.c b/hw/bt/l2cap.c index 806525194..e34204514 100644 --- a/hw/bt/l2cap.c +++ b/hw/bt/l2cap.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/timer.h" +#include "qemu/bswap.h" #include "hw/bt.h" #define L2CAP_CID_MAX 0x100 /* Between 0x40 and 0x10000 */ @@ -525,9 +526,9 @@ static int l2cap_channel_config(struct l2cap_instance_s *l2cap, } /* MTU */ - val = le16_to_cpup((void *) opt->val); + val = lduw_le_p(opt->val); if (val < ch->min_mtu) { - cpu_to_le16w((void *) opt->val, ch->min_mtu); + stw_le_p(opt->val, ch->min_mtu); result = L2CAP_CONF_UNACCEPT; break; } @@ -542,7 +543,7 @@ static int l2cap_channel_config(struct l2cap_instance_s *l2cap, } /* Flush Timeout */ - val = le16_to_cpup((void *) opt->val); + val = lduw_le_p(opt->val); if (val < 0x0001) { opt->val[0] = 0xff; opt->val[1] = 0xff; @@ -986,7 +987,7 @@ static void l2cap_bframe_in(struct l2cap_chan_s *ch, uint16_t cid, static void l2cap_iframe_in(struct l2cap_chan_s *ch, uint16_t cid, const l2cap_hdr *hdr, int len) { - uint16_t fcs = le16_to_cpup((void *) (hdr->data + len - 2)); + uint16_t fcs = lduw_le_p(hdr->data + len - 2); if (len < 4) goto len_error; @@ -1001,7 +1002,7 @@ static void l2cap_iframe_in(struct l2cap_chan_s *ch, uint16_t cid, /* TODO: Signal an error? */ return; } - l2cap_sframe_in(ch, le16_to_cpup((void *) hdr->data)); + l2cap_sframe_in(ch, lduw_le_p(hdr->data)); return; } @@ -1021,7 +1022,7 @@ static void l2cap_iframe_in(struct l2cap_chan_s *ch, uint16_t cid, if (len - 6 > ch->mps) goto len_error; - ch->len_total = le16_to_cpup((void *) (hdr->data + 2)); + ch->len_total = lduw_le_p(hdr->data + 2); if (len >= 6 + ch->len_total) goto seg_error; diff --git a/hw/bt/sdp.c b/hw/bt/sdp.c index be26009b0..f67b3b89c 100644 --- a/hw/bt/sdp.c +++ b/hw/bt/sdp.c @@ -19,6 +19,7 @@ #include "qemu/osdep.h" #include "qemu-common.h" +#include "qemu/host-utils.h" #include "hw/bt.h" struct bt_l2cap_sdp_state_s { diff --git a/hw/char/bcm2835_aux.c b/hw/char/bcm2835_aux.c index 0394d11a8..319f1652f 100644 --- a/hw/char/bcm2835_aux.c +++ b/hw/char/bcm2835_aux.c @@ -22,6 +22,7 @@ #include "qemu/osdep.h" #include "hw/char/bcm2835_aux.h" +#include "qemu/log.h" #define AUX_IRQ 0x0 #define AUX_ENABLES 0x4 diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c index 797787823..e3bc52f7d 100644 --- a/hw/char/cadence_uart.c +++ b/hw/char/cadence_uart.c @@ -17,6 +17,10 @@ */ #include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "sysemu/char.h" +#include "qemu/timer.h" +#include "qemu/log.h" #include "hw/char/cadence_uart.h" #ifdef CADENCE_UART_ERR_DEBUG @@ -284,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); @@ -464,9 +474,6 @@ static void cadence_uart_realize(DeviceState *dev, Error **errp) s->fifo_trigger_handle = timer_new_ns(QEMU_CLOCK_VIRTUAL, fifo_trigger_update, s); - /* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */ - s->chr = qemu_char_get_next_serial(); - if (s->chr) { qemu_chr_add_handlers(s->chr, uart_can_receive, uart_receive, uart_event, s); @@ -513,6 +520,11 @@ static const VMStateDescription vmstate_cadence_uart = { } }; +static Property cadence_uart_properties[] = { + DEFINE_PROP_CHR("chardev", CadenceUARTState, chr), + DEFINE_PROP_END_OF_LIST(), +}; + static void cadence_uart_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -520,9 +532,8 @@ static void cadence_uart_class_init(ObjectClass *klass, void *data) dc->realize = cadence_uart_realize; dc->vmsd = &vmstate_cadence_uart; dc->reset = cadence_uart_reset; - /* Reason: realize() method uses qemu_char_get_next_serial() */ - dc->cannot_instantiate_with_device_add_yet = true; -} + dc->props = cadence_uart_properties; + } static const TypeInfo cadence_uart_info = { .name = TYPE_CADENCE_UART, diff --git a/hw/char/digic-uart.c b/hw/char/digic-uart.c index d3bc533d7..c7604e676 100644 --- a/hw/char/digic-uart.c +++ b/hw/char/digic-uart.c @@ -30,6 +30,7 @@ #include "hw/hw.h" #include "hw/sysbus.h" #include "sysemu/char.h" +#include "qemu/log.h" #include "hw/char/digic-uart.h" @@ -144,8 +145,6 @@ static void digic_uart_realize(DeviceState *dev, Error **errp) { DigicUartState *s = DIGIC_UART(dev); - /* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */ - s->chr = qemu_char_get_next_serial(); if (s->chr) { qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s); } @@ -171,6 +170,11 @@ static const VMStateDescription vmstate_digic_uart = { } }; +static Property digic_uart_properties[] = { + DEFINE_PROP_CHR("chardev", DigicUartState, chr), + DEFINE_PROP_END_OF_LIST(), +}; + static void digic_uart_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -178,8 +182,7 @@ static void digic_uart_class_init(ObjectClass *klass, void *data) dc->realize = digic_uart_realize; dc->reset = digic_uart_reset; dc->vmsd = &vmstate_digic_uart; - /* Reason: realize() method uses qemu_char_get_next_serial() */ - dc->cannot_instantiate_with_device_add_yet = true; + dc->props = digic_uart_properties; } static const TypeInfo digic_uart_info = { diff --git a/hw/char/escc.c b/hw/char/escc.c index 7bf09a007..31a5f902f 100644 --- a/hw/char/escc.c +++ b/hw/char/escc.c @@ -983,28 +983,40 @@ void slavio_serial_ms_kbd_init(hwaddr base, qemu_irq irq, sysbus_mmio_map(s, 0, base); } -static int escc_init1(SysBusDevice *dev) +static void escc_init1(Object *obj) +{ + ESCCState *s = ESCC(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); + unsigned int i; + + for (i = 0; i < 2; i++) { + sysbus_init_irq(dev, &s->chn[i].irq); + s->chn[i].chn = 1 - i; + } + s->chn[0].otherchn = &s->chn[1]; + s->chn[1].otherchn = &s->chn[0]; + + sysbus_init_mmio(dev, &s->mmio); +} + +static void escc_realize(DeviceState *dev, Error **errp) { ESCCState *s = ESCC(dev); unsigned int i; s->chn[0].disabled = s->disabled; s->chn[1].disabled = s->disabled; + + memory_region_init_io(&s->mmio, OBJECT(dev), &escc_mem_ops, s, "escc", + ESCC_SIZE << s->it_shift); + for (i = 0; i < 2; i++) { - sysbus_init_irq(dev, &s->chn[i].irq); - s->chn[i].chn = 1 - i; - s->chn[i].clock = s->frequency / 2; if (s->chn[i].chr) { + s->chn[i].clock = s->frequency / 2; qemu_chr_add_handlers(s->chn[i].chr, serial_can_receive, serial_receive1, serial_event, &s->chn[i]); } } - s->chn[0].otherchn = &s->chn[1]; - s->chn[1].otherchn = &s->chn[0]; - - memory_region_init_io(&s->mmio, OBJECT(s), &escc_mem_ops, s, "escc", - ESCC_SIZE << s->it_shift); - sysbus_init_mmio(dev, &s->mmio); if (s->chn[0].type == mouse) { qemu_add_mouse_event_handler(sunmouse_event, &s->chn[0], 0, @@ -1014,8 +1026,6 @@ static int escc_init1(SysBusDevice *dev) s->chn[1].hs = qemu_input_handler_register((DeviceState *)(&s->chn[1]), &sunkbd_handler); } - - return 0; } static Property escc_properties[] = { @@ -1032,10 +1042,9 @@ static Property escc_properties[] = { static void escc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = escc_init1; dc->reset = escc_reset; + dc->realize = escc_realize; dc->vmsd = &vmstate_escc; dc->props = escc_properties; set_bit(DEVICE_CATEGORY_INPUT, dc->categories); @@ -1045,6 +1054,7 @@ static const TypeInfo escc_info = { .name = TYPE_ESCC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(ESCCState), + .instance_init = escc_init1, .class_init = escc_class_init, }; diff --git a/hw/char/etraxfs_ser.c b/hw/char/etraxfs_ser.c index 146b387e7..04ca04fe2 100644 --- a/hw/char/etraxfs_ser.c +++ b/hw/char/etraxfs_ser.c @@ -159,6 +159,11 @@ static const MemoryRegionOps ser_ops = { } }; +static Property etraxfs_ser_properties[] = { + DEFINE_PROP_CHR("chardev", ETRAXSerial, chr), + DEFINE_PROP_END_OF_LIST(), +}; + static void serial_receive(void *opaque, const uint8_t *buf, int size) { ETRAXSerial *s = opaque; @@ -209,40 +214,42 @@ static void etraxfs_ser_reset(DeviceState *d) } -static int etraxfs_ser_init(SysBusDevice *dev) +static void etraxfs_ser_init(Object *obj) { - ETRAXSerial *s = ETRAX_SERIAL(dev); + ETRAXSerial *s = ETRAX_SERIAL(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); sysbus_init_irq(dev, &s->irq); - memory_region_init_io(&s->mmio, OBJECT(s), &ser_ops, s, + memory_region_init_io(&s->mmio, obj, &ser_ops, s, "etraxfs-serial", R_MAX * 4); sysbus_init_mmio(dev, &s->mmio); +} + +static void etraxfs_ser_realize(DeviceState *dev, Error **errp) +{ + ETRAXSerial *s = ETRAX_SERIAL(dev); - /* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */ - s->chr = qemu_char_get_next_serial(); if (s->chr) { qemu_chr_add_handlers(s->chr, serial_can_receive, serial_receive, serial_event, s); } - return 0; } static void etraxfs_ser_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = etraxfs_ser_init; dc->reset = etraxfs_ser_reset; - /* Reason: init() method uses qemu_char_get_next_serial() */ - dc->cannot_instantiate_with_device_add_yet = true; + dc->props = etraxfs_ser_properties; + dc->realize = etraxfs_ser_realize; } static const TypeInfo etraxfs_ser_info = { .name = TYPE_ETRAX_FS_SERIAL, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(ETRAXSerial), + .instance_init = etraxfs_ser_init, .class_init = etraxfs_ser_class_init, }; diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c index 6df74ac7c..44856d671 100644 --- a/hw/char/imx_serial.c +++ b/hw/char/imx_serial.c @@ -22,6 +22,7 @@ #include "hw/char/imx_serial.h" #include "sysemu/sysemu.h" #include "sysemu/char.h" +#include "qemu/log.h" #ifndef DEBUG_IMX_UART #define DEBUG_IMX_UART 0 diff --git a/hw/char/ipoctal232.c b/hw/char/ipoctal232.c index bc0ae4980..9ead32af6 100644 --- a/hw/char/ipoctal232.c +++ b/hw/char/ipoctal232.c @@ -2,7 +2,7 @@ * QEMU GE IP-Octal 232 IndustryPack emulation * * Copyright (C) 2012 Igalia, S.L. - * Author: Alberto Garcia <agarcia@igalia.com> + * Author: Alberto Garcia <berto@igalia.com> * * This code is licensed under the GNU GPL v2 or (at your option) any * later version. diff --git a/hw/char/lm32_juart.c b/hw/char/lm32_juart.c index 5bf8acfe8..28c2cf702 100644 --- a/hw/char/lm32_juart.c +++ b/hw/char/lm32_juart.c @@ -114,17 +114,13 @@ static void juart_reset(DeviceState *d) s->jrx = 0; } -static int lm32_juart_init(SysBusDevice *dev) +static void lm32_juart_realize(DeviceState *dev, Error **errp) { LM32JuartState *s = LM32_JUART(dev); - /* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */ - s->chr = qemu_char_get_next_serial(); if (s->chr) { qemu_chr_add_handlers(s->chr, juart_can_rx, juart_rx, juart_event, s); } - - return 0; } static const VMStateDescription vmstate_lm32_juart = { @@ -138,16 +134,19 @@ static const VMStateDescription vmstate_lm32_juart = { } }; +static Property lm32_juart_properties[] = { + DEFINE_PROP_CHR("chardev", LM32JuartState, chr), + DEFINE_PROP_END_OF_LIST(), +}; + static void lm32_juart_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = lm32_juart_init; dc->reset = juart_reset; dc->vmsd = &vmstate_lm32_juart; - /* Reason: init() method uses qemu_char_get_next_serial() */ - dc->cannot_instantiate_with_device_add_yet = true; + dc->props = lm32_juart_properties; + dc->realize = lm32_juart_realize; } static const TypeInfo lm32_juart_info = { diff --git a/hw/char/lm32_uart.c b/hw/char/lm32_uart.c index 036813d0f..b5c760dda 100644 --- a/hw/char/lm32_uart.c +++ b/hw/char/lm32_uart.c @@ -249,23 +249,25 @@ static void uart_reset(DeviceState *d) s->regs[R_LSR] = LSR_THRE | LSR_TEMT; } -static int lm32_uart_init(SysBusDevice *dev) +static void lm32_uart_init(Object *obj) { - LM32UartState *s = LM32_UART(dev); + LM32UartState *s = LM32_UART(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); sysbus_init_irq(dev, &s->irq); - memory_region_init_io(&s->iomem, OBJECT(s), &uart_ops, s, + memory_region_init_io(&s->iomem, obj, &uart_ops, s, "uart", R_MAX * 4); sysbus_init_mmio(dev, &s->iomem); +} + +static void lm32_uart_realize(DeviceState *dev, Error **errp) +{ + LM32UartState *s = LM32_UART(dev); - /* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */ - s->chr = qemu_char_get_next_serial(); if (s->chr) { qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s); } - - return 0; } static const VMStateDescription vmstate_lm32_uart = { @@ -278,22 +280,26 @@ static const VMStateDescription vmstate_lm32_uart = { } }; +static Property lm32_uart_properties[] = { + DEFINE_PROP_CHR("chardev", LM32UartState, chr), + DEFINE_PROP_END_OF_LIST(), +}; + static void lm32_uart_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = lm32_uart_init; dc->reset = uart_reset; dc->vmsd = &vmstate_lm32_uart; - /* Reason: init() method uses qemu_char_get_next_serial() */ - dc->cannot_instantiate_with_device_add_yet = true; + dc->props = lm32_uart_properties; + dc->realize = lm32_uart_realize; } static const TypeInfo lm32_uart_info = { .name = TYPE_LM32_UART, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(LM32UartState), + .instance_init = lm32_uart_init, .class_init = lm32_uart_class_init, }; diff --git a/hw/char/milkymist-uart.c b/hw/char/milkymist-uart.c index 03b36b223..baddb3764 100644 --- a/hw/char/milkymist-uart.c +++ b/hw/char/milkymist-uart.c @@ -18,7 +18,7 @@ * * * Specification available at: - * http://www.milkymist.org/socdoc/uart.pdf + * http://milkymist.walle.cc/socdoc/uart.pdf */ #include "qemu/osdep.h" @@ -200,8 +200,6 @@ static void milkymist_uart_realize(DeviceState *dev, Error **errp) { MilkymistUartState *s = MILKYMIST_UART(dev); - /* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */ - s->chr = qemu_char_get_next_serial(); if (s->chr) { qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s); } @@ -229,6 +227,11 @@ static const VMStateDescription vmstate_milkymist_uart = { } }; +static Property milkymist_uart_properties[] = { + DEFINE_PROP_CHR("chardev", MilkymistUartState, chr), + DEFINE_PROP_END_OF_LIST(), +}; + static void milkymist_uart_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -236,8 +239,7 @@ static void milkymist_uart_class_init(ObjectClass *klass, void *data) dc->realize = milkymist_uart_realize; dc->reset = milkymist_uart_reset; dc->vmsd = &vmstate_milkymist_uart; - /* Reason: realize() method uses qemu_char_get_next_serial() */ - dc->cannot_instantiate_with_device_add_yet = true; + dc->props = milkymist_uart_properties; } static const TypeInfo milkymist_uart_info = { diff --git a/hw/char/pl011.c b/hw/char/pl011.c index 210c87b4c..c0fbf8a87 100644 --- a/hw/char/pl011.c +++ b/hw/char/pl011.c @@ -10,6 +10,7 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" #include "sysemu/char.h" +#include "qemu/log.h" #define TYPE_PL011 "pl011" #define PL011(obj) OBJECT_CHECK(PL011State, (obj), TYPE_PL011) @@ -273,6 +274,11 @@ static const VMStateDescription vmstate_pl011 = { } }; +static Property pl011_properties[] = { + DEFINE_PROP_CHR("chardev", PL011State, chr), + DEFINE_PROP_END_OF_LIST(), +}; + static void pl011_init(Object *obj) { SysBusDevice *sbd = SYS_BUS_DEVICE(obj); @@ -294,9 +300,6 @@ static void pl011_realize(DeviceState *dev, Error **errp) { PL011State *s = PL011(dev); - /* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */ - s->chr = qemu_char_get_next_serial(); - if (s->chr) { qemu_chr_add_handlers(s->chr, pl011_can_receive, pl011_receive, pl011_event, s); @@ -309,8 +312,7 @@ static void pl011_class_init(ObjectClass *oc, void *data) dc->realize = pl011_realize; dc->vmsd = &vmstate_pl011; - /* Reason: realize() method uses qemu_char_get_next_serial() */ - dc->cannot_instantiate_with_device_add_yet = true; + dc->props = pl011_properties; } static const TypeInfo pl011_arm_info = { diff --git a/hw/char/sclpconsole-lm.c b/hw/char/sclpconsole-lm.c index 7d4ff8120..a22ad8d01 100644 --- a/hw/char/sclpconsole-lm.c +++ b/hw/char/sclpconsole-lm.c @@ -44,6 +44,10 @@ typedef struct SCLPConsoleLM { uint8_t buf[SIZE_CONSOLE_BUFFER]; } SCLPConsoleLM; +#define TYPE_SCLPLM_CONSOLE "sclplmconsole" +#define SCLPLM_CONSOLE(obj) \ + OBJECT_CHECK(SCLPConsoleLM, (obj), TYPE_SCLPLM_CONSOLE) + /* * Character layer call-back functions * @@ -116,7 +120,7 @@ static int get_console_data(SCLPEvent *event, uint8_t *buf, size_t *size, { int len; - SCLPConsoleLM *cons = DO_UPCAST(SCLPConsoleLM, event, event); + SCLPConsoleLM *cons = SCLPLM_CONSOLE(event); len = cons->length; /* data need to fit into provided SCLP buffer */ @@ -190,7 +194,7 @@ static int write_console_data(SCLPEvent *event, const uint8_t *buf, int len) int ret = 0; const uint8_t *buf_offset; - SCLPConsoleLM *scon = DO_UPCAST(SCLPConsoleLM, event, event); + SCLPConsoleLM *scon = SCLPLM_CONSOLE(event); if (!scon->chr) { /* If there's no backend, we can just say we consumed all data. */ @@ -244,7 +248,7 @@ static int write_event_data(SCLPEvent *event, EventBufferHeader *ebh) int errors = 0; MDBO *mdbo; SclpMsg *data = (SclpMsg *) ebh; - SCLPConsoleLM *scon = DO_UPCAST(SCLPConsoleLM, event, event); + SCLPConsoleLM *scon = SCLPLM_CONSOLE(event); len = be16_to_cpu(data->mdb.header.length); if (len < sizeof(data->mdb.header)) { @@ -313,7 +317,7 @@ static int console_init(SCLPEvent *event) { static bool console_available; - SCLPConsoleLM *scon = DO_UPCAST(SCLPConsoleLM, event, event); + SCLPConsoleLM *scon = SCLPLM_CONSOLE(event); if (console_available) { error_report("Multiple line-mode operator consoles are not supported"); @@ -336,7 +340,7 @@ static int console_exit(SCLPEvent *event) static void console_reset(DeviceState *dev) { SCLPEvent *event = SCLP_EVENT(dev); - SCLPConsoleLM *scon = DO_UPCAST(SCLPConsoleLM, event, event); + SCLPConsoleLM *scon = SCLPLM_CONSOLE(event); event->event_pending = false; scon->length = 0; diff --git a/hw/char/sclpconsole.c b/hw/char/sclpconsole.c index 45997ff4a..d22464826 100644 --- a/hw/char/sclpconsole.c +++ b/hw/char/sclpconsole.c @@ -13,7 +13,7 @@ */ #include "qemu/osdep.h" -#include <hw/qdev.h> +#include "hw/qdev.h" #include "qemu/thread.h" #include "qemu/error-report.h" @@ -40,6 +40,10 @@ typedef struct SCLPConsole { bool notify; /* qemu_notify_event() req'd if true */ } SCLPConsole; +#define TYPE_SCLP_CONSOLE "sclpconsole" +#define SCLP_CONSOLE(obj) \ + OBJECT_CHECK(SCLPConsole, (obj), TYPE_SCLP_CONSOLE) + /* character layer call-back functions */ /* Return number of bytes that fit into iov buffer */ @@ -95,7 +99,7 @@ static unsigned int receive_mask(void) static void get_console_data(SCLPEvent *event, uint8_t *buf, size_t *size, int avail) { - SCLPConsole *cons = DO_UPCAST(SCLPConsole, event, event); + SCLPConsole *cons = SCLP_CONSOLE(event); /* first byte is hex 0 saying an ascii string follows */ *buf++ = '\0'; @@ -157,7 +161,7 @@ static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr, static ssize_t write_console_data(SCLPEvent *event, const uint8_t *buf, size_t len) { - SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event); + SCLPConsole *scon = SCLP_CONSOLE(event); if (!scon->chr) { /* If there's no backend, we can just say we consumed all data. */ @@ -214,7 +218,7 @@ static int console_init(SCLPEvent *event) { static bool console_available; - SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event); + SCLPConsole *scon = SCLP_CONSOLE(event); if (console_available) { error_report("Multiple VT220 operator consoles are not supported"); @@ -232,7 +236,7 @@ static int console_init(SCLPEvent *event) static void console_reset(DeviceState *dev) { SCLPEvent *event = SCLP_EVENT(dev); - SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event); + SCLPConsole *scon = SCLP_CONSOLE(event); event->event_pending = false; scon->iov_sclp = 0; diff --git a/hw/char/serial.c b/hw/char/serial.c index 6d815b5c6..3442f47d3 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/char/stm32f2xx_usart.c b/hw/char/stm32f2xx_usart.c index a94d61ceb..15657abda 100644 --- a/hw/char/stm32f2xx_usart.c +++ b/hw/char/stm32f2xx_usart.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" #include "hw/char/stm32f2xx_usart.h" +#include "qemu/log.h" #ifndef STM_USART_ERR_DEBUG #define STM_USART_ERR_DEBUG 0 @@ -189,6 +190,11 @@ static const MemoryRegionOps stm32f2xx_usart_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; +static Property stm32f2xx_usart_properties[] = { + DEFINE_PROP_CHR("chardev", STM32F2XXUsartState, chr), + DEFINE_PROP_END_OF_LIST(), +}; + static void stm32f2xx_usart_init(Object *obj) { STM32F2XXUsartState *s = STM32F2XX_USART(obj); @@ -198,9 +204,11 @@ static void stm32f2xx_usart_init(Object *obj) memory_region_init_io(&s->mmio, obj, &stm32f2xx_usart_ops, s, TYPE_STM32F2XX_USART, 0x2000); sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); +} - /* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */ - s->chr = qemu_char_get_next_serial(); +static void stm32f2xx_usart_realize(DeviceState *dev, Error **errp) +{ + STM32F2XXUsartState *s = STM32F2XX_USART(dev); if (s->chr) { qemu_chr_add_handlers(s->chr, stm32f2xx_usart_can_receive, @@ -213,8 +221,8 @@ static void stm32f2xx_usart_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->reset = stm32f2xx_usart_reset; - /* Reason: instance_init() method uses qemu_char_get_next_serial() */ - dc->cannot_instantiate_with_device_add_yet = true; + dc->props = stm32f2xx_usart_properties; + dc->realize = stm32f2xx_usart_realize; } static const TypeInfo stm32f2xx_usart_info = { diff --git a/hw/char/trace-events b/hw/char/trace-events new file mode 100644 index 000000000..d53577c99 --- /dev/null +++ b/hw/char/trace-events @@ -0,0 +1,49 @@ +# See docs/tracing.txt for syntax documentation. + +# hw/char/virtio-serial-bus.c +virtio_serial_send_control_event(unsigned int port, uint16_t event, uint16_t value) "port %u, event %u, value %u" +virtio_serial_throttle_port(unsigned int port, bool throttle) "port %u, throttle %d" +virtio_serial_handle_control_message(uint16_t event, uint16_t value) "event %u, value %u" +virtio_serial_handle_control_message_port(unsigned int port) "port %u" + +# hw/char/virtio-console.c +virtio_console_flush_buf(unsigned int port, size_t len, ssize_t ret) "port %u, in_len %zu, out_len %zd" +virtio_console_chr_read(unsigned int port, int size) "port %u, size %d" +virtio_console_chr_event(unsigned int port, int event) "port %u, event %d" + +# hw/char/grlib_apbuart.c +grlib_apbuart_event(int event) "event:%d" +grlib_apbuart_writel_unknown(uint64_t addr, uint32_t value) "addr 0x%"PRIx64" value 0x%x" +grlib_apbuart_readl_unknown(uint64_t addr) "addr 0x%"PRIx64 + +# hw/char/lm32_juart.c +lm32_juart_get_jtx(uint32_t value) "jtx 0x%08x" +lm32_juart_set_jtx(uint32_t value) "jtx 0x%08x" +lm32_juart_get_jrx(uint32_t value) "jrx 0x%08x" +lm32_juart_set_jrx(uint32_t value) "jrx 0x%08x" + +# hw/char/lm32_uart.c +lm32_uart_memory_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" +lm32_uart_memory_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" +lm32_uart_irq_state(int level) "irq state %d" + +# hw/char/milkymist-uart.c +milkymist_uart_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x" +milkymist_uart_memory_write(uint32_t addr, uint32_t value) "addr %08x value %08x" +milkymist_uart_raise_irq(void) "Raise IRQ" +milkymist_uart_lower_irq(void) "Lower IRQ" + +# hw/char/escc.c +escc_put_queue(char channel, int b) "channel %c put: 0x%02x" +escc_get_queue(char channel, int val) "channel %c get 0x%02x" +escc_update_irq(int irq) "IRQ = %d" +escc_update_parameters(char channel, int speed, int parity, int data_bits, int stop_bits) "channel %c: speed=%d parity=%c data=%d stop=%d" +escc_mem_writeb_ctrl(char channel, uint32_t reg, uint32_t val) "Write channel %c, reg[%d] = %2.2x" +escc_mem_writeb_data(char channel, uint32_t val) "Write channel %c, ch %d" +escc_mem_readb_ctrl(char channel, uint32_t reg, uint8_t val) "Read channel %c, reg[%d] = %2.2x" +escc_mem_readb_data(char channel, uint32_t ret) "Read channel %c, ch %d" +escc_serial_receive_byte(char channel, int ch) "channel %c put ch %d" +escc_sunkbd_event_in(int ch, const char *name, int down) "QKeyCode 0x%2.2x [%s], down %d" +escc_sunkbd_event_out(int ch) "Translated keycode 0x%2.2x" +escc_kbd_command(int val) "Command %d" +escc_sunmouse_event(int dx, int dy, int buttons_state) "dx=%d dy=%d buttons=%01x" diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c index 2e36481a7..4f0e03d3b 100644 --- a/hw/char/virtio-console.c +++ b/hw/char/virtio-console.c @@ -85,8 +85,9 @@ static void set_guest_connected(VirtIOSerialPort *port, int guest_connected) { VirtConsole *vcon = VIRTIO_CONSOLE(port); DeviceState *dev = DEVICE(port); + VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port); - if (vcon->chr) { + if (vcon->chr && !k->is_console) { qemu_chr_fe_set_open(vcon->chr, guest_connected); } @@ -156,9 +157,25 @@ static void virtconsole_realize(DeviceState *dev, Error **errp) } if (vcon->chr) { - vcon->chr->explicit_fe_open = 1; - qemu_chr_add_handlers(vcon->chr, chr_can_read, chr_read, chr_event, - vcon); + /* + * For consoles we don't block guest data transfer just + * because nothing is connected - we'll just let it go + * whetherever the chardev wants - /dev/null probably. + * + * For serial ports we need 100% reliable data transfer + * so we use the opened/closed signals from chardev to + * trigger open/close of the device + */ + if (k->is_console) { + vcon->chr->explicit_fe_open = 0; + qemu_chr_add_handlers(vcon->chr, chr_can_read, chr_read, + NULL, vcon); + virtio_serial_open(port); + } else { + vcon->chr->explicit_fe_open = 1; + qemu_chr_add_handlers(vcon->chr, chr_can_read, chr_read, + chr_event, vcon); + } } } diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index 6e5de6dec..db57a3854 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -594,12 +594,6 @@ static void vser_reset(VirtIODevice *vdev) guest_reset(vser); } -static void virtio_serial_save(QEMUFile *f, void *opaque) -{ - /* The virtio device */ - virtio_save(VIRTIO_DEVICE(opaque), f); -} - static void virtio_serial_save_device(VirtIODevice *vdev, QEMUFile *f) { VirtIOSerial *s = VIRTIO_SERIAL(vdev); @@ -685,7 +679,7 @@ static void virtio_serial_post_load_timer_cb(void *opaque) s->post_load = NULL; } -static int fetch_active_ports_list(QEMUFile *f, int version_id, +static int fetch_active_ports_list(QEMUFile *f, VirtIOSerial *s, uint32_t nr_active_ports) { uint32_t i; @@ -702,6 +696,7 @@ static int fetch_active_ports_list(QEMUFile *f, int version_id, /* Items in struct VirtIOSerialPort */ for (i = 0; i < nr_active_ports; i++) { VirtIOSerialPort *port; + uint32_t elem_popped; uint32_t id; id = qemu_get_be32(f); @@ -714,37 +709,29 @@ static int fetch_active_ports_list(QEMUFile *f, int version_id, s->post_load->connected[i].port = port; s->post_load->connected[i].host_connected = qemu_get_byte(f); - if (version_id > 2) { - uint32_t elem_popped; - - qemu_get_be32s(f, &elem_popped); - if (elem_popped) { - qemu_get_be32s(f, &port->iov_idx); - qemu_get_be64s(f, &port->iov_offset); + qemu_get_be32s(f, &elem_popped); + if (elem_popped) { + qemu_get_be32s(f, &port->iov_idx); + qemu_get_be64s(f, &port->iov_offset); - port->elem = - qemu_get_virtqueue_element(f, sizeof(VirtQueueElement)); + port->elem = + qemu_get_virtqueue_element(f, sizeof(VirtQueueElement)); - /* - * Port was throttled on source machine. Let's - * unthrottle it here so data starts flowing again. - */ - virtio_serial_throttle_port(port, false); - } + /* + * Port was throttled on source machine. Let's + * unthrottle it here so data starts flowing again. + */ + virtio_serial_throttle_port(port, false); } } timer_mod(s->post_load->timer, 1); return 0; } -static int virtio_serial_load(QEMUFile *f, void *opaque, int version_id) +static int virtio_serial_load(QEMUFile *f, void *opaque, size_t size) { - if (version_id > 3) { - return -EINVAL; - } - /* The virtio device */ - return virtio_load(VIRTIO_DEVICE(opaque), f, version_id); + return virtio_load(VIRTIO_DEVICE(opaque), f, 3); } static int virtio_serial_load_device(VirtIODevice *vdev, QEMUFile *f, @@ -756,10 +743,6 @@ static int virtio_serial_load_device(VirtIODevice *vdev, QEMUFile *f, int ret; uint32_t tmp; - if (version_id < 2) { - return 0; - } - /* Unused */ qemu_get_be16s(f, (uint16_t *) &tmp); qemu_get_be16s(f, (uint16_t *) &tmp); @@ -781,7 +764,7 @@ static int virtio_serial_load_device(VirtIODevice *vdev, QEMUFile *f, qemu_get_be32s(f, &nr_active_ports); if (nr_active_ports) { - ret = fetch_active_ports_list(f, version_id, s, nr_active_ports); + ret = fetch_active_ports_list(f, s, nr_active_ports); if (ret) { return ret; } @@ -1049,13 +1032,6 @@ static void virtio_serial_device_realize(DeviceState *dev, Error **errp) vser->post_load = NULL; - /* - * Register for the savevm section with the virtio-console name - * to preserve backward compat - */ - register_savevm(dev, "virtio-console", -1, 3, virtio_serial_save, - virtio_serial_load, vser); - QLIST_INSERT_HEAD(&vserdevices.devices, vser, next); } @@ -1086,8 +1062,6 @@ static void virtio_serial_device_unrealize(DeviceState *dev, Error **errp) QLIST_REMOVE(vser, next); - unregister_savevm(dev, "virtio-console", vser); - g_free(vser->ivqs); g_free(vser->ovqs); g_free(vser->ports_map); @@ -1100,6 +1074,9 @@ static void virtio_serial_device_unrealize(DeviceState *dev, Error **errp) virtio_cleanup(vdev); } +/* Note: 'console' is used for backwards compatibility */ +VMSTATE_VIRTIO_DEVICE(console, 3, virtio_serial_load, virtio_vmstate_save); + static Property virtio_serial_properties[] = { DEFINE_PROP_UINT32("max_ports", VirtIOSerial, serial.max_virtserial_ports, 31), @@ -1115,6 +1092,7 @@ static void virtio_serial_class_init(ObjectClass *klass, void *data) QLIST_INIT(&vserdevices.devices); dc->props = virtio_serial_properties; + dc->vmsd = &vmstate_virtio_console; set_bit(DEVICE_CATEGORY_INPUT, dc->categories); vdc->realize = virtio_serial_device_realize; vdc->unrealize = virtio_serial_device_unrealize; diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c index cbf1dccbb..83108b0bd 100644 --- a/hw/char/xen_console.c +++ b/hw/char/xen_console.c @@ -22,7 +22,6 @@ #include "qemu/osdep.h" #include <sys/select.h> #include <termios.h> -#include <sys/mman.h> #include "hw/hw.h" #include "sysemu/char.h" diff --git a/hw/char/xilinx_uartlite.c b/hw/char/xilinx_uartlite.c index 911af4a0d..4847efb29 100644 --- a/hw/char/xilinx_uartlite.c +++ b/hw/char/xilinx_uartlite.c @@ -172,6 +172,11 @@ static const MemoryRegionOps uart_ops = { } }; +static Property xilinx_uartlite_properties[] = { + DEFINE_PROP_CHR("chardev", XilinxUARTLite, chr), + DEFINE_PROP_END_OF_LIST(), +}; + static void uart_rx(void *opaque, const uint8_t *buf, int size) { XilinxUARTLite *s = opaque; @@ -206,8 +211,6 @@ static void xilinx_uartlite_realize(DeviceState *dev, Error **errp) { XilinxUARTLite *s = XILINX_UARTLITE(dev); - /* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */ - s->chr = qemu_char_get_next_serial(); if (s->chr) qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s); } @@ -229,8 +232,7 @@ static void xilinx_uartlite_class_init(ObjectClass *klass, void *data) dc->reset = xilinx_uartlite_reset; dc->realize = xilinx_uartlite_realize; - /* Reason: realize() method uses qemu_char_get_next_serial() */ - dc->cannot_instantiate_with_device_add_yet = true; + dc->props = xilinx_uartlite_properties; } static const TypeInfo xilinx_uartlite_info = { diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs index abb3560be..cfd484039 100644 --- a/hw/core/Makefile.objs +++ b/hw/core/Makefile.objs @@ -1,10 +1,11 @@ # core qdev-related obj files, also used by *-user: common-obj-y += qdev.o qdev-properties.o +common-obj-y += bus.o common-obj-y += fw-path-provider.o # irq.o needed for qdev GPIO handling: common-obj-y += irq.o common-obj-y += hotplug.o -common-obj-y += nmi.o +obj-y += nmi.o common-obj-$(CONFIG_EMPTY_SLOT) += empty_slot.o common-obj-$(CONFIG_XILINX_AXI) += stream.o @@ -14,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/bus.c b/hw/core/bus.c new file mode 100644 index 000000000..3e3f8ac74 --- /dev/null +++ b/hw/core/bus.c @@ -0,0 +1,251 @@ +/* + * Dynamic device configuration and creation -- buses. + * + * Copyright (c) 2009 CodeSourcery + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "hw/qdev.h" +#include "qapi/error.h" + +static void qbus_set_hotplug_handler_internal(BusState *bus, Object *handler, + Error **errp) +{ + + object_property_set_link(OBJECT(bus), OBJECT(handler), + QDEV_HOTPLUG_HANDLER_PROPERTY, errp); +} + +void qbus_set_hotplug_handler(BusState *bus, DeviceState *handler, Error **errp) +{ + qbus_set_hotplug_handler_internal(bus, OBJECT(handler), errp); +} + +void qbus_set_bus_hotplug_handler(BusState *bus, Error **errp) +{ + qbus_set_hotplug_handler_internal(bus, OBJECT(bus), errp); +} + +int qbus_walk_children(BusState *bus, + qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn, + qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn, + void *opaque) +{ + BusChild *kid; + int err; + + if (pre_busfn) { + err = pre_busfn(bus, opaque); + if (err) { + return err; + } + } + + QTAILQ_FOREACH(kid, &bus->children, sibling) { + err = qdev_walk_children(kid->child, + pre_devfn, pre_busfn, + post_devfn, post_busfn, opaque); + if (err < 0) { + return err; + } + } + + if (post_busfn) { + err = post_busfn(bus, opaque); + if (err) { + return err; + } + } + + return 0; +} + +static void qbus_realize(BusState *bus, DeviceState *parent, const char *name) +{ + const char *typename = object_get_typename(OBJECT(bus)); + BusClass *bc; + char *buf; + int i, len, bus_id; + + bus->parent = parent; + + if (name) { + bus->name = g_strdup(name); + } else if (bus->parent && bus->parent->id) { + /* parent device has id -> use it plus parent-bus-id for bus name */ + bus_id = bus->parent->num_child_bus; + + len = strlen(bus->parent->id) + 16; + buf = g_malloc(len); + snprintf(buf, len, "%s.%d", bus->parent->id, bus_id); + bus->name = buf; + } else { + /* no id -> use lowercase bus type plus global bus-id for bus name */ + bc = BUS_GET_CLASS(bus); + bus_id = bc->automatic_ids++; + + len = strlen(typename) + 16; + buf = g_malloc(len); + len = snprintf(buf, len, "%s.%d", typename, bus_id); + for (i = 0; i < len; i++) { + buf[i] = qemu_tolower(buf[i]); + } + bus->name = buf; + } + + if (bus->parent) { + QLIST_INSERT_HEAD(&bus->parent->child_bus, bus, sibling); + bus->parent->num_child_bus++; + object_property_add_child(OBJECT(bus->parent), bus->name, OBJECT(bus), NULL); + object_unref(OBJECT(bus)); + } else if (bus != sysbus_get_default()) { + /* TODO: once all bus devices are qdevified, + only reset handler for main_system_bus should be registered here. */ + qemu_register_reset(qbus_reset_all_fn, bus); + } +} + +static void bus_unparent(Object *obj) +{ + BusState *bus = BUS(obj); + BusChild *kid; + + while ((kid = QTAILQ_FIRST(&bus->children)) != NULL) { + DeviceState *dev = kid->child; + object_unparent(OBJECT(dev)); + } + if (bus->parent) { + QLIST_REMOVE(bus, sibling); + bus->parent->num_child_bus--; + bus->parent = NULL; + } else { + assert(bus != sysbus_get_default()); /* main_system_bus is never freed */ + qemu_unregister_reset(qbus_reset_all_fn, bus); + } +} + +void qbus_create_inplace(void *bus, size_t size, const char *typename, + DeviceState *parent, const char *name) +{ + object_initialize(bus, size, typename); + qbus_realize(bus, parent, name); +} + +BusState *qbus_create(const char *typename, DeviceState *parent, const char *name) +{ + BusState *bus; + + bus = BUS(object_new(typename)); + qbus_realize(bus, parent, name); + + return bus; +} + +static bool bus_get_realized(Object *obj, Error **errp) +{ + BusState *bus = BUS(obj); + + return bus->realized; +} + +static void bus_set_realized(Object *obj, bool value, Error **errp) +{ + BusState *bus = BUS(obj); + BusClass *bc = BUS_GET_CLASS(bus); + BusChild *kid; + Error *local_err = NULL; + + if (value && !bus->realized) { + if (bc->realize) { + bc->realize(bus, &local_err); + } + + /* TODO: recursive realization */ + } else if (!value && bus->realized) { + QTAILQ_FOREACH(kid, &bus->children, sibling) { + DeviceState *dev = kid->child; + object_property_set_bool(OBJECT(dev), false, "realized", + &local_err); + if (local_err != NULL) { + break; + } + } + if (bc->unrealize && local_err == NULL) { + bc->unrealize(bus, &local_err); + } + } + + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } + + bus->realized = value; +} + +static void qbus_initfn(Object *obj) +{ + BusState *bus = BUS(obj); + + QTAILQ_INIT(&bus->children); + object_property_add_link(obj, QDEV_HOTPLUG_HANDLER_PROPERTY, + TYPE_HOTPLUG_HANDLER, + (Object **)&bus->hotplug_handler, + object_property_allow_set_link, + OBJ_PROP_LINK_UNREF_ON_RELEASE, + NULL); + object_property_add_bool(obj, "realized", + bus_get_realized, bus_set_realized, NULL); +} + +static char *default_bus_get_fw_dev_path(DeviceState *dev) +{ + return g_strdup(object_get_typename(OBJECT(dev))); +} + +static void bus_class_init(ObjectClass *class, void *data) +{ + BusClass *bc = BUS_CLASS(class); + + class->unparent = bus_unparent; + bc->get_fw_dev_path = default_bus_get_fw_dev_path; +} + +static void qbus_finalize(Object *obj) +{ + BusState *bus = BUS(obj); + + g_free((char *)bus->name); +} + +static const TypeInfo bus_info = { + .name = TYPE_BUS, + .parent = TYPE_OBJECT, + .instance_size = sizeof(BusState), + .abstract = true, + .class_size = sizeof(BusClass), + .instance_init = qbus_initfn, + .instance_finalize = qbus_finalize, + .class_init = bus_class_init, +}; + +static void bus_register_types(void) +{ + type_register_static(&bus_info); +} + +type_init(bus_register_types) diff --git a/hw/core/hotplug.c b/hw/core/hotplug.c index 645cfca1b..17ac98668 100644 --- a/hw/core/hotplug.c +++ b/hw/core/hotplug.c @@ -13,6 +13,17 @@ #include "hw/hotplug.h" #include "qemu/module.h" +void hotplug_handler_pre_plug(HotplugHandler *plug_handler, + DeviceState *plugged_dev, + Error **errp) +{ + HotplugHandlerClass *hdc = HOTPLUG_HANDLER_GET_CLASS(plug_handler); + + if (hdc->pre_plug) { + hdc->pre_plug(plug_handler, plugged_dev, errp); + } +} + void hotplug_handler_plug(HotplugHandler *plug_handler, DeviceState *plugged_dev, Error **errp) diff --git a/hw/core/loader.c b/hw/core/loader.c index c0499571c..53e0e4155 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -914,10 +914,16 @@ int rom_add_file(const char *file, const char *fw_dir, err: if (fd != -1) close(fd); + g_free(rom->data); g_free(rom->path); g_free(rom->name); + if (fw_dir) { + g_free(rom->fw_dir); + g_free(rom->fw_file); + } g_free(rom); + return -1; } diff --git a/hw/core/machine.c b/hw/core/machine.c index 6dbbc85b9..e5a456f21 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -65,6 +65,9 @@ static void machine_set_kernel_irqchip(Object *obj, Visitor *v, ms->kernel_irqchip_split = true; break; default: + /* The value was checked in visit_type_OnOffSplit() above. If + * we get here, then something is wrong in QEMU. + */ abort(); } } @@ -257,47 +260,47 @@ static void machine_set_usb(Object *obj, bool value, Error **errp) ms->usb_disabled = !value; } -static bool machine_get_igd_gfx_passthru(Object *obj, Error **errp) +static bool machine_get_graphics(Object *obj, Error **errp) { MachineState *ms = MACHINE(obj); - return ms->igd_gfx_passthru; + return ms->enable_graphics; } -static void machine_set_igd_gfx_passthru(Object *obj, bool value, Error **errp) +static void machine_set_graphics(Object *obj, bool value, Error **errp) { MachineState *ms = MACHINE(obj); - ms->igd_gfx_passthru = value; + ms->enable_graphics = value; } -static char *machine_get_firmware(Object *obj, Error **errp) +static bool machine_get_igd_gfx_passthru(Object *obj, Error **errp) { MachineState *ms = MACHINE(obj); - return g_strdup(ms->firmware); + return ms->igd_gfx_passthru; } -static void machine_set_firmware(Object *obj, const char *value, Error **errp) +static void machine_set_igd_gfx_passthru(Object *obj, bool value, Error **errp) { MachineState *ms = MACHINE(obj); - g_free(ms->firmware); - ms->firmware = g_strdup(value); + ms->igd_gfx_passthru = value; } -static bool machine_get_iommu(Object *obj, Error **errp) +static char *machine_get_firmware(Object *obj, Error **errp) { MachineState *ms = MACHINE(obj); - return ms->iommu; + return g_strdup(ms->firmware); } -static void machine_set_iommu(Object *obj, bool value, Error **errp) +static void machine_set_firmware(Object *obj, const char *value, Error **errp) { MachineState *ms = MACHINE(obj); - ms->iommu = value; + g_free(ms->firmware); + ms->firmware = g_strdup(value); } static void machine_set_suppress_vmdesc(Object *obj, bool value, Error **errp) @@ -382,6 +385,7 @@ static void machine_initfn(Object *obj) ms->kvm_shadow_mem = -1; ms->dump_guest_core = true; ms->mem_merge = true; + ms->enable_graphics = true; object_property_add_str(obj, "accel", machine_get_accel, machine_set_accel, NULL); @@ -460,6 +464,12 @@ static void machine_initfn(Object *obj) object_property_set_description(obj, "usb", "Set on/off to enable/disable usb", NULL); + object_property_add_bool(obj, "graphics", + machine_get_graphics, + machine_set_graphics, NULL); + object_property_set_description(obj, "graphics", + "Set on/off to enable/disable graphics emulation", + NULL); object_property_add_bool(obj, "igd-passthru", machine_get_igd_gfx_passthru, machine_set_igd_gfx_passthru, NULL); @@ -472,12 +482,6 @@ static void machine_initfn(Object *obj) object_property_set_description(obj, "firmware", "Firmware image", NULL); - object_property_add_bool(obj, "iommu", - machine_get_iommu, - machine_set_iommu, NULL); - object_property_set_description(obj, "iommu", - "Set on/off to enable/disable Intel IOMMU (VT-d)", - NULL); object_property_add_bool(obj, "suppress-vmdesc", machine_get_suppress_vmdesc, machine_set_suppress_vmdesc, NULL); @@ -550,6 +554,33 @@ bool machine_mem_merge(MachineState *machine) return machine->mem_merge; } +static void machine_class_finalize(ObjectClass *klass, void *data) +{ + MachineClass *mc = MACHINE_CLASS(klass); + + if (mc->compat_props) { + g_array_free(mc->compat_props, true); + } +} + +void machine_register_compat_props(MachineState *machine) +{ + MachineClass *mc = MACHINE_GET_CLASS(machine); + int i; + GlobalProperty *p; + + if (!mc->compat_props) { + return; + } + + for (i = 0; i < mc->compat_props->len; i++) { + p = g_array_index(mc->compat_props, GlobalProperty *, i); + /* Machine compat_props must never cause errors: */ + p->errp = &error_abort; + qdev_prop_register_global(p); + } +} + static const TypeInfo machine_info = { .name = TYPE_MACHINE, .parent = TYPE_OBJECT, @@ -557,6 +588,7 @@ static const TypeInfo machine_info = { .class_size = sizeof(MachineClass), .class_init = machine_class_init, .class_base_init = machine_class_base_init, + .class_finalize = machine_class_finalize, .instance_size = sizeof(MachineState), .instance_init = machine_initfn, .instance_finalize = machine_finalize, diff --git a/hw/core/nmi.c b/hw/core/nmi.c index e8bcc4177..bfd0896da 100644 --- a/hw/core/nmi.c +++ b/hw/core/nmi.c @@ -73,25 +73,6 @@ void nmi_monitor_handle(int cpu_index, Error **errp) } } -void inject_nmi(void) -{ -#if defined(TARGET_I386) - CPUState *cs; - - CPU_FOREACH(cs) { - X86CPU *cpu = X86_CPU(cs); - - if (!cpu->apic_state) { - cpu_interrupt(cs, CPU_INTERRUPT_NMI); - } else { - apic_deliver_nmi(cpu->apic_state); - } - } -#else - nmi_monitor_handle(0, NULL); -#endif -} - static const TypeInfo nmi_info = { .name = TYPE_NMI, .parent = TYPE_INTERFACE, diff --git a/hw/core/ptimer.c b/hw/core/ptimer.c index 153c83513..30829ee97 100644 --- a/hw/core/ptimer.c +++ b/hw/core/ptimer.c @@ -35,6 +35,9 @@ static void ptimer_trigger(ptimer_state *s) static void ptimer_reload(ptimer_state *s) { + uint32_t period_frac = s->period_frac; + uint64_t period = s->period; + if (s->delta == 0) { ptimer_trigger(s); s->delta = s->limit; @@ -45,10 +48,24 @@ static void ptimer_reload(ptimer_state *s) return; } + /* + * Artificially limit timeout rate to something + * achievable under QEMU. Otherwise, QEMU spends all + * its time generating timer interrupts, and there + * is no forward progress. + * About ten microseconds is the fastest that really works + * on the current generation of host machines. + */ + + if (s->enabled == 1 && (s->delta * period < 10000) && !use_icount) { + period = 10000 / s->delta; + period_frac = 0; + } + s->last_event = s->next_event; - s->next_event = s->last_event + s->delta * s->period; - if (s->period_frac) { - s->next_event += ((int64_t)s->period_frac * s->delta) >> 32; + s->next_event = s->last_event + s->delta * period; + if (period_frac) { + s->next_event += ((int64_t)period_frac * s->delta) >> 32; } timer_mod(s->timer, s->next_event); } @@ -67,14 +84,16 @@ static void ptimer_tick(void *opaque) uint64_t ptimer_get_count(ptimer_state *s) { - int64_t now; uint64_t counter; if (s->enabled) { - now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + int64_t next = s->next_event; + bool expired = (now - next >= 0); + bool oneshot = (s->enabled == 2); + /* Figure out the current counter value. */ - if (now - s->next_event > 0 - || s->period == 0) { + if (expired) { /* Prevent timer underflowing if it should already have triggered. */ counter = 0; @@ -83,6 +102,13 @@ uint64_t ptimer_get_count(ptimer_state *s) uint64_t div; int clz1, clz2; int shift; + uint32_t period_frac = s->period_frac; + uint64_t period = s->period; + + if (!oneshot && (s->delta * period < 10000) && !use_icount) { + period = 10000 / s->delta; + period_frac = 0; + } /* We need to divide time by period, where time is stored in rem (64-bit integer) and period is stored in period/period_frac @@ -94,8 +120,8 @@ uint64_t ptimer_get_count(ptimer_state *s) backwards. */ - rem = s->next_event - now; - div = s->period; + rem = next - now; + div = period; clz1 = clz64(rem); clz2 = clz64(div); @@ -104,13 +130,13 @@ uint64_t ptimer_get_count(ptimer_state *s) rem <<= shift; div <<= shift; if (shift >= 32) { - div |= ((uint64_t)s->period_frac << (shift - 32)); + div |= ((uint64_t)period_frac << (shift - 32)); } else { if (shift != 0) - div |= (s->period_frac >> (32 - shift)); + div |= (period_frac >> (32 - shift)); /* Look at remaining bits of period_frac and round div up if necessary. */ - if ((uint32_t)(s->period_frac << shift)) + if ((uint32_t)(period_frac << shift)) div += 1; } counter = rem / div; @@ -132,16 +158,17 @@ void ptimer_set_count(ptimer_state *s, uint64_t count) void ptimer_run(ptimer_state *s, int oneshot) { - if (s->enabled) { - return; - } - if (s->period == 0) { + bool was_disabled = !s->enabled; + + if (was_disabled && s->period == 0) { fprintf(stderr, "Timer with period zero, disabling\n"); return; } s->enabled = oneshot ? 2 : 1; - s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - ptimer_reload(s); + if (was_disabled) { + s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + ptimer_reload(s); + } } /* Pause a timer. Note that this may cause it to "lose" time, even if it @@ -159,6 +186,7 @@ void ptimer_stop(ptimer_state *s) /* Set counter increment interval in nanoseconds. */ void ptimer_set_period(ptimer_state *s, int64_t period) { + s->delta = ptimer_get_count(s); s->period = period; s->period_frac = 0; if (s->enabled) { @@ -170,6 +198,7 @@ void ptimer_set_period(ptimer_state *s, int64_t period) /* Set counter frequency in Hz. */ void ptimer_set_freq(ptimer_state *s, uint32_t freq) { + s->delta = ptimer_get_count(s); s->period = 1000000000ll / freq; s->period_frac = (1000000000ll << 32) / freq; if (s->enabled) { @@ -182,19 +211,6 @@ void ptimer_set_freq(ptimer_state *s, uint32_t freq) count = limit. */ void ptimer_set_limit(ptimer_state *s, uint64_t limit, int reload) { - /* - * Artificially limit timeout rate to something - * achievable under QEMU. Otherwise, QEMU spends all - * its time generating timer interrupts, and there - * is no forward progress. - * About ten microseconds is the fastest that really works - * on the current generation of host machines. - */ - - if (!use_icount && limit * s->period < 10000 && s->period) { - limit = 10000 / s->period; - } - s->limit = limit; if (reload) s->delta = limit; @@ -204,6 +220,11 @@ void ptimer_set_limit(ptimer_state *s, uint64_t limit, int reload) } } +uint64_t ptimer_get_limit(ptimer_state *s) +{ + return s->limit; +} + const VMStateDescription vmstate_ptimer = { .name = "ptimer", .version_id = 1, diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index 891219ae0..e55afe6bf 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -1,5 +1,5 @@ /* - * qdev property parsing and global properties + * qdev property parsing * (parts specific for qemu-system-*) * * This file is based on code from hw/qdev-properties.c from @@ -72,17 +72,26 @@ static void parse_drive(DeviceState *dev, const char *str, void **ptr, const char *propname, Error **errp) { BlockBackend *blk; + bool blk_created = false; blk = blk_by_name(str); if (!blk) { + BlockDriverState *bs = bdrv_lookup_bs(NULL, str, NULL); + if (bs) { + blk = blk_new(); + blk_insert_bs(blk, bs); + blk_created = true; + } + } + if (!blk) { error_setg(errp, "Property '%s.%s' can't find value '%s'", object_get_typename(OBJECT(dev)), propname, str); - return; + goto fail; } if (blk_attach_dev(blk, dev) < 0) { DriveInfo *dinfo = blk_legacy_dinfo(blk); - if (dinfo->type != IF_NONE) { + if (dinfo && dinfo->type != IF_NONE) { error_setg(errp, "Drive '%s' is already in use because " "it has been automatically connected to another " "device (did you need 'if=none' in the drive options?)", @@ -91,9 +100,16 @@ static void parse_drive(DeviceState *dev, const char *str, void **ptr, error_setg(errp, "Drive '%s' is already in use by another device", str); } - return; + goto fail; } + *ptr = blk; + +fail: + if (blk_created) { + /* If we need to keep a reference, blk_attach_dev() took it */ + blk_unref(blk); + } } static void release_drive(Object *obj, const char *name, void *opaque) @@ -103,14 +119,23 @@ static void release_drive(Object *obj, const char *name, void *opaque) BlockBackend **ptr = qdev_get_prop_ptr(dev, prop); if (*ptr) { - blk_detach_dev(*ptr, dev); blockdev_auto_del(*ptr); + blk_detach_dev(*ptr, dev); } } static char *print_drive(void *ptr) { - return g_strdup(blk_name(ptr)); + const char *name; + + name = blk_name(ptr); + if (!*name) { + BlockDriverState *bs = blk_bs(ptr); + if (bs) { + name = bdrv_get_node_name(bs); + } + } + return g_strdup(name); } static void get_drive(Object *obj, Visitor *v, const char *name, void *opaque, @@ -127,7 +152,7 @@ static void set_drive(Object *obj, Visitor *v, const char *name, void *opaque, PropertyInfo qdev_prop_drive = { .name = "str", - .description = "ID of a drive to use as a backend", + .description = "Node name or ID of a block device to use as a backend", .get = get_drive, .set = set_drive, .release = release_drive, @@ -231,7 +256,7 @@ static void set_netdev(Object *obj, Visitor *v, const char *name, } queues = qemu_find_net_clients_except(str, peers, - NET_CLIENT_OPTIONS_KIND_NIC, + NET_CLIENT_DRIVER_NIC, MAX_QUEUE_NUM); if (queues == 0) { err = -ENOENT; @@ -362,8 +387,19 @@ PropertyInfo qdev_prop_vlan = { void qdev_prop_set_drive(DeviceState *dev, const char *name, BlockBackend *value, Error **errp) { - object_property_set_str(OBJECT(dev), value ? blk_name(value) : "", - name, errp); + const char *ref = ""; + + if (value) { + ref = blk_name(value); + if (!*ref) { + BlockDriverState *bs = blk_bs(value); + if (bs) { + ref = bdrv_get_node_name(bs); + } + } + } + + object_property_set_str(OBJECT(dev), ref, name, errp); } void qdev_prop_set_chr(DeviceState *dev, const char *name, @@ -394,22 +430,3 @@ void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd) } nd->instantiated = 1; } - -static int qdev_add_one_global(void *opaque, QemuOpts *opts, Error **errp) -{ - GlobalProperty *g; - - g = g_malloc0(sizeof(*g)); - g->driver = qemu_opt_get(opts, "driver"); - g->property = qemu_opt_get(opts, "property"); - g->value = qemu_opt_get(opts, "value"); - g->user_provided = true; - qdev_prop_register_global(g); - return 0; -} - -void qemu_add_globals(void) -{ - qemu_opts_foreach(qemu_find_opts("global"), - qdev_add_one_global, NULL, NULL); -} diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 737d29c63..311af6da7 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -539,6 +539,19 @@ PropertyInfo qdev_prop_losttickpolicy = { .set = set_enum, }; +/* --- Block device error handling policy --- */ + +QEMU_BUILD_BUG_ON(sizeof(BlockdevOnError) != sizeof(int)); + +PropertyInfo qdev_prop_blockdev_on_error = { + .name = "BlockdevOnError", + .description = "Error handling policy, " + "report/ignore/enospc/stop/auto", + .enum_table = BlockdevOnError_lookup, + .get = get_enum, + .set = set_enum, +}; + /* --- BIOS CHS translation */ QEMU_BUILD_BUG_ON(sizeof(BiosAtaTranslation) != sizeof(int)); @@ -1020,12 +1033,11 @@ void qdev_prop_set_ptr(DeviceState *dev, const char *name, void *value) *ptr = value; } -static QTAILQ_HEAD(, GlobalProperty) global_props = - QTAILQ_HEAD_INITIALIZER(global_props); +static GList *global_props; void qdev_prop_register_global(GlobalProperty *prop) { - QTAILQ_INSERT_TAIL(&global_props, prop, next); + global_props = g_list_append(global_props, prop); } void qdev_prop_register_global_list(GlobalProperty *props) @@ -1039,10 +1051,11 @@ void qdev_prop_register_global_list(GlobalProperty *props) int qdev_prop_check_globals(void) { - GlobalProperty *prop; + GList *l; int ret = 0; - QTAILQ_FOREACH(prop, &global_props, next) { + for (l = global_props; l; l = l->next) { + GlobalProperty *prop = l->data; ObjectClass *oc; DeviceClass *dc; if (prop->used) { @@ -1071,11 +1084,12 @@ int qdev_prop_check_globals(void) } static void qdev_prop_set_globals_for_type(DeviceState *dev, - const char *typename) + const char *typename) { - GlobalProperty *prop; + GList *l; - QTAILQ_FOREACH(prop, &global_props, next) { + for (l = global_props; l; l = l->next) { + GlobalProperty *prop = l->data; Error *err = NULL; if (strcmp(typename, prop->driver) != 0) { @@ -1084,10 +1098,14 @@ static void qdev_prop_set_globals_for_type(DeviceState *dev, prop->used = true; object_property_parse(OBJECT(dev), prop->value, prop->property, &err); if (err != NULL) { - assert(prop->user_provided); - error_reportf_err(err, "Warning: global %s.%s=%s ignored: ", - prop->driver, prop->property, prop->value); - return; + error_prepend(&err, "can't apply global %s.%s=%s: ", + prop->driver, prop->property, prop->value); + if (!dev->hotplugged && prop->errp) { + error_propagate(prop->errp, err); + } else { + assert(prop->user_provided); + error_reportf_err(err, "Warning: "); + } } } } diff --git a/hw/core/qdev.c b/hw/core/qdev.c index db41aa1f2..57834423b 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -35,6 +35,7 @@ #include "qemu/error-report.h" #include "hw/hotplug.h" #include "hw/boards.h" +#include "hw/sysbus.h" #include "qapi-event.h" int qdev_hotplug = 0; @@ -58,9 +59,6 @@ const char *qdev_fw_name(DeviceState *dev) return object_get_typename(OBJECT(dev)); } -static void qdev_property_add_legacy(DeviceState *dev, Property *prop, - Error **errp); - static void bus_remove_child(BusState *bus, DeviceState *child) { BusChild *kid; @@ -109,24 +107,6 @@ void qdev_set_parent_bus(DeviceState *dev, BusState *bus) bus_add_child(bus, dev); } -static void qbus_set_hotplug_handler_internal(BusState *bus, Object *handler, - Error **errp) -{ - - object_property_set_link(OBJECT(bus), OBJECT(handler), - QDEV_HOTPLUG_HANDLER_PROPERTY, errp); -} - -void qbus_set_hotplug_handler(BusState *bus, DeviceState *handler, Error **errp) -{ - qbus_set_hotplug_handler_internal(bus, OBJECT(handler), errp); -} - -void qbus_set_bus_hotplug_handler(BusState *bus, Error **errp) -{ - qbus_set_hotplug_handler_internal(bus, OBJECT(bus), errp); -} - /* Create a new device. This only initializes the device state structure and allows properties to be set. The device still needs to be realized. See qdev-core.h. */ @@ -161,6 +141,12 @@ DeviceState *qdev_try_create(BusState *bus, const char *type) } if (!bus) { + /* Assert that the device really is a SysBusDevice before + * we put it onto the sysbus. Non-sysbus devices which aren't + * being put onto a bus should be created with object_new(TYPE_FOO), + * not qdev_create(NULL, TYPE_FOO). + */ + g_assert(object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE)); bus = sysbus_get_default(); } @@ -368,12 +354,14 @@ void qdev_init_nofail(DeviceState *dev) assert(!dev->realized); + object_ref(OBJECT(dev)); object_property_set_bool(OBJECT(dev), true, "realized", &err); if (err) { error_reportf_err(err, "Initialization of device %s failed: ", object_get_typename(OBJECT(dev))); exit(1); } + object_unref(OBJECT(dev)); } void qdev_machine_creation_done(void) @@ -595,40 +583,6 @@ BusState *qdev_get_child_bus(DeviceState *dev, const char *name) return NULL; } -int qbus_walk_children(BusState *bus, - qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn, - qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn, - void *opaque) -{ - BusChild *kid; - int err; - - if (pre_busfn) { - err = pre_busfn(bus, opaque); - if (err) { - return err; - } - } - - QTAILQ_FOREACH(kid, &bus->children, sibling) { - err = qdev_walk_children(kid->child, - pre_devfn, pre_busfn, - post_devfn, post_busfn, opaque); - if (err < 0) { - return err; - } - } - - if (post_busfn) { - err = post_busfn(bus, opaque); - if (err) { - return err; - } - } - - return 0; -} - int qdev_walk_children(DeviceState *dev, qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn, qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn, @@ -685,129 +639,6 @@ DeviceState *qdev_find_recursive(BusState *bus, const char *id) return NULL; } -static void qbus_realize(BusState *bus, DeviceState *parent, const char *name) -{ - const char *typename = object_get_typename(OBJECT(bus)); - BusClass *bc; - char *buf; - int i, len, bus_id; - - bus->parent = parent; - - if (name) { - bus->name = g_strdup(name); - } else if (bus->parent && bus->parent->id) { - /* parent device has id -> use it plus parent-bus-id for bus name */ - bus_id = bus->parent->num_child_bus; - - len = strlen(bus->parent->id) + 16; - buf = g_malloc(len); - snprintf(buf, len, "%s.%d", bus->parent->id, bus_id); - bus->name = buf; - } else { - /* no id -> use lowercase bus type plus global bus-id for bus name */ - bc = BUS_GET_CLASS(bus); - bus_id = bc->automatic_ids++; - - len = strlen(typename) + 16; - buf = g_malloc(len); - len = snprintf(buf, len, "%s.%d", typename, bus_id); - for (i = 0; i < len; i++) { - buf[i] = qemu_tolower(buf[i]); - } - bus->name = buf; - } - - if (bus->parent) { - QLIST_INSERT_HEAD(&bus->parent->child_bus, bus, sibling); - bus->parent->num_child_bus++; - object_property_add_child(OBJECT(bus->parent), bus->name, OBJECT(bus), NULL); - object_unref(OBJECT(bus)); - } else if (bus != sysbus_get_default()) { - /* TODO: once all bus devices are qdevified, - only reset handler for main_system_bus should be registered here. */ - qemu_register_reset(qbus_reset_all_fn, bus); - } -} - -static void bus_unparent(Object *obj) -{ - BusState *bus = BUS(obj); - BusChild *kid; - - while ((kid = QTAILQ_FIRST(&bus->children)) != NULL) { - DeviceState *dev = kid->child; - object_unparent(OBJECT(dev)); - } - if (bus->parent) { - QLIST_REMOVE(bus, sibling); - bus->parent->num_child_bus--; - bus->parent = NULL; - } else { - assert(bus != sysbus_get_default()); /* main_system_bus is never freed */ - qemu_unregister_reset(qbus_reset_all_fn, bus); - } -} - -static bool bus_get_realized(Object *obj, Error **errp) -{ - BusState *bus = BUS(obj); - - return bus->realized; -} - -static void bus_set_realized(Object *obj, bool value, Error **errp) -{ - BusState *bus = BUS(obj); - BusClass *bc = BUS_GET_CLASS(bus); - BusChild *kid; - Error *local_err = NULL; - - if (value && !bus->realized) { - if (bc->realize) { - bc->realize(bus, &local_err); - } - - /* TODO: recursive realization */ - } else if (!value && bus->realized) { - QTAILQ_FOREACH(kid, &bus->children, sibling) { - DeviceState *dev = kid->child; - object_property_set_bool(OBJECT(dev), false, "realized", - &local_err); - if (local_err != NULL) { - break; - } - } - if (bc->unrealize && local_err == NULL) { - bc->unrealize(bus, &local_err); - } - } - - if (local_err != NULL) { - error_propagate(errp, local_err); - return; - } - - bus->realized = value; -} - -void qbus_create_inplace(void *bus, size_t size, const char *typename, - DeviceState *parent, const char *name) -{ - object_initialize(bus, size, typename); - qbus_realize(bus, parent, name); -} - -BusState *qbus_create(const char *typename, DeviceState *parent, const char *name) -{ - BusState *bus; - - bus = BUS(object_new(typename)); - qbus_realize(bus, parent, name); - - return bus; -} - static char *bus_get_fw_dev_path(BusState *bus, DeviceState *dev) { BusClass *bc = BUS_GET_CLASS(bus); @@ -908,13 +739,20 @@ static void qdev_get_legacy_property(Object *obj, Visitor *v, } /** - * @qdev_add_legacy_property - adds a legacy property + * qdev_property_add_legacy: + * @dev: Device to add the property to. + * @prop: The qdev property definition. + * @errp: location to store error information. * - * Do not use this is new code! Properties added through this interface will - * be given names and types in the "legacy" namespace. + * Add a legacy QOM property to @dev for qdev property @prop. + * On error, store error in @errp. * - * Legacy properties are string versions of other OOM properties. The format - * of the string depends on the property type. + * Legacy properties are string versions of QOM properties. The format of + * the string depends on the property type. Legacy properties are only + * needed for "info qtree". + * + * Do not use this is new code! QOM Properties added through this interface + * will be given names in the "legacy" namespace. */ static void qdev_property_add_legacy(DeviceState *dev, Property *prop, Error **errp) @@ -937,10 +775,14 @@ static void qdev_property_add_legacy(DeviceState *dev, Property *prop, } /** - * @qdev_property_add_static - add a @Property to a device. + * qdev_property_add_static: + * @dev: Device to add the property to. + * @prop: The qdev property definition. + * @errp: location to store error information. * - * Static properties access data in a struct. The actual type of the - * property and the field depends on the property type. + * Add a static QOM property to @dev for qdev property @prop. + * On error, store error in @errp. Static properties access data in a struct. + * The type of the QOM property is derived from prop->info. */ void qdev_property_add_static(DeviceState *dev, Property *prop, Error **errp) @@ -1045,6 +887,8 @@ static void device_set_realized(Object *obj, bool value, Error **errp) HotplugHandler *hotplug_ctrl; BusState *bus; Error *local_err = NULL; + bool unattached_parent = false; + static int unattached_count; if (dev->hotplugged && !dc->hotpluggable) { error_setg(errp, QERR_DEVICE_NO_HOTPLUG, object_get_typename(obj)); @@ -1053,15 +897,23 @@ static void device_set_realized(Object *obj, bool value, Error **errp) if (value && !dev->realized) { if (!obj->parent) { - static int unattached_count; gchar *name = g_strdup_printf("device[%d]", unattached_count++); object_property_add_child(container_get(qdev_get_machine(), "/unattached"), name, obj, &error_abort); + unattached_parent = true; g_free(name); } + hotplug_ctrl = qdev_get_hotplug_handler(dev); + if (hotplug_ctrl) { + hotplug_handler_pre_plug(hotplug_ctrl, dev, &local_err); + if (local_err != NULL) { + goto fail; + } + } + if (dc->realize) { dc->realize(dev, &local_err); } @@ -1072,7 +924,6 @@ static void device_set_realized(Object *obj, bool value, Error **errp) DEVICE_LISTENER_CALL(realize, Forward, dev); - hotplug_ctrl = qdev_get_hotplug_handler(dev); if (hotplug_ctrl) { hotplug_handler_plug(hotplug_ctrl, dev, &local_err); } @@ -1140,6 +991,10 @@ post_realize_fail: fail: error_propagate(errp, local_err); + if (unattached_parent) { + object_unparent(OBJECT(dev)); + unattached_count--; + } } static bool device_get_hotpluggable(Object *obj, Error **errp) @@ -1315,55 +1170,8 @@ static const TypeInfo device_type_info = { .class_size = sizeof(DeviceClass), }; -static void qbus_initfn(Object *obj) -{ - BusState *bus = BUS(obj); - - QTAILQ_INIT(&bus->children); - object_property_add_link(obj, QDEV_HOTPLUG_HANDLER_PROPERTY, - TYPE_HOTPLUG_HANDLER, - (Object **)&bus->hotplug_handler, - object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, - NULL); - object_property_add_bool(obj, "realized", - bus_get_realized, bus_set_realized, NULL); -} - -static char *default_bus_get_fw_dev_path(DeviceState *dev) -{ - return g_strdup(object_get_typename(OBJECT(dev))); -} - -static void bus_class_init(ObjectClass *class, void *data) -{ - BusClass *bc = BUS_CLASS(class); - - class->unparent = bus_unparent; - bc->get_fw_dev_path = default_bus_get_fw_dev_path; -} - -static void qbus_finalize(Object *obj) -{ - BusState *bus = BUS(obj); - - g_free((char *)bus->name); -} - -static const TypeInfo bus_info = { - .name = TYPE_BUS, - .parent = TYPE_OBJECT, - .instance_size = sizeof(BusState), - .abstract = true, - .class_size = sizeof(BusClass), - .instance_init = qbus_initfn, - .instance_finalize = qbus_finalize, - .class_init = bus_class_init, -}; - static void qdev_register_types(void) { - type_register_static(&bus_info); type_register_static(&device_type_info); } diff --git a/hw/core/register.c b/hw/core/register.c new file mode 100644 index 000000000..4bfbc508d --- /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(®ister_info); +} + +type_init(register_register_types) diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c index a7dbe2b32..c0f560b28 100644 --- a/hw/core/sysbus.c +++ b/hw/core/sysbus.c @@ -190,9 +190,9 @@ MemoryRegion *sysbus_mmio_get_region(SysBusDevice *dev, int n) return dev->mmio[n].memory; } -void sysbus_init_ioports(SysBusDevice *dev, pio_addr_t ioport, pio_addr_t size) +void sysbus_init_ioports(SysBusDevice *dev, uint32_t ioport, uint32_t size) { - pio_addr_t i; + uint32_t i; for (i = 0; i < size; i++) { assert(dev->num_pio < QDEV_MAX_PIO); diff --git a/hw/core/uboot_image.h b/hw/core/uboot_image.h index 9fc2760b5..34c11a70a 100644 --- a/hw/core/uboot_image.h +++ b/hw/core/uboot_image.h @@ -26,8 +26,8 @@ ******************************************************************** */ -#ifndef __UBOOT_IMAGE_H__ -#define __UBOOT_IMAGE_H__ +#ifndef UBOOT_IMAGE_H +#define UBOOT_IMAGE_H /* * Operating System Codes @@ -155,4 +155,4 @@ typedef struct uboot_image_header { } uboot_image_header_t; -#endif /* __IMAGE_H__ */ +#endif /* UBOOT_IMAGE_H */ diff --git a/hw/cpu/Makefile.objs b/hw/cpu/Makefile.objs index 0954a1872..942a4bb82 100644 --- a/hw/cpu/Makefile.objs +++ b/hw/cpu/Makefile.objs @@ -2,4 +2,5 @@ obj-$(CONFIG_ARM11MPCORE) += arm11mpcore.o obj-$(CONFIG_REALVIEW) += realview_mpcore.o obj-$(CONFIG_A9MPCORE) += a9mpcore.o obj-$(CONFIG_A15MPCORE) += a15mpcore.o +obj-y += core.o diff --git a/hw/cpu/a9mpcore.c b/hw/cpu/a9mpcore.c index 5459ae8c1..f17f29209 100644 --- a/hw/cpu/a9mpcore.c +++ b/hw/cpu/a9mpcore.c @@ -11,6 +11,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/cpu/a9mpcore.h" +#include "qom/cpu.h" static void a9mp_priv_set_irq(void *opaque, int irq, int level) { diff --git a/hw/cpu/core.c b/hw/cpu/core.c new file mode 100644 index 000000000..eff90c12b --- /dev/null +++ b/hw/cpu/core.c @@ -0,0 +1,88 @@ +/* + * CPU core abstract device + * + * Copyright (C) 2016 Bharata B Rao <bharata@linux.vnet.ibm.com> + * + * 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 "hw/cpu/core.h" +#include "qapi/visitor.h" +#include "qapi/error.h" +#include "sysemu/cpus.h" + +static void core_prop_get_core_id(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + CPUCore *core = CPU_CORE(obj); + int64_t value = core->core_id; + + visit_type_int(v, name, &value, errp); +} + +static void core_prop_set_core_id(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + CPUCore *core = CPU_CORE(obj); + Error *local_err = NULL; + int64_t value; + + visit_type_int(v, name, &value, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + core->core_id = value; +} + +static void core_prop_get_nr_threads(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + CPUCore *core = CPU_CORE(obj); + int64_t value = core->nr_threads; + + visit_type_int(v, name, &value, errp); +} + +static void core_prop_set_nr_threads(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + CPUCore *core = CPU_CORE(obj); + Error *local_err = NULL; + int64_t value; + + visit_type_int(v, name, &value, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + core->nr_threads = value; +} + +static void cpu_core_instance_init(Object *obj) +{ + CPUCore *core = CPU_CORE(obj); + + object_property_add(obj, "core-id", "int", core_prop_get_core_id, + core_prop_set_core_id, NULL, NULL, NULL); + object_property_add(obj, "nr-threads", "int", core_prop_get_nr_threads, + core_prop_set_nr_threads, NULL, NULL, NULL); + core->nr_threads = smp_threads; +} + +static const TypeInfo cpu_core_type_info = { + .name = TYPE_CPU_CORE, + .parent = TYPE_DEVICE, + .abstract = true, + .instance_size = sizeof(CPUCore), + .instance_init = cpu_core_instance_init, +}; + +static void cpu_core_register_types(void) +{ + type_register_static(&cpu_core_type_info); +} + +type_init(cpu_core_register_types) diff --git a/hw/cris/axis_dev88.c b/hw/cris/axis_dev88.c index 9f5865874..60df8877c 100644 --- a/hw/cris/axis_dev88.c +++ b/hw/cris/axis_dev88.c @@ -37,6 +37,7 @@ #include "sysemu/block-backend.h" #include "exec/address-spaces.h" #include "sysemu/qtest.h" +#include "sysemu/sysemu.h" #define D(x) #define DNAND(x) @@ -341,8 +342,7 @@ void axisdev88_init(MachineState *machine) sysbus_create_varargs("etraxfs,timer", 0x3005e000, irq[0x1b], nmi[1], NULL); for (i = 0; i < 4; i++) { - sysbus_create_simple("etraxfs,serial", 0x30026000 + i * 0x2000, - irq[0x14 + i]); + etraxfs_ser_create(0x30026000 + i * 0x2000, irq[0x14 + i], serial_hds[i]); } if (kernel_filename) { diff --git a/hw/cris/boot.h b/hw/cris/boot.h index c4d3fa6f6..218854e5d 100644 --- a/hw/cris/boot.h +++ b/hw/cris/boot.h @@ -1,5 +1,5 @@ -#ifndef _CRIS_BOOT_H -#define HW_CRIS_BOOT_H 1 +#ifndef HW_CRIS_BOOT_H +#define HW_CRIS_BOOT_H struct cris_load_info { diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs index d99780eeb..063889bea 100644 --- a/hw/display/Makefile.objs +++ b/hw/display/Makefile.objs @@ -43,3 +43,5 @@ virtio-gpu.o-cflags := $(VIRGL_CFLAGS) virtio-gpu.o-libs += $(VIRGL_LIBS) virtio-gpu-3d.o-cflags := $(VIRGL_CFLAGS) virtio-gpu-3d.o-libs += $(VIRGL_LIBS) +obj-$(CONFIG_DPCD) += dpcd.o +obj-$(CONFIG_XLNX_ZYNQMP) += xlnx_dp.o diff --git a/hw/display/ads7846.c b/hw/display/ads7846.c index 05aa2d1e6..166edade7 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/bcm2835_fb.c b/hw/display/bcm2835_fb.c index 506f1d3d9..7eab92765 100644 --- a/hw/display/bcm2835_fb.c +++ b/hw/display/bcm2835_fb.c @@ -29,6 +29,7 @@ #include "hw/display/framebuffer.h" #include "ui/pixel_ops.h" #include "hw/misc/bcm2835_mbox_defs.h" +#include "qemu/log.h" #define DEFAULT_VCRAM_SIZE 0x4000000 #define BCM2835_FB_OFFSET 0x00100000 diff --git a/hw/display/blizzard.c b/hw/display/blizzard.c index c231960d9..cbf07d14d 100644 --- a/hw/display/blizzard.c +++ b/hw/display/blizzard.c @@ -925,16 +925,83 @@ static void blizzard_update_display(void *opaque) s->my[1] = 0; } -#define DEPTH 8 -#include "blizzard_template.h" -#define DEPTH 15 -#include "blizzard_template.h" -#define DEPTH 16 -#include "blizzard_template.h" -#define DEPTH 24 -#include "blizzard_template.h" -#define DEPTH 32 -#include "blizzard_template.h" +static void blizzard_draw_line16_32(uint32_t *dest, + const uint16_t *src, unsigned int width) +{ + uint16_t data; + unsigned int r, g, b; + const uint16_t *end = (const void *) src + width; + while (src < end) { + data = *src ++; + b = (data & 0x1f) << 3; + data >>= 5; + g = (data & 0x3f) << 2; + data >>= 6; + r = (data & 0x1f) << 3; + data >>= 5; + *dest++ = rgb_to_pixel32(r, g, b); + } +} + +static void blizzard_draw_line24mode1_32(uint32_t *dest, + const uint8_t *src, unsigned int width) +{ + /* TODO: check if SDL 24-bit planes are not in the same format and + * if so, use memcpy */ + unsigned int r[2], g[2], b[2]; + const uint8_t *end = src + width; + while (src < end) { + g[0] = *src ++; + r[0] = *src ++; + r[1] = *src ++; + b[0] = *src ++; + *dest++ = rgb_to_pixel32(r[0], g[0], b[0]); + b[1] = *src ++; + g[1] = *src ++; + *dest++ = rgb_to_pixel32(r[1], g[1], b[1]); + } +} + +static void blizzard_draw_line24mode2_32(uint32_t *dest, + const uint8_t *src, unsigned int width) +{ + unsigned int r, g, b; + const uint8_t *end = src + width; + while (src < end) { + r = *src ++; + src ++; + b = *src ++; + g = *src ++; + *dest++ = rgb_to_pixel32(r, g, b); + } +} + +/* No rotation */ +static blizzard_fn_t blizzard_draw_fn_32[0x10] = { + NULL, + /* RGB 5:6:5*/ + (blizzard_fn_t) blizzard_draw_line16_32, + /* RGB 6:6:6 mode 1 */ + (blizzard_fn_t) blizzard_draw_line24mode1_32, + /* RGB 8:8:8 mode 1 */ + (blizzard_fn_t) blizzard_draw_line24mode1_32, + NULL, NULL, + /* RGB 6:6:6 mode 2 */ + (blizzard_fn_t) blizzard_draw_line24mode2_32, + /* RGB 8:8:8 mode 2 */ + (blizzard_fn_t) blizzard_draw_line24mode2_32, + /* YUV 4:2:2 */ + NULL, + /* YUV 4:2:0 */ + NULL, + NULL, NULL, NULL, NULL, NULL, NULL, +}; + +/* 90deg, 180deg and 270deg rotation */ +static blizzard_fn_t blizzard_draw_fn_r_32[0x10] = { + /* TODO */ + [0 ... 0xf] = NULL, +}; static const GraphicHwOps blizzard_ops = { .invalidate = blizzard_invalidate_display, @@ -951,35 +1018,10 @@ void *s1d13745_init(qemu_irq gpio_int) s->con = graphic_console_init(NULL, 0, &blizzard_ops, s); surface = qemu_console_surface(s->con); - switch (surface_bits_per_pixel(surface)) { - case 0: - s->line_fn_tab[0] = s->line_fn_tab[1] = - g_malloc0(sizeof(blizzard_fn_t) * 0x10); - break; - case 8: - s->line_fn_tab[0] = blizzard_draw_fn_8; - s->line_fn_tab[1] = blizzard_draw_fn_r_8; - break; - case 15: - s->line_fn_tab[0] = blizzard_draw_fn_15; - s->line_fn_tab[1] = blizzard_draw_fn_r_15; - break; - case 16: - s->line_fn_tab[0] = blizzard_draw_fn_16; - s->line_fn_tab[1] = blizzard_draw_fn_r_16; - break; - case 24: - s->line_fn_tab[0] = blizzard_draw_fn_24; - s->line_fn_tab[1] = blizzard_draw_fn_r_24; - break; - case 32: - s->line_fn_tab[0] = blizzard_draw_fn_32; - s->line_fn_tab[1] = blizzard_draw_fn_r_32; - break; - default: - fprintf(stderr, "%s: Bad color depth\n", __FUNCTION__); - exit(1); - } + assert(surface_bits_per_pixel(surface) == 32); + + s->line_fn_tab[0] = blizzard_draw_fn_32; + s->line_fn_tab[1] = blizzard_draw_fn_r_32; blizzard_reset(s); diff --git a/hw/display/blizzard_template.h b/hw/display/blizzard_template.h deleted file mode 100644 index b7ef27c80..000000000 --- a/hw/display/blizzard_template.h +++ /dev/null @@ -1,146 +0,0 @@ -/* - * QEMU Epson S1D13744/S1D13745 templates - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski <andrew@openedhand.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#define SKIP_PIXEL(to) (to += deststep) -#if DEPTH == 8 -# define PIXEL_TYPE uint8_t -# define COPY_PIXEL(to, from) do { *to = from; SKIP_PIXEL(to); } while (0) -# define COPY_PIXEL1(to, from) (*to++ = from) -#elif DEPTH == 15 || DEPTH == 16 -# define PIXEL_TYPE uint16_t -# define COPY_PIXEL(to, from) do { *to = from; SKIP_PIXEL(to); } while (0) -# define COPY_PIXEL1(to, from) (*to++ = from) -#elif DEPTH == 24 -# define PIXEL_TYPE uint8_t -# define COPY_PIXEL(to, from) \ - do { \ - to[0] = from; \ - to[1] = (from) >> 8; \ - to[2] = (from) >> 16; \ - SKIP_PIXEL(to); \ - } while (0) - -# define COPY_PIXEL1(to, from) \ - do { \ - *to++ = from; \ - *to++ = (from) >> 8; \ - *to++ = (from) >> 16; \ - } while (0) -#elif DEPTH == 32 -# define PIXEL_TYPE uint32_t -# define COPY_PIXEL(to, from) do { *to = from; SKIP_PIXEL(to); } while (0) -# define COPY_PIXEL1(to, from) (*to++ = from) -#else -# error unknown bit depth -#endif - -#ifdef HOST_WORDS_BIGENDIAN -# define SWAP_WORDS 1 -#endif - -static void glue(blizzard_draw_line16_, DEPTH)(PIXEL_TYPE *dest, - const uint16_t *src, unsigned int width) -{ -#if !defined(SWAP_WORDS) && DEPTH == 16 - memcpy(dest, src, width); -#else - uint16_t data; - unsigned int r, g, b; - const uint16_t *end = (const void *) src + width; - while (src < end) { - data = *src ++; - b = (data & 0x1f) << 3; - data >>= 5; - g = (data & 0x3f) << 2; - data >>= 6; - r = (data & 0x1f) << 3; - data >>= 5; - COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r, g, b)); - } -#endif -} - -static void glue(blizzard_draw_line24mode1_, DEPTH)(PIXEL_TYPE *dest, - const uint8_t *src, unsigned int width) -{ - /* TODO: check if SDL 24-bit planes are not in the same format and - * if so, use memcpy */ - unsigned int r[2], g[2], b[2]; - const uint8_t *end = src + width; - while (src < end) { - g[0] = *src ++; - r[0] = *src ++; - r[1] = *src ++; - b[0] = *src ++; - COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r[0], g[0], b[0])); - b[1] = *src ++; - g[1] = *src ++; - COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r[1], g[1], b[1])); - } -} - -static void glue(blizzard_draw_line24mode2_, DEPTH)(PIXEL_TYPE *dest, - const uint8_t *src, unsigned int width) -{ - unsigned int r, g, b; - const uint8_t *end = src + width; - while (src < end) { - r = *src ++; - src ++; - b = *src ++; - g = *src ++; - COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r, g, b)); - } -} - -/* No rotation */ -static blizzard_fn_t glue(blizzard_draw_fn_, DEPTH)[0x10] = { - NULL, - /* RGB 5:6:5*/ - (blizzard_fn_t) glue(blizzard_draw_line16_, DEPTH), - /* RGB 6:6:6 mode 1 */ - (blizzard_fn_t) glue(blizzard_draw_line24mode1_, DEPTH), - /* RGB 8:8:8 mode 1 */ - (blizzard_fn_t) glue(blizzard_draw_line24mode1_, DEPTH), - NULL, NULL, - /* RGB 6:6:6 mode 2 */ - (blizzard_fn_t) glue(blizzard_draw_line24mode2_, DEPTH), - /* RGB 8:8:8 mode 2 */ - (blizzard_fn_t) glue(blizzard_draw_line24mode2_, DEPTH), - /* YUV 4:2:2 */ - NULL, - /* YUV 4:2:0 */ - NULL, - NULL, NULL, NULL, NULL, NULL, NULL, -}; - -/* 90deg, 180deg and 270deg rotation */ -static blizzard_fn_t glue(blizzard_draw_fn_r_, DEPTH)[0x10] = { - /* TODO */ - [0 ... 0xf] = NULL, -}; - -#undef DEPTH -#undef SKIP_PIXEL -#undef COPY_PIXEL -#undef COPY_PIXEL1 -#undef PIXEL_TYPE - -#undef SWAP_WORDS diff --git a/hw/display/cg3.c b/hw/display/cg3.c index fc0d97fa4..117422039 100644 --- a/hw/display/cg3.c +++ b/hw/display/cg3.c @@ -26,10 +26,12 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu-common.h" +#include "cpu.h" #include "qemu/error-report.h" #include "ui/console.h" #include "hw/sysbus.h" #include "hw/loader.h" +#include "qemu/log.h" /* Change to 1 to enable debugging */ #define DEBUG_CG3 0 diff --git a/hw/display/dpcd.c b/hw/display/dpcd.c new file mode 100644 index 000000000..ce92ff6e2 --- /dev/null +++ b/hw/display/dpcd.c @@ -0,0 +1,173 @@ +/* + * dpcd.c + * + * Copyright (C) 2015 : GreenSocs Ltd + * http://www.greensocs.com/ , email: info@greensocs.com + * + * Developed by : + * Frederic Konrad <fred.konrad@greensocs.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option)any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + * + */ + +/* + * This is a simple AUX slave which emulates a connected screen. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/misc/auxbus.h" +#include "hw/display/dpcd.h" + +#ifndef DEBUG_DPCD +#define DEBUG_DPCD 0 +#endif + +#define DPRINTF(fmt, ...) do { \ + if (DEBUG_DPCD) { \ + qemu_log("dpcd: " fmt, ## __VA_ARGS__); \ + } \ +} while (0); + +#define DPCD_READABLE_AREA 0x600 + +struct DPCDState { + /*< private >*/ + AUXSlave parent_obj; + + /*< public >*/ + /* + * The DCPD is 0x7FFFF length but read as 0 after offset 0x5FF. + */ + uint8_t dpcd_info[DPCD_READABLE_AREA]; + + MemoryRegion iomem; +}; + +static uint64_t dpcd_read(void *opaque, hwaddr offset, unsigned size) +{ + uint8_t ret; + DPCDState *e = DPCD(opaque); + + if (offset < DPCD_READABLE_AREA) { + ret = e->dpcd_info[offset]; + } else { + qemu_log_mask(LOG_GUEST_ERROR, "dpcd: Bad offset 0x%" HWADDR_PRIX "\n", + offset); + ret = 0; + } + + DPRINTF("read 0x%" PRIX8 " @0x%" HWADDR_PRIX "\n", ret, offset); + return ret; +} + +static void dpcd_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + DPCDState *e = DPCD(opaque); + + DPRINTF("write 0x%" PRIX8 " @0x%" HWADDR_PRIX "\n", (uint8_t)value, offset); + + if (offset < DPCD_READABLE_AREA) { + e->dpcd_info[offset] = value; + } else { + qemu_log_mask(LOG_GUEST_ERROR, "dpcd: Bad offset 0x%" HWADDR_PRIX "\n", + offset); + } +} + +static const MemoryRegionOps aux_ops = { + .read = dpcd_read, + .write = dpcd_write, + .valid = { + .min_access_size = 1, + .max_access_size = 1, + }, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, +}; + +static void dpcd_reset(DeviceState *dev) +{ + DPCDState *s = DPCD(dev); + + memset(&(s->dpcd_info), 0, sizeof(s->dpcd_info)); + + s->dpcd_info[DPCD_REVISION] = DPCD_REV_1_0; + s->dpcd_info[DPCD_MAX_LINK_RATE] = DPCD_5_4GBPS; + s->dpcd_info[DPCD_MAX_LANE_COUNT] = DPCD_FOUR_LANES; + s->dpcd_info[DPCD_RECEIVE_PORT0_CAP_0] = DPCD_EDID_PRESENT; + /* buffer size */ + s->dpcd_info[DPCD_RECEIVE_PORT0_CAP_1] = 0xFF; + + s->dpcd_info[DPCD_LANE0_1_STATUS] = DPCD_LANE0_CR_DONE + | DPCD_LANE0_CHANNEL_EQ_DONE + | DPCD_LANE0_SYMBOL_LOCKED + | DPCD_LANE1_CR_DONE + | DPCD_LANE1_CHANNEL_EQ_DONE + | DPCD_LANE1_SYMBOL_LOCKED; + s->dpcd_info[DPCD_LANE2_3_STATUS] = DPCD_LANE2_CR_DONE + | DPCD_LANE2_CHANNEL_EQ_DONE + | DPCD_LANE2_SYMBOL_LOCKED + | DPCD_LANE3_CR_DONE + | DPCD_LANE3_CHANNEL_EQ_DONE + | DPCD_LANE3_SYMBOL_LOCKED; + + s->dpcd_info[DPCD_LANE_ALIGN_STATUS_UPDATED] = DPCD_INTERLANE_ALIGN_DONE; + s->dpcd_info[DPCD_SINK_STATUS] = DPCD_RECEIVE_PORT_0_STATUS; +} + +static void dpcd_init(Object *obj) +{ + DPCDState *s = DPCD(obj); + + memory_region_init_io(&s->iomem, obj, &aux_ops, s, TYPE_DPCD, 0x7FFFF); + aux_init_mmio(AUX_SLAVE(obj), &s->iomem); +} + +static const VMStateDescription vmstate_dpcd = { + .name = TYPE_DPCD, + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT8_ARRAY_V(dpcd_info, DPCDState, DPCD_READABLE_AREA, 0), + VMSTATE_END_OF_LIST() + } +}; + +static void dpcd_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->reset = dpcd_reset; + dc->vmsd = &vmstate_dpcd; +} + +static const TypeInfo dpcd_info = { + .name = TYPE_DPCD, + .parent = TYPE_AUX_SLAVE, + .instance_size = sizeof(DPCDState), + .class_init = dpcd_class_init, + .instance_init = dpcd_init, +}; + +static void dpcd_register_types(void) +{ + type_register_static(&dpcd_info); +} + +type_init(dpcd_register_types) diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c index 728eb214a..e5be71340 100644 --- a/hw/display/exynos4210_fimd.c +++ b/hw/display/exynos4210_fimd.c @@ -1909,9 +1909,10 @@ static const GraphicHwOps exynos4210_fimd_ops = { .gfx_update = exynos4210_fimd_update, }; -static int exynos4210_fimd_init(SysBusDevice *dev) +static void exynos4210_fimd_init(Object *obj) { - Exynos4210fimdState *s = EXYNOS4210_FIMD(dev); + Exynos4210fimdState *s = EXYNOS4210_FIMD(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); s->ifb = NULL; @@ -1919,28 +1920,32 @@ static int exynos4210_fimd_init(SysBusDevice *dev) sysbus_init_irq(dev, &s->irq[1]); sysbus_init_irq(dev, &s->irq[2]); - memory_region_init_io(&s->iomem, OBJECT(s), &exynos4210_fimd_mmio_ops, s, + memory_region_init_io(&s->iomem, obj, &exynos4210_fimd_mmio_ops, s, "exynos4210.fimd", FIMD_REGS_SIZE); sysbus_init_mmio(dev, &s->iomem); - s->console = graphic_console_init(DEVICE(dev), 0, &exynos4210_fimd_ops, s); +} - return 0; +static void exynos4210_fimd_realize(DeviceState *dev, Error **errp) +{ + Exynos4210fimdState *s = EXYNOS4210_FIMD(dev); + + s->console = graphic_console_init(dev, 0, &exynos4210_fimd_ops, s); } static void exynos4210_fimd_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); dc->vmsd = &exynos4210_fimd_vmstate; dc->reset = exynos4210_fimd_reset; - k->init = exynos4210_fimd_init; + dc->realize = exynos4210_fimd_realize; } static const TypeInfo exynos4210_fimd_info = { .name = TYPE_EXYNOS4210_FIMD, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Exynos4210fimdState), + .instance_init = exynos4210_fimd_init, .class_init = exynos4210_fimd_class_init, }; diff --git a/hw/display/jazz_led.c b/hw/display/jazz_led.c index 09dcdb46a..b72fdb171 100644 --- a/hw/display/jazz_led.c +++ b/hw/display/jazz_led.c @@ -267,16 +267,20 @@ static const GraphicHwOps jazz_led_ops = { .text_update = jazz_led_text_update, }; -static int jazz_led_init(SysBusDevice *dev) +static void jazz_led_init(Object *obj) { - LedState *s = JAZZ_LED(dev); + LedState *s = JAZZ_LED(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); - memory_region_init_io(&s->iomem, OBJECT(s), &led_ops, s, "led", 1); + memory_region_init_io(&s->iomem, obj, &led_ops, s, "led", 1); sysbus_init_mmio(dev, &s->iomem); +} - s->con = graphic_console_init(DEVICE(dev), 0, &jazz_led_ops, s); +static void jazz_led_realize(DeviceState *dev, Error **errp) +{ + LedState *s = JAZZ_LED(dev); - return 0; + s->con = graphic_console_init(dev, 0, &jazz_led_ops, s); } static void jazz_led_reset(DeviceState *d) @@ -291,18 +295,18 @@ static void jazz_led_reset(DeviceState *d) static void jazz_led_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = jazz_led_init; dc->desc = "Jazz LED display", dc->vmsd = &vmstate_jazz_led; dc->reset = jazz_led_reset; + dc->realize = jazz_led_realize; } static const TypeInfo jazz_led_info = { .name = TYPE_JAZZ_LED, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(LedState), + .instance_init = jazz_led_init, .class_init = jazz_led_class_init, }; diff --git a/hw/display/milkymist-tmu2.c b/hw/display/milkymist-tmu2.c index 9bc88f93b..9c0018448 100644 --- a/hw/display/milkymist-tmu2.c +++ b/hw/display/milkymist-tmu2.c @@ -20,7 +20,7 @@ * * * Specification available at: - * http://www.milkymist.org/socdoc/tmu2.pdf + * http://milkymist.walle.cc/socdoc/tmu2.pdf * */ @@ -29,6 +29,7 @@ #include "hw/sysbus.h" #include "trace.h" #include "qemu/error-report.h" +#include "qapi/error.h" #include <X11/Xlib.h> #include <epoxy/gl.h> @@ -443,21 +444,25 @@ static void milkymist_tmu2_reset(DeviceState *d) } } -static int milkymist_tmu2_init(SysBusDevice *dev) +static void milkymist_tmu2_init(Object *obj) { - MilkymistTMU2State *s = MILKYMIST_TMU2(dev); - - if (tmu2_glx_init(s)) { - return 1; - } + MilkymistTMU2State *s = MILKYMIST_TMU2(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); sysbus_init_irq(dev, &s->irq); - memory_region_init_io(&s->regs_region, OBJECT(s), &tmu2_mmio_ops, s, + memory_region_init_io(&s->regs_region, obj, &tmu2_mmio_ops, s, "milkymist-tmu2", R_MAX * 4); sysbus_init_mmio(dev, &s->regs_region); +} - return 0; +static void milkymist_tmu2_realize(DeviceState *dev, Error **errp) +{ + MilkymistTMU2State *s = MILKYMIST_TMU2(dev); + + if (tmu2_glx_init(s)) { + error_setg(errp, "tmu2_glx_init failed"); + } } static const VMStateDescription vmstate_milkymist_tmu2 = { @@ -473,9 +478,8 @@ static const VMStateDescription vmstate_milkymist_tmu2 = { static void milkymist_tmu2_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = milkymist_tmu2_init; + dc->realize = milkymist_tmu2_realize; dc->reset = milkymist_tmu2_reset; dc->vmsd = &vmstate_milkymist_tmu2; } @@ -484,6 +488,7 @@ static const TypeInfo milkymist_tmu2_info = { .name = TYPE_MILKYMIST_TMU2, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(MilkymistTMU2State), + .instance_init = milkymist_tmu2_init, .class_init = milkymist_tmu2_class_init, }; diff --git a/hw/display/milkymist-vgafb.c b/hw/display/milkymist-vgafb.c index 19ca25647..177fdac7d 100644 --- a/hw/display/milkymist-vgafb.c +++ b/hw/display/milkymist-vgafb.c @@ -19,7 +19,7 @@ * * * Specification available at: - * http://www.milkymist.org/socdoc/vgafb.pdf + * http://milkymist.walle.cc/socdoc/vgafb.pdf */ #include "qemu/osdep.h" @@ -292,17 +292,21 @@ static const GraphicHwOps vgafb_ops = { .gfx_update = vgafb_update_display, }; -static int milkymist_vgafb_init(SysBusDevice *dev) +static void milkymist_vgafb_init(Object *obj) { - MilkymistVgafbState *s = MILKYMIST_VGAFB(dev); + MilkymistVgafbState *s = MILKYMIST_VGAFB(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); memory_region_init_io(&s->regs_region, OBJECT(s), &vgafb_mmio_ops, s, "milkymist-vgafb", R_MAX * 4); sysbus_init_mmio(dev, &s->regs_region); +} - s->con = graphic_console_init(DEVICE(dev), 0, &vgafb_ops, s); +static void milkymist_vgafb_realize(DeviceState *dev, Error **errp) +{ + MilkymistVgafbState *s = MILKYMIST_VGAFB(dev); - return 0; + s->con = graphic_console_init(dev, 0, &vgafb_ops, s); } static int vgafb_post_load(void *opaque, int version_id) @@ -331,18 +335,18 @@ static Property milkymist_vgafb_properties[] = { static void milkymist_vgafb_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = milkymist_vgafb_init; dc->reset = milkymist_vgafb_reset; dc->vmsd = &vmstate_milkymist_vgafb; dc->props = milkymist_vgafb_properties; + dc->realize = milkymist_vgafb_realize; } static const TypeInfo milkymist_vgafb_info = { .name = TYPE_MILKYMIST_VGAFB, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(MilkymistVgafbState), + .instance_init = milkymist_vgafb_init, .class_init = milkymist_vgafb_class_init, }; diff --git a/hw/display/omap_lcd_template.h b/hw/display/omap_lcd_template.h index f0ce71fd6..1025ff382 100644 --- a/hw/display/omap_lcd_template.h +++ b/hw/display/omap_lcd_template.h @@ -27,13 +27,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#if DEPTH == 8 -# define BPP 1 -# define PIXEL_TYPE uint8_t -#elif DEPTH == 15 || DEPTH == 16 -# define BPP 2 -# define PIXEL_TYPE uint16_t -#elif DEPTH == 32 +#if DEPTH == 32 # define BPP 4 # define PIXEL_TYPE uint32_t #else @@ -152,7 +146,7 @@ static void glue(draw_line12_, DEPTH)(void *opaque, static void glue(draw_line16_, DEPTH)(void *opaque, uint8_t *d, const uint8_t *s, int width, int deststep) { -#if DEPTH == 16 && defined(HOST_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN) +#if defined(HOST_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN) memcpy(d, s, width * 2); #else uint16_t v; diff --git a/hw/display/omap_lcdc.c b/hw/display/omap_lcdc.c index ce1058bf8..07a5effe0 100644 --- a/hw/display/omap_lcdc.c +++ b/hw/display/omap_lcdc.c @@ -71,47 +71,9 @@ static void omap_lcd_interrupts(struct omap_lcd_panel_s *s) #define draw_line_func drawfn -#define DEPTH 8 -#include "omap_lcd_template.h" -#define DEPTH 15 -#include "omap_lcd_template.h" -#define DEPTH 16 -#include "omap_lcd_template.h" #define DEPTH 32 #include "omap_lcd_template.h" -static draw_line_func draw_line_table2[33] = { - [0 ... 32] = NULL, - [8] = draw_line2_8, - [15] = draw_line2_15, - [16] = draw_line2_16, - [32] = draw_line2_32, -}, draw_line_table4[33] = { - [0 ... 32] = NULL, - [8] = draw_line4_8, - [15] = draw_line4_15, - [16] = draw_line4_16, - [32] = draw_line4_32, -}, draw_line_table8[33] = { - [0 ... 32] = NULL, - [8] = draw_line8_8, - [15] = draw_line8_15, - [16] = draw_line8_16, - [32] = draw_line8_32, -}, draw_line_table12[33] = { - [0 ... 32] = NULL, - [8] = draw_line12_8, - [15] = draw_line12_15, - [16] = draw_line12_16, - [32] = draw_line12_32, -}, draw_line_table16[33] = { - [0 ... 32] = NULL, - [8] = draw_line16_8, - [15] = draw_line16_15, - [16] = draw_line16_16, - [32] = draw_line16_32, -}; - static void omap_update_display(void *opaque) { struct omap_lcd_panel_s *omap_lcd = (struct omap_lcd_panel_s *) opaque; @@ -143,25 +105,25 @@ static void omap_update_display(void *opaque) /* Colour depth */ switch ((omap_lcd->palette[0] >> 12) & 7) { case 1: - draw_line = draw_line_table2[surface_bits_per_pixel(surface)]; + draw_line = draw_line2_32; bpp = 2; break; case 2: - draw_line = draw_line_table4[surface_bits_per_pixel(surface)]; + draw_line = draw_line4_32; bpp = 4; break; case 3: - draw_line = draw_line_table8[surface_bits_per_pixel(surface)]; + draw_line = draw_line8_32; bpp = 8; break; case 4 ... 7: if (!omap_lcd->tft) - draw_line = draw_line_table12[surface_bits_per_pixel(surface)]; + draw_line = draw_line12_32; else - draw_line = draw_line_table16[surface_bits_per_pixel(surface)]; + draw_line = draw_line16_32; bpp = 16; break; diff --git a/hw/display/pl110.c b/hw/display/pl110.c index d589959f1..c069c0b7f 100644 --- a/hw/display/pl110.c +++ b/hw/display/pl110.c @@ -12,6 +12,7 @@ #include "ui/console.h" #include "framebuffer.h" #include "ui/pixel_ops.h" +#include "qemu/log.h" #define PL110_CR_EN 0x001 #define PL110_CR_BGR 0x100 diff --git a/hw/display/qxl.c b/hw/display/qxl.c index 919dc5cd3..0e2682d28 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -504,6 +504,7 @@ static void interface_set_compression_level(QXLInstance *sin, int level) qxl_rom_set_dirty(qxl); } +#if SPICE_NEEDS_SET_MM_TIME static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time) { PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); @@ -517,6 +518,7 @@ static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time) qxl->rom->mm_clock = cpu_to_le32(mm_time); qxl_rom_set_dirty(qxl); } +#endif static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info) { @@ -893,7 +895,8 @@ static void interface_update_area_complete(QXLInstance *sin, int qxl_i; qemu_mutex_lock(&qxl->ssd.lock); - if (surface_id != 0 || !qxl->render_update_cookie_num) { + if (surface_id != 0 || !num_updated_rects || + !qxl->render_update_cookie_num) { qemu_mutex_unlock(&qxl->ssd.lock); return; } @@ -1068,7 +1071,9 @@ static const QXLInterface qxl_interface = { .attache_worker = interface_attach_worker, .set_compression_level = interface_set_compression_level, +#if SPICE_NEEDS_SET_MM_TIME .set_mm_time = interface_set_mm_time, +#endif .get_init_info = interface_get_init_info, /* the callbacks below are called from spice server thread context */ @@ -1243,6 +1248,7 @@ static int qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta, int pci_region; pcibus_t pci_start; pcibus_t pci_end; + MemoryRegion *mr; intptr_t virt_start; QXLDevMemSlot memslot; int i; @@ -1289,11 +1295,11 @@ static int qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta, switch (pci_region) { case QXL_RAM_RANGE_INDEX: - virt_start = (intptr_t)memory_region_get_ram_ptr(&d->vga.vram); + mr = &d->vga.vram; break; case QXL_VRAM_RANGE_INDEX: case 4 /* vram 64bit */: - virt_start = (intptr_t)memory_region_get_ram_ptr(&d->vram_bar); + mr = &d->vram_bar; break; default: /* should not happen */ @@ -1301,6 +1307,7 @@ static int qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta, return 1; } + virt_start = (intptr_t)memory_region_get_ram_ptr(mr); memslot.slot_id = slot_id; memslot.slot_group_id = MEMSLOT_GROUP_GUEST; /* guest group */ memslot.virt_start = virt_start + (guest_start - pci_start); @@ -1310,7 +1317,8 @@ static int qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta, qxl_rom_set_dirty(d); qemu_spice_add_memslot(&d->ssd, &memslot, async); - d->guest_slots[slot_id].ptr = (void*)memslot.virt_start; + d->guest_slots[slot_id].mr = mr; + d->guest_slots[slot_id].offset = memslot.virt_start - virt_start; d->guest_slots[slot_id].size = memslot.virt_end - memslot.virt_start; d->guest_slots[slot_id].delta = delta; d->guest_slots[slot_id].active = 1; @@ -1337,39 +1345,60 @@ static void qxl_reset_surfaces(PCIQXLDevice *d) } /* can be also called from spice server thread context */ -void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id) +static bool qxl_get_check_slot_offset(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, + uint32_t *s, uint64_t *o) { uint64_t phys = le64_to_cpu(pqxl); uint32_t slot = (phys >> (64 - 8)) & 0xff; uint64_t offset = phys & 0xffffffffffff; - switch (group_id) { - case MEMSLOT_GROUP_HOST: - return (void *)(intptr_t)offset; - case MEMSLOT_GROUP_GUEST: - if (slot >= NUM_MEMSLOTS) { - qxl_set_guest_bug(qxl, "slot too large %d >= %d", slot, - NUM_MEMSLOTS); - return NULL; - } - if (!qxl->guest_slots[slot].active) { - qxl_set_guest_bug(qxl, "inactive slot %d\n", slot); - return NULL; - } - if (offset < qxl->guest_slots[slot].delta) { - qxl_set_guest_bug(qxl, + if (slot >= NUM_MEMSLOTS) { + qxl_set_guest_bug(qxl, "slot too large %d >= %d", slot, + NUM_MEMSLOTS); + return false; + } + if (!qxl->guest_slots[slot].active) { + qxl_set_guest_bug(qxl, "inactive slot %d\n", slot); + return false; + } + if (offset < qxl->guest_slots[slot].delta) { + qxl_set_guest_bug(qxl, "slot %d offset %"PRIu64" < delta %"PRIu64"\n", slot, offset, qxl->guest_slots[slot].delta); - return NULL; - } - offset -= qxl->guest_slots[slot].delta; - if (offset > qxl->guest_slots[slot].size) { - qxl_set_guest_bug(qxl, + return false; + } + offset -= qxl->guest_slots[slot].delta; + if (offset > qxl->guest_slots[slot].size) { + qxl_set_guest_bug(qxl, "slot %d offset %"PRIu64" > size %"PRIu64"\n", slot, offset, qxl->guest_slots[slot].size); + return false; + } + + *s = slot; + *o = offset; + return true; +} + +/* can be also called from spice server thread context */ +void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id) +{ + uint64_t offset; + uint32_t slot; + void *ptr; + + switch (group_id) { + case MEMSLOT_GROUP_HOST: + offset = le64_to_cpu(pqxl) & 0xffffffffffff; + return (void *)(intptr_t)offset; + case MEMSLOT_GROUP_GUEST: + if (!qxl_get_check_slot_offset(qxl, pqxl, &slot, &offset)) { return NULL; } - return qxl->guest_slots[slot].ptr + offset; + ptr = memory_region_get_ram_ptr(qxl->guest_slots[slot].mr); + ptr += qxl->guest_slots[slot].offset; + ptr += offset; + return ptr; } return NULL; } @@ -1784,9 +1813,24 @@ static void qxl_hw_update(void *opaque) qxl_render_update(qxl); } +static void qxl_dirty_one_surface(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, + uint32_t height, int32_t stride) +{ + uint64_t offset, size; + uint32_t slot; + bool rc; + + rc = qxl_get_check_slot_offset(qxl, pqxl, &slot, &offset); + assert(rc == true); + size = (uint64_t)height * abs(stride); + trace_qxl_surfaces_dirty(qxl->id, offset, size); + qxl_set_dirty(qxl->guest_slots[slot].mr, + qxl->guest_slots[slot].offset + offset, + qxl->guest_slots[slot].offset + offset + size); +} + static void qxl_dirty_surfaces(PCIQXLDevice *qxl) { - uintptr_t vram_start; int i; if (qxl->mode != QXL_MODE_NATIVE && qxl->mode != QXL_MODE_COMPAT) { @@ -1794,16 +1838,13 @@ static void qxl_dirty_surfaces(PCIQXLDevice *qxl) } /* dirty the primary surface */ - qxl_set_dirty(&qxl->vga.vram, qxl->shadow_rom.draw_area_offset, - qxl->shadow_rom.surface0_area_size); - - vram_start = (uintptr_t)memory_region_get_ram_ptr(&qxl->vram_bar); + qxl_dirty_one_surface(qxl, qxl->guest_primary.surface.mem, + qxl->guest_primary.surface.height, + qxl->guest_primary.surface.stride); /* dirty the off-screen surfaces */ for (i = 0; i < qxl->ssd.num_surfaces; i++) { QXLSurfaceCmd *cmd; - intptr_t surface_offset; - int surface_size; if (qxl->guest_surfaces.cmds[i] == 0) { continue; @@ -1813,15 +1854,9 @@ static void qxl_dirty_surfaces(PCIQXLDevice *qxl) MEMSLOT_GROUP_GUEST); assert(cmd); assert(cmd->type == QXL_SURFACE_CMD_CREATE); - surface_offset = (intptr_t)qxl_phys2virt(qxl, - cmd->u.surface_create.data, - MEMSLOT_GROUP_GUEST); - assert(surface_offset); - surface_offset -= vram_start; - surface_size = cmd->u.surface_create.height * - abs(cmd->u.surface_create.stride); - trace_qxl_surfaces_dirty(qxl->id, i, (int)surface_offset, surface_size); - qxl_set_dirty(&qxl->vram_bar, surface_offset, surface_size); + qxl_dirty_one_surface(qxl, cmd->u.surface_create.data, + cmd->u.surface_create.height, + cmd->u.surface_create.stride); } } @@ -1914,7 +1949,7 @@ static void qxl_init_ramsize(PCIQXLDevice *qxl) /* vram (surfaces, 64bit, bar 4+5) */ if (qxl->vram_size_mb != -1) { - qxl->vram_size = qxl->vram_size_mb * 1024 * 1024; + qxl->vram_size = (uint64_t)qxl->vram_size_mb * 1024 * 1024; } if (qxl->vram_size < qxl->vram32_size) { qxl->vram_size = qxl->vram32_size; @@ -2020,9 +2055,9 @@ static void qxl_realize_common(PCIQXLDevice *qxl, Error **errp) dprint(qxl, 1, "ram/%s: %d MB [region 0]\n", qxl->id == 0 ? "pri" : "sec", qxl->vga.vram_size / (1024*1024)); - dprint(qxl, 1, "vram/32: %d MB [region 1]\n", + dprint(qxl, 1, "vram/32: %" PRIx64 "d MB [region 1]\n", qxl->vram32_size / (1024*1024)); - dprint(qxl, 1, "vram/64: %d MB %s\n", + dprint(qxl, 1, "vram/64: %" PRIx64 "d MB %s\n", qxl->vram_size / (1024*1024), qxl->vram32_size < qxl->vram_size ? "[region 4]" : "[unmapped]"); @@ -2276,7 +2311,7 @@ static VMStateDescription qxl_vmstate = { static Property qxl_properties[] = { DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, 64 * 1024 * 1024), - DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram32_size, + DEFINE_PROP_UINT64("vram_size", PCIQXLDevice, vram32_size, 64 * 1024 * 1024), DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision, QXL_DEFAULT_REVISION), diff --git a/hw/display/qxl.h b/hw/display/qxl.h index 2ddf065e1..d2d49dd93 100644 --- a/hw/display/qxl.h +++ b/hw/display/qxl.h @@ -1,5 +1,5 @@ #ifndef HW_QXL_H -#define HW_QXL_H 1 +#define HW_QXL_H #include "qemu-common.h" @@ -53,7 +53,8 @@ typedef struct PCIQXLDevice { struct guest_slots { QXLMemSlot slot; - void *ptr; + MemoryRegion *mr; + uint64_t offset; uint64_t size; uint64_t delta; uint32_t active; @@ -104,9 +105,9 @@ typedef struct PCIQXLDevice { #endif /* vram pci bar */ - uint32_t vram_size; + uint64_t vram_size; MemoryRegion vram_bar; - uint32_t vram32_size; + uint64_t vram32_size; MemoryRegion vram32_bar; /* io bar */ diff --git a/hw/display/ssd0323.c b/hw/display/ssd0323.c index 14c1bf339..6d1faf44a 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/display/tc6393xb.c b/hw/display/tc6393xb.c index da3ceceb0..92f7120ac 100644 --- a/hw/display/tc6393xb.c +++ b/hw/display/tc6393xb.c @@ -12,6 +12,7 @@ */ #include "qemu/osdep.h" #include "qapi/error.h" +#include "qemu/host-utils.h" #include "hw/hw.h" #include "hw/devices.h" #include "hw/block/flash.h" diff --git a/hw/display/trace-events b/hw/display/trace-events new file mode 100644 index 000000000..332ababd8 --- /dev/null +++ b/hw/display/trace-events @@ -0,0 +1,122 @@ +# See docs/tracing.txt for syntax documentation. + +# hw/display/jazz_led.c +jazz_led_read(uint64_t addr, uint8_t val) "read addr=0x%"PRIx64": 0x%x" +jazz_led_write(uint64_t addr, uint8_t new) "write addr=0x%"PRIx64": 0x%x" + +# hw/display/xenfb.c +xenfb_mouse_event(void *opaque, int dx, int dy, int dz, int button_state, int abs_pointer_wanted) "%p x %d y %d z %d bs %#x abs %d" +xenfb_input_connected(void *xendev, int abs_pointer_wanted) "%p abs %d" + +# hw/display/g364fb.c +g364fb_read(uint64_t addr, uint32_t val) "read addr=0x%"PRIx64": 0x%x" +g364fb_write(uint64_t addr, uint32_t new) "write addr=0x%"PRIx64": 0x%x" + +# hw/display/milkymist-tmu2.c +milkymist_tmu2_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x" +milkymist_tmu2_memory_write(uint32_t addr, uint32_t value) "addr %08x value %08x" +milkymist_tmu2_start(void) "Start TMU" +milkymist_tmu2_pulse_irq(void) "Pulse IRQ" + +# hw/display/milkymist-vgafb.c +milkymist_vgafb_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x" +milkymist_vgafb_memory_write(uint32_t addr, uint32_t value) "addr %08x value %08x" + +# hw/display/vmware_vga.c +vmware_value_read(uint32_t index, uint32_t value) "index %d, value 0x%x" +vmware_value_write(uint32_t index, uint32_t value) "index %d, value 0x%x" +vmware_palette_read(uint32_t index, uint32_t value) "index %d, value 0x%x" +vmware_palette_write(uint32_t index, uint32_t value) "index %d, value 0x%x" +vmware_scratch_read(uint32_t index, uint32_t value) "index %d, value 0x%x" +vmware_scratch_write(uint32_t index, uint32_t value) "index %d, value 0x%x" +vmware_setmode(uint32_t w, uint32_t h, uint32_t bpp) "%dx%d @ %d bpp" + +# hw/display/virtio-gpu.c +virtio_gpu_features(bool virgl) "virgl %d" +virtio_gpu_cmd_get_display_info(void) "" +virtio_gpu_cmd_get_caps(void) "" +virtio_gpu_cmd_set_scanout(uint32_t id, uint32_t res, uint32_t w, uint32_t h, uint32_t x, uint32_t y) "id %d, res 0x%x, w %d, h %d, x %d, y %d" +virtio_gpu_cmd_res_create_2d(uint32_t res, uint32_t fmt, uint32_t w, uint32_t h) "res 0x%x, fmt 0x%x, w %d, h %d" +virtio_gpu_cmd_res_create_3d(uint32_t res, uint32_t fmt, uint32_t w, uint32_t h, uint32_t d) "res 0x%x, fmt 0x%x, w %d, h %d, d %d" +virtio_gpu_cmd_res_unref(uint32_t res) "res 0x%x" +virtio_gpu_cmd_res_back_attach(uint32_t res) "res 0x%x" +virtio_gpu_cmd_res_back_detach(uint32_t res) "res 0x%x" +virtio_gpu_cmd_res_xfer_toh_2d(uint32_t res) "res 0x%x" +virtio_gpu_cmd_res_xfer_toh_3d(uint32_t res) "res 0x%x" +virtio_gpu_cmd_res_xfer_fromh_3d(uint32_t res) "res 0x%x" +virtio_gpu_cmd_res_flush(uint32_t res, uint32_t w, uint32_t h, uint32_t x, uint32_t y) "res 0x%x, w %d, h %d, x %d, y %d" +virtio_gpu_cmd_ctx_create(uint32_t ctx, const char *name) "ctx 0x%x, name %s" +virtio_gpu_cmd_ctx_destroy(uint32_t ctx) "ctx 0x%x" +virtio_gpu_cmd_ctx_res_attach(uint32_t ctx, uint32_t res) "ctx 0x%x, res 0x%x" +virtio_gpu_cmd_ctx_res_detach(uint32_t ctx, uint32_t res) "ctx 0x%x, res 0x%x" +virtio_gpu_cmd_ctx_submit(uint32_t ctx, uint32_t size) "ctx 0x%x, size %d" +virtio_gpu_update_cursor(uint32_t scanout, uint32_t x, uint32_t y, const char *type, uint32_t res) "scanout %d, x %d, y %d, %s, res 0x%x" +virtio_gpu_fence_ctrl(uint64_t fence, uint32_t type) "fence 0x%" PRIx64 ", type 0x%x" +virtio_gpu_fence_resp(uint64_t fence) "fence 0x%" PRIx64 + +# hw/display/qxl.c +disable qxl_interface_set_mm_time(int qid, uint32_t mm_time) "%d %d" +disable qxl_io_write_vga(int qid, const char *mode, uint32_t addr, uint32_t val) "%d %s addr=%u val=%u" +qxl_create_guest_primary(int qid, uint32_t width, uint32_t height, uint64_t mem, uint32_t format, uint32_t position) "%d %ux%u mem=%" PRIx64 " %u,%u" +qxl_create_guest_primary_rest(int qid, int32_t stride, uint32_t type, uint32_t flags) "%d %d,%d,%d" +qxl_destroy_primary(int qid) "%d" +qxl_enter_vga_mode(int qid) "%d" +qxl_exit_vga_mode(int qid) "%d" +qxl_hard_reset(int qid, int64_t loadvm) "%d loadvm=%"PRId64 +qxl_interface_async_complete_io(int qid, uint32_t current_async, void *cookie) "%d current=%d cookie=%p" +qxl_interface_attach_worker(int qid) "%d" +qxl_interface_get_init_info(int qid) "%d" +qxl_interface_set_compression_level(int qid, int64_t level) "%d %"PRId64 +qxl_interface_update_area_complete(int qid, uint32_t surface_id, uint32_t dirty_left, uint32_t dirty_right, uint32_t dirty_top, uint32_t dirty_bottom) "%d surface=%d [%d,%d,%d,%d]" +qxl_interface_update_area_complete_rest(int qid, uint32_t num_updated_rects) "%d #=%d" +qxl_interface_update_area_complete_overflow(int qid, int max) "%d max=%d" +qxl_interface_update_area_complete_schedule_bh(int qid, uint32_t num_dirty) "%d #dirty=%d" +qxl_io_destroy_primary_ignored(int qid, const char *mode) "%d %s" +qxl_io_log(int qid, const uint8_t *log_buf) "%d %s" +qxl_io_read_unexpected(int qid) "%d" +qxl_io_unexpected_vga_mode(int qid, uint64_t addr, uint64_t val, const char *desc) "%d 0x%"PRIx64"=%"PRIu64" (%s)" +qxl_io_write(int qid, const char *mode, uint64_t addr, const char *aname, uint64_t val, unsigned size, int async) "%d %s addr=%"PRIu64 " (%s) val=%"PRIu64" size=%u async=%d" +qxl_memslot_add_guest(int qid, uint32_t slot_id, uint64_t guest_start, uint64_t guest_end) "%d %u: guest phys 0x%"PRIx64 " - 0x%" PRIx64 +qxl_post_load(int qid, const char *mode) "%d %s" +qxl_pre_load(int qid) "%d" +qxl_pre_save(int qid) "%d" +qxl_reset_surfaces(int qid) "%d" +qxl_ring_command_check(int qid, const char *mode) "%d %s" +qxl_ring_command_get(int qid, const char *mode) "%d %s" +qxl_ring_command_req_notification(int qid) "%d" +qxl_ring_cursor_check(int qid, const char *mode) "%d %s" +qxl_ring_cursor_get(int qid, const char *mode) "%d %s" +qxl_ring_cursor_req_notification(int qid) "%d" +qxl_ring_res_push(int qid, const char *mode, uint32_t surface_count, uint32_t free_res, void *last_release, const char *notify) "%d %s s#=%d res#=%d last=%p notify=%s" +qxl_ring_res_push_rest(int qid, uint32_t ring_has, uint32_t ring_size, uint32_t prod, uint32_t cons) "%d ring %d/%d [%d,%d]" +qxl_ring_res_put(int qid, uint32_t free_res) "%d #res=%d" +qxl_set_mode(int qid, int modenr, uint32_t x_res, uint32_t y_res, uint32_t bits, uint64_t devmem) "%d mode=%d [ x=%d y=%d @ bpp=%d devmem=0x%" PRIx64 " ]" +qxl_soft_reset(int qid) "%d" +qxl_spice_destroy_surfaces_complete(int qid) "%d" +qxl_spice_destroy_surfaces(int qid, int async) "%d async=%d" +qxl_spice_destroy_surface_wait_complete(int qid, uint32_t id) "%d sid=%d" +qxl_spice_destroy_surface_wait(int qid, uint32_t id, int async) "%d sid=%d async=%d" +qxl_spice_flush_surfaces_async(int qid, uint32_t surface_count, uint32_t num_free_res) "%d s#=%d, res#=%d" +qxl_spice_monitors_config(int qid) "%d" +qxl_spice_loadvm_commands(int qid, void *ext, uint32_t count) "%d ext=%p count=%d" +qxl_spice_oom(int qid) "%d" +qxl_spice_reset_cursor(int qid) "%d" +qxl_spice_reset_image_cache(int qid) "%d" +qxl_spice_reset_memslots(int qid) "%d" +qxl_spice_update_area(int qid, uint32_t surface_id, uint32_t left, uint32_t right, uint32_t top, uint32_t bottom) "%d sid=%d [%d,%d,%d,%d]" +qxl_spice_update_area_rest(int qid, uint32_t num_dirty_rects, uint32_t clear_dirty_region) "%d #d=%d clear=%d" +qxl_surfaces_dirty(int qid, uint64_t offset, uint64_t size) "%d offset=0x%"PRIx64" size=0x%"PRIx64 +qxl_send_events(int qid, uint32_t events) "%d %d" +qxl_send_events_vm_stopped(int qid, uint32_t events) "%d %d" +qxl_set_guest_bug(int qid) "%d" +qxl_interrupt_client_monitors_config(int qid, int num_heads, void *heads) "%d %d %p" +qxl_client_monitors_config_unsupported_by_guest(int qid, uint32_t int_mask, void *client_monitors_config) "%d %X %p" +qxl_client_monitors_config_unsupported_by_device(int qid, int revision) "%d revision=%d" +qxl_client_monitors_config_capped(int qid, int requested, int limit) "%d %d %d" +qxl_client_monitors_config_crc(int qid, unsigned size, uint32_t crc32) "%d %u %u" +qxl_set_client_capabilities_unsupported_by_revision(int qid, int revision) "%d revision=%d" + +# hw/display/qxl-render.c +qxl_render_blit(int32_t stride, int32_t left, int32_t right, int32_t top, int32_t bottom) "stride=%d [%d, %d, %d, %d]" +qxl_render_guest_primary_resized(int32_t width, int32_t height, int32_t stride, int32_t bytes_pp, int32_t bits_pp) "%dx%d, stride %d, bpp %d, depth %d" +qxl_render_update_area_done(void *cookie) "%p" diff --git a/hw/display/vga.c b/hw/display/vga.c index 4a55ec6db..2a88b3c1b 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -149,6 +149,11 @@ static inline bool vbe_enabled(VGACommonState *s) return s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED; } +static inline uint8_t sr(VGACommonState *s, int idx) +{ + return vbe_enabled(s) ? s->sr_vbe[idx] : s->sr[idx]; +} + static void vga_update_memory_access(VGACommonState *s) { hwaddr base, offset, size; @@ -163,8 +168,8 @@ static void vga_update_memory_access(VGACommonState *s) s->has_chain4_alias = false; s->plane_updated = 0xf; } - if ((s->sr[VGA_SEQ_PLANE_WRITE] & VGA_SR02_ALL_PLANES) == - VGA_SR02_ALL_PLANES && s->sr[VGA_SEQ_MEMORY_MODE] & VGA_SR04_CHN_4M) { + if ((sr(s, VGA_SEQ_PLANE_WRITE) & VGA_SR02_ALL_PLANES) == + VGA_SR02_ALL_PLANES && sr(s, VGA_SEQ_MEMORY_MODE) & VGA_SR04_CHN_4M) { offset = 0; switch ((s->gr[VGA_GFX_MISC] >> 2) & 3) { case 0: @@ -234,7 +239,7 @@ static void vga_precise_update_retrace_info(VGACommonState *s) ((s->cr[VGA_CRTC_OVERFLOW] >> 6) & 2)) << 8); vretr_end_line = s->cr[VGA_CRTC_V_SYNC_END] & 0xf; - clocking_mode = (s->sr[VGA_SEQ_CLOCK_MODE] >> 3) & 1; + clocking_mode = (sr(s, VGA_SEQ_CLOCK_MODE) >> 3) & 1; clock_sel = (s->msr >> 2) & 3; dots = (s->msr & 1) ? 8 : 9; @@ -486,7 +491,6 @@ void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val) printf("vga: write SR%x = 0x%02x\n", s->sr_index, val); #endif s->sr[s->sr_index] = val & sr_mask[s->sr_index]; - vbe_update_vgaregs(s); if (s->sr_index == VGA_SEQ_CLOCK_MODE) { s->update_retrace_info(s); } @@ -680,13 +684,13 @@ static void vbe_update_vgaregs(VGACommonState *s) if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) { shift_control = 0; - s->sr[VGA_SEQ_CLOCK_MODE] &= ~8; /* no double line */ + s->sr_vbe[VGA_SEQ_CLOCK_MODE] &= ~8; /* no double line */ } else { shift_control = 2; /* set chain 4 mode */ - s->sr[VGA_SEQ_MEMORY_MODE] |= VGA_SR04_CHN_4M; + s->sr_vbe[VGA_SEQ_MEMORY_MODE] |= VGA_SR04_CHN_4M; /* activate all planes */ - s->sr[VGA_SEQ_PLANE_WRITE] |= VGA_SR02_ALL_PLANES; + s->sr_vbe[VGA_SEQ_PLANE_WRITE] |= VGA_SR02_ALL_PLANES; } s->gr[VGA_GFX_MODE] = (s->gr[VGA_GFX_MODE] & ~0x60) | (shift_control << 5); @@ -696,9 +700,7 @@ static void vbe_update_vgaregs(VGACommonState *s) static uint32_t vbe_ioport_read_index(void *opaque, uint32_t addr) { VGACommonState *s = opaque; - uint32_t val; - val = s->vbe_index; - return val; + return s->vbe_index; } uint32_t vbe_ioport_read_data(void *opaque, uint32_t addr) @@ -836,7 +838,7 @@ uint32_t vga_mem_readb(VGACommonState *s, hwaddr addr) break; } - if (s->sr[VGA_SEQ_MEMORY_MODE] & VGA_SR04_CHN_4M) { + if (sr(s, VGA_SEQ_MEMORY_MODE) & VGA_SR04_CHN_4M) { /* chain 4 mode : simplest access */ assert(addr < s->vram_size); ret = s->vram_ptr[addr]; @@ -904,11 +906,11 @@ void vga_mem_writeb(VGACommonState *s, hwaddr addr, uint32_t val) break; } - if (s->sr[VGA_SEQ_MEMORY_MODE] & VGA_SR04_CHN_4M) { + if (sr(s, VGA_SEQ_MEMORY_MODE) & VGA_SR04_CHN_4M) { /* chain 4 mode : simplest access */ plane = addr & 3; mask = (1 << plane); - if (s->sr[VGA_SEQ_PLANE_WRITE] & mask) { + if (sr(s, VGA_SEQ_PLANE_WRITE) & mask) { assert(addr < s->vram_size); s->vram_ptr[addr] = val; #ifdef DEBUG_VGA_MEM @@ -921,7 +923,7 @@ void vga_mem_writeb(VGACommonState *s, hwaddr addr, uint32_t val) /* odd/even mode (aka text mode mapping) */ plane = (s->gr[VGA_GFX_PLANE_READ] & 2) | (addr & 1); mask = (1 << plane); - if (s->sr[VGA_SEQ_PLANE_WRITE] & mask) { + if (sr(s, VGA_SEQ_PLANE_WRITE) & mask) { addr = ((addr & ~1) << 1) | plane; if (addr >= s->vram_size) { return; @@ -996,7 +998,7 @@ void vga_mem_writeb(VGACommonState *s, hwaddr addr, uint32_t val) do_write: /* mask data according to sr[2] */ - mask = s->sr[VGA_SEQ_PLANE_WRITE]; + mask = sr(s, VGA_SEQ_PLANE_WRITE); s->plane_updated |= mask; /* only used to detect font change */ write_mask = mask16[mask]; if (addr * sizeof(uint32_t) >= s->vram_size) { @@ -1152,10 +1154,10 @@ static void vga_get_text_resolution(VGACommonState *s, int *pwidth, int *pheight /* total width & height */ cheight = (s->cr[VGA_CRTC_MAX_SCAN] & 0x1f) + 1; cwidth = 8; - if (!(s->sr[VGA_SEQ_CLOCK_MODE] & VGA_SR01_CHAR_CLK_8DOTS)) { + if (!(sr(s, VGA_SEQ_CLOCK_MODE) & VGA_SR01_CHAR_CLK_8DOTS)) { cwidth = 9; } - if (s->sr[VGA_SEQ_CLOCK_MODE] & 0x08) { + if (sr(s, VGA_SEQ_CLOCK_MODE) & 0x08) { cwidth = 16; /* NOTE: no 18 pixel wide */ } width = (s->cr[VGA_CRTC_H_DISP] + 1); @@ -1197,7 +1199,7 @@ static void vga_draw_text(VGACommonState *s, int full_update) int64_t now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); /* compute font data address (in plane 2) */ - v = s->sr[VGA_SEQ_CHARACTER_MAP]; + v = sr(s, VGA_SEQ_CHARACTER_MAP); offset = (((v >> 4) & 1) | ((v << 1) & 6)) * 8192 * 4 + 2; if (offset != s->font_offsets[0]) { s->font_offsets[0] = offset; @@ -1506,11 +1508,11 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) } if (shift_control == 0) { - if (s->sr[VGA_SEQ_CLOCK_MODE] & 8) { + if (sr(s, VGA_SEQ_CLOCK_MODE) & 8) { disp_width <<= 1; } } else if (shift_control == 1) { - if (s->sr[VGA_SEQ_CLOCK_MODE] & 8) { + if (sr(s, VGA_SEQ_CLOCK_MODE) & 8) { disp_width <<= 1; } } @@ -1574,7 +1576,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) if (shift_control == 0) { full_update |= update_palette16(s); - if (s->sr[VGA_SEQ_CLOCK_MODE] & 8) { + if (sr(s, VGA_SEQ_CLOCK_MODE) & 8) { v = VGA_DRAW_LINE4D2; } else { v = VGA_DRAW_LINE4; @@ -1582,7 +1584,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) bits = 4; } else if (shift_control == 1) { full_update |= update_palette16(s); - if (s->sr[VGA_SEQ_CLOCK_MODE] & 8) { + if (sr(s, VGA_SEQ_CLOCK_MODE) & 8) { v = VGA_DRAW_LINE2D2; } else { v = VGA_DRAW_LINE2; @@ -1629,7 +1631,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) #if 0 printf("w=%d h=%d v=%d line_offset=%d cr[0x09]=0x%02x cr[0x17]=0x%02x linecmp=%d sr[0x01]=0x%02x\n", width, height, v, line_offset, s->cr[9], s->cr[VGA_CRTC_MODE], - s->line_compare, s->sr[VGA_SEQ_CLOCK_MODE]); + s->line_compare, sr(s, VGA_SEQ_CLOCK_MODE)); #endif addr1 = (s->start_addr * 4); bwidth = (width * bits + 7) / 8; @@ -1781,6 +1783,7 @@ void vga_common_reset(VGACommonState *s) { s->sr_index = 0; memset(s->sr, '\0', sizeof(s->sr)); + memset(s->sr_vbe, '\0', sizeof(s->sr_vbe)); s->gr_index = 0; memset(s->gr, '\0', sizeof(s->gr)); s->ar_index = 0; @@ -1883,10 +1886,10 @@ static void vga_update_text(void *opaque, console_ch_t *chardata) /* total width & height */ cheight = (s->cr[VGA_CRTC_MAX_SCAN] & 0x1f) + 1; cw = 8; - if (!(s->sr[VGA_SEQ_CLOCK_MODE] & VGA_SR01_CHAR_CLK_8DOTS)) { + if (!(sr(s, VGA_SEQ_CLOCK_MODE) & VGA_SR01_CHAR_CLK_8DOTS)) { cw = 9; } - if (s->sr[VGA_SEQ_CLOCK_MODE] & 0x08) { + if (sr(s, VGA_SEQ_CLOCK_MODE) & 0x08) { cw = 16; /* NOTE: no 18 pixel wide */ } width = (s->cr[VGA_CRTC_H_DISP] + 1); @@ -2053,6 +2056,7 @@ static int vga_common_post_load(void *opaque, int version_id) /* force refresh */ s->graphic_mode = -1; + vbe_update_vgaregs(s); return 0; } diff --git a/hw/display/vga.h b/hw/display/vga.h index d917046da..16886f5ee 100644 --- a/hw/display/vga.h +++ b/hw/display/vga.h @@ -14,8 +14,8 @@ * */ -#ifndef __linux_video_vga_h__ -#define __linux_video_vga_h__ +#ifndef LINUX_VIDEO_VGA_H +#define LINUX_VIDEO_VGA_H /* Some of the code below is taken from SVGAlib. The original, unmodified copyright notice for that code is below. */ @@ -156,4 +156,4 @@ /* VGA graphics controller bit masks */ #define VGA_GR06_GRAPHICS_MODE 0x01 -#endif /* __linux_video_vga_h__ */ +#endif /* LINUX_VIDEO_VGA_H */ diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h index bdb43a5a3..dd6c958da 100644 --- a/hw/display/vga_int.h +++ b/hw/display/vga_int.h @@ -21,10 +21,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ + #ifndef HW_VGA_INT_H -#define HW_VGA_INT_H 1 +#define HW_VGA_INT_H -#include <hw/hw.h> +#include "hw/hw.h" #include "exec/memory.h" #define ST01_V_RETRACE 0x08 @@ -98,6 +99,7 @@ typedef struct VGACommonState { MemoryRegion chain4_alias; uint8_t sr_index; uint8_t sr[256]; + uint8_t sr_vbe[256]; uint8_t gr_index; uint8_t gr[256]; uint8_t ar_index; diff --git a/hw/display/virtio-gpu-3d.c b/hw/display/virtio-gpu-3d.c index fa192946a..758d33a09 100644 --- a/hw/display/virtio-gpu-3d.c +++ b/hw/display/virtio-gpu-3d.c @@ -17,10 +17,11 @@ #include "trace.h" #include "hw/virtio/virtio.h" #include "hw/virtio/virtio-gpu.h" +#include "qapi/error.h" #ifdef CONFIG_VIRGL -#include "virglrenderer.h" +#include <virglrenderer.h> static struct virgl_renderer_callbacks virtio_gpu_3d_cbs; @@ -127,7 +128,7 @@ static void virgl_cmd_resource_flush(VirtIOGPU *g, trace_virtio_gpu_cmd_res_flush(rf.resource_id, rf.r.width, rf.r.height, rf.r.x, rf.r.y); - for (i = 0; i < VIRTIO_GPU_MAX_SCANOUT; i++) { + for (i = 0; i < g->conf.max_outputs; i++) { if (g->scanout[i].resource_id != rf.resource_id) { continue; } @@ -146,7 +147,7 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g, trace_virtio_gpu_cmd_set_scanout(ss.scanout_id, ss.resource_id, ss.r.width, ss.r.height, ss.r.x, ss.r.y); - if (ss.scanout_id >= VIRTIO_GPU_MAX_SCANOUT) { + if (ss.scanout_id >= g->conf.max_outputs) { qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout id specified %d", __func__, ss.scanout_id); cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID; @@ -170,13 +171,14 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g, virgl_renderer_force_ctx_0(); dpy_gl_scanout(g->scanout[ss.scanout_id].con, info.tex_id, info.flags & 1 /* FIXME: Y_0_TOP */, + info.width, info.height, ss.r.x, ss.r.y, ss.r.width, ss.r.height); } else { if (ss.scanout_id != 0) { dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, NULL); } dpy_gl_scanout(g->scanout[ss.scanout_id].con, 0, false, - 0, 0, 0, 0); + 0, 0, 0, 0, 0, 0); } g->scanout[ss.scanout_id].resource_id = ss.resource_id; } @@ -283,7 +285,7 @@ static void virgl_resource_attach_backing(VirtIOGPU *g, VIRTIO_GPU_FILL_CMD(att_rb); trace_virtio_gpu_cmd_res_back_attach(att_rb.resource_id); - ret = virtio_gpu_create_mapping_iov(&att_rb, cmd, &res_iovs); + ret = virtio_gpu_create_mapping_iov(&att_rb, cmd, NULL, &res_iovs); if (ret != 0) { cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; return; @@ -579,7 +581,7 @@ void virtio_gpu_virgl_reset(VirtIOGPU *g) if (i != 0) { dpy_gfx_replace_surface(g->scanout[i].con, NULL); } - dpy_gl_scanout(g->scanout[i].con, 0, false, 0, 0, 0, 0); + dpy_gl_scanout(g->scanout[i].con, 0, false, 0, 0, 0, 0, 0, 0); } } diff --git a/hw/display/virtio-gpu-pci.c b/hw/display/virtio-gpu-pci.c index a71b230d3..34a724c75 100644 --- a/hw/display/virtio-gpu-pci.c +++ b/hw/display/virtio-gpu-pci.c @@ -30,9 +30,7 @@ static void virtio_gpu_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) int i; qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); - /* force virtio-1.0 */ - vpci_dev->flags &= ~VIRTIO_PCI_FLAG_DISABLE_MODERN; - vpci_dev->flags |= VIRTIO_PCI_FLAG_DISABLE_LEGACY; + virtio_pci_force_virtio_1(vpci_dev); object_property_set_bool(OBJECT(vdev), true, "realized", errp); for (i = 0; i < g->conf.max_outputs; i++) { diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index c181fb364..7fe6ed8bf 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -19,12 +19,17 @@ #include "hw/virtio/virtio.h" #include "hw/virtio/virtio-gpu.h" #include "hw/virtio/virtio-bus.h" +#include "migration/migration.h" +#include "qemu/log.h" +#include "qapi/error.h" + +#define VIRTIO_GPU_VM_VERSION 1 static struct virtio_gpu_simple_resource* virtio_gpu_find_resource(VirtIOGPU *g, uint32_t resource_id); #ifdef CONFIG_VIRGL -#include "virglrenderer.h" +#include <virglrenderer.h> #define VIRGL(_g, _virgl, _simple, ...) \ do { \ if (_g->use_virgl_renderer) { \ @@ -92,7 +97,7 @@ static void update_cursor_data_virgl(VirtIOGPU *g, static void update_cursor(VirtIOGPU *g, struct virtio_gpu_update_cursor *cursor) { struct virtio_gpu_scanout *s; - bool move = cursor->hdr.type != VIRTIO_GPU_CMD_MOVE_CURSOR; + bool move = cursor->hdr.type == VIRTIO_GPU_CMD_MOVE_CURSOR; if (cursor->pos.scanout_id >= g->conf.max_outputs) { return; @@ -105,7 +110,7 @@ static void update_cursor(VirtIOGPU *g, struct virtio_gpu_update_cursor *cursor) move ? "move" : "update", cursor->resource_id); - if (move) { + if (!move) { if (!s->current_cursor) { s->current_cursor = cursor_alloc(64, 64); } @@ -118,6 +123,11 @@ static void update_cursor(VirtIOGPU *g, struct virtio_gpu_update_cursor *cursor) g, s, cursor->resource_id); } dpy_cursor_define(s->con, s->current_cursor); + + s->cursor = *cursor; + } else { + s->cursor.pos.x = cursor->pos.x; + s->cursor.pos.y = cursor->pos.y; } dpy_mouse_set(s->con, cursor->pos.x, cursor->pos.y, cursor->resource_id ? 1 : 0); @@ -464,7 +474,7 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g, pixman_region_init_rect(&flush_region, rf.r.x, rf.r.y, rf.r.width, rf.r.height); - for (i = 0; i < VIRTIO_GPU_MAX_SCANOUT; i++) { + for (i = 0; i < g->conf.max_outputs; i++) { struct virtio_gpu_scanout *scanout; pixman_region16_t region, finalregion; pixman_box16_t *extents; @@ -493,6 +503,11 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g, pixman_region_fini(&flush_region); } +static void virtio_unref_resource(pixman_image_t *image, void *data) +{ + pixman_image_unref(data); +} + static void virtio_gpu_set_scanout(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) { @@ -507,6 +522,13 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g, trace_virtio_gpu_cmd_set_scanout(ss.scanout_id, ss.resource_id, ss.r.width, ss.r.height, ss.r.x, ss.r.y); + if (ss.scanout_id >= g->conf.max_outputs) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout id specified %d", + __func__, ss.scanout_id); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID; + return; + } + g->enable = 1; if (ss.resource_id == 0) { scanout = &g->scanout[ss.scanout_id]; @@ -516,8 +538,7 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g, res->scanout_bitmask &= ~(1 << ss.scanout_id); } } - if (ss.scanout_id == 0 || - ss.scanout_id >= g->conf.max_outputs) { + if (ss.scanout_id == 0) { qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout id specified %d", __func__, ss.scanout_id); @@ -532,14 +553,6 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g, } /* create a surface for this scanout */ - if (ss.scanout_id >= VIRTIO_GPU_MAX_SCANOUT || - ss.scanout_id >= g->conf.max_outputs) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout id specified %d", - __func__, ss.scanout_id); - cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID; - return; - } - res = virtio_gpu_find_resource(g, ss.resource_id); if (!res) { qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal resource specified %d\n", @@ -571,8 +584,15 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g, != ((uint8_t *)pixman_image_get_data(res->image) + offset) || scanout->width != ss.r.width || scanout->height != ss.r.height) { + pixman_image_t *rect; + void *ptr = (uint8_t *)pixman_image_get_data(res->image) + offset; + rect = pixman_image_create_bits(format, ss.r.width, ss.r.height, ptr, + pixman_image_get_stride(res->image)); + pixman_image_ref(res->image); + pixman_image_set_destroy_function(rect, virtio_unref_resource, + res->image); /* realloc the surface ptr */ - scanout->ds = qemu_create_displaysurface_pixman(res->image); + scanout->ds = qemu_create_displaysurface_pixman(rect); if (!scanout->ds) { cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; return; @@ -590,7 +610,7 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g, int virtio_gpu_create_mapping_iov(struct virtio_gpu_resource_attach_backing *ab, struct virtio_gpu_ctrl_command *cmd, - struct iovec **iov) + uint64_t **addr, struct iovec **iov) { struct virtio_gpu_mem_entry *ents; size_t esize, s; @@ -616,10 +636,16 @@ int virtio_gpu_create_mapping_iov(struct virtio_gpu_resource_attach_backing *ab, } *iov = g_malloc0(sizeof(struct iovec) * ab->nr_entries); + if (addr) { + *addr = g_malloc0(sizeof(uint64_t) * ab->nr_entries); + } for (i = 0; i < ab->nr_entries; i++) { hwaddr len = ents[i].length; (*iov)[i].iov_len = ents[i].length; (*iov)[i].iov_base = cpu_physical_memory_map(ents[i].addr, &len, 1); + if (addr) { + (*addr)[i] = ents[i].addr; + } if (!(*iov)[i].iov_base || len != ents[i].length) { qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to map MMIO memory for" " resource %d element %d\n", @@ -627,6 +653,10 @@ int virtio_gpu_create_mapping_iov(struct virtio_gpu_resource_attach_backing *ab, virtio_gpu_cleanup_mapping_iov(*iov, i); g_free(ents); *iov = NULL; + if (addr) { + g_free(*addr); + *addr = NULL; + } return -1; } } @@ -650,6 +680,8 @@ static void virtio_gpu_cleanup_mapping(struct virtio_gpu_simple_resource *res) virtio_gpu_cleanup_mapping_iov(res->iov, res->iov_cnt); res->iov = NULL; res->iov_cnt = 0; + g_free(res->addrs); + res->addrs = NULL; } static void @@ -671,7 +703,7 @@ virtio_gpu_resource_attach_backing(VirtIOGPU *g, return; } - ret = virtio_gpu_create_mapping_iov(&ab, cmd, &res->iov); + ret = virtio_gpu_create_mapping_iov(&ab, cmd, &res->addrs, &res->iov); if (ret != 0) { cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; return; @@ -879,7 +911,7 @@ static int virtio_gpu_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info) { VirtIOGPU *g = opaque; - if (idx > g->conf.max_outputs) { + if (idx >= g->conf.max_outputs) { return -1; } @@ -903,8 +935,14 @@ static void virtio_gpu_gl_block(void *opaque, bool block) { VirtIOGPU *g = opaque; - g->renderer_blocked = block; - if (!block) { + if (block) { + g->renderer_blocked++; + } else { + g->renderer_blocked--; + } + assert(g->renderer_blocked >= 0); + + if (g->renderer_blocked == 0) { virtio_gpu_process_cmdq(g); } } @@ -917,11 +955,154 @@ const GraphicHwOps virtio_gpu_ops = { .gl_block = virtio_gpu_gl_block, }; -static const VMStateDescription vmstate_virtio_gpu_unmigratable = { - .name = "virtio-gpu", - .unmigratable = 1, +static const VMStateDescription vmstate_virtio_gpu_scanout = { + .name = "virtio-gpu-one-scanout", + .version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(resource_id, struct virtio_gpu_scanout), + VMSTATE_UINT32(width, struct virtio_gpu_scanout), + VMSTATE_UINT32(height, struct virtio_gpu_scanout), + VMSTATE_INT32(x, struct virtio_gpu_scanout), + VMSTATE_INT32(y, struct virtio_gpu_scanout), + VMSTATE_UINT32(cursor.resource_id, struct virtio_gpu_scanout), + VMSTATE_UINT32(cursor.hot_x, struct virtio_gpu_scanout), + VMSTATE_UINT32(cursor.hot_y, struct virtio_gpu_scanout), + VMSTATE_UINT32(cursor.pos.x, struct virtio_gpu_scanout), + VMSTATE_UINT32(cursor.pos.y, struct virtio_gpu_scanout), + VMSTATE_END_OF_LIST() + }, }; +static const VMStateDescription vmstate_virtio_gpu_scanouts = { + .name = "virtio-gpu-scanouts", + .version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32(enable, struct VirtIOGPU), + VMSTATE_UINT32_EQUAL(conf.max_outputs, struct VirtIOGPU), + VMSTATE_STRUCT_VARRAY_UINT32(scanout, struct VirtIOGPU, + conf.max_outputs, 1, + vmstate_virtio_gpu_scanout, + struct virtio_gpu_scanout), + VMSTATE_END_OF_LIST() + }, +}; + +static void virtio_gpu_save(QEMUFile *f, void *opaque, size_t size) +{ + VirtIOGPU *g = opaque; + VirtIODevice *vdev = VIRTIO_DEVICE(g); + struct virtio_gpu_simple_resource *res; + int i; + + virtio_save(vdev, f); + + /* in 2d mode we should never find unprocessed commands here */ + assert(QTAILQ_EMPTY(&g->cmdq)); + + QTAILQ_FOREACH(res, &g->reslist, next) { + qemu_put_be32(f, res->resource_id); + qemu_put_be32(f, res->width); + qemu_put_be32(f, res->height); + qemu_put_be32(f, res->format); + qemu_put_be32(f, res->iov_cnt); + for (i = 0; i < res->iov_cnt; i++) { + qemu_put_be64(f, res->addrs[i]); + qemu_put_be32(f, res->iov[i].iov_len); + } + qemu_put_buffer(f, (void *)pixman_image_get_data(res->image), + pixman_image_get_stride(res->image) * res->height); + } + qemu_put_be32(f, 0); /* end of list */ + + vmstate_save_state(f, &vmstate_virtio_gpu_scanouts, g, NULL); +} + +static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size) +{ + VirtIOGPU *g = opaque; + VirtIODevice *vdev = VIRTIO_DEVICE(g); + struct virtio_gpu_simple_resource *res; + struct virtio_gpu_scanout *scanout; + uint32_t resource_id, pformat; + int i, ret; + + ret = virtio_load(vdev, f, VIRTIO_GPU_VM_VERSION); + if (ret) { + return ret; + } + + resource_id = qemu_get_be32(f); + while (resource_id != 0) { + res = g_new0(struct virtio_gpu_simple_resource, 1); + res->resource_id = resource_id; + res->width = qemu_get_be32(f); + res->height = qemu_get_be32(f); + res->format = qemu_get_be32(f); + res->iov_cnt = qemu_get_be32(f); + + /* allocate */ + pformat = get_pixman_format(res->format); + if (!pformat) { + return -EINVAL; + } + res->image = pixman_image_create_bits(pformat, + res->width, res->height, + NULL, 0); + if (!res->image) { + return -EINVAL; + } + + res->addrs = g_new(uint64_t, res->iov_cnt); + res->iov = g_new(struct iovec, res->iov_cnt); + + /* read data */ + for (i = 0; i < res->iov_cnt; i++) { + res->addrs[i] = qemu_get_be64(f); + res->iov[i].iov_len = qemu_get_be32(f); + } + qemu_get_buffer(f, (void *)pixman_image_get_data(res->image), + pixman_image_get_stride(res->image) * res->height); + + /* restore mapping */ + for (i = 0; i < res->iov_cnt; i++) { + hwaddr len = res->iov[i].iov_len; + res->iov[i].iov_base = + cpu_physical_memory_map(res->addrs[i], &len, 1); + if (!res->iov[i].iov_base || len != res->iov[i].iov_len) { + return -EINVAL; + } + } + + QTAILQ_INSERT_HEAD(&g->reslist, res, next); + + resource_id = qemu_get_be32(f); + } + + /* load & apply scanout state */ + vmstate_load_state(f, &vmstate_virtio_gpu_scanouts, g, 1); + for (i = 0; i < g->conf.max_outputs; i++) { + scanout = &g->scanout[i]; + if (!scanout->resource_id) { + continue; + } + res = virtio_gpu_find_resource(g, scanout->resource_id); + if (!res) { + return -EINVAL; + } + scanout->ds = qemu_create_displaysurface_pixman(res->image); + if (!scanout->ds) { + return -EINVAL; + } + + dpy_gfx_replace_surface(scanout->con, scanout->ds); + dpy_gfx_update(scanout->con, 0, 0, scanout->width, scanout->height); + update_cursor(g, &scanout->cursor); + res->scanout_bitmask |= (1 << i); + } + + return 0; +} + static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp) { VirtIODevice *vdev = VIRTIO_DEVICE(qdev); @@ -929,6 +1110,11 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp) bool have_virgl; int i; + if (g->conf.max_outputs > VIRTIO_GPU_MAX_SCANOUTS) { + error_setg(errp, "invalid max_outputs > %d", VIRTIO_GPU_MAX_SCANOUTS); + return; + } + g->config_size = sizeof(struct virtio_gpu_config); g->virtio_config.num_scanouts = g->conf.max_outputs; virtio_init(VIRTIO_DEVICE(g), "virtio-gpu", VIRTIO_ID_GPU, @@ -974,7 +1160,19 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp) } } - vmstate_register(qdev, -1, &vmstate_virtio_gpu_unmigratable, g); + if (virtio_gpu_virgl_enabled(g->conf)) { + error_setg(&g->migration_blocker, "virgl is not yet migratable"); + migrate_add_blocker(g->migration_blocker); + } +} + +static void virtio_gpu_device_unrealize(DeviceState *qdev, Error **errp) +{ + VirtIOGPU *g = VIRTIO_GPU(qdev); + if (g->migration_blocker) { + migrate_del_blocker(g->migration_blocker); + error_free(g->migration_blocker); + } } static void virtio_gpu_instance_init(Object *obj) @@ -1021,6 +1219,9 @@ static void virtio_gpu_reset(VirtIODevice *vdev) #endif } +VMSTATE_VIRTIO_DEVICE(gpu, VIRTIO_GPU_VM_VERSION, virtio_gpu_load, + virtio_gpu_save); + static Property virtio_gpu_properties[] = { DEFINE_PROP_UINT32("max_outputs", VirtIOGPU, conf.max_outputs, 1), #ifdef CONFIG_VIRGL @@ -1038,6 +1239,7 @@ static void virtio_gpu_class_init(ObjectClass *klass, void *data) VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); vdc->realize = virtio_gpu_device_realize; + vdc->unrealize = virtio_gpu_device_unrealize; vdc->get_config = virtio_gpu_get_config; vdc->set_config = virtio_gpu_set_config; vdc->get_features = virtio_gpu_get_features; @@ -1046,6 +1248,7 @@ static void virtio_gpu_class_init(ObjectClass *klass, void *data) vdc->reset = virtio_gpu_reset; dc->props = virtio_gpu_properties; + dc->vmsd = &vmstate_virtio_gpu; } static const TypeInfo virtio_gpu_info = { diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c index e58b165ae..5b510a17f 100644 --- a/hw/display/virtio-vga.c +++ b/hw/display/virtio-vga.c @@ -4,6 +4,7 @@ #include "ui/console.h" #include "vga_int.h" #include "hw/virtio/virtio-pci.h" +#include "qapi/error.h" /* * virtio-vga: This extends VirtioPCIProxy. @@ -83,12 +84,24 @@ static const GraphicHwOps virtio_vga_ops = { .gl_block = virtio_vga_gl_block, }; +static const VMStateDescription vmstate_virtio_vga = { + .name = "virtio-vga", + .version_id = 2, + .minimum_version_id = 2, + .fields = (VMStateField[]) { + /* no pci stuff here, saving the virtio device will handle that */ + VMSTATE_STRUCT(vga, VirtIOVGA, 0, vmstate_vga_common, VGACommonState), + VMSTATE_END_OF_LIST() + } +}; + /* VGA device wrapper around PCI device around virtio GPU */ static void virtio_vga_realize(VirtIOPCIProxy *vpci_dev, Error **errp) { VirtIOVGA *vvga = VIRTIO_VGA(vpci_dev); VirtIOGPU *g = &vvga->vdev; VGACommonState *vga = &vvga->vga; + Error *err = NULL; uint32_t offset; int i; @@ -121,10 +134,12 @@ static void virtio_vga_realize(VirtIOPCIProxy *vpci_dev, Error **errp) /* init virtio bits */ qdev_set_parent_bus(DEVICE(g), BUS(&vpci_dev->bus)); - /* force virtio-1.0 */ - vpci_dev->flags &= ~VIRTIO_PCI_FLAG_DISABLE_MODERN; - vpci_dev->flags |= VIRTIO_PCI_FLAG_DISABLE_LEGACY; - object_property_set_bool(OBJECT(g), true, "realized", errp); + virtio_pci_force_virtio_1(vpci_dev); + object_property_set_bool(OBJECT(g), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } /* add stdvga mmio regions */ pci_std_vga_mmio_region_init(vga, &vpci_dev->modern_bar, @@ -162,6 +177,7 @@ static void virtio_vga_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); dc->props = virtio_vga_properties; dc->reset = virtio_vga_reset; + dc->vmsd = &vmstate_virtio_vga; dc->hotpluggable = false; k->realize = virtio_vga_realize; diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index 0c63fa851..e51a05ea7 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -66,17 +66,11 @@ struct vmsvga_state_s { uint8_t *fifo_ptr; unsigned int fifo_size; - union { - uint32_t *fifo; - struct QEMU_PACKED { - uint32_t min; - uint32_t max; - uint32_t next_cmd; - uint32_t stop; - /* Add registers here when adding capabilities. */ - uint32_t fifo[0]; - } *cmd; - }; + uint32_t *fifo; + uint32_t fifo_min; + uint32_t fifo_max; + uint32_t fifo_next; + uint32_t fifo_stop; #define REDRAW_FIFO_LEN 512 struct vmsvga_rect_s { @@ -198,7 +192,7 @@ enum { */ SVGA_FIFO_MIN = 0, SVGA_FIFO_MAX, /* The distance from MIN to MAX must be at least 10K */ - SVGA_FIFO_NEXT_CMD, + SVGA_FIFO_NEXT, SVGA_FIFO_STOP, /* @@ -546,8 +540,6 @@ static inline void vmsvga_cursor_define(struct vmsvga_state_s *s, } #endif -#define CMD(f) le32_to_cpu(s->cmd->f) - static inline int vmsvga_fifo_length(struct vmsvga_state_s *s) { int num; @@ -555,21 +547,45 @@ static inline int vmsvga_fifo_length(struct vmsvga_state_s *s) if (!s->config || !s->enable) { return 0; } - num = CMD(next_cmd) - CMD(stop); + + s->fifo_min = le32_to_cpu(s->fifo[SVGA_FIFO_MIN]); + s->fifo_max = le32_to_cpu(s->fifo[SVGA_FIFO_MAX]); + s->fifo_next = le32_to_cpu(s->fifo[SVGA_FIFO_NEXT]); + s->fifo_stop = le32_to_cpu(s->fifo[SVGA_FIFO_STOP]); + + /* Check range and alignment. */ + if ((s->fifo_min | s->fifo_max | s->fifo_next | s->fifo_stop) & 3) { + return 0; + } + if (s->fifo_min < sizeof(uint32_t) * 4) { + return 0; + } + if (s->fifo_max > SVGA_FIFO_SIZE || + s->fifo_min >= SVGA_FIFO_SIZE || + s->fifo_stop >= SVGA_FIFO_SIZE || + s->fifo_next >= SVGA_FIFO_SIZE) { + return 0; + } + if (s->fifo_max < s->fifo_min + 10 * 1024) { + return 0; + } + + num = s->fifo_next - s->fifo_stop; if (num < 0) { - num += CMD(max) - CMD(min); + num += s->fifo_max - s->fifo_min; } return num >> 2; } static inline uint32_t vmsvga_fifo_read_raw(struct vmsvga_state_s *s) { - uint32_t cmd = s->fifo[CMD(stop) >> 2]; + uint32_t cmd = s->fifo[s->fifo_stop >> 2]; - s->cmd->stop = cpu_to_le32(CMD(stop) + 4); - if (CMD(stop) >= CMD(max)) { - s->cmd->stop = s->cmd->min; + s->fifo_stop += 4; + if (s->fifo_stop >= s->fifo_max) { + s->fifo_stop = s->fifo_min; } + s->fifo[SVGA_FIFO_STOP] = cpu_to_le32(s->fifo_stop); return cmd; } @@ -581,15 +597,15 @@ static inline uint32_t vmsvga_fifo_read(struct vmsvga_state_s *s) static void vmsvga_fifo_run(struct vmsvga_state_s *s) { uint32_t cmd, colour; - int args, len; + int args, len, maxloop = 1024; int x, y, dx, dy, width, height; struct vmsvga_cursor_definition_s cursor; uint32_t cmd_start; len = vmsvga_fifo_length(s); - while (len > 0) { + while (len > 0 && --maxloop > 0) { /* May need to go back to the start of the command if incomplete */ - cmd_start = s->cmd->stop; + cmd_start = s->fifo_stop; switch (cmd = vmsvga_fifo_read(s)) { case SVGA_CMD_UPDATE: @@ -748,7 +764,8 @@ static void vmsvga_fifo_run(struct vmsvga_state_s *s) break; rewind: - s->cmd->stop = cmd_start; + s->fifo_stop = cmd_start; + s->fifo[SVGA_FIFO_STOP] = cpu_to_le32(s->fifo_stop); break; } } @@ -1005,19 +1022,6 @@ static void vmsvga_value_write(void *opaque, uint32_t address, uint32_t value) case SVGA_REG_CONFIG_DONE: if (value) { s->fifo = (uint32_t *) s->fifo_ptr; - /* Check range and alignment. */ - if ((CMD(min) | CMD(max) | CMD(next_cmd) | CMD(stop)) & 3) { - break; - } - if (CMD(min) < (uint8_t *) s->cmd->fifo - (uint8_t *) s->fifo) { - break; - } - if (CMD(max) > SVGA_FIFO_SIZE) { - break; - } - if (CMD(max) < CMD(min) + 10 * 1024) { - break; - } vga_dirty_log_stop(&s->vga); } s->config = !!value; diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c index 9866dfda5..46b7d5ede 100644 --- a/hw/display/xenfb.c +++ b/hw/display/xenfb.c @@ -25,7 +25,6 @@ */ #include "qemu/osdep.h" -#include <sys/mman.h> #include "hw/hw.h" #include "ui/console.h" @@ -472,9 +471,9 @@ static int xenfb_map_fb(struct XenFB *xenfb) xenfb->pixels = NULL; } - xenfb->fbpages = (xenfb->fb_len + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; + xenfb->fbpages = DIV_ROUND_UP(xenfb->fb_len, XC_PAGE_SIZE); n_fbdirs = xenfb->fbpages * mode / 8; - n_fbdirs = (n_fbdirs + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; + n_fbdirs = DIV_ROUND_UP(n_fbdirs, XC_PAGE_SIZE); pgmfns = g_malloc0(sizeof(xen_pfn_t) * n_fbdirs); fbmfns = g_malloc0(sizeof(xen_pfn_t) * xenfb->fbpages); diff --git a/hw/display/xlnx_dp.c b/hw/display/xlnx_dp.c new file mode 100644 index 000000000..f43eb0930 --- /dev/null +++ b/hw/display/xlnx_dp.c @@ -0,0 +1,1338 @@ +/* + * xlnx_dp.c + * + * Copyright (C) 2015 : GreenSocs Ltd + * http://www.greensocs.com/ , email: info@greensocs.com + * + * Developed by : + * Frederic Konrad <fred.konrad@greensocs.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option)any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/display/xlnx_dp.h" + +#ifndef DEBUG_DP +#define DEBUG_DP 0 +#endif + +#define DPRINTF(fmt, ...) do { \ + if (DEBUG_DP) { \ + qemu_log("xlnx_dp: " fmt , ## __VA_ARGS__); \ + } \ +} while (0); + +/* + * Register offset for DP. + */ +#define DP_LINK_BW_SET (0x0000 >> 2) +#define DP_LANE_COUNT_SET (0x0004 >> 2) +#define DP_ENHANCED_FRAME_EN (0x0008 >> 2) +#define DP_TRAINING_PATTERN_SET (0x000C >> 2) +#define DP_LINK_QUAL_PATTERN_SET (0x0010 >> 2) +#define DP_SCRAMBLING_DISABLE (0x0014 >> 2) +#define DP_DOWNSPREAD_CTRL (0x0018 >> 2) +#define DP_SOFTWARE_RESET (0x001C >> 2) +#define DP_TRANSMITTER_ENABLE (0x0080 >> 2) +#define DP_MAIN_STREAM_ENABLE (0x0084 >> 2) +#define DP_FORCE_SCRAMBLER_RESET (0x00C0 >> 2) +#define DP_VERSION_REGISTER (0x00F8 >> 2) +#define DP_CORE_ID (0x00FC >> 2) + +#define DP_AUX_COMMAND_REGISTER (0x0100 >> 2) +#define AUX_ADDR_ONLY_MASK (0x1000) +#define AUX_COMMAND_MASK (0x0F00) +#define AUX_COMMAND_SHIFT (8) +#define AUX_COMMAND_NBYTES (0x000F) + +#define DP_AUX_WRITE_FIFO (0x0104 >> 2) +#define DP_AUX_ADDRESS (0x0108 >> 2) +#define DP_AUX_CLOCK_DIVIDER (0x010C >> 2) +#define DP_TX_USER_FIFO_OVERFLOW (0x0110 >> 2) +#define DP_INTERRUPT_SIGNAL_STATE (0x0130 >> 2) +#define DP_AUX_REPLY_DATA (0x0134 >> 2) +#define DP_AUX_REPLY_CODE (0x0138 >> 2) +#define DP_AUX_REPLY_COUNT (0x013C >> 2) +#define DP_REPLY_DATA_COUNT (0x0148 >> 2) +#define DP_REPLY_STATUS (0x014C >> 2) +#define DP_HPD_DURATION (0x0150 >> 2) +#define DP_MAIN_STREAM_HTOTAL (0x0180 >> 2) +#define DP_MAIN_STREAM_VTOTAL (0x0184 >> 2) +#define DP_MAIN_STREAM_POLARITY (0x0188 >> 2) +#define DP_MAIN_STREAM_HSWIDTH (0x018C >> 2) +#define DP_MAIN_STREAM_VSWIDTH (0x0190 >> 2) +#define DP_MAIN_STREAM_HRES (0x0194 >> 2) +#define DP_MAIN_STREAM_VRES (0x0198 >> 2) +#define DP_MAIN_STREAM_HSTART (0x019C >> 2) +#define DP_MAIN_STREAM_VSTART (0x01A0 >> 2) +#define DP_MAIN_STREAM_MISC0 (0x01A4 >> 2) +#define DP_MAIN_STREAM_MISC1 (0x01A8 >> 2) +#define DP_MAIN_STREAM_M_VID (0x01AC >> 2) +#define DP_MSA_TRANSFER_UNIT_SIZE (0x01B0 >> 2) +#define DP_MAIN_STREAM_N_VID (0x01B4 >> 2) +#define DP_USER_DATA_COUNT_PER_LANE (0x01BC >> 2) +#define DP_MIN_BYTES_PER_TU (0x01C4 >> 2) +#define DP_FRAC_BYTES_PER_TU (0x01C8 >> 2) +#define DP_INIT_WAIT (0x01CC >> 2) +#define DP_PHY_RESET (0x0200 >> 2) +#define DP_PHY_VOLTAGE_DIFF_LANE_0 (0x0220 >> 2) +#define DP_PHY_VOLTAGE_DIFF_LANE_1 (0x0224 >> 2) +#define DP_TRANSMIT_PRBS7 (0x0230 >> 2) +#define DP_PHY_CLOCK_SELECT (0x0234 >> 2) +#define DP_TX_PHY_POWER_DOWN (0x0238 >> 2) +#define DP_PHY_PRECURSOR_LANE_0 (0x023C >> 2) +#define DP_PHY_PRECURSOR_LANE_1 (0x0240 >> 2) +#define DP_PHY_POSTCURSOR_LANE_0 (0x024C >> 2) +#define DP_PHY_POSTCURSOR_LANE_1 (0x0250 >> 2) +#define DP_PHY_STATUS (0x0280 >> 2) + +#define DP_TX_AUDIO_CONTROL (0x0300 >> 2) +#define DP_TX_AUD_CTRL (1) + +#define DP_TX_AUDIO_CHANNELS (0x0304 >> 2) +#define DP_TX_AUDIO_INFO_DATA(n) ((0x0308 + 4 * n) >> 2) +#define DP_TX_M_AUD (0x0328 >> 2) +#define DP_TX_N_AUD (0x032C >> 2) +#define DP_TX_AUDIO_EXT_DATA(n) ((0x0330 + 4 * n) >> 2) +#define DP_INT_STATUS (0x03A0 >> 2) +#define DP_INT_MASK (0x03A4 >> 2) +#define DP_INT_EN (0x03A8 >> 2) +#define DP_INT_DS (0x03AC >> 2) + +/* + * Registers offset for Audio Video Buffer configuration. + */ +#define V_BLEND_OFFSET (0xA000) +#define V_BLEND_BG_CLR_0 (0x0000 >> 2) +#define V_BLEND_BG_CLR_1 (0x0004 >> 2) +#define V_BLEND_BG_CLR_2 (0x0008 >> 2) +#define V_BLEND_SET_GLOBAL_ALPHA_REG (0x000C >> 2) +#define V_BLEND_OUTPUT_VID_FORMAT (0x0014 >> 2) +#define V_BLEND_LAYER0_CONTROL (0x0018 >> 2) +#define V_BLEND_LAYER1_CONTROL (0x001C >> 2) + +#define V_BLEND_RGB2YCBCR_COEFF(n) ((0x0020 + 4 * n) >> 2) +#define V_BLEND_IN1CSC_COEFF(n) ((0x0044 + 4 * n) >> 2) + +#define V_BLEND_LUMA_IN1CSC_OFFSET (0x0068 >> 2) +#define V_BLEND_CR_IN1CSC_OFFSET (0x006C >> 2) +#define V_BLEND_CB_IN1CSC_OFFSET (0x0070 >> 2) +#define V_BLEND_LUMA_OUTCSC_OFFSET (0x0074 >> 2) +#define V_BLEND_CR_OUTCSC_OFFSET (0x0078 >> 2) +#define V_BLEND_CB_OUTCSC_OFFSET (0x007C >> 2) + +#define V_BLEND_IN2CSC_COEFF(n) ((0x0080 + 4 * n) >> 2) + +#define V_BLEND_LUMA_IN2CSC_OFFSET (0x00A4 >> 2) +#define V_BLEND_CR_IN2CSC_OFFSET (0x00A8 >> 2) +#define V_BLEND_CB_IN2CSC_OFFSET (0x00AC >> 2) +#define V_BLEND_CHROMA_KEY_ENABLE (0x01D0 >> 2) +#define V_BLEND_CHROMA_KEY_COMP1 (0x01D4 >> 2) +#define V_BLEND_CHROMA_KEY_COMP2 (0x01D8 >> 2) +#define V_BLEND_CHROMA_KEY_COMP3 (0x01DC >> 2) + +/* + * Registers offset for Audio Video Buffer configuration. + */ +#define AV_BUF_MANAGER_OFFSET (0xB000) +#define AV_BUF_FORMAT (0x0000 >> 2) +#define AV_BUF_NON_LIVE_LATENCY (0x0008 >> 2) +#define AV_CHBUF0 (0x0010 >> 2) +#define AV_CHBUF1 (0x0014 >> 2) +#define AV_CHBUF2 (0x0018 >> 2) +#define AV_CHBUF3 (0x001C >> 2) +#define AV_CHBUF4 (0x0020 >> 2) +#define AV_CHBUF5 (0x0024 >> 2) +#define AV_BUF_STC_CONTROL (0x002C >> 2) +#define AV_BUF_STC_INIT_VALUE0 (0x0030 >> 2) +#define AV_BUF_STC_INIT_VALUE1 (0x0034 >> 2) +#define AV_BUF_STC_ADJ (0x0038 >> 2) +#define AV_BUF_STC_VIDEO_VSYNC_TS_REG0 (0x003C >> 2) +#define AV_BUF_STC_VIDEO_VSYNC_TS_REG1 (0x0040 >> 2) +#define AV_BUF_STC_EXT_VSYNC_TS_REG0 (0x0044 >> 2) +#define AV_BUF_STC_EXT_VSYNC_TS_REG1 (0x0048 >> 2) +#define AV_BUF_STC_CUSTOM_EVENT_TS_REG0 (0x004C >> 2) +#define AV_BUF_STC_CUSTOM_EVENT_TS_REG1 (0x0050 >> 2) +#define AV_BUF_STC_CUSTOM_EVENT2_TS_REG0 (0x0054 >> 2) +#define AV_BUF_STC_CUSTOM_EVENT2_TS_REG1 (0x0058 >> 2) +#define AV_BUF_STC_SNAPSHOT0 (0x0060 >> 2) +#define AV_BUF_STC_SNAPSHOT1 (0x0064 >> 2) +#define AV_BUF_OUTPUT_AUDIO_VIDEO_SELECT (0x0070 >> 2) +#define AV_BUF_HCOUNT_VCOUNT_INT0 (0x0074 >> 2) +#define AV_BUF_HCOUNT_VCOUNT_INT1 (0x0078 >> 2) +#define AV_BUF_DITHER_CONFIG (0x007C >> 2) +#define AV_BUF_DITHER_CONFIG_MAX (0x008C >> 2) +#define AV_BUF_DITHER_CONFIG_MIN (0x0090 >> 2) +#define AV_BUF_PATTERN_GEN_SELECT (0x0100 >> 2) +#define AV_BUF_AUD_VID_CLK_SOURCE (0x0120 >> 2) +#define AV_BUF_SRST_REG (0x0124 >> 2) +#define AV_BUF_AUDIO_RDY_INTERVAL (0x0128 >> 2) +#define AV_BUF_AUDIO_CH_CONFIG (0x012C >> 2) + +#define AV_BUF_GRAPHICS_COMP_SCALE_FACTOR(n)((0x0200 + 4 * n) >> 2) + +#define AV_BUF_VIDEO_COMP_SCALE_FACTOR(n) ((0x020C + 4 * n) >> 2) + +#define AV_BUF_LIVE_VIDEO_COMP_SF(n) ((0x0218 + 4 * n) >> 2) + +#define AV_BUF_LIVE_VID_CONFIG (0x0224 >> 2) + +#define AV_BUF_LIVE_GFX_COMP_SF(n) ((0x0228 + 4 * n) >> 2) + +#define AV_BUF_LIVE_GFX_CONFIG (0x0234 >> 2) + +#define AUDIO_MIXER_REGISTER_OFFSET (0xC000) +#define AUDIO_MIXER_VOLUME_CONTROL (0x0000 >> 2) +#define AUDIO_MIXER_META_DATA (0x0004 >> 2) +#define AUD_CH_STATUS_REG(n) ((0x0008 + 4 * n) >> 2) +#define AUD_CH_A_DATA_REG(n) ((0x0020 + 4 * n) >> 2) +#define AUD_CH_B_DATA_REG(n) ((0x0038 + 4 * n) >> 2) + +#define DP_AUDIO_DMA_CHANNEL(n) (4 + n) +#define DP_GRAPHIC_DMA_CHANNEL (3) +#define DP_VIDEO_DMA_CHANNEL (0) + +enum DPGraphicFmt { + DP_GRAPHIC_RGBA8888 = 0 << 8, + DP_GRAPHIC_ABGR8888 = 1 << 8, + DP_GRAPHIC_RGB888 = 2 << 8, + DP_GRAPHIC_BGR888 = 3 << 8, + DP_GRAPHIC_RGBA5551 = 4 << 8, + DP_GRAPHIC_RGBA4444 = 5 << 8, + DP_GRAPHIC_RGB565 = 6 << 8, + DP_GRAPHIC_8BPP = 7 << 8, + DP_GRAPHIC_4BPP = 8 << 8, + DP_GRAPHIC_2BPP = 9 << 8, + DP_GRAPHIC_1BPP = 10 << 8, + DP_GRAPHIC_MASK = 0xF << 8 +}; + +enum DPVideoFmt { + DP_NL_VID_CB_Y0_CR_Y1 = 0, + DP_NL_VID_CR_Y0_CB_Y1 = 1, + DP_NL_VID_Y0_CR_Y1_CB = 2, + DP_NL_VID_Y0_CB_Y1_CR = 3, + DP_NL_VID_YV16 = 4, + DP_NL_VID_YV24 = 5, + DP_NL_VID_YV16CL = 6, + DP_NL_VID_MONO = 7, + DP_NL_VID_YV16CL2 = 8, + DP_NL_VID_YUV444 = 9, + DP_NL_VID_RGB888 = 10, + DP_NL_VID_RGBA8880 = 11, + DP_NL_VID_RGB888_10BPC = 12, + DP_NL_VID_YUV444_10BPC = 13, + DP_NL_VID_YV16CL2_10BPC = 14, + DP_NL_VID_YV16CL_10BPC = 15, + DP_NL_VID_YV16_10BPC = 16, + DP_NL_VID_YV24_10BPC = 17, + DP_NL_VID_Y_ONLY_10BPC = 18, + DP_NL_VID_YV16_420 = 19, + DP_NL_VID_YV16CL_420 = 20, + DP_NL_VID_YV16CL2_420 = 21, + DP_NL_VID_YV16_420_10BPC = 22, + DP_NL_VID_YV16CL_420_10BPC = 23, + DP_NL_VID_YV16CL2_420_10BPC = 24, + DP_NL_VID_FMT_MASK = 0x1F +}; + +typedef enum DPGraphicFmt DPGraphicFmt; +typedef enum DPVideoFmt DPVideoFmt; + +static const VMStateDescription vmstate_dp = { + .name = TYPE_XLNX_DP, + .version_id = 1, + .fields = (VMStateField[]){ + VMSTATE_UINT32_ARRAY(core_registers, XlnxDPState, + DP_CORE_REG_ARRAY_SIZE), + VMSTATE_UINT32_ARRAY(avbufm_registers, XlnxDPState, + DP_AVBUF_REG_ARRAY_SIZE), + VMSTATE_UINT32_ARRAY(vblend_registers, XlnxDPState, + DP_VBLEND_REG_ARRAY_SIZE), + VMSTATE_UINT32_ARRAY(audio_registers, XlnxDPState, + DP_AUDIO_REG_ARRAY_SIZE), + VMSTATE_END_OF_LIST() + } +}; + +static void xlnx_dp_update_irq(XlnxDPState *s); + +static uint64_t xlnx_dp_audio_read(void *opaque, hwaddr offset, unsigned size) +{ + XlnxDPState *s = XLNX_DP(opaque); + + offset = offset >> 2; + return s->audio_registers[offset]; +} + +static void xlnx_dp_audio_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + XlnxDPState *s = XLNX_DP(opaque); + + offset = offset >> 2; + + switch (offset) { + case AUDIO_MIXER_META_DATA: + s->audio_registers[offset] = value & 0x00000001; + break; + default: + s->audio_registers[offset] = value; + break; + } +} + +static const MemoryRegionOps audio_ops = { + .read = xlnx_dp_audio_read, + .write = xlnx_dp_audio_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static inline uint32_t xlnx_dp_audio_get_volume(XlnxDPState *s, + uint8_t channel) +{ + switch (channel) { + case 0: + return extract32(s->audio_registers[AUDIO_MIXER_VOLUME_CONTROL], 0, 16); + case 1: + return extract32(s->audio_registers[AUDIO_MIXER_VOLUME_CONTROL], 16, + 16); + default: + return 0; + } +} + +static inline void xlnx_dp_audio_activate(XlnxDPState *s) +{ + bool activated = ((s->core_registers[DP_TX_AUDIO_CONTROL] + & DP_TX_AUD_CTRL) != 0); + AUD_set_active_out(s->amixer_output_stream, activated); + xlnx_dpdma_set_host_data_location(s->dpdma, DP_AUDIO_DMA_CHANNEL(0), + &s->audio_buffer_0); + xlnx_dpdma_set_host_data_location(s->dpdma, DP_AUDIO_DMA_CHANNEL(1), + &s->audio_buffer_1); +} + +static inline void xlnx_dp_audio_mix_buffer(XlnxDPState *s) +{ + /* + * Audio packets are signed and have this shape: + * | 16 | 16 | 16 | 16 | 16 | 16 | 16 | 16 | + * | R3 | L3 | R2 | L2 | R1 | L1 | R0 | L0 | + * + * Output audio is 16bits saturated. + */ + int i; + + if ((s->audio_data_available[0]) && (xlnx_dp_audio_get_volume(s, 0))) { + for (i = 0; i < s->audio_data_available[0] / 2; i++) { + s->temp_buffer[i] = (int64_t)(s->audio_buffer_0[i]) + * xlnx_dp_audio_get_volume(s, 0) / 8192; + } + s->byte_left = s->audio_data_available[0]; + } else { + memset(s->temp_buffer, 0, s->audio_data_available[1] / 2); + } + + if ((s->audio_data_available[1]) && (xlnx_dp_audio_get_volume(s, 1))) { + if ((s->audio_data_available[0] == 0) + || (s->audio_data_available[1] == s->audio_data_available[0])) { + for (i = 0; i < s->audio_data_available[1] / 2; i++) { + s->temp_buffer[i] += (int64_t)(s->audio_buffer_1[i]) + * xlnx_dp_audio_get_volume(s, 1) / 8192; + } + s->byte_left = s->audio_data_available[1]; + } + } + + for (i = 0; i < s->byte_left / 2; i++) { + s->out_buffer[i] = MAX(-32767, MIN(s->temp_buffer[i], 32767)); + } + + s->data_ptr = 0; +} + +static void xlnx_dp_audio_callback(void *opaque, int avail) +{ + /* + * Get some data from the DPDMA and compute these datas. + * Then wait for QEMU's audio subsystem to call this callback. + */ + XlnxDPState *s = XLNX_DP(opaque); + size_t written = 0; + + /* If there are already some data don't get more data. */ + if (s->byte_left == 0) { + s->audio_data_available[0] = xlnx_dpdma_start_operation(s->dpdma, 4, + true); + s->audio_data_available[1] = xlnx_dpdma_start_operation(s->dpdma, 5, + true); + xlnx_dp_audio_mix_buffer(s); + } + + /* Send the buffer through the audio. */ + if (s->byte_left <= MAX_QEMU_BUFFER_SIZE) { + if (s->byte_left != 0) { + written = AUD_write(s->amixer_output_stream, + &s->out_buffer[s->data_ptr], s->byte_left); + } else { + /* + * There is nothing to play.. We don't have any data! Fill the + * buffer with zero's and send it. + */ + written = 0; + memset(s->out_buffer, 0, 1024); + AUD_write(s->amixer_output_stream, s->out_buffer, 1024); + } + } else { + written = AUD_write(s->amixer_output_stream, + &s->out_buffer[s->data_ptr], MAX_QEMU_BUFFER_SIZE); + } + s->byte_left -= written; + s->data_ptr += written; +} + +/* + * AUX channel related function. + */ +static void xlnx_dp_aux_clear_rx_fifo(XlnxDPState *s) +{ + fifo8_reset(&s->rx_fifo); +} + +static void xlnx_dp_aux_push_rx_fifo(XlnxDPState *s, uint8_t *buf, size_t len) +{ + DPRINTF("Push %u data in rx_fifo\n", (unsigned)len); + fifo8_push_all(&s->rx_fifo, buf, len); +} + +static uint8_t xlnx_dp_aux_pop_rx_fifo(XlnxDPState *s) +{ + uint8_t ret; + + if (fifo8_is_empty(&s->rx_fifo)) { + DPRINTF("rx_fifo underflow..\n"); + abort(); + } + ret = fifo8_pop(&s->rx_fifo); + DPRINTF("pop 0x%" PRIX8 " from rx_fifo.\n", ret); + return ret; +} + +static void xlnx_dp_aux_clear_tx_fifo(XlnxDPState *s) +{ + fifo8_reset(&s->tx_fifo); +} + +static void xlnx_dp_aux_push_tx_fifo(XlnxDPState *s, uint8_t *buf, size_t len) +{ + DPRINTF("Push %u data in tx_fifo\n", (unsigned)len); + fifo8_push_all(&s->tx_fifo, buf, len); +} + +static uint8_t xlnx_dp_aux_pop_tx_fifo(XlnxDPState *s) +{ + uint8_t ret; + + if (fifo8_is_empty(&s->tx_fifo)) { + DPRINTF("tx_fifo underflow..\n"); + abort(); + } + ret = fifo8_pop(&s->tx_fifo); + DPRINTF("pop 0x%2.2X from tx_fifo.\n", ret); + return ret; +} + +static uint32_t xlnx_dp_aux_get_address(XlnxDPState *s) +{ + return s->core_registers[DP_AUX_ADDRESS]; +} + +/* + * Get command from the register. + */ +static void xlnx_dp_aux_set_command(XlnxDPState *s, uint32_t value) +{ + bool address_only = (value & AUX_ADDR_ONLY_MASK) != 0; + AUXCommand cmd = (value & AUX_COMMAND_MASK) >> AUX_COMMAND_SHIFT; + uint8_t nbytes = (value & AUX_COMMAND_NBYTES) + 1; + uint8_t buf[16]; + int i; + + /* + * When an address_only command is executed nothing happen to the fifo, so + * just make nbytes = 0. + */ + if (address_only) { + nbytes = 0; + } + + switch (cmd) { + case READ_AUX: + case READ_I2C: + case READ_I2C_MOT: + s->core_registers[DP_AUX_REPLY_CODE] = aux_request(s->aux_bus, cmd, + xlnx_dp_aux_get_address(s), + nbytes, buf); + s->core_registers[DP_REPLY_DATA_COUNT] = nbytes; + + if (s->core_registers[DP_AUX_REPLY_CODE] == AUX_I2C_ACK) { + xlnx_dp_aux_push_rx_fifo(s, buf, nbytes); + } + break; + case WRITE_AUX: + case WRITE_I2C: + case WRITE_I2C_MOT: + for (i = 0; i < nbytes; i++) { + buf[i] = xlnx_dp_aux_pop_tx_fifo(s); + } + s->core_registers[DP_AUX_REPLY_CODE] = aux_request(s->aux_bus, cmd, + xlnx_dp_aux_get_address(s), + nbytes, buf); + xlnx_dp_aux_clear_tx_fifo(s); + break; + case WRITE_I2C_STATUS: + qemu_log_mask(LOG_UNIMP, "xlnx_dp: Write i2c status not implemented\n"); + break; + default: + abort(); + } + + s->core_registers[DP_INTERRUPT_SIGNAL_STATE] |= 0x04; +} + +static void xlnx_dp_set_dpdma(Object *obj, const char *name, Object *val, + Error **errp) +{ + XlnxDPState *s = XLNX_DP(obj); + if (s->console) { + DisplaySurface *surface = qemu_console_surface(s->console); + XlnxDPDMAState *dma = XLNX_DPDMA(val); + xlnx_dpdma_set_host_data_location(dma, DP_GRAPHIC_DMA_CHANNEL, + surface_data(surface)); + } +} + +static inline uint8_t xlnx_dp_global_alpha_value(XlnxDPState *s) +{ + return (s->vblend_registers[V_BLEND_SET_GLOBAL_ALPHA_REG] & 0x1FE) >> 1; +} + +static inline bool xlnx_dp_global_alpha_enabled(XlnxDPState *s) +{ + /* + * If the alpha is totally opaque (255) we consider the alpha is disabled to + * reduce CPU consumption. + */ + return ((xlnx_dp_global_alpha_value(s) != 0xFF) && + ((s->vblend_registers[V_BLEND_SET_GLOBAL_ALPHA_REG] & 0x01) != 0)); +} + +static void xlnx_dp_recreate_surface(XlnxDPState *s) +{ + /* + * Two possibilities, if blending is enabled the console displays + * bout_plane, if not g_plane is displayed. + */ + uint16_t width = s->core_registers[DP_MAIN_STREAM_HRES]; + uint16_t height = s->core_registers[DP_MAIN_STREAM_VRES]; + DisplaySurface *current_console_surface = qemu_console_surface(s->console); + + if ((width != 0) && (height != 0)) { + /* + * As dpy_gfx_replace_surface calls qemu_free_displaysurface on the + * surface we need to be carefull and don't free the surface associated + * to the console or double free will happen. + */ + if (s->bout_plane.surface != current_console_surface) { + qemu_free_displaysurface(s->bout_plane.surface); + } + if (s->v_plane.surface != current_console_surface) { + qemu_free_displaysurface(s->v_plane.surface); + } + if (s->g_plane.surface != current_console_surface) { + qemu_free_displaysurface(s->g_plane.surface); + } + + s->g_plane.surface + = qemu_create_displaysurface_from(width, height, + s->g_plane.format, 0, NULL); + s->v_plane.surface + = qemu_create_displaysurface_from(width, height, + s->v_plane.format, 0, NULL); + if (xlnx_dp_global_alpha_enabled(s)) { + s->bout_plane.surface = + qemu_create_displaysurface_from(width, + height, + s->g_plane.format, + 0, NULL); + dpy_gfx_replace_surface(s->console, s->bout_plane.surface); + } else { + s->bout_plane.surface = NULL; + dpy_gfx_replace_surface(s->console, s->g_plane.surface); + } + + xlnx_dpdma_set_host_data_location(s->dpdma, DP_GRAPHIC_DMA_CHANNEL, + surface_data(s->g_plane.surface)); + xlnx_dpdma_set_host_data_location(s->dpdma, DP_VIDEO_DMA_CHANNEL, + surface_data(s->v_plane.surface)); + } +} + +/* + * Change the graphic format of the surface. + */ +static void xlnx_dp_change_graphic_fmt(XlnxDPState *s) +{ + switch (s->avbufm_registers[AV_BUF_FORMAT] & DP_GRAPHIC_MASK) { + case DP_GRAPHIC_RGBA8888: + s->g_plane.format = PIXMAN_r8g8b8a8; + break; + case DP_GRAPHIC_ABGR8888: + s->g_plane.format = PIXMAN_a8b8g8r8; + break; + case DP_GRAPHIC_RGB565: + s->g_plane.format = PIXMAN_r5g6b5; + break; + case DP_GRAPHIC_RGB888: + s->g_plane.format = PIXMAN_r8g8b8; + break; + case DP_GRAPHIC_BGR888: + s->g_plane.format = PIXMAN_b8g8r8; + break; + default: + DPRINTF("error: unsupported graphic format %u.\n", + s->avbufm_registers[AV_BUF_FORMAT] & DP_GRAPHIC_MASK); + abort(); + } + + switch (s->avbufm_registers[AV_BUF_FORMAT] & DP_NL_VID_FMT_MASK) { + case 0: + s->v_plane.format = PIXMAN_x8b8g8r8; + break; + case DP_NL_VID_RGBA8880: + s->v_plane.format = PIXMAN_x8b8g8r8; + break; + default: + DPRINTF("error: unsupported video format %u.\n", + s->avbufm_registers[AV_BUF_FORMAT] & DP_NL_VID_FMT_MASK); + abort(); + } + + xlnx_dp_recreate_surface(s); +} + +static void xlnx_dp_update_irq(XlnxDPState *s) +{ + uint32_t flags; + + flags = s->core_registers[DP_INT_STATUS] & ~s->core_registers[DP_INT_MASK]; + DPRINTF("update IRQ value = %" PRIx32 "\n", flags); + qemu_set_irq(s->irq, flags != 0); +} + +static uint64_t xlnx_dp_read(void *opaque, hwaddr offset, unsigned size) +{ + XlnxDPState *s = XLNX_DP(opaque); + uint64_t ret = 0; + + offset = offset >> 2; + + switch (offset) { + case DP_TX_USER_FIFO_OVERFLOW: + /* This register is cleared after a read */ + ret = s->core_registers[DP_TX_USER_FIFO_OVERFLOW]; + s->core_registers[DP_TX_USER_FIFO_OVERFLOW] = 0; + break; + case DP_AUX_REPLY_DATA: + ret = xlnx_dp_aux_pop_rx_fifo(s); + break; + case DP_INTERRUPT_SIGNAL_STATE: + /* + * XXX: Not sure it is the right thing to do actually. + * The register is not written by the device driver so it's stuck + * to 0x04. + */ + ret = s->core_registers[DP_INTERRUPT_SIGNAL_STATE]; + s->core_registers[DP_INTERRUPT_SIGNAL_STATE] &= ~0x04; + break; + case DP_AUX_WRITE_FIFO: + case DP_TX_AUDIO_INFO_DATA(0): + case DP_TX_AUDIO_INFO_DATA(1): + case DP_TX_AUDIO_INFO_DATA(2): + case DP_TX_AUDIO_INFO_DATA(3): + case DP_TX_AUDIO_INFO_DATA(4): + case DP_TX_AUDIO_INFO_DATA(5): + case DP_TX_AUDIO_INFO_DATA(6): + case DP_TX_AUDIO_INFO_DATA(7): + case DP_TX_AUDIO_EXT_DATA(0): + case DP_TX_AUDIO_EXT_DATA(1): + case DP_TX_AUDIO_EXT_DATA(2): + case DP_TX_AUDIO_EXT_DATA(3): + case DP_TX_AUDIO_EXT_DATA(4): + case DP_TX_AUDIO_EXT_DATA(5): + case DP_TX_AUDIO_EXT_DATA(6): + case DP_TX_AUDIO_EXT_DATA(7): + case DP_TX_AUDIO_EXT_DATA(8): + /* write only registers */ + ret = 0; + break; + default: + assert(offset <= (0x3AC >> 2)); + ret = s->core_registers[offset]; + break; + } + + DPRINTF("core read @%" PRIx64 " = 0x%8.8" PRIX64 "\n", offset << 2, ret); + return ret; +} + +static void xlnx_dp_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + XlnxDPState *s = XLNX_DP(opaque); + + DPRINTF("core write @%" PRIx64 " = 0x%8.8" PRIX64 "\n", offset, value); + + offset = offset >> 2; + + switch (offset) { + /* + * Only special write case are handled. + */ + case DP_LINK_BW_SET: + s->core_registers[offset] = value & 0x000000FF; + break; + case DP_LANE_COUNT_SET: + case DP_MAIN_STREAM_MISC0: + s->core_registers[offset] = value & 0x0000000F; + break; + case DP_TRAINING_PATTERN_SET: + case DP_LINK_QUAL_PATTERN_SET: + case DP_MAIN_STREAM_POLARITY: + case DP_PHY_VOLTAGE_DIFF_LANE_0: + case DP_PHY_VOLTAGE_DIFF_LANE_1: + s->core_registers[offset] = value & 0x00000003; + break; + case DP_ENHANCED_FRAME_EN: + case DP_SCRAMBLING_DISABLE: + case DP_DOWNSPREAD_CTRL: + case DP_MAIN_STREAM_ENABLE: + case DP_TRANSMIT_PRBS7: + s->core_registers[offset] = value & 0x00000001; + break; + case DP_PHY_CLOCK_SELECT: + s->core_registers[offset] = value & 0x00000007; + break; + case DP_SOFTWARE_RESET: + /* + * No need to update this bit as it's read '0'. + */ + /* + * TODO: reset IP. + */ + break; + case DP_TRANSMITTER_ENABLE: + s->core_registers[offset] = value & 0x01; + break; + case DP_FORCE_SCRAMBLER_RESET: + /* + * No need to update this bit as it's read '0'. + */ + /* + * TODO: force a scrambler reset?? + */ + break; + case DP_AUX_COMMAND_REGISTER: + s->core_registers[offset] = value & 0x00001F0F; + xlnx_dp_aux_set_command(s, s->core_registers[offset]); + break; + case DP_MAIN_STREAM_HTOTAL: + case DP_MAIN_STREAM_VTOTAL: + case DP_MAIN_STREAM_HSTART: + case DP_MAIN_STREAM_VSTART: + s->core_registers[offset] = value & 0x0000FFFF; + break; + case DP_MAIN_STREAM_HRES: + case DP_MAIN_STREAM_VRES: + s->core_registers[offset] = value & 0x0000FFFF; + xlnx_dp_recreate_surface(s); + break; + case DP_MAIN_STREAM_HSWIDTH: + case DP_MAIN_STREAM_VSWIDTH: + s->core_registers[offset] = value & 0x00007FFF; + break; + case DP_MAIN_STREAM_MISC1: + s->core_registers[offset] = value & 0x00000086; + break; + case DP_MAIN_STREAM_M_VID: + case DP_MAIN_STREAM_N_VID: + s->core_registers[offset] = value & 0x00FFFFFF; + break; + case DP_MSA_TRANSFER_UNIT_SIZE: + case DP_MIN_BYTES_PER_TU: + case DP_INIT_WAIT: + s->core_registers[offset] = value & 0x00000007; + break; + case DP_USER_DATA_COUNT_PER_LANE: + s->core_registers[offset] = value & 0x0003FFFF; + break; + case DP_FRAC_BYTES_PER_TU: + s->core_registers[offset] = value & 0x000003FF; + break; + case DP_PHY_RESET: + s->core_registers[offset] = value & 0x00010003; + /* + * TODO: Reset something? + */ + break; + case DP_TX_PHY_POWER_DOWN: + s->core_registers[offset] = value & 0x0000000F; + /* + * TODO: Power down things? + */ + break; + case DP_AUX_WRITE_FIFO: { + uint8_t c = value; + xlnx_dp_aux_push_tx_fifo(s, &c, 1); + break; + } + case DP_AUX_CLOCK_DIVIDER: + break; + case DP_AUX_REPLY_COUNT: + /* + * Writing to this register clear the counter. + */ + s->core_registers[offset] = 0x00000000; + break; + case DP_AUX_ADDRESS: + s->core_registers[offset] = value & 0x000FFFFF; + break; + case DP_VERSION_REGISTER: + case DP_CORE_ID: + case DP_TX_USER_FIFO_OVERFLOW: + case DP_AUX_REPLY_DATA: + case DP_AUX_REPLY_CODE: + case DP_REPLY_DATA_COUNT: + case DP_REPLY_STATUS: + case DP_HPD_DURATION: + /* + * Write to read only location.. + */ + break; + case DP_TX_AUDIO_CONTROL: + s->core_registers[offset] = value & 0x00000001; + xlnx_dp_audio_activate(s); + break; + case DP_TX_AUDIO_CHANNELS: + s->core_registers[offset] = value & 0x00000007; + xlnx_dp_audio_activate(s); + break; + case DP_INT_STATUS: + s->core_registers[DP_INT_STATUS] &= ~value; + xlnx_dp_update_irq(s); + break; + case DP_INT_EN: + s->core_registers[DP_INT_MASK] &= ~value; + xlnx_dp_update_irq(s); + break; + case DP_INT_DS: + s->core_registers[DP_INT_MASK] |= ~value; + xlnx_dp_update_irq(s); + break; + default: + assert(offset <= (0x504C >> 2)); + s->core_registers[offset] = value; + break; + } +} + +static const MemoryRegionOps dp_ops = { + .read = xlnx_dp_read, + .write = xlnx_dp_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +/* + * This is to handle Read/Write to the Video Blender. + */ +static void xlnx_dp_vblend_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + XlnxDPState *s = XLNX_DP(opaque); + bool alpha_was_enabled; + + DPRINTF("vblend: write @0x%" HWADDR_PRIX " = 0x%" PRIX32 "\n", offset, + (uint32_t)value); + offset = offset >> 2; + + switch (offset) { + case V_BLEND_BG_CLR_0: + case V_BLEND_BG_CLR_1: + case V_BLEND_BG_CLR_2: + s->vblend_registers[offset] = value & 0x00000FFF; + break; + case V_BLEND_SET_GLOBAL_ALPHA_REG: + /* + * A write to this register can enable or disable blending. Thus we need + * to recreate the surfaces. + */ + alpha_was_enabled = xlnx_dp_global_alpha_enabled(s); + s->vblend_registers[offset] = value & 0x000001FF; + if (xlnx_dp_global_alpha_enabled(s) != alpha_was_enabled) { + xlnx_dp_recreate_surface(s); + } + break; + case V_BLEND_OUTPUT_VID_FORMAT: + s->vblend_registers[offset] = value & 0x00000017; + break; + case V_BLEND_LAYER0_CONTROL: + case V_BLEND_LAYER1_CONTROL: + s->vblend_registers[offset] = value & 0x00000103; + break; + case V_BLEND_RGB2YCBCR_COEFF(0): + case V_BLEND_RGB2YCBCR_COEFF(1): + case V_BLEND_RGB2YCBCR_COEFF(2): + case V_BLEND_RGB2YCBCR_COEFF(3): + case V_BLEND_RGB2YCBCR_COEFF(4): + case V_BLEND_RGB2YCBCR_COEFF(5): + case V_BLEND_RGB2YCBCR_COEFF(6): + case V_BLEND_RGB2YCBCR_COEFF(7): + case V_BLEND_RGB2YCBCR_COEFF(8): + case V_BLEND_IN1CSC_COEFF(0): + case V_BLEND_IN1CSC_COEFF(1): + case V_BLEND_IN1CSC_COEFF(2): + case V_BLEND_IN1CSC_COEFF(3): + case V_BLEND_IN1CSC_COEFF(4): + case V_BLEND_IN1CSC_COEFF(5): + case V_BLEND_IN1CSC_COEFF(6): + case V_BLEND_IN1CSC_COEFF(7): + case V_BLEND_IN1CSC_COEFF(8): + case V_BLEND_IN2CSC_COEFF(0): + case V_BLEND_IN2CSC_COEFF(1): + case V_BLEND_IN2CSC_COEFF(2): + case V_BLEND_IN2CSC_COEFF(3): + case V_BLEND_IN2CSC_COEFF(4): + case V_BLEND_IN2CSC_COEFF(5): + case V_BLEND_IN2CSC_COEFF(6): + case V_BLEND_IN2CSC_COEFF(7): + case V_BLEND_IN2CSC_COEFF(8): + s->vblend_registers[offset] = value & 0x0000FFFF; + break; + case V_BLEND_LUMA_IN1CSC_OFFSET: + case V_BLEND_CR_IN1CSC_OFFSET: + case V_BLEND_CB_IN1CSC_OFFSET: + case V_BLEND_LUMA_IN2CSC_OFFSET: + case V_BLEND_CR_IN2CSC_OFFSET: + case V_BLEND_CB_IN2CSC_OFFSET: + case V_BLEND_LUMA_OUTCSC_OFFSET: + case V_BLEND_CR_OUTCSC_OFFSET: + case V_BLEND_CB_OUTCSC_OFFSET: + s->vblend_registers[offset] = value & 0x3FFF7FFF; + break; + case V_BLEND_CHROMA_KEY_ENABLE: + s->vblend_registers[offset] = value & 0x00000003; + break; + case V_BLEND_CHROMA_KEY_COMP1: + case V_BLEND_CHROMA_KEY_COMP2: + case V_BLEND_CHROMA_KEY_COMP3: + s->vblend_registers[offset] = value & 0x0FFF0FFF; + break; + default: + s->vblend_registers[offset] = value; + break; + } +} + +static uint64_t xlnx_dp_vblend_read(void *opaque, hwaddr offset, + unsigned size) +{ + XlnxDPState *s = XLNX_DP(opaque); + + DPRINTF("vblend: read @0x%" HWADDR_PRIX " = 0x%" PRIX32 "\n", offset, + s->vblend_registers[offset >> 2]); + return s->vblend_registers[offset >> 2]; +} + +static const MemoryRegionOps vblend_ops = { + .read = xlnx_dp_vblend_read, + .write = xlnx_dp_vblend_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +/* + * This is to handle Read/Write to the Audio Video buffer manager. + */ +static void xlnx_dp_avbufm_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + XlnxDPState *s = XLNX_DP(opaque); + + DPRINTF("avbufm: write @0x%" HWADDR_PRIX " = 0x%" PRIX32 "\n", offset, + (uint32_t)value); + offset = offset >> 2; + + switch (offset) { + case AV_BUF_FORMAT: + s->avbufm_registers[offset] = value & 0x00000FFF; + xlnx_dp_change_graphic_fmt(s); + break; + case AV_CHBUF0: + case AV_CHBUF1: + case AV_CHBUF2: + case AV_CHBUF3: + case AV_CHBUF4: + case AV_CHBUF5: + s->avbufm_registers[offset] = value & 0x0000007F; + break; + case AV_BUF_OUTPUT_AUDIO_VIDEO_SELECT: + s->avbufm_registers[offset] = value & 0x0000007F; + break; + case AV_BUF_DITHER_CONFIG: + s->avbufm_registers[offset] = value & 0x000007FF; + break; + case AV_BUF_DITHER_CONFIG_MAX: + case AV_BUF_DITHER_CONFIG_MIN: + s->avbufm_registers[offset] = value & 0x00000FFF; + break; + case AV_BUF_PATTERN_GEN_SELECT: + s->avbufm_registers[offset] = value & 0xFFFFFF03; + break; + case AV_BUF_AUD_VID_CLK_SOURCE: + s->avbufm_registers[offset] = value & 0x00000007; + break; + case AV_BUF_SRST_REG: + s->avbufm_registers[offset] = value & 0x00000002; + break; + case AV_BUF_AUDIO_CH_CONFIG: + s->avbufm_registers[offset] = value & 0x00000003; + break; + case AV_BUF_GRAPHICS_COMP_SCALE_FACTOR(0): + case AV_BUF_GRAPHICS_COMP_SCALE_FACTOR(1): + case AV_BUF_GRAPHICS_COMP_SCALE_FACTOR(2): + case AV_BUF_VIDEO_COMP_SCALE_FACTOR(0): + case AV_BUF_VIDEO_COMP_SCALE_FACTOR(1): + case AV_BUF_VIDEO_COMP_SCALE_FACTOR(2): + s->avbufm_registers[offset] = value & 0x0000FFFF; + break; + case AV_BUF_LIVE_VIDEO_COMP_SF(0): + case AV_BUF_LIVE_VIDEO_COMP_SF(1): + case AV_BUF_LIVE_VIDEO_COMP_SF(2): + case AV_BUF_LIVE_VID_CONFIG: + case AV_BUF_LIVE_GFX_COMP_SF(0): + case AV_BUF_LIVE_GFX_COMP_SF(1): + case AV_BUF_LIVE_GFX_COMP_SF(2): + case AV_BUF_LIVE_GFX_CONFIG: + case AV_BUF_NON_LIVE_LATENCY: + case AV_BUF_STC_CONTROL: + case AV_BUF_STC_INIT_VALUE0: + case AV_BUF_STC_INIT_VALUE1: + case AV_BUF_STC_ADJ: + case AV_BUF_STC_VIDEO_VSYNC_TS_REG0: + case AV_BUF_STC_VIDEO_VSYNC_TS_REG1: + case AV_BUF_STC_EXT_VSYNC_TS_REG0: + case AV_BUF_STC_EXT_VSYNC_TS_REG1: + case AV_BUF_STC_CUSTOM_EVENT_TS_REG0: + case AV_BUF_STC_CUSTOM_EVENT_TS_REG1: + case AV_BUF_STC_CUSTOM_EVENT2_TS_REG0: + case AV_BUF_STC_CUSTOM_EVENT2_TS_REG1: + case AV_BUF_STC_SNAPSHOT0: + case AV_BUF_STC_SNAPSHOT1: + case AV_BUF_HCOUNT_VCOUNT_INT0: + case AV_BUF_HCOUNT_VCOUNT_INT1: + qemu_log_mask(LOG_UNIMP, "avbufm: unimplmented"); + break; + default: + s->avbufm_registers[offset] = value; + break; + } +} + +static uint64_t xlnx_dp_avbufm_read(void *opaque, hwaddr offset, + unsigned size) +{ + XlnxDPState *s = XLNX_DP(opaque); + + offset = offset >> 2; + return s->avbufm_registers[offset]; +} + +static const MemoryRegionOps avbufm_ops = { + .read = xlnx_dp_avbufm_read, + .write = xlnx_dp_avbufm_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +/* + * This is a global alpha blending using pixman. + * Both graphic and video planes are multiplied with the global alpha + * coefficient and added. + */ +static inline void xlnx_dp_blend_surface(XlnxDPState *s) +{ + pixman_fixed_t alpha1[] = { pixman_double_to_fixed(1), + pixman_double_to_fixed(1), + pixman_double_to_fixed(1.0) }; + pixman_fixed_t alpha2[] = { pixman_double_to_fixed(1), + pixman_double_to_fixed(1), + pixman_double_to_fixed(1.0) }; + + if ((surface_width(s->g_plane.surface) + != surface_width(s->v_plane.surface)) || + (surface_height(s->g_plane.surface) + != surface_height(s->v_plane.surface))) { + return; + } + + alpha1[2] = pixman_double_to_fixed((double)(xlnx_dp_global_alpha_value(s)) + / 256.0); + alpha2[2] = pixman_double_to_fixed((255.0 + - (double)xlnx_dp_global_alpha_value(s)) + / 256.0); + + pixman_image_set_filter(s->g_plane.surface->image, + PIXMAN_FILTER_CONVOLUTION, alpha1, 3); + pixman_image_composite(PIXMAN_OP_SRC, s->g_plane.surface->image, 0, + s->bout_plane.surface->image, 0, 0, 0, 0, 0, 0, + surface_width(s->g_plane.surface), + surface_height(s->g_plane.surface)); + pixman_image_set_filter(s->v_plane.surface->image, + PIXMAN_FILTER_CONVOLUTION, alpha2, 3); + pixman_image_composite(PIXMAN_OP_ADD, s->v_plane.surface->image, 0, + s->bout_plane.surface->image, 0, 0, 0, 0, 0, 0, + surface_width(s->g_plane.surface), + surface_height(s->g_plane.surface)); +} + +static void xlnx_dp_update_display(void *opaque) +{ + XlnxDPState *s = XLNX_DP(opaque); + + if ((s->core_registers[DP_TRANSMITTER_ENABLE] & 0x01) == 0) { + return; + } + + s->core_registers[DP_INT_STATUS] |= (1 << 13); + xlnx_dp_update_irq(s); + + xlnx_dpdma_trigger_vsync_irq(s->dpdma); + + /* + * Trigger the DMA channel. + */ + if (!xlnx_dpdma_start_operation(s->dpdma, 3, false)) { + /* + * An error occured don't do anything with the data.. + * Trigger an underflow interrupt. + */ + s->core_registers[DP_INT_STATUS] |= (1 << 21); + xlnx_dp_update_irq(s); + return; + } + + if (xlnx_dp_global_alpha_enabled(s)) { + if (!xlnx_dpdma_start_operation(s->dpdma, 0, false)) { + s->core_registers[DP_INT_STATUS] |= (1 << 21); + xlnx_dp_update_irq(s); + return; + } + xlnx_dp_blend_surface(s); + } + + /* + * XXX: We might want to update only what changed. + */ + dpy_gfx_update(s->console, 0, 0, surface_width(s->g_plane.surface), + surface_height(s->g_plane.surface)); +} + +static const GraphicHwOps xlnx_dp_gfx_ops = { + .gfx_update = xlnx_dp_update_display, +}; + +static void xlnx_dp_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + XlnxDPState *s = XLNX_DP(obj); + + memory_region_init(&s->container, obj, TYPE_XLNX_DP, 0xC050); + + memory_region_init_io(&s->core_iomem, obj, &dp_ops, s, TYPE_XLNX_DP + ".core", 0x3AF); + memory_region_add_subregion(&s->container, 0x0000, &s->core_iomem); + + memory_region_init_io(&s->vblend_iomem, obj, &vblend_ops, s, TYPE_XLNX_DP + ".v_blend", 0x1DF); + memory_region_add_subregion(&s->container, 0xA000, &s->vblend_iomem); + + memory_region_init_io(&s->avbufm_iomem, obj, &avbufm_ops, s, TYPE_XLNX_DP + ".av_buffer_manager", 0x238); + memory_region_add_subregion(&s->container, 0xB000, &s->avbufm_iomem); + + memory_region_init_io(&s->audio_iomem, obj, &audio_ops, s, TYPE_XLNX_DP + ".audio", sizeof(s->audio_registers)); + memory_region_add_subregion(&s->container, 0xC000, &s->audio_iomem); + + sysbus_init_mmio(sbd, &s->container); + sysbus_init_irq(sbd, &s->irq); + + object_property_add_link(obj, "dpdma", TYPE_XLNX_DPDMA, + (Object **) &s->dpdma, + xlnx_dp_set_dpdma, + OBJ_PROP_LINK_UNREF_ON_RELEASE, + &error_abort); + + /* + * Initialize AUX Bus. + */ + s->aux_bus = aux_init_bus(DEVICE(obj), "aux"); + + /* + * Initialize DPCD and EDID.. + */ + s->dpcd = DPCD(aux_create_slave(s->aux_bus, "dpcd", 0x00000)); + s->edid = I2CDDC(qdev_create(BUS(aux_get_i2c_bus(s->aux_bus)), "i2c-ddc")); + i2c_set_slave_address(I2C_SLAVE(s->edid), 0x50); + + fifo8_create(&s->rx_fifo, 16); + fifo8_create(&s->tx_fifo, 16); +} + +static void xlnx_dp_realize(DeviceState *dev, Error **errp) +{ + XlnxDPState *s = XLNX_DP(dev); + DisplaySurface *surface; + struct audsettings as; + + s->console = graphic_console_init(dev, 0, &xlnx_dp_gfx_ops, s); + surface = qemu_console_surface(s->console); + xlnx_dpdma_set_host_data_location(s->dpdma, DP_GRAPHIC_DMA_CHANNEL, + surface_data(surface)); + + as.freq = 44100; + as.nchannels = 2; + as.fmt = AUD_FMT_S16; + as.endianness = 0; + + AUD_register_card("xlnx_dp.audio", &s->aud_card); + + s->amixer_output_stream = AUD_open_out(&s->aud_card, + s->amixer_output_stream, + "xlnx_dp.audio.out", + s, + xlnx_dp_audio_callback, + &as); + AUD_set_volume_out(s->amixer_output_stream, 0, 255, 255); + xlnx_dp_audio_activate(s); +} + +static void xlnx_dp_reset(DeviceState *dev) +{ + XlnxDPState *s = XLNX_DP(dev); + + memset(s->core_registers, 0, sizeof(s->core_registers)); + s->core_registers[DP_VERSION_REGISTER] = 0x04010000; + s->core_registers[DP_CORE_ID] = 0x01020000; + s->core_registers[DP_REPLY_STATUS] = 0x00000010; + s->core_registers[DP_MSA_TRANSFER_UNIT_SIZE] = 0x00000040; + s->core_registers[DP_INIT_WAIT] = 0x00000020; + s->core_registers[DP_PHY_RESET] = 0x00010003; + s->core_registers[DP_INT_MASK] = 0xFFFFF03F; + s->core_registers[DP_PHY_STATUS] = 0x00000043; + s->core_registers[DP_INTERRUPT_SIGNAL_STATE] = 0x00000001; + + s->vblend_registers[V_BLEND_RGB2YCBCR_COEFF(0)] = 0x00001000; + s->vblend_registers[V_BLEND_RGB2YCBCR_COEFF(4)] = 0x00001000; + s->vblend_registers[V_BLEND_RGB2YCBCR_COEFF(8)] = 0x00001000; + s->vblend_registers[V_BLEND_IN1CSC_COEFF(0)] = 0x00001000; + s->vblend_registers[V_BLEND_IN1CSC_COEFF(4)] = 0x00001000; + s->vblend_registers[V_BLEND_IN1CSC_COEFF(8)] = 0x00001000; + s->vblend_registers[V_BLEND_IN2CSC_COEFF(0)] = 0x00001000; + s->vblend_registers[V_BLEND_IN2CSC_COEFF(4)] = 0x00001000; + s->vblend_registers[V_BLEND_IN2CSC_COEFF(8)] = 0x00001000; + + s->avbufm_registers[AV_BUF_NON_LIVE_LATENCY] = 0x00000180; + s->avbufm_registers[AV_BUF_OUTPUT_AUDIO_VIDEO_SELECT] = 0x00000008; + s->avbufm_registers[AV_BUF_DITHER_CONFIG_MAX] = 0x00000FFF; + s->avbufm_registers[AV_BUF_GRAPHICS_COMP_SCALE_FACTOR(0)] = 0x00010101; + s->avbufm_registers[AV_BUF_GRAPHICS_COMP_SCALE_FACTOR(1)] = 0x00010101; + s->avbufm_registers[AV_BUF_GRAPHICS_COMP_SCALE_FACTOR(2)] = 0x00010101; + s->avbufm_registers[AV_BUF_VIDEO_COMP_SCALE_FACTOR(0)] = 0x00010101; + s->avbufm_registers[AV_BUF_VIDEO_COMP_SCALE_FACTOR(1)] = 0x00010101; + s->avbufm_registers[AV_BUF_VIDEO_COMP_SCALE_FACTOR(2)] = 0x00010101; + s->avbufm_registers[AV_BUF_LIVE_VIDEO_COMP_SF(0)] = 0x00010101; + s->avbufm_registers[AV_BUF_LIVE_VIDEO_COMP_SF(1)] = 0x00010101; + s->avbufm_registers[AV_BUF_LIVE_VIDEO_COMP_SF(2)] = 0x00010101; + s->avbufm_registers[AV_BUF_LIVE_GFX_COMP_SF(0)] = 0x00010101; + s->avbufm_registers[AV_BUF_LIVE_GFX_COMP_SF(1)] = 0x00010101; + s->avbufm_registers[AV_BUF_LIVE_GFX_COMP_SF(2)] = 0x00010101; + + memset(s->audio_registers, 0, sizeof(s->audio_registers)); + s->byte_left = 0; + + xlnx_dp_aux_clear_rx_fifo(s); + xlnx_dp_change_graphic_fmt(s); + xlnx_dp_update_irq(s); +} + +static void xlnx_dp_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = xlnx_dp_realize; + dc->vmsd = &vmstate_dp; + dc->reset = xlnx_dp_reset; +} + +static const TypeInfo xlnx_dp_info = { + .name = TYPE_XLNX_DP, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XlnxDPState), + .instance_init = xlnx_dp_init, + .class_init = xlnx_dp_class_init, +}; + +static void xlnx_dp_register_types(void) +{ + type_register_static(&xlnx_dp_info); +} + +type_init(xlnx_dp_register_types) diff --git a/hw/dma/Makefile.objs b/hw/dma/Makefile.objs index a1abbcf74..087c8e685 100644 --- a/hw/dma/Makefile.objs +++ b/hw/dma/Makefile.objs @@ -5,9 +5,11 @@ 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 +obj-$(CONFIG_XLNX_ZYNQMP) += xlnx_dpdma.o obj-$(CONFIG_OMAP) += omap_dma.o soc_dma.o obj-$(CONFIG_PXA2XX) += pxa2xx_dma.o diff --git a/hw/dma/bcm2835_dma.c b/hw/dma/bcm2835_dma.c index 542117599..5d144a263 100644 --- a/hw/dma/bcm2835_dma.c +++ b/hw/dma/bcm2835_dma.c @@ -6,6 +6,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/dma/bcm2835_dma.h" +#include "qemu/log.h" /* DMA CS Control and Status bits */ #define BCM2708_DMA_ACTIVE (1 << 0) diff --git a/hw/dma/pl080.c b/hw/dma/pl080.c index 9318108b8..3bed5c339 100644 --- a/hw/dma/pl080.c +++ b/hw/dma/pl080.c @@ -10,6 +10,7 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" #include "exec/address-spaces.h" +#include "qemu/log.h" #define PL080_MAX_CHANNELS 8 #define PL080_CONF_E 0x1 diff --git a/hw/dma/pl330.c b/hw/dma/pl330.c index ea89ecb00..c0bd9fec3 100644 --- a/hw/dma/pl330.c +++ b/hw/dma/pl330.c @@ -19,6 +19,7 @@ #include "qapi/error.h" #include "qemu/timer.h" #include "sysemu/dma.h" +#include "qemu/log.h" #ifndef PL330_ERR_DEBUG #define PL330_ERR_DEBUG 0 diff --git a/hw/dma/pxa2xx_dma.c b/hw/dma/pxa2xx_dma.c index 2306abc35..634a4328f 100644 --- a/hw/dma/pxa2xx_dma.c +++ b/hw/dma/pxa2xx_dma.c @@ -12,6 +12,7 @@ #include "hw/hw.h" #include "hw/arm/pxa.h" #include "hw/sysbus.h" +#include "qapi/error.h" #define PXA255_DMA_NUM_CHANNELS 16 #define PXA27X_DMA_NUM_CHANNELS 32 @@ -450,31 +451,36 @@ static void pxa2xx_dma_request(void *opaque, int req_num, int on) } } -static int pxa2xx_dma_init(SysBusDevice *sbd) +static void pxa2xx_dma_init(Object *obj) +{ + DeviceState *dev = DEVICE(obj); + PXA2xxDMAState *s = PXA2XX_DMA(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memset(s->req, 0, sizeof(uint8_t) * PXA2XX_DMA_NUM_REQUESTS); + + qdev_init_gpio_in(dev, pxa2xx_dma_request, PXA2XX_DMA_NUM_REQUESTS); + + memory_region_init_io(&s->iomem, obj, &pxa2xx_dma_ops, s, + "pxa2xx.dma", 0x00010000); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); +} + +static void pxa2xx_dma_realize(DeviceState *dev, Error **errp) { - DeviceState *dev = DEVICE(sbd); PXA2xxDMAState *s = PXA2XX_DMA(dev); int i; if (s->channels <= 0) { - return -1; + error_setg(errp, "channels value invalid"); + return; } s->chan = g_new0(PXA2xxDMAChannel, s->channels); for (i = 0; i < s->channels; i ++) s->chan[i].state = DCSR_STOPINTR; - - memset(s->req, 0, sizeof(uint8_t) * PXA2XX_DMA_NUM_REQUESTS); - - qdev_init_gpio_in(dev, pxa2xx_dma_request, PXA2XX_DMA_NUM_REQUESTS); - - memory_region_init_io(&s->iomem, OBJECT(s), &pxa2xx_dma_ops, s, - "pxa2xx.dma", 0x00010000); - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq); - - return 0; } DeviceState *pxa27x_dma_init(hwaddr base, qemu_irq irq) @@ -553,18 +559,18 @@ static Property pxa2xx_dma_properties[] = { static void pxa2xx_dma_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = pxa2xx_dma_init; dc->desc = "PXA2xx DMA controller"; dc->vmsd = &vmstate_pxa2xx_dma; dc->props = pxa2xx_dma_properties; + dc->realize = pxa2xx_dma_realize; } static const TypeInfo pxa2xx_dma_info = { .name = TYPE_PXA2XX_DMA, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PXA2xxDMAState), + .instance_init = pxa2xx_dma_init, .class_init = pxa2xx_dma_class_init, }; diff --git a/hw/dma/rc4030.c b/hw/dma/rc4030.c index a06c2359a..2f2576faf 100644 --- a/hw/dma/rc4030.c +++ b/hw/dma/rc4030.c @@ -27,6 +27,7 @@ #include "hw/mips/mips.h" #include "hw/sysbus.h" #include "qemu/timer.h" +#include "qemu/log.h" #include "exec/address-spaces.h" #include "trace.h" diff --git a/hw/dma/trace-events b/hw/dma/trace-events new file mode 100644 index 000000000..22878dfdb --- /dev/null +++ b/hw/dma/trace-events @@ -0,0 +1,32 @@ +# See docs/tracing.txt for syntax documentation. + +# hw/dma/rc4030.c +jazzio_read(uint64_t addr, uint32_t ret) "read reg[0x%"PRIx64"] = 0x%x" +jazzio_write(uint64_t addr, uint32_t val) "write reg[0x%"PRIx64"] = 0x%x" +rc4030_read(uint64_t addr, uint32_t ret) "read reg[0x%"PRIx64"] = 0x%x" +rc4030_write(uint64_t addr, uint32_t val) "write reg[0x%"PRIx64"] = 0x%x" + +# hw/dma/sparc32_dma.c +ledma_memory_read(uint64_t addr) "DMA read addr 0x%"PRIx64 +ledma_memory_write(uint64_t addr) "DMA write addr 0x%"PRIx64 +sparc32_dma_set_irq_raise(void) "Raise IRQ" +sparc32_dma_set_irq_lower(void) "Lower IRQ" +espdma_memory_read(uint32_t addr) "DMA read addr 0x%08x" +espdma_memory_write(uint32_t addr) "DMA write addr 0x%08x" +sparc32_dma_mem_readl(uint64_t addr, uint32_t ret) "read dmareg %"PRIx64": 0x%08x" +sparc32_dma_mem_writel(uint64_t addr, uint32_t old, uint32_t val) "write dmareg %"PRIx64": 0x%08x -> 0x%08x" +sparc32_dma_enable_raise(void) "Raise DMA enable" +sparc32_dma_enable_lower(void) "Lower DMA enable" + +# hw/dma/sun4m_iommu.c +sun4m_iommu_mem_readl(uint64_t addr, uint32_t ret) "read reg[%"PRIx64"] = %x" +sun4m_iommu_mem_writel(uint64_t addr, uint32_t val) "write reg[%"PRIx64"] = %x" +sun4m_iommu_mem_writel_ctrl(uint64_t iostart) "iostart = %"PRIx64 +sun4m_iommu_mem_writel_tlbflush(uint32_t val) "tlb flush %x" +sun4m_iommu_mem_writel_pgflush(uint32_t val) "page flush %x" +sun4m_iommu_page_get_flags(uint64_t pa, uint64_t iopte, uint32_t ret) "get flags addr %"PRIx64" => pte %"PRIx64", *pte = %x" +sun4m_iommu_translate_pa(uint64_t addr, uint64_t pa, uint32_t iopte) "xlate dva %"PRIx64" => pa %"PRIx64" iopte = %x" +sun4m_iommu_bad_addr(uint64_t addr) "bad addr %"PRIx64 + +# hw/dma/i8257.c +i8257_unregistered_dma(int nchan, int dma_pos, int dma_len) "unregistered DMA channel used nchan=%d dma_pos=%d dma_len=%d" diff --git a/hw/dma/xlnx-zynq-devcfg.c b/hw/dma/xlnx-zynq-devcfg.c new file mode 100644 index 000000000..3b1052343 --- /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, + ®_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/dma/xlnx_dpdma.c b/hw/dma/xlnx_dpdma.c new file mode 100644 index 000000000..8ceb21ddb --- /dev/null +++ b/hw/dma/xlnx_dpdma.c @@ -0,0 +1,786 @@ +/* + * xlnx_dpdma.c + * + * Copyright (C) 2015 : GreenSocs Ltd + * http://www.greensocs.com/ , email: info@greensocs.com + * + * Developed by : + * Frederic Konrad <fred.konrad@greensocs.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/dma/xlnx_dpdma.h" + +#ifndef DEBUG_DPDMA +#define DEBUG_DPDMA 0 +#endif + +#define DPRINTF(fmt, ...) do { \ + if (DEBUG_DPDMA) { \ + qemu_log("xlnx_dpdma: " fmt , ## __VA_ARGS__); \ + } \ +} while (0); + +/* + * Registers offset for DPDMA. + */ +#define DPDMA_ERR_CTRL (0x0000) +#define DPDMA_ISR (0x0004 >> 2) +#define DPDMA_IMR (0x0008 >> 2) +#define DPDMA_IEN (0x000C >> 2) +#define DPDMA_IDS (0x0010 >> 2) +#define DPDMA_EISR (0x0014 >> 2) +#define DPDMA_EIMR (0x0018 >> 2) +#define DPDMA_EIEN (0x001C >> 2) +#define DPDMA_EIDS (0x0020 >> 2) +#define DPDMA_CNTL (0x0100 >> 2) + +#define DPDMA_GBL (0x0104 >> 2) +#define DPDMA_GBL_TRG_CH(n) (1 << n) +#define DPDMA_GBL_RTRG_CH(n) (1 << 6 << n) + +#define DPDMA_ALC0_CNTL (0x0108 >> 2) +#define DPDMA_ALC0_STATUS (0x010C >> 2) +#define DPDMA_ALC0_MAX (0x0110 >> 2) +#define DPDMA_ALC0_MIN (0x0114 >> 2) +#define DPDMA_ALC0_ACC (0x0118 >> 2) +#define DPDMA_ALC0_ACC_TRAN (0x011C >> 2) +#define DPDMA_ALC1_CNTL (0x0120 >> 2) +#define DPDMA_ALC1_STATUS (0x0124 >> 2) +#define DPDMA_ALC1_MAX (0x0128 >> 2) +#define DPDMA_ALC1_MIN (0x012C >> 2) +#define DPDMA_ALC1_ACC (0x0130 >> 2) +#define DPDMA_ALC1_ACC_TRAN (0x0134 >> 2) + +#define DPDMA_DSCR_STRT_ADDRE_CH(n) ((0x0200 + n * 0x100) >> 2) +#define DPDMA_DSCR_STRT_ADDR_CH(n) ((0x0204 + n * 0x100) >> 2) +#define DPDMA_DSCR_NEXT_ADDRE_CH(n) ((0x0208 + n * 0x100) >> 2) +#define DPDMA_DSCR_NEXT_ADDR_CH(n) ((0x020C + n * 0x100) >> 2) +#define DPDMA_PYLD_CUR_ADDRE_CH(n) ((0x0210 + n * 0x100) >> 2) +#define DPDMA_PYLD_CUR_ADDR_CH(n) ((0x0214 + n * 0x100) >> 2) + +#define DPDMA_CNTL_CH(n) ((0x0218 + n * 0x100) >> 2) +#define DPDMA_CNTL_CH_EN (1) +#define DPDMA_CNTL_CH_PAUSED (1 << 1) + +#define DPDMA_STATUS_CH(n) ((0x021C + n * 0x100) >> 2) +#define DPDMA_STATUS_BURST_TYPE (1 << 4) +#define DPDMA_STATUS_MODE (1 << 5) +#define DPDMA_STATUS_EN_CRC (1 << 6) +#define DPDMA_STATUS_LAST_DSCR (1 << 7) +#define DPDMA_STATUS_LDSCR_FRAME (1 << 8) +#define DPDMA_STATUS_IGNR_DONE (1 << 9) +#define DPDMA_STATUS_DSCR_DONE (1 << 10) +#define DPDMA_STATUS_EN_DSCR_UP (1 << 11) +#define DPDMA_STATUS_EN_DSCR_INTR (1 << 12) +#define DPDMA_STATUS_PREAMBLE_OFF (13) + +#define DPDMA_VDO_CH(n) ((0x0220 + n * 0x100) >> 2) +#define DPDMA_PYLD_SZ_CH(n) ((0x0224 + n * 0x100) >> 2) +#define DPDMA_DSCR_ID_CH(n) ((0x0228 + n * 0x100) >> 2) + +/* + * Descriptor control field. + */ +#define CONTROL_PREAMBLE_VALUE 0xA5 + +#define DSCR_CTRL_PREAMBLE 0xFF +#define DSCR_CTRL_EN_DSCR_DONE_INTR (1 << 8) +#define DSCR_CTRL_EN_DSCR_UPDATE (1 << 9) +#define DSCR_CTRL_IGNORE_DONE (1 << 10) +#define DSCR_CTRL_AXI_BURST_TYPE (1 << 11) +#define DSCR_CTRL_AXCACHE (0x0F << 12) +#define DSCR_CTRL_AXPROT (0x2 << 16) +#define DSCR_CTRL_DESCRIPTOR_MODE (1 << 18) +#define DSCR_CTRL_LAST_DESCRIPTOR (1 << 19) +#define DSCR_CTRL_ENABLE_CRC (1 << 20) +#define DSCR_CTRL_LAST_DESCRIPTOR_OF_FRAME (1 << 21) + +/* + * Descriptor timestamp field. + */ +#define STATUS_DONE (1 << 31) + +#define DPDMA_FRAG_MAX_SZ (4096) + +enum DPDMABurstType { + DPDMA_INCR = 0, + DPDMA_FIXED = 1 +}; + +enum DPDMAMode { + DPDMA_CONTIGOUS = 0, + DPDMA_FRAGMENTED = 1 +}; + +struct DPDMADescriptor { + uint32_t control; + uint32_t descriptor_id; + /* transfer size in byte. */ + uint32_t xfer_size; + uint32_t line_size_stride; + uint32_t timestamp_lsb; + uint32_t timestamp_msb; + /* contains extension for both descriptor and source. */ + uint32_t address_extension; + uint32_t next_descriptor; + uint32_t source_address; + uint32_t address_extension_23; + uint32_t address_extension_45; + uint32_t source_address2; + uint32_t source_address3; + uint32_t source_address4; + uint32_t source_address5; + uint32_t crc; +}; + +typedef enum DPDMABurstType DPDMABurstType; +typedef enum DPDMAMode DPDMAMode; +typedef struct DPDMADescriptor DPDMADescriptor; + +static bool xlnx_dpdma_desc_is_last(DPDMADescriptor *desc) +{ + return ((desc->control & DSCR_CTRL_LAST_DESCRIPTOR) != 0); +} + +static bool xlnx_dpdma_desc_is_last_of_frame(DPDMADescriptor *desc) +{ + return ((desc->control & DSCR_CTRL_LAST_DESCRIPTOR_OF_FRAME) != 0); +} + +static uint64_t xlnx_dpdma_desc_get_source_address(DPDMADescriptor *desc, + uint8_t frag) +{ + uint64_t addr = 0; + assert(frag < 5); + + switch (frag) { + case 0: + addr = desc->source_address + + (extract32(desc->address_extension, 16, 12) << 20); + break; + case 1: + addr = desc->source_address2 + + (extract32(desc->address_extension_23, 0, 12) << 8); + break; + case 2: + addr = desc->source_address3 + + (extract32(desc->address_extension_23, 16, 12) << 20); + break; + case 3: + addr = desc->source_address4 + + (extract32(desc->address_extension_45, 0, 12) << 8); + break; + case 4: + addr = desc->source_address5 + + (extract32(desc->address_extension_45, 16, 12) << 20); + break; + default: + addr = 0; + break; + } + + return addr; +} + +static uint32_t xlnx_dpdma_desc_get_transfer_size(DPDMADescriptor *desc) +{ + return desc->xfer_size; +} + +static uint32_t xlnx_dpdma_desc_get_line_size(DPDMADescriptor *desc) +{ + return extract32(desc->line_size_stride, 0, 18); +} + +static uint32_t xlnx_dpdma_desc_get_line_stride(DPDMADescriptor *desc) +{ + return extract32(desc->line_size_stride, 18, 14) * 16; +} + +static inline bool xlnx_dpdma_desc_crc_enabled(DPDMADescriptor *desc) +{ + return (desc->control & DSCR_CTRL_ENABLE_CRC) != 0; +} + +static inline bool xlnx_dpdma_desc_check_crc(DPDMADescriptor *desc) +{ + uint32_t *p = (uint32_t *)desc; + uint32_t crc = 0; + uint8_t i; + + /* + * CRC is calculated on the whole descriptor except the last 32bits word + * using 32bits addition. + */ + for (i = 0; i < 15; i++) { + crc += p[i]; + } + + return crc == desc->crc; +} + +static inline bool xlnx_dpdma_desc_completion_interrupt(DPDMADescriptor *desc) +{ + return (desc->control & DSCR_CTRL_EN_DSCR_DONE_INTR) != 0; +} + +static inline bool xlnx_dpdma_desc_is_valid(DPDMADescriptor *desc) +{ + return (desc->control & DSCR_CTRL_PREAMBLE) == CONTROL_PREAMBLE_VALUE; +} + +static inline bool xlnx_dpdma_desc_is_contiguous(DPDMADescriptor *desc) +{ + return (desc->control & DSCR_CTRL_DESCRIPTOR_MODE) == 0; +} + +static inline bool xlnx_dpdma_desc_update_enabled(DPDMADescriptor *desc) +{ + return (desc->control & DSCR_CTRL_EN_DSCR_UPDATE) != 0; +} + +static inline void xlnx_dpdma_desc_set_done(DPDMADescriptor *desc) +{ + desc->timestamp_msb |= STATUS_DONE; +} + +static inline bool xlnx_dpdma_desc_is_already_done(DPDMADescriptor *desc) +{ + return (desc->timestamp_msb & STATUS_DONE) != 0; +} + +static inline bool xlnx_dpdma_desc_ignore_done_bit(DPDMADescriptor *desc) +{ + return (desc->control & DSCR_CTRL_IGNORE_DONE) != 0; +} + +static const VMStateDescription vmstate_xlnx_dpdma = { + .name = TYPE_XLNX_DPDMA, + .version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(registers, XlnxDPDMAState, + XLNX_DPDMA_REG_ARRAY_SIZE), + VMSTATE_BOOL_ARRAY(operation_finished, XlnxDPDMAState, 6), + VMSTATE_END_OF_LIST() + } +}; + +static void xlnx_dpdma_update_irq(XlnxDPDMAState *s) +{ + bool flags; + + flags = ((s->registers[DPDMA_ISR] & (~s->registers[DPDMA_IMR])) + || (s->registers[DPDMA_EISR] & (~s->registers[DPDMA_EIMR]))); + qemu_set_irq(s->irq, flags); +} + +static uint64_t xlnx_dpdma_descriptor_start_address(XlnxDPDMAState *s, + uint8_t channel) +{ + return (s->registers[DPDMA_DSCR_STRT_ADDRE_CH(channel)] << 16) + + s->registers[DPDMA_DSCR_STRT_ADDR_CH(channel)]; +} + +static uint64_t xlnx_dpdma_descriptor_next_address(XlnxDPDMAState *s, + uint8_t channel) +{ + return ((uint64_t)s->registers[DPDMA_DSCR_NEXT_ADDRE_CH(channel)] << 32) + + s->registers[DPDMA_DSCR_NEXT_ADDR_CH(channel)]; +} + +static bool xlnx_dpdma_is_channel_enabled(XlnxDPDMAState *s, + uint8_t channel) +{ + return (s->registers[DPDMA_CNTL_CH(channel)] & DPDMA_CNTL_CH_EN) != 0; +} + +static bool xlnx_dpdma_is_channel_paused(XlnxDPDMAState *s, + uint8_t channel) +{ + return (s->registers[DPDMA_CNTL_CH(channel)] & DPDMA_CNTL_CH_PAUSED) != 0; +} + +static inline bool xlnx_dpdma_is_channel_retriggered(XlnxDPDMAState *s, + uint8_t channel) +{ + /* Clear the retriggered bit after reading it. */ + bool channel_is_retriggered = s->registers[DPDMA_GBL] + & DPDMA_GBL_RTRG_CH(channel); + s->registers[DPDMA_GBL] &= ~DPDMA_GBL_RTRG_CH(channel); + return channel_is_retriggered; +} + +static inline bool xlnx_dpdma_is_channel_triggered(XlnxDPDMAState *s, + uint8_t channel) +{ + return s->registers[DPDMA_GBL] & DPDMA_GBL_TRG_CH(channel); +} + +static void xlnx_dpdma_update_desc_info(XlnxDPDMAState *s, uint8_t channel, + DPDMADescriptor *desc) +{ + s->registers[DPDMA_DSCR_NEXT_ADDRE_CH(channel)] = + extract32(desc->address_extension, 0, 16); + s->registers[DPDMA_DSCR_NEXT_ADDR_CH(channel)] = desc->next_descriptor; + s->registers[DPDMA_PYLD_CUR_ADDRE_CH(channel)] = + extract32(desc->address_extension, 16, 16); + s->registers[DPDMA_PYLD_CUR_ADDR_CH(channel)] = desc->source_address; + s->registers[DPDMA_VDO_CH(channel)] = + extract32(desc->line_size_stride, 18, 14) + + (extract32(desc->line_size_stride, 0, 18) + << 14); + s->registers[DPDMA_PYLD_SZ_CH(channel)] = desc->xfer_size; + s->registers[DPDMA_DSCR_ID_CH(channel)] = desc->descriptor_id; + + /* Compute the status register with the descriptor information. */ + s->registers[DPDMA_STATUS_CH(channel)] = + extract32(desc->control, 0, 8) << 13; + if ((desc->control & DSCR_CTRL_EN_DSCR_DONE_INTR) != 0) { + s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_EN_DSCR_INTR; + } + if ((desc->control & DSCR_CTRL_EN_DSCR_UPDATE) != 0) { + s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_EN_DSCR_UP; + } + if ((desc->timestamp_msb & STATUS_DONE) != 0) { + s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_DSCR_DONE; + } + if ((desc->control & DSCR_CTRL_IGNORE_DONE) != 0) { + s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_IGNR_DONE; + } + if ((desc->control & DSCR_CTRL_LAST_DESCRIPTOR_OF_FRAME) != 0) { + s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_LDSCR_FRAME; + } + if ((desc->control & DSCR_CTRL_LAST_DESCRIPTOR) != 0) { + s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_LAST_DSCR; + } + if ((desc->control & DSCR_CTRL_ENABLE_CRC) != 0) { + s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_EN_CRC; + } + if ((desc->control & DSCR_CTRL_DESCRIPTOR_MODE) != 0) { + s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_MODE; + } + if ((desc->control & DSCR_CTRL_AXI_BURST_TYPE) != 0) { + s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_BURST_TYPE; + } +} + +static void xlnx_dpdma_dump_descriptor(DPDMADescriptor *desc) +{ + if (DEBUG_DPDMA) { + qemu_log("DUMP DESCRIPTOR:\n"); + qemu_hexdump((char *)desc, stdout, "", sizeof(DPDMADescriptor)); + } +} + +static uint64_t xlnx_dpdma_read(void *opaque, hwaddr offset, + unsigned size) +{ + XlnxDPDMAState *s = XLNX_DPDMA(opaque); + + DPRINTF("read @%" HWADDR_PRIx "\n", offset); + offset = offset >> 2; + + switch (offset) { + /* + * Trying to read a write only register. + */ + case DPDMA_GBL: + return 0; + default: + assert(offset <= (0xFFC >> 2)); + return s->registers[offset]; + } + return 0; +} + +static void xlnx_dpdma_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + XlnxDPDMAState *s = XLNX_DPDMA(opaque); + + DPRINTF("write @%" HWADDR_PRIx " = %" PRIx64 "\n", offset, value); + offset = offset >> 2; + + switch (offset) { + case DPDMA_ISR: + s->registers[DPDMA_ISR] &= ~value; + xlnx_dpdma_update_irq(s); + break; + case DPDMA_IEN: + s->registers[DPDMA_IMR] &= ~value; + break; + case DPDMA_IDS: + s->registers[DPDMA_IMR] |= value; + break; + case DPDMA_EISR: + s->registers[DPDMA_EISR] &= ~value; + xlnx_dpdma_update_irq(s); + break; + case DPDMA_EIEN: + s->registers[DPDMA_EIMR] &= ~value; + break; + case DPDMA_EIDS: + s->registers[DPDMA_EIMR] |= value; + break; + case DPDMA_IMR: + case DPDMA_EIMR: + case DPDMA_DSCR_NEXT_ADDRE_CH(0): + case DPDMA_DSCR_NEXT_ADDRE_CH(1): + case DPDMA_DSCR_NEXT_ADDRE_CH(2): + case DPDMA_DSCR_NEXT_ADDRE_CH(3): + case DPDMA_DSCR_NEXT_ADDRE_CH(4): + case DPDMA_DSCR_NEXT_ADDRE_CH(5): + case DPDMA_DSCR_NEXT_ADDR_CH(0): + case DPDMA_DSCR_NEXT_ADDR_CH(1): + case DPDMA_DSCR_NEXT_ADDR_CH(2): + case DPDMA_DSCR_NEXT_ADDR_CH(3): + case DPDMA_DSCR_NEXT_ADDR_CH(4): + case DPDMA_DSCR_NEXT_ADDR_CH(5): + case DPDMA_PYLD_CUR_ADDRE_CH(0): + case DPDMA_PYLD_CUR_ADDRE_CH(1): + case DPDMA_PYLD_CUR_ADDRE_CH(2): + case DPDMA_PYLD_CUR_ADDRE_CH(3): + case DPDMA_PYLD_CUR_ADDRE_CH(4): + case DPDMA_PYLD_CUR_ADDRE_CH(5): + case DPDMA_PYLD_CUR_ADDR_CH(0): + case DPDMA_PYLD_CUR_ADDR_CH(1): + case DPDMA_PYLD_CUR_ADDR_CH(2): + case DPDMA_PYLD_CUR_ADDR_CH(3): + case DPDMA_PYLD_CUR_ADDR_CH(4): + case DPDMA_PYLD_CUR_ADDR_CH(5): + case DPDMA_STATUS_CH(0): + case DPDMA_STATUS_CH(1): + case DPDMA_STATUS_CH(2): + case DPDMA_STATUS_CH(3): + case DPDMA_STATUS_CH(4): + case DPDMA_STATUS_CH(5): + case DPDMA_VDO_CH(0): + case DPDMA_VDO_CH(1): + case DPDMA_VDO_CH(2): + case DPDMA_VDO_CH(3): + case DPDMA_VDO_CH(4): + case DPDMA_VDO_CH(5): + case DPDMA_PYLD_SZ_CH(0): + case DPDMA_PYLD_SZ_CH(1): + case DPDMA_PYLD_SZ_CH(2): + case DPDMA_PYLD_SZ_CH(3): + case DPDMA_PYLD_SZ_CH(4): + case DPDMA_PYLD_SZ_CH(5): + case DPDMA_DSCR_ID_CH(0): + case DPDMA_DSCR_ID_CH(1): + case DPDMA_DSCR_ID_CH(2): + case DPDMA_DSCR_ID_CH(3): + case DPDMA_DSCR_ID_CH(4): + case DPDMA_DSCR_ID_CH(5): + /* + * Trying to write to a read only register.. + */ + break; + case DPDMA_GBL: + /* + * This is a write only register so it's read as zero in the read + * callback. + * We store the value anyway so we can know if the channel is + * enabled. + */ + s->registers[offset] |= value & 0x00000FFF; + break; + case DPDMA_DSCR_STRT_ADDRE_CH(0): + case DPDMA_DSCR_STRT_ADDRE_CH(1): + case DPDMA_DSCR_STRT_ADDRE_CH(2): + case DPDMA_DSCR_STRT_ADDRE_CH(3): + case DPDMA_DSCR_STRT_ADDRE_CH(4): + case DPDMA_DSCR_STRT_ADDRE_CH(5): + value &= 0x0000FFFF; + s->registers[offset] = value; + break; + case DPDMA_CNTL_CH(0): + s->registers[DPDMA_GBL] &= ~DPDMA_GBL_TRG_CH(0); + value &= 0x3FFFFFFF; + s->registers[offset] = value; + break; + case DPDMA_CNTL_CH(1): + s->registers[DPDMA_GBL] &= ~DPDMA_GBL_TRG_CH(1); + value &= 0x3FFFFFFF; + s->registers[offset] = value; + break; + case DPDMA_CNTL_CH(2): + s->registers[DPDMA_GBL] &= ~DPDMA_GBL_TRG_CH(2); + value &= 0x3FFFFFFF; + s->registers[offset] = value; + break; + case DPDMA_CNTL_CH(3): + s->registers[DPDMA_GBL] &= ~DPDMA_GBL_TRG_CH(3); + value &= 0x3FFFFFFF; + s->registers[offset] = value; + break; + case DPDMA_CNTL_CH(4): + s->registers[DPDMA_GBL] &= ~DPDMA_GBL_TRG_CH(4); + value &= 0x3FFFFFFF; + s->registers[offset] = value; + break; + case DPDMA_CNTL_CH(5): + s->registers[DPDMA_GBL] &= ~DPDMA_GBL_TRG_CH(5); + value &= 0x3FFFFFFF; + s->registers[offset] = value; + break; + default: + assert(offset <= (0xFFC >> 2)); + s->registers[offset] = value; + break; + } +} + +static const MemoryRegionOps dma_ops = { + .read = xlnx_dpdma_read, + .write = xlnx_dpdma_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void xlnx_dpdma_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + XlnxDPDMAState *s = XLNX_DPDMA(obj); + + memory_region_init_io(&s->iomem, obj, &dma_ops, s, + TYPE_XLNX_DPDMA, 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); +} + +static void xlnx_dpdma_reset(DeviceState *dev) +{ + XlnxDPDMAState *s = XLNX_DPDMA(dev); + size_t i; + + memset(s->registers, 0, sizeof(s->registers)); + s->registers[DPDMA_IMR] = 0x07FFFFFF; + s->registers[DPDMA_EIMR] = 0xFFFFFFFF; + s->registers[DPDMA_ALC0_MIN] = 0x0000FFFF; + s->registers[DPDMA_ALC1_MIN] = 0x0000FFFF; + + for (i = 0; i < 6; i++) { + s->data[i] = NULL; + s->operation_finished[i] = true; + } +} + +static void xlnx_dpdma_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->vmsd = &vmstate_xlnx_dpdma; + dc->reset = xlnx_dpdma_reset; +} + +static const TypeInfo xlnx_dpdma_info = { + .name = TYPE_XLNX_DPDMA, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XlnxDPDMAState), + .instance_init = xlnx_dpdma_init, + .class_init = xlnx_dpdma_class_init, +}; + +static void xlnx_dpdma_register_types(void) +{ + type_register_static(&xlnx_dpdma_info); +} + +size_t xlnx_dpdma_start_operation(XlnxDPDMAState *s, uint8_t channel, + bool one_desc) +{ + uint64_t desc_addr; + uint64_t source_addr[6]; + DPDMADescriptor desc; + bool done = false; + size_t ptr = 0; + + assert(channel <= 5); + + DPRINTF("start dpdma channel 0x%" PRIX8 "\n", channel); + + if (!xlnx_dpdma_is_channel_triggered(s, channel)) { + DPRINTF("Channel isn't triggered..\n"); + return 0; + } + + if (!xlnx_dpdma_is_channel_enabled(s, channel)) { + DPRINTF("Channel isn't enabled..\n"); + return 0; + } + + if (xlnx_dpdma_is_channel_paused(s, channel)) { + DPRINTF("Channel is paused..\n"); + return 0; + } + + do { + if ((s->operation_finished[channel]) + || xlnx_dpdma_is_channel_retriggered(s, channel)) { + desc_addr = xlnx_dpdma_descriptor_start_address(s, channel); + s->operation_finished[channel] = false; + } else { + desc_addr = xlnx_dpdma_descriptor_next_address(s, channel); + } + + if (dma_memory_read(&address_space_memory, desc_addr, &desc, + sizeof(DPDMADescriptor))) { + s->registers[DPDMA_EISR] |= ((1 << 1) << channel); + xlnx_dpdma_update_irq(s); + s->operation_finished[channel] = true; + DPRINTF("Can't get the descriptor.\n"); + break; + } + + xlnx_dpdma_update_desc_info(s, channel, &desc); + +#ifdef DEBUG_DPDMA + xlnx_dpdma_dump_descriptor(&desc); +#endif + + DPRINTF("location of the descriptor: %" PRIx64 "\n", desc_addr); + if (!xlnx_dpdma_desc_is_valid(&desc)) { + s->registers[DPDMA_EISR] |= ((1 << 7) << channel); + xlnx_dpdma_update_irq(s); + s->operation_finished[channel] = true; + DPRINTF("Invalid descriptor..\n"); + break; + } + + if (xlnx_dpdma_desc_crc_enabled(&desc) + && !xlnx_dpdma_desc_check_crc(&desc)) { + s->registers[DPDMA_EISR] |= ((1 << 13) << channel); + xlnx_dpdma_update_irq(s); + s->operation_finished[channel] = true; + DPRINTF("Bad CRC for descriptor..\n"); + break; + } + + if (xlnx_dpdma_desc_is_already_done(&desc) + && !xlnx_dpdma_desc_ignore_done_bit(&desc)) { + /* We are trying to process an already processed descriptor. */ + s->registers[DPDMA_EISR] |= ((1 << 25) << channel); + xlnx_dpdma_update_irq(s); + s->operation_finished[channel] = true; + DPRINTF("Already processed descriptor..\n"); + break; + } + + done = xlnx_dpdma_desc_is_last(&desc) + || xlnx_dpdma_desc_is_last_of_frame(&desc); + + s->operation_finished[channel] = done; + if (s->data[channel]) { + int64_t transfer_len = xlnx_dpdma_desc_get_transfer_size(&desc); + uint32_t line_size = xlnx_dpdma_desc_get_line_size(&desc); + uint32_t line_stride = xlnx_dpdma_desc_get_line_stride(&desc); + if (xlnx_dpdma_desc_is_contiguous(&desc)) { + source_addr[0] = xlnx_dpdma_desc_get_source_address(&desc, 0); + while (transfer_len != 0) { + if (dma_memory_read(&address_space_memory, + source_addr[0], + &s->data[channel][ptr], + line_size)) { + s->registers[DPDMA_ISR] |= ((1 << 12) << channel); + xlnx_dpdma_update_irq(s); + DPRINTF("Can't get data.\n"); + break; + } + ptr += line_size; + transfer_len -= line_size; + source_addr[0] += line_stride; + } + } else { + DPRINTF("Source address:\n"); + int frag; + for (frag = 0; frag < 5; frag++) { + source_addr[frag] = + xlnx_dpdma_desc_get_source_address(&desc, frag); + DPRINTF("Fragment %u: %" PRIx64 "\n", frag + 1, + source_addr[frag]); + } + + frag = 0; + while ((transfer_len < 0) && (frag < 5)) { + size_t fragment_len = DPDMA_FRAG_MAX_SZ + - (source_addr[frag] % DPDMA_FRAG_MAX_SZ); + + if (dma_memory_read(&address_space_memory, + source_addr[frag], + &(s->data[channel][ptr]), + fragment_len)) { + s->registers[DPDMA_ISR] |= ((1 << 12) << channel); + xlnx_dpdma_update_irq(s); + DPRINTF("Can't get data.\n"); + break; + } + ptr += fragment_len; + transfer_len -= fragment_len; + frag += 1; + } + } + } + + if (xlnx_dpdma_desc_update_enabled(&desc)) { + /* The descriptor need to be updated when it's completed. */ + DPRINTF("update the descriptor with the done flag set.\n"); + xlnx_dpdma_desc_set_done(&desc); + dma_memory_write(&address_space_memory, desc_addr, &desc, + sizeof(DPDMADescriptor)); + } + + if (xlnx_dpdma_desc_completion_interrupt(&desc)) { + DPRINTF("completion interrupt enabled!\n"); + s->registers[DPDMA_ISR] |= (1 << channel); + xlnx_dpdma_update_irq(s); + } + + } while (!done && !one_desc); + + return ptr; +} + +void xlnx_dpdma_set_host_data_location(XlnxDPDMAState *s, uint8_t channel, + void *p) +{ + if (!s) { + qemu_log_mask(LOG_UNIMP, "DPDMA client not attached to valid DPDMA" + " instance\n"); + return; + } + + assert(channel <= 5); + s->data[channel] = p; +} + +void xlnx_dpdma_trigger_vsync_irq(XlnxDPDMAState *s) +{ + s->registers[DPDMA_ISR] |= (1 << 27); + xlnx_dpdma_update_irq(s); +} + +type_init(xlnx_dpdma_register_types) diff --git a/hw/gpio/gpio_key.c b/hw/gpio/gpio_key.c index ef287727b..b34aa49df 100644 --- a/hw/gpio/gpio_key.c +++ b/hw/gpio/gpio_key.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" +#include "qemu/timer.h" #define TYPE_GPIOKEY "gpio-key" #define GPIOKEY(obj) OBJECT_CHECK(GPIOKEYState, (obj), TYPE_GPIOKEY) diff --git a/hw/gpio/imx_gpio.c b/hw/gpio/imx_gpio.c index ed7e247f5..f3574aa8f 100644 --- a/hw/gpio/imx_gpio.c +++ b/hw/gpio/imx_gpio.c @@ -19,6 +19,7 @@ #include "qemu/osdep.h" #include "hw/gpio/imx_gpio.h" +#include "qemu/log.h" #ifndef DEBUG_IMX_GPIO #define DEBUG_IMX_GPIO 0 diff --git a/hw/gpio/omap_gpio.c b/hw/gpio/omap_gpio.c index 9b1b004fc..dabef4a11 100644 --- a/hw/gpio/omap_gpio.c +++ b/hw/gpio/omap_gpio.c @@ -23,6 +23,7 @@ #include "hw/arm/omap.h" #include "hw/sysbus.h" #include "qemu/error-report.h" +#include "qapi/error.h" struct omap_gpio_s { qemu_irq irq; @@ -678,48 +679,46 @@ static const MemoryRegionOps omap2_gpif_top_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int omap_gpio_init(SysBusDevice *sbd) +static void omap_gpio_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - struct omap_gpif_s *s = OMAP1_GPIO(dev); + DeviceState *dev = DEVICE(obj); + struct omap_gpif_s *s = OMAP1_GPIO(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - if (!s->clk) { - error_report("omap-gpio: clk not connected"); - return -1; - } qdev_init_gpio_in(dev, omap_gpio_set, 16); qdev_init_gpio_out(dev, s->omap1.handler, 16); sysbus_init_irq(sbd, &s->omap1.irq); - memory_region_init_io(&s->iomem, OBJECT(s), &omap_gpio_ops, &s->omap1, + memory_region_init_io(&s->iomem, obj, &omap_gpio_ops, &s->omap1, "omap.gpio", 0x1000); sysbus_init_mmio(sbd, &s->iomem); - return 0; } -static int omap2_gpio_init(SysBusDevice *sbd) +static void omap_gpio_realize(DeviceState *dev, Error **errp) +{ + struct omap_gpif_s *s = OMAP1_GPIO(dev); + + if (!s->clk) { + error_setg(errp, "omap-gpio: clk not connected"); + } +} + +static void omap2_gpio_realize(DeviceState *dev, Error **errp) { - DeviceState *dev = DEVICE(sbd); struct omap2_gpif_s *s = OMAP2_GPIO(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); int i; if (!s->iclk) { - error_report("omap2-gpio: iclk not connected"); - return -1; + error_setg(errp, "omap2-gpio: iclk not connected"); + return; } s->modulecount = s->mpu_model < omap2430 ? 4 - : s->mpu_model < omap3430 ? 5 - : 6; - - for (i = 0; i < s->modulecount; i++) { - if (!s->fclk[i]) { - error_report("omap2-gpio: fclk%d not connected", i); - return -1; - } - } + : s->mpu_model < omap3430 ? 5 + : 6; if (s->mpu_model < omap3430) { - memory_region_init_io(&s->iomem, OBJECT(s), &omap2_gpif_top_ops, s, + memory_region_init_io(&s->iomem, OBJECT(dev), &omap2_gpif_top_ops, s, "omap2.gpio", 0x1000); sysbus_init_mmio(sbd, &s->iomem); } @@ -732,17 +731,20 @@ static int omap2_gpio_init(SysBusDevice *sbd) for (i = 0; i < s->modulecount; i++) { struct omap2_gpio_s *m = &s->modules[i]; + if (!s->fclk[i]) { + error_setg(errp, "omap2-gpio: fclk%d not connected", i); + return; + } + m->revision = (s->mpu_model < omap3430) ? 0x18 : 0x25; m->handler = &s->handler[i * 32]; sysbus_init_irq(sbd, &m->irq[0]); /* mpu irq */ sysbus_init_irq(sbd, &m->irq[1]); /* dsp irq */ sysbus_init_irq(sbd, &m->wkup); - memory_region_init_io(&m->iomem, OBJECT(s), &omap2_gpio_module_ops, m, + memory_region_init_io(&m->iomem, OBJECT(dev), &omap2_gpio_module_ops, m, "omap.gpio-module", 0x1000); sysbus_init_mmio(sbd, &m->iomem); } - - return 0; } /* Using qdev pointer properties for the clocks is not ideal. @@ -766,9 +768,8 @@ static Property omap_gpio_properties[] = { static void omap_gpio_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = omap_gpio_init; + dc->realize = omap_gpio_realize; dc->reset = omap_gpif_reset; dc->props = omap_gpio_properties; /* Reason: pointer property "clk" */ @@ -779,6 +780,7 @@ static const TypeInfo omap_gpio_info = { .name = TYPE_OMAP1_GPIO, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(struct omap_gpif_s), + .instance_init = omap_gpio_init, .class_init = omap_gpio_class_init, }; @@ -797,9 +799,8 @@ static Property omap2_gpio_properties[] = { static void omap2_gpio_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = omap2_gpio_init; + dc->realize = omap2_gpio_realize; dc->reset = omap2_gpif_reset; dc->props = omap2_gpio_properties; /* Reason: pointer properties "iclk", "fclk0", ..., "fclk5" */ diff --git a/hw/gpio/pl061.c b/hw/gpio/pl061.c index 29dc7fc38..4ae2aa156 100644 --- a/hw/gpio/pl061.c +++ b/hw/gpio/pl061.c @@ -10,6 +10,7 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" +#include "qemu/log.h" //#define DEBUG_PL061 1 @@ -340,20 +341,6 @@ static const MemoryRegionOps pl061_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int pl061_initfn(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - PL061State *s = PL061(dev); - - memory_region_init_io(&s->iomem, OBJECT(s), &pl061_ops, s, "pl061", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq); - qdev_init_gpio_in(dev, pl061_set_irq, 8); - qdev_init_gpio_out(dev, s->out, 8); - - return 0; -} - static void pl061_luminary_init(Object *obj) { PL061State *s = PL061(obj); @@ -365,17 +352,23 @@ static void pl061_luminary_init(Object *obj) static void pl061_init(Object *obj) { PL061State *s = PL061(obj); + DeviceState *dev = DEVICE(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); s->id = pl061_id; s->rsvd_start = 0x424; + + memory_region_init_io(&s->iomem, obj, &pl061_ops, s, "pl061", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); + qdev_init_gpio_in(dev, pl061_set_irq, 8); + qdev_init_gpio_out(dev, s->out, 8); } static void pl061_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = pl061_initfn; dc->vmsd = &vmstate_pl061; dc->reset = &pl061_reset; } diff --git a/hw/gpio/zaurus.c b/hw/gpio/zaurus.c index 555da281c..15865e108 100644 --- a/hw/gpio/zaurus.c +++ b/hw/gpio/zaurus.c @@ -167,19 +167,18 @@ static void scoop_gpio_set(void *opaque, int line, int level) s->gpio_level &= ~(1 << line); } -static int scoop_init(SysBusDevice *sbd) +static void scoop_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - ScoopInfo *s = SCOOP(dev); + DeviceState *dev = DEVICE(obj); + ScoopInfo *s = SCOOP(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); s->status = 0x02; qdev_init_gpio_out(dev, s->handler, 16); qdev_init_gpio_in(dev, scoop_gpio_set, 16); - memory_region_init_io(&s->iomem, OBJECT(s), &scoop_ops, s, "scoop", 0x1000); + memory_region_init_io(&s->iomem, obj, &scoop_ops, s, "scoop", 0x1000); sysbus_init_mmio(sbd, &s->iomem); - - return 0; } static int scoop_post_load(void *opaque, int version_id) @@ -239,9 +238,7 @@ static const VMStateDescription vmstate_scoop_regs = { static void scoop_sysbus_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = scoop_init; dc->desc = "Scoop2 Sharp custom ASIC"; dc->vmsd = &vmstate_scoop_regs; } @@ -250,6 +247,7 @@ static const TypeInfo scoop_sysbus_info = { .name = TYPE_SCOOP, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(ScoopInfo), + .instance_init = scoop_init, .class_init = scoop_sysbus_class_init, }; diff --git a/hw/i2c/Makefile.objs b/hw/i2c/Makefile.objs index aeb8f38d7..a081b8ef2 100644 --- a/hw/i2c/Makefile.objs +++ b/hw/i2c/Makefile.objs @@ -1,8 +1,10 @@ common-obj-y += core.o smbus.o smbus_eeprom.o +common-obj-$(CONFIG_DDC) += i2c-ddc.o common-obj-$(CONFIG_VERSATILE_I2C) += versatile_i2c.o common-obj-$(CONFIG_ACPI_X86) += smbus_ich9.o common-obj-$(CONFIG_APM) += pm_smbus.o common-obj-$(CONFIG_BITBANG_I2C) += bitbang_i2c.o common-obj-$(CONFIG_EXYNOS4) += exynos4210_i2c.o common-obj-$(CONFIG_IMX_I2C) += imx_i2c.o +common-obj-$(CONFIG_ASPEED_SOC) += aspeed_i2c.o obj-$(CONFIG_OMAP) += omap_i2c.o diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c new file mode 100644 index 000000000..ce5b1f0fa --- /dev/null +++ b/hw/i2c/aspeed_i2c.c @@ -0,0 +1,440 @@ +/* + * ARM Aspeed I2C controller + * + * Copyright (C) 2016 IBM Corp. + * + * 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 "hw/sysbus.h" +#include "qemu/log.h" +#include "hw/i2c/aspeed_i2c.h" + +/* I2C Global Register */ + +#define I2C_CTRL_STATUS 0x00 /* Device Interrupt Status */ +#define I2C_CTRL_ASSIGN 0x08 /* Device Interrupt Target + Assignment */ + +/* I2C Device (Bus) Register */ + +#define I2CD_FUN_CTRL_REG 0x00 /* I2CD Function Control */ +#define I2CD_BUFF_SEL_MASK (0x7 << 20) +#define I2CD_BUFF_SEL(x) (x << 20) +#define I2CD_M_SDA_LOCK_EN (0x1 << 16) +#define I2CD_MULTI_MASTER_DIS (0x1 << 15) +#define I2CD_M_SCL_DRIVE_EN (0x1 << 14) +#define I2CD_MSB_STS (0x1 << 9) +#define I2CD_SDA_DRIVE_1T_EN (0x1 << 8) +#define I2CD_M_SDA_DRIVE_1T_EN (0x1 << 7) +#define I2CD_M_HIGH_SPEED_EN (0x1 << 6) +#define I2CD_DEF_ADDR_EN (0x1 << 5) +#define I2CD_DEF_ALERT_EN (0x1 << 4) +#define I2CD_DEF_ARP_EN (0x1 << 3) +#define I2CD_DEF_GCALL_EN (0x1 << 2) +#define I2CD_SLAVE_EN (0x1 << 1) +#define I2CD_MASTER_EN (0x1) + +#define I2CD_AC_TIMING_REG1 0x04 /* Clock and AC Timing Control #1 */ +#define I2CD_AC_TIMING_REG2 0x08 /* Clock and AC Timing Control #1 */ +#define I2CD_INTR_CTRL_REG 0x0c /* I2CD Interrupt Control */ +#define I2CD_INTR_STS_REG 0x10 /* I2CD Interrupt Status */ +#define I2CD_INTR_SDA_DL_TIMEOUT (0x1 << 14) +#define I2CD_INTR_BUS_RECOVER_DONE (0x1 << 13) +#define I2CD_INTR_SMBUS_ALERT (0x1 << 12) /* Bus [0-3] only */ +#define I2CD_INTR_SMBUS_ARP_ADDR (0x1 << 11) /* Removed */ +#define I2CD_INTR_SMBUS_DEV_ALERT_ADDR (0x1 << 10) /* Removed */ +#define I2CD_INTR_SMBUS_DEF_ADDR (0x1 << 9) /* Removed */ +#define I2CD_INTR_GCALL_ADDR (0x1 << 8) /* Removed */ +#define I2CD_INTR_SLAVE_MATCH (0x1 << 7) /* use RX_DONE */ +#define I2CD_INTR_SCL_TIMEOUT (0x1 << 6) +#define I2CD_INTR_ABNORMAL (0x1 << 5) +#define I2CD_INTR_NORMAL_STOP (0x1 << 4) +#define I2CD_INTR_ARBIT_LOSS (0x1 << 3) +#define I2CD_INTR_RX_DONE (0x1 << 2) +#define I2CD_INTR_TX_NAK (0x1 << 1) +#define I2CD_INTR_TX_ACK (0x1 << 0) + +#define I2CD_CMD_REG 0x14 /* I2CD Command/Status */ +#define I2CD_SDA_OE (0x1 << 28) +#define I2CD_SDA_O (0x1 << 27) +#define I2CD_SCL_OE (0x1 << 26) +#define I2CD_SCL_O (0x1 << 25) +#define I2CD_TX_TIMING (0x1 << 24) +#define I2CD_TX_STATUS (0x1 << 23) + +#define I2CD_TX_STATE_SHIFT 19 /* Tx State Machine */ +#define I2CD_TX_STATE_MASK 0xf +#define I2CD_IDLE 0x0 +#define I2CD_MACTIVE 0x8 +#define I2CD_MSTART 0x9 +#define I2CD_MSTARTR 0xa +#define I2CD_MSTOP 0xb +#define I2CD_MTXD 0xc +#define I2CD_MRXACK 0xd +#define I2CD_MRXD 0xe +#define I2CD_MTXACK 0xf +#define I2CD_SWAIT 0x1 +#define I2CD_SRXD 0x4 +#define I2CD_STXACK 0x5 +#define I2CD_STXD 0x6 +#define I2CD_SRXACK 0x7 +#define I2CD_RECOVER 0x3 + +#define I2CD_SCL_LINE_STS (0x1 << 18) +#define I2CD_SDA_LINE_STS (0x1 << 17) +#define I2CD_BUS_BUSY_STS (0x1 << 16) +#define I2CD_SDA_OE_OUT_DIR (0x1 << 15) +#define I2CD_SDA_O_OUT_DIR (0x1 << 14) +#define I2CD_SCL_OE_OUT_DIR (0x1 << 13) +#define I2CD_SCL_O_OUT_DIR (0x1 << 12) +#define I2CD_BUS_RECOVER_CMD_EN (0x1 << 11) +#define I2CD_S_ALT_EN (0x1 << 10) +#define I2CD_RX_DMA_ENABLE (0x1 << 9) +#define I2CD_TX_DMA_ENABLE (0x1 << 8) + +/* Command Bit */ +#define I2CD_M_STOP_CMD (0x1 << 5) +#define I2CD_M_S_RX_CMD_LAST (0x1 << 4) +#define I2CD_M_RX_CMD (0x1 << 3) +#define I2CD_S_TX_CMD (0x1 << 2) +#define I2CD_M_TX_CMD (0x1 << 1) +#define I2CD_M_START_CMD (0x1) + +#define I2CD_DEV_ADDR_REG 0x18 /* Slave Device Address */ +#define I2CD_BUF_CTRL_REG 0x1c /* Pool Buffer Control */ +#define I2CD_BYTE_BUF_REG 0x20 /* Transmit/Receive Byte Buffer */ +#define I2CD_BYTE_BUF_TX_SHIFT 0 +#define I2CD_BYTE_BUF_TX_MASK 0xff +#define I2CD_BYTE_BUF_RX_SHIFT 8 +#define I2CD_BYTE_BUF_RX_MASK 0xff + + +static inline bool aspeed_i2c_bus_is_master(AspeedI2CBus *bus) +{ + return bus->ctrl & I2CD_MASTER_EN; +} + +static inline bool aspeed_i2c_bus_is_enabled(AspeedI2CBus *bus) +{ + return bus->ctrl & (I2CD_MASTER_EN | I2CD_SLAVE_EN); +} + +static inline void aspeed_i2c_bus_raise_interrupt(AspeedI2CBus *bus) +{ + bus->intr_status &= bus->intr_ctrl; + if (bus->intr_status) { + bus->controller->intr_status |= 1 << bus->id; + qemu_irq_raise(bus->controller->irq); + } +} + +static uint64_t aspeed_i2c_bus_read(void *opaque, hwaddr offset, + unsigned size) +{ + AspeedI2CBus *bus = opaque; + + switch (offset) { + case I2CD_FUN_CTRL_REG: + return bus->ctrl; + case I2CD_AC_TIMING_REG1: + return bus->timing[0]; + case I2CD_AC_TIMING_REG2: + return bus->timing[1]; + case I2CD_INTR_CTRL_REG: + return bus->intr_ctrl; + case I2CD_INTR_STS_REG: + return bus->intr_status; + case I2CD_BYTE_BUF_REG: + return bus->buf; + case I2CD_CMD_REG: + return bus->cmd | (i2c_bus_busy(bus->bus) << 16); + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset); + return -1; + } +} + +static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) +{ + bus->cmd |= value & 0xFFFF; + bus->intr_status = 0; + + if (bus->cmd & I2CD_M_START_CMD) { + if (i2c_start_transfer(bus->bus, extract32(bus->buf, 1, 7), + extract32(bus->buf, 0, 1))) { + bus->intr_status |= I2CD_INTR_TX_NAK; + } else { + bus->intr_status |= I2CD_INTR_TX_ACK; + } + + } else if (bus->cmd & I2CD_M_TX_CMD) { + if (i2c_send(bus->bus, bus->buf)) { + bus->intr_status |= (I2CD_INTR_TX_NAK | I2CD_INTR_ABNORMAL); + i2c_end_transfer(bus->bus); + } else { + bus->intr_status |= I2CD_INTR_TX_ACK; + } + + } else if (bus->cmd & I2CD_M_RX_CMD) { + int ret = i2c_recv(bus->bus); + if (ret < 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: read failed\n", __func__); + ret = 0xff; + } else { + bus->intr_status |= I2CD_INTR_RX_DONE; + } + bus->buf = (ret & I2CD_BYTE_BUF_RX_MASK) << I2CD_BYTE_BUF_RX_SHIFT; + } + + if (bus->cmd & (I2CD_M_STOP_CMD | I2CD_M_S_RX_CMD_LAST)) { + if (!i2c_bus_busy(bus->bus)) { + bus->intr_status |= I2CD_INTR_ABNORMAL; + } else { + i2c_end_transfer(bus->bus); + bus->intr_status |= I2CD_INTR_NORMAL_STOP; + } + } + + /* command is handled, reset it and check for interrupts */ + bus->cmd &= ~0xFFFF; + aspeed_i2c_bus_raise_interrupt(bus); +} + +static void aspeed_i2c_bus_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + AspeedI2CBus *bus = opaque; + + switch (offset) { + case I2CD_FUN_CTRL_REG: + if (value & I2CD_SLAVE_EN) { + qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n", + __func__); + break; + } + bus->ctrl = value & 0x0071C3FF; + break; + case I2CD_AC_TIMING_REG1: + bus->timing[0] = value & 0xFFFFF0F; + break; + case I2CD_AC_TIMING_REG2: + bus->timing[1] = value & 0x7; + break; + case I2CD_INTR_CTRL_REG: + bus->intr_ctrl = value & 0x7FFF; + break; + case I2CD_INTR_STS_REG: + bus->intr_status &= ~(value & 0x7FFF); + bus->controller->intr_status &= ~(1 << bus->id); + qemu_irq_lower(bus->controller->irq); + break; + case I2CD_DEV_ADDR_REG: + qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n", + __func__); + break; + case I2CD_BYTE_BUF_REG: + bus->buf = (value & I2CD_BYTE_BUF_TX_MASK) << I2CD_BYTE_BUF_TX_SHIFT; + break; + case I2CD_CMD_REG: + if (!aspeed_i2c_bus_is_enabled(bus)) { + break; + } + + if (!aspeed_i2c_bus_is_master(bus)) { + qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n", + __func__); + break; + } + + aspeed_i2c_bus_handle_cmd(bus, value); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + } +} + +static uint64_t aspeed_i2c_ctrl_read(void *opaque, hwaddr offset, + unsigned size) +{ + AspeedI2CState *s = opaque; + + switch (offset) { + case I2C_CTRL_STATUS: + return s->intr_status; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + break; + } + + return -1; +} + +static void aspeed_i2c_ctrl_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + switch (offset) { + case I2C_CTRL_STATUS: + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + break; + } +} + +static const MemoryRegionOps aspeed_i2c_bus_ops = { + .read = aspeed_i2c_bus_read, + .write = aspeed_i2c_bus_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static const MemoryRegionOps aspeed_i2c_ctrl_ops = { + .read = aspeed_i2c_ctrl_read, + .write = aspeed_i2c_ctrl_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static const VMStateDescription aspeed_i2c_bus_vmstate = { + .name = TYPE_ASPEED_I2C, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(id, AspeedI2CBus), + VMSTATE_UINT32(ctrl, AspeedI2CBus), + VMSTATE_UINT32_ARRAY(timing, AspeedI2CBus, 2), + VMSTATE_UINT32(intr_ctrl, AspeedI2CBus), + VMSTATE_UINT32(intr_status, AspeedI2CBus), + VMSTATE_UINT32(cmd, AspeedI2CBus), + VMSTATE_UINT32(buf, AspeedI2CBus), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription aspeed_i2c_vmstate = { + .name = TYPE_ASPEED_I2C, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(intr_status, AspeedI2CState), + VMSTATE_STRUCT_ARRAY(busses, AspeedI2CState, + ASPEED_I2C_NR_BUSSES, 1, aspeed_i2c_bus_vmstate, + AspeedI2CBus), + VMSTATE_END_OF_LIST() + } +}; + +static void aspeed_i2c_reset(DeviceState *dev) +{ + int i; + AspeedI2CState *s = ASPEED_I2C(dev); + + s->intr_status = 0; + + for (i = 0; i < ASPEED_I2C_NR_BUSSES; i++) { + s->busses[i].intr_ctrl = 0; + s->busses[i].intr_status = 0; + s->busses[i].cmd = 0; + s->busses[i].buf = 0; + i2c_end_transfer(s->busses[i].bus); + } +} + +/* + * Address Definitions + * + * 0x000 ... 0x03F: Global Register + * 0x040 ... 0x07F: Device 1 + * 0x080 ... 0x0BF: Device 2 + * 0x0C0 ... 0x0FF: Device 3 + * 0x100 ... 0x13F: Device 4 + * 0x140 ... 0x17F: Device 5 + * 0x180 ... 0x1BF: Device 6 + * 0x1C0 ... 0x1FF: Device 7 + * 0x200 ... 0x2FF: Buffer Pool (unused in linux driver) + * 0x300 ... 0x33F: Device 8 + * 0x340 ... 0x37F: Device 9 + * 0x380 ... 0x3BF: Device 10 + * 0x3C0 ... 0x3FF: Device 11 + * 0x400 ... 0x43F: Device 12 + * 0x440 ... 0x47F: Device 13 + * 0x480 ... 0x4BF: Device 14 + * 0x800 ... 0xFFF: Buffer Pool (unused in linux driver) + */ +static void aspeed_i2c_realize(DeviceState *dev, Error **errp) +{ + int i; + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + AspeedI2CState *s = ASPEED_I2C(dev); + + sysbus_init_irq(sbd, &s->irq); + memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_i2c_ctrl_ops, s, + "aspeed.i2c", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + + for (i = 0; i < ASPEED_I2C_NR_BUSSES; i++) { + char name[16]; + int offset = i < 7 ? 1 : 5; + snprintf(name, sizeof(name), "aspeed.i2c.%d", i); + s->busses[i].controller = s; + s->busses[i].id = i; + s->busses[i].bus = i2c_init_bus(dev, name); + memory_region_init_io(&s->busses[i].mr, OBJECT(dev), + &aspeed_i2c_bus_ops, &s->busses[i], name, 0x40); + memory_region_add_subregion(&s->iomem, 0x40 * (i + offset), + &s->busses[i].mr); + } +} + +static void aspeed_i2c_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &aspeed_i2c_vmstate; + dc->reset = aspeed_i2c_reset; + dc->realize = aspeed_i2c_realize; + dc->desc = "Aspeed I2C Controller"; +} + +static const TypeInfo aspeed_i2c_info = { + .name = TYPE_ASPEED_I2C, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AspeedI2CState), + .class_init = aspeed_i2c_class_init, +}; + +static void aspeed_i2c_register_types(void) +{ + type_register_static(&aspeed_i2c_info); +} + +type_init(aspeed_i2c_register_types) + + +I2CBus *aspeed_i2c_get_bus(DeviceState *dev, int busnr) +{ + AspeedI2CState *s = ASPEED_I2C(dev); + I2CBus *bus = NULL; + + if (busnr >= 0 && busnr < ASPEED_I2C_NR_BUSSES) { + bus = s->busses[busnr].bus; + } + + return bus; +} diff --git a/hw/i2c/bitbang_i2c.c b/hw/i2c/bitbang_i2c.c index 6ed206020..d3a29891f 100644 --- a/hw/i2c/bitbang_i2c.c +++ b/hw/i2c/bitbang_i2c.c @@ -210,13 +210,14 @@ static void bitbang_i2c_gpio_set(void *opaque, int irq, int level) } } -static int gpio_i2c_init(SysBusDevice *sbd) +static void gpio_i2c_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - GPIOI2CState *s = GPIO_I2C(dev); + DeviceState *dev = DEVICE(obj); + GPIOI2CState *s = GPIO_I2C(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); I2CBus *bus; - memory_region_init(&s->dummy_iomem, OBJECT(s), "gpio_i2c", 0); + memory_region_init(&s->dummy_iomem, obj, "gpio_i2c", 0); sysbus_init_mmio(sbd, &s->dummy_iomem); bus = i2c_init_bus(dev, "i2c"); @@ -224,16 +225,12 @@ static int gpio_i2c_init(SysBusDevice *sbd) qdev_init_gpio_in(dev, bitbang_i2c_gpio_set, 2); qdev_init_gpio_out(dev, &s->out, 1); - - return 0; } static void gpio_i2c_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = gpio_i2c_init; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->desc = "Virtual GPIO to I2C bridge"; } @@ -242,6 +239,7 @@ static const TypeInfo gpio_i2c_info = { .name = TYPE_GPIO_I2C, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(GPIOI2CState), + .instance_init = gpio_i2c_init, .class_init = gpio_i2c_class_init, }; diff --git a/hw/i2c/core.c b/hw/i2c/core.c index ba22104af..4afbe0bde 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -10,12 +10,21 @@ #include "qemu/osdep.h" #include "hw/i2c/i2c.h" +typedef struct I2CNode I2CNode; + +struct I2CNode { + I2CSlave *elt; + QLIST_ENTRY(I2CNode) next; +}; + +#define I2C_BROADCAST 0x00 + struct I2CBus { BusState qbus; - I2CSlave *current_dev; - I2CSlave *dev; + QLIST_HEAD(, I2CNode) current_devs; uint8_t saved_address; + bool broadcast; }; static Property i2c_props[] = { @@ -36,17 +45,14 @@ static void i2c_bus_pre_save(void *opaque) { I2CBus *bus = opaque; - bus->saved_address = bus->current_dev ? bus->current_dev->address : -1; -} - -static int i2c_bus_post_load(void *opaque, int version_id) -{ - I2CBus *bus = opaque; - - /* The bus is loaded before attached devices, so load and save the - current device id. Devices will check themselves as loaded. */ - bus->current_dev = NULL; - return 0; + bus->saved_address = -1; + if (!QLIST_EMPTY(&bus->current_devs)) { + if (!bus->broadcast) { + bus->saved_address = QLIST_FIRST(&bus->current_devs)->elt->address; + } else { + bus->saved_address = I2C_BROADCAST; + } + } } static const VMStateDescription vmstate_i2c_bus = { @@ -54,7 +60,6 @@ static const VMStateDescription vmstate_i2c_bus = { .version_id = 1, .minimum_version_id = 1, .pre_save = i2c_bus_pre_save, - .post_load = i2c_bus_post_load, .fields = (VMStateField[]) { VMSTATE_UINT8(saved_address, I2CBus), VMSTATE_END_OF_LIST() @@ -67,6 +72,7 @@ I2CBus *i2c_init_bus(DeviceState *parent, const char *name) I2CBus *bus; bus = I2C_BUS(qbus_create(TYPE_I2C_BUS, parent, name)); + QLIST_INIT(&bus->current_devs); vmstate_register(NULL, -1, &vmstate_i2c_bus, bus); return bus; } @@ -79,7 +85,7 @@ void i2c_set_slave_address(I2CSlave *dev, uint8_t address) /* Return nonzero if bus is busy. */ int i2c_bus_busy(I2CBus *bus) { - return bus->current_dev != NULL; + return !QLIST_EMPTY(&bus->current_devs); } /* Returns non-zero if the address is not valid. */ @@ -87,95 +93,127 @@ int i2c_bus_busy(I2CBus *bus) int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv) { BusChild *kid; - I2CSlave *slave = NULL; I2CSlaveClass *sc; + I2CNode *node; + + if (address == I2C_BROADCAST) { + /* + * This is a broadcast, the current_devs will be all the devices of the + * bus. + */ + bus->broadcast = true; + } QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) { DeviceState *qdev = kid->child; I2CSlave *candidate = I2C_SLAVE(qdev); - if (candidate->address == address) { - slave = candidate; - break; + if ((candidate->address == address) || (bus->broadcast)) { + node = g_malloc(sizeof(struct I2CNode)); + node->elt = candidate; + QLIST_INSERT_HEAD(&bus->current_devs, node, next); + if (!bus->broadcast) { + break; + } } } - if (!slave) { + if (QLIST_EMPTY(&bus->current_devs)) { return 1; } - sc = I2C_SLAVE_GET_CLASS(slave); - /* If the bus is already busy, assume this is a repeated - start condition. */ - bus->current_dev = slave; - if (sc->event) { - sc->event(slave, recv ? I2C_START_RECV : I2C_START_SEND); + QLIST_FOREACH(node, &bus->current_devs, next) { + sc = I2C_SLAVE_GET_CLASS(node->elt); + /* If the bus is already busy, assume this is a repeated + start condition. */ + if (sc->event) { + sc->event(node->elt, recv ? I2C_START_RECV : I2C_START_SEND); + } } return 0; } void i2c_end_transfer(I2CBus *bus) { - I2CSlave *dev = bus->current_dev; I2CSlaveClass *sc; + I2CNode *node, *next; - if (!dev) { + if (QLIST_EMPTY(&bus->current_devs)) { return; } - sc = I2C_SLAVE_GET_CLASS(dev); - if (sc->event) { - sc->event(dev, I2C_FINISH); + QLIST_FOREACH_SAFE(node, &bus->current_devs, next, next) { + sc = I2C_SLAVE_GET_CLASS(node->elt); + if (sc->event) { + sc->event(node->elt, I2C_FINISH); + } + QLIST_REMOVE(node, next); + g_free(node); } - - bus->current_dev = NULL; + bus->broadcast = false; } -int i2c_send(I2CBus *bus, uint8_t data) +int i2c_send_recv(I2CBus *bus, uint8_t *data, bool send) { - I2CSlave *dev = bus->current_dev; I2CSlaveClass *sc; + I2CNode *node; + int ret = 0; + + if (send) { + QLIST_FOREACH(node, &bus->current_devs, next) { + sc = I2C_SLAVE_GET_CLASS(node->elt); + if (sc->send) { + ret = ret || sc->send(node->elt, *data); + } else { + ret = -1; + } + } + return ret ? -1 : 0; + } else { + if ((QLIST_EMPTY(&bus->current_devs)) || (bus->broadcast)) { + return -1; + } - if (!dev) { + sc = I2C_SLAVE_GET_CLASS(QLIST_FIRST(&bus->current_devs)->elt); + if (sc->recv) { + ret = sc->recv(QLIST_FIRST(&bus->current_devs)->elt); + if (ret < 0) { + return ret; + } else { + *data = ret; + return 0; + } + } return -1; } +} - sc = I2C_SLAVE_GET_CLASS(dev); - if (sc->send) { - return sc->send(dev, data); - } - - return -1; +int i2c_send(I2CBus *bus, uint8_t data) +{ + return i2c_send_recv(bus, &data, true); } int i2c_recv(I2CBus *bus) { - I2CSlave *dev = bus->current_dev; - I2CSlaveClass *sc; - - if (!dev) { - return -1; - } + uint8_t data; + int ret = i2c_send_recv(bus, &data, false); - sc = I2C_SLAVE_GET_CLASS(dev); - if (sc->recv) { - return sc->recv(dev); - } - - return -1; + return ret < 0 ? ret : data; } void i2c_nack(I2CBus *bus) { - I2CSlave *dev = bus->current_dev; I2CSlaveClass *sc; + I2CNode *node; - if (!dev) { + if (QLIST_EMPTY(&bus->current_devs)) { return; } - sc = I2C_SLAVE_GET_CLASS(dev); - if (sc->event) { - sc->event(dev, I2C_NACK); + QLIST_FOREACH(node, &bus->current_devs, next) { + sc = I2C_SLAVE_GET_CLASS(node->elt); + if (sc->event) { + sc->event(node->elt, I2C_NACK); + } } } @@ -183,9 +221,14 @@ static int i2c_slave_post_load(void *opaque, int version_id) { I2CSlave *dev = opaque; I2CBus *bus; + I2CNode *node; + bus = I2C_BUS(qdev_get_parent_bus(DEVICE(dev))); - if (bus->saved_address == dev->address) { - bus->current_dev = dev; + if ((bus->saved_address == dev->address) || + (bus->saved_address == I2C_BROADCAST)) { + node = g_malloc(sizeof(struct I2CNode)); + node->elt = dev; + QLIST_INSERT_HEAD(&bus->current_devs, node, next); } return 0; } diff --git a/hw/i2c/exynos4210_i2c.c b/hw/i2c/exynos4210_i2c.c index 8c2a2c163..c96fa7d7b 100644 --- a/hw/i2c/exynos4210_i2c.c +++ b/hw/i2c/exynos4210_i2c.c @@ -299,33 +299,32 @@ static void exynos4210_i2c_reset(DeviceState *d) s->scl_free = true; } -static int exynos4210_i2c_realize(SysBusDevice *sbd) +static void exynos4210_i2c_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - Exynos4210I2CState *s = EXYNOS4_I2C(dev); + DeviceState *dev = DEVICE(obj); + Exynos4210I2CState *s = EXYNOS4_I2C(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - memory_region_init_io(&s->iomem, OBJECT(s), &exynos4210_i2c_ops, s, + memory_region_init_io(&s->iomem, obj, &exynos4210_i2c_ops, s, TYPE_EXYNOS4_I2C, EXYNOS4_I2C_MEM_SIZE); sysbus_init_mmio(sbd, &s->iomem); sysbus_init_irq(sbd, &s->irq); s->bus = i2c_init_bus(dev, "i2c"); - return 0; } static void exynos4210_i2c_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *sbdc = SYS_BUS_DEVICE_CLASS(klass); dc->vmsd = &exynos4210_i2c_vmstate; dc->reset = exynos4210_i2c_reset; - sbdc->init = exynos4210_i2c_realize; } static const TypeInfo exynos4210_i2c_type_info = { .name = TYPE_EXYNOS4_I2C, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Exynos4210I2CState), + .instance_init = exynos4210_i2c_init, .class_init = exynos4210_i2c_class_init, }; diff --git a/hw/i2c/i2c-ddc.c b/hw/i2c/i2c-ddc.c new file mode 100644 index 000000000..122721293 --- /dev/null +++ b/hw/i2c/i2c-ddc.c @@ -0,0 +1,308 @@ +/* A simple I2C slave for returning monitor EDID data via DDC. + * + * Copyright (c) 2011 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/i2c/i2c.h" +#include "hw/i2c/i2c-ddc.h" + +#ifndef DEBUG_I2CDDC +#define DEBUG_I2CDDC 0 +#endif + +#define DPRINTF(fmt, ...) do { \ + if (DEBUG_I2CDDC) { \ + qemu_log("i2c-ddc: " fmt , ## __VA_ARGS__); \ + } \ +} while (0); + +/* Structure defining a monitor's characteristics in a + * readable format: this should be passed to build_edid_blob() + * to convert it into the 128 byte binary EDID blob. + * Not all bits of the EDID are customisable here. + */ +struct EDIDData { + char manuf_id[3]; /* three upper case letters */ + uint16_t product_id; + uint32_t serial_no; + uint8_t manuf_week; + int manuf_year; + uint8_t h_cm; + uint8_t v_cm; + uint8_t gamma; + char monitor_name[14]; + char serial_no_string[14]; + /* Range limits */ + uint8_t vmin; /* Hz */ + uint8_t vmax; /* Hz */ + uint8_t hmin; /* kHz */ + uint8_t hmax; /* kHz */ + uint8_t pixclock; /* MHz / 10 */ + uint8_t timing_data[18]; +}; + +typedef struct EDIDData EDIDData; + +/* EDID data for a simple LCD monitor */ +static const EDIDData lcd_edid = { + /* The manuf_id ought really to be an assigned EISA ID */ + .manuf_id = "QMU", + .product_id = 0, + .serial_no = 1, + .manuf_week = 1, + .manuf_year = 2011, + .h_cm = 40, + .v_cm = 30, + .gamma = 0x78, + .monitor_name = "QEMU monitor", + .serial_no_string = "1", + .vmin = 40, + .vmax = 120, + .hmin = 30, + .hmax = 100, + .pixclock = 18, + .timing_data = { + /* Borrowed from a 21" LCD */ + 0x48, 0x3f, 0x40, 0x30, 0x62, 0xb0, 0x32, 0x40, 0x40, + 0xc0, 0x13, 0x00, 0x98, 0x32, 0x11, 0x00, 0x00, 0x1e + } +}; + +static uint8_t manuf_char_to_int(char c) +{ + return (c - 'A') & 0x1f; +} + +static void write_ascii_descriptor_block(uint8_t *descblob, uint8_t blocktype, + const char *string) +{ + /* Write an EDID Descriptor Block of the "ascii string" type */ + int i; + descblob[0] = descblob[1] = descblob[2] = descblob[4] = 0; + descblob[3] = blocktype; + /* The rest is 13 bytes of ASCII; if less then the rest must + * be filled with newline then spaces + */ + for (i = 5; i < 19; i++) { + descblob[i] = string[i - 5]; + if (!descblob[i]) { + break; + } + } + if (i < 19) { + descblob[i++] = '\n'; + } + for ( ; i < 19; i++) { + descblob[i] = ' '; + } +} + +static void write_range_limits_descriptor(const EDIDData *edid, + uint8_t *descblob) +{ + int i; + descblob[0] = descblob[1] = descblob[2] = descblob[4] = 0; + descblob[3] = 0xfd; + descblob[5] = edid->vmin; + descblob[6] = edid->vmax; + descblob[7] = edid->hmin; + descblob[8] = edid->hmax; + descblob[9] = edid->pixclock; + descblob[10] = 0; + descblob[11] = 0xa; + for (i = 12; i < 19; i++) { + descblob[i] = 0x20; + } +} + +static void build_edid_blob(const EDIDData *edid, uint8_t *blob) +{ + /* Write an EDID 1.3 format blob (128 bytes) based + * on the EDIDData structure. + */ + int i; + uint8_t cksum; + + /* 00-07 : header */ + blob[0] = blob[7] = 0; + for (i = 1 ; i < 7; i++) { + blob[i] = 0xff; + } + /* 08-09 : manufacturer ID */ + blob[8] = (manuf_char_to_int(edid->manuf_id[0]) << 2) + | (manuf_char_to_int(edid->manuf_id[1]) >> 3); + blob[9] = (manuf_char_to_int(edid->manuf_id[1]) << 5) + | manuf_char_to_int(edid->manuf_id[2]); + /* 10-11 : product ID code */ + blob[10] = edid->product_id; + blob[11] = edid->product_id >> 8; + blob[12] = edid->serial_no; + blob[13] = edid->serial_no >> 8; + blob[14] = edid->serial_no >> 16; + blob[15] = edid->serial_no >> 24; + /* 16 : week of manufacture */ + blob[16] = edid->manuf_week; + /* 17 : year of manufacture - 1990 */ + blob[17] = edid->manuf_year - 1990; + /* 18, 19 : EDID version and revision */ + blob[18] = 1; + blob[19] = 3; + /* 20 - 24 : basic display parameters */ + /* We are always a digital display */ + blob[20] = 0x80; + /* 21, 22 : max h/v size in cm */ + blob[21] = edid->h_cm; + blob[22] = edid->v_cm; + /* 23 : gamma (divide by 100 then add 1 for actual value) */ + blob[23] = edid->gamma; + /* 24 feature support: no power management, RGB, preferred timing mode, + * standard colour space + */ + blob[24] = 0x0e; + /* 25 - 34 : chromaticity coordinates. These are the + * standard sRGB chromaticity values + */ + blob[25] = 0xee; + blob[26] = 0x91; + blob[27] = 0xa3; + blob[28] = 0x54; + blob[29] = 0x4c; + blob[30] = 0x99; + blob[31] = 0x26; + blob[32] = 0x0f; + blob[33] = 0x50; + blob[34] = 0x54; + /* 35, 36 : Established timings: claim to support everything */ + blob[35] = blob[36] = 0xff; + /* 37 : manufacturer's reserved timing: none */ + blob[37] = 0; + /* 38 - 53 : standard timing identification + * don't claim anything beyond what the 'established timings' + * already provide. Unused slots must be (0x1, 0x1) + */ + for (i = 38; i < 54; i++) { + blob[i] = 0x1; + } + /* 54 - 71 : descriptor block 1 : must be preferred timing data */ + memcpy(blob + 54, edid->timing_data, 18); + /* 72 - 89, 90 - 107, 108 - 125 : descriptor block 2, 3, 4 + * Order not important, but we must have a monitor name and a + * range limits descriptor. + */ + write_range_limits_descriptor(edid, blob + 72); + write_ascii_descriptor_block(blob + 90, 0xfc, edid->monitor_name); + write_ascii_descriptor_block(blob + 108, 0xff, edid->serial_no_string); + + /* 126 : extension flag */ + blob[126] = 0; + + cksum = 0; + for (i = 0; i < 127; i++) { + cksum += blob[i]; + } + /* 127 : checksum */ + blob[127] = -cksum; + if (DEBUG_I2CDDC) { + qemu_hexdump((char *)blob, stdout, "", 128); + } +} + +static void i2c_ddc_reset(DeviceState *ds) +{ + I2CDDCState *s = I2CDDC(ds); + + s->firstbyte = false; + s->reg = 0; +} + +static void i2c_ddc_event(I2CSlave *i2c, enum i2c_event event) +{ + I2CDDCState *s = I2CDDC(i2c); + + if (event == I2C_START_SEND) { + s->firstbyte = true; + } +} + +static int i2c_ddc_rx(I2CSlave *i2c) +{ + I2CDDCState *s = I2CDDC(i2c); + + int value; + value = s->edid_blob[s->reg]; + s->reg++; + return value; +} + +static int i2c_ddc_tx(I2CSlave *i2c, uint8_t data) +{ + I2CDDCState *s = I2CDDC(i2c); + if (s->firstbyte) { + s->reg = data; + s->firstbyte = false; + DPRINTF("[EDID] Written new pointer: %u\n", data); + return 1; + } + + /* Ignore all writes */ + s->reg++; + return 1; +} + +static void i2c_ddc_init(Object *obj) +{ + I2CDDCState *s = I2CDDC(obj); + build_edid_blob(&lcd_edid, s->edid_blob); +} + +static const VMStateDescription vmstate_i2c_ddc = { + .name = TYPE_I2CDDC, + .version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_BOOL(firstbyte, I2CDDCState), + VMSTATE_UINT8(reg, I2CDDCState), + VMSTATE_END_OF_LIST() + } +}; + +static void i2c_ddc_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + I2CSlaveClass *isc = I2C_SLAVE_CLASS(oc); + + dc->reset = i2c_ddc_reset; + dc->vmsd = &vmstate_i2c_ddc; + isc->event = i2c_ddc_event; + isc->recv = i2c_ddc_rx; + isc->send = i2c_ddc_tx; +} + +static TypeInfo i2c_ddc_info = { + .name = TYPE_I2CDDC, + .parent = TYPE_I2C_SLAVE, + .instance_size = sizeof(I2CDDCState), + .instance_init = i2c_ddc_init, + .class_init = i2c_ddc_class_init +}; + +static void ddc_register_devices(void) +{ + type_register_static(&i2c_ddc_info); +} + +type_init(ddc_register_devices); diff --git a/hw/i2c/imx_i2c.c b/hw/i2c/imx_i2c.c index a01e43ebe..37e5a62ce 100644 --- a/hw/i2c/imx_i2c.c +++ b/hw/i2c/imx_i2c.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "hw/i2c/imx_i2c.h" #include "hw/i2c/i2c.h" +#include "qemu/log.h" #ifndef DEBUG_IMX_I2C #define DEBUG_IMX_I2C 0 @@ -247,7 +248,7 @@ static void imx_i2c_write(void *opaque, hwaddr offset, if (s->address == ADDR_RESET) { if (i2c_start_transfer(s->bus, extract32(s->i2dr_write, 1, 7), extract32(s->i2dr_write, 0, 1))) { - /* if non zero is returned, the adress is not valid */ + /* if non zero is returned, the address is not valid */ s->i2sr |= I2SR_RXAK; } else { s->address = s->i2dr_write; diff --git a/hw/i2c/omap_i2c.c b/hw/i2c/omap_i2c.c index 67fbbff8e..f7c92ea00 100644 --- a/hw/i2c/omap_i2c.c +++ b/hw/i2c/omap_i2c.c @@ -22,6 +22,7 @@ #include "hw/arm/omap.h" #include "hw/sysbus.h" #include "qemu/error-report.h" +#include "qapi/error.h" #define TYPE_OMAP_I2C "omap_i2c" #define OMAP_I2C(obj) OBJECT_CHECK(OMAPI2CState, (obj), TYPE_OMAP_I2C) @@ -445,29 +446,35 @@ static const MemoryRegionOps omap_i2c_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int omap_i2c_init(SysBusDevice *sbd) +static void omap_i2c_init(Object *obj) +{ + DeviceState *dev = DEVICE(obj); + OMAPI2CState *s = OMAP_I2C(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + sysbus_init_irq(sbd, &s->irq); + sysbus_init_irq(sbd, &s->drq[0]); + sysbus_init_irq(sbd, &s->drq[1]); + sysbus_init_mmio(sbd, &s->iomem); + s->bus = i2c_init_bus(dev, NULL); +} + +static void omap_i2c_realize(DeviceState *dev, Error **errp) { - DeviceState *dev = DEVICE(sbd); OMAPI2CState *s = OMAP_I2C(dev); + memory_region_init_io(&s->iomem, OBJECT(dev), &omap_i2c_ops, s, "omap.i2c", + (s->revision < OMAP2_INTR_REV) ? 0x800 : 0x1000); + if (!s->fclk) { - error_report("omap_i2c: fclk not connected"); - return -1; + error_setg(errp, "omap_i2c: fclk not connected"); + return; } if (s->revision >= OMAP2_INTR_REV && !s->iclk) { /* Note that OMAP1 doesn't have a separate interface clock */ - error_report("omap_i2c: iclk not connected"); - return -1; + error_setg(errp, "omap_i2c: iclk not connected"); + return; } - - sysbus_init_irq(sbd, &s->irq); - sysbus_init_irq(sbd, &s->drq[0]); - sysbus_init_irq(sbd, &s->drq[1]); - memory_region_init_io(&s->iomem, OBJECT(s), &omap_i2c_ops, s, "omap.i2c", - (s->revision < OMAP2_INTR_REV) ? 0x800 : 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - s->bus = i2c_init_bus(dev, NULL); - return 0; } static Property omap_i2c_properties[] = { @@ -480,18 +487,19 @@ static Property omap_i2c_properties[] = { static void omap_i2c_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = omap_i2c_init; + dc->props = omap_i2c_properties; dc->reset = omap_i2c_reset; /* Reason: pointer properties "iclk", "fclk" */ dc->cannot_instantiate_with_device_add_yet = true; + dc->realize = omap_i2c_realize; } static const TypeInfo omap_i2c_info = { .name = TYPE_OMAP_I2C, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OMAPI2CState), + .instance_init = omap_i2c_init, .class_init = omap_i2c_class_init, }; diff --git a/hw/i2c/smbus_ich9.c b/hw/i2c/smbus_ich9.c index 498f03e83..48fab2262 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/i2c/versatile_i2c.c b/hw/i2c/versatile_i2c.c index fee3bc761..da9f298ee 100644 --- a/hw/i2c/versatile_i2c.c +++ b/hw/i2c/versatile_i2c.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" #include "bitbang_i2c.h" +#include "qemu/log.h" #define TYPE_VERSATILE_I2C "versatile_i2c" #define VERSATILE_I2C(obj) \ @@ -78,32 +79,25 @@ static const MemoryRegionOps versatile_i2c_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int versatile_i2c_init(SysBusDevice *sbd) +static void versatile_i2c_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - VersatileI2CState *s = VERSATILE_I2C(dev); + DeviceState *dev = DEVICE(obj); + VersatileI2CState *s = VERSATILE_I2C(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); I2CBus *bus; bus = i2c_init_bus(dev, "i2c"); s->bitbang = bitbang_i2c_init(bus); - memory_region_init_io(&s->iomem, OBJECT(s), &versatile_i2c_ops, s, + memory_region_init_io(&s->iomem, obj, &versatile_i2c_ops, s, "versatile_i2c", 0x1000); sysbus_init_mmio(sbd, &s->iomem); - return 0; -} - -static void versatile_i2c_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = versatile_i2c_init; } static const TypeInfo versatile_i2c_info = { .name = TYPE_VERSATILE_I2C, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(VersatileI2CState), - .class_init = versatile_i2c_class_init, + .instance_init = versatile_i2c_init, }; static void versatile_i2c_register_types(void) diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs index b52d5b875..90e94ffef 100644 --- a/hw/i386/Makefile.objs +++ b/hw/i386/Makefile.objs @@ -2,7 +2,7 @@ obj-$(CONFIG_KVM) += kvm/ obj-y += multiboot.o obj-y += pc.o pc_piix.o pc_q35.o obj-y += pc_sysfw.o -obj-y += intel_iommu.o +obj-y += x86-iommu.o intel_iommu.o obj-$(CONFIG_XEN) += ../xenpv/ xen/ obj-y += kvmvapic.o diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 64770034f..a26a4bb03 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -23,7 +23,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "acpi-build.h" -#include <glib.h> #include "qemu-common.h" #include "qemu/bitmap.h" #include "qemu/error-report.h" @@ -34,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" @@ -44,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" @@ -51,13 +52,16 @@ #include "hw/i386/ich9.h" #include "hw/pci/pci_bus.h" #include "hw/pci-host/q35.h" -#include "hw/i386/intel_iommu.h" +#include "hw/i386/x86-iommu.h" #include "hw/timer/hpet.h" #include "hw/acpi/aml-build.h" #include "qapi/qmp/qint.h" #include "qom/qom-qobject.h" +#include "hw/i386/x86-iommu.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 @@ -77,6 +81,9 @@ #define ACPI_BUILD_DPRINTF(fmt, ...) #endif +/* Default IOAPIC ID */ +#define ACPI_BUILD_IOAPIC_ID 0x0 + typedef struct AcpiMcfgInfo { uint64_t mcfg_base; uint32_t mcfg_size; @@ -94,7 +101,6 @@ typedef struct AcpiPmInfo { uint32_t gpe0_blk_len; uint32_t io_base; uint16_t cpu_hp_io_base; - uint16_t cpu_hp_io_len; uint16_t mem_hp_io_base; uint16_t mem_hp_io_len; uint16_t pcihp_io_base; @@ -142,7 +148,6 @@ static void acpi_get_pm_info(AcpiPmInfo *pm) } assert(obj); - pm->cpu_hp_io_len = ACPI_GPE_PROC_LEN; pm->mem_hp_io_base = ACPI_MEMORY_HOTPLUG_BASE; pm->mem_hp_io_len = ACPI_MEMORY_HOTPLUG_IO_LEN; @@ -228,26 +233,27 @@ static Object *acpi_get_i386_pci_host(void) return OBJECT(host); } -static void acpi_get_pci_info(PcPciInfo *info) +static void acpi_get_pci_holes(Range *hole, Range *hole64) { Object *pci_host; - pci_host = acpi_get_i386_pci_host(); g_assert(pci_host); - info->w32.begin = object_property_get_int(pci_host, + range_set_bounds1(hole, + object_property_get_int(pci_host, PCI_HOST_PROP_PCI_HOLE_START, - NULL); - info->w32.end = object_property_get_int(pci_host, - PCI_HOST_PROP_PCI_HOLE_END, - NULL); - info->w64.begin = object_property_get_int(pci_host, + NULL), + object_property_get_int(pci_host, + PCI_HOST_PROP_PCI_HOLE_END, + NULL)); + range_set_bounds1(hole64, + object_property_get_int(pci_host, PCI_HOST_PROP_PCI_HOLE64_START, - NULL); - info->w64.end = object_property_get_int(pci_host, - PCI_HOST_PROP_PCI_HOLE64_END, - NULL); + NULL), + object_property_get_int(pci_host, + PCI_HOST_PROP_PCI_HOLE64_END, + NULL)); } #define ACPI_PORT_SMI_CMD 0x00b2 /* TODO: this is APM_CNT_IOPORT */ @@ -262,7 +268,7 @@ static void acpi_align_size(GArray *blob, unsigned align) /* FACS */ static void -build_facs(GArray *table_data, GArray *linker) +build_facs(GArray *table_data, BIOSLinker *linker) { AcpiFacsDescriptorRev1 *facs = acpi_data_push(table_data, sizeof *facs); memcpy(&facs->signature, "FACS", 4); @@ -307,38 +313,61 @@ static void fadt_setup(AcpiFadtDescriptorRev1 *fadt, AcpiPmInfo *pm) /* FADT */ static void -build_fadt(GArray *table_data, GArray *linker, AcpiPmInfo *pm, - unsigned facs, unsigned dsdt, +build_fadt(GArray *table_data, BIOSLinker *linker, AcpiPmInfo *pm, + unsigned facs_tbl_offset, unsigned dsdt_tbl_offset, const char *oem_id, const char *oem_table_id) { AcpiFadtDescriptorRev1 *fadt = acpi_data_push(table_data, sizeof(*fadt)); + unsigned fw_ctrl_offset = (char *)&fadt->firmware_ctrl - table_data->data; + unsigned dsdt_entry_offset = (char *)&fadt->dsdt - table_data->data; - fadt->firmware_ctrl = cpu_to_le32(facs); /* FACS address to be filled by Guest linker */ - bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE, - ACPI_BUILD_TABLE_FILE, - table_data, &fadt->firmware_ctrl, - sizeof fadt->firmware_ctrl); + bios_linker_loader_add_pointer(linker, + ACPI_BUILD_TABLE_FILE, fw_ctrl_offset, sizeof(fadt->firmware_ctrl), + ACPI_BUILD_TABLE_FILE, facs_tbl_offset); - fadt->dsdt = cpu_to_le32(dsdt); /* DSDT address to be filled by Guest linker */ - bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE, - ACPI_BUILD_TABLE_FILE, - table_data, &fadt->dsdt, - sizeof fadt->dsdt); - fadt_setup(fadt, pm); + bios_linker_loader_add_pointer(linker, + ACPI_BUILD_TABLE_FILE, dsdt_entry_offset, sizeof(fadt->dsdt), + ACPI_BUILD_TABLE_FILE, dsdt_tbl_offset); build_header(linker, table_data, (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, GArray *linker, PCMachineState *pcms) +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; @@ -351,31 +380,13 @@ build_madt(GArray *table_data, GArray *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 = apic_id; - 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); io_apic = acpi_data_push(table_data, sizeof *io_apic); io_apic->type = ACPI_APIC_IO; io_apic->length = sizeof(*io_apic); -#define ACPI_BUILD_IOAPIC_ID 0x0 io_apic->io_apic_id = ACPI_BUILD_IOAPIC_ID; io_apic->address = cpu_to_le32(IO_APIC_DEFAULT_ADDRESS); io_apic->interrupt = cpu_to_le32(0); @@ -589,6 +600,10 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus, QLIST_FOREACH(sec, &bus->child, sibling) { int32_t devfn = sec->parent_dev->devfn; + if (pci_bus_is_root(sec) || pci_bus_is_express(sec)) { + continue; + } + aml_append(method, aml_name("^S%.02X.PCNT", devfn)); } } @@ -736,6 +751,27 @@ static void crs_range_free(gpointer data) g_free(entry); } +typedef struct CrsRangeSet { + GPtrArray *io_ranges; + GPtrArray *mem_ranges; + GPtrArray *mem_64bit_ranges; + } CrsRangeSet; + +static void crs_range_set_init(CrsRangeSet *range_set) +{ + range_set->io_ranges = g_ptr_array_new_with_free_func(crs_range_free); + range_set->mem_ranges = g_ptr_array_new_with_free_func(crs_range_free); + range_set->mem_64bit_ranges = + g_ptr_array_new_with_free_func(crs_range_free); +} + +static void crs_range_set_free(CrsRangeSet *range_set) +{ + g_ptr_array_free(range_set->io_ranges, true); + g_ptr_array_free(range_set->mem_ranges, true); + g_ptr_array_free(range_set->mem_64bit_ranges, true); +} + static gint crs_range_compare(gconstpointer a, gconstpointer b) { CrsRangeEntry *entry_a = *(CrsRangeEntry **)a; @@ -820,18 +856,17 @@ static void crs_range_merge(GPtrArray *range) g_ptr_array_free(tmp, true); } -static Aml *build_crs(PCIHostState *host, - GPtrArray *io_ranges, GPtrArray *mem_ranges) +static Aml *build_crs(PCIHostState *host, CrsRangeSet *range_set) { Aml *crs = aml_resource_template(); - GPtrArray *host_io_ranges = g_ptr_array_new_with_free_func(crs_range_free); - GPtrArray *host_mem_ranges = g_ptr_array_new_with_free_func(crs_range_free); + CrsRangeSet temp_range_set; CrsRangeEntry *entry; uint8_t max_bus = pci_bus_num(host->bus); uint8_t type; int devfn; int i; + crs_range_set_init(&temp_range_set); for (devfn = 0; devfn < ARRAY_SIZE(host->bus->devices); devfn++) { uint64_t range_base, range_limit; PCIDevice *dev = host->bus->devices[devfn]; @@ -855,9 +890,11 @@ static Aml *build_crs(PCIHostState *host, } if (r->type & PCI_BASE_ADDRESS_SPACE_IO) { - crs_range_insert(host_io_ranges, range_base, range_limit); + crs_range_insert(temp_range_set.io_ranges, + range_base, range_limit); } else { /* "memory" */ - crs_range_insert(host_mem_ranges, range_base, range_limit); + crs_range_insert(temp_range_set.mem_ranges, + range_base, range_limit); } } @@ -876,7 +913,8 @@ static Aml *build_crs(PCIHostState *host, * that do not support multiple root buses */ if (range_base && range_base <= range_limit) { - crs_range_insert(host_io_ranges, range_base, range_limit); + crs_range_insert(temp_range_set.io_ranges, + range_base, range_limit); } range_base = @@ -889,7 +927,14 @@ static Aml *build_crs(PCIHostState *host, * that do not support multiple root buses */ if (range_base && range_base <= range_limit) { - crs_range_insert(host_mem_ranges, range_base, range_limit); + uint64_t length = range_limit - range_base + 1; + if (range_limit <= UINT32_MAX && length <= UINT32_MAX) { + crs_range_insert(temp_range_set.mem_ranges, + range_base, range_limit); + } else { + crs_range_insert(temp_range_set.mem_64bit_ranges, + range_base, range_limit); + } } range_base = @@ -902,35 +947,55 @@ static Aml *build_crs(PCIHostState *host, * that do not support multiple root buses */ if (range_base && range_base <= range_limit) { - crs_range_insert(host_mem_ranges, range_base, range_limit); + uint64_t length = range_limit - range_base + 1; + if (range_limit <= UINT32_MAX && length <= UINT32_MAX) { + crs_range_insert(temp_range_set.mem_ranges, + range_base, range_limit); + } else { + crs_range_insert(temp_range_set.mem_64bit_ranges, + range_base, range_limit); + } } } } - crs_range_merge(host_io_ranges); - for (i = 0; i < host_io_ranges->len; i++) { - entry = g_ptr_array_index(host_io_ranges, i); + crs_range_merge(temp_range_set.io_ranges); + for (i = 0; i < temp_range_set.io_ranges->len; i++) { + entry = g_ptr_array_index(temp_range_set.io_ranges, i); aml_append(crs, aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE, AML_ENTIRE_RANGE, 0, entry->base, entry->limit, 0, entry->limit - entry->base + 1)); - crs_range_insert(io_ranges, entry->base, entry->limit); + crs_range_insert(range_set->io_ranges, entry->base, entry->limit); } - g_ptr_array_free(host_io_ranges, true); - crs_range_merge(host_mem_ranges); - for (i = 0; i < host_mem_ranges->len; i++) { - entry = g_ptr_array_index(host_mem_ranges, i); + crs_range_merge(temp_range_set.mem_ranges); + for (i = 0; i < temp_range_set.mem_ranges->len; i++) { + entry = g_ptr_array_index(temp_range_set.mem_ranges, i); aml_append(crs, aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, AML_NON_CACHEABLE, AML_READ_WRITE, 0, entry->base, entry->limit, 0, entry->limit - entry->base + 1)); - crs_range_insert(mem_ranges, entry->base, entry->limit); + crs_range_insert(range_set->mem_ranges, entry->base, entry->limit); + } + + crs_range_merge(temp_range_set.mem_64bit_ranges); + for (i = 0; i < temp_range_set.mem_64bit_ranges->len; i++) { + entry = g_ptr_array_index(temp_range_set.mem_64bit_ranges, i); + aml_append(crs, + aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, + AML_MAX_FIXED, AML_NON_CACHEABLE, + AML_READ_WRITE, + 0, entry->base, entry->limit, 0, + entry->limit - entry->base + 1)); + crs_range_insert(range_set->mem_64bit_ranges, + entry->base, entry->limit); } - g_ptr_array_free(host_mem_ranges, true); + + crs_range_set_free(&temp_range_set); aml_append(crs, aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE, @@ -943,114 +1008,6 @@ static Aml *build_crs(PCIHostState *host, return crs; } -static void build_processor_devices(Aml *sb_scope, MachineState *machine, - AcpiPmInfo *pm) -{ - int i, apic_idx; - Aml *dev; - Aml *crs; - Aml *pkg; - Aml *field; - Aml *ifctx; - Aml *method; - MachineClass *mc = MACHINE_GET_CLASS(machine); - CPUArchIdList *apic_ids = mc->possible_cpu_arch_ids(machine); - PCMachineState *pcms = PC_MACHINE(machine); - - /* The current AML generator can cover the APIC ID range [0..255], - * inclusive, for VCPU hotplug. */ - QEMU_BUILD_BUG_ON(ACPI_CPU_HOTPLUG_ID_LIMIT > 256); - g_assert(pcms->apic_id_limit <= ACPI_CPU_HOTPLUG_ID_LIMIT); - - /* create PCI0.PRES device and its _CRS to reserve CPU hotplug MMIO */ - dev = aml_device("PCI0." stringify(CPU_HOTPLUG_RESOURCE_DEVICE)); - aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A06"))); - aml_append(dev, - aml_name_decl("_UID", aml_string("CPU Hotplug resources")) - ); - /* device present, functioning, decoding, not shown in UI */ - aml_append(dev, aml_name_decl("_STA", aml_int(0xB))); - crs = aml_resource_template(); - aml_append(crs, - aml_io(AML_DECODE16, pm->cpu_hp_io_base, pm->cpu_hp_io_base, 1, - pm->cpu_hp_io_len) - ); - aml_append(dev, aml_name_decl("_CRS", crs)); - aml_append(sb_scope, dev); - /* declare CPU hotplug MMIO region and PRS field to access it */ - aml_append(sb_scope, aml_operation_region( - "PRST", AML_SYSTEM_IO, aml_int(pm->cpu_hp_io_base), pm->cpu_hp_io_len)); - field = aml_field("PRST", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE); - aml_append(field, aml_named_field("PRS", 256)); - aml_append(sb_scope, field); - - /* build Processor object for each processor */ - for (i = 0; i < apic_ids->len; i++) { - int apic_id = apic_ids->cpus[i].arch_id; - - assert(apic_id < ACPI_CPU_HOTPLUG_ID_LIMIT); - - dev = aml_processor(apic_id, 0, 0, "CP%.02X", apic_id); - - method = aml_method("_MAT", 0, AML_NOTSERIALIZED); - aml_append(method, - aml_return(aml_call1(CPU_MAT_METHOD, aml_int(apic_id)))); - aml_append(dev, method); - - method = aml_method("_STA", 0, AML_NOTSERIALIZED); - aml_append(method, - aml_return(aml_call1(CPU_STATUS_METHOD, aml_int(apic_id)))); - aml_append(dev, method); - - method = aml_method("_EJ0", 1, AML_NOTSERIALIZED); - aml_append(method, - aml_return(aml_call2(CPU_EJECT_METHOD, aml_int(apic_id), - aml_arg(0))) - ); - aml_append(dev, method); - - aml_append(sb_scope, dev); - } - - /* build this code: - * Method(NTFY, 2) {If (LEqual(Arg0, 0x00)) {Notify(CP00, Arg1)} ...} - */ - /* Arg0 = Processor ID = APIC ID */ - method = aml_method(AML_NOTIFY_METHOD, 2, AML_NOTSERIALIZED); - for (i = 0; i < apic_ids->len; i++) { - int apic_id = apic_ids->cpus[i].arch_id; - - ifctx = aml_if(aml_equal(aml_arg(0), aml_int(apic_id))); - aml_append(ifctx, - aml_notify(aml_name("CP%.02X", apic_id), aml_arg(1)) - ); - aml_append(method, ifctx); - } - aml_append(sb_scope, method); - - /* build "Name(CPON, Package() { One, One, ..., Zero, Zero, ... })" - * - * Note: The ability to create variable-sized packages was first - * introduced in ACPI 2.0. ACPI 1.0 only allowed fixed-size packages - * ith up to 255 elements. Windows guests up to win2k8 fail when - * VarPackageOp is used. - */ - pkg = pcms->apic_id_limit <= 255 ? aml_package(pcms->apic_id_limit) : - aml_varpackage(pcms->apic_id_limit); - - for (i = 0, apic_idx = 0; i < apic_ids->len; i++) { - int apic_id = apic_ids->cpus[i].arch_id; - - for (; apic_idx < apic_id; apic_idx++) { - aml_append(pkg, aml_int(0)); - } - aml_append(pkg, aml_int(apic_ids->cpus[i].cpu ? 1 : 0)); - apic_idx = apic_id + 1; - } - aml_append(sb_scope, aml_name_decl(CPU_ON_BITMAP, pkg)); - g_free(apic_ids); -} - static void build_memory_devices(Aml *sb_scope, int nr_mem, uint16_t io_base, uint16_t io_len) { @@ -1448,8 +1405,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()); @@ -1461,6 +1420,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); } @@ -1979,15 +1946,15 @@ static Aml *build_q35_osc_method(void) } static void -build_dsdt(GArray *table_data, GArray *linker, +build_dsdt(GArray *table_data, BIOSLinker *linker, AcpiPmInfo *pm, AcpiMiscInfo *misc, - PcPciInfo *pci, MachineState *machine) + Range *pci_hole, Range *pci_hole64, MachineState *machine) { CrsRangeEntry *entry; Aml *dsdt, *sb_scope, *scope, *dev, *method, *field, *pkg, *crs; - 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); + CrsRangeSet crs_range_set; 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; @@ -2043,7 +2010,15 @@ build_dsdt(GArray *table_data, GArray *linker, build_q35_pci0_int(dsdt); } - build_cpu_hotplug_aml(dsdt); + 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); @@ -2051,8 +2026,6 @@ build_dsdt(GArray *table_data, GArray *linker, { aml_append(scope, aml_name_decl("_HID", aml_string("ACPI0006"))); - aml_append(scope, aml_method("_L00", 0, AML_NOTSERIALIZED)); - if (misc->is_piix4) { method = aml_method("_E01", 0, AML_NOTSERIALIZED); aml_append(method, @@ -2060,33 +2033,15 @@ build_dsdt(GArray *table_data, GArray *linker, aml_append(method, aml_call0("\\_SB.PCI0.PCNT")); aml_append(method, aml_release(aml_name("\\_SB.PCI0.BLCK"))); aml_append(scope, method); - } else { - aml_append(scope, aml_method("_L01", 0, AML_NOTSERIALIZED)); } - method = aml_method("_E02", 0, AML_NOTSERIALIZED); - aml_append(method, aml_call0("\\_SB." CPU_SCAN_METHOD)); - aml_append(scope, method); - method = aml_method("_E03", 0, AML_NOTSERIALIZED); aml_append(method, aml_call0(MEMORY_HOTPLUG_HANDLER_PATH)); aml_append(scope, method); - - aml_append(scope, aml_method("_L04", 0, AML_NOTSERIALIZED)); - aml_append(scope, aml_method("_L05", 0, AML_NOTSERIALIZED)); - aml_append(scope, aml_method("_L06", 0, AML_NOTSERIALIZED)); - aml_append(scope, aml_method("_L07", 0, AML_NOTSERIALIZED)); - aml_append(scope, aml_method("_L08", 0, AML_NOTSERIALIZED)); - aml_append(scope, aml_method("_L09", 0, AML_NOTSERIALIZED)); - aml_append(scope, aml_method("_L0A", 0, AML_NOTSERIALIZED)); - aml_append(scope, aml_method("_L0B", 0, AML_NOTSERIALIZED)); - aml_append(scope, aml_method("_L0C", 0, AML_NOTSERIALIZED)); - aml_append(scope, aml_method("_L0D", 0, AML_NOTSERIALIZED)); - aml_append(scope, aml_method("_L0E", 0, AML_NOTSERIALIZED)); - aml_append(scope, aml_method("_L0F", 0, AML_NOTSERIALIZED)); } aml_append(dsdt, scope); + crs_range_set_init(&crs_range_set); bus = PC_MACHINE(machine)->bus; if (bus) { QLIST_FOREACH(bus, &bus->child, sibling) { @@ -2113,8 +2068,7 @@ build_dsdt(GArray *table_data, GArray *linker, } aml_append(dev, build_prt(false)); - crs = build_crs(PCI_HOST_BRIDGE(BUS(bus)->parent), - io_ranges, mem_ranges); + crs = build_crs(PCI_HOST_BRIDGE(BUS(bus)->parent), &crs_range_set); aml_append(dev, aml_name_decl("_CRS", crs)); aml_append(scope, dev); aml_append(dsdt, scope); @@ -2135,9 +2089,9 @@ build_dsdt(GArray *table_data, GArray *linker, AML_POS_DECODE, AML_ENTIRE_RANGE, 0x0000, 0x0000, 0x0CF7, 0x0000, 0x0CF8)); - crs_replace_with_free_ranges(io_ranges, 0x0D00, 0xFFFF); - for (i = 0; i < io_ranges->len; i++) { - entry = g_ptr_array_index(io_ranges, i); + crs_replace_with_free_ranges(crs_range_set.io_ranges, 0x0D00, 0xFFFF); + for (i = 0; i < crs_range_set.io_ranges->len; i++) { + entry = g_ptr_array_index(crs_range_set.io_ranges, i); aml_append(crs, aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE, AML_ENTIRE_RANGE, @@ -2150,9 +2104,11 @@ build_dsdt(GArray *table_data, GArray *linker, AML_CACHEABLE, AML_READ_WRITE, 0, 0x000A0000, 0x000BFFFF, 0, 0x00020000)); - crs_replace_with_free_ranges(mem_ranges, pci->w32.begin, pci->w32.end - 1); - for (i = 0; i < mem_ranges->len; i++) { - entry = g_ptr_array_index(mem_ranges, i); + crs_replace_with_free_ranges(crs_range_set.mem_ranges, + range_lob(pci_hole), + range_upb(pci_hole)); + for (i = 0; i < crs_range_set.mem_ranges->len; i++) { + entry = g_ptr_array_index(crs_range_set.mem_ranges, i); aml_append(crs, aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, AML_NON_CACHEABLE, AML_READ_WRITE, @@ -2160,12 +2116,19 @@ build_dsdt(GArray *table_data, GArray *linker, 0, entry->limit - entry->base + 1)); } - if (pci->w64.begin) { - aml_append(crs, - aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, - AML_CACHEABLE, AML_READ_WRITE, - 0, pci->w64.begin, pci->w64.end - 1, 0, - pci->w64.end - pci->w64.begin)); + if (!range_is_empty(pci_hole64)) { + crs_replace_with_free_ranges(crs_range_set.mem_64bit_ranges, + range_lob(pci_hole64), + range_upb(pci_hole64)); + for (i = 0; i < crs_range_set.mem_64bit_ranges->len; i++) { + entry = g_ptr_array_index(crs_range_set.mem_64bit_ranges, i); + aml_append(crs, + aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, + AML_MAX_FIXED, + AML_CACHEABLE, AML_READ_WRITE, + 0, entry->base, entry->limit, + 0, entry->limit - entry->base + 1)); + } } if (misc->tpm_version != TPM_VERSION_UNSPEC) { @@ -2187,8 +2150,7 @@ build_dsdt(GArray *table_data, GArray *linker, aml_append(dev, aml_name_decl("_CRS", crs)); aml_append(scope, dev); - g_ptr_array_free(io_ranges, true); - g_ptr_array_free(mem_ranges, true); + crs_range_set_free(&crs_range_set); /* reserve PCIHP resources */ if (pm->pcihp_io_len) { @@ -2322,8 +2284,6 @@ build_dsdt(GArray *table_data, GArray *linker, sb_scope = aml_scope("\\_SB"); { - build_processor_devices(sb_scope, machine, pm); - build_memory_devices(sb_scope, nr_mem, pm->mem_hp_io_base, pm->mem_hp_io_len); @@ -2373,7 +2333,7 @@ build_dsdt(GArray *table_data, GArray *linker, } static void -build_hpet(GArray *table_data, GArray *linker) +build_hpet(GArray *table_data, BIOSLinker *linker) { Acpi20Hpet *hpet; @@ -2388,32 +2348,31 @@ build_hpet(GArray *table_data, GArray *linker) } static void -build_tpm_tcpa(GArray *table_data, GArray *linker, GArray *tcpalog) +build_tpm_tcpa(GArray *table_data, BIOSLinker *linker, GArray *tcpalog) { Acpi20Tcpa *tcpa = acpi_data_push(table_data, sizeof *tcpa); - uint64_t log_area_start_address = acpi_data_len(tcpalog); + unsigned log_addr_size = sizeof(tcpa->log_area_start_address); + unsigned log_addr_offset = + (char *)&tcpa->log_area_start_address - table_data->data; tcpa->platform_class = cpu_to_le16(TPM_TCPA_ACPI_CLASS_CLIENT); tcpa->log_area_minimum_length = cpu_to_le32(TPM_LOG_AREA_MINIMUM_SIZE); - tcpa->log_area_start_address = cpu_to_le64(log_area_start_address); + acpi_data_push(tcpalog, le32_to_cpu(tcpa->log_area_minimum_length)); - bios_linker_loader_alloc(linker, ACPI_BUILD_TPMLOG_FILE, 1, + bios_linker_loader_alloc(linker, ACPI_BUILD_TPMLOG_FILE, tcpalog, 1, false /* high memory */); /* log area start address to be filled by Guest linker */ - bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE, - ACPI_BUILD_TPMLOG_FILE, - table_data, &tcpa->log_area_start_address, - sizeof(tcpa->log_area_start_address)); + bios_linker_loader_add_pointer(linker, + ACPI_BUILD_TABLE_FILE, log_addr_offset, log_addr_size, + ACPI_BUILD_TPMLOG_FILE, 0); build_header(linker, table_data, (void *)tcpa, "TCPA", sizeof(*tcpa), 2, NULL, NULL); - - acpi_data_push(tcpalog, TPM_LOG_AREA_MINIMUM_SIZE); } static void -build_tpm2(GArray *table_data, GArray *linker) +build_tpm2(GArray *table_data, BIOSLinker *linker) { Acpi20TPM2 *tpm2_ptr; @@ -2427,35 +2386,14 @@ build_tpm2(GArray *table_data, GArray *linker) (void *)tpm2_ptr, "TPM2", sizeof(*tpm2_ptr), 4, NULL, NULL); } -typedef enum { - MEM_AFFINITY_NOFLAGS = 0, - MEM_AFFINITY_ENABLED = (1 << 0), - MEM_AFFINITY_HOTPLUGGABLE = (1 << 1), - MEM_AFFINITY_NON_VOLATILE = (1 << 2), -} MemoryAffinityFlags; - -static void -acpi_build_srat_memory(AcpiSratMemoryAffinity *numamem, uint64_t base, - uint64_t len, int node, MemoryAffinityFlags flags) -{ - numamem->type = ACPI_SRAT_MEMORY; - numamem->length = sizeof(*numamem); - memset(numamem->proximity, 0, 4); - numamem->proximity[0] = node; - numamem->flags = cpu_to_le32(flags); - numamem->base_addr = cpu_to_le64(base); - numamem->range_length = cpu_to_le64(len); -} - static void -build_srat(GArray *table_data, GArray *linker, MachineState *machine) +build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) { AcpiSystemResourceAffinityTable *srat; AcpiSratProcessorAffinity *core; 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); @@ -2471,14 +2409,19 @@ build_srat(GArray *table_data, GArray *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; + 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); @@ -2492,7 +2435,7 @@ build_srat(GArray *table_data, GArray *linker, MachineState *machine) numa_start = table_data->len; numamem = acpi_data_push(table_data, sizeof *numamem); - acpi_build_srat_memory(numamem, 0, 640*1024, 0, MEM_AFFINITY_ENABLED); + build_srat_memory(numamem, 0, 640 * 1024, 0, MEM_AFFINITY_ENABLED); next_base = 1024 * 1024; for (i = 1; i < pcms->numa_nodes + 1; ++i) { mem_base = next_base; @@ -2508,21 +2451,21 @@ build_srat(GArray *table_data, GArray *linker, MachineState *machine) mem_len -= next_base - pcms->below_4g_mem_size; if (mem_len > 0) { numamem = acpi_data_push(table_data, sizeof *numamem); - acpi_build_srat_memory(numamem, mem_base, mem_len, i - 1, - MEM_AFFINITY_ENABLED); + build_srat_memory(numamem, mem_base, mem_len, i - 1, + MEM_AFFINITY_ENABLED); } mem_base = 1ULL << 32; mem_len = next_base - pcms->below_4g_mem_size; next_base += (1ULL << 32) - pcms->below_4g_mem_size; } numamem = acpi_data_push(table_data, sizeof *numamem); - acpi_build_srat_memory(numamem, mem_base, mem_len, i - 1, - MEM_AFFINITY_ENABLED); + build_srat_memory(numamem, mem_base, mem_len, i - 1, + MEM_AFFINITY_ENABLED); } slots = (table_data->len - numa_start) / sizeof *numamem; for (; slots < pcms->numa_nodes + 2; slots++) { numamem = acpi_data_push(table_data, sizeof *numamem); - acpi_build_srat_memory(numamem, 0, 0, 0, MEM_AFFINITY_NOFLAGS); + build_srat_memory(numamem, 0, 0, 0, MEM_AFFINITY_NOFLAGS); } /* @@ -2532,10 +2475,9 @@ build_srat(GArray *table_data, GArray *linker, MachineState *machine) */ if (hotplugabble_address_space_size) { numamem = acpi_data_push(table_data, sizeof *numamem); - acpi_build_srat_memory(numamem, pcms->hotplug_memory.base, - hotplugabble_address_space_size, 0, - MEM_AFFINITY_HOTPLUGGABLE | - MEM_AFFINITY_ENABLED); + build_srat_memory(numamem, pcms->hotplug_memory.base, + hotplugabble_address_space_size, 0, + MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED); } build_header(linker, table_data, @@ -2546,7 +2488,7 @@ build_srat(GArray *table_data, GArray *linker, MachineState *machine) } static void -build_mcfg_q35(GArray *table_data, GArray *linker, AcpiMcfgInfo *info) +build_mcfg_q35(GArray *table_data, BIOSLinker *linker, AcpiMcfgInfo *info) { AcpiTableMcfg *mcfg; const char *sig; @@ -2574,51 +2516,75 @@ build_mcfg_q35(GArray *table_data, GArray *linker, AcpiMcfgInfo *info) build_header(linker, table_data, (void *)mcfg, sig, len, 1, NULL, NULL); } +/* + * VT-d spec 8.1 DMA Remapping Reporting Structure + * (version Oct. 2014 or later) + */ static void -build_dmar_q35(GArray *table_data, GArray *linker) +build_dmar_q35(GArray *table_data, BIOSLinker *linker) { int dmar_start = table_data->len; AcpiTableDmar *dmar; AcpiDmarHardwareUnit *drhd; + uint8_t dmar_flags = 0; + X86IOMMUState *iommu = x86_iommu_get_default(); + AcpiDmarDeviceScope *scope = NULL; + /* Root complex IOAPIC use one path[0] only */ + size_t ioapic_scope_size = sizeof(*scope) + sizeof(scope->path[0]); + + assert(iommu); + if (iommu->intr_supported) { + dmar_flags |= 0x1; /* Flags: 0x1: INT_REMAP */ + } dmar = acpi_data_push(table_data, sizeof(*dmar)); dmar->host_address_width = VTD_HOST_ADDRESS_WIDTH - 1; - dmar->flags = 0; /* No intr_remap for now */ + dmar->flags = dmar_flags; /* DMAR Remapping Hardware Unit Definition structure */ - drhd = acpi_data_push(table_data, sizeof(*drhd)); + drhd = acpi_data_push(table_data, sizeof(*drhd) + ioapic_scope_size); drhd->type = cpu_to_le16(ACPI_DMAR_TYPE_HARDWARE_UNIT); - drhd->length = cpu_to_le16(sizeof(*drhd)); /* No device scope now */ + drhd->length = cpu_to_le16(sizeof(*drhd) + ioapic_scope_size); drhd->flags = ACPI_DMAR_INCLUDE_PCI_ALL; drhd->pci_segment = cpu_to_le16(0); drhd->address = cpu_to_le64(Q35_HOST_BRIDGE_IOMMU_ADDR); + /* Scope definition for the root-complex IOAPIC. See VT-d spec + * 8.3.1 (version Oct. 2014 or later). */ + scope = &drhd->scope[0]; + scope->entry_type = 0x03; /* Type: 0x03 for IOAPIC */ + scope->length = ioapic_scope_size; + scope->enumeration_id = ACPI_BUILD_IOAPIC_ID; + scope->bus = Q35_PSEUDO_BUS_PLATFORM; + scope->path[0] = cpu_to_le16(Q35_PSEUDO_DEVFN_IOAPIC); + build_header(linker, table_data, (void *)(table_data->data + dmar_start), "DMAR", table_data->len - dmar_start, 1, NULL, NULL); } static GArray * -build_rsdp(GArray *rsdp_table, GArray *linker, unsigned rsdt) +build_rsdp(GArray *rsdp_table, BIOSLinker *linker, unsigned rsdt_tbl_offset) { AcpiRsdpDescriptor *rsdp = acpi_data_push(rsdp_table, sizeof *rsdp); + unsigned rsdt_pa_size = sizeof(rsdp->rsdt_physical_address); + unsigned rsdt_pa_offset = + (char *)&rsdp->rsdt_physical_address - rsdp_table->data; - bios_linker_loader_alloc(linker, ACPI_BUILD_RSDP_FILE, 16, + bios_linker_loader_alloc(linker, ACPI_BUILD_RSDP_FILE, rsdp_table, 16, true /* fseg memory */); memcpy(&rsdp->signature, "RSD PTR ", 8); memcpy(rsdp->oem_id, ACPI_BUILD_APPNAME6, 6); - rsdp->rsdt_physical_address = cpu_to_le32(rsdt); /* Address to be filled by Guest linker */ - bios_linker_loader_add_pointer(linker, ACPI_BUILD_RSDP_FILE, - ACPI_BUILD_TABLE_FILE, - rsdp_table, &rsdp->rsdt_physical_address, - sizeof rsdp->rsdt_physical_address); - rsdp->checksum = 0; + bios_linker_loader_add_pointer(linker, + ACPI_BUILD_RSDP_FILE, rsdt_pa_offset, rsdt_pa_size, + ACPI_BUILD_TABLE_FILE, rsdt_tbl_offset); + /* Checksum to be filled by Guest linker */ bios_linker_loader_add_checksum(linker, ACPI_BUILD_RSDP_FILE, - rsdp_table, rsdp, sizeof *rsdp, - &rsdp->checksum); + (char *)rsdp - rsdp_table->data, sizeof *rsdp, + (char *)&rsdp->checksum - rsdp_table->data); return rsdp_table; } @@ -2658,12 +2624,7 @@ static bool acpi_get_mcfg(AcpiMcfgInfo *mcfg) static bool acpi_has_iommu(void) { - bool ambiguous; - Object *intel_iommu; - - intel_iommu = object_resolve_path_type("", TYPE_INTEL_IOMMU_DEVICE, - &ambiguous); - return intel_iommu && !ambiguous; + return !!x86_iommu_get_default(); } static @@ -2676,7 +2637,7 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) AcpiPmInfo pm; AcpiMiscInfo misc; AcpiMcfgInfo mcfg; - PcPciInfo pci; + Range pci_hole, pci_hole64; uint8_t *u; size_t aml_len = 0; GArray *tables_blob = tables->table_data; @@ -2684,14 +2645,15 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) acpi_get_pm_info(&pm); acpi_get_misc_info(&misc); - acpi_get_pci_info(&pci); + acpi_get_pci_holes(&pci_hole, &pci_hole64); acpi_get_slic_oem(&slic_oem); table_offsets = g_array_new(false, true /* clear */, sizeof(uint32_t)); ACPI_BUILD_DPRINTF("init ACPI tables\n"); - bios_linker_loader_alloc(tables->linker, ACPI_BUILD_TABLE_FILE, + bios_linker_loader_alloc(tables->linker, + ACPI_BUILD_TABLE_FILE, tables_blob, 64 /* Ensure FACS is aligned */, false /* high memory */); @@ -2705,7 +2667,8 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) /* DSDT is pointed to by FADT */ dsdt = tables_blob->len; - build_dsdt(tables_blob, tables->linker, &pm, &misc, &pci, machine); + build_dsdt(tables_blob, tables->linker, &pm, &misc, + &pci_hole, &pci_hole64, machine); /* Count the size of the DSDT and SSDT, we will need it for legacy * sizing of ACPI tables. @@ -2748,7 +2711,8 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) build_dmar_q35(tables_blob, tables->linker); } if (pcms->acpi_nvdimm_state.is_enabled) { - nvdimm_build_acpi(table_offsets, tables_blob, tables->linker); + nvdimm_build_acpi(table_offsets, tables_blob, tables->linker, + pcms->acpi_nvdimm_state.dsm_mem); } /* Add tables supplied by user (if any) */ @@ -2811,7 +2775,7 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) acpi_align_size(tables_blob, ACPI_BUILD_TABLE_SIZE); } - acpi_align_size(tables->linker, ACPI_BUILD_ALIGN_SIZE); + acpi_align_size(tables->linker->cmd_blob, ACPI_BUILD_ALIGN_SIZE); /* Cleanup memory that's no longer used. */ g_array_free(table_offsets, true); @@ -2851,7 +2815,7 @@ static void acpi_build_update(void *build_opaque) acpi_ram_update(build_state->rsdp_mr, tables.rsdp); } - acpi_ram_update(build_state->linker_mr, tables.linker); + acpi_ram_update(build_state->linker_mr, tables.linker->cmd_blob); acpi_build_tables_cleanup(&tables, true); } @@ -2915,7 +2879,8 @@ void acpi_setup(void) assert(build_state->table_mr != NULL); build_state->linker_mr = - acpi_add_rom_blob(build_state, tables.linker, "etc/table-loader", 0); + acpi_add_rom_blob(build_state, tables.linker->cmd_blob, + "etc/table-loader", 0); fw_cfg_add_file(pcms->fw_cfg, ACPI_BUILD_TPMLOG_FILE, tables.tcpalog->data, acpi_data_len(tables.tcpalog)); diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 347718f93..28c31a2cd 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -20,16 +20,23 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "hw/sysbus.h" #include "exec/address-spaces.h" #include "intel_iommu_internal.h" #include "hw/pci/pci.h" +#include "hw/pci/pci_bus.h" +#include "hw/i386/pc.h" +#include "hw/boards.h" +#include "hw/i386/x86-iommu.h" +#include "hw/pci-host/q35.h" +#include "sysemu/kvm.h" /*#define DEBUG_INTEL_IOMMU*/ #ifdef DEBUG_INTEL_IOMMU enum { DEBUG_GENERAL, DEBUG_CSR, DEBUG_INV, DEBUG_MMU, DEBUG_FLOG, - DEBUG_CACHE, + DEBUG_CACHE, DEBUG_IR, }; #define VTD_DBGBIT(x) (1 << DEBUG_##x) static int vtd_dbgflags = VTD_DBGBIT(GENERAL) | VTD_DBGBIT(CSR); @@ -190,7 +197,7 @@ static void vtd_reset_context_cache(IntelIOMMUState *s) VTD_DPRINTF(CACHE, "global context_cache_gen=1"); while (g_hash_table_iter_next (&bus_it, NULL, (void**)&vtd_bus)) { - for (devfn_it = 0; devfn_it < VTD_PCI_DEVFN_MAX; ++devfn_it) { + for (devfn_it = 0; devfn_it < X86_IOMMU_PCI_DEVFN_MAX; ++devfn_it) { vtd_as = vtd_bus->dev_as[devfn_it]; if (!vtd_as) { continue; @@ -899,6 +906,27 @@ static void vtd_root_table_setup(IntelIOMMUState *s) (s->root_extended ? "(extended)" : "")); } +static void vtd_iec_notify_all(IntelIOMMUState *s, bool global, + uint32_t index, uint32_t mask) +{ + x86_iommu_iec_notify_all(X86_IOMMU_DEVICE(s), global, index, mask); +} + +static void vtd_interrupt_remap_table_setup(IntelIOMMUState *s) +{ + uint64_t value = 0; + value = vtd_get_quad_raw(s, DMAR_IRTA_REG); + s->intr_size = 1UL << ((value & VTD_IRTA_SIZE_MASK) + 1); + s->intr_root = value & VTD_IRTA_ADDR_MASK; + s->intr_eime = value & VTD_IRTA_EIME; + + /* Notify global invalidation */ + vtd_iec_notify_all(s, true, 0, 0); + + VTD_DPRINTF(CSR, "int remap table addr 0x%"PRIx64 " size %"PRIu32, + s->intr_root, s->intr_size); +} + static void vtd_context_global_invalidate(IntelIOMMUState *s) { s->context_cache_gen++; @@ -962,7 +990,7 @@ static void vtd_context_device_invalidate(IntelIOMMUState *s, vtd_bus = vtd_find_as_from_bus_num(s, VTD_SID_TO_BUS(source_id)); if (vtd_bus) { devfn = VTD_SID_TO_DEVFN(source_id); - for (devfn_it = 0; devfn_it < VTD_PCI_DEVFN_MAX; ++devfn_it) { + for (devfn_it = 0; devfn_it < X86_IOMMU_PCI_DEVFN_MAX; ++devfn_it) { vtd_as = vtd_bus->dev_as[devfn_it]; if (vtd_as && ((devfn_it & mask) == (devfn & mask))) { VTD_DPRINTF(INV, "invalidate context-cahce of devfn 0x%"PRIx16, @@ -1137,6 +1165,16 @@ static void vtd_handle_gcmd_srtp(IntelIOMMUState *s) vtd_set_clear_mask_long(s, DMAR_GSTS_REG, 0, VTD_GSTS_RTPS); } +/* Set Interrupt Remap Table Pointer */ +static void vtd_handle_gcmd_sirtp(IntelIOMMUState *s) +{ + VTD_DPRINTF(CSR, "set Interrupt Remap Table Pointer"); + + vtd_interrupt_remap_table_setup(s); + /* Ok - report back to driver */ + vtd_set_clear_mask_long(s, DMAR_GSTS_REG, 0, VTD_GSTS_IRTPS); +} + /* Handle Translation Enable/Disable */ static void vtd_handle_gcmd_te(IntelIOMMUState *s, bool en) { @@ -1156,6 +1194,22 @@ static void vtd_handle_gcmd_te(IntelIOMMUState *s, bool en) } } +/* Handle Interrupt Remap Enable/Disable */ +static void vtd_handle_gcmd_ire(IntelIOMMUState *s, bool en) +{ + VTD_DPRINTF(CSR, "Interrupt Remap Enable %s", (en ? "on" : "off")); + + if (en) { + s->intr_enabled = true; + /* Ok - report back to driver */ + vtd_set_clear_mask_long(s, DMAR_GSTS_REG, 0, VTD_GSTS_IRES); + } else { + s->intr_enabled = false; + /* Ok - report back to driver */ + vtd_set_clear_mask_long(s, DMAR_GSTS_REG, VTD_GSTS_IRES, 0); + } +} + /* Handle write to Global Command Register */ static void vtd_handle_gcmd_write(IntelIOMMUState *s) { @@ -1176,6 +1230,14 @@ static void vtd_handle_gcmd_write(IntelIOMMUState *s) /* Queued Invalidation Enable */ vtd_handle_gcmd_qie(s, val & VTD_GCMD_QIE); } + if (val & VTD_GCMD_SIRTP) { + /* Set/update the interrupt remapping root-table pointer */ + vtd_handle_gcmd_sirtp(s); + } + if (changed & VTD_GCMD_IRE) { + /* Interrupt remap enable/disable */ + vtd_handle_gcmd_ire(s, val & VTD_GCMD_IRE); + } } /* Handle write to Context Command Register */ @@ -1361,6 +1423,21 @@ static bool vtd_process_iotlb_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) return true; } +static bool vtd_process_inv_iec_desc(IntelIOMMUState *s, + VTDInvDesc *inv_desc) +{ + VTD_DPRINTF(INV, "inv ir glob %d index %d mask %d", + inv_desc->iec.granularity, + inv_desc->iec.index, + inv_desc->iec.index_mask); + + vtd_iec_notify_all(s, !inv_desc->iec.granularity, + inv_desc->iec.index, + inv_desc->iec.index_mask); + + return true; +} + static bool vtd_process_inv_desc(IntelIOMMUState *s) { VTDInvDesc inv_desc; @@ -1400,6 +1477,15 @@ static bool vtd_process_inv_desc(IntelIOMMUState *s) } break; + case VTD_INV_DESC_IEC: + VTD_DPRINTF(INV, "Invalidation Interrupt Entry Cache " + "Descriptor hi 0x%"PRIx64 " lo 0x%"PRIx64, + inv_desc.hi, inv_desc.lo); + if (!vtd_process_inv_iec_desc(s, &inv_desc)) { + return false; + } + break; + default: VTD_DPRINTF(GENERAL, "error: unkonw Invalidation Descriptor type " "hi 0x%"PRIx64 " lo 0x%"PRIx64 " type %"PRIu8, @@ -1828,6 +1914,23 @@ static void vtd_mem_write(void *opaque, hwaddr addr, vtd_update_fsts_ppf(s); break; + case DMAR_IRTA_REG: + VTD_DPRINTF(IR, "DMAR_IRTA_REG write addr 0x%"PRIx64 + ", size %d, val 0x%"PRIx64, addr, size, val); + if (size == 4) { + vtd_set_long(s, addr, val); + } else { + vtd_set_quad(s, addr, val); + } + break; + + case DMAR_IRTA_REG_HI: + VTD_DPRINTF(IR, "DMAR_IRTA_REG_HI write addr 0x%"PRIx64 + ", size %d, val 0x%"PRIx64, addr, size, val); + assert(size == 4); + vtd_set_long(s, addr, val); + break; + default: VTD_DPRINTF(GENERAL, "error: unhandled reg write addr 0x%"PRIx64 ", size %d, val 0x%"PRIx64, addr, size, val); @@ -1871,6 +1974,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, @@ -1895,6 +2008,295 @@ static Property vtd_properties[] = { DEFINE_PROP_END_OF_LIST(), }; +/* Read IRTE entry with specific index */ +static int vtd_irte_get(IntelIOMMUState *iommu, uint16_t index, + VTD_IR_TableEntry *entry, uint16_t sid) +{ + static const uint16_t vtd_svt_mask[VTD_SQ_MAX] = \ + {0xffff, 0xfffb, 0xfff9, 0xfff8}; + dma_addr_t addr = 0x00; + uint16_t mask, source_id; + uint8_t bus, bus_max, bus_min; + + addr = iommu->intr_root + index * sizeof(*entry); + if (dma_memory_read(&address_space_memory, addr, entry, + sizeof(*entry))) { + VTD_DPRINTF(GENERAL, "error: fail to access IR root at 0x%"PRIx64 + " + %"PRIu16, iommu->intr_root, index); + return -VTD_FR_IR_ROOT_INVAL; + } + + if (!entry->irte.present) { + VTD_DPRINTF(GENERAL, "error: present flag not set in IRTE" + " entry index %u value 0x%"PRIx64 " 0x%"PRIx64, + index, le64_to_cpu(entry->data[1]), + le64_to_cpu(entry->data[0])); + return -VTD_FR_IR_ENTRY_P; + } + + if (entry->irte.__reserved_0 || entry->irte.__reserved_1 || + entry->irte.__reserved_2) { + VTD_DPRINTF(GENERAL, "error: IRTE entry index %"PRIu16 + " reserved fields non-zero: 0x%"PRIx64 " 0x%"PRIx64, + index, le64_to_cpu(entry->data[1]), + le64_to_cpu(entry->data[0])); + return -VTD_FR_IR_IRTE_RSVD; + } + + if (sid != X86_IOMMU_SID_INVALID) { + /* Validate IRTE SID */ + source_id = le32_to_cpu(entry->irte.source_id); + switch (entry->irte.sid_vtype) { + case VTD_SVT_NONE: + VTD_DPRINTF(IR, "No SID validation for IRTE index %d", index); + break; + + case VTD_SVT_ALL: + mask = vtd_svt_mask[entry->irte.sid_q]; + if ((source_id & mask) != (sid & mask)) { + VTD_DPRINTF(GENERAL, "SID validation for IRTE index " + "%d failed (reqid 0x%04x sid 0x%04x)", index, + sid, source_id); + return -VTD_FR_IR_SID_ERR; + } + break; + + case VTD_SVT_BUS: + bus_max = source_id >> 8; + bus_min = source_id & 0xff; + bus = sid >> 8; + if (bus > bus_max || bus < bus_min) { + VTD_DPRINTF(GENERAL, "SID validation for IRTE index %d " + "failed (bus %d outside %d-%d)", index, bus, + bus_min, bus_max); + return -VTD_FR_IR_SID_ERR; + } + break; + + default: + VTD_DPRINTF(GENERAL, "Invalid SVT bits (0x%x) in IRTE index " + "%d", entry->irte.sid_vtype, index); + /* Take this as verification failure. */ + return -VTD_FR_IR_SID_ERR; + break; + } + } + + return 0; +} + +/* Fetch IRQ information of specific IR index */ +static int vtd_remap_irq_get(IntelIOMMUState *iommu, uint16_t index, + VTDIrq *irq, uint16_t sid) +{ + VTD_IR_TableEntry irte = {}; + int ret = 0; + + ret = vtd_irte_get(iommu, index, &irte, sid); + if (ret) { + return ret; + } + + irq->trigger_mode = irte.irte.trigger_mode; + irq->vector = irte.irte.vector; + irq->delivery_mode = irte.irte.delivery_mode; + irq->dest = le32_to_cpu(irte.irte.dest_id); + if (!iommu->intr_eime) { +#define VTD_IR_APIC_DEST_MASK (0xff00ULL) +#define VTD_IR_APIC_DEST_SHIFT (8) + irq->dest = (irq->dest & VTD_IR_APIC_DEST_MASK) >> + VTD_IR_APIC_DEST_SHIFT; + } + irq->dest_mode = irte.irte.dest_mode; + irq->redir_hint = irte.irte.redir_hint; + + VTD_DPRINTF(IR, "remapping interrupt index %d: trig:%u,vec:%u," + "deliver:%u,dest:%u,dest_mode:%u", index, + irq->trigger_mode, irq->vector, irq->delivery_mode, + irq->dest, irq->dest_mode); + + return 0; +} + +/* Generate one MSI message from VTDIrq info */ +static void vtd_generate_msi_message(VTDIrq *irq, MSIMessage *msg_out) +{ + VTD_MSIMessage msg = {}; + + /* Generate address bits */ + msg.dest_mode = irq->dest_mode; + msg.redir_hint = irq->redir_hint; + msg.dest = irq->dest; + msg.__addr_head = cpu_to_le32(0xfee); + /* Keep this from original MSI address bits */ + msg.__not_used = irq->msi_addr_last_bits; + + /* Generate data bits */ + msg.vector = irq->vector; + msg.delivery_mode = irq->delivery_mode; + msg.level = 1; + msg.trigger_mode = irq->trigger_mode; + + msg_out->address = msg.msi_addr; + msg_out->data = msg.msi_data; +} + +/* Interrupt remapping for MSI/MSI-X entry */ +static int vtd_interrupt_remap_msi(IntelIOMMUState *iommu, + MSIMessage *origin, + MSIMessage *translated, + uint16_t sid) +{ + int ret = 0; + VTD_IR_MSIAddress addr; + uint16_t index; + VTDIrq irq = {}; + + assert(origin && translated); + + if (!iommu || !iommu->intr_enabled) { + goto do_not_translate; + } + + if (origin->address & VTD_MSI_ADDR_HI_MASK) { + VTD_DPRINTF(GENERAL, "error: MSI addr high 32 bits nonzero" + " during interrupt remapping: 0x%"PRIx32, + (uint32_t)((origin->address & VTD_MSI_ADDR_HI_MASK) >> \ + VTD_MSI_ADDR_HI_SHIFT)); + return -VTD_FR_IR_REQ_RSVD; + } + + addr.data = origin->address & VTD_MSI_ADDR_LO_MASK; + if (le16_to_cpu(addr.addr.__head) != 0xfee) { + VTD_DPRINTF(GENERAL, "error: MSI addr low 32 bits invalid: " + "0x%"PRIx32, addr.data); + return -VTD_FR_IR_REQ_RSVD; + } + + /* This is compatible mode. */ + if (addr.addr.int_mode != VTD_IR_INT_FORMAT_REMAP) { + goto do_not_translate; + } + + index = addr.addr.index_h << 15 | le16_to_cpu(addr.addr.index_l); + +#define VTD_IR_MSI_DATA_SUBHANDLE (0x0000ffff) +#define VTD_IR_MSI_DATA_RESERVED (0xffff0000) + + if (addr.addr.sub_valid) { + /* See VT-d spec 5.1.2.2 and 5.1.3 on subhandle */ + index += origin->data & VTD_IR_MSI_DATA_SUBHANDLE; + } + + ret = vtd_remap_irq_get(iommu, index, &irq, sid); + if (ret) { + return ret; + } + + if (addr.addr.sub_valid) { + VTD_DPRINTF(IR, "received MSI interrupt"); + if (origin->data & VTD_IR_MSI_DATA_RESERVED) { + VTD_DPRINTF(GENERAL, "error: MSI data bits non-zero for " + "interrupt remappable entry: 0x%"PRIx32, + origin->data); + return -VTD_FR_IR_REQ_RSVD; + } + } else { + uint8_t vector = origin->data & 0xff; + VTD_DPRINTF(IR, "received IOAPIC interrupt"); + /* IOAPIC entry vector should be aligned with IRTE vector + * (see vt-d spec 5.1.5.1). */ + if (vector != irq.vector) { + VTD_DPRINTF(GENERAL, "IOAPIC vector inconsistent: " + "entry: %d, IRTE: %d, index: %d", + vector, irq.vector, index); + } + } + + /* + * We'd better keep the last two bits, assuming that guest OS + * might modify it. Keep it does not hurt after all. + */ + irq.msi_addr_last_bits = addr.addr.__not_care; + + /* Translate VTDIrq to MSI message */ + vtd_generate_msi_message(&irq, translated); + + VTD_DPRINTF(IR, "mapping MSI 0x%"PRIx64":0x%"PRIx32 " -> " + "0x%"PRIx64":0x%"PRIx32, origin->address, origin->data, + translated->address, translated->data); + return 0; + +do_not_translate: + memcpy(translated, origin, sizeof(*origin)); + return 0; +} + +static int vtd_int_remap(X86IOMMUState *iommu, MSIMessage *src, + MSIMessage *dst, uint16_t sid) +{ + return vtd_interrupt_remap_msi(INTEL_IOMMU_DEVICE(iommu), + src, dst, sid); +} + +static MemTxResult vtd_mem_ir_read(void *opaque, hwaddr addr, + uint64_t *data, unsigned size, + MemTxAttrs attrs) +{ + return MEMTX_OK; +} + +static MemTxResult vtd_mem_ir_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size, + MemTxAttrs attrs) +{ + int ret = 0; + MSIMessage from = {}, to = {}; + uint16_t sid = X86_IOMMU_SID_INVALID; + + from.address = (uint64_t) addr + VTD_INTERRUPT_ADDR_FIRST; + from.data = (uint32_t) value; + + if (!attrs.unspecified) { + /* We have explicit Source ID */ + sid = attrs.requester_id; + } + + ret = vtd_interrupt_remap_msi(opaque, &from, &to, sid); + if (ret) { + /* TODO: report error */ + VTD_DPRINTF(GENERAL, "int remap fail for addr 0x%"PRIx64 + " data 0x%"PRIx32, from.address, from.data); + /* Drop this interrupt */ + return MEMTX_ERROR; + } + + VTD_DPRINTF(IR, "delivering MSI 0x%"PRIx64":0x%"PRIx32 + " for device sid 0x%04x", + to.address, to.data, sid); + + if (dma_memory_write(&address_space_memory, to.address, + &to.data, size)) { + VTD_DPRINTF(GENERAL, "error: fail to write 0x%"PRIx64 + " value 0x%"PRIx32, to.address, to.data); + } + + return MEMTX_OK; +} + +static const MemoryRegionOps vtd_mem_ir_ops = { + .read_with_attrs = vtd_mem_ir_read, + .write_with_attrs = vtd_mem_ir_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn) { @@ -1904,7 +2306,8 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn) if (!vtd_bus) { /* No corresponding free() */ - vtd_bus = g_malloc0(sizeof(VTDBus) + sizeof(VTDAddressSpace *) * VTD_PCI_DEVFN_MAX); + vtd_bus = g_malloc0(sizeof(VTDBus) + sizeof(VTDAddressSpace *) * \ + X86_IOMMU_PCI_DEVFN_MAX); vtd_bus->bus = bus; key = (uintptr_t)bus; g_hash_table_insert(s->vtd_as_by_busptr, &key, vtd_bus); @@ -1921,6 +2324,11 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn) vtd_dev_as->context_cache_entry.context_cache_gen = 0; memory_region_init_iommu(&vtd_dev_as->iommu, OBJECT(s), &s->iommu_ops, "intel_iommu", UINT64_MAX); + memory_region_init_io(&vtd_dev_as->iommu_ir, OBJECT(s), + &vtd_mem_ir_ops, s, "intel_iommu_ir", + VTD_INTERRUPT_ADDR_SIZE); + memory_region_add_subregion(&vtd_dev_as->iommu, VTD_INTERRUPT_ADDR_FIRST, + &vtd_dev_as->iommu_ir); address_space_init(&vtd_dev_as->as, &vtd_dev_as->iommu, "intel_iommu"); } @@ -1932,12 +2340,15 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn) */ static void vtd_init(IntelIOMMUState *s) { + X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s); + memset(s->csr, 0, DMAR_REG_SIZE); memset(s->wmask, 0, DMAR_REG_SIZE); memset(s->w1cmask, 0, DMAR_REG_SIZE); memset(s->womask, 0, DMAR_REG_SIZE); s->iommu_ops.translate = vtd_iommu_translate; + s->iommu_ops.notify_started = vtd_iommu_notify_started; s->root = 0; s->root_extended = false; s->dmar_enabled = false; @@ -1952,6 +2363,10 @@ static void vtd_init(IntelIOMMUState *s) VTD_CAP_SAGAW | VTD_CAP_MAMV | VTD_CAP_PSI | VTD_CAP_SLLPS; s->ecap = VTD_ECAP_QI | VTD_ECAP_IRO; + if (x86_iommu->intr_supported) { + s->ecap |= VTD_ECAP_IR | VTD_ECAP_EIM | VTD_ECAP_MHMV; + } + vtd_reset_context_cache(s); vtd_reset_iotlb(s); @@ -2001,6 +2416,11 @@ static void vtd_init(IntelIOMMUState *s) /* Fault Recording Registers, 128-bit */ vtd_define_quad(s, DMAR_FRCD_REG_0_0, 0, 0, 0); vtd_define_quad(s, DMAR_FRCD_REG_0_2, 0, 0, 0x8000000000000000ULL); + + /* + * Interrupt remapping registers. + */ + vtd_define_quad(s, DMAR_IRTA_REG, 0, 0xfffffffffffff80fULL, 0); } /* Should not reset address_spaces when reset because devices will still use @@ -2014,9 +2434,23 @@ static void vtd_reset(DeviceState *dev) vtd_init(s); } +static AddressSpace *vtd_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) +{ + IntelIOMMUState *s = opaque; + VTDAddressSpace *vtd_as; + + assert(0 <= devfn && devfn <= X86_IOMMU_PCI_DEVFN_MAX); + + vtd_as = vtd_find_add_as(s, bus, devfn); + return &vtd_as->as; +} + static void vtd_realize(DeviceState *dev, Error **errp) { + PCMachineState *pcms = PC_MACHINE(qdev_get_machine()); + PCIBus *bus = pcms->bus; IntelIOMMUState *s = INTEL_IOMMU_DEVICE(dev); + X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(dev); VTD_DPRINTF(GENERAL, ""); memset(s->vtd_as_by_bus_num, 0, sizeof(s->vtd_as_by_bus_num)); @@ -2029,21 +2463,36 @@ static void vtd_realize(DeviceState *dev, Error **errp) s->vtd_as_by_busptr = g_hash_table_new_full(vtd_uint64_hash, vtd_uint64_equal, g_free, g_free); vtd_init(s); + sysbus_mmio_map(SYS_BUS_DEVICE(s), 0, Q35_HOST_BRIDGE_IOMMU_ADDR); + pci_setup_iommu(bus, vtd_host_dma_iommu, dev); + /* Pseudo address space under root PCI bus. */ + pcms->ioapic_as = vtd_host_dma_iommu(bus, s, Q35_PSEUDO_DEVFN_IOAPIC); + + /* Currently Intel IOMMU IR only support "kernel-irqchip={off|split}" */ + if (x86_iommu->intr_supported && kvm_irqchip_in_kernel() && + !kvm_irqchip_is_split()) { + error_report("Intel Interrupt Remapping cannot work with " + "kernel-irqchip=on, please use 'split|off'."); + exit(1); + } } static void vtd_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + X86IOMMUClass *x86_class = X86_IOMMU_CLASS(klass); dc->reset = vtd_reset; - dc->realize = vtd_realize; dc->vmsd = &vtd_vmstate; dc->props = vtd_properties; + dc->hotpluggable = false; + x86_class->realize = vtd_realize; + x86_class->int_remap = vtd_int_remap; } static const TypeInfo vtd_info = { .name = TYPE_INTEL_IOMMU_DEVICE, - .parent = TYPE_SYS_BUS_DEVICE, + .parent = TYPE_X86_IOMMU_DEVICE, .instance_size = sizeof(IntelIOMMUState), .class_init = vtd_class_init, }; diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h index e5f514c6e..0829a5064 100644 --- a/hw/i386/intel_iommu_internal.h +++ b/hw/i386/intel_iommu_internal.h @@ -110,6 +110,8 @@ /* Interrupt Address Range */ #define VTD_INTERRUPT_ADDR_FIRST 0xfee00000ULL #define VTD_INTERRUPT_ADDR_LAST 0xfeefffffULL +#define VTD_INTERRUPT_ADDR_SIZE (VTD_INTERRUPT_ADDR_LAST - \ + VTD_INTERRUPT_ADDR_FIRST + 1) /* The shift of source_id in the key of IOTLB hash table */ #define VTD_IOTLB_SID_SHIFT 36 @@ -172,10 +174,19 @@ #define VTD_RTADDR_RTT (1ULL << 11) #define VTD_RTADDR_ADDR_MASK (VTD_HAW_MASK ^ 0xfffULL) +/* IRTA_REG */ +#define VTD_IRTA_ADDR_MASK (VTD_HAW_MASK ^ 0xfffULL) +#define VTD_IRTA_EIME (1ULL << 11) +#define VTD_IRTA_SIZE_MASK (0xfULL) + /* ECAP_REG */ /* (offset >> 4) << 8 */ #define VTD_ECAP_IRO (DMAR_IOTLB_REG_OFFSET << 4) #define VTD_ECAP_QI (1ULL << 1) +/* Interrupt Remapping support */ +#define VTD_ECAP_IR (1ULL << 3) +#define VTD_ECAP_EIM (1ULL << 4) +#define VTD_ECAP_MHMV (15ULL << 20) /* CAP_REG */ /* (offset >> 4) << 24 */ @@ -265,6 +276,19 @@ typedef enum VTDFaultReason { * context-entry. */ VTD_FR_CONTEXT_ENTRY_TT, + + /* Interrupt remapping transition faults */ + VTD_FR_IR_REQ_RSVD = 0x20, /* One or more IR request reserved + * fields set */ + VTD_FR_IR_INDEX_OVER = 0x21, /* Index value greater than max */ + VTD_FR_IR_ENTRY_P = 0x22, /* Present (P) not set in IRTE */ + VTD_FR_IR_ROOT_INVAL = 0x23, /* IR Root table invalid */ + VTD_FR_IR_IRTE_RSVD = 0x24, /* IRTE Rsvd field non-zero with + * Present flag set */ + VTD_FR_IR_REQ_COMPAT = 0x25, /* Encountered compatible IR + * request while disabled */ + VTD_FR_IR_SID_ERR = 0x26, /* Invalid Source-ID */ + /* This is not a normal fault reason. We use this to indicate some faults * that are not referenced by the VT-d specification. * Fault event with such reason should not be recorded. @@ -275,17 +299,35 @@ typedef enum VTDFaultReason { #define VTD_CONTEXT_CACHE_GEN_MAX 0xffffffffUL +/* Interrupt Entry Cache Invalidation Descriptor: VT-d 6.5.2.7. */ +struct VTDInvDescIEC { + uint32_t type:4; /* Should always be 0x4 */ + uint32_t granularity:1; /* If set, it's global IR invalidation */ + uint32_t resved_1:22; + uint32_t index_mask:5; /* 2^N for continuous int invalidation */ + uint32_t index:16; /* Start index to invalidate */ + uint32_t reserved_2:16; +}; +typedef struct VTDInvDescIEC VTDInvDescIEC; + /* Queued Invalidation Descriptor */ -struct VTDInvDesc { - uint64_t lo; - uint64_t hi; +union VTDInvDesc { + struct { + uint64_t lo; + uint64_t hi; + }; + union { + VTDInvDescIEC iec; + }; }; -typedef struct VTDInvDesc VTDInvDesc; +typedef union VTDInvDesc VTDInvDesc; /* Masks for struct VTDInvDesc */ #define VTD_INV_DESC_TYPE 0xf #define VTD_INV_DESC_CC 0x1 /* Context-cache Invalidate Desc */ #define VTD_INV_DESC_IOTLB 0x2 +#define VTD_INV_DESC_IEC 0x4 /* Interrupt Entry Cache + Invalidate Descriptor */ #define VTD_INV_DESC_WAIT 0x5 /* Invalidation Wait Descriptor */ #define VTD_INV_DESC_NONE 0 /* Not an Invalidate Descriptor */ diff --git a/hw/i386/kvm/apic.c b/hw/i386/kvm/apic.c index 3c7c8fa00..2bd0de82b 100644 --- a/hw/i386/kvm/apic.c +++ b/hw/i386/kvm/apic.c @@ -10,6 +10,8 @@ * See the COPYING file in the top-level directory. */ #include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/i386/apic_internal.h" #include "hw/pci/msi.h" #include "sysemu/kvm.h" @@ -182,19 +184,24 @@ static void kvm_apic_realize(DeviceState *dev, Error **errp) { APICCommonState *s = APIC_COMMON(dev); - memory_region_init_io(&s->io_memory, NULL, &kvm_apic_io_ops, s, "kvm-apic-msi", - APIC_SPACE_SIZE); + memory_region_init_io(&s->io_memory, OBJECT(s), &kvm_apic_io_ops, s, + "kvm-apic-msi", APIC_SPACE_SIZE); if (kvm_has_gsi_routing()) { msi_nonbroken = true; } } +static void kvm_apic_unrealize(DeviceState *dev, Error **errp) +{ +} + static void kvm_apic_class_init(ObjectClass *klass, void *data) { APICCommonClass *k = APIC_COMMON_CLASS(klass); k->realize = kvm_apic_realize; + k->unrealize = kvm_apic_unrealize; k->reset = kvm_apic_reset; k->set_base = kvm_apic_set_base; k->set_tpr = kvm_apic_set_tpr; diff --git a/hw/i386/kvm/clock.c b/hw/i386/kvm/clock.c index a3b300cad..0f75dd385 100644 --- a/hw/i386/kvm/clock.c +++ b/hw/i386/kvm/clock.c @@ -15,6 +15,7 @@ #include "qemu/osdep.h" #include "qemu-common.h" +#include "cpu.h" #include "qemu/host-utils.h" #include "sysemu/sysemu.h" #include "sysemu/kvm.h" diff --git a/hw/i386/kvm/i8254.c b/hw/i386/kvm/i8254.c index a4462e5ca..521a58498 100644 --- a/hw/i386/kvm/i8254.c +++ b/hw/i386/kvm/i8254.c @@ -22,7 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ + #include "qemu/osdep.h" +#include <linux/kvm.h> #include "qapi/error.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" diff --git a/hw/i386/kvm/pci-assign.c b/hw/i386/kvm/pci-assign.c index bf425a2b9..8238fbc63 100644 --- a/hw/i386/kvm/pci-assign.c +++ b/hw/i386/kvm/pci-assign.c @@ -20,9 +20,10 @@ * Copyright (C) 2008, Red Hat, Amit Shah (amit.shah@redhat.com) * Copyright (C) 2008, IBM, Muli Ben-Yehuda (muli@il.ibm.com) */ + #include "qemu/osdep.h" +#include <linux/kvm.h> #include "qapi/error.h" -#include <sys/mman.h> #include "hw/hw.h" #include "hw/i386/pc.h" #include "qemu/error-report.h" @@ -36,8 +37,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 +121,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; @@ -974,10 +974,9 @@ static void assigned_dev_update_msi(PCIDevice *pci_dev) } if (ctrl_byte & PCI_MSI_FLAGS_ENABLE) { - MSIMessage msg = msi_get_message(pci_dev, 0); int virq; - virq = kvm_irqchip_add_msi_route(kvm_state, msg, pci_dev); + virq = kvm_irqchip_add_msi_route(kvm_state, 0, pci_dev); if (virq < 0) { perror("assigned_dev_update_msi: kvm_irqchip_add_msi_route"); return; @@ -1016,6 +1015,7 @@ static void assigned_dev_update_msi_msg(PCIDevice *pci_dev) kvm_irqchip_update_msi_route(kvm_state, assigned_dev->msi_virq[0], msi_get_message(pci_dev, 0), pci_dev); + kvm_irqchip_commit_routes(kvm_state); } static bool assigned_dev_msix_masked(MSIXTableEntry *entry) @@ -1042,7 +1042,6 @@ static int assigned_dev_update_msix_mmio(PCIDevice *pci_dev) uint16_t entries_nr = 0; int i, r = 0; MSIXTableEntry *entry = adev->msix_table; - MSIMessage msg; /* Get the usable entry number for allocating */ for (i = 0; i < adev->msix_max; i++, entry++) { @@ -1079,9 +1078,7 @@ static int assigned_dev_update_msix_mmio(PCIDevice *pci_dev) continue; } - msg.address = entry->addr_lo | ((uint64_t)entry->addr_hi << 32); - msg.data = entry->data; - r = kvm_irqchip_add_msi_route(kvm_state, msg, pci_dev); + r = kvm_irqchip_add_msi_route(kvm_state, i, pci_dev); if (r < 0) { return r; } @@ -1310,6 +1307,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; } @@ -1481,7 +1479,7 @@ static int assigned_device_pci_cap_init(PCIDevice *pci_dev, Error **errp) * error bits, leave the rest. */ status = pci_get_long(pci_dev->config + pos + PCI_X_STATUS); status &= ~(PCI_X_STATUS_BUS | PCI_X_STATUS_DEVFN); - status |= pci_requester_id(pci_dev); + status |= pci_get_bdf(pci_dev); status &= ~(PCI_X_STATUS_SPL_DISC | PCI_X_STATUS_UNX_SPL | PCI_X_STATUS_SPL_ERR); pci_set_long(pci_dev->config + pos + PCI_X_STATUS, status); @@ -1605,6 +1603,7 @@ static void assigned_dev_msix_mmio_write(void *opaque, hwaddr addr, if (ret) { error_report("Error updating irq routing entry (%d)", ret); } + kvm_irqchip_commit_routes(kvm_state); } } } @@ -1633,7 +1632,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 +1641,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 +1652,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 +1661,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 +1890,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/kvmvapic.c b/hw/i386/kvmvapic.c index c69f37404..3bf1ddd97 100644 --- a/hw/i386/kvmvapic.c +++ b/hw/i386/kvmvapic.c @@ -9,6 +9,9 @@ * top-level directory. */ #include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" +#include "exec/exec-all.h" #include "sysemu/sysemu.h" #include "sysemu/cpus.h" #include "sysemu/kvm.h" @@ -394,10 +397,10 @@ static void patch_instruction(VAPICROMState *s, X86CPU *cpu, target_ulong ip) CPUX86State *env = &cpu->env; VAPICHandlers *handlers; uint8_t opcode[2]; - uint32_t imm32; + uint32_t imm32 = 0; target_ulong current_pc = 0; target_ulong current_cs_base = 0; - int current_flags = 0; + uint32_t current_flags = 0; if (smp_cpus == 1) { handlers = &s->rom_state.up; @@ -446,9 +449,8 @@ static void patch_instruction(VAPICROMState *s, X86CPU *cpu, target_ulong ip) resume_all_vcpus(); if (!kvm_enabled()) { - cs->current_tb = NULL; tb_gen_code(cs, current_pc, current_cs_base, current_flags, 1); - cpu_resume_from_signal(cs, NULL); + cpu_loop_exit_noexc(cs); } } diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 99437e0b7..022dd1b20 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -67,6 +67,7 @@ #include "qapi/visitor.h" #include "qapi-visit.h" #include "qom/cpu.h" +#include "hw/nmi.h" /* debug PC/ISA interrupts */ //#define DEBUG_IRQ @@ -380,7 +381,7 @@ ISADevice *pc_find_fdc0(void) error_report("warning: multiple floppy disk controllers with " "iobase=0x3f0 have been found"); error_printf("the one being picked for CMOS setup might not reflect " - "your intent"); + "your intent\n"); } return state.floppy; @@ -470,9 +471,6 @@ void pc_cmos_init(PCMachineState *pcms, rtc_set_memory(s, 0x5c, val >> 8); rtc_set_memory(s, 0x5d, val >> 16); - /* set the number of CPU */ - rtc_set_memory(s, 0x5f, smp_cpus - 1); - object_property_add_link(OBJECT(pcms), "rtc_state", TYPE_ISA_DEVICE, (Object **)&pcms->rtc, @@ -504,7 +502,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, @@ -515,7 +513,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(); } @@ -534,9 +532,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 = { @@ -573,6 +569,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) @@ -764,8 +762,6 @@ static FWCfgState *bochs_bios_init(AddressSpace *as, PCMachineState *pcms) acpi_tables, acpi_tables_len); fw_cfg_add_i32(fw_cfg, FW_CFG_IRQ0_OVERRIDE, kvm_allows_irq0_override()); - pc_build_smbios(fw_cfg); - fw_cfg_add_bytes(fw_cfg, FW_CFG_E820_TABLE, &e820_reserve, sizeof(e820_reserve)); fw_cfg_add_file(fw_cfg, "etc/e820", e820_table, @@ -813,11 +809,26 @@ static long get_file_size(FILE *f) return size; } +/* setup_data types */ +#define SETUP_NONE 0 +#define SETUP_E820_EXT 1 +#define SETUP_DTB 2 +#define SETUP_PCI 3 +#define SETUP_EFI 4 + +struct setup_data { + uint64_t next; + uint32_t type; + uint32_t len; + uint8_t data[0]; +} __attribute__((packed)); + static void load_linux(PCMachineState *pcms, FWCfgState *fw_cfg) { uint16_t protocol; int setup_size, kernel_size, initrd_size = 0, cmdline_size; + int dtb_size, setup_data_offset; uint32_t initrd_max; uint8_t header[8192], *setup, *kernel, *initrd_data; hwaddr real_addr, prot_addr, cmdline_addr, initrd_addr = 0; @@ -825,8 +836,10 @@ static void load_linux(PCMachineState *pcms, char *vmode; MachineState *machine = MACHINE(pcms); PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); + struct setup_data *setup_data; const char *kernel_filename = machine->kernel_filename; const char *initrd_filename = machine->initrd_filename; + const char *dtb_filename = machine->dtb; const char *kernel_cmdline = machine->kernel_cmdline; /* Align to 16 bytes as a paranoia measure */ @@ -989,6 +1002,35 @@ static void load_linux(PCMachineState *pcms, exit(1); } fclose(f); + + /* append dtb to kernel */ + if (dtb_filename) { + if (protocol < 0x209) { + fprintf(stderr, "qemu: Linux kernel too old to load a dtb\n"); + exit(1); + } + + dtb_size = get_image_size(dtb_filename); + if (dtb_size <= 0) { + fprintf(stderr, "qemu: error reading dtb %s: %s\n", + dtb_filename, strerror(errno)); + exit(1); + } + + setup_data_offset = QEMU_ALIGN_UP(kernel_size, 16); + kernel_size = setup_data_offset + sizeof(struct setup_data) + dtb_size; + kernel = g_realloc(kernel, kernel_size); + + stq_p(header+0x250, prot_addr + setup_data_offset); + + setup_data = (struct setup_data *)(kernel + setup_data_offset); + setup_data->next = 0; + setup_data->type = cpu_to_le32(SETUP_DTB); + setup_data->len = cpu_to_le32(dtb_size); + + load_image_size(dtb_filename, setup_data->data, dtb_size); + } + memcpy(setup, header, MIN(sizeof(header), setup_size)); fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, prot_addr); @@ -999,8 +1041,13 @@ static void load_linux(PCMachineState *pcms, fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, setup_size); fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, setup, setup_size); - option_rom[nb_option_roms].name = "linuxboot.bin"; - option_rom[nb_option_roms].bootindex = 0; + if (fw_cfg_dma_enabled(fw_cfg)) { + option_rom[nb_option_roms].name = "linuxboot_dma.bin"; + option_rom[nb_option_roms].bootindex = 0; + } else { + option_rom[nb_option_roms].name = "linuxboot.bin"; + option_rom[nb_option_roms].bootindex = 0; + } nb_option_roms++; } @@ -1040,21 +1087,28 @@ void pc_acpi_smi_interrupt(void *opaque, int irq, int level) } } -static X86CPU *pc_new_cpu(const char *cpu_model, int64_t apic_id, +static int pc_present_cpus_count(PCMachineState *pcms) +{ + int i, boot_cpus = 0; + for (i = 0; i < pcms->possible_cpus->len; i++) { + if (pcms->possible_cpus->cpus[i].cpu) { + boot_cpus++; + } + } + return boot_cpus; +} + +static X86CPU *pc_new_cpu(const char *typename, int64_t apic_id, Error **errp) { X86CPU *cpu = NULL; Error *local_err = NULL; - cpu = cpu_x86_create(cpu_model, &local_err); - if (local_err != NULL) { - goto out; - } + cpu = X86_CPU(object_new(typename)); object_property_set_int(OBJECT(cpu), apic_id, "apic-id", &local_err); object_property_set_bool(OBJECT(cpu), true, "realized", &local_err); -out: if (local_err) { error_propagate(errp, local_err); object_unref(OBJECT(cpu)); @@ -1066,7 +1120,8 @@ out: void pc_hot_add_cpu(const int64_t id, Error **errp) { X86CPU *cpu; - MachineState *machine = MACHINE(qdev_get_machine()); + ObjectClass *oc; + PCMachineState *pcms = PC_MACHINE(qdev_get_machine()); int64_t apic_id = x86_cpu_apic_id_from_index(id); Error *local_err = NULL; @@ -1075,18 +1130,6 @@ void pc_hot_add_cpu(const int64_t id, Error **errp) return; } - if (cpu_exists(apic_id)) { - error_setg(errp, "Unable to add CPU: %" PRIi64 - ", it already exists", id); - return; - } - - if (id >= max_cpus) { - error_setg(errp, "Unable to add CPU: %" PRIi64 - ", max allowed: %d", id, max_cpus - 1); - return; - } - if (apic_id >= ACPI_CPU_HOTPLUG_ID_LIMIT) { error_setg(errp, "Unable to add CPU: %" PRIi64 ", resulting APIC ID (%" PRIi64 ") is too large", @@ -1094,7 +1137,9 @@ void pc_hot_add_cpu(const int64_t id, Error **errp) return; } - cpu = pc_new_cpu(machine->cpu_model, apic_id, &local_err); + assert(pcms->possible_cpus->cpus[0].cpu); /* BSP is always present */ + oc = OBJECT_CLASS(CPU_GET_CLASS(pcms->possible_cpus->cpus[0].cpu)); + cpu = pc_new_cpu(object_class_get_name(oc), apic_id, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -1105,6 +1150,10 @@ void pc_hot_add_cpu(const int64_t id, Error **errp) void pc_cpus_init(PCMachineState *pcms) { int i; + CPUClass *cc; + ObjectClass *oc; + const char *typename; + gchar **model_pieces; X86CPU *cpu = NULL; MachineState *machine = MACHINE(pcms); @@ -1117,6 +1166,22 @@ void pc_cpus_init(PCMachineState *pcms) #endif } + model_pieces = g_strsplit(machine->cpu_model, ",", 2); + if (!model_pieces[0]) { + error_report("Invalid/empty CPU model name"); + exit(1); + } + + oc = cpu_class_by_name(TYPE_X86_CPU, model_pieces[0]); + if (oc == NULL) { + error_report("Unable to find CPU definition: %s", model_pieces[0]); + exit(1); + } + typename = object_class_get_name(oc); + cc = CPU_CLASS(oc); + cc->parse_features(typename, model_pieces[1], &error_fatal); + g_strfreev(model_pieces); + /* Calculates the limit to CPU APIC ID values * * Limit for the APIC ID value, so that all @@ -1137,9 +1202,8 @@ void pc_cpus_init(PCMachineState *pcms) pcms->possible_cpus->cpus[i].arch_id = x86_cpu_apic_id_from_index(i); pcms->possible_cpus->len++; if (i < smp_cpus) { - cpu = pc_new_cpu(machine->cpu_model, x86_cpu_apic_id_from_index(i), + cpu = pc_new_cpu(typename, x86_cpu_apic_id_from_index(i), &error_fatal); - pcms->possible_cpus->cpus[i].cpu = CPU(cpu); object_unref(OBJECT(cpu)); } } @@ -1148,13 +1212,33 @@ void pc_cpus_init(PCMachineState *pcms) smbios_set_cpuid(cpu->env.cpuid_version, cpu->env.features[FEAT_1_EDX]); } -/* pci-info ROM file. Little endian format */ -typedef struct PcRomPciInfo { - uint64_t w32_min; - uint64_t w32_max; - uint64_t w64_min; - uint64_t w64_max; -} PcRomPciInfo; +static void pc_build_feature_control_file(PCMachineState *pcms) +{ + X86CPU *cpu = X86_CPU(pcms->possible_cpus->cpus[0].cpu); + CPUX86State *env = &cpu->env; + uint32_t unused, ecx, edx; + uint64_t feature_control_bits = 0; + uint64_t *val; + + cpu_x86_cpuid(env, 1, 0, &unused, &unused, &ecx, &edx); + if (ecx & CPUID_EXT_VMX) { + feature_control_bits |= FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX; + } + + if ((edx & (CPUID_EXT2_MCE | CPUID_EXT2_MCA)) == + (CPUID_EXT2_MCE | CPUID_EXT2_MCA) && + (env->mcg_cap & MCG_LMCE_P)) { + feature_control_bits |= FEATURE_CONTROL_LMCE; + } + + if (!feature_control_bits) { + return; + } + + val = g_malloc(sizeof(*val)); + *val = cpu_to_le64(feature_control_bits | FEATURE_CONTROL_LOCKED); + fw_cfg_add_file(pcms->fw_cfg, "etc/msr_feature_control", val, sizeof(*val)); +} static void pc_machine_done(Notifier *notifier, void *data) @@ -1163,6 +1247,9 @@ void pc_machine_done(Notifier *notifier, void *data) PCMachineState, machine_done); PCIBus *bus = pcms->bus; + /* set the number of CPUs */ + rtc_set_memory(pcms->rtc, 0x5f, pc_present_cpus_count(pcms) - 1); + if (bus) { int extra_hosts = 0; @@ -1181,11 +1268,15 @@ void pc_machine_done(Notifier *notifier, void *data) } acpi_setup(); + if (pcms->fw_cfg) { + pc_build_smbios(pcms->fw_cfg); + pc_build_feature_control_file(pcms); + } } 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; @@ -1195,20 +1286,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); } @@ -1263,6 +1340,7 @@ void xen_load_linux(PCMachineState *pcms) load_linux(pcms, fw_cfg); for (i = 0; i < nb_option_roms; i++) { assert(!strcmp(option_rom[i].name, "linuxboot.bin") || + !strcmp(option_rom[i].name, "linuxboot_dma.bin") || !strcmp(option_rom[i].name, "multiboot.bin")); rom_add_option(option_rom[i].name, option_rom[i].bootindex); } @@ -1395,6 +1473,9 @@ void pc_memory_init(PCMachineState *pcms, rom_add_option(option_rom[i].name, option_rom[i].bootindex); } pcms->fw_cfg = fw_cfg; + + /* Init default IOAPIC address space */ + pcms->ioapic_as = &address_space_memory; } qemu_irq pc_allocate_cpu_irq(void) @@ -1676,44 +1757,204 @@ static int pc_apic_cmp(const void *a, const void *b) return apic_a->arch_id - apic_b->arch_id; } +/* returns pointer to CPUArchId descriptor that matches CPU's apic_id + * in pcms->possible_cpus->cpus, if pcms->possible_cpus->cpus has no + * entry correponding to CPU's apic_id returns NULL. + */ +static CPUArchId *pc_find_cpu_slot(PCMachineState *pcms, CPUState *cpu, + int *idx) +{ + CPUClass *cc = CPU_GET_CLASS(cpu); + CPUArchId apic_id, *found_cpu; + + apic_id.arch_id = cc->get_arch_id(CPU(cpu)); + found_cpu = bsearch(&apic_id, pcms->possible_cpus->cpus, + pcms->possible_cpus->len, sizeof(*pcms->possible_cpus->cpus), + pc_apic_cmp); + if (found_cpu && idx) { + *idx = found_cpu - pcms->possible_cpus->cpus; + } + return found_cpu; +} + static void pc_cpu_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - CPUClass *cc = CPU_GET_CLASS(dev); - CPUArchId apic_id, *found_cpu; + CPUArchId *found_cpu; + HotplugHandlerClass *hhc; + Error *local_err = NULL; + PCMachineState *pcms = PC_MACHINE(hotplug_dev); + + if (pcms->acpi_dev) { + hhc = HOTPLUG_HANDLER_GET_CLASS(pcms->acpi_dev); + hhc->plug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err); + if (local_err) { + goto out; + } + } + + if (dev->hotplugged) { + /* increment the number of CPUs */ + rtc_set_memory(pcms->rtc, 0x5f, rtc_get_memory(pcms->rtc, 0x5f) + 1); + } + + found_cpu = pc_find_cpu_slot(pcms, CPU(dev), NULL); + found_cpu->cpu = CPU(dev); +out: + error_propagate(errp, local_err); +} +static void pc_cpu_unplug_request_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + int idx = -1; HotplugHandlerClass *hhc; Error *local_err = NULL; PCMachineState *pcms = PC_MACHINE(hotplug_dev); - if (!dev->hotplugged) { + pc_find_cpu_slot(pcms, CPU(dev), &idx); + assert(idx != -1); + if (idx == 0) { + error_setg(&local_err, "Boot CPU is unpluggable"); goto out; } - if (!pcms->acpi_dev) { - error_setg(&local_err, - "cpu hotplug is not enabled: missing acpi device"); + 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) +{ + CPUArchId *found_cpu; + HotplugHandlerClass *hhc; + Error *local_err = NULL; + PCMachineState *pcms = PC_MACHINE(hotplug_dev); + hhc = HOTPLUG_HANDLER_GET_CLASS(pcms->acpi_dev); - hhc->plug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err); + hhc->unplug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err); + if (local_err) { goto out; } - /* increment the number of CPUs */ - rtc_set_memory(pcms->rtc, 0x5f, rtc_get_memory(pcms->rtc, 0x5f) + 1); + found_cpu = pc_find_cpu_slot(pcms, CPU(dev), NULL); + found_cpu->cpu = NULL; + object_unparent(OBJECT(dev)); - apic_id.arch_id = cc->get_arch_id(CPU(dev)); - found_cpu = bsearch(&apic_id, pcms->possible_cpus->cpus, - pcms->possible_cpus->len, sizeof(*pcms->possible_cpus->cpus), - pc_apic_cmp); - assert(found_cpu); - found_cpu->cpu = CPU(dev); -out: + rtc_set_memory(pcms->rtc, 0x5f, rtc_get_memory(pcms->rtc, 0x5f) - 1); + out: error_propagate(errp, local_err); } +static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + int idx; + CPUState *cs; + CPUArchId *cpu_slot; + X86CPUTopoInfo topo; + X86CPU *cpu = X86_CPU(dev); + PCMachineState *pcms = PC_MACHINE(hotplug_dev); + + /* if APIC ID is not set, set it based on socket/core/thread properties */ + if (cpu->apic_id == UNASSIGNED_APIC_ID) { + int max_socket = (max_cpus - 1) / smp_threads / smp_cores; + + if (cpu->socket_id < 0) { + error_setg(errp, "CPU socket-id is not set"); + return; + } else if (cpu->socket_id > max_socket) { + error_setg(errp, "Invalid CPU socket-id: %u must be in range 0:%u", + cpu->socket_id, max_socket); + return; + } + if (cpu->core_id < 0) { + error_setg(errp, "CPU core-id is not set"); + return; + } else if (cpu->core_id > (smp_cores - 1)) { + error_setg(errp, "Invalid CPU core-id: %u must be in range 0:%u", + cpu->core_id, smp_cores - 1); + return; + } + if (cpu->thread_id < 0) { + error_setg(errp, "CPU thread-id is not set"); + return; + } else if (cpu->thread_id > (smp_threads - 1)) { + error_setg(errp, "Invalid CPU thread-id: %u must be in range 0:%u", + cpu->thread_id, smp_threads - 1); + return; + } + + topo.pkg_id = cpu->socket_id; + topo.core_id = cpu->core_id; + topo.smt_id = cpu->thread_id; + cpu->apic_id = apicid_from_topo_ids(smp_cores, smp_threads, &topo); + } + + cpu_slot = pc_find_cpu_slot(pcms, CPU(dev), &idx); + if (!cpu_slot) { + x86_topo_ids_from_apicid(cpu->apic_id, smp_cores, smp_threads, &topo); + error_setg(errp, "Invalid CPU [socket: %u, core: %u, thread: %u] with" + " APIC ID %" PRIu32 ", valid index range 0:%d", + topo.pkg_id, topo.core_id, topo.smt_id, cpu->apic_id, + pcms->possible_cpus->len - 1); + return; + } + + if (cpu_slot->cpu) { + error_setg(errp, "CPU[%d] with APIC ID %" PRIu32 " exists", + idx, cpu->apic_id); + return; + } + + /* if 'address' properties socket-id/core-id/thread-id are not set, set them + * so that query_hotpluggable_cpus would show correct values + */ + /* TODO: move socket_id/core_id/thread_id checks into x86_cpu_realizefn() + * once -smp refactoring is complete and there will be CPU private + * CPUState::nr_cores and CPUState::nr_threads fields instead of globals */ + x86_topo_ids_from_apicid(cpu->apic_id, smp_cores, smp_threads, &topo); + if (cpu->socket_id != -1 && cpu->socket_id != topo.pkg_id) { + error_setg(errp, "property socket-id: %u doesn't match set apic-id:" + " 0x%x (socket-id: %u)", cpu->socket_id, cpu->apic_id, topo.pkg_id); + return; + } + cpu->socket_id = topo.pkg_id; + + if (cpu->core_id != -1 && cpu->core_id != topo.core_id) { + error_setg(errp, "property core-id: %u doesn't match set apic-id:" + " 0x%x (core-id: %u)", cpu->core_id, cpu->apic_id, topo.core_id); + return; + } + cpu->core_id = topo.core_id; + + if (cpu->thread_id != -1 && cpu->thread_id != topo.smt_id) { + error_setg(errp, "property thread-id: %u doesn't match set apic-id:" + " 0x%x (thread-id: %u)", cpu->thread_id, cpu->apic_id, topo.smt_id); + return; + } + cpu->thread_id = topo.smt_id; + + cs = CPU(cpu); + cs->cpu_index = idx; +} + +static void pc_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + pc_cpu_pre_plug(hotplug_dev, dev, errp); + } +} + static void pc_machine_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { @@ -1729,6 +1970,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))); @@ -1740,6 +1983,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))); @@ -1892,7 +2137,7 @@ static void pc_machine_initfn(Object *obj) pc_machine_get_hotplug_memory_region_size, NULL, NULL, NULL, &error_abort); - pcms->max_ram_below_4g = 1ULL << 32; /* 4G */ + pcms->max_ram_below_4g = 0; /* use default */ object_property_add(obj, PC_MACHINE_MAX_RAM_BELOW_4G, "size", pc_machine_get_max_ram_below_4g, pc_machine_set_max_ram_below_4g, @@ -1963,11 +2208,72 @@ static CPUArchIdList *pc_possible_cpu_arch_ids(MachineState *machine) return list; } +static HotpluggableCPUList *pc_query_hotpluggable_cpus(MachineState *machine) +{ + int i; + CPUState *cpu; + HotpluggableCPUList *head = NULL; + PCMachineState *pcms = PC_MACHINE(machine); + const char *cpu_type; + + cpu = pcms->possible_cpus->cpus[0].cpu; + assert(cpu); /* BSP is always present */ + cpu_type = object_class_get_name(OBJECT_CLASS(CPU_GET_CLASS(cpu))); + + for (i = 0; i < pcms->possible_cpus->len; i++) { + X86CPUTopoInfo topo; + HotpluggableCPUList *list_item = g_new0(typeof(*list_item), 1); + HotpluggableCPU *cpu_item = g_new0(typeof(*cpu_item), 1); + CpuInstanceProperties *cpu_props = g_new0(typeof(*cpu_props), 1); + const uint32_t apic_id = pcms->possible_cpus->cpus[i].arch_id; + + x86_topo_ids_from_apicid(apic_id, smp_cores, smp_threads, &topo); + + cpu_item->type = g_strdup(cpu_type); + cpu_item->vcpus_count = 1; + cpu_props->has_socket_id = true; + cpu_props->socket_id = topo.pkg_id; + cpu_props->has_core_id = true; + cpu_props->core_id = topo.core_id; + cpu_props->has_thread_id = true; + cpu_props->thread_id = topo.smt_id; + cpu_item->props = cpu_props; + + cpu = pcms->possible_cpus->cpus[i].cpu; + if (cpu) { + cpu_item->has_qom_path = true; + cpu_item->qom_path = object_get_canonical_path(OBJECT(cpu)); + } + + list_item->value = cpu_item; + list_item->next = head; + head = list_item; + } + return head; +} + +static void x86_nmi(NMIState *n, int cpu_index, Error **errp) +{ + /* cpu index isn't used */ + CPUState *cs; + + CPU_FOREACH(cs) { + X86CPU *cpu = X86_CPU(cs); + + if (!cpu->apic_state) { + cpu_interrupt(cs, CPU_INTERRUPT_NMI); + } else { + apic_deliver_nmi(cpu->apic_state); + } + } +} + static void pc_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); PCMachineClass *pcmc = PC_MACHINE_CLASS(oc); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); + NMIClass *nc = NMI_CLASS(oc); pcmc->get_hotplug_handler = mc->get_hotplug_handler; pcmc->pci_enabled = true; @@ -1986,13 +2292,16 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) mc->get_hotplug_handler = pc_get_hotpug_handler; mc->cpu_index_to_socket_id = pc_cpu_index_to_socket_id; mc->possible_cpu_arch_ids = pc_possible_cpu_arch_ids; + mc->query_hotpluggable_cpus = pc_query_hotpluggable_cpus; mc->default_boot_order = "cad"; mc->hot_add_cpu = pc_hot_add_cpu; mc->max_cpus = 255; mc->reset = pc_machine_reset; + hc->pre_plug = pc_machine_device_pre_plug_cb; hc->plug = pc_machine_device_plug_cb; hc->unplug_request = pc_machine_device_unplug_request_cb; hc->unplug = pc_machine_device_unplug_cb; + nc->nmi_monitor_handler = x86_nmi; } static const TypeInfo pc_machine_info = { @@ -2005,6 +2314,7 @@ static const TypeInfo pc_machine_info = { .class_init = pc_machine_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, + { TYPE_NMI }, { } }, }; diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 7f50116bc..a07dc816b 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -23,7 +23,6 @@ */ #include "qemu/osdep.h" -#include <glib.h> #include "hw/hw.h" #include "hw/loader.h" @@ -87,42 +86,65 @@ static void pc_init1(MachineState *machine, MemoryRegion *rom_memory; ram_addr_t lowmem; - /* Check whether RAM fits below 4G (leaving 1/2 GByte for IO memory). - * If it doesn't, we need to split it in chunks below and above 4G. - * In any case, try to make sure that guest addresses aligned at - * 1G boundaries get mapped to host addresses aligned at 1G boundaries. - * For old machine types, use whatever split we used historically to avoid - * breaking migration. + /* + * Calculate ram split, for memory below and above 4G. It's a bit + * complicated for backward compatibility reasons ... + * + * - Traditional split is 3.5G (lowmem = 0xe0000000). This is the + * default value for max_ram_below_4g now. + * + * - Then, to gigabyte align the memory, we move the split to 3G + * (lowmem = 0xc0000000). But only in case we have to split in + * the first place, i.e. ram_size is larger than (traditional) + * lowmem. And for new machine types (gigabyte_align = true) + * only, for live migration compatibility reasons. + * + * - Next the max-ram-below-4g option was added, which allowed to + * reduce lowmem to a smaller value, to allow a larger PCI I/O + * window below 4G. qemu doesn't enforce gigabyte alignment here, + * but prints a warning. + * + * - Finally max-ram-below-4g got updated to also allow raising lowmem, + * so legacy non-PAE guests can get as much memory as possible in + * the 32bit address space below 4G. + * + * - Note that Xen has its own ram setp code in xen_ram_init(), + * called via xen_hvm_init(). + * + * Examples: + * qemu -M pc-1.7 -m 4G (old default) -> 3584M low, 512M high + * qemu -M pc -m 4G (new default) -> 3072M low, 1024M high + * qemu -M pc,max-ram-below-4g=2G -m 4G -> 2048M low, 2048M high + * qemu -M pc,max-ram-below-4g=4G -m 3968M -> 3968M low (=4G-128M) */ - if (machine->ram_size >= 0xe0000000) { - lowmem = pcmc->gigabyte_align ? 0xc0000000 : 0xe0000000; + if (xen_enabled()) { + xen_hvm_init(pcms, &ram_memory); } else { - lowmem = 0xe0000000; - } - - /* Handle the machine opt max-ram-below-4g. It is basically doing - * min(qemu limit, user limit). - */ - if (lowmem > pcms->max_ram_below_4g) { + if (!pcms->max_ram_below_4g) { + pcms->max_ram_below_4g = 0xe0000000; /* default: 3.5G */ + } lowmem = pcms->max_ram_below_4g; - if (machine->ram_size - lowmem > lowmem && - lowmem & ((1ULL << 30) - 1)) { - error_report("Warning: Large machine and max_ram_below_4g(%"PRIu64 - ") not a multiple of 1G; possible bad performance.", - pcms->max_ram_below_4g); + if (machine->ram_size >= pcms->max_ram_below_4g) { + if (pcmc->gigabyte_align) { + if (lowmem > 0xc0000000) { + lowmem = 0xc0000000; + } + if (lowmem & ((1ULL << 30) - 1)) { + error_report("Warning: Large machine and max_ram_below_4g " + "(%" PRIu64 ") not a multiple of 1G; " + "possible bad performance.", + pcms->max_ram_below_4g); + } + } } - } - - if (machine->ram_size >= lowmem) { - pcms->above_4g_mem_size = machine->ram_size - lowmem; - pcms->below_4g_mem_size = lowmem; - } else { - pcms->above_4g_mem_size = 0; - pcms->below_4g_mem_size = machine->ram_size; - } - if (xen_enabled()) { - xen_hvm_init(pcms, &ram_memory); + if (machine->ram_size >= lowmem) { + pcms->above_4g_mem_size = machine->ram_size - lowmem; + pcms->below_4g_mem_size = lowmem; + } else { + pcms->above_4g_mem_size = 0; + pcms->below_4g_mem_size = machine->ram_size; + } } pc_cpus_init(pcms); @@ -246,7 +268,7 @@ static void pc_init1(MachineState *machine, pc_cmos_init(pcms, idebus[0], idebus[1], rtc_state); - if (pcmc->pci_enabled && usb_enabled()) { + if (pcmc->pci_enabled && machine_usb(machine)) { pci_create_simple(pci_bus, piix3_devfn + 2, "piix3-usb-uhci"); } @@ -416,13 +438,27 @@ static void pc_i440fx_machine_options(MachineClass *m) m->default_display = "std"; } -static void pc_i440fx_2_6_machine_options(MachineClass *m) +static void pc_i440fx_2_7_machine_options(MachineClass *m) { pc_i440fx_machine_options(m); m->alias = "pc"; m->is_default = 1; } +DEFINE_I440FX_MACHINE(v2_7, "pc-i440fx-2.7", NULL, + pc_i440fx_2_7_machine_options); + + +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); +} + DEFINE_I440FX_MACHINE(v2_6, "pc-i440fx-2.6", NULL, pc_i440fx_2_6_machine_options); @@ -431,8 +467,6 @@ static void pc_i440fx_2_5_machine_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_i440fx_2_6_machine_options(m); - m->alias = NULL; - m->is_default = 0; pcmc->save_tsc_khz = false; m->legacy_fw_cfg_order = 1; SET_MACHINE_COMPAT(m, PC_COMPAT_2_5); @@ -582,7 +616,7 @@ DEFINE_I440FX_MACHINE(v1_4, "pc-i440fx-1.4", pc_compat_1_4, #define PC_COMPAT_1_3 \ - PC_COMPAT_1_4 \ + PC_CPU_MODEL_IDS("1.3.0") \ {\ .driver = "usb-tablet",\ .property = "usb_version",\ @@ -614,7 +648,7 @@ DEFINE_I440FX_MACHINE(v1_3, "pc-1.3", pc_compat_1_3, #define PC_COMPAT_1_2 \ - PC_COMPAT_1_3 \ + PC_CPU_MODEL_IDS("1.2.0") \ {\ .driver = "nec-usb-xhci",\ .property = "msi",\ @@ -653,7 +687,7 @@ DEFINE_I440FX_MACHINE(v1_2, "pc-1.2", pc_compat_1_2, #define PC_COMPAT_1_1 \ - PC_COMPAT_1_2 \ + PC_CPU_MODEL_IDS("1.1.0") \ {\ .driver = "virtio-scsi-pci",\ .property = "hotplug",\ @@ -696,7 +730,7 @@ DEFINE_I440FX_MACHINE(v1_1, "pc-1.1", pc_compat_1_2, #define PC_COMPAT_1_0 \ - PC_COMPAT_1_1 \ + PC_CPU_MODEL_IDS("1.0") \ {\ .driver = TYPE_ISA_FDC,\ .property = "check_media_rate",\ @@ -727,7 +761,7 @@ DEFINE_I440FX_MACHINE(v1_0, "pc-1.0", pc_compat_1_2, #define PC_COMPAT_0_15 \ - PC_COMPAT_1_0 + PC_CPU_MODEL_IDS("0.15") static void pc_i440fx_0_15_machine_options(MachineClass *m) { @@ -741,7 +775,7 @@ DEFINE_I440FX_MACHINE(v0_15, "pc-0.15", pc_compat_1_2, #define PC_COMPAT_0_14 \ - PC_COMPAT_0_15 \ + PC_CPU_MODEL_IDS("0.14") \ {\ .driver = "virtio-blk-pci",\ .property = "event_idx",\ @@ -780,7 +814,7 @@ DEFINE_I440FX_MACHINE(v0_14, "pc-0.14", pc_compat_1_2, #define PC_COMPAT_0_13 \ - PC_COMPAT_0_14 \ + PC_CPU_MODEL_IDS("0.13") \ {\ .driver = TYPE_PCI_DEVICE,\ .property = "command_serr_enable",\ @@ -817,7 +851,7 @@ DEFINE_I440FX_MACHINE(v0_13, "pc-0.13", pc_compat_0_13, #define PC_COMPAT_0_12 \ - PC_COMPAT_0_13 \ + PC_CPU_MODEL_IDS("0.12") \ {\ .driver = "virtio-serial-pci",\ .property = "max_ports",\ @@ -852,7 +886,7 @@ DEFINE_I440FX_MACHINE(v0_12, "pc-0.12", pc_compat_0_13, #define PC_COMPAT_0_11 \ - PC_COMPAT_0_12 \ + PC_CPU_MODEL_IDS("0.11") \ {\ .driver = "virtio-blk-pci",\ .property = "vectors",\ @@ -883,7 +917,7 @@ DEFINE_I440FX_MACHINE(v0_11, "pc-0.11", pc_compat_0_13, #define PC_COMPAT_0_10 \ - PC_COMPAT_0_11 \ + PC_CPU_MODEL_IDS("0.10") \ {\ .driver = "virtio-blk-pci",\ .property = "class",\ diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 04aae8958..c0b996192 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(); @@ -93,6 +94,9 @@ static void pc_q35_init(MachineState *machine) /* Handle the machine opt max-ram-below-4g. It is basically doing * min(qemu limit, user limit). */ + if (!pcms->max_ram_below_4g) { + pcms->max_ram_below_4g = 1ULL << 32; /* default: 4G */; + } if (lowmem > pcms->max_ram_below_4g) { lowmem = pcms->max_ram_below_4g; if (machine->ram_size - lowmem > lowmem && @@ -159,17 +163,22 @@ 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); host_bus = phb->bus; - pcms->bus = phb->bus; /* create ISA bus */ lpc = pci_create_simple_multifunction(host_bus, PCI_DEVFN(ICH9_LPC_DEV, ICH9_LPC_FUNC), true, @@ -184,16 +193,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()) { @@ -234,7 +242,7 @@ static void pc_q35_init(MachineState *machine) ide_drive_get(hd, ICH_AHCI(ahci)->ahci.ports); ahci_ide_create_devs(ahci, hd); - if (usb_enabled()) { + if (machine_usb(machine)) { /* Should we create 6 UHCI according to ich9 spec? */ ehci_create_ich9_with_companions(host_bus, 0x1d); } @@ -281,14 +289,27 @@ static void pc_q35_machine_options(MachineClass *m) m->default_machine_opts = "firmware=bios-256k.bin"; m->default_display = "std"; m->no_floppy = 1; + m->has_dynamic_sysbus = true; } -static void pc_q35_2_6_machine_options(MachineClass *m) +static void pc_q35_2_7_machine_options(MachineClass *m) { pc_q35_machine_options(m); m->alias = "q35"; } +DEFINE_Q35_MACHINE(v2_7, "pc-q35-2.7", NULL, + pc_q35_2_7_machine_options); + +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); +} + DEFINE_Q35_MACHINE(v2_6, "pc-q35-2.6", NULL, pc_q35_2_6_machine_options); @@ -296,7 +317,6 @@ static void pc_q35_2_5_machine_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_q35_2_6_machine_options(m); - m->alias = NULL; pcmc->save_tsc_khz = false; m->legacy_fw_cfg_order = 1; SET_MACHINE_COMPAT(m, PC_COMPAT_2_5); diff --git a/hw/i386/pci-assign-load-rom.c b/hw/i386/pci-assign-load-rom.c index 4bbb08c95..0d8e4b282 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/i386/trace-events b/hw/i386/trace-events new file mode 100644 index 000000000..7735e46ea --- /dev/null +++ b/hw/i386/trace-events @@ -0,0 +1,15 @@ +# See docs/tracing.txt for syntax documentation. + +# hw/i386/xen/xen_platform.c +xen_platform_log(char *s) "xen platform: %s" + +# hw/i386/xen/xen_pvdevice.c +xen_pv_mmio_read(uint64_t addr) "WARNING: read from Xen PV Device MMIO space (address %"PRIx64")" +xen_pv_mmio_write(uint64_t addr) "WARNING: write to Xen PV Device MMIO space (address %"PRIx64")" + +# hw/i386/pc.c +mhp_pc_dimm_assigned_slot(int slot) "0x%d" +mhp_pc_dimm_assigned_address(uint64_t addr) "0x%"PRIx64 + +# hw/i386/x86-iommu.c +x86_iommu_iec_notify(bool global, uint32_t index, uint32_t mask) "Notify IEC invalidation: global=%d index=%" PRIu32 " mask=%" PRIu32 diff --git a/hw/i386/x86-iommu.c b/hw/i386/x86-iommu.c new file mode 100644 index 000000000..ce26b2a71 --- /dev/null +++ b/hw/i386/x86-iommu.c @@ -0,0 +1,128 @@ +/* + * QEMU emulation of common X86 IOMMU + * + * Copyright (C) 2016 Peter Xu, Red Hat <peterx@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "hw/boards.h" +#include "hw/i386/x86-iommu.h" +#include "qemu/error-report.h" +#include "trace.h" + +void x86_iommu_iec_register_notifier(X86IOMMUState *iommu, + iec_notify_fn fn, void *data) +{ + IEC_Notifier *notifier = g_new0(IEC_Notifier, 1); + + notifier->iec_notify = fn; + notifier->private = data; + + QLIST_INSERT_HEAD(&iommu->iec_notifiers, notifier, list); +} + +void x86_iommu_iec_notify_all(X86IOMMUState *iommu, bool global, + uint32_t index, uint32_t mask) +{ + IEC_Notifier *notifier; + + trace_x86_iommu_iec_notify(global, index, mask); + + QLIST_FOREACH(notifier, &iommu->iec_notifiers, list) { + if (notifier->iec_notify) { + notifier->iec_notify(notifier->private, global, + index, mask); + } + } +} + +/* Default X86 IOMMU device */ +static X86IOMMUState *x86_iommu_default = NULL; + +static void x86_iommu_set_default(X86IOMMUState *x86_iommu) +{ + assert(x86_iommu); + + if (x86_iommu_default) { + error_report("QEMU does not support multiple vIOMMUs " + "for x86 yet."); + exit(1); + } + + x86_iommu_default = x86_iommu; +} + +X86IOMMUState *x86_iommu_get_default(void) +{ + return x86_iommu_default; +} + +static void x86_iommu_realize(DeviceState *dev, Error **errp) +{ + X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(dev); + X86IOMMUClass *x86_class = X86_IOMMU_GET_CLASS(dev); + QLIST_INIT(&x86_iommu->iec_notifiers); + if (x86_class->realize) { + x86_class->realize(dev, errp); + } + x86_iommu_set_default(X86_IOMMU_DEVICE(dev)); +} + +static void x86_iommu_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + dc->realize = x86_iommu_realize; +} + +static bool x86_iommu_intremap_prop_get(Object *o, Error **errp) +{ + X86IOMMUState *s = X86_IOMMU_DEVICE(o); + return s->intr_supported; +} + +static void x86_iommu_intremap_prop_set(Object *o, bool value, Error **errp) +{ + X86IOMMUState *s = X86_IOMMU_DEVICE(o); + s->intr_supported = value; +} + +static void x86_iommu_instance_init(Object *o) +{ + X86IOMMUState *s = X86_IOMMU_DEVICE(o); + + /* By default, do not support IR */ + s->intr_supported = false; + object_property_add_bool(o, "intremap", x86_iommu_intremap_prop_get, + x86_iommu_intremap_prop_set, NULL); +} + +static const TypeInfo x86_iommu_info = { + .name = TYPE_X86_IOMMU_DEVICE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = x86_iommu_instance_init, + .instance_size = sizeof(X86IOMMUState), + .class_init = x86_iommu_class_init, + .class_size = sizeof(X86IOMMUClass), + .abstract = true, +}; + +static void x86_iommu_register_types(void) +{ + type_register_static(&x86_iommu_info); +} + +type_init(x86_iommu_register_types) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index f244bc01c..f3438ad78 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -22,17 +22,17 @@ */ #include "qemu/osdep.h" -#include <hw/hw.h> -#include <hw/pci/msi.h> -#include <hw/i386/pc.h> -#include <hw/pci/pci.h> +#include "hw/hw.h" +#include "hw/pci/msi.h" +#include "hw/i386/pc.h" +#include "hw/pci/pci.h" #include "qemu/error-report.h" #include "sysemu/block-backend.h" #include "sysemu/dma.h" -#include "internal.h" -#include <hw/ide/pci.h> -#include <hw/ide/ahci.h> +#include "hw/ide/internal.h" +#include "hw/ide/pci.h" +#include "hw/ide/ahci.h" #define DEBUG_AHCI 0 @@ -919,6 +919,7 @@ static void ncq_err(NCQTransferState *ncq_tfs) ide_state->error = ABRT_ERR; ide_state->status = READY_STAT | ERR_STAT; ncq_tfs->drive->port_regs.scr_err |= (1 << ncq_tfs->tag); + qemu_sglist_destroy(&ncq_tfs->sglist); ncq_tfs->used = 0; } @@ -1006,7 +1007,8 @@ static void execute_ncq_command(NCQTransferState *ncq_tfs) dma_acct_start(ide_state->blk, &ncq_tfs->acct, &ncq_tfs->sglist, BLOCK_ACCT_READ); ncq_tfs->aiocb = dma_blk_read(ide_state->blk, &ncq_tfs->sglist, - ncq_tfs->lba, ncq_cb, ncq_tfs); + ncq_tfs->lba << BDRV_SECTOR_BITS, + ncq_cb, ncq_tfs); break; case WRITE_FPDMA_QUEUED: DPRINTF(port, "NCQ writing %d sectors to LBA %"PRId64", tag %d\n", @@ -1018,12 +1020,12 @@ static void execute_ncq_command(NCQTransferState *ncq_tfs) dma_acct_start(ide_state->blk, &ncq_tfs->acct, &ncq_tfs->sglist, BLOCK_ACCT_WRITE); ncq_tfs->aiocb = dma_blk_write(ide_state->blk, &ncq_tfs->sglist, - ncq_tfs->lba, ncq_cb, ncq_tfs); + ncq_tfs->lba << BDRV_SECTOR_BITS, + ncq_cb, ncq_tfs); break; default: DPRINTF(port, "error: unsupported NCQ command (0x%02x) received\n", ncq_tfs->cmd); - qemu_sglist_destroy(&ncq_tfs->sglist); ncq_err(ncq_tfs); } } @@ -1090,7 +1092,6 @@ static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis, error_report("ahci: PRDT length for NCQ command (0x%zx) " "is smaller than the requested size (0x%zx)", ncq_tfs->sglist.size, size); - qemu_sglist_destroy(&ncq_tfs->sglist); ncq_err(ncq_tfs); ahci_trigger_irq(ad->hba, ad, PORT_IRQ_OVERFLOW); return; @@ -1476,6 +1477,7 @@ void ahci_realize(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports) ad->port.dma->ops = &ahci_dma_ops; ide_register_restart_cb(&ad->port); } + g_free(irqs); } void ahci_uninit(AHCIState *s) diff --git a/hw/ide/ahci.h b/hw/ide/ahci.h deleted file mode 100644 index bc777ed5c..000000000 --- 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/atapi.c b/hw/ide/atapi.c index 2bb606c1c..618967503 100644 --- a/hw/ide/atapi.c +++ b/hw/ide/atapi.c @@ -28,6 +28,9 @@ #include "hw/scsi/scsi.h" #include "sysemu/block-backend.h" +#define ATAPI_SECTOR_BITS (2 + BDRV_SECTOR_BITS) +#define ATAPI_SECTOR_SIZE (1 << ATAPI_SECTOR_BITS) + static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret); static void padstr8(uint8_t *buf, int buf_size, const char *src) @@ -111,7 +114,7 @@ cd_read_sector_sync(IDEState *s) { int ret; block_acct_start(blk_get_stats(s->blk), &s->acct, - 4 * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ); + ATAPI_SECTOR_SIZE, BLOCK_ACCT_READ); #ifdef DEBUG_IDE_ATAPI printf("cd_read_sector_sync: lba=%d\n", s->lba); @@ -119,12 +122,12 @@ cd_read_sector_sync(IDEState *s) switch (s->cd_sector_size) { case 2048: - ret = blk_read(s->blk, (int64_t)s->lba << 2, - s->io_buffer, 4); + ret = blk_pread(s->blk, (int64_t)s->lba << ATAPI_SECTOR_BITS, + s->io_buffer, ATAPI_SECTOR_SIZE); break; case 2352: - ret = blk_read(s->blk, (int64_t)s->lba << 2, - s->io_buffer + 16, 4); + ret = blk_pread(s->blk, (int64_t)s->lba << ATAPI_SECTOR_BITS, + s->io_buffer + 16, ATAPI_SECTOR_SIZE); if (ret >= 0) { cd_data_to_raw(s->io_buffer, s->lba); } @@ -182,7 +185,7 @@ static int cd_read_sector(IDEState *s) s->iov.iov_base = (s->cd_sector_size == 2352) ? s->io_buffer + 16 : s->io_buffer; - s->iov.iov_len = 4 * BDRV_SECTOR_SIZE; + s->iov.iov_len = ATAPI_SECTOR_SIZE; qemu_iovec_init_external(&s->qiov, &s->iov, 1); #ifdef DEBUG_IDE_ATAPI @@ -190,7 +193,7 @@ static int cd_read_sector(IDEState *s) #endif block_acct_start(blk_get_stats(s->blk), &s->acct, - 4 * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ); + ATAPI_SECTOR_SIZE, BLOCK_ACCT_READ); ide_buffered_readv(s, (int64_t)s->lba << 2, &s->qiov, 4, cd_read_sector_cb, s); @@ -383,6 +386,7 @@ static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret) if (ret < 0) { if (ide_handle_rw_error(s, -ret, ide_dma_cmd_to_retry(s->dma_cmd))) { if (s->bus->error_status) { + s->bus->dma->aiocb = NULL; return; } goto eot; @@ -435,7 +439,7 @@ static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret) #endif s->bus->dma->iov.iov_base = (void *)(s->io_buffer + data_offset); - s->bus->dma->iov.iov_len = n * 4 * 512; + s->bus->dma->iov.iov_len = n * ATAPI_SECTOR_SIZE; qemu_iovec_init_external(&s->bus->dma->qiov, &s->bus->dma->iov, 1); s->bus->dma->aiocb = ide_buffered_readv(s, (int64_t)s->lba << 2, diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c index 49294a531..9ebb8d4fb 100644 --- a/hw/ide/cmd646.c +++ b/hw/ide/cmd646.c @@ -23,15 +23,15 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" -#include <hw/hw.h> -#include <hw/i386/pc.h> -#include <hw/pci/pci.h> -#include <hw/isa/isa.h> +#include "hw/hw.h" +#include "hw/i386/pc.h" +#include "hw/pci/pci.h" +#include "hw/isa/isa.h" #include "sysemu/block-backend.h" #include "sysemu/sysemu.h" #include "sysemu/dma.h" -#include <hw/ide/pci.h> +#include "hw/ide/pci.h" /* CMD646 specific */ #define CFR 0x50 diff --git a/hw/ide/core.c b/hw/ide/core.c index 41e6a2dc4..45b6df132 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -23,10 +23,10 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" -#include <hw/hw.h> -#include <hw/i386/pc.h> -#include <hw/pci/pci.h> -#include <hw/isa/isa.h> +#include "hw/hw.h" +#include "hw/i386/pc.h" +#include "hw/pci/pci.h" +#include "hw/isa/isa.h" #include "qemu/error-report.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" @@ -35,7 +35,7 @@ #include "sysemu/block-backend.h" #include "qemu/cutils.h" -#include <hw/ide/internal.h> +#include "hw/ide/internal.h" /* These values were based on a Seagate ST3500418AS but have been modified to make more sense in QEMU */ @@ -423,8 +423,10 @@ static void ide_issue_trim_cb(void *opaque, int ret) } /* Got an entry! Submit and exit. */ - iocb->aiocb = blk_aio_discard(iocb->blk, sector, count, - ide_issue_trim_cb, opaque); + iocb->aiocb = blk_aio_pdiscard(iocb->blk, + sector << BDRV_SECTOR_BITS, + count << BDRV_SECTOR_BITS, + ide_issue_trim_cb, opaque); return; } @@ -441,13 +443,14 @@ static void ide_issue_trim_cb(void *opaque, int ret) } } -BlockAIOCB *ide_issue_trim(BlockBackend *blk, - int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, - BlockCompletionFunc *cb, void *opaque) +BlockAIOCB *ide_issue_trim( + int64_t offset, QEMUIOVector *qiov, + BlockCompletionFunc *cb, void *cb_opaque, void *opaque) { + BlockBackend *blk = opaque; TrimAIOCB *iocb; - iocb = blk_aio_get(&trim_aiocb_info, blk, cb, opaque); + iocb = blk_aio_get(&trim_aiocb_info, blk, cb, cb_opaque); iocb->blk = blk; iocb->bh = qemu_bh_new(ide_trim_bh_cb, iocb); iocb->ret = 0; @@ -465,6 +468,20 @@ void ide_abort_command(IDEState *s) s->error = ABRT_ERR; } +static void ide_set_retry(IDEState *s) +{ + s->bus->retry_unit = s->unit; + s->bus->retry_sector_num = ide_get_sector(s); + s->bus->retry_nsector = s->nsector; +} + +static void ide_clear_retry(IDEState *s) +{ + s->bus->retry_unit = -1; + s->bus->retry_sector_num = 0; + s->bus->retry_nsector = 0; +} + /* prepare data transfer and tell what to do after */ void ide_transfer_start(IDEState *s, uint8_t *buf, int size, EndTransferFunc *end_transfer_func) @@ -472,6 +489,7 @@ void ide_transfer_start(IDEState *s, uint8_t *buf, int size, s->end_transfer_func = end_transfer_func; s->data_ptr = buf; s->data_end = buf + size; + ide_set_retry(s); if (!(s->status & ERR_STAT)) { s->status |= DRQ_STAT; } @@ -616,8 +634,8 @@ BlockAIOCB *ide_buffered_readv(IDEState *s, int64_t sector_num, req->iov.iov_len = iov->size; qemu_iovec_init_external(&req->qiov, &req->iov, 1); - aioreq = blk_aio_readv(s->blk, sector_num, &req->qiov, nb_sectors, - ide_buffered_readv_cb, req); + aioreq = blk_aio_preadv(s->blk, sector_num << BDRV_SECTOR_BITS, + &req->qiov, 0, ide_buffered_readv_cb, req); QLIST_INSERT_HEAD(&s->buffered_requests, req, list); return aioreq; @@ -755,9 +773,7 @@ void dma_buf_commit(IDEState *s, uint32_t tx_bytes) void ide_set_inactive(IDEState *s, bool more) { s->bus->dma->aiocb = NULL; - s->bus->retry_unit = -1; - s->bus->retry_sector_num = 0; - s->bus->retry_nsector = 0; + ide_clear_retry(s); if (s->bus->dma->ops->set_inactive) { s->bus->dma->ops->set_inactive(s->bus->dma, more); } @@ -799,6 +815,7 @@ static void ide_dma_cb(void *opaque, int ret) IDEState *s = opaque; int n; int64_t sector_num; + uint64_t offset; bool stay_active = false; if (ret == -ECANCELED) { @@ -806,6 +823,8 @@ static void ide_dma_cb(void *opaque, int ret) } if (ret < 0) { if (ide_handle_rw_error(s, -ret, ide_dma_cmd_to_retry(s->dma_cmd))) { + s->bus->dma->aiocb = NULL; + dma_buf_commit(s, 0); return; } } @@ -859,18 +878,20 @@ static void ide_dma_cb(void *opaque, int ret) return; } + offset = sector_num << BDRV_SECTOR_BITS; switch (s->dma_cmd) { case IDE_DMA_READ: - s->bus->dma->aiocb = dma_blk_read(s->blk, &s->sg, sector_num, + s->bus->dma->aiocb = dma_blk_read(s->blk, &s->sg, offset, ide_dma_cb, s); break; case IDE_DMA_WRITE: - s->bus->dma->aiocb = dma_blk_write(s->blk, &s->sg, sector_num, + s->bus->dma->aiocb = dma_blk_write(s->blk, &s->sg, offset, ide_dma_cb, s); break; case IDE_DMA_TRIM: - s->bus->dma->aiocb = dma_blk_io(s->blk, &s->sg, sector_num, - ide_issue_trim, ide_dma_cb, s, + s->bus->dma->aiocb = dma_blk_io(blk_get_aio_context(s->blk), + &s->sg, offset, + ide_issue_trim, s->blk, ide_dma_cb, s, DMA_DIRECTION_TO_DEVICE); break; default: @@ -910,9 +931,7 @@ static void ide_sector_start_dma(IDEState *s, enum ide_dma_cmd dma_cmd) void ide_start_dma(IDEState *s, BlockCompletionFunc *cb) { s->io_buffer_index = 0; - s->bus->retry_unit = s->unit; - s->bus->retry_sector_num = ide_get_sector(s); - s->bus->retry_nsector = s->nsector; + ide_set_retry(s); if (s->bus->dma->ops->start_dma) { s->bus->dma->ops->start_dma(s->bus->dma, s, cb); } @@ -1006,8 +1025,8 @@ static void ide_sector_write(IDEState *s) block_acct_start(blk_get_stats(s->blk), &s->acct, n * BDRV_SECTOR_SIZE, BLOCK_ACCT_WRITE); - s->pio_aiocb = blk_aio_writev(s->blk, sector_num, &s->qiov, n, - ide_sector_write_cb, s); + s->pio_aiocb = blk_aio_pwritev(s->blk, sector_num << BDRV_SECTOR_BITS, + &s->qiov, 0, ide_sector_write_cb, s); } static void ide_flush_cb(void *opaque, int ret) @@ -1042,6 +1061,7 @@ static void ide_flush_cache(IDEState *s) } s->status |= BUSY_STAT; + ide_set_retry(s); block_acct_start(blk_get_stats(s->blk), &s->acct, 0, BLOCK_ACCT_FLUSH); s->pio_aiocb = blk_aio_flush(s->blk, ide_flush_cb, s); } diff --git a/hw/ide/ich.c b/hw/ide/ich.c index 0a13334ba..459916977 100644 --- a/hw/ide/ich.c +++ b/hw/ide/ich.c @@ -61,16 +61,15 @@ */ #include "qemu/osdep.h" -#include <hw/hw.h> -#include <hw/pci/msi.h> -#include <hw/i386/pc.h> -#include <hw/pci/pci.h> -#include <hw/isa/isa.h> +#include "hw/hw.h" +#include "hw/pci/msi.h" +#include "hw/i386/pc.h" +#include "hw/pci/pci.h" +#include "hw/isa/isa.h" #include "sysemu/block-backend.h" #include "sysemu/dma.h" - -#include <hw/ide/pci.h> -#include <hw/ide/ahci.h> +#include "hw/ide/pci.h" +#include "hw/ide/ahci.h" #define ICH9_MSI_CAP_OFFSET 0x80 #define ICH9_SATA_CAP_OFFSET 0xA8 @@ -111,6 +110,7 @@ static void pci_ich9_ahci_realize(PCIDevice *dev, Error **errp) int sata_cap_offset; uint8_t *sata_cap; d = ICH_AHCI(dev); + int ret; ahci_realize(&d->ahci, DEVICE(dev), pci_get_address_space(dev), 6); @@ -146,7 +146,10 @@ static void pci_ich9_ahci_realize(PCIDevice *dev, Error **errp) /* Although the AHCI 1.3 specification states that the first capability * should be PMCAP, the Intel ICH9 data sheet specifies that the ICH9 * AHCI device puts the MSI capability first, pointing to 0x80. */ - msi_init(dev, ICH9_MSI_CAP_OFFSET, 1, true, false); + ret = msi_init(dev, ICH9_MSI_CAP_OFFSET, 1, true, false, NULL); + /* Any error other than -ENOTSUP(board's MSI support is broken) + * is a programming error. Fall back to INTx silently on -ENOTSUP */ + assert(!ret || ret == -ENOTSUP); } static void pci_ich9_uninit(PCIDevice *dev) diff --git a/hw/ide/internal.h b/hw/ide/internal.h deleted file mode 100644 index d2c458f57..000000000 --- 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(BlockBackend *blk, - int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, - BlockCompletionFunc *cb, 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/isa.c b/hw/ide/isa.c index eba567c87..40213d662 100644 --- a/hw/ide/isa.c +++ b/hw/ide/isa.c @@ -23,13 +23,13 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" -#include <hw/hw.h> -#include <hw/i386/pc.h> -#include <hw/isa/isa.h> +#include "hw/hw.h" +#include "hw/i386/pc.h" +#include "hw/isa/isa.h" #include "sysemu/block-backend.h" #include "sysemu/dma.h" -#include <hw/ide/internal.h> +#include "hw/ide/internal.h" /***********************************************************/ /* ISA IDE definitions */ diff --git a/hw/ide/macio.c b/hw/ide/macio.c index 76256eb8a..76f97c253 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -29,7 +29,7 @@ #include "sysemu/block-backend.h" #include "sysemu/dma.h" -#include <hw/ide/internal.h> +#include "hw/ide/internal.h" /* debug MACIO */ // #define DEBUG_MACIO @@ -55,8 +55,8 @@ static const int debug_macio = 0; /* * Unaligned DMA read/write access functions required for OS X/Darwin which * don't perform DMA transactions on sector boundaries. These functions are - * modelled on bdrv_co_do_preadv()/bdrv_co_do_pwritev() and so should be - * easy to remove if the unaligned block APIs are ever exposed. + * modelled on bdrv_co_preadv()/bdrv_co_pwritev() and so should be easy to + * remove if the unaligned block APIs are ever exposed. */ static void pmac_dma_read(BlockBackend *blk, @@ -66,8 +66,7 @@ static void pmac_dma_read(BlockBackend *blk, DBDMA_io *io = opaque; MACIOIDEState *m = io->opaque; IDEState *s = idebus_active_if(&m->bus); - dma_addr_t dma_addr, dma_len; - void *mem; + dma_addr_t dma_addr; int64_t sector_num; int nsector; uint64_t align = BDRV_SECTOR_SIZE; @@ -84,9 +83,10 @@ static void pmac_dma_read(BlockBackend *blk, sector_num, nsector); dma_addr = io->addr; - dma_len = io->len; - mem = dma_memory_map(&address_space_memory, dma_addr, &dma_len, - DMA_DIRECTION_FROM_DEVICE); + io->dir = DMA_DIRECTION_FROM_DEVICE; + io->dma_len = io->len; + io->dma_mem = dma_memory_map(&address_space_memory, dma_addr, &io->dma_len, + io->dir); if (offset & (align - 1)) { head_bytes = offset & (align - 1); @@ -100,7 +100,7 @@ static void pmac_dma_read(BlockBackend *blk, offset = offset & ~(align - 1); } - qemu_iovec_add(&io->iov, mem, io->len); + qemu_iovec_add(&io->iov, io->dma_mem, io->len); if ((offset + bytes) & (align - 1)) { tail_bytes = (offset + bytes) & (align - 1); @@ -120,8 +120,7 @@ static void pmac_dma_read(BlockBackend *blk, MACIO_DPRINTF("--- Block read transfer - sector_num: %" PRIx64 " " "nsector: %x\n", (offset >> 9), (bytes >> 9)); - s->bus->dma->aiocb = blk_aio_readv(blk, (offset >> 9), &io->iov, - (bytes >> 9), cb, io); + s->bus->dma->aiocb = blk_aio_preadv(blk, offset, &io->iov, 0, cb, io); } static void pmac_dma_write(BlockBackend *blk, @@ -131,8 +130,7 @@ static void pmac_dma_write(BlockBackend *blk, DBDMA_io *io = opaque; MACIOIDEState *m = io->opaque; IDEState *s = idebus_active_if(&m->bus); - dma_addr_t dma_addr, dma_len; - void *mem; + dma_addr_t dma_addr; int64_t sector_num; int nsector; uint64_t align = BDRV_SECTOR_SIZE; @@ -150,9 +148,10 @@ static void pmac_dma_write(BlockBackend *blk, sector_num, nsector); dma_addr = io->addr; - dma_len = io->len; - mem = dma_memory_map(&address_space_memory, dma_addr, &dma_len, - DMA_DIRECTION_TO_DEVICE); + io->dir = DMA_DIRECTION_TO_DEVICE; + io->dma_len = io->len; + io->dma_mem = dma_memory_map(&address_space_memory, dma_addr, &io->dma_len, + io->dir); if (offset & (align - 1)) { head_bytes = offset & (align - 1); @@ -164,7 +163,7 @@ static void pmac_dma_write(BlockBackend *blk, blk_pread(s->blk, (sector_num << 9), &io->head_remainder, align); qemu_iovec_add(&io->iov, &io->head_remainder, head_bytes); - qemu_iovec_add(&io->iov, mem, io->len); + qemu_iovec_add(&io->iov, io->dma_mem, io->len); bytes += offset & (align - 1); offset = offset & ~(align - 1); @@ -182,7 +181,7 @@ static void pmac_dma_write(BlockBackend *blk, blk_pread(s->blk, (sector_num << 9), &io->tail_remainder, align); if (!unaligned_head) { - qemu_iovec_add(&io->iov, mem, io->len); + qemu_iovec_add(&io->iov, io->dma_mem, io->len); } qemu_iovec_add(&io->iov, &io->tail_remainder + tail_bytes, @@ -194,7 +193,7 @@ static void pmac_dma_write(BlockBackend *blk, } if (!unaligned_head && !unaligned_tail) { - qemu_iovec_add(&io->iov, mem, io->len); + qemu_iovec_add(&io->iov, io->dma_mem, io->len); } s->io_buffer_size -= io->len; @@ -205,8 +204,7 @@ static void pmac_dma_write(BlockBackend *blk, MACIO_DPRINTF("--- Block write transfer - sector_num: %" PRIx64 " " "nsector: %x\n", (offset >> 9), (bytes >> 9)); - s->bus->dma->aiocb = blk_aio_writev(blk, (offset >> 9), &io->iov, - (bytes >> 9), cb, io); + s->bus->dma->aiocb = blk_aio_pwritev(blk, offset, &io->iov, 0, cb, io); } static void pmac_dma_trim(BlockBackend *blk, @@ -216,24 +214,23 @@ static void pmac_dma_trim(BlockBackend *blk, DBDMA_io *io = opaque; MACIOIDEState *m = io->opaque; IDEState *s = idebus_active_if(&m->bus); - dma_addr_t dma_addr, dma_len; - void *mem; + dma_addr_t dma_addr; qemu_iovec_destroy(&io->iov); qemu_iovec_init(&io->iov, io->len / MACIO_PAGE_SIZE + 1); dma_addr = io->addr; - dma_len = io->len; - mem = dma_memory_map(&address_space_memory, dma_addr, &dma_len, - DMA_DIRECTION_TO_DEVICE); + io->dir = DMA_DIRECTION_TO_DEVICE; + io->dma_len = io->len; + io->dma_mem = dma_memory_map(&address_space_memory, dma_addr, &io->dma_len, + io->dir); - qemu_iovec_add(&io->iov, mem, io->len); + qemu_iovec_add(&io->iov, io->dma_mem, io->len); s->io_buffer_size -= io->len; s->io_buffer_index += io->len; io->len = 0; - s->bus->dma->aiocb = ide_issue_trim(blk, (offset >> 9), &io->iov, - (bytes >> 9), cb, io); + s->bus->dma->aiocb = ide_issue_trim(offset, &io->iov, cb, io, blk); } static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) @@ -274,7 +271,9 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) if (s->lba == -1) { /* Non-block ATAPI transfer - just copy to RAM */ s->io_buffer_size = MIN(s->io_buffer_size, io->len); - cpu_physical_memory_write(io->addr, s->io_buffer, s->io_buffer_size); + dma_memory_write(&address_space_memory, io->addr, s->io_buffer, + s->io_buffer_size); + io->len = 0; ide_atapi_cmd_ok(s); m->dma_active = false; goto done; @@ -287,6 +286,9 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) return; done: + dma_memory_unmap(&address_space_memory, io->dma_mem, io->dma_len, + io->dir, io->dma_len); + if (ret < 0) { block_acct_failed(blk_get_stats(s->blk), &s->acct); } else { @@ -353,6 +355,9 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) return; done: + dma_memory_unmap(&address_space_memory, io->dma_mem, io->dma_len, + io->dir, io->dma_len); + if (s->dma_cmd == IDE_DMA_READ || s->dma_cmd == IDE_DMA_WRITE) { if (ret < 0) { block_acct_failed(blk_get_stats(s->blk), &s->acct); @@ -402,7 +407,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/microdrive.c b/hw/ide/microdrive.c index 5c9db8047..e3fd30e45 100644 --- a/hw/ide/microdrive.c +++ b/hw/ide/microdrive.c @@ -23,13 +23,13 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" -#include <hw/hw.h> -#include <hw/i386/pc.h> -#include <hw/pcmcia.h> +#include "hw/hw.h" +#include "hw/i386/pc.h" +#include "hw/pcmcia.h" #include "sysemu/block-backend.h" #include "sysemu/dma.h" -#include <hw/ide/internal.h> +#include "hw/ide/internal.h" #define TYPE_MICRODRIVE "microdrive" #define MICRODRIVE(obj) OBJECT_CHECK(MicroDriveState, (obj), TYPE_MICRODRIVE) diff --git a/hw/ide/mmio.c b/hw/ide/mmio.c index 493f65a1d..6f12f456e 100644 --- a/hw/ide/mmio.c +++ b/hw/ide/mmio.c @@ -28,7 +28,7 @@ #include "sysemu/block-backend.h" #include "sysemu/dma.h" -#include <hw/ide/internal.h> +#include "hw/ide/internal.h" /***********************************************************/ /* MMIO based ide port diff --git a/hw/ide/pci.c b/hw/ide/pci.c index 8d56a00b1..3cfb510af 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -23,14 +23,14 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" -#include <hw/hw.h> -#include <hw/i386/pc.h> -#include <hw/pci/pci.h> -#include <hw/isa/isa.h> +#include "hw/hw.h" +#include "hw/i386/pc.h" +#include "hw/pci/pci.h" +#include "hw/isa/isa.h" #include "sysemu/block-backend.h" #include "sysemu/dma.h" #include "qemu/error-report.h" -#include <hw/ide/pci.h> +#include "hw/ide/pci.h" #define BMDMA_PAGE_SIZE 4096 diff --git a/hw/ide/pci.h b/hw/ide/pci.h deleted file mode 100644 index 0f2d4b91a..000000000 --- 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/ide/piix.c b/hw/ide/piix.c index 6d76ce980..c190fcaa3 100644 --- a/hw/ide/piix.c +++ b/hw/ide/piix.c @@ -24,15 +24,15 @@ */ #include "qemu/osdep.h" -#include <hw/hw.h> -#include <hw/i386/pc.h> -#include <hw/pci/pci.h> -#include <hw/isa/isa.h> +#include "hw/hw.h" +#include "hw/i386/pc.h" +#include "hw/pci/pci.h" +#include "hw/isa/isa.h" #include "sysemu/block-backend.h" #include "sysemu/sysemu.h" #include "sysemu/dma.h" -#include <hw/ide/pci.h> +#include "hw/ide/pci.h" static uint64_t bmdma_read(void *opaque, hwaddr addr, unsigned size) { diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c index 4bc74a32d..67c76bfcd 100644 --- a/hw/ide/qdev.c +++ b/hw/ide/qdev.c @@ -17,11 +17,11 @@ * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ #include "qemu/osdep.h" -#include <hw/hw.h> +#include "hw/hw.h" #include "sysemu/dma.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include <hw/ide/internal.h> +#include "hw/ide/internal.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "hw/block/block.h" @@ -180,6 +180,7 @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind) return -1; } } + blkconf_apply_backend_options(&dev->conf); if (ide_init_drive(s, dev->conf.blk, kind, dev->version, dev->serial, dev->model, dev->wwn, @@ -233,9 +234,7 @@ static void ide_dev_set_bootindex(Object *obj, Visitor *v, const char *name, d->unit ? "/disk@1" : "/disk@0"); } out: - if (local_err) { - error_propagate(errp, local_err); - } + error_propagate(errp, local_err); } static void ide_dev_instance_init(Object *obj) @@ -265,6 +264,7 @@ static int ide_drive_initfn(IDEDevice *dev) #define DEFINE_IDE_DEV_PROPERTIES() \ DEFINE_BLOCK_PROPERTIES(IDEDrive, dev.conf), \ + DEFINE_BLOCK_ERROR_PROPERTIES(IDEDrive, dev.conf), \ DEFINE_PROP_STRING("ver", IDEDrive, dev.version), \ DEFINE_PROP_UINT64("wwn", IDEDrive, dev.wwn, 0), \ DEFINE_PROP_STRING("serial", IDEDrive, dev.serial),\ diff --git a/hw/ide/via.c b/hw/ide/via.c index d3f72267a..5b32ecb38 100644 --- a/hw/ide/via.c +++ b/hw/ide/via.c @@ -24,15 +24,15 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" -#include <hw/hw.h> -#include <hw/i386/pc.h> -#include <hw/pci/pci.h> -#include <hw/isa/isa.h> +#include "hw/hw.h" +#include "hw/i386/pc.h" +#include "hw/pci/pci.h" +#include "hw/isa/isa.h" #include "sysemu/block-backend.h" #include "sysemu/sysemu.h" #include "sysemu/dma.h" -#include <hw/ide/pci.h> +#include "hw/ide/pci.h" static uint64_t bmdma_read(void *opaque, hwaddr addr, unsigned size) diff --git a/hw/input/hid.c b/hw/input/hid.c index d92c7463b..5e2850e65 100644 --- a/hw/input/hid.c +++ b/hw/input/hid.c @@ -27,6 +27,7 @@ #include "ui/console.h" #include "qemu/timer.h" #include "hw/input/hid.h" +#include "trace.h" #define HID_USAGE_ERROR_ROLLOVER 0x01 #define HID_USAGE_POSTFAIL 0x02 @@ -234,7 +235,7 @@ static void hid_keyboard_event(DeviceState *dev, QemuConsole *src, key->down, scancodes); if (hs->n + count > QUEUE_LENGTH) { - fprintf(stderr, "usb-kbd: warning: key event queue full\n"); + trace_hid_kbd_queue_full(); return; } for (i = 0; i < count; i++) { diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c index 1d932ec19..dc57e2c76 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/input/pl050.c b/hw/input/pl050.c index 3092b0fe3..be9cd57b1 100644 --- a/hw/input/pl050.c +++ b/hw/input/pl050.c @@ -10,6 +10,7 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" #include "hw/input/ps2.h" +#include "qemu/log.h" #define TYPE_PL050 "pl050" #define PL050(obj) OBJECT_CHECK(PL050State, (obj), TYPE_PL050) diff --git a/hw/input/trace-events b/hw/input/trace-events new file mode 100644 index 000000000..8c4003f36 --- /dev/null +++ b/hw/input/trace-events @@ -0,0 +1,31 @@ +# See docs/tracing.txt for syntax documentation. + +# hw/input/ps2.c +ps2_put_keycode(void *opaque, int keycode) "%p keycode %d" +ps2_read_data(void *opaque) "%p" +ps2_set_ledstate(void *s, int ledstate) "%p ledstate %d" +ps2_reset_keyboard(void *s) "%p" +ps2_write_keyboard(void *opaque, int val) "%p val %d" +ps2_keyboard_set_translation(void *opaque, int mode) "%p mode %d" +ps2_mouse_send_packet(void *s, int dx1, int dy1, int dz1, int b) "%p x %d y %d z %d bs %#x" +ps2_mouse_event_disabled(void *opaque, int dx, int dy, int dz, int buttons_state, int mouse_dx, int mouse_dy, int mouse_dz) "%p x %d y %d z %d bs %#x mx %d my %d mz %d " +ps2_mouse_event(void *opaque, int dx, int dy, int dz, int buttons_state, int mouse_dx, int mouse_dy, int mouse_dz) "%p x %d y %d z %d bs %#x mx %d my %d mz %d " +ps2_mouse_fake_event(void *opaque) "%p" +ps2_write_mouse(void *opaque, int val) "%p val %d" +ps2_kbd_reset(void *opaque) "%p" +ps2_mouse_reset(void *opaque) "%p" +ps2_kbd_init(void *s) "%p" +ps2_mouse_init(void *s) "%p" + +# hw/input/milkymist-softusb.c +milkymist_softusb_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x" +milkymist_softusb_memory_write(uint32_t addr, uint32_t value) "addr %08x value %08x" +milkymist_softusb_mevt(uint8_t m) "m %d" +milkymist_softusb_kevt(uint8_t m) "m %d" +milkymist_softusb_pulse_irq(void) "Pulse IRQ" + +# hw/input/hid.c +hid_kbd_queue_full(void) "queue full" + +# hw/input/virtio +virtio_input_queue_full(void) "queue full" diff --git a/hw/input/virtio-input.c b/hw/input/virtio-input.c index f59749a94..ccdf7308a 100644 --- a/hw/input/virtio-input.c +++ b/hw/input/virtio-input.c @@ -7,6 +7,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/iov.h" +#include "trace.h" #include "hw/qdev.h" #include "hw/virtio/virtio.h" @@ -47,7 +48,7 @@ void virtio_input_send(VirtIOInput *vinput, virtio_input_event *event) virtqueue_get_avail_bytes(vinput->evt, &have, NULL, need, 0); if (have < need) { vinput->qindex = 0; - fprintf(stderr, "%s: ENOSPC in vq, dropping events\n", __func__); + trace_virtio_input_queue_full(); return; } @@ -216,26 +217,14 @@ static void virtio_input_reset(VirtIODevice *vdev) } } -static void virtio_input_save(QEMUFile *f, void *opaque) -{ - VirtIOInput *vinput = opaque; - VirtIODevice *vdev = VIRTIO_DEVICE(vinput); - - virtio_save(vdev, f); -} - -static int virtio_input_load(QEMUFile *f, void *opaque, int version_id) +static int virtio_input_load(QEMUFile *f, void *opaque, size_t size) { VirtIOInput *vinput = opaque; VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vinput); VirtIODevice *vdev = VIRTIO_DEVICE(vinput); int ret; - if (version_id != VIRTIO_INPUT_VM_VERSION) { - return -EINVAL; - } - - ret = virtio_load(vdev, f, version_id); + ret = virtio_load(vdev, f, VIRTIO_INPUT_VM_VERSION); if (ret) { return ret; } @@ -279,20 +268,24 @@ static void virtio_input_device_realize(DeviceState *dev, Error **errp) vinput->cfg_size); vinput->evt = virtio_add_queue(vdev, 64, virtio_input_handle_evt); vinput->sts = virtio_add_queue(vdev, 64, virtio_input_handle_sts); - - register_savevm(dev, "virtio-input", -1, VIRTIO_INPUT_VM_VERSION, - virtio_input_save, virtio_input_load, vinput); } +static void virtio_input_finalize(Object *obj) +{ + VirtIOInput *vinput = VIRTIO_INPUT(obj); + VirtIOInputConfig *cfg, *next; + + QTAILQ_FOREACH_SAFE(cfg, &vinput->cfg_list, node, next) { + QTAILQ_REMOVE(&vinput->cfg_list, cfg, node); + g_free(cfg); + } +} static void virtio_input_device_unrealize(DeviceState *dev, Error **errp) { VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(dev); VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VirtIOInput *vinput = VIRTIO_INPUT(dev); Error *local_err = NULL; - unregister_savevm(dev, "virtio-input", vinput); - if (vic->unrealize) { vic->unrealize(dev, &local_err); if (local_err) { @@ -303,6 +296,9 @@ static void virtio_input_device_unrealize(DeviceState *dev, Error **errp) virtio_cleanup(vdev); } +VMSTATE_VIRTIO_DEVICE(input, VIRTIO_INPUT_VM_VERSION, virtio_input_load, + virtio_vmstate_save); + static Property virtio_input_properties[] = { DEFINE_PROP_STRING("serial", VirtIOInput, serial), DEFINE_PROP_END_OF_LIST(), @@ -314,6 +310,7 @@ static void virtio_input_class_init(ObjectClass *klass, void *data) VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); dc->props = virtio_input_properties; + dc->vmsd = &vmstate_virtio_input; set_bit(DEVICE_CATEGORY_INPUT, dc->categories); vdc->realize = virtio_input_device_realize; vdc->unrealize = virtio_input_device_unrealize; @@ -331,6 +328,7 @@ static const TypeInfo virtio_input_info = { .class_size = sizeof(VirtIOInputClass), .class_init = virtio_input_class_init, .abstract = true, + .instance_finalize = virtio_input_finalize, }; /* ----------------------------------------------------------------- */ diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs index 0e47f0f9e..05ec21b21 100644 --- a/hw/intc/Makefile.objs +++ b/hw/intc/Makefile.objs @@ -13,6 +13,9 @@ common-obj-$(CONFIG_ARM_GIC) += arm_gic_common.o common-obj-$(CONFIG_ARM_GIC) += arm_gic.o common-obj-$(CONFIG_ARM_GIC) += arm_gicv2m.o common-obj-$(CONFIG_ARM_GIC) += arm_gicv3_common.o +common-obj-$(CONFIG_ARM_GIC) += arm_gicv3.o +common-obj-$(CONFIG_ARM_GIC) += arm_gicv3_dist.o +common-obj-$(CONFIG_ARM_GIC) += arm_gicv3_redist.o common-obj-$(CONFIG_OPENPIC) += openpic.o obj-$(CONFIG_APIC) += apic.o apic_common.o @@ -27,8 +30,11 @@ 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 obj-$(CONFIG_S390_FLIC_KVM) += s390_flic_kvm.o obj-$(CONFIG_ASPEED_SOC) += aspeed_vic.o +obj-$(CONFIG_ARM_GIC) += arm_gicv3_cpuif.o +obj-$(CONFIG_MIPS_CPS) += mips_gic.o diff --git a/hw/intc/allwinner-a10-pic.c b/hw/intc/allwinner-a10-pic.c index dc971a160..11f13663c 100644 --- a/hw/intc/allwinner-a10-pic.c +++ b/hw/intc/allwinner-a10-pic.c @@ -20,6 +20,7 @@ #include "hw/devices.h" #include "sysemu/sysemu.h" #include "hw/intc/allwinner-a10-pic.h" +#include "qemu/log.h" static void aw_a10_pic_update(AwA10PICState *s) { diff --git a/hw/intc/apic.c b/hw/intc/apic.c index 28c2ea540..45887d99c 100644 --- a/hw/intc/apic.c +++ b/hw/intc/apic.c @@ -17,6 +17,8 @@ * License along with this library; if not, see <http://www.gnu.org/licenses/> */ #include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" #include "qemu/thread.h" #include "hw/i386/apic_internal.h" #include "hw/i386/apic.h" @@ -26,7 +28,9 @@ #include "trace.h" #include "hw/i386/pc.h" #include "hw/i386/apic-msidef.h" +#include "qapi/error.h" +#define MAX_APICS 255 #define MAX_APIC_WORDS 8 #define SYNC_FROM_VAPIC 0x1 @@ -417,7 +421,7 @@ static int apic_find_dest(uint8_t dest) int i; if (apic && apic->id == dest) - return dest; /* shortcut in case apic->id == apic->idx */ + return dest; /* shortcut in case apic->id == local_apics[dest]->id */ for (i = 0; i < MAX_APICS; i++) { apic = local_apics[i]; @@ -500,14 +504,14 @@ static void apic_deliver(DeviceState *dev, uint8_t dest, uint8_t dest_mode, break; case 1: memset(deliver_bitmask, 0x00, sizeof(deliver_bitmask)); - apic_set_bit(deliver_bitmask, s->idx); + apic_set_bit(deliver_bitmask, s->id); break; case 2: memset(deliver_bitmask, 0xff, sizeof(deliver_bitmask)); break; case 3: memset(deliver_bitmask, 0xff, sizeof(deliver_bitmask)); - apic_reset_bit(deliver_bitmask, s->idx); + apic_reset_bit(deliver_bitmask, s->id); break; } @@ -868,20 +872,36 @@ static void apic_realize(DeviceState *dev, Error **errp) { APICCommonState *s = APIC_COMMON(dev); + if (s->id >= MAX_APICS) { + error_setg(errp, "%s initialization failed. APIC ID %d is invalid", + object_get_typename(OBJECT(dev)), s->id); + return; + } + memory_region_init_io(&s->io_memory, OBJECT(s), &apic_io_ops, s, "apic-msi", APIC_SPACE_SIZE); s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, apic_timer, s); - local_apics[s->idx] = s; + local_apics[s->id] = s; msi_nonbroken = true; } +static void apic_unrealize(DeviceState *dev, Error **errp) +{ + APICCommonState *s = APIC_COMMON(dev); + + timer_del(s->timer); + timer_free(s->timer); + local_apics[s->id] = NULL; +} + static void apic_class_init(ObjectClass *klass, void *data) { APICCommonClass *k = APIC_COMMON_CLASS(klass); k->realize = apic_realize; + k->unrealize = apic_unrealize; k->set_base = apic_set_base; k->set_tpr = apic_set_tpr; k->get_tpr = apic_get_tpr; diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c index 4abe145c6..14ac43c18 100644 --- a/hw/intc/apic_common.c +++ b/hw/intc/apic_common.c @@ -19,6 +19,8 @@ */ #include "qemu/osdep.h" #include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/i386/apic.h" #include "hw/i386/apic_internal.h" #include "trace.h" @@ -292,19 +294,14 @@ static int apic_load_old(QEMUFile *f, void *opaque, int version_id) return 0; } +static const VMStateDescription vmstate_apic_common; + static void apic_common_realize(DeviceState *dev, Error **errp) { APICCommonState *s = APIC_COMMON(dev); APICCommonClass *info; static DeviceState *vapic; - static int apic_no; - - if (apic_no >= MAX_APICS) { - error_setg(errp, "%s initialization failed.", - object_get_typename(OBJECT(dev))); - return; - } - s->idx = apic_no++; + int instance_id = s->id; info = APIC_COMMON_GET_CLASS(s); info->realize(dev, errp); @@ -319,6 +316,24 @@ static void apic_common_realize(DeviceState *dev, Error **errp) info->enable_tpr_reporting(s, true); } + if (s->legacy_instance_id) { + instance_id = -1; + } + vmstate_register_with_alias_id(NULL, instance_id, &vmstate_apic_common, + s, -1, 0); +} + +static void apic_common_unrealize(DeviceState *dev, Error **errp) +{ + APICCommonState *s = APIC_COMMON(dev); + APICCommonClass *info = APIC_COMMON_GET_CLASS(s); + + vmstate_unregister(NULL, &vmstate_apic_common, s); + info->unrealize(dev, errp); + + if (apic_report_tpr_access && info->enable_tpr_reporting) { + info->enable_tpr_reporting(s, false); + } } static int apic_pre_load(void *opaque) @@ -416,6 +431,8 @@ static Property apic_properties_common[] = { DEFINE_PROP_UINT8("version", APICCommonState, version, 0x14), DEFINE_PROP_BIT("vapic", APICCommonState, vapic_control, VAPIC_ENABLE_BIT, true), + DEFINE_PROP_BOOL("legacy-instance-id", APICCommonState, legacy_instance_id, + false), DEFINE_PROP_END_OF_LIST(), }; @@ -423,10 +440,10 @@ static void apic_common_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->vmsd = &vmstate_apic_common; dc->reset = apic_reset_common; dc->props = apic_properties_common; dc->realize = apic_common_realize; + dc->unrealize = apic_common_unrealize; /* * Reason: APIC and CPU need to be wired up by * x86_cpu_apic_create() diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index f55124174..b30cc9174 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -23,6 +23,8 @@ #include "gic_internal.h" #include "qapi/error.h" #include "qom/cpu.h" +#include "qemu/log.h" +#include "trace.h" //#define DEBUG_GIC @@ -93,6 +95,11 @@ void gic_update(GICState *s) } } + if (best_irq != 1023) { + trace_gic_update_bestirq(cpu, best_irq, best_prio, + s->priority_mask[cpu], s->running_priority[cpu]); + } + irq_level = fiq_level = 0; if (best_prio < s->priority_mask[cpu]) { @@ -106,10 +113,12 @@ void gic_update(GICState *s) DPRINTF("Raised pending FIQ %d (cpu %d)\n", best_irq, cpu); fiq_level = 1; + trace_gic_update_set_irq(cpu, "fiq", fiq_level); } else { DPRINTF("Raised pending IRQ %d (cpu %d)\n", best_irq, cpu); irq_level = 1; + trace_gic_update_set_irq(cpu, "irq", irq_level); } } } @@ -197,6 +206,7 @@ static void gic_set_irq(void *opaque, int irq, int level) } else { gic_set_irq_generic(s, irq, level, cm, target); } + trace_gic_set_irq(irq, level, cm, target); gic_update(s); } @@ -332,6 +342,7 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs) * is in the wrong group. */ irq = gic_get_current_pending_irq(s, cpu, attrs); + trace_gic_acknowledge_irq(cpu, irq); if (irq >= GIC_MAXIRQ) { DPRINTF("ACK, no pending interrupt or it is hidden: %d\n", irq); @@ -650,6 +661,11 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs) goto bad_reg; res = 0; for (i = 0; i < 8; i++) { + if (s->security_extn && !attrs.secure && + !GIC_TEST_GROUP(irq + i, 1 << cpu)) { + continue; /* Ignore Non-secure access of Group0 IRQ */ + } + if (GIC_TEST_ENABLED(irq + i, cm)) { res |= (1 << i); } @@ -666,6 +682,11 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs) res = 0; mask = (irq < GIC_INTERNAL) ? cm : ALL_CPU_MASK; for (i = 0; i < 8; i++) { + if (s->security_extn && !attrs.secure && + !GIC_TEST_GROUP(irq + i, 1 << cpu)) { + continue; /* Ignore Non-secure access of Group0 IRQ */ + } + if (gic_test_pending(s, irq + i, mask)) { res |= (1 << i); } @@ -678,6 +699,11 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs) res = 0; mask = (irq < GIC_INTERNAL) ? cm : ALL_CPU_MASK; for (i = 0; i < 8; i++) { + if (s->security_extn && !attrs.secure && + !GIC_TEST_GROUP(irq + i, 1 << cpu)) { + continue; /* Ignore Non-secure access of Group0 IRQ */ + } + if (GIC_TEST_ACTIVE(irq + i, mask)) { res |= (1 << i); } @@ -711,6 +737,11 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs) goto bad_reg; res = 0; for (i = 0; i < 4; i++) { + if (s->security_extn && !attrs.secure && + !GIC_TEST_GROUP(irq + i, 1 << cpu)) { + continue; /* Ignore Non-secure access of Group0 IRQ */ + } + if (GIC_TEST_MODEL(irq + i)) res |= (1 << (i * 2)); if (GIC_TEST_EDGE_TRIGGER(irq + i)) @@ -731,7 +762,12 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs) /* GICD_SPENDSGIRn */ } - res = s->sgi_pending[irq][cpu]; + if (s->security_extn && !attrs.secure && + !GIC_TEST_GROUP(irq, 1 << cpu)) { + res = 0; /* Ignore Non-secure access of Group0 IRQ */ + } else { + res = s->sgi_pending[irq][cpu]; + } } else if (offset < 0xfd0) { goto bad_reg; } else if (offset < 0x1000) { @@ -851,8 +887,14 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, (irq < GIC_INTERNAL) ? (1 << cpu) : GIC_TARGET(irq + i); int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK; + if (s->security_extn && !attrs.secure && + !GIC_TEST_GROUP(irq + i, 1 << cpu)) { + continue; /* Ignore Non-secure access of Group0 IRQ */ + } + if (!GIC_TEST_ENABLED(irq + i, cm)) { DPRINTF("Enabled IRQ %d\n", irq + i); + trace_gic_enable_irq(irq + i); } GIC_SET_ENABLED(irq + i, cm); /* If a raised level triggered IRQ enabled then mark @@ -877,8 +919,14 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, if (value & (1 << i)) { int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK; + if (s->security_extn && !attrs.secure && + !GIC_TEST_GROUP(irq + i, 1 << cpu)) { + continue; /* Ignore Non-secure access of Group0 IRQ */ + } + if (GIC_TEST_ENABLED(irq + i, cm)) { DPRINTF("Disabled IRQ %d\n", irq + i); + trace_gic_disable_irq(irq + i); } GIC_CLEAR_ENABLED(irq + i, cm); } @@ -894,6 +942,11 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, for (i = 0; i < 8; i++) { if (value & (1 << i)) { + if (s->security_extn && !attrs.secure && + !GIC_TEST_GROUP(irq + i, 1 << cpu)) { + continue; /* Ignore Non-secure access of Group0 IRQ */ + } + GIC_SET_PENDING(irq + i, GIC_TARGET(irq + i)); } } @@ -907,6 +960,11 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, } for (i = 0; i < 8; i++) { + if (s->security_extn && !attrs.secure && + !GIC_TEST_GROUP(irq + i, 1 << cpu)) { + continue; /* Ignore Non-secure access of Group0 IRQ */ + } + /* ??? This currently clears the pending bit for all CPUs, even for per-CPU interrupts. It's unclear whether this is the corect behavior. */ @@ -947,6 +1005,11 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, if (irq < GIC_NR_SGIS) value |= 0xaa; for (i = 0; i < 4; i++) { + if (s->security_extn && !attrs.secure && + !GIC_TEST_GROUP(irq + i, 1 << cpu)) { + continue; /* Ignore Non-secure access of Group0 IRQ */ + } + if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) { if (value & (1 << (i * 2))) { GIC_SET_MODEL(irq + i); @@ -970,9 +1033,12 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, } irq = (offset - 0xf10); - s->sgi_pending[irq][cpu] &= ~value; - if (s->sgi_pending[irq][cpu] == 0) { - GIC_CLEAR_PENDING(irq, 1 << cpu); + if (!s->security_extn || attrs.secure || + GIC_TEST_GROUP(irq, 1 << cpu)) { + s->sgi_pending[irq][cpu] &= ~value; + if (s->sgi_pending[irq][cpu] == 0) { + GIC_CLEAR_PENDING(irq, 1 << cpu); + } } } else if (offset < 0xf30) { /* GICD_SPENDSGIRn */ @@ -981,8 +1047,11 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, } irq = (offset - 0xf20); - GIC_SET_PENDING(irq, 1 << cpu); - s->sgi_pending[irq][cpu] |= value; + if (!s->security_extn || attrs.secure || + GIC_TEST_GROUP(irq, 1 << cpu)) { + GIC_SET_PENDING(irq, 1 << cpu); + s->sgi_pending[irq][cpu] |= value; + } } else { goto bad_reg; } diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c index bc85ab769..5593cdb3e 100644 --- a/hw/intc/arm_gic_kvm.c +++ b/hw/intc/arm_gic_kvm.c @@ -21,6 +21,8 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/sysbus.h" #include "migration/migration.h" #include "sysemu/kvm.h" diff --git a/hw/intc/arm_gicv2m.c b/hw/intc/arm_gicv2m.c index e8b5177dc..3922fbc1c 100644 --- a/hw/intc/arm_gicv2m.c +++ b/hw/intc/arm_gicv2m.c @@ -29,6 +29,8 @@ #include "qapi/error.h" #include "hw/sysbus.h" #include "hw/pci/msi.h" +#include "sysemu/kvm.h" +#include "qemu/log.h" #define TYPE_ARM_GICV2M "arm-gicv2m" #define ARM_GICV2M(obj) OBJECT_CHECK(ARMGICv2mState, (obj), TYPE_ARM_GICV2M) diff --git a/hw/intc/arm_gicv3.c b/hw/intc/arm_gicv3.c new file mode 100644 index 000000000..8a6c64721 --- /dev/null +++ b/hw/intc/arm_gicv3.c @@ -0,0 +1,400 @@ +/* + * ARM Generic Interrupt Controller v3 + * + * Copyright (c) 2015 Huawei. + * Copyright (c) 2016 Linaro Limited + * Written by Shlomo Pongratz, Peter Maydell + * + * This code is licensed under the GPL, version 2 or (at your option) + * any later version. + */ + +/* This file contains implementation code for an interrupt controller + * which implements the GICv3 architecture. Specifically this is where + * the device class itself and the functions for handling interrupts + * coming in and going out live. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/sysbus.h" +#include "hw/intc/arm_gicv3.h" +#include "gicv3_internal.h" + +static bool irqbetter(GICv3CPUState *cs, int irq, uint8_t prio) +{ + /* Return true if this IRQ at this priority should take + * precedence over the current recorded highest priority + * pending interrupt for this CPU. We also return true if + * the current recorded highest priority pending interrupt + * is the same as this one (a property which the calling code + * relies on). + */ + if (prio < cs->hppi.prio) { + return true; + } + /* If multiple pending interrupts have the same priority then it is an + * IMPDEF choice which of them to signal to the CPU. We choose to + * signal the one with the lowest interrupt number. + */ + if (prio == cs->hppi.prio && irq <= cs->hppi.irq) { + return true; + } + return false; +} + +static uint32_t gicd_int_pending(GICv3State *s, int irq) +{ + /* Recalculate which distributor interrupts are actually pending + * in the group of 32 interrupts starting at irq (which should be a multiple + * of 32), and return a 32-bit integer which has a bit set for each + * interrupt that is eligible to be signaled to the CPU interface. + * + * An interrupt is pending if: + * + the PENDING latch is set OR it is level triggered and the input is 1 + * + its ENABLE bit is set + * + the GICD enable bit for its group is set + * Conveniently we can bulk-calculate this with bitwise operations. + */ + uint32_t pend, grpmask; + uint32_t pending = *gic_bmp_ptr32(s->pending, irq); + uint32_t edge_trigger = *gic_bmp_ptr32(s->edge_trigger, irq); + uint32_t level = *gic_bmp_ptr32(s->level, irq); + uint32_t group = *gic_bmp_ptr32(s->group, irq); + uint32_t grpmod = *gic_bmp_ptr32(s->grpmod, irq); + uint32_t enable = *gic_bmp_ptr32(s->enabled, irq); + + pend = pending | (~edge_trigger & level); + pend &= enable; + + if (s->gicd_ctlr & GICD_CTLR_DS) { + grpmod = 0; + } + + grpmask = 0; + if (s->gicd_ctlr & GICD_CTLR_EN_GRP1NS) { + grpmask |= group; + } + if (s->gicd_ctlr & GICD_CTLR_EN_GRP1S) { + grpmask |= (~group & grpmod); + } + if (s->gicd_ctlr & GICD_CTLR_EN_GRP0) { + grpmask |= (~group & ~grpmod); + } + pend &= grpmask; + + return pend; +} + +static uint32_t gicr_int_pending(GICv3CPUState *cs) +{ + /* Recalculate which redistributor interrupts are actually pending, + * and return a 32-bit integer which has a bit set for each interrupt + * that is eligible to be signaled to the CPU interface. + * + * An interrupt is pending if: + * + the PENDING latch is set OR it is level triggered and the input is 1 + * + its ENABLE bit is set + * + the GICD enable bit for its group is set + * Conveniently we can bulk-calculate this with bitwise operations. + */ + uint32_t pend, grpmask, grpmod; + + pend = cs->gicr_ipendr0 | (~cs->edge_trigger & cs->level); + pend &= cs->gicr_ienabler0; + + if (cs->gic->gicd_ctlr & GICD_CTLR_DS) { + grpmod = 0; + } else { + grpmod = cs->gicr_igrpmodr0; + } + + grpmask = 0; + if (cs->gic->gicd_ctlr & GICD_CTLR_EN_GRP1NS) { + grpmask |= cs->gicr_igroupr0; + } + if (cs->gic->gicd_ctlr & GICD_CTLR_EN_GRP1S) { + grpmask |= (~cs->gicr_igroupr0 & grpmod); + } + if (cs->gic->gicd_ctlr & GICD_CTLR_EN_GRP0) { + grpmask |= (~cs->gicr_igroupr0 & ~grpmod); + } + pend &= grpmask; + + return pend; +} + +/* Update the interrupt status after state in a redistributor + * or CPU interface has changed, but don't tell the CPU i/f. + */ +static void gicv3_redist_update_noirqset(GICv3CPUState *cs) +{ + /* Find the highest priority pending interrupt among the + * redistributor interrupts (SGIs and PPIs). + */ + bool seenbetter = false; + uint8_t prio; + int i; + uint32_t pend; + + /* Find out which redistributor interrupts are eligible to be + * signaled to the CPU interface. + */ + pend = gicr_int_pending(cs); + + if (pend) { + for (i = 0; i < GIC_INTERNAL; i++) { + if (!(pend & (1 << i))) { + continue; + } + prio = cs->gicr_ipriorityr[i]; + if (irqbetter(cs, i, prio)) { + cs->hppi.irq = i; + cs->hppi.prio = prio; + seenbetter = true; + } + } + } + + if (seenbetter) { + cs->hppi.grp = gicv3_irq_group(cs->gic, cs, cs->hppi.irq); + } + + /* If the best interrupt we just found would preempt whatever + * was the previous best interrupt before this update, then + * we know it's definitely the best one now. + * If we didn't find an interrupt that would preempt the previous + * best, and the previous best is outside our range (or there was no + * previous pending interrupt at all), then that is still valid, and + * we leave it as the best. + * Otherwise, we need to do a full update (because the previous best + * interrupt has reduced in priority and any other interrupt could + * now be the new best one). + */ + if (!seenbetter && cs->hppi.prio != 0xff && cs->hppi.irq < GIC_INTERNAL) { + gicv3_full_update_noirqset(cs->gic); + } +} + +/* Update the GIC status after state in a redistributor or + * CPU interface has changed, and inform the CPU i/f of + * its new highest priority pending interrupt. + */ +void gicv3_redist_update(GICv3CPUState *cs) +{ + gicv3_redist_update_noirqset(cs); + gicv3_cpuif_update(cs); +} + +/* Update the GIC status after state in the distributor has + * changed affecting @len interrupts starting at @start, + * but don't tell the CPU i/f. + */ +static void gicv3_update_noirqset(GICv3State *s, int start, int len) +{ + int i; + uint8_t prio; + uint32_t pend = 0; + + assert(start >= GIC_INTERNAL); + assert(len > 0); + + for (i = 0; i < s->num_cpu; i++) { + s->cpu[i].seenbetter = false; + } + + /* Find the highest priority pending interrupt in this range. */ + for (i = start; i < start + len; i++) { + GICv3CPUState *cs; + + if (i == start || (i & 0x1f) == 0) { + /* Calculate the next 32 bits worth of pending status */ + pend = gicd_int_pending(s, i & ~0x1f); + } + + if (!(pend & (1 << (i & 0x1f)))) { + continue; + } + cs = s->gicd_irouter_target[i]; + if (!cs) { + /* Interrupts targeting no implemented CPU should remain pending + * and not be forwarded to any CPU. + */ + continue; + } + prio = s->gicd_ipriority[i]; + if (irqbetter(cs, i, prio)) { + cs->hppi.irq = i; + cs->hppi.prio = prio; + cs->seenbetter = true; + } + } + + /* If the best interrupt we just found would preempt whatever + * was the previous best interrupt before this update, then + * we know it's definitely the best one now. + * If we didn't find an interrupt that would preempt the previous + * best, and the previous best is outside our range (or there was + * no previous pending interrupt at all), then that + * is still valid, and we leave it as the best. + * Otherwise, we need to do a full update (because the previous best + * interrupt has reduced in priority and any other interrupt could + * now be the new best one). + */ + for (i = 0; i < s->num_cpu; i++) { + GICv3CPUState *cs = &s->cpu[i]; + + if (cs->seenbetter) { + cs->hppi.grp = gicv3_irq_group(cs->gic, cs, cs->hppi.irq); + } + + if (!cs->seenbetter && cs->hppi.prio != 0xff && + cs->hppi.irq >= start && cs->hppi.irq < start + len) { + gicv3_full_update_noirqset(s); + break; + } + } +} + +void gicv3_update(GICv3State *s, int start, int len) +{ + int i; + + gicv3_update_noirqset(s, start, len); + for (i = 0; i < s->num_cpu; i++) { + gicv3_cpuif_update(&s->cpu[i]); + } +} + +void gicv3_full_update_noirqset(GICv3State *s) +{ + /* Completely recalculate the GIC status from scratch, but + * don't update any outbound IRQ lines. + */ + int i; + + for (i = 0; i < s->num_cpu; i++) { + s->cpu[i].hppi.prio = 0xff; + } + + /* Note that we can guarantee that these functions will not + * recursively call back into gicv3_full_update(), because + * at each point the "previous best" is always outside the + * range we ask them to update. + */ + gicv3_update_noirqset(s, GIC_INTERNAL, s->num_irq - GIC_INTERNAL); + + for (i = 0; i < s->num_cpu; i++) { + gicv3_redist_update_noirqset(&s->cpu[i]); + } +} + +void gicv3_full_update(GICv3State *s) +{ + /* Completely recalculate the GIC status from scratch, including + * updating outbound IRQ lines. + */ + int i; + + gicv3_full_update_noirqset(s); + for (i = 0; i < s->num_cpu; i++) { + gicv3_cpuif_update(&s->cpu[i]); + } +} + +/* Process a change in an external IRQ input. */ +static void gicv3_set_irq(void *opaque, int irq, int level) +{ + /* Meaning of the 'irq' parameter: + * [0..N-1] : external interrupts + * [N..N+31] : PPI (internal) interrupts for CPU 0 + * [N+32..N+63] : PPI (internal interrupts for CPU 1 + * ... + */ + GICv3State *s = opaque; + + if (irq < (s->num_irq - GIC_INTERNAL)) { + /* external interrupt (SPI) */ + gicv3_dist_set_irq(s, irq + GIC_INTERNAL, level); + } else { + /* per-cpu interrupt (PPI) */ + int cpu; + + irq -= (s->num_irq - GIC_INTERNAL); + cpu = irq / GIC_INTERNAL; + irq %= GIC_INTERNAL; + assert(cpu < s->num_cpu); + /* Raising SGIs via this function would be a bug in how the board + * model wires up interrupts. + */ + assert(irq >= GIC_NR_SGIS); + gicv3_redist_set_irq(&s->cpu[cpu], irq, level); + } +} + +static void arm_gicv3_post_load(GICv3State *s) +{ + /* Recalculate our cached idea of the current highest priority + * pending interrupt, but don't set IRQ or FIQ lines. + */ + gicv3_full_update_noirqset(s); + /* Repopulate the cache of GICv3CPUState pointers for target CPUs */ + gicv3_cache_all_target_cpustates(s); +} + +static const MemoryRegionOps gic_ops[] = { + { + .read_with_attrs = gicv3_dist_read, + .write_with_attrs = gicv3_dist_write, + .endianness = DEVICE_NATIVE_ENDIAN, + }, + { + .read_with_attrs = gicv3_redist_read, + .write_with_attrs = gicv3_redist_write, + .endianness = DEVICE_NATIVE_ENDIAN, + } +}; + +static void arm_gic_realize(DeviceState *dev, Error **errp) +{ + /* Device instance realize function for the GIC sysbus device */ + GICv3State *s = ARM_GICV3(dev); + ARMGICv3Class *agc = ARM_GICV3_GET_CLASS(s); + Error *local_err = NULL; + + agc->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + gicv3_init_irqs_and_mmio(s, gicv3_set_irq, gic_ops); + + gicv3_init_cpuif(s); +} + +static void arm_gicv3_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ARMGICv3CommonClass *agcc = ARM_GICV3_COMMON_CLASS(klass); + ARMGICv3Class *agc = ARM_GICV3_CLASS(klass); + + agcc->post_load = arm_gicv3_post_load; + agc->parent_realize = dc->realize; + dc->realize = arm_gic_realize; +} + +static const TypeInfo arm_gicv3_info = { + .name = TYPE_ARM_GICV3, + .parent = TYPE_ARM_GICV3_COMMON, + .instance_size = sizeof(GICv3State), + .class_init = arm_gicv3_class_init, + .class_size = sizeof(ARMGICv3Class), +}; + +static void arm_gicv3_register_types(void) +{ + type_register_static(&arm_gicv3_info); +} + +type_init(arm_gicv3_register_types) diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index b9d3824f2..0f8c4b86e 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -3,8 +3,9 @@ * * Copyright (c) 2012 Linaro Limited * Copyright (c) 2015 Huawei. + * Copyright (c) 2015 Samsung Electronics Co., Ltd. * Written by Peter Maydell - * Extended to 64 cores by Shlomo Pongratz + * Reworked for GICv3 by Shlomo Pongratz and Pavel Fedin * * 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 @@ -22,7 +23,10 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "qom/cpu.h" #include "hw/intc/arm_gicv3_common.h" +#include "gicv3_internal.h" +#include "hw/arm/linux-boot-if.h" static void gicv3_pre_save(void *opaque) { @@ -45,11 +49,59 @@ static int gicv3_post_load(void *opaque, int version_id) return 0; } +static const VMStateDescription vmstate_gicv3_cpu = { + .name = "arm_gicv3_cpu", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(level, GICv3CPUState), + VMSTATE_UINT32(gicr_ctlr, GICv3CPUState), + VMSTATE_UINT32_ARRAY(gicr_statusr, GICv3CPUState, 2), + VMSTATE_UINT32(gicr_waker, GICv3CPUState), + VMSTATE_UINT64(gicr_propbaser, GICv3CPUState), + VMSTATE_UINT64(gicr_pendbaser, GICv3CPUState), + VMSTATE_UINT32(gicr_igroupr0, GICv3CPUState), + VMSTATE_UINT32(gicr_ienabler0, GICv3CPUState), + VMSTATE_UINT32(gicr_ipendr0, GICv3CPUState), + VMSTATE_UINT32(gicr_iactiver0, GICv3CPUState), + VMSTATE_UINT32(edge_trigger, GICv3CPUState), + VMSTATE_UINT32(gicr_igrpmodr0, GICv3CPUState), + VMSTATE_UINT32(gicr_nsacr, GICv3CPUState), + VMSTATE_UINT8_ARRAY(gicr_ipriorityr, GICv3CPUState, GIC_INTERNAL), + VMSTATE_UINT64_ARRAY(icc_ctlr_el1, GICv3CPUState, 2), + VMSTATE_UINT64(icc_pmr_el1, GICv3CPUState), + VMSTATE_UINT64_ARRAY(icc_bpr, GICv3CPUState, 3), + VMSTATE_UINT64_2DARRAY(icc_apr, GICv3CPUState, 3, 4), + VMSTATE_UINT64_ARRAY(icc_igrpen, GICv3CPUState, 3), + VMSTATE_UINT64(icc_ctlr_el3, GICv3CPUState), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_gicv3 = { .name = "arm_gicv3", - .unmigratable = 1, + .version_id = 1, + .minimum_version_id = 1, .pre_save = gicv3_pre_save, .post_load = gicv3_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32(gicd_ctlr, GICv3State), + VMSTATE_UINT32_ARRAY(gicd_statusr, GICv3State, 2), + VMSTATE_UINT32_ARRAY(group, GICv3State, GICV3_BMP_SIZE), + VMSTATE_UINT32_ARRAY(grpmod, GICv3State, GICV3_BMP_SIZE), + VMSTATE_UINT32_ARRAY(enabled, GICv3State, GICV3_BMP_SIZE), + VMSTATE_UINT32_ARRAY(pending, GICv3State, GICV3_BMP_SIZE), + VMSTATE_UINT32_ARRAY(active, GICv3State, GICV3_BMP_SIZE), + VMSTATE_UINT32_ARRAY(level, GICv3State, GICV3_BMP_SIZE), + VMSTATE_UINT32_ARRAY(edge_trigger, GICv3State, GICV3_BMP_SIZE), + VMSTATE_UINT8_ARRAY(gicd_ipriority, GICv3State, GICV3_MAXIRQ), + VMSTATE_UINT64_ARRAY(gicd_irouter, GICv3State, GICV3_MAXIRQ), + VMSTATE_UINT32_ARRAY(gicd_nsacr, GICv3State, + DIV_ROUND_UP(GICV3_MAXIRQ, 16)), + VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, GICv3State, num_cpu, + vmstate_gicv3_cpu, GICv3CPUState), + VMSTATE_END_OF_LIST() + } }; void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler, @@ -68,14 +120,11 @@ void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler, i = s->num_irq - GIC_INTERNAL + GIC_INTERNAL * s->num_cpu; qdev_init_gpio_in(DEVICE(s), handler, i); - s->parent_irq = g_malloc(s->num_cpu * sizeof(qemu_irq)); - s->parent_fiq = g_malloc(s->num_cpu * sizeof(qemu_irq)); - for (i = 0; i < s->num_cpu; i++) { - sysbus_init_irq(sbd, &s->parent_irq[i]); + sysbus_init_irq(sbd, &s->cpu[i].parent_irq); } for (i = 0; i < s->num_cpu; i++) { - sysbus_init_irq(sbd, &s->parent_fiq[i]); + sysbus_init_irq(sbd, &s->cpu[i].parent_fiq); } memory_region_init_io(&s->iomem_dist, OBJECT(s), ops, s, @@ -90,6 +139,7 @@ void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler, static void arm_gicv3_common_realize(DeviceState *dev, Error **errp) { GICv3State *s = ARM_GICV3_COMMON(dev); + int i; /* revision property is actually reserved and currently used only in order * to keep the interface compatible with GICv2 code, avoiding extra @@ -100,11 +150,164 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp) error_setg(errp, "unsupported GIC revision %d", s->revision); return; } + + if (s->num_irq > GICV3_MAXIRQ) { + error_setg(errp, + "requested %u interrupt lines exceeds GIC maximum %d", + s->num_irq, GICV3_MAXIRQ); + return; + } + if (s->num_irq < GIC_INTERNAL) { + error_setg(errp, + "requested %u interrupt lines is below GIC minimum %d", + s->num_irq, GIC_INTERNAL); + return; + } + + /* ITLinesNumber is represented as (N / 32) - 1, so this is an + * implementation imposed restriction, not an architectural one, + * so we don't have to deal with bitfields where only some of the + * bits in a 32-bit word should be valid. + */ + if (s->num_irq % 32) { + error_setg(errp, + "%d interrupt lines unsupported: not divisible by 32", + s->num_irq); + return; + } + + s->cpu = g_new0(GICv3CPUState, s->num_cpu); + + for (i = 0; i < s->num_cpu; i++) { + CPUState *cpu = qemu_get_cpu(i); + uint64_t cpu_affid; + int last; + + s->cpu[i].cpu = cpu; + s->cpu[i].gic = s; + + /* Pre-construct the GICR_TYPER: + * For our implementation: + * Top 32 bits are the affinity value of the associated CPU + * CommonLPIAff == 01 (redistributors with same Aff3 share LPI table) + * Processor_Number == CPU index starting from 0 + * DPGS == 0 (GICR_CTLR.DPG* not supported) + * Last == 1 if this is the last redistributor in a series of + * contiguous redistributor pages + * DirectLPI == 0 (direct injection of LPIs not supported) + * VLPIS == 0 (virtual LPIs not supported) + * PLPIS == 0 (physical LPIs not supported) + */ + cpu_affid = object_property_get_int(OBJECT(cpu), "mp-affinity", NULL); + last = (i == s->num_cpu - 1); + + /* The CPU mp-affinity property is in MPIDR register format; squash + * the affinity bytes into 32 bits as the GICR_TYPER has them. + */ + cpu_affid = (cpu_affid & 0xFF00000000ULL >> 8) | (cpu_affid & 0xFFFFFF); + s->cpu[i].gicr_typer = (cpu_affid << 32) | + (1 << 24) | + (i << 8) | + (last << 4); + } } static void arm_gicv3_common_reset(DeviceState *dev) { - /* TODO */ + GICv3State *s = ARM_GICV3_COMMON(dev); + int i; + + for (i = 0; i < s->num_cpu; i++) { + GICv3CPUState *cs = &s->cpu[i]; + + cs->level = 0; + cs->gicr_ctlr = 0; + cs->gicr_statusr[GICV3_S] = 0; + cs->gicr_statusr[GICV3_NS] = 0; + cs->gicr_waker = GICR_WAKER_ProcessorSleep | GICR_WAKER_ChildrenAsleep; + cs->gicr_propbaser = 0; + cs->gicr_pendbaser = 0; + /* If we're resetting a TZ-aware GIC as if secure firmware + * had set it up ready to start a kernel in non-secure, we + * need to set interrupts to group 1 so the kernel can use them. + * Otherwise they reset to group 0 like the hardware. + */ + if (s->irq_reset_nonsecure) { + cs->gicr_igroupr0 = 0xffffffff; + } else { + cs->gicr_igroupr0 = 0; + } + + cs->gicr_ienabler0 = 0; + cs->gicr_ipendr0 = 0; + cs->gicr_iactiver0 = 0; + cs->edge_trigger = 0xffff; + cs->gicr_igrpmodr0 = 0; + cs->gicr_nsacr = 0; + memset(cs->gicr_ipriorityr, 0, sizeof(cs->gicr_ipriorityr)); + + cs->hppi.prio = 0xff; + + /* State in the CPU interface must *not* be reset here, because it + * is part of the CPU's reset domain, not the GIC device's. + */ + } + + /* For our implementation affinity routing is always enabled */ + if (s->security_extn) { + s->gicd_ctlr = GICD_CTLR_ARE_S | GICD_CTLR_ARE_NS; + } else { + s->gicd_ctlr = GICD_CTLR_DS | GICD_CTLR_ARE; + } + + s->gicd_statusr[GICV3_S] = 0; + s->gicd_statusr[GICV3_NS] = 0; + + memset(s->group, 0, sizeof(s->group)); + memset(s->grpmod, 0, sizeof(s->grpmod)); + memset(s->enabled, 0, sizeof(s->enabled)); + memset(s->pending, 0, sizeof(s->pending)); + memset(s->active, 0, sizeof(s->active)); + memset(s->level, 0, sizeof(s->level)); + memset(s->edge_trigger, 0, sizeof(s->edge_trigger)); + memset(s->gicd_ipriority, 0, sizeof(s->gicd_ipriority)); + memset(s->gicd_irouter, 0, sizeof(s->gicd_irouter)); + memset(s->gicd_nsacr, 0, sizeof(s->gicd_nsacr)); + /* GICD_IROUTER are UNKNOWN at reset so in theory the guest must + * write these to get sane behaviour and we need not populate the + * pointer cache here; however having the cache be different for + * "happened to be 0 from reset" and "guest wrote 0" would be + * too confusing. + */ + gicv3_cache_all_target_cpustates(s); + + if (s->irq_reset_nonsecure) { + /* If we're resetting a TZ-aware GIC as if secure firmware + * had set it up ready to start a kernel in non-secure, we + * need to set interrupts to group 1 so the kernel can use them. + * Otherwise they reset to group 0 like the hardware. + */ + for (i = GIC_INTERNAL; i < s->num_irq; i++) { + gicv3_gicd_group_set(s, i); + } + } +} + +static void arm_gic_common_linux_init(ARMLinuxBootIf *obj, + bool secure_boot) +{ + GICv3State *s = ARM_GICV3_COMMON(obj); + + if (s->security_extn && !secure_boot) { + /* We're directly booting a kernel into NonSecure. If this GIC + * implements the security extensions then we must configure it + * to have all the interrupts be NonSecure (this is a job that + * is done by the Secure boot firmware in real hardware, and in + * this mode QEMU is acting as a minimalist firmware-and-bootloader + * equivalent). + */ + s->irq_reset_nonsecure = true; + } } static Property arm_gicv3_common_properties[] = { @@ -118,11 +321,13 @@ static Property arm_gicv3_common_properties[] = { static void arm_gicv3_common_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ARMLinuxBootIfClass *albifc = ARM_LINUX_BOOT_IF_CLASS(klass); dc->reset = arm_gicv3_common_reset; dc->realize = arm_gicv3_common_realize; dc->props = arm_gicv3_common_properties; dc->vmsd = &vmstate_gicv3; + albifc->arm_linux_init = arm_gic_common_linux_init; } static const TypeInfo arm_gicv3_common_type = { @@ -132,6 +337,10 @@ static const TypeInfo arm_gicv3_common_type = { .class_size = sizeof(ARMGICv3CommonClass), .class_init = arm_gicv3_common_class_init, .abstract = true, + .interfaces = (InterfaceInfo []) { + { TYPE_ARM_LINUX_BOOT_IF }, + { }, + }, }; static void register_types(void) diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c new file mode 100644 index 000000000..4633172be --- /dev/null +++ b/hw/intc/arm_gicv3_cpuif.c @@ -0,0 +1,1348 @@ +/* + * ARM Generic Interrupt Controller v3 + * + * Copyright (c) 2016 Linaro Limited + * Written by Peter Maydell + * + * This code is licensed under the GPL, version 2 or (at your option) + * any later version. + */ + +/* This file contains the code for the system register interface + * portions of the GICv3. + */ + +#include "qemu/osdep.h" +#include "trace.h" +#include "gicv3_internal.h" +#include "cpu.h" + +static GICv3CPUState *icc_cs_from_env(CPUARMState *env) +{ + /* Given the CPU, find the right GICv3CPUState struct. + * Since we registered the CPU interface with the EL change hook as + * the opaque pointer, we can just directly get from the CPU to it. + */ + return arm_get_el_change_hook_opaque(arm_env_get_cpu(env)); +} + +static bool gicv3_use_ns_bank(CPUARMState *env) +{ + /* Return true if we should use the NonSecure bank for a banked GIC + * CPU interface register. Note that this differs from the + * access_secure_reg() function because GICv3 banked registers are + * banked even for AArch64, unlike the other CPU system registers. + */ + return !arm_is_secure_below_el3(env); +} + +static int icc_highest_active_prio(GICv3CPUState *cs) +{ + /* Calculate the current running priority based on the set bits + * in the Active Priority Registers. + */ + int i; + + for (i = 0; i < ARRAY_SIZE(cs->icc_apr[0]); i++) { + uint32_t apr = cs->icc_apr[GICV3_G0][i] | + cs->icc_apr[GICV3_G1][i] | cs->icc_apr[GICV3_G1NS][i]; + + if (!apr) { + continue; + } + return (i * 32 + ctz32(apr)) << (GIC_MIN_BPR + 1); + } + /* No current active interrupts: return idle priority */ + return 0xff; +} + +static uint32_t icc_gprio_mask(GICv3CPUState *cs, int group) +{ + /* Return a mask word which clears the subpriority bits from + * a priority value for an interrupt in the specified group. + * This depends on the BPR value: + * a BPR of 0 means the group priority bits are [7:1]; + * a BPR of 1 means they are [7:2], and so on down to + * a BPR of 7 meaning no group priority bits at all. + * Which BPR to use depends on the group of the interrupt and + * the current ICC_CTLR.CBPR settings. + */ + if ((group == GICV3_G1 && cs->icc_ctlr_el1[GICV3_S] & ICC_CTLR_EL1_CBPR) || + (group == GICV3_G1NS && + cs->icc_ctlr_el1[GICV3_NS] & ICC_CTLR_EL1_CBPR)) { + group = GICV3_G0; + } + + return ~0U << ((cs->icc_bpr[group] & 7) + 1); +} + +static bool icc_no_enabled_hppi(GICv3CPUState *cs) +{ + /* Return true if there is no pending interrupt, or the + * highest priority pending interrupt is in a group which has been + * disabled at the CPU interface by the ICC_IGRPEN* register enable bits. + */ + return cs->hppi.prio == 0xff || (cs->icc_igrpen[cs->hppi.grp] == 0); +} + +static bool icc_hppi_can_preempt(GICv3CPUState *cs) +{ + /* Return true if we have a pending interrupt of sufficient + * priority to preempt. + */ + int rprio; + uint32_t mask; + + if (icc_no_enabled_hppi(cs)) { + return false; + } + + if (cs->hppi.prio >= cs->icc_pmr_el1) { + /* Priority mask masks this interrupt */ + return false; + } + + rprio = icc_highest_active_prio(cs); + if (rprio == 0xff) { + /* No currently running interrupt so we can preempt */ + return true; + } + + mask = icc_gprio_mask(cs, cs->hppi.grp); + + /* We only preempt a running interrupt if the pending interrupt's + * group priority is sufficient (the subpriorities are not considered). + */ + if ((cs->hppi.prio & mask) < (rprio & mask)) { + return true; + } + + return false; +} + +void gicv3_cpuif_update(GICv3CPUState *cs) +{ + /* Tell the CPU about its highest priority pending interrupt */ + int irqlevel = 0; + int fiqlevel = 0; + ARMCPU *cpu = ARM_CPU(cs->cpu); + CPUARMState *env = &cpu->env; + + trace_gicv3_cpuif_update(gicv3_redist_affid(cs), cs->hppi.irq, + cs->hppi.grp, cs->hppi.prio); + + if (cs->hppi.grp == GICV3_G1 && !arm_feature(env, ARM_FEATURE_EL3)) { + /* If a Security-enabled GIC sends a G1S interrupt to a + * Security-disabled CPU, we must treat it as if it were G0. + */ + cs->hppi.grp = GICV3_G0; + } + + if (icc_hppi_can_preempt(cs)) { + /* We have an interrupt: should we signal it as IRQ or FIQ? + * This is described in the GICv3 spec section 4.6.2. + */ + bool isfiq; + + switch (cs->hppi.grp) { + case GICV3_G0: + isfiq = true; + break; + case GICV3_G1: + isfiq = (!arm_is_secure(env) || + (arm_current_el(env) == 3 && arm_el_is_aa64(env, 3))); + break; + case GICV3_G1NS: + isfiq = arm_is_secure(env); + break; + default: + g_assert_not_reached(); + } + + if (isfiq) { + fiqlevel = 1; + } else { + irqlevel = 1; + } + } + + trace_gicv3_cpuif_set_irqs(gicv3_redist_affid(cs), fiqlevel, irqlevel); + + qemu_set_irq(cs->parent_fiq, fiqlevel); + qemu_set_irq(cs->parent_irq, irqlevel); +} + +static uint64_t icc_pmr_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + uint32_t value = cs->icc_pmr_el1; + + if (arm_feature(env, ARM_FEATURE_EL3) && !arm_is_secure(env) && + (env->cp15.scr_el3 & SCR_FIQ)) { + /* NS access and Group 0 is inaccessible to NS: return the + * NS view of the current priority + */ + if (value & 0x80) { + /* Secure priorities not visible to NS */ + value = 0; + } else if (value != 0xff) { + value = (value << 1) & 0xff; + } + } + + trace_gicv3_icc_pmr_read(gicv3_redist_affid(cs), value); + + return value; +} + +static void icc_pmr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + + trace_gicv3_icc_pmr_write(gicv3_redist_affid(cs), value); + + value &= 0xff; + + if (arm_feature(env, ARM_FEATURE_EL3) && !arm_is_secure(env) && + (env->cp15.scr_el3 & SCR_FIQ)) { + /* NS access and Group 0 is inaccessible to NS: return the + * NS view of the current priority + */ + if (!(cs->icc_pmr_el1 & 0x80)) { + /* Current PMR in the secure range, don't allow NS to change it */ + return; + } + value = (value >> 1) & 0x80; + } + cs->icc_pmr_el1 = value; + gicv3_cpuif_update(cs); +} + +static void icc_activate_irq(GICv3CPUState *cs, int irq) +{ + /* Move the interrupt from the Pending state to Active, and update + * the Active Priority Registers + */ + uint32_t mask = icc_gprio_mask(cs, cs->hppi.grp); + int prio = cs->hppi.prio & mask; + int aprbit = prio >> 1; + int regno = aprbit / 32; + int regbit = aprbit % 32; + + cs->icc_apr[cs->hppi.grp][regno] |= (1 << regbit); + + if (irq < GIC_INTERNAL) { + cs->gicr_iactiver0 = deposit32(cs->gicr_iactiver0, irq, 1, 1); + cs->gicr_ipendr0 = deposit32(cs->gicr_ipendr0, irq, 1, 0); + gicv3_redist_update(cs); + } else { + gicv3_gicd_active_set(cs->gic, irq); + gicv3_gicd_pending_clear(cs->gic, irq); + gicv3_update(cs->gic, irq, 1); + } +} + +static uint64_t icc_hppir0_value(GICv3CPUState *cs, CPUARMState *env) +{ + /* Return the highest priority pending interrupt register value + * for group 0. + */ + bool irq_is_secure; + + if (cs->hppi.prio == 0xff) { + return INTID_SPURIOUS; + } + + /* Check whether we can return the interrupt or if we should return + * a special identifier, as per the CheckGroup0ForSpecialIdentifiers + * pseudocode. (We can simplify a little because for us ICC_SRE_EL1.RM + * is always zero.) + */ + irq_is_secure = (!(cs->gic->gicd_ctlr & GICD_CTLR_DS) && + (cs->hppi.grp != GICV3_G1NS)); + + if (cs->hppi.grp != GICV3_G0 && !arm_is_el3_or_mon(env)) { + return INTID_SPURIOUS; + } + if (irq_is_secure && !arm_is_secure(env)) { + /* Secure interrupts not visible to Nonsecure */ + return INTID_SPURIOUS; + } + + if (cs->hppi.grp != GICV3_G0) { + /* Indicate to EL3 that there's a Group 1 interrupt for the other + * state pending. + */ + return irq_is_secure ? INTID_SECURE : INTID_NONSECURE; + } + + return cs->hppi.irq; +} + +static uint64_t icc_hppir1_value(GICv3CPUState *cs, CPUARMState *env) +{ + /* Return the highest priority pending interrupt register value + * for group 1. + */ + bool irq_is_secure; + + if (cs->hppi.prio == 0xff) { + return INTID_SPURIOUS; + } + + /* Check whether we can return the interrupt or if we should return + * a special identifier, as per the CheckGroup1ForSpecialIdentifiers + * pseudocode. (We can simplify a little because for us ICC_SRE_EL1.RM + * is always zero.) + */ + irq_is_secure = (!(cs->gic->gicd_ctlr & GICD_CTLR_DS) && + (cs->hppi.grp != GICV3_G1NS)); + + if (cs->hppi.grp == GICV3_G0) { + /* Group 0 interrupts not visible via HPPIR1 */ + return INTID_SPURIOUS; + } + if (irq_is_secure) { + if (!arm_is_secure(env)) { + /* Secure interrupts not visible in Non-secure */ + return INTID_SPURIOUS; + } + } else if (!arm_is_el3_or_mon(env) && arm_is_secure(env)) { + /* Group 1 non-secure interrupts not visible in Secure EL1 */ + return INTID_SPURIOUS; + } + + return cs->hppi.irq; +} + +static uint64_t icc_iar0_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + uint64_t intid; + + if (!icc_hppi_can_preempt(cs)) { + intid = INTID_SPURIOUS; + } else { + intid = icc_hppir0_value(cs, env); + } + + if (!(intid >= INTID_SECURE && intid <= INTID_SPURIOUS)) { + icc_activate_irq(cs, intid); + } + + trace_gicv3_icc_iar0_read(gicv3_redist_affid(cs), intid); + return intid; +} + +static uint64_t icc_iar1_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + uint64_t intid; + + if (!icc_hppi_can_preempt(cs)) { + intid = INTID_SPURIOUS; + } else { + intid = icc_hppir1_value(cs, env); + } + + if (!(intid >= INTID_SECURE && intid <= INTID_SPURIOUS)) { + icc_activate_irq(cs, intid); + } + + trace_gicv3_icc_iar1_read(gicv3_redist_affid(cs), intid); + return intid; +} + +static void icc_drop_prio(GICv3CPUState *cs, int grp) +{ + /* Drop the priority of the currently active interrupt in + * the specified group. + * + * Note that we can guarantee (because of the requirement to nest + * ICC_IAR reads [which activate an interrupt and raise priority] + * with ICC_EOIR writes [which drop the priority for the interrupt]) + * that the interrupt we're being called for is the highest priority + * active interrupt, meaning that it has the lowest set bit in the + * APR registers. + * + * If the guest does not honour the ordering constraints then the + * behaviour of the GIC is UNPREDICTABLE, which for us means that + * the values of the APR registers might become incorrect and the + * running priority will be wrong, so interrupts that should preempt + * might not do so, and interrupts that should not preempt might do so. + */ + int i; + + for (i = 0; i < ARRAY_SIZE(cs->icc_apr[grp]); i++) { + uint64_t *papr = &cs->icc_apr[grp][i]; + + if (!*papr) { + continue; + } + /* Clear the lowest set bit */ + *papr &= *papr - 1; + break; + } + + /* running priority change means we need an update for this cpu i/f */ + gicv3_cpuif_update(cs); +} + +static bool icc_eoi_split(CPUARMState *env, GICv3CPUState *cs) +{ + /* Return true if we should split priority drop and interrupt + * deactivation, ie whether the relevant EOIMode bit is set. + */ + if (arm_is_el3_or_mon(env)) { + return cs->icc_ctlr_el3 & ICC_CTLR_EL3_EOIMODE_EL3; + } + if (arm_is_secure_below_el3(env)) { + return cs->icc_ctlr_el1[GICV3_S] & ICC_CTLR_EL1_EOIMODE; + } else { + return cs->icc_ctlr_el1[GICV3_NS] & ICC_CTLR_EL1_EOIMODE; + } +} + +static int icc_highest_active_group(GICv3CPUState *cs) +{ + /* Return the group with the highest priority active interrupt. + * We can do this by just comparing the APRs to see which one + * has the lowest set bit. + * (If more than one group is active at the same priority then + * we're in UNPREDICTABLE territory.) + */ + int i; + + for (i = 0; i < ARRAY_SIZE(cs->icc_apr[0]); i++) { + int g0ctz = ctz32(cs->icc_apr[GICV3_G0][i]); + int g1ctz = ctz32(cs->icc_apr[GICV3_G1][i]); + int g1nsctz = ctz32(cs->icc_apr[GICV3_G1NS][i]); + + if (g1nsctz < g0ctz && g1nsctz < g1ctz) { + return GICV3_G1NS; + } + if (g1ctz < g0ctz) { + return GICV3_G1; + } + if (g0ctz < 32) { + return GICV3_G0; + } + } + /* No set active bits? UNPREDICTABLE; return -1 so the caller + * ignores the spurious EOI attempt. + */ + return -1; +} + +static void icc_deactivate_irq(GICv3CPUState *cs, int irq) +{ + if (irq < GIC_INTERNAL) { + cs->gicr_iactiver0 = deposit32(cs->gicr_iactiver0, irq, 1, 0); + gicv3_redist_update(cs); + } else { + gicv3_gicd_active_clear(cs->gic, irq); + gicv3_update(cs->gic, irq, 1); + } +} + +static void icc_eoir_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* End of Interrupt */ + GICv3CPUState *cs = icc_cs_from_env(env); + int irq = value & 0xffffff; + int grp; + + trace_gicv3_icc_eoir_write(gicv3_redist_affid(cs), value); + + if (ri->crm == 8) { + /* EOIR0 */ + grp = GICV3_G0; + } else { + /* EOIR1 */ + if (arm_is_secure(env)) { + grp = GICV3_G1; + } else { + grp = GICV3_G1NS; + } + } + + if (irq >= cs->gic->num_irq) { + /* This handles two cases: + * 1. If software writes the ID of a spurious interrupt [ie 1020-1023] + * to the GICC_EOIR, the GIC ignores that write. + * 2. If software writes the number of a non-existent interrupt + * this must be a subcase of "value written does not match the last + * valid interrupt value read from the Interrupt Acknowledge + * register" and so this is UNPREDICTABLE. We choose to ignore it. + */ + return; + } + + if (icc_highest_active_group(cs) != grp) { + return; + } + + icc_drop_prio(cs, grp); + + if (!icc_eoi_split(env, cs)) { + /* Priority drop and deactivate not split: deactivate irq now */ + icc_deactivate_irq(cs, irq); + } +} + +static uint64_t icc_hppir0_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + uint64_t value = icc_hppir0_value(cs, env); + + trace_gicv3_icc_hppir0_read(gicv3_redist_affid(cs), value); + return value; +} + +static uint64_t icc_hppir1_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + uint64_t value = icc_hppir1_value(cs, env); + + trace_gicv3_icc_hppir1_read(gicv3_redist_affid(cs), value); + return value; +} + +static uint64_t icc_bpr_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + int grp = (ri->crm == 8) ? GICV3_G0 : GICV3_G1; + bool satinc = false; + uint64_t bpr; + + if (grp == GICV3_G1 && gicv3_use_ns_bank(env)) { + grp = GICV3_G1NS; + } + + if (grp == GICV3_G1 && !arm_is_el3_or_mon(env) && + (cs->icc_ctlr_el1[GICV3_S] & ICC_CTLR_EL1_CBPR)) { + /* CBPR_EL1S means secure EL1 or AArch32 EL3 !Mon BPR1 accesses + * modify BPR0 + */ + grp = GICV3_G0; + } + + if (grp == GICV3_G1NS && arm_current_el(env) < 3 && + (cs->icc_ctlr_el1[GICV3_NS] & ICC_CTLR_EL1_CBPR)) { + /* reads return bpr0 + 1 sat to 7, writes ignored */ + grp = GICV3_G0; + satinc = true; + } + + bpr = cs->icc_bpr[grp]; + if (satinc) { + bpr++; + bpr = MIN(bpr, 7); + } + + trace_gicv3_icc_bpr_read(gicv3_redist_affid(cs), bpr); + + return bpr; +} + +static void icc_bpr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + int grp = (ri->crm == 8) ? GICV3_G0 : GICV3_G1; + + trace_gicv3_icc_pmr_write(gicv3_redist_affid(cs), value); + + if (grp == GICV3_G1 && gicv3_use_ns_bank(env)) { + grp = GICV3_G1NS; + } + + if (grp == GICV3_G1 && !arm_is_el3_or_mon(env) && + (cs->icc_ctlr_el1[GICV3_S] & ICC_CTLR_EL1_CBPR)) { + /* CBPR_EL1S means secure EL1 or AArch32 EL3 !Mon BPR1 accesses + * modify BPR0 + */ + grp = GICV3_G0; + } + + if (grp == GICV3_G1NS && arm_current_el(env) < 3 && + (cs->icc_ctlr_el1[GICV3_NS] & ICC_CTLR_EL1_CBPR)) { + /* reads return bpr0 + 1 sat to 7, writes ignored */ + return; + } + + cs->icc_bpr[grp] = value & 7; + gicv3_cpuif_update(cs); +} + +static uint64_t icc_ap_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + uint64_t value; + + int regno = ri->opc2 & 3; + int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1; + + if (grp == GICV3_G1 && gicv3_use_ns_bank(env)) { + grp = GICV3_G1NS; + } + + value = cs->icc_apr[grp][regno]; + + trace_gicv3_icc_ap_read(regno, gicv3_redist_affid(cs), value); + return value; +} + +static void icc_ap_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + + int regno = ri->opc2 & 3; + int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1; + + trace_gicv3_icc_ap_write(regno, gicv3_redist_affid(cs), value); + + if (grp == GICV3_G1 && gicv3_use_ns_bank(env)) { + grp = GICV3_G1NS; + } + + /* It's not possible to claim that a Non-secure interrupt is active + * at a priority outside the Non-secure range (128..255), since this + * would otherwise allow malicious NS code to block delivery of S interrupts + * by writing a bad value to these registers. + */ + if (grp == GICV3_G1NS && regno < 2 && arm_feature(env, ARM_FEATURE_EL3)) { + return; + } + + cs->icc_apr[grp][regno] = value & 0xFFFFFFFFU; + gicv3_cpuif_update(cs); +} + +static void icc_dir_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Deactivate interrupt */ + GICv3CPUState *cs = icc_cs_from_env(env); + int irq = value & 0xffffff; + bool irq_is_secure, single_sec_state, irq_is_grp0; + bool route_fiq_to_el3, route_irq_to_el3, route_fiq_to_el2, route_irq_to_el2; + + trace_gicv3_icc_dir_write(gicv3_redist_affid(cs), value); + + if (irq >= cs->gic->num_irq) { + /* Also catches special interrupt numbers and LPIs */ + return; + } + + if (!icc_eoi_split(env, cs)) { + return; + } + + int grp = gicv3_irq_group(cs->gic, cs, irq); + + single_sec_state = cs->gic->gicd_ctlr & GICD_CTLR_DS; + irq_is_secure = !single_sec_state && (grp != GICV3_G1NS); + irq_is_grp0 = grp == GICV3_G0; + + /* Check whether we're allowed to deactivate this interrupt based + * on its group and the current CPU state. + * These checks are laid out to correspond to the spec's pseudocode. + */ + route_fiq_to_el3 = env->cp15.scr_el3 & SCR_FIQ; + route_irq_to_el3 = env->cp15.scr_el3 & SCR_IRQ; + /* No need to include !IsSecure in route_*_to_el2 as it's only + * tested in cases where we know !IsSecure is true. + */ + route_fiq_to_el2 = env->cp15.hcr_el2 & HCR_FMO; + route_irq_to_el2 = env->cp15.hcr_el2 & HCR_FMO; + + switch (arm_current_el(env)) { + case 3: + break; + case 2: + if (single_sec_state && irq_is_grp0 && !route_fiq_to_el3) { + break; + } + if (!irq_is_secure && !irq_is_grp0 && !route_irq_to_el3) { + break; + } + return; + case 1: + if (!arm_is_secure_below_el3(env)) { + if (single_sec_state && irq_is_grp0 && + !route_fiq_to_el3 && !route_fiq_to_el2) { + break; + } + if (!irq_is_secure && !irq_is_grp0 && + !route_irq_to_el3 && !route_irq_to_el2) { + break; + } + } else { + if (irq_is_grp0 && !route_fiq_to_el3) { + break; + } + if (!irq_is_grp0 && + (!irq_is_secure || !single_sec_state) && + !route_irq_to_el3) { + break; + } + } + return; + default: + g_assert_not_reached(); + } + + icc_deactivate_irq(cs, irq); +} + +static uint64_t icc_rpr_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + int prio = icc_highest_active_prio(cs); + + if (arm_feature(env, ARM_FEATURE_EL3) && + !arm_is_secure(env) && (env->cp15.scr_el3 & SCR_FIQ)) { + /* NS GIC access and Group 0 is inaccessible to NS */ + if (prio & 0x80) { + /* NS mustn't see priorities in the Secure half of the range */ + prio = 0; + } else if (prio != 0xff) { + /* Non-idle priority: show the Non-secure view of it */ + prio = (prio << 1) & 0xff; + } + } + + trace_gicv3_icc_rpr_read(gicv3_redist_affid(cs), prio); + return prio; +} + +static void icc_generate_sgi(CPUARMState *env, GICv3CPUState *cs, + uint64_t value, int grp, bool ns) +{ + GICv3State *s = cs->gic; + + /* Extract Aff3/Aff2/Aff1 and shift into the bottom 24 bits */ + uint64_t aff = extract64(value, 48, 8) << 16 | + extract64(value, 32, 8) << 8 | + extract64(value, 16, 8); + uint32_t targetlist = extract64(value, 0, 16); + uint32_t irq = extract64(value, 24, 4); + bool irm = extract64(value, 40, 1); + int i; + + if (grp == GICV3_G1 && s->gicd_ctlr & GICD_CTLR_DS) { + /* If GICD_CTLR.DS == 1, the Distributor treats Secure Group 1 + * interrupts as Group 0 interrupts and must send Secure Group 0 + * interrupts to the target CPUs. + */ + grp = GICV3_G0; + } + + trace_gicv3_icc_generate_sgi(gicv3_redist_affid(cs), irq, irm, + aff, targetlist); + + for (i = 0; i < s->num_cpu; i++) { + GICv3CPUState *ocs = &s->cpu[i]; + + if (irm) { + /* IRM == 1 : route to all CPUs except self */ + if (cs == ocs) { + continue; + } + } else { + /* IRM == 0 : route to Aff3.Aff2.Aff1.n for all n in [0..15] + * where the corresponding bit is set in targetlist + */ + int aff0; + + if (ocs->gicr_typer >> 40 != aff) { + continue; + } + aff0 = extract64(ocs->gicr_typer, 32, 8); + if (aff0 > 15 || extract32(targetlist, aff0, 1) == 0) { + continue; + } + } + + /* The redistributor will check against its own GICR_NSACR as needed */ + gicv3_redist_send_sgi(ocs, grp, irq, ns); + } +} + +static void icc_sgi0r_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Generate Secure Group 0 SGI. */ + GICv3CPUState *cs = icc_cs_from_env(env); + bool ns = !arm_is_secure(env); + + icc_generate_sgi(env, cs, value, GICV3_G0, ns); +} + +static void icc_sgi1r_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Generate Group 1 SGI for the current Security state */ + GICv3CPUState *cs = icc_cs_from_env(env); + int grp; + bool ns = !arm_is_secure(env); + + grp = ns ? GICV3_G1NS : GICV3_G1; + icc_generate_sgi(env, cs, value, grp, ns); +} + +static void icc_asgi1r_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Generate Group 1 SGI for the Security state that is not + * the current state + */ + GICv3CPUState *cs = icc_cs_from_env(env); + int grp; + bool ns = !arm_is_secure(env); + + grp = ns ? GICV3_G1 : GICV3_G1NS; + icc_generate_sgi(env, cs, value, grp, ns); +} + +static uint64_t icc_igrpen_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + int grp = ri->opc2 & 1 ? GICV3_G1 : GICV3_G0; + uint64_t value; + + if (grp == GICV3_G1 && gicv3_use_ns_bank(env)) { + grp = GICV3_G1NS; + } + + value = cs->icc_igrpen[grp]; + trace_gicv3_icc_igrpen_read(gicv3_redist_affid(cs), value); + return value; +} + +static void icc_igrpen_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + int grp = ri->opc2 & 1 ? GICV3_G1 : GICV3_G0; + + trace_gicv3_icc_igrpen_write(gicv3_redist_affid(cs), value); + + if (grp == GICV3_G1 && gicv3_use_ns_bank(env)) { + grp = GICV3_G1NS; + } + + cs->icc_igrpen[grp] = value & ICC_IGRPEN_ENABLE; + gicv3_cpuif_update(cs); +} + +static uint64_t icc_igrpen1_el3_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + + /* IGRPEN1_EL3 bits 0 and 1 are r/w aliases into IGRPEN1_EL1 NS and S */ + return cs->icc_igrpen[GICV3_G1NS] | (cs->icc_igrpen[GICV3_G1] << 1); +} + +static void icc_igrpen1_el3_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + + trace_gicv3_icc_igrpen1_el3_write(gicv3_redist_affid(cs), value); + + /* IGRPEN1_EL3 bits 0 and 1 are r/w aliases into IGRPEN1_EL1 NS and S */ + cs->icc_igrpen[GICV3_G1NS] = extract32(value, 0, 1); + cs->icc_igrpen[GICV3_G1] = extract32(value, 1, 1); + gicv3_cpuif_update(cs); +} + +static uint64_t icc_ctlr_el1_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + int bank = gicv3_use_ns_bank(env) ? GICV3_NS : GICV3_S; + uint64_t value; + + value = cs->icc_ctlr_el1[bank]; + trace_gicv3_icc_ctlr_read(gicv3_redist_affid(cs), value); + return value; +} + +static void icc_ctlr_el1_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + int bank = gicv3_use_ns_bank(env) ? GICV3_NS : GICV3_S; + uint64_t mask; + + trace_gicv3_icc_ctlr_write(gicv3_redist_affid(cs), value); + + /* Only CBPR and EOIMODE can be RW; + * for us PMHE is RAZ/WI (we don't implement 1-of-N interrupts or + * the asseciated priority-based routing of them); + * if EL3 is implemented and GICD_CTLR.DS == 0, then PMHE and CBPR are RO. + */ + if (arm_feature(env, ARM_FEATURE_EL3) && + ((cs->gic->gicd_ctlr & GICD_CTLR_DS) == 0)) { + mask = ICC_CTLR_EL1_EOIMODE; + } else { + mask = ICC_CTLR_EL1_CBPR | ICC_CTLR_EL1_EOIMODE; + } + + cs->icc_ctlr_el1[bank] &= ~mask; + cs->icc_ctlr_el1[bank] |= (value & mask); + gicv3_cpuif_update(cs); +} + + +static uint64_t icc_ctlr_el3_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + uint64_t value; + + value = cs->icc_ctlr_el3; + if (cs->icc_ctlr_el1[GICV3_NS] & ICC_CTLR_EL1_EOIMODE) { + value |= ICC_CTLR_EL3_EOIMODE_EL1NS; + } + if (cs->icc_ctlr_el1[GICV3_NS] & ICC_CTLR_EL1_CBPR) { + value |= ICC_CTLR_EL3_CBPR_EL1NS; + } + if (cs->icc_ctlr_el1[GICV3_NS] & ICC_CTLR_EL1_EOIMODE) { + value |= ICC_CTLR_EL3_EOIMODE_EL1S; + } + if (cs->icc_ctlr_el1[GICV3_NS] & ICC_CTLR_EL1_CBPR) { + value |= ICC_CTLR_EL3_CBPR_EL1S; + } + + trace_gicv3_icc_ctlr_el3_read(gicv3_redist_affid(cs), value); + return value; +} + +static void icc_ctlr_el3_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + uint64_t mask; + + trace_gicv3_icc_ctlr_el3_write(gicv3_redist_affid(cs), value); + + /* *_EL1NS and *_EL1S bits are aliases into the ICC_CTLR_EL1 bits. */ + cs->icc_ctlr_el1[GICV3_NS] &= (ICC_CTLR_EL1_CBPR | ICC_CTLR_EL1_EOIMODE); + if (value & ICC_CTLR_EL3_EOIMODE_EL1NS) { + cs->icc_ctlr_el1[GICV3_NS] |= ICC_CTLR_EL1_EOIMODE; + } + if (value & ICC_CTLR_EL3_CBPR_EL1NS) { + cs->icc_ctlr_el1[GICV3_NS] |= ICC_CTLR_EL1_CBPR; + } + + cs->icc_ctlr_el1[GICV3_S] &= (ICC_CTLR_EL1_CBPR | ICC_CTLR_EL1_EOIMODE); + if (value & ICC_CTLR_EL3_EOIMODE_EL1S) { + cs->icc_ctlr_el1[GICV3_S] |= ICC_CTLR_EL1_EOIMODE; + } + if (value & ICC_CTLR_EL3_CBPR_EL1S) { + cs->icc_ctlr_el1[GICV3_S] |= ICC_CTLR_EL1_CBPR; + } + + /* The only bit stored in icc_ctlr_el3 which is writeable is EOIMODE_EL3: */ + mask = ICC_CTLR_EL3_EOIMODE_EL3; + + cs->icc_ctlr_el3 &= ~mask; + cs->icc_ctlr_el3 |= (value & mask); + gicv3_cpuif_update(cs); +} + +static CPAccessResult gicv3_irqfiq_access(CPUARMState *env, + const ARMCPRegInfo *ri, bool isread) +{ + CPAccessResult r = CP_ACCESS_OK; + + if ((env->cp15.scr_el3 & (SCR_FIQ | SCR_IRQ)) == (SCR_FIQ | SCR_IRQ)) { + switch (arm_current_el(env)) { + case 1: + if (arm_is_secure_below_el3(env) || + ((env->cp15.hcr_el2 & (HCR_IMO | HCR_FMO)) == 0)) { + r = CP_ACCESS_TRAP_EL3; + } + break; + case 2: + r = CP_ACCESS_TRAP_EL3; + break; + case 3: + if (!is_a64(env) && !arm_is_el3_or_mon(env)) { + r = CP_ACCESS_TRAP_EL3; + } + break; + default: + g_assert_not_reached(); + } + } + + if (r == CP_ACCESS_TRAP_EL3 && !arm_el_is_aa64(env, 3)) { + r = CP_ACCESS_TRAP; + } + return r; +} + +static CPAccessResult gicv3_fiq_access(CPUARMState *env, + const ARMCPRegInfo *ri, bool isread) +{ + CPAccessResult r = CP_ACCESS_OK; + + if (env->cp15.scr_el3 & SCR_FIQ) { + switch (arm_current_el(env)) { + case 1: + if (arm_is_secure_below_el3(env) || + ((env->cp15.hcr_el2 & HCR_FMO) == 0)) { + r = CP_ACCESS_TRAP_EL3; + } + break; + case 2: + r = CP_ACCESS_TRAP_EL3; + break; + case 3: + if (!is_a64(env) && !arm_is_el3_or_mon(env)) { + r = CP_ACCESS_TRAP_EL3; + } + break; + default: + g_assert_not_reached(); + } + } + + if (r == CP_ACCESS_TRAP_EL3 && !arm_el_is_aa64(env, 3)) { + r = CP_ACCESS_TRAP; + } + return r; +} + +static CPAccessResult gicv3_irq_access(CPUARMState *env, + const ARMCPRegInfo *ri, bool isread) +{ + CPAccessResult r = CP_ACCESS_OK; + + if (env->cp15.scr_el3 & SCR_IRQ) { + switch (arm_current_el(env)) { + case 1: + if (arm_is_secure_below_el3(env) || + ((env->cp15.hcr_el2 & HCR_IMO) == 0)) { + r = CP_ACCESS_TRAP_EL3; + } + break; + case 2: + r = CP_ACCESS_TRAP_EL3; + break; + case 3: + if (!is_a64(env) && !arm_is_el3_or_mon(env)) { + r = CP_ACCESS_TRAP_EL3; + } + break; + default: + g_assert_not_reached(); + } + } + + if (r == CP_ACCESS_TRAP_EL3 && !arm_el_is_aa64(env, 3)) { + r = CP_ACCESS_TRAP; + } + return r; +} + +static void icc_reset(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + + cs->icc_ctlr_el1[GICV3_S] = ICC_CTLR_EL1_A3V | + (1 << ICC_CTLR_EL1_IDBITS_SHIFT) | + (7 << ICC_CTLR_EL1_PRIBITS_SHIFT); + cs->icc_ctlr_el1[GICV3_NS] = ICC_CTLR_EL1_A3V | + (1 << ICC_CTLR_EL1_IDBITS_SHIFT) | + (7 << ICC_CTLR_EL1_PRIBITS_SHIFT); + cs->icc_pmr_el1 = 0; + cs->icc_bpr[GICV3_G0] = GIC_MIN_BPR; + cs->icc_bpr[GICV3_G1] = GIC_MIN_BPR; + if (arm_feature(env, ARM_FEATURE_EL3)) { + cs->icc_bpr[GICV3_G1NS] = GIC_MIN_BPR_NS; + } else { + cs->icc_bpr[GICV3_G1NS] = GIC_MIN_BPR; + } + memset(cs->icc_apr, 0, sizeof(cs->icc_apr)); + memset(cs->icc_igrpen, 0, sizeof(cs->icc_igrpen)); + cs->icc_ctlr_el3 = ICC_CTLR_EL3_NDS | ICC_CTLR_EL3_A3V | + (1 << ICC_CTLR_EL3_IDBITS_SHIFT) | + (7 << ICC_CTLR_EL3_PRIBITS_SHIFT); +} + +static const ARMCPRegInfo gicv3_cpuif_reginfo[] = { + { .name = "ICC_PMR_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 6, .opc2 = 0, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL1_RW, .accessfn = gicv3_irqfiq_access, + .readfn = icc_pmr_read, + .writefn = icc_pmr_write, + /* We hang the whole cpu interface reset routine off here + * rather than parcelling it out into one little function + * per register + */ + .resetfn = icc_reset, + }, + { .name = "ICC_IAR0_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 8, .opc2 = 0, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL1_R, .accessfn = gicv3_fiq_access, + .readfn = icc_iar0_read, + }, + { .name = "ICC_EOIR0_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 8, .opc2 = 1, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = gicv3_fiq_access, + .writefn = icc_eoir_write, + }, + { .name = "ICC_HPPIR0_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 8, .opc2 = 2, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL1_R, .accessfn = gicv3_fiq_access, + .readfn = icc_hppir0_read, + }, + { .name = "ICC_BPR0_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 8, .opc2 = 3, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL1_RW, .accessfn = gicv3_fiq_access, + .fieldoffset = offsetof(GICv3CPUState, icc_bpr[GICV3_G0]), + .writefn = icc_bpr_write, + }, + { .name = "ICC_AP0R0_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 8, .opc2 = 4, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL1_RW, .accessfn = gicv3_fiq_access, + .fieldoffset = offsetof(GICv3CPUState, icc_apr[GICV3_G0][0]), + .writefn = icc_ap_write, + }, + { .name = "ICC_AP0R1_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 8, .opc2 = 5, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL1_RW, .accessfn = gicv3_fiq_access, + .fieldoffset = offsetof(GICv3CPUState, icc_apr[GICV3_G0][1]), + .writefn = icc_ap_write, + }, + { .name = "ICC_AP0R2_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 8, .opc2 = 6, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL1_RW, .accessfn = gicv3_fiq_access, + .fieldoffset = offsetof(GICv3CPUState, icc_apr[GICV3_G0][2]), + .writefn = icc_ap_write, + }, + { .name = "ICC_AP0R3_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 8, .opc2 = 7, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL1_RW, .accessfn = gicv3_fiq_access, + .fieldoffset = offsetof(GICv3CPUState, icc_apr[GICV3_G0][3]), + .writefn = icc_ap_write, + }, + /* All the ICC_AP1R*_EL1 registers are banked */ + { .name = "ICC_AP1R0_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 9, .opc2 = 0, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL1_RW, .accessfn = gicv3_irq_access, + .readfn = icc_ap_read, + .writefn = icc_ap_write, + }, + { .name = "ICC_AP1R1_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 9, .opc2 = 1, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL1_RW, .accessfn = gicv3_irq_access, + .readfn = icc_ap_read, + .writefn = icc_ap_write, + }, + { .name = "ICC_AP1R2_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 9, .opc2 = 2, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL1_RW, .accessfn = gicv3_irq_access, + .readfn = icc_ap_read, + .writefn = icc_ap_write, + }, + { .name = "ICC_AP1R3_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 9, .opc2 = 3, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL1_RW, .accessfn = gicv3_irq_access, + .readfn = icc_ap_read, + .writefn = icc_ap_write, + }, + { .name = "ICC_DIR_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 11, .opc2 = 1, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = gicv3_irqfiq_access, + .writefn = icc_dir_write, + }, + { .name = "ICC_RPR_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 11, .opc2 = 3, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL1_R, .accessfn = gicv3_irqfiq_access, + .readfn = icc_rpr_read, + }, + { .name = "ICC_SGI1R_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 11, .opc2 = 5, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = gicv3_irqfiq_access, + .writefn = icc_sgi1r_write, + }, + { .name = "ICC_SGI1R", + .cp = 15, .opc1 = 0, .crm = 12, + .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = gicv3_irqfiq_access, + .writefn = icc_sgi1r_write, + }, + { .name = "ICC_ASGI1R_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 11, .opc2 = 6, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = gicv3_irqfiq_access, + .writefn = icc_asgi1r_write, + }, + { .name = "ICC_ASGI1R", + .cp = 15, .opc1 = 1, .crm = 12, + .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = gicv3_irqfiq_access, + .writefn = icc_asgi1r_write, + }, + { .name = "ICC_SGI0R_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 11, .opc2 = 7, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = gicv3_irqfiq_access, + .writefn = icc_sgi0r_write, + }, + { .name = "ICC_SGI0R", + .cp = 15, .opc1 = 2, .crm = 12, + .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = gicv3_irqfiq_access, + .writefn = icc_sgi0r_write, + }, + { .name = "ICC_IAR1_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 12, .opc2 = 0, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL1_R, .accessfn = gicv3_irq_access, + .readfn = icc_iar1_read, + }, + { .name = "ICC_EOIR1_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 12, .opc2 = 1, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL1_W, .accessfn = gicv3_irq_access, + .writefn = icc_eoir_write, + }, + { .name = "ICC_HPPIR1_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 12, .opc2 = 2, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL1_R, .accessfn = gicv3_irq_access, + .readfn = icc_hppir1_read, + }, + /* This register is banked */ + { .name = "ICC_BPR1_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 12, .opc2 = 3, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL1_RW, .accessfn = gicv3_irq_access, + .readfn = icc_bpr_read, + .writefn = icc_bpr_write, + }, + /* This register is banked */ + { .name = "ICC_CTLR_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 12, .opc2 = 4, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL1_RW, .accessfn = gicv3_irqfiq_access, + .readfn = icc_ctlr_el1_read, + .writefn = icc_ctlr_el1_write, + }, + { .name = "ICC_SRE_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 12, .opc2 = 5, + .type = ARM_CP_NO_RAW | ARM_CP_CONST, + .access = PL1_RW, + /* We don't support IRQ/FIQ bypass and system registers are + * always enabled, so all our bits are RAZ/WI or RAO/WI. + * This register is banked but since it's constant we don't + * need to do anything special. + */ + .resetvalue = 0x7, + }, + { .name = "ICC_IGRPEN0_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 12, .opc2 = 6, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL1_RW, .accessfn = gicv3_fiq_access, + .fieldoffset = offsetof(GICv3CPUState, icc_igrpen[GICV3_G0]), + .writefn = icc_igrpen_write, + }, + /* This register is banked */ + { .name = "ICC_IGRPEN1_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 12, .opc2 = 7, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL1_RW, .accessfn = gicv3_irq_access, + .readfn = icc_igrpen_read, + .writefn = icc_igrpen_write, + }, + { .name = "ICC_SRE_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 9, .opc2 = 5, + .type = ARM_CP_NO_RAW | ARM_CP_CONST, + .access = PL2_RW, + /* We don't support IRQ/FIQ bypass and system registers are + * always enabled, so all our bits are RAZ/WI or RAO/WI. + */ + .resetvalue = 0xf, + }, + { .name = "ICC_CTLR_EL3", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 6, .crn = 12, .crm = 12, .opc2 = 4, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL3_RW, + .fieldoffset = offsetof(GICv3CPUState, icc_ctlr_el3), + .readfn = icc_ctlr_el3_read, + .writefn = icc_ctlr_el3_write, + }, + { .name = "ICC_SRE_EL3", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 6, .crn = 12, .crm = 12, .opc2 = 5, + .type = ARM_CP_NO_RAW | ARM_CP_CONST, + .access = PL3_RW, + /* We don't support IRQ/FIQ bypass and system registers are + * always enabled, so all our bits are RAZ/WI or RAO/WI. + */ + .resetvalue = 0xf, + }, + { .name = "ICC_IGRPEN1_EL3", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 6, .crn = 12, .crm = 12, .opc2 = 7, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL3_RW, + .readfn = icc_igrpen1_el3_read, + .writefn = icc_igrpen1_el3_write, + }, + REGINFO_SENTINEL +}; + +static void gicv3_cpuif_el_change_hook(ARMCPU *cpu, void *opaque) +{ + GICv3CPUState *cs = opaque; + + gicv3_cpuif_update(cs); +} + +void gicv3_init_cpuif(GICv3State *s) +{ + /* Called from the GICv3 realize function; register our system + * registers with the CPU + */ + int i; + + for (i = 0; i < s->num_cpu; i++) { + ARMCPU *cpu = ARM_CPU(qemu_get_cpu(i)); + GICv3CPUState *cs = &s->cpu[i]; + + /* Note that we can't just use the GICv3CPUState as an opaque pointer + * in define_arm_cp_regs_with_opaque(), because when we're called back + * it might be with code translated by CPU 0 but run by CPU 1, in + * which case we'd get the wrong value. + * So instead we define the regs with no ri->opaque info, and + * get back to the GICv3CPUState from the ARMCPU by reading back + * the opaque pointer from the el_change_hook, which we're going + * to need to register anyway. + */ + define_arm_cp_regs(cpu, gicv3_cpuif_reginfo); + arm_register_el_change_hook(cpu, gicv3_cpuif_el_change_hook, cs); + } +} diff --git a/hw/intc/arm_gicv3_dist.c b/hw/intc/arm_gicv3_dist.c new file mode 100644 index 000000000..3ea3dd0d4 --- /dev/null +++ b/hw/intc/arm_gicv3_dist.c @@ -0,0 +1,880 @@ +/* + * ARM GICv3 emulation: Distributor + * + * Copyright (c) 2015 Huawei. + * Copyright (c) 2016 Linaro Limited. + * Written by Shlomo Pongratz, Peter Maydell + * + * This code is licensed under the GPL, version 2 or (at your option) + * any later version. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "trace.h" +#include "gicv3_internal.h" + +/* The GICD_NSACR registers contain a two bit field for each interrupt which + * allows the guest to give NonSecure code access to registers controlling + * Secure interrupts: + * 0b00: no access (NS accesses to bits for Secure interrupts will RAZ/WI) + * 0b01: NS r/w accesses permitted to ISPENDR, SETSPI_NSR, SGIR + * 0b10: as 0b01, and also r/w to ICPENDR, r/o to ISACTIVER/ICACTIVER, + * and w/o to CLRSPI_NSR + * 0b11: as 0b10, and also r/w to IROUTER and ITARGETSR + * + * Given a (multiple-of-32) interrupt number, these mask functions return + * a mask word where each bit is 1 if the NSACR settings permit access + * to the interrupt. The mask returned can then be ORed with the GICD_GROUP + * word for this set of interrupts to give an overall mask. + */ + +typedef uint32_t maskfn(GICv3State *s, int irq); + +static uint32_t mask_nsacr_ge1(GICv3State *s, int irq) +{ + /* Return a mask where each bit is set if the NSACR field is >= 1 */ + uint64_t raw_nsacr = s->gicd_nsacr[irq / 16 + 1]; + + raw_nsacr = raw_nsacr << 32 | s->gicd_nsacr[irq / 16]; + raw_nsacr = (raw_nsacr >> 1) | raw_nsacr; + return half_unshuffle64(raw_nsacr); +} + +static uint32_t mask_nsacr_ge2(GICv3State *s, int irq) +{ + /* Return a mask where each bit is set if the NSACR field is >= 2 */ + uint64_t raw_nsacr = s->gicd_nsacr[irq / 16 + 1]; + + raw_nsacr = raw_nsacr << 32 | s->gicd_nsacr[irq / 16]; + raw_nsacr = raw_nsacr >> 1; + return half_unshuffle64(raw_nsacr); +} + +/* We don't need a mask_nsacr_ge3() because IROUTER<n> isn't a bitmap register, + * but it would be implemented using: + * raw_nsacr = (raw_nsacr >> 1) & raw_nsacr; + */ + +static uint32_t mask_group_and_nsacr(GICv3State *s, MemTxAttrs attrs, + maskfn *maskfn, int irq) +{ + /* Return a 32-bit mask which should be applied for this set of 32 + * interrupts; each bit is 1 if access is permitted by the + * combination of attrs.secure, GICD_GROUPR and GICD_NSACR. + */ + uint32_t mask; + + if (!attrs.secure && !(s->gicd_ctlr & GICD_CTLR_DS)) { + /* bits for Group 0 or Secure Group 1 interrupts are RAZ/WI + * unless the NSACR bits permit access. + */ + mask = *gic_bmp_ptr32(s->group, irq); + if (maskfn) { + mask |= maskfn(s, irq); + } + return mask; + } + return 0xFFFFFFFFU; +} + +static int gicd_ns_access(GICv3State *s, int irq) +{ + /* Return the 2 bit NS_access<x> field from GICD_NSACR<n> for the + * specified interrupt. + */ + if (irq < GIC_INTERNAL || irq >= s->num_irq) { + return 0; + } + return extract32(s->gicd_nsacr[irq / 16], (irq % 16) * 2, 2); +} + +static void gicd_write_set_bitmap_reg(GICv3State *s, MemTxAttrs attrs, + uint32_t *bmp, + maskfn *maskfn, + int offset, uint32_t val) +{ + /* Helper routine to implement writing to a "set-bitmap" register + * (GICD_ISENABLER, GICD_ISPENDR, etc). + * Semantics implemented here: + * RAZ/WI for SGIs, PPIs, unimplemented IRQs + * Bits corresponding to Group 0 or Secure Group 1 interrupts RAZ/WI. + * Writing 1 means "set bit in bitmap"; writing 0 is ignored. + * offset should be the offset in bytes of the register from the start + * of its group. + */ + int irq = offset * 8; + + if (irq < GIC_INTERNAL || irq >= s->num_irq) { + return; + } + val &= mask_group_and_nsacr(s, attrs, maskfn, irq); + *gic_bmp_ptr32(bmp, irq) |= val; + gicv3_update(s, irq, 32); +} + +static void gicd_write_clear_bitmap_reg(GICv3State *s, MemTxAttrs attrs, + uint32_t *bmp, + maskfn *maskfn, + int offset, uint32_t val) +{ + /* Helper routine to implement writing to a "clear-bitmap" register + * (GICD_ICENABLER, GICD_ICPENDR, etc). + * Semantics implemented here: + * RAZ/WI for SGIs, PPIs, unimplemented IRQs + * Bits corresponding to Group 0 or Secure Group 1 interrupts RAZ/WI. + * Writing 1 means "clear bit in bitmap"; writing 0 is ignored. + * offset should be the offset in bytes of the register from the start + * of its group. + */ + int irq = offset * 8; + + if (irq < GIC_INTERNAL || irq >= s->num_irq) { + return; + } + val &= mask_group_and_nsacr(s, attrs, maskfn, irq); + *gic_bmp_ptr32(bmp, irq) &= ~val; + gicv3_update(s, irq, 32); +} + +static uint32_t gicd_read_bitmap_reg(GICv3State *s, MemTxAttrs attrs, + uint32_t *bmp, + maskfn *maskfn, + int offset) +{ + /* Helper routine to implement reading a "set/clear-bitmap" register + * (GICD_ICENABLER, GICD_ISENABLER, GICD_ICPENDR, etc). + * Semantics implemented here: + * RAZ/WI for SGIs, PPIs, unimplemented IRQs + * Bits corresponding to Group 0 or Secure Group 1 interrupts RAZ/WI. + * offset should be the offset in bytes of the register from the start + * of its group. + */ + int irq = offset * 8; + uint32_t val; + + if (irq < GIC_INTERNAL || irq >= s->num_irq) { + return 0; + } + val = *gic_bmp_ptr32(bmp, irq); + if (bmp == s->pending) { + /* The PENDING register is a special case -- for level triggered + * interrupts, the PENDING state is the logical OR of the state of + * the PENDING latch with the input line level. + */ + uint32_t edge = *gic_bmp_ptr32(s->edge_trigger, irq); + uint32_t level = *gic_bmp_ptr32(s->level, irq); + val |= (~edge & level); + } + val &= mask_group_and_nsacr(s, attrs, maskfn, irq); + return val; +} + +static uint8_t gicd_read_ipriorityr(GICv3State *s, MemTxAttrs attrs, int irq) +{ + /* Read the value of GICD_IPRIORITYR<n> for the specified interrupt, + * honouring security state (these are RAZ/WI for Group 0 or Secure + * Group 1 interrupts). + */ + uint32_t prio; + + if (irq < GIC_INTERNAL || irq >= s->num_irq) { + return 0; + } + + prio = s->gicd_ipriority[irq]; + + if (!attrs.secure && !(s->gicd_ctlr & GICD_CTLR_DS)) { + if (!gicv3_gicd_group_test(s, irq)) { + /* Fields for Group 0 or Secure Group 1 interrupts are RAZ/WI */ + return 0; + } + /* NS view of the interrupt priority */ + prio = (prio << 1) & 0xff; + } + return prio; +} + +static void gicd_write_ipriorityr(GICv3State *s, MemTxAttrs attrs, int irq, + uint8_t value) +{ + /* Write the value of GICD_IPRIORITYR<n> for the specified interrupt, + * honouring security state (these are RAZ/WI for Group 0 or Secure + * Group 1 interrupts). + */ + if (irq < GIC_INTERNAL || irq >= s->num_irq) { + return; + } + + if (!attrs.secure && !(s->gicd_ctlr & GICD_CTLR_DS)) { + if (!gicv3_gicd_group_test(s, irq)) { + /* Fields for Group 0 or Secure Group 1 interrupts are RAZ/WI */ + return; + } + /* NS view of the interrupt priority */ + value = 0x80 | (value >> 1); + } + s->gicd_ipriority[irq] = value; +} + +static uint64_t gicd_read_irouter(GICv3State *s, MemTxAttrs attrs, int irq) +{ + /* Read the value of GICD_IROUTER<n> for the specified interrupt, + * honouring security state. + */ + if (irq < GIC_INTERNAL || irq >= s->num_irq) { + return 0; + } + + if (!attrs.secure && !(s->gicd_ctlr & GICD_CTLR_DS)) { + /* RAZ/WI for NS accesses to secure interrupts */ + if (!gicv3_gicd_group_test(s, irq)) { + if (gicd_ns_access(s, irq) != 3) { + return 0; + } + } + } + + return s->gicd_irouter[irq]; +} + +static void gicd_write_irouter(GICv3State *s, MemTxAttrs attrs, int irq, + uint64_t val) +{ + /* Write the value of GICD_IROUTER<n> for the specified interrupt, + * honouring security state. + */ + if (irq < GIC_INTERNAL || irq >= s->num_irq) { + return; + } + + if (!attrs.secure && !(s->gicd_ctlr & GICD_CTLR_DS)) { + /* RAZ/WI for NS accesses to secure interrupts */ + if (!gicv3_gicd_group_test(s, irq)) { + if (gicd_ns_access(s, irq) != 3) { + return; + } + } + } + + s->gicd_irouter[irq] = val; + gicv3_cache_target_cpustate(s, irq); + gicv3_update(s, irq, 1); +} + +static MemTxResult gicd_readb(GICv3State *s, hwaddr offset, + uint64_t *data, MemTxAttrs attrs) +{ + /* Most GICv3 distributor registers do not support byte accesses. */ + switch (offset) { + case GICD_CPENDSGIR ... GICD_CPENDSGIR + 0xf: + case GICD_SPENDSGIR ... GICD_SPENDSGIR + 0xf: + case GICD_ITARGETSR ... GICD_ITARGETSR + 0x3ff: + /* This GIC implementation always has affinity routing enabled, + * so these registers are all RAZ/WI. + */ + return MEMTX_OK; + case GICD_IPRIORITYR ... GICD_IPRIORITYR + 0x3ff: + *data = gicd_read_ipriorityr(s, attrs, offset - GICD_IPRIORITYR); + return MEMTX_OK; + default: + return MEMTX_ERROR; + } +} + +static MemTxResult gicd_writeb(GICv3State *s, hwaddr offset, + uint64_t value, MemTxAttrs attrs) +{ + /* Most GICv3 distributor registers do not support byte accesses. */ + switch (offset) { + case GICD_CPENDSGIR ... GICD_CPENDSGIR + 0xf: + case GICD_SPENDSGIR ... GICD_SPENDSGIR + 0xf: + case GICD_ITARGETSR ... GICD_ITARGETSR + 0x3ff: + /* This GIC implementation always has affinity routing enabled, + * so these registers are all RAZ/WI. + */ + return MEMTX_OK; + case GICD_IPRIORITYR ... GICD_IPRIORITYR + 0x3ff: + { + int irq = offset - GICD_IPRIORITYR; + + if (irq < GIC_INTERNAL || irq >= s->num_irq) { + return MEMTX_OK; + } + gicd_write_ipriorityr(s, attrs, irq, value); + gicv3_update(s, irq, 1); + return MEMTX_OK; + } + default: + return MEMTX_ERROR; + } +} + +static MemTxResult gicd_readw(GICv3State *s, hwaddr offset, + uint64_t *data, MemTxAttrs attrs) +{ + /* Only GICD_SETSPI_NSR, GICD_CLRSPI_NSR, GICD_SETSPI_SR and GICD_SETSPI_NSR + * support 16 bit accesses, and those registers are all part of the + * optional message-based SPI feature which this GIC does not currently + * implement (ie for us GICD_TYPER.MBIS == 0), so for us they are + * reserved. + */ + return MEMTX_ERROR; +} + +static MemTxResult gicd_writew(GICv3State *s, hwaddr offset, + uint64_t value, MemTxAttrs attrs) +{ + /* Only GICD_SETSPI_NSR, GICD_CLRSPI_NSR, GICD_SETSPI_SR and GICD_SETSPI_NSR + * support 16 bit accesses, and those registers are all part of the + * optional message-based SPI feature which this GIC does not currently + * implement (ie for us GICD_TYPER.MBIS == 0), so for us they are + * reserved. + */ + return MEMTX_ERROR; +} + +static MemTxResult gicd_readl(GICv3State *s, hwaddr offset, + uint64_t *data, MemTxAttrs attrs) +{ + /* Almost all GICv3 distributor registers are 32-bit. + * Note that WO registers must return an UNKNOWN value on reads, + * not an abort. + */ + + switch (offset) { + case GICD_CTLR: + if (!attrs.secure && !(s->gicd_ctlr & GICD_CTLR_DS)) { + /* The NS view of the GICD_CTLR sees only certain bits: + * + bit [31] (RWP) is an alias of the Secure bit [31] + * + bit [4] (ARE_NS) is an alias of Secure bit [5] + * + bit [1] (EnableGrp1A) is an alias of Secure bit [1] if + * NS affinity routing is enabled, otherwise RES0 + * + bit [0] (EnableGrp1) is an alias of Secure bit [1] if + * NS affinity routing is not enabled, otherwise RES0 + * Since for QEMU affinity routing is always enabled + * for both S and NS this means that bits [4] and [5] are + * both always 1, and we can simply make the NS view + * be bits 31, 4 and 1 of the S view. + */ + *data = s->gicd_ctlr & (GICD_CTLR_ARE_S | + GICD_CTLR_EN_GRP1NS | + GICD_CTLR_RWP); + } else { + *data = s->gicd_ctlr; + } + return MEMTX_OK; + case GICD_TYPER: + { + /* For this implementation: + * No1N == 1 (1-of-N SPI interrupts not supported) + * A3V == 1 (non-zero values of Affinity level 3 supported) + * IDbits == 0xf (we support 16-bit interrupt identifiers) + * DVIS == 0 (Direct virtual LPI injection not supported) + * LPIS == 0 (LPIs not supported) + * MBIS == 0 (message-based SPIs not supported) + * SecurityExtn == 1 if security extns supported + * CPUNumber == 0 since for us ARE is always 1 + * ITLinesNumber == (num external irqs / 32) - 1 + */ + int itlinesnumber = ((s->num_irq - GIC_INTERNAL) / 32) - 1; + + *data = (1 << 25) | (1 << 24) | (s->security_extn << 10) | + (0xf << 19) | itlinesnumber; + return MEMTX_OK; + } + case GICD_IIDR: + /* We claim to be an ARM r0p0 with a zero ProductID. + * This is the same as an r0p0 GIC-500. + */ + *data = gicv3_iidr(); + return MEMTX_OK; + case GICD_STATUSR: + /* RAZ/WI for us (this is an optional register and our implementation + * does not track RO/WO/reserved violations to report them to the guest) + */ + *data = 0; + return MEMTX_OK; + case GICD_IGROUPR ... GICD_IGROUPR + 0x7f: + { + int irq; + + if (!attrs.secure && !(s->gicd_ctlr & GICD_CTLR_DS)) { + *data = 0; + return MEMTX_OK; + } + /* RAZ/WI for SGIs, PPIs, unimplemented irqs */ + irq = (offset - GICD_IGROUPR) * 8; + if (irq < GIC_INTERNAL || irq >= s->num_irq) { + *data = 0; + return MEMTX_OK; + } + *data = *gic_bmp_ptr32(s->group, irq); + return MEMTX_OK; + } + case GICD_ISENABLER ... GICD_ISENABLER + 0x7f: + *data = gicd_read_bitmap_reg(s, attrs, s->enabled, NULL, + offset - GICD_ISENABLER); + return MEMTX_OK; + case GICD_ICENABLER ... GICD_ICENABLER + 0x7f: + *data = gicd_read_bitmap_reg(s, attrs, s->enabled, NULL, + offset - GICD_ICENABLER); + return MEMTX_OK; + case GICD_ISPENDR ... GICD_ISPENDR + 0x7f: + *data = gicd_read_bitmap_reg(s, attrs, s->pending, mask_nsacr_ge1, + offset - GICD_ISPENDR); + return MEMTX_OK; + case GICD_ICPENDR ... GICD_ICPENDR + 0x7f: + *data = gicd_read_bitmap_reg(s, attrs, s->pending, mask_nsacr_ge2, + offset - GICD_ICPENDR); + return MEMTX_OK; + case GICD_ISACTIVER ... GICD_ISACTIVER + 0x7f: + *data = gicd_read_bitmap_reg(s, attrs, s->active, mask_nsacr_ge2, + offset - GICD_ISACTIVER); + return MEMTX_OK; + case GICD_ICACTIVER ... GICD_ICACTIVER + 0x7f: + *data = gicd_read_bitmap_reg(s, attrs, s->active, mask_nsacr_ge2, + offset - GICD_ICACTIVER); + return MEMTX_OK; + case GICD_IPRIORITYR ... GICD_IPRIORITYR + 0x3ff: + { + int i, irq = offset - GICD_IPRIORITYR; + uint32_t value = 0; + + for (i = irq + 3; i >= irq; i--, value <<= 8) { + value |= gicd_read_ipriorityr(s, attrs, i); + } + *data = value; + return MEMTX_OK; + } + case GICD_ITARGETSR ... GICD_ITARGETSR + 0x3ff: + /* RAZ/WI since affinity routing is always enabled */ + *data = 0; + return MEMTX_OK; + case GICD_ICFGR ... GICD_ICFGR + 0xff: + { + /* Here only the even bits are used; odd bits are RES0 */ + int irq = (offset - GICD_ICFGR) * 4; + uint32_t value = 0; + + if (irq < GIC_INTERNAL || irq >= s->num_irq) { + *data = 0; + return MEMTX_OK; + } + + /* Since our edge_trigger bitmap is one bit per irq, we only need + * half of the 32-bit word, which we can then spread out + * into the odd bits. + */ + value = *gic_bmp_ptr32(s->edge_trigger, irq & ~0x1f); + value &= mask_group_and_nsacr(s, attrs, NULL, irq & ~0x1f); + value = extract32(value, (irq & 0x1f) ? 16 : 0, 16); + value = half_shuffle32(value) << 1; + *data = value; + return MEMTX_OK; + } + case GICD_IGRPMODR ... GICD_IGRPMODR + 0xff: + { + int irq; + + if ((s->gicd_ctlr & GICD_CTLR_DS) || !attrs.secure) { + /* RAZ/WI if security disabled, or if + * security enabled and this is an NS access + */ + *data = 0; + return MEMTX_OK; + } + /* RAZ/WI for SGIs, PPIs, unimplemented irqs */ + irq = (offset - GICD_IGRPMODR) * 8; + if (irq < GIC_INTERNAL || irq >= s->num_irq) { + *data = 0; + return MEMTX_OK; + } + *data = *gic_bmp_ptr32(s->grpmod, irq); + return MEMTX_OK; + } + case GICD_NSACR ... GICD_NSACR + 0xff: + { + /* Two bits per interrupt */ + int irq = (offset - GICD_NSACR) * 4; + + if (irq < GIC_INTERNAL || irq >= s->num_irq) { + *data = 0; + return MEMTX_OK; + } + + if ((s->gicd_ctlr & GICD_CTLR_DS) || !attrs.secure) { + /* RAZ/WI if security disabled, or if + * security enabled and this is an NS access + */ + *data = 0; + return MEMTX_OK; + } + + *data = s->gicd_nsacr[irq / 16]; + return MEMTX_OK; + } + case GICD_CPENDSGIR ... GICD_CPENDSGIR + 0xf: + case GICD_SPENDSGIR ... GICD_SPENDSGIR + 0xf: + /* RAZ/WI since affinity routing is always enabled */ + *data = 0; + return MEMTX_OK; + case GICD_IROUTER ... GICD_IROUTER + 0x1fdf: + { + uint64_t r; + int irq = (offset - GICD_IROUTER) / 8; + + r = gicd_read_irouter(s, attrs, irq); + if (offset & 7) { + *data = r >> 32; + } else { + *data = (uint32_t)r; + } + return MEMTX_OK; + } + case GICD_IDREGS ... GICD_IDREGS + 0x1f: + /* ID registers */ + *data = gicv3_idreg(offset - GICD_IDREGS); + return MEMTX_OK; + case GICD_SGIR: + /* WO registers, return unknown value */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid guest read from WO register at offset " + TARGET_FMT_plx "\n", __func__, offset); + *data = 0; + return MEMTX_OK; + default: + return MEMTX_ERROR; + } +} + +static MemTxResult gicd_writel(GICv3State *s, hwaddr offset, + uint64_t value, MemTxAttrs attrs) +{ + /* Almost all GICv3 distributor registers are 32-bit. Note that + * RO registers must ignore writes, not abort. + */ + + switch (offset) { + case GICD_CTLR: + { + uint32_t mask; + /* GICv3 5.3.20 */ + if (s->gicd_ctlr & GICD_CTLR_DS) { + /* With only one security state, E1NWF is RAZ/WI, DS is RAO/WI, + * ARE is RAO/WI (affinity routing always on), and only + * bits 0 and 1 (group enables) are writable. + */ + mask = GICD_CTLR_EN_GRP0 | GICD_CTLR_EN_GRP1NS; + } else { + if (attrs.secure) { + /* for secure access: + * ARE_NS and ARE_S are RAO/WI (affinity routing always on) + * E1NWF is RAZ/WI (we don't support enable-1-of-n-wakeup) + * + * We can only modify bits[2:0] (the group enables). + */ + mask = GICD_CTLR_DS | GICD_CTLR_EN_GRP0 | GICD_CTLR_EN_GRP1_ALL; + } else { + /* For non secure access ARE_NS is RAO/WI and EnableGrp1 + * is RES0. The only writable bit is [1] (EnableGrp1A), which + * is an alias of the Secure bit [1]. + */ + mask = GICD_CTLR_EN_GRP1NS; + } + } + s->gicd_ctlr = (s->gicd_ctlr & ~mask) | (value & mask); + if (value & mask & GICD_CTLR_DS) { + /* We just set DS, so the ARE_NS and EnG1S bits are now RES0. + * Note that this is a one-way transition because if DS is set + * then it's not writeable, so it can only go back to 0 with a + * hardware reset. + */ + s->gicd_ctlr &= ~(GICD_CTLR_EN_GRP1S | GICD_CTLR_ARE_NS); + } + gicv3_full_update(s); + return MEMTX_OK; + } + case GICD_STATUSR: + /* RAZ/WI for our implementation */ + return MEMTX_OK; + case GICD_IGROUPR ... GICD_IGROUPR + 0x7f: + { + int irq; + + if (!attrs.secure && !(s->gicd_ctlr & GICD_CTLR_DS)) { + return MEMTX_OK; + } + /* RAZ/WI for SGIs, PPIs, unimplemented irqs */ + irq = (offset - GICD_IGROUPR) * 8; + if (irq < GIC_INTERNAL || irq >= s->num_irq) { + return MEMTX_OK; + } + *gic_bmp_ptr32(s->group, irq) = value; + gicv3_update(s, irq, 32); + return MEMTX_OK; + } + case GICD_ISENABLER ... GICD_ISENABLER + 0x7f: + gicd_write_set_bitmap_reg(s, attrs, s->enabled, NULL, + offset - GICD_ISENABLER, value); + return MEMTX_OK; + case GICD_ICENABLER ... GICD_ICENABLER + 0x7f: + gicd_write_clear_bitmap_reg(s, attrs, s->enabled, NULL, + offset - GICD_ICENABLER, value); + return MEMTX_OK; + case GICD_ISPENDR ... GICD_ISPENDR + 0x7f: + gicd_write_set_bitmap_reg(s, attrs, s->pending, mask_nsacr_ge1, + offset - GICD_ISPENDR, value); + return MEMTX_OK; + case GICD_ICPENDR ... GICD_ICPENDR + 0x7f: + gicd_write_clear_bitmap_reg(s, attrs, s->pending, mask_nsacr_ge2, + offset - GICD_ICPENDR, value); + return MEMTX_OK; + case GICD_ISACTIVER ... GICD_ISACTIVER + 0x7f: + gicd_write_set_bitmap_reg(s, attrs, s->active, NULL, + offset - GICD_ISACTIVER, value); + return MEMTX_OK; + case GICD_ICACTIVER ... GICD_ICACTIVER + 0x7f: + gicd_write_clear_bitmap_reg(s, attrs, s->active, NULL, + offset - GICD_ICACTIVER, value); + return MEMTX_OK; + case GICD_IPRIORITYR ... GICD_IPRIORITYR + 0x3ff: + { + int i, irq = offset - GICD_IPRIORITYR; + + if (irq < GIC_INTERNAL || irq + 3 >= s->num_irq) { + return MEMTX_OK; + } + + for (i = irq; i < irq + 4; i++, value >>= 8) { + gicd_write_ipriorityr(s, attrs, i, value); + } + gicv3_update(s, irq, 4); + return MEMTX_OK; + } + case GICD_ITARGETSR ... GICD_ITARGETSR + 0x3ff: + /* RAZ/WI since affinity routing is always enabled */ + return MEMTX_OK; + case GICD_ICFGR ... GICD_ICFGR + 0xff: + { + /* Here only the odd bits are used; even bits are RES0 */ + int irq = (offset - GICD_ICFGR) * 4; + uint32_t mask, oldval; + + if (irq < GIC_INTERNAL || irq >= s->num_irq) { + return MEMTX_OK; + } + + /* Since our edge_trigger bitmap is one bit per irq, our input + * 32-bits will compress down into 16 bits which we need + * to write into the bitmap. + */ + value = half_unshuffle32(value >> 1); + mask = mask_group_and_nsacr(s, attrs, NULL, irq & ~0x1f); + if (irq & 0x1f) { + value <<= 16; + mask &= 0xffff0000U; + } else { + mask &= 0xffff; + } + oldval = *gic_bmp_ptr32(s->edge_trigger, (irq & ~0x1f)); + value = (oldval & ~mask) | (value & mask); + *gic_bmp_ptr32(s->edge_trigger, irq & ~0x1f) = value; + return MEMTX_OK; + } + case GICD_IGRPMODR ... GICD_IGRPMODR + 0xff: + { + int irq; + + if ((s->gicd_ctlr & GICD_CTLR_DS) || !attrs.secure) { + /* RAZ/WI if security disabled, or if + * security enabled and this is an NS access + */ + return MEMTX_OK; + } + /* RAZ/WI for SGIs, PPIs, unimplemented irqs */ + irq = (offset - GICD_IGRPMODR) * 8; + if (irq < GIC_INTERNAL || irq >= s->num_irq) { + return MEMTX_OK; + } + *gic_bmp_ptr32(s->grpmod, irq) = value; + gicv3_update(s, irq, 32); + return MEMTX_OK; + } + case GICD_NSACR ... GICD_NSACR + 0xff: + { + /* Two bits per interrupt */ + int irq = (offset - GICD_NSACR) * 4; + + if (irq < GIC_INTERNAL || irq >= s->num_irq) { + return MEMTX_OK; + } + + if ((s->gicd_ctlr & GICD_CTLR_DS) || !attrs.secure) { + /* RAZ/WI if security disabled, or if + * security enabled and this is an NS access + */ + return MEMTX_OK; + } + + s->gicd_nsacr[irq / 16] = value; + /* No update required as this only affects access permission checks */ + return MEMTX_OK; + } + case GICD_SGIR: + /* RES0 if affinity routing is enabled */ + return MEMTX_OK; + case GICD_CPENDSGIR ... GICD_CPENDSGIR + 0xf: + case GICD_SPENDSGIR ... GICD_SPENDSGIR + 0xf: + /* RAZ/WI since affinity routing is always enabled */ + return MEMTX_OK; + case GICD_IROUTER ... GICD_IROUTER + 0x1fdf: + { + uint64_t r; + int irq = (offset - GICD_IROUTER) / 8; + + if (irq < GIC_INTERNAL || irq >= s->num_irq) { + return MEMTX_OK; + } + + /* Write half of the 64-bit register */ + r = gicd_read_irouter(s, attrs, irq); + r = deposit64(r, (offset & 7) ? 32 : 0, 32, value); + gicd_write_irouter(s, attrs, irq, r); + return MEMTX_OK; + } + case GICD_IDREGS ... GICD_IDREGS + 0x1f: + case GICD_TYPER: + case GICD_IIDR: + /* RO registers, ignore the write */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid guest write to RO register at offset " + TARGET_FMT_plx "\n", __func__, offset); + return MEMTX_OK; + default: + return MEMTX_ERROR; + } +} + +static MemTxResult gicd_writell(GICv3State *s, hwaddr offset, + uint64_t value, MemTxAttrs attrs) +{ + /* Our only 64-bit registers are GICD_IROUTER<n> */ + int irq; + + switch (offset) { + case GICD_IROUTER ... GICD_IROUTER + 0x1fdf: + irq = (offset - GICD_IROUTER) / 8; + gicd_write_irouter(s, attrs, irq, value); + return MEMTX_OK; + default: + return MEMTX_ERROR; + } +} + +static MemTxResult gicd_readll(GICv3State *s, hwaddr offset, + uint64_t *data, MemTxAttrs attrs) +{ + /* Our only 64-bit registers are GICD_IROUTER<n> */ + int irq; + + switch (offset) { + case GICD_IROUTER ... GICD_IROUTER + 0x1fdf: + irq = (offset - GICD_IROUTER) / 8; + *data = gicd_read_irouter(s, attrs, irq); + return MEMTX_OK; + default: + return MEMTX_ERROR; + } +} + +MemTxResult gicv3_dist_read(void *opaque, hwaddr offset, uint64_t *data, + unsigned size, MemTxAttrs attrs) +{ + GICv3State *s = (GICv3State *)opaque; + MemTxResult r; + + switch (size) { + case 1: + r = gicd_readb(s, offset, data, attrs); + break; + case 2: + r = gicd_readw(s, offset, data, attrs); + break; + case 4: + r = gicd_readl(s, offset, data, attrs); + break; + case 8: + r = gicd_readll(s, offset, data, attrs); + break; + default: + r = MEMTX_ERROR; + break; + } + + if (r == MEMTX_ERROR) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid guest read at offset " TARGET_FMT_plx + "size %u\n", __func__, offset, size); + trace_gicv3_dist_badread(offset, size, attrs.secure); + } else { + trace_gicv3_dist_read(offset, *data, size, attrs.secure); + } + return r; +} + +MemTxResult gicv3_dist_write(void *opaque, hwaddr offset, uint64_t data, + unsigned size, MemTxAttrs attrs) +{ + GICv3State *s = (GICv3State *)opaque; + MemTxResult r; + + switch (size) { + case 1: + r = gicd_writeb(s, offset, data, attrs); + break; + case 2: + r = gicd_writew(s, offset, data, attrs); + break; + case 4: + r = gicd_writel(s, offset, data, attrs); + break; + case 8: + r = gicd_writell(s, offset, data, attrs); + break; + default: + r = MEMTX_ERROR; + break; + } + + if (r == MEMTX_ERROR) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid guest write at offset " TARGET_FMT_plx + "size %u\n", __func__, offset, size); + trace_gicv3_dist_badwrite(offset, data, size, attrs.secure); + } else { + trace_gicv3_dist_write(offset, data, size, attrs.secure); + } + return r; +} + +void gicv3_dist_set_irq(GICv3State *s, int irq, int level) +{ + /* Update distributor state for a change in an external SPI input line */ + if (level == gicv3_gicd_level_test(s, irq)) { + return; + } + + trace_gicv3_dist_set_irq(irq, level); + + gicv3_gicd_level_replace(s, irq, level); + + if (level) { + /* 0->1 edges latch the pending bit for edge-triggered interrupts */ + if (gicv3_gicd_edge_trigger_test(s, irq)) { + gicv3_gicd_pending_set(s, irq); + } + } + + gicv3_update(s, irq, 1); +} diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index acc173004..711fde38f 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -26,6 +26,7 @@ #include "sysemu/kvm.h" #include "kvm_arm.h" #include "vgic_common.h" +#include "migration/migration.h" #ifdef DEBUG_GICV3_KVM #define DPRINTF(fmt, ...) \ @@ -119,6 +120,13 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) KVM_VGIC_V3_ADDR_TYPE_DIST, s->dev_fd); kvm_arm_register_device(&s->iomem_redist, -1, KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V3_ADDR_TYPE_REDIST, s->dev_fd); + + /* Block migration of a KVM GICv3 device: the API for saving and restoring + * the state in the kernel is not yet finalised in the kernel or + * implemented in QEMU. + */ + error_setg(&s->migration_blocker, "vGICv3 migration is not implemented"); + migrate_add_blocker(s->migration_blocker); } static void kvm_arm_gicv3_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c new file mode 100644 index 000000000..77e5cfa32 --- /dev/null +++ b/hw/intc/arm_gicv3_redist.c @@ -0,0 +1,567 @@ +/* + * ARM GICv3 emulation: Redistributor + * + * Copyright (c) 2015 Huawei. + * Copyright (c) 2016 Linaro Limited. + * Written by Shlomo Pongratz, Peter Maydell + * + * This code is licensed under the GPL, version 2 or (at your option) + * any later version. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "trace.h" +#include "gicv3_internal.h" + +static uint32_t mask_group(GICv3CPUState *cs, MemTxAttrs attrs) +{ + /* Return a 32-bit mask which should be applied for this set of 32 + * interrupts; each bit is 1 if access is permitted by the + * combination of attrs.secure and GICR_GROUPR. (GICR_NSACR does + * not affect config register accesses, unlike GICD_NSACR.) + */ + if (!attrs.secure && !(cs->gic->gicd_ctlr & GICD_CTLR_DS)) { + /* bits for Group 0 or Secure Group 1 interrupts are RAZ/WI */ + return cs->gicr_igroupr0; + } + return 0xFFFFFFFFU; +} + +static int gicr_ns_access(GICv3CPUState *cs, int irq) +{ + /* Return the 2 bit NSACR.NS_access field for this SGI */ + assert(irq < 16); + return extract32(cs->gicr_nsacr, irq * 2, 2); +} + +static void gicr_write_set_bitmap_reg(GICv3CPUState *cs, MemTxAttrs attrs, + uint32_t *reg, uint32_t val) +{ + /* Helper routine to implement writing to a "set-bitmap" register */ + val &= mask_group(cs, attrs); + *reg |= val; + gicv3_redist_update(cs); +} + +static void gicr_write_clear_bitmap_reg(GICv3CPUState *cs, MemTxAttrs attrs, + uint32_t *reg, uint32_t val) +{ + /* Helper routine to implement writing to a "clear-bitmap" register */ + val &= mask_group(cs, attrs); + *reg &= ~val; + gicv3_redist_update(cs); +} + +static uint32_t gicr_read_bitmap_reg(GICv3CPUState *cs, MemTxAttrs attrs, + uint32_t reg) +{ + reg &= mask_group(cs, attrs); + return reg; +} + +static uint8_t gicr_read_ipriorityr(GICv3CPUState *cs, MemTxAttrs attrs, + int irq) +{ + /* Read the value of GICR_IPRIORITYR<n> for the specified interrupt, + * honouring security state (these are RAZ/WI for Group 0 or Secure + * Group 1 interrupts). + */ + uint32_t prio; + + prio = cs->gicr_ipriorityr[irq]; + + if (!attrs.secure && !(cs->gic->gicd_ctlr & GICD_CTLR_DS)) { + if (!(cs->gicr_igroupr0 & (1U << irq))) { + /* Fields for Group 0 or Secure Group 1 interrupts are RAZ/WI */ + return 0; + } + /* NS view of the interrupt priority */ + prio = (prio << 1) & 0xff; + } + return prio; +} + +static void gicr_write_ipriorityr(GICv3CPUState *cs, MemTxAttrs attrs, int irq, + uint8_t value) +{ + /* Write the value of GICD_IPRIORITYR<n> for the specified interrupt, + * honouring security state (these are RAZ/WI for Group 0 or Secure + * Group 1 interrupts). + */ + if (!attrs.secure && !(cs->gic->gicd_ctlr & GICD_CTLR_DS)) { + if (!(cs->gicr_igroupr0 & (1U << irq))) { + /* Fields for Group 0 or Secure Group 1 interrupts are RAZ/WI */ + return; + } + /* NS view of the interrupt priority */ + value = 0x80 | (value >> 1); + } + cs->gicr_ipriorityr[irq] = value; +} + +static MemTxResult gicr_readb(GICv3CPUState *cs, hwaddr offset, + uint64_t *data, MemTxAttrs attrs) +{ + switch (offset) { + case GICR_IPRIORITYR ... GICR_IPRIORITYR + 0x1f: + *data = gicr_read_ipriorityr(cs, attrs, offset - GICR_IPRIORITYR); + return MEMTX_OK; + default: + return MEMTX_ERROR; + } +} + +static MemTxResult gicr_writeb(GICv3CPUState *cs, hwaddr offset, + uint64_t value, MemTxAttrs attrs) +{ + switch (offset) { + case GICR_IPRIORITYR ... GICR_IPRIORITYR + 0x1f: + gicr_write_ipriorityr(cs, attrs, offset - GICR_IPRIORITYR, value); + gicv3_redist_update(cs); + return MEMTX_OK; + default: + return MEMTX_ERROR; + } +} + +static MemTxResult gicr_readl(GICv3CPUState *cs, hwaddr offset, + uint64_t *data, MemTxAttrs attrs) +{ + switch (offset) { + case GICR_CTLR: + *data = cs->gicr_ctlr; + return MEMTX_OK; + case GICR_IIDR: + *data = gicv3_iidr(); + return MEMTX_OK; + case GICR_TYPER: + *data = extract64(cs->gicr_typer, 0, 32); + return MEMTX_OK; + case GICR_TYPER + 4: + *data = extract64(cs->gicr_typer, 32, 32); + return MEMTX_OK; + case GICR_STATUSR: + /* RAZ/WI for us (this is an optional register and our implementation + * does not track RO/WO/reserved violations to report them to the guest) + */ + *data = 0; + return MEMTX_OK; + case GICR_WAKER: + *data = cs->gicr_waker; + return MEMTX_OK; + case GICR_PROPBASER: + *data = extract64(cs->gicr_propbaser, 0, 32); + return MEMTX_OK; + case GICR_PROPBASER + 4: + *data = extract64(cs->gicr_propbaser, 32, 32); + return MEMTX_OK; + case GICR_PENDBASER: + *data = extract64(cs->gicr_pendbaser, 0, 32); + return MEMTX_OK; + case GICR_PENDBASER + 4: + *data = extract64(cs->gicr_pendbaser, 32, 32); + return MEMTX_OK; + case GICR_IGROUPR0: + if (!attrs.secure && !(cs->gic->gicd_ctlr & GICD_CTLR_DS)) { + *data = 0; + return MEMTX_OK; + } + *data = cs->gicr_igroupr0; + return MEMTX_OK; + case GICR_ISENABLER0: + case GICR_ICENABLER0: + *data = gicr_read_bitmap_reg(cs, attrs, cs->gicr_ienabler0); + return MEMTX_OK; + case GICR_ISPENDR0: + case GICR_ICPENDR0: + { + /* The pending register reads as the logical OR of the pending + * latch and the input line level for level-triggered interrupts. + */ + uint32_t val = cs->gicr_ipendr0 | (~cs->edge_trigger & cs->level); + *data = gicr_read_bitmap_reg(cs, attrs, val); + return MEMTX_OK; + } + case GICR_ISACTIVER0: + case GICR_ICACTIVER0: + *data = gicr_read_bitmap_reg(cs, attrs, cs->gicr_iactiver0); + return MEMTX_OK; + case GICR_IPRIORITYR ... GICR_IPRIORITYR + 0x1f: + { + int i, irq = offset - GICR_IPRIORITYR; + uint32_t value = 0; + + for (i = irq + 3; i >= irq; i--, value <<= 8) { + value |= gicr_read_ipriorityr(cs, attrs, i); + } + *data = value; + return MEMTX_OK; + } + case GICR_ICFGR0: + case GICR_ICFGR1: + { + /* Our edge_trigger bitmap is one bit per irq; take the correct + * half of it, and spread it out into the odd bits. + */ + uint32_t value; + + value = cs->edge_trigger & mask_group(cs, attrs); + value = extract32(value, (offset == GICR_ICFGR1) ? 16 : 0, 16); + value = half_shuffle32(value) << 1; + *data = value; + return MEMTX_OK; + } + case GICR_IGRPMODR0: + if ((cs->gic->gicd_ctlr & GICD_CTLR_DS) || !attrs.secure) { + /* RAZ/WI if security disabled, or if + * security enabled and this is an NS access + */ + *data = 0; + return MEMTX_OK; + } + *data = cs->gicr_igrpmodr0; + return MEMTX_OK; + case GICR_NSACR: + if ((cs->gic->gicd_ctlr & GICD_CTLR_DS) || !attrs.secure) { + /* RAZ/WI if security disabled, or if + * security enabled and this is an NS access + */ + *data = 0; + return MEMTX_OK; + } + *data = cs->gicr_nsacr; + return MEMTX_OK; + case GICR_IDREGS ... GICR_IDREGS + 0x1f: + *data = gicv3_idreg(offset - GICR_IDREGS); + return MEMTX_OK; + default: + return MEMTX_ERROR; + } +} + +static MemTxResult gicr_writel(GICv3CPUState *cs, hwaddr offset, + uint64_t value, MemTxAttrs attrs) +{ + switch (offset) { + case GICR_CTLR: + /* For our implementation, GICR_TYPER.DPGS is 0 and so all + * the DPG bits are RAZ/WI. We don't do anything asynchronously, + * so UWP and RWP are RAZ/WI. And GICR_TYPER.LPIS is 0 (we don't + * implement LPIs) so Enable_LPIs is RES0. So there are no writable + * bits for us. + */ + return MEMTX_OK; + case GICR_STATUSR: + /* RAZ/WI for our implementation */ + return MEMTX_OK; + case GICR_WAKER: + /* Only the ProcessorSleep bit is writeable. When the guest sets + * it it requests that we transition the channel between the + * redistributor and the cpu interface to quiescent, and that + * we set the ChildrenAsleep bit once the inteface has reached the + * quiescent state. + * Setting the ProcessorSleep to 0 reverses the quiescing, and + * ChildrenAsleep is cleared once the transition is complete. + * Since our interface is not asynchronous, we complete these + * transitions instantaneously, so we set ChildrenAsleep to the + * same value as ProcessorSleep here. + */ + value &= GICR_WAKER_ProcessorSleep; + if (value & GICR_WAKER_ProcessorSleep) { + value |= GICR_WAKER_ChildrenAsleep; + } + cs->gicr_waker = value; + return MEMTX_OK; + case GICR_PROPBASER: + cs->gicr_propbaser = deposit64(cs->gicr_propbaser, 0, 32, value); + return MEMTX_OK; + case GICR_PROPBASER + 4: + cs->gicr_propbaser = deposit64(cs->gicr_propbaser, 32, 32, value); + return MEMTX_OK; + case GICR_PENDBASER: + cs->gicr_pendbaser = deposit64(cs->gicr_pendbaser, 0, 32, value); + return MEMTX_OK; + case GICR_PENDBASER + 4: + cs->gicr_pendbaser = deposit64(cs->gicr_pendbaser, 32, 32, value); + return MEMTX_OK; + case GICR_IGROUPR0: + if (!attrs.secure && !(cs->gic->gicd_ctlr & GICD_CTLR_DS)) { + return MEMTX_OK; + } + cs->gicr_igroupr0 = value; + gicv3_redist_update(cs); + return MEMTX_OK; + case GICR_ISENABLER0: + gicr_write_set_bitmap_reg(cs, attrs, &cs->gicr_ienabler0, value); + return MEMTX_OK; + case GICR_ICENABLER0: + gicr_write_clear_bitmap_reg(cs, attrs, &cs->gicr_ienabler0, value); + return MEMTX_OK; + case GICR_ISPENDR0: + gicr_write_set_bitmap_reg(cs, attrs, &cs->gicr_ipendr0, value); + return MEMTX_OK; + case GICR_ICPENDR0: + gicr_write_clear_bitmap_reg(cs, attrs, &cs->gicr_ipendr0, value); + return MEMTX_OK; + case GICR_ISACTIVER0: + gicr_write_set_bitmap_reg(cs, attrs, &cs->gicr_iactiver0, value); + return MEMTX_OK; + case GICR_ICACTIVER0: + gicr_write_clear_bitmap_reg(cs, attrs, &cs->gicr_iactiver0, value); + return MEMTX_OK; + case GICR_IPRIORITYR ... GICR_IPRIORITYR + 0x1f: + { + int i, irq = offset - GICR_IPRIORITYR; + + for (i = irq; i < irq + 4; i++, value >>= 8) { + gicr_write_ipriorityr(cs, attrs, i, value); + } + gicv3_redist_update(cs); + return MEMTX_OK; + } + case GICR_ICFGR0: + /* Register is all RAZ/WI or RAO/WI bits */ + return MEMTX_OK; + case GICR_ICFGR1: + { + uint32_t mask; + + /* Since our edge_trigger bitmap is one bit per irq, our input + * 32-bits will compress down into 16 bits which we need + * to write into the bitmap. + */ + value = half_unshuffle32(value >> 1) << 16; + mask = mask_group(cs, attrs) & 0xffff0000U; + + cs->edge_trigger &= ~mask; + cs->edge_trigger |= (value & mask); + + gicv3_redist_update(cs); + return MEMTX_OK; + } + case GICR_IGRPMODR0: + if ((cs->gic->gicd_ctlr & GICD_CTLR_DS) || !attrs.secure) { + /* RAZ/WI if security disabled, or if + * security enabled and this is an NS access + */ + return MEMTX_OK; + } + cs->gicr_igrpmodr0 = value; + gicv3_redist_update(cs); + return MEMTX_OK; + case GICR_NSACR: + if ((cs->gic->gicd_ctlr & GICD_CTLR_DS) || !attrs.secure) { + /* RAZ/WI if security disabled, or if + * security enabled and this is an NS access + */ + return MEMTX_OK; + } + cs->gicr_nsacr = value; + /* no update required as this only affects access permission checks */ + return MEMTX_OK; + case GICR_IIDR: + case GICR_TYPER: + case GICR_IDREGS ... GICR_IDREGS + 0x1f: + /* RO registers, ignore the write */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid guest write to RO register at offset " + TARGET_FMT_plx "\n", __func__, offset); + return MEMTX_OK; + default: + return MEMTX_ERROR; + } +} + +static MemTxResult gicr_readll(GICv3CPUState *cs, hwaddr offset, + uint64_t *data, MemTxAttrs attrs) +{ + switch (offset) { + case GICR_TYPER: + *data = cs->gicr_typer; + return MEMTX_OK; + case GICR_PROPBASER: + *data = cs->gicr_propbaser; + return MEMTX_OK; + case GICR_PENDBASER: + *data = cs->gicr_pendbaser; + return MEMTX_OK; + default: + return MEMTX_ERROR; + } +} + +static MemTxResult gicr_writell(GICv3CPUState *cs, hwaddr offset, + uint64_t value, MemTxAttrs attrs) +{ + switch (offset) { + case GICR_PROPBASER: + cs->gicr_propbaser = value; + return MEMTX_OK; + case GICR_PENDBASER: + cs->gicr_pendbaser = value; + return MEMTX_OK; + case GICR_TYPER: + /* RO register, ignore the write */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid guest write to RO register at offset " + TARGET_FMT_plx "\n", __func__, offset); + return MEMTX_OK; + default: + return MEMTX_ERROR; + } +} + +MemTxResult gicv3_redist_read(void *opaque, hwaddr offset, uint64_t *data, + unsigned size, MemTxAttrs attrs) +{ + GICv3State *s = opaque; + GICv3CPUState *cs; + MemTxResult r; + int cpuidx; + + assert((offset & (size - 1)) == 0); + + /* This region covers all the redistributor pages; there are + * (for GICv3) two 64K pages per CPU. At the moment they are + * all contiguous (ie in this one region), though we might later + * want to allow splitting of redistributor pages into several + * blocks so we can support more CPUs. + */ + cpuidx = offset / 0x20000; + offset %= 0x20000; + assert(cpuidx < s->num_cpu); + + cs = &s->cpu[cpuidx]; + + switch (size) { + case 1: + r = gicr_readb(cs, offset, data, attrs); + break; + case 4: + r = gicr_readl(cs, offset, data, attrs); + break; + case 8: + r = gicr_readll(cs, offset, data, attrs); + break; + default: + r = MEMTX_ERROR; + break; + } + + if (r == MEMTX_ERROR) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid guest read at offset " TARGET_FMT_plx + "size %u\n", __func__, offset, size); + trace_gicv3_redist_badread(gicv3_redist_affid(cs), offset, + size, attrs.secure); + } else { + trace_gicv3_redist_read(gicv3_redist_affid(cs), offset, *data, + size, attrs.secure); + } + return r; +} + +MemTxResult gicv3_redist_write(void *opaque, hwaddr offset, uint64_t data, + unsigned size, MemTxAttrs attrs) +{ + GICv3State *s = opaque; + GICv3CPUState *cs; + MemTxResult r; + int cpuidx; + + assert((offset & (size - 1)) == 0); + + /* This region covers all the redistributor pages; there are + * (for GICv3) two 64K pages per CPU. At the moment they are + * all contiguous (ie in this one region), though we might later + * want to allow splitting of redistributor pages into several + * blocks so we can support more CPUs. + */ + cpuidx = offset / 0x20000; + offset %= 0x20000; + assert(cpuidx < s->num_cpu); + + cs = &s->cpu[cpuidx]; + + switch (size) { + case 1: + r = gicr_writeb(cs, offset, data, attrs); + break; + case 4: + r = gicr_writel(cs, offset, data, attrs); + break; + case 8: + r = gicr_writell(cs, offset, data, attrs); + break; + default: + r = MEMTX_ERROR; + break; + } + + if (r == MEMTX_ERROR) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid guest write at offset " TARGET_FMT_plx + "size %u\n", __func__, offset, size); + trace_gicv3_redist_badwrite(gicv3_redist_affid(cs), offset, data, + size, attrs.secure); + } else { + trace_gicv3_redist_write(gicv3_redist_affid(cs), offset, data, + size, attrs.secure); + } + return r; +} + +void gicv3_redist_set_irq(GICv3CPUState *cs, int irq, int level) +{ + /* Update redistributor state for a change in an external PPI input line */ + if (level == extract32(cs->level, irq, 1)) { + return; + } + + trace_gicv3_redist_set_irq(gicv3_redist_affid(cs), irq, level); + + cs->level = deposit32(cs->level, irq, 1, level); + + if (level) { + /* 0->1 edges latch the pending bit for edge-triggered interrupts */ + if (extract32(cs->edge_trigger, irq, 1)) { + cs->gicr_ipendr0 = deposit32(cs->gicr_ipendr0, irq, 1, 1); + } + } + + gicv3_redist_update(cs); +} + +void gicv3_redist_send_sgi(GICv3CPUState *cs, int grp, int irq, bool ns) +{ + /* Update redistributor state for a generated SGI */ + int irqgrp = gicv3_irq_group(cs->gic, cs, irq); + + /* If we are asked for a Secure Group 1 SGI and it's actually + * configured as Secure Group 0 this is OK (subject to the usual + * NSACR checks). + */ + if (grp == GICV3_G1 && irqgrp == GICV3_G0) { + grp = GICV3_G0; + } + + if (grp != irqgrp) { + return; + } + + if (ns && !(cs->gic->gicd_ctlr & GICD_CTLR_DS)) { + /* If security is enabled we must test the NSACR bits */ + int nsaccess = gicr_ns_access(cs, irq); + + if ((irqgrp == GICV3_G0 && nsaccess < 1) || + (irqgrp == GICV3_G1 && nsaccess < 2)) { + return; + } + } + + /* OK, we can accept the SGI */ + trace_gicv3_redist_send_sgi(gicv3_redist_affid(cs), irq); + cs->gicr_ipendr0 = deposit32(cs->gicr_ipendr0, irq, 1, 1); + gicv3_redist_update(cs); +} diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 669e82adf..06d8db6bd 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -13,11 +13,13 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu-common.h" +#include "cpu.h" #include "hw/sysbus.h" #include "qemu/timer.h" #include "hw/arm/arm.h" #include "exec/address-spaces.h" #include "gic_internal.h" +#include "qemu/log.h" typedef struct { GICState gic; @@ -185,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; @@ -220,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; @@ -347,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/aspeed_vic.c b/hw/intc/aspeed_vic.c index 19a0ff748..2370e7485 100644 --- a/hw/intc/aspeed_vic.c +++ b/hw/intc/aspeed_vic.c @@ -28,9 +28,9 @@ */ #include "qemu/osdep.h" -#include <inttypes.h> #include "hw/intc/aspeed_vic.h" #include "qemu/bitops.h" +#include "qemu/log.h" #include "trace.h" #define AVIC_NEW_BASE_OFFSET 0x80 diff --git a/hw/intc/bcm2835_ic.c b/hw/intc/bcm2835_ic.c index 80513b28f..00d25306f 100644 --- a/hw/intc/bcm2835_ic.c +++ b/hw/intc/bcm2835_ic.c @@ -14,6 +14,7 @@ #include "qemu/osdep.h" #include "hw/intc/bcm2835_ic.h" +#include "qemu/log.h" #define GPU_IRQS 64 #define ARM_IRQS 8 diff --git a/hw/intc/bcm2836_control.c b/hw/intc/bcm2836_control.c index d0271810c..cfa5bc736 100644 --- a/hw/intc/bcm2836_control.c +++ b/hw/intc/bcm2836_control.c @@ -15,6 +15,7 @@ #include "qemu/osdep.h" #include "hw/intc/bcm2836_control.h" +#include "qemu/log.h" #define REG_GPU_ROUTE 0x0c #define REG_TIMERCONTROL 0x40 diff --git a/hw/intc/etraxfs_pic.c b/hw/intc/etraxfs_pic.c index 48f947706..64a6f4b4b 100644 --- a/hw/intc/etraxfs_pic.c +++ b/hw/intc/etraxfs_pic.c @@ -146,19 +146,19 @@ static void irq_handler(void *opaque, int irq, int level) pic_update(fs); } -static int etraxfs_pic_init(SysBusDevice *sbd) +static void etraxfs_pic_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - struct etrax_pic *s = ETRAX_FS_PIC(dev); + DeviceState *dev = DEVICE(obj); + struct etrax_pic *s = ETRAX_FS_PIC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); qdev_init_gpio_in(dev, irq_handler, 32); sysbus_init_irq(sbd, &s->parent_irq); sysbus_init_irq(sbd, &s->parent_nmi); - memory_region_init_io(&s->mmio, OBJECT(s), &pic_ops, s, + memory_region_init_io(&s->mmio, obj, &pic_ops, s, "etraxfs-pic", R_MAX * 4); sysbus_init_mmio(sbd, &s->mmio); - return 0; } static Property etraxfs_pic_properties[] = { @@ -169,9 +169,7 @@ static Property etraxfs_pic_properties[] = { static void etraxfs_pic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = etraxfs_pic_init; dc->props = etraxfs_pic_properties; /* * Note: pointer property "interrupt_vector" may remain null, thus @@ -183,6 +181,7 @@ static const TypeInfo etraxfs_pic_info = { .name = TYPE_ETRAX_FS_PIC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(struct etrax_pic), + .instance_init = etraxfs_pic_init, .class_init = etraxfs_pic_class_init, }; diff --git a/hw/intc/exynos4210_combiner.c b/hw/intc/exynos4210_combiner.c index dc0c90326..f19a7062b 100644 --- a/hw/intc/exynos4210_combiner.c +++ b/hw/intc/exynos4210_combiner.c @@ -406,10 +406,11 @@ static const MemoryRegionOps exynos4210_combiner_ops = { /* * Internal Combiner initialization. */ -static int exynos4210_combiner_init(SysBusDevice *sbd) +static void exynos4210_combiner_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - Exynos4210CombinerState *s = EXYNOS4210_COMBINER(dev); + DeviceState *dev = DEVICE(obj); + Exynos4210CombinerState *s = EXYNOS4210_COMBINER(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); unsigned int i; /* Allocate general purpose input signals and connect a handler to each of @@ -421,11 +422,9 @@ static int exynos4210_combiner_init(SysBusDevice *sbd) sysbus_init_irq(sbd, &s->output_irq[i]); } - memory_region_init_io(&s->iomem, OBJECT(s), &exynos4210_combiner_ops, s, + memory_region_init_io(&s->iomem, obj, &exynos4210_combiner_ops, s, "exynos4210-combiner", IIC_REGION_SIZE); sysbus_init_mmio(sbd, &s->iomem); - - return 0; } static Property exynos4210_combiner_properties[] = { @@ -436,9 +435,7 @@ static Property exynos4210_combiner_properties[] = { static void exynos4210_combiner_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = exynos4210_combiner_init; dc->reset = exynos4210_combiner_reset; dc->props = exynos4210_combiner_properties; dc->vmsd = &vmstate_exynos4210_combiner; @@ -448,6 +445,7 @@ static const TypeInfo exynos4210_combiner_info = { .name = TYPE_EXYNOS4210_COMBINER, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Exynos4210CombinerState), + .instance_init = exynos4210_combiner_init, .class_init = exynos4210_combiner_class_init, }; diff --git a/hw/intc/exynos4210_gic.c b/hw/intc/exynos4210_gic.c index 4f7e89f7b..fd7a8f305 100644 --- a/hw/intc/exynos4210_gic.c +++ b/hw/intc/exynos4210_gic.c @@ -281,10 +281,11 @@ static void exynos4210_gic_set_irq(void *opaque, int irq, int level) qemu_set_irq(qdev_get_gpio_in(s->gic, irq), level); } -static int exynos4210_gic_init(SysBusDevice *sbd) +static void exynos4210_gic_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - Exynos4210GicState *s = EXYNOS4210_GIC(dev); + DeviceState *dev = DEVICE(obj); + Exynos4210GicState *s = EXYNOS4210_GIC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); uint32_t i; const char cpu_prefix[] = "exynos4210-gic-alias_cpu"; const char dist_prefix[] = "exynos4210-gic-alias_dist"; @@ -305,15 +306,15 @@ static int exynos4210_gic_init(SysBusDevice *sbd) qdev_init_gpio_in(dev, exynos4210_gic_set_irq, EXYNOS4210_GIC_NIRQ - 32); - memory_region_init(&s->cpu_container, OBJECT(s), "exynos4210-cpu-container", + memory_region_init(&s->cpu_container, obj, "exynos4210-cpu-container", EXYNOS4210_EXT_GIC_CPU_REGION_SIZE); - memory_region_init(&s->dist_container, OBJECT(s), "exynos4210-dist-container", + memory_region_init(&s->dist_container, obj, "exynos4210-dist-container", EXYNOS4210_EXT_GIC_DIST_REGION_SIZE); for (i = 0; i < s->num_cpu; i++) { /* Map CPU interface per SMP Core */ sprintf(cpu_alias_name, "%s%x", cpu_prefix, i); - memory_region_init_alias(&s->cpu_alias[i], OBJECT(s), + memory_region_init_alias(&s->cpu_alias[i], obj, cpu_alias_name, sysbus_mmio_get_region(busdev, 1), 0, @@ -323,7 +324,7 @@ static int exynos4210_gic_init(SysBusDevice *sbd) /* Map Distributor per SMP Core */ sprintf(dist_alias_name, "%s%x", dist_prefix, i); - memory_region_init_alias(&s->dist_alias[i], OBJECT(s), + memory_region_init_alias(&s->dist_alias[i], obj, dist_alias_name, sysbus_mmio_get_region(busdev, 0), 0, @@ -334,8 +335,6 @@ static int exynos4210_gic_init(SysBusDevice *sbd) sysbus_init_mmio(sbd, &s->cpu_container); sysbus_init_mmio(sbd, &s->dist_container); - - return 0; } static Property exynos4210_gic_properties[] = { @@ -346,9 +345,7 @@ static Property exynos4210_gic_properties[] = { static void exynos4210_gic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = exynos4210_gic_init; dc->props = exynos4210_gic_properties; } @@ -356,6 +353,7 @@ static const TypeInfo exynos4210_gic_info = { .name = TYPE_EXYNOS4210_GIC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Exynos4210GicState), + .instance_init = exynos4210_gic_init, .class_init = exynos4210_gic_class_init, }; @@ -430,9 +428,16 @@ static void exynos4210_irq_gate_reset(DeviceState *d) /* * IRQ Gate initialization. */ -static int exynos4210_irq_gate_init(SysBusDevice *sbd) +static void exynos4210_irq_gate_init(Object *obj) +{ + Exynos4210IRQGateState *s = EXYNOS4210_IRQ_GATE(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + sysbus_init_irq(sbd, &s->out); +} + +static void exynos4210_irq_gate_realize(DeviceState *dev, Error **errp) { - DeviceState *dev = DEVICE(sbd); Exynos4210IRQGateState *s = EXYNOS4210_IRQ_GATE(dev); /* Allocate general purpose input signals and connect a handler to each of @@ -440,27 +445,23 @@ static int exynos4210_irq_gate_init(SysBusDevice *sbd) qdev_init_gpio_in(dev, exynos4210_irq_gate_handler, s->n_in); s->level = g_malloc0(s->n_in * sizeof(*s->level)); - - sysbus_init_irq(sbd, &s->out); - - return 0; } static void exynos4210_irq_gate_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = exynos4210_irq_gate_init; dc->reset = exynos4210_irq_gate_reset; dc->vmsd = &vmstate_exynos4210_irq_gate; dc->props = exynos4210_irq_gate_properties; + dc->realize = exynos4210_irq_gate_realize; } static const TypeInfo exynos4210_irq_gate_info = { .name = TYPE_EXYNOS4210_IRQ_GATE, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Exynos4210IRQGateState), + .instance_init = exynos4210_irq_gate_init, .class_init = exynos4210_irq_gate_class_init, }; diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h index 20c1e8a24..3f311740d 100644 --- a/hw/intc/gic_internal.h +++ b/hw/intc/gic_internal.h @@ -100,4 +100,4 @@ static inline bool gic_test_pending(GICState *s, int irq, int cm) } } -#endif /* !QEMU_ARM_GIC_INTERNAL_H */ +#endif /* QEMU_ARM_GIC_INTERNAL_H */ diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h new file mode 100644 index 000000000..8f3567eda --- /dev/null +++ b/hw/intc/gicv3_internal.h @@ -0,0 +1,331 @@ +/* + * ARM GICv3 support - internal interfaces + * + * Copyright (c) 2012 Linaro Limited + * Copyright (c) 2015 Huawei. + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * Written by Peter Maydell + * Reworked for GICv3 by Shlomo Pongratz and Pavel Fedin + * + * 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/>. + */ + +#ifndef QEMU_ARM_GICV3_INTERNAL_H +#define QEMU_ARM_GICV3_INTERNAL_H + +#include "hw/intc/arm_gicv3_common.h" + +/* Distributor registers, as offsets from the distributor base address */ +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_STATUSR 0x0010 +#define GICD_SETSPI_NSR 0x0040 +#define GICD_CLRSPI_NSR 0x0048 +#define GICD_SETSPI_SR 0x0050 +#define GICD_CLRSPI_SR 0x0058 +#define GICD_SEIR 0x0068 +#define GICD_IGROUPR 0x0080 +#define GICD_ISENABLER 0x0100 +#define GICD_ICENABLER 0x0180 +#define GICD_ISPENDR 0x0200 +#define GICD_ICPENDR 0x0280 +#define GICD_ISACTIVER 0x0300 +#define GICD_ICACTIVER 0x0380 +#define GICD_IPRIORITYR 0x0400 +#define GICD_ITARGETSR 0x0800 +#define GICD_ICFGR 0x0C00 +#define GICD_IGRPMODR 0x0D00 +#define GICD_NSACR 0x0E00 +#define GICD_SGIR 0x0F00 +#define GICD_CPENDSGIR 0x0F10 +#define GICD_SPENDSGIR 0x0F20 +#define GICD_IROUTER 0x6000 +#define GICD_IDREGS 0xFFD0 + +/* GICD_CTLR fields */ +#define GICD_CTLR_EN_GRP0 (1U << 0) +#define GICD_CTLR_EN_GRP1NS (1U << 1) /* GICv3 5.3.20 */ +#define GICD_CTLR_EN_GRP1S (1U << 2) +#define GICD_CTLR_EN_GRP1_ALL (GICD_CTLR_EN_GRP1NS | GICD_CTLR_EN_GRP1S) +/* Bit 4 is ARE if the system doesn't support TrustZone, ARE_S otherwise */ +#define GICD_CTLR_ARE (1U << 4) +#define GICD_CTLR_ARE_S (1U << 4) +#define GICD_CTLR_ARE_NS (1U << 5) +#define GICD_CTLR_DS (1U << 6) +#define GICD_CTLR_E1NWF (1U << 7) +#define GICD_CTLR_RWP (1U << 31) + +/* + * Redistributor frame offsets from RD_base + */ +#define GICR_SGI_OFFSET 0x10000 + +/* + * Redistributor registers, offsets from RD_base + */ +#define GICR_CTLR 0x0000 +#define GICR_IIDR 0x0004 +#define GICR_TYPER 0x0008 +#define GICR_STATUSR 0x0010 +#define GICR_WAKER 0x0014 +#define GICR_SETLPIR 0x0040 +#define GICR_CLRLPIR 0x0048 +#define GICR_PROPBASER 0x0070 +#define GICR_PENDBASER 0x0078 +#define GICR_INVLPIR 0x00A0 +#define GICR_INVALLR 0x00B0 +#define GICR_SYNCR 0x00C0 +#define GICR_IDREGS 0xFFD0 + +/* SGI and PPI Redistributor registers, offsets from RD_base */ +#define GICR_IGROUPR0 (GICR_SGI_OFFSET + 0x0080) +#define GICR_ISENABLER0 (GICR_SGI_OFFSET + 0x0100) +#define GICR_ICENABLER0 (GICR_SGI_OFFSET + 0x0180) +#define GICR_ISPENDR0 (GICR_SGI_OFFSET + 0x0200) +#define GICR_ICPENDR0 (GICR_SGI_OFFSET + 0x0280) +#define GICR_ISACTIVER0 (GICR_SGI_OFFSET + 0x0300) +#define GICR_ICACTIVER0 (GICR_SGI_OFFSET + 0x0380) +#define GICR_IPRIORITYR (GICR_SGI_OFFSET + 0x0400) +#define GICR_ICFGR0 (GICR_SGI_OFFSET + 0x0C00) +#define GICR_ICFGR1 (GICR_SGI_OFFSET + 0x0C04) +#define GICR_IGRPMODR0 (GICR_SGI_OFFSET + 0x0D00) +#define GICR_NSACR (GICR_SGI_OFFSET + 0x0E00) + +#define GICR_CTLR_ENABLE_LPIS (1U << 0) +#define GICR_CTLR_RWP (1U << 3) +#define GICR_CTLR_DPG0 (1U << 24) +#define GICR_CTLR_DPG1NS (1U << 25) +#define GICR_CTLR_DPG1S (1U << 26) +#define GICR_CTLR_UWP (1U << 31) + +#define GICR_TYPER_PLPIS (1U << 0) +#define GICR_TYPER_VLPIS (1U << 1) +#define GICR_TYPER_DIRECTLPI (1U << 3) +#define GICR_TYPER_LAST (1U << 4) +#define GICR_TYPER_DPGS (1U << 5) +#define GICR_TYPER_PROCNUM (0xFFFFU << 8) +#define GICR_TYPER_COMMONLPIAFF (0x3 << 24) +#define GICR_TYPER_AFFINITYVALUE (0xFFFFFFFFULL << 32) + +#define GICR_WAKER_ProcessorSleep (1U << 1) +#define GICR_WAKER_ChildrenAsleep (1U << 2) + +#define GICR_PROPBASER_OUTER_CACHEABILITY_MASK (7ULL << 56) +#define GICR_PROPBASER_ADDR_MASK (0xfffffffffULL << 12) +#define GICR_PROPBASER_SHAREABILITY_MASK (3U << 10) +#define GICR_PROPBASER_CACHEABILITY_MASK (7U << 7) +#define GICR_PROPBASER_IDBITS_MASK (0x1f) + +#define GICR_PENDBASER_PTZ (1ULL << 62) +#define GICR_PENDBASER_OUTER_CACHEABILITY_MASK (7ULL << 56) +#define GICR_PENDBASER_ADDR_MASK (0xffffffffULL << 16) +#define GICR_PENDBASER_SHAREABILITY_MASK (3U << 10) +#define GICR_PENDBASER_CACHEABILITY_MASK (7U << 7) + +#define ICC_CTLR_EL1_CBPR (1U << 0) +#define ICC_CTLR_EL1_EOIMODE (1U << 1) +#define ICC_CTLR_EL1_PMHE (1U << 6) +#define ICC_CTLR_EL1_PRIBITS_SHIFT 8 +#define ICC_CTLR_EL1_IDBITS_SHIFT 11 +#define ICC_CTLR_EL1_SEIS (1U << 14) +#define ICC_CTLR_EL1_A3V (1U << 15) + +#define ICC_PMR_PRIORITY_MASK 0xff +#define ICC_BPR_BINARYPOINT_MASK 0x07 +#define ICC_IGRPEN_ENABLE 0x01 + +#define ICC_CTLR_EL3_CBPR_EL1S (1U << 0) +#define ICC_CTLR_EL3_CBPR_EL1NS (1U << 1) +#define ICC_CTLR_EL3_EOIMODE_EL3 (1U << 2) +#define ICC_CTLR_EL3_EOIMODE_EL1S (1U << 3) +#define ICC_CTLR_EL3_EOIMODE_EL1NS (1U << 4) +#define ICC_CTLR_EL3_RM (1U << 5) +#define ICC_CTLR_EL3_PMHE (1U << 6) +#define ICC_CTLR_EL3_PRIBITS_SHIFT 8 +#define ICC_CTLR_EL3_IDBITS_SHIFT 11 +#define ICC_CTLR_EL3_SEIS (1U << 14) +#define ICC_CTLR_EL3_A3V (1U << 15) +#define ICC_CTLR_EL3_NDS (1U << 17) + +/* Special interrupt IDs */ +#define INTID_SECURE 1020 +#define INTID_NONSECURE 1021 +#define INTID_SPURIOUS 1023 + +/* Functions internal to the emulated GICv3 */ + +/** + * gicv3_redist_update: + * @cs: GICv3CPUState for this redistributor + * + * Recalculate the highest priority pending interrupt after a + * change to redistributor state, and inform the CPU accordingly. + */ +void gicv3_redist_update(GICv3CPUState *cs); + +/** + * gicv3_update: + * @s: GICv3State + * @start: first interrupt whose state changed + * @len: length of the range of interrupts whose state changed + * + * Recalculate the highest priority pending interrupts after a + * change to the distributor state affecting @len interrupts + * starting at @start, and inform the CPUs accordingly. + */ +void gicv3_update(GICv3State *s, int start, int len); + +/** + * gicv3_full_update_noirqset: + * @s: GICv3State + * + * Recalculate the cached information about highest priority + * pending interrupts, but don't inform the CPUs. This should be + * called after an incoming migration has loaded new state. + */ +void gicv3_full_update_noirqset(GICv3State *s); + +/** + * gicv3_full_update: + * @s: GICv3State + * + * Recalculate the highest priority pending interrupts after + * a change that could affect the status of all interrupts, + * and inform the CPUs accordingly. + */ +void gicv3_full_update(GICv3State *s); +MemTxResult gicv3_dist_read(void *opaque, hwaddr offset, uint64_t *data, + unsigned size, MemTxAttrs attrs); +MemTxResult gicv3_dist_write(void *opaque, hwaddr addr, uint64_t data, + unsigned size, MemTxAttrs attrs); +MemTxResult gicv3_redist_read(void *opaque, hwaddr offset, uint64_t *data, + unsigned size, MemTxAttrs attrs); +MemTxResult gicv3_redist_write(void *opaque, hwaddr offset, uint64_t data, + unsigned size, MemTxAttrs attrs); +void gicv3_dist_set_irq(GICv3State *s, int irq, int level); +void gicv3_redist_set_irq(GICv3CPUState *cs, int irq, int level); +void gicv3_redist_send_sgi(GICv3CPUState *cs, int grp, int irq, bool ns); +void gicv3_init_cpuif(GICv3State *s); + +/** + * gicv3_cpuif_update: + * @cs: GICv3CPUState for the CPU to update + * + * Recalculate whether to assert the IRQ or FIQ lines after a change + * to the current highest priority pending interrupt, the CPU's + * current running priority or the CPU's current exception level or + * security state. + */ +void gicv3_cpuif_update(GICv3CPUState *cs); + +static inline uint32_t gicv3_iidr(void) +{ + /* Return the Implementer Identification Register value + * for the emulated GICv3, as reported in GICD_IIDR and GICR_IIDR. + * + * We claim to be an ARM r0p0 with a zero ProductID. + * This is the same as an r0p0 GIC-500. + */ + return 0x43b; +} + +static inline uint32_t gicv3_idreg(int regoffset) +{ + /* Return the value of the CoreSight ID register at the specified + * offset from the first ID register (as found in the distributor + * and redistributor register banks). + * These values indicate an ARM implementation of a GICv3. + */ + static const uint8_t gicd_ids[] = { + 0x44, 0x00, 0x00, 0x00, 0x92, 0xB4, 0x3B, 0x00, 0x0D, 0xF0, 0x05, 0xB1 + }; + return gicd_ids[regoffset / 4]; +} + +/** + * gicv3_irq_group: + * + * Return the group which this interrupt is configured as (GICV3_G0, + * GICV3_G1 or GICV3_G1NS). + */ +static inline int gicv3_irq_group(GICv3State *s, GICv3CPUState *cs, int irq) +{ + bool grpbit, grpmodbit; + + if (irq < GIC_INTERNAL) { + grpbit = extract32(cs->gicr_igroupr0, irq, 1); + grpmodbit = extract32(cs->gicr_igrpmodr0, irq, 1); + } else { + grpbit = gicv3_gicd_group_test(s, irq); + grpmodbit = gicv3_gicd_grpmod_test(s, irq); + } + if (grpbit) { + return GICV3_G1NS; + } + if (s->gicd_ctlr & GICD_CTLR_DS) { + return GICV3_G0; + } + return grpmodbit ? GICV3_G1 : GICV3_G0; +} + +/** + * gicv3_redist_affid: + * + * Return the 32-bit affinity ID of the CPU connected to this redistributor + */ +static inline uint32_t gicv3_redist_affid(GICv3CPUState *cs) +{ + return cs->gicr_typer >> 32; +} + +/** + * gicv3_cache_target_cpustate: + * + * Update the cached CPU state corresponding to the target for this interrupt + * (which is kept in s->gicd_irouter_target[]). + */ +static inline void gicv3_cache_target_cpustate(GICv3State *s, int irq) +{ + GICv3CPUState *cs = NULL; + int i; + uint32_t tgtaff = extract64(s->gicd_irouter[irq], 0, 24) | + extract64(s->gicd_irouter[irq], 32, 8) << 24; + + for (i = 0; i < s->num_cpu; i++) { + if (s->cpu[i].gicr_typer >> 32 == tgtaff) { + cs = &s->cpu[i]; + break; + } + } + + s->gicd_irouter_target[irq] = cs; +} + +/** + * gicv3_cache_all_target_cpustates: + * + * Populate the entire cache of CPU state pointers for interrupt targets + * (eg after inbound migration or CPU reset) + */ +static inline void gicv3_cache_all_target_cpustates(GICv3State *s) +{ + int irq; + + for (irq = GIC_INTERNAL; irq < GICV3_MAXIRQ; irq++) { + gicv3_cache_target_cpustate(s, irq); + } +} + +#endif /* QEMU_ARM_GICV3_INTERNAL_H */ diff --git a/hw/intc/grlib_irqmp.c b/hw/intc/grlib_irqmp.c index f5ca8f752..ac7e63f38 100644 --- a/hw/intc/grlib_irqmp.c +++ b/hw/intc/grlib_irqmp.c @@ -31,6 +31,7 @@ #include "hw/sparc/grlib.h" #include "trace.h" +#include "qapi/error.h" #define IRQMP_MAX_CPU 16 #define IRQMP_REG_SIZE 256 /* Size of memory mapped registers */ @@ -323,23 +324,27 @@ static void grlib_irqmp_reset(DeviceState *d) irqmp->state->parent = irqmp; } -static int grlib_irqmp_init(SysBusDevice *dev) +static void grlib_irqmp_init(Object *obj) { - IRQMP *irqmp = GRLIB_IRQMP(dev); - - /* Check parameters */ - if (irqmp->set_pil_in == NULL) { - return -1; - } + IRQMP *irqmp = GRLIB_IRQMP(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); - memory_region_init_io(&irqmp->iomem, OBJECT(dev), &grlib_irqmp_ops, irqmp, + memory_region_init_io(&irqmp->iomem, obj, &grlib_irqmp_ops, irqmp, "irqmp", IRQMP_REG_SIZE); irqmp->state = g_malloc0(sizeof *irqmp->state); sysbus_init_mmio(dev, &irqmp->iomem); +} - return 0; +static void grlib_irqmp_realize(DeviceState *dev, Error **errp) +{ + IRQMP *irqmp = GRLIB_IRQMP(dev); + + /* Check parameters */ + if (irqmp->set_pil_in == NULL) { + error_setg(errp, "set_pil_in cannot be NULL."); + } } static Property grlib_irqmp_properties[] = { @@ -351,19 +356,19 @@ static Property grlib_irqmp_properties[] = { static void grlib_irqmp_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = grlib_irqmp_init; dc->reset = grlib_irqmp_reset; dc->props = grlib_irqmp_properties; /* Reason: pointer properties "set_pil_in", "set_pil_in_opaque" */ dc->cannot_instantiate_with_device_add_yet = true; + dc->realize = grlib_irqmp_realize; } static const TypeInfo grlib_irqmp_info = { .name = TYPE_GRLIB_IRQMP, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(IRQMP), + .instance_init = grlib_irqmp_init, .class_init = grlib_irqmp_class_init, }; diff --git a/hw/intc/i8259.c b/hw/intc/i8259.c index bb43669b9..c2607a586 100644 --- a/hw/intc/i8259.c +++ b/hw/intc/i8259.c @@ -27,6 +27,7 @@ #include "hw/isa/isa.h" #include "monitor/monitor.h" #include "qemu/timer.h" +#include "qemu/log.h" #include "hw/isa/i8259_internal.h" /* debug PIC */ diff --git a/hw/intc/imx_avic.c b/hw/intc/imx_avic.c index 702765577..813e587a6 100644 --- a/hw/intc/imx_avic.c +++ b/hw/intc/imx_avic.c @@ -17,6 +17,7 @@ #include "qemu/osdep.h" #include "hw/intc/imx_avic.h" +#include "qemu/log.h" #ifndef DEBUG_IMX_AVIC #define DEBUG_IMX_AVIC 0 @@ -321,28 +322,26 @@ static void imx_avic_reset(DeviceState *dev) memset(s->prio, 0, sizeof s->prio); } -static int imx_avic_init(SysBusDevice *sbd) +static void imx_avic_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - IMXAVICState *s = IMX_AVIC(dev); + DeviceState *dev = DEVICE(obj); + IMXAVICState *s = IMX_AVIC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - memory_region_init_io(&s->iomem, OBJECT(s), &imx_avic_ops, s, + memory_region_init_io(&s->iomem, obj, &imx_avic_ops, s, TYPE_IMX_AVIC, 0x1000); sysbus_init_mmio(sbd, &s->iomem); qdev_init_gpio_in(dev, imx_avic_set_irq, IMX_AVIC_NUM_IRQS); sysbus_init_irq(sbd, &s->irq); sysbus_init_irq(sbd, &s->fiq); - - return 0; } static void imx_avic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = imx_avic_init; + dc->vmsd = &vmstate_imx_avic; dc->reset = imx_avic_reset; dc->desc = "i.MX Advanced Vector Interrupt Controller"; @@ -352,6 +351,7 @@ static const TypeInfo imx_avic_info = { .name = TYPE_IMX_AVIC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(IMXAVICState), + .instance_init = imx_avic_init, .class_init = imx_avic_class_init, }; diff --git a/hw/intc/ioapic.c b/hw/intc/ioapic.c index 378e663f6..31791b098 100644 --- a/hw/intc/ioapic.c +++ b/hw/intc/ioapic.c @@ -21,13 +21,18 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "monitor/monitor.h" #include "hw/hw.h" #include "hw/i386/pc.h" +#include "hw/i386/apic.h" #include "hw/i386/ioapic.h" #include "hw/i386/ioapic_internal.h" #include "include/hw/pci/msi.h" #include "sysemu/kvm.h" +#include "target-i386/cpu.h" +#include "hw/i386/apic-msidef.h" +#include "hw/i386/x86-iommu.h" //#define DEBUG_IOAPIC @@ -47,16 +52,56 @@ static IOAPICCommonState *ioapics[MAX_IOAPICS]; /* global variable from ioapic_common.c */ extern int ioapic_no; +struct ioapic_entry_info { + /* fields parsed from IOAPIC entries */ + uint8_t masked; + uint8_t trig_mode; + uint16_t dest_idx; + uint8_t dest_mode; + uint8_t delivery_mode; + uint8_t vector; + + /* MSI message generated from above parsed fields */ + uint32_t addr; + uint32_t data; +}; + +static void ioapic_entry_parse(uint64_t entry, struct ioapic_entry_info *info) +{ + memset(info, 0, sizeof(*info)); + info->masked = (entry >> IOAPIC_LVT_MASKED_SHIFT) & 1; + info->trig_mode = (entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1; + /* + * By default, this would be dest_id[8] + reserved[8]. When IR + * is enabled, this would be interrupt_index[15] + + * interrupt_format[1]. This field never means anything, but + * only used to generate corresponding MSI. + */ + info->dest_idx = (entry >> IOAPIC_LVT_DEST_IDX_SHIFT) & 0xffff; + info->dest_mode = (entry >> IOAPIC_LVT_DEST_MODE_SHIFT) & 1; + info->delivery_mode = (entry >> IOAPIC_LVT_DELIV_MODE_SHIFT) \ + & IOAPIC_DM_MASK; + if (info->delivery_mode == IOAPIC_DM_EXTINT) { + info->vector = pic_read_irq(isa_pic); + } else { + info->vector = entry & IOAPIC_VECTOR_MASK; + } + + info->addr = APIC_DEFAULT_ADDRESS | \ + (info->dest_idx << MSI_ADDR_DEST_IDX_SHIFT) | \ + (info->dest_mode << MSI_ADDR_DEST_MODE_SHIFT); + info->data = (info->vector << MSI_DATA_VECTOR_SHIFT) | \ + (info->trig_mode << MSI_DATA_TRIGGER_SHIFT) | \ + (info->delivery_mode << MSI_DATA_DELIVERY_MODE_SHIFT); +} + static void ioapic_service(IOAPICCommonState *s) { + AddressSpace *ioapic_as = PC_MACHINE(qdev_get_machine())->ioapic_as; + struct ioapic_entry_info info; uint8_t i; - uint8_t trig_mode; - uint8_t vector; - uint8_t delivery_mode; uint32_t mask; uint64_t entry; - uint8_t dest; - uint8_t dest_mode; for (i = 0; i < IOAPIC_NUM_PINS; i++) { mask = 1 << i; @@ -64,40 +109,39 @@ static void ioapic_service(IOAPICCommonState *s) int coalesce = 0; entry = s->ioredtbl[i]; - if (!(entry & IOAPIC_LVT_MASKED)) { - trig_mode = ((entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1); - dest = entry >> IOAPIC_LVT_DEST_SHIFT; - dest_mode = (entry >> IOAPIC_LVT_DEST_MODE_SHIFT) & 1; - delivery_mode = - (entry >> IOAPIC_LVT_DELIV_MODE_SHIFT) & IOAPIC_DM_MASK; - if (trig_mode == IOAPIC_TRIGGER_EDGE) { + ioapic_entry_parse(entry, &info); + if (!info.masked) { + if (info.trig_mode == IOAPIC_TRIGGER_EDGE) { s->irr &= ~mask; } else { coalesce = s->ioredtbl[i] & IOAPIC_LVT_REMOTE_IRR; s->ioredtbl[i] |= IOAPIC_LVT_REMOTE_IRR; } - if (delivery_mode == IOAPIC_DM_EXTINT) { - vector = pic_read_irq(isa_pic); - } else { - vector = entry & IOAPIC_VECTOR_MASK; + + if (coalesce) { + /* We are level triggered interrupts, and the + * guest should be still working on previous one, + * so skip it. */ + continue; } + #ifdef CONFIG_KVM if (kvm_irqchip_is_split()) { - if (trig_mode == IOAPIC_TRIGGER_EDGE) { + if (info.trig_mode == IOAPIC_TRIGGER_EDGE) { kvm_set_irq(kvm_state, i, 1); kvm_set_irq(kvm_state, i, 0); } else { - if (!coalesce) { - kvm_set_irq(kvm_state, i, 1); - } + kvm_set_irq(kvm_state, i, 1); } continue; } -#else - (void)coalesce; #endif - apic_deliver_irq(dest, dest_mode, delivery_mode, vector, - trig_mode); + + /* No matter whether IR is enabled, we translate + * the IOAPIC message into a MSI one, and its + * address space will decide whether we need a + * translation. */ + stl_le_phys(ioapic_as, info.addr, info.data); } } } @@ -148,30 +192,11 @@ static void ioapic_update_kvm_routes(IOAPICCommonState *s) if (kvm_irqchip_is_split()) { for (i = 0; i < IOAPIC_NUM_PINS; i++) { - uint64_t entry = s->ioredtbl[i]; - uint8_t trig_mode; - uint8_t delivery_mode; - uint8_t dest; - uint8_t dest_mode; - uint64_t pin_polarity; MSIMessage msg; - - trig_mode = ((entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1); - dest = entry >> IOAPIC_LVT_DEST_SHIFT; - dest_mode = (entry >> IOAPIC_LVT_DEST_MODE_SHIFT) & 1; - pin_polarity = (entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1; - delivery_mode = - (entry >> IOAPIC_LVT_DELIV_MODE_SHIFT) & IOAPIC_DM_MASK; - - msg.address = APIC_DEFAULT_ADDRESS; - msg.address |= dest_mode << 2; - msg.address |= dest << 12; - - msg.data = entry & IOAPIC_VECTOR_MASK; - msg.data |= delivery_mode << APIC_DELIVERY_MODE_SHIFT; - msg.data |= pin_polarity << APIC_POLARITY_SHIFT; - msg.data |= trig_mode << APIC_TRIG_MODE_SHIFT; - + struct ioapic_entry_info info; + ioapic_entry_parse(s->ioredtbl[i], &info); + msg.address = info.addr; + msg.data = info.data; kvm_irqchip_update_msi_route(kvm_state, i, msg, NULL); } kvm_irqchip_commit_routes(kvm_state); @@ -179,6 +204,16 @@ static void ioapic_update_kvm_routes(IOAPICCommonState *s) #endif } +#ifdef CONFIG_KVM +static void ioapic_iec_notifier(void *private, bool global, + uint32_t index, uint32_t mask) +{ + IOAPICCommonState *s = (IOAPICCommonState *)private; + /* For simplicity, we just update all the routes */ + ioapic_update_kvm_routes(s); +} +#endif + void ioapic_eoi_broadcast(int vector) { IOAPICCommonState *s; @@ -235,7 +270,7 @@ ioapic_mem_read(void *opaque, hwaddr addr, unsigned int size) val = s->id << IOAPIC_ID_SHIFT; break; case IOAPIC_REG_VER: - val = IOAPIC_VERSION | + val = s->version | ((IOAPIC_NUM_PINS - 1) << IOAPIC_VER_ENTRIES_SHIFT); break; default: @@ -254,6 +289,34 @@ ioapic_mem_read(void *opaque, hwaddr addr, unsigned int size) return val; } +/* + * This is to satisfy the hack in Linux kernel. One hack of it is to + * simulate clearing the Remote IRR bit of IOAPIC entry using the + * following: + * + * "For IO-APIC's with EOI register, we use that to do an explicit EOI. + * Otherwise, we simulate the EOI message manually by changing the trigger + * mode to edge and then back to level, with RTE being masked during + * this." + * + * (See linux kernel __eoi_ioapic_pin() comment in commit c0205701) + * + * This is based on the assumption that, Remote IRR bit will be + * cleared by IOAPIC hardware when configured as edge-triggered + * interrupts. + * + * Without this, level-triggered interrupts in IR mode might fail to + * work correctly. + */ +static inline void +ioapic_fix_edge_remote_irr(uint64_t *entry) +{ + if (!(*entry & IOAPIC_LVT_TRIGGER_MODE)) { + /* Edge-triggered interrupts, make sure remote IRR is zero */ + *entry &= ~((uint64_t)IOAPIC_LVT_REMOTE_IRR); + } +} + static void ioapic_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size) @@ -280,6 +343,7 @@ ioapic_mem_write(void *opaque, hwaddr addr, uint64_t val, default: index = (s->ioregsel - IOAPIC_REG_REDTBL_BASE) >> 1; if (index >= 0 && index < IOAPIC_NUM_PINS) { + uint64_t ro_bits = s->ioredtbl[index] & IOAPIC_RO_BITS; if (s->ioregsel & 1) { s->ioredtbl[index] &= 0xffffffff; s->ioredtbl[index] |= (uint64_t)val << 32; @@ -287,10 +351,21 @@ ioapic_mem_write(void *opaque, hwaddr addr, uint64_t val, s->ioredtbl[index] &= ~0xffffffffULL; s->ioredtbl[index] |= val; } + /* restore RO bits */ + s->ioredtbl[index] &= IOAPIC_RW_BITS; + s->ioredtbl[index] |= ro_bits; + ioapic_fix_edge_remote_irr(&s->ioredtbl[index]); ioapic_service(s); } } break; + case IOAPIC_EOI: + /* Explicit EOI is only supported for IOAPIC version 0x20 */ + if (size != 4 || s->version != 0x20) { + break; + } + ioapic_eoi_broadcast(val); + break; } ioapic_update_kvm_routes(s); @@ -302,18 +377,49 @@ static const MemoryRegionOps ioapic_io_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; +static void ioapic_machine_done_notify(Notifier *notifier, void *data) +{ +#ifdef CONFIG_KVM + IOAPICCommonState *s = container_of(notifier, IOAPICCommonState, + machine_done); + + if (kvm_irqchip_is_split()) { + X86IOMMUState *iommu = x86_iommu_get_default(); + if (iommu) { + /* Register this IOAPIC with IOMMU IEC notifier, so that + * when there are IR invalidates, we can be notified to + * update kernel IR cache. */ + x86_iommu_iec_register_notifier(iommu, ioapic_iec_notifier, s); + } + } +#endif +} + static void ioapic_realize(DeviceState *dev, Error **errp) { IOAPICCommonState *s = IOAPIC_COMMON(dev); + if (s->version != 0x11 && s->version != 0x20) { + error_report("IOAPIC only supports version 0x11 or 0x20 " + "(default: 0x11)."); + exit(1); + } + memory_region_init_io(&s->io_memory, OBJECT(s), &ioapic_io_ops, s, "ioapic", 0x1000); qdev_init_gpio_in(dev, ioapic_set_irq, IOAPIC_NUM_PINS); ioapics[ioapic_no] = s; + s->machine_done.notify = ioapic_machine_done_notify; + qemu_add_machine_init_done_notifier(&s->machine_done); } +static Property ioapic_properties[] = { + DEFINE_PROP_UINT8("version", IOAPICCommonState, version, 0x11), + DEFINE_PROP_END_OF_LIST(), +}; + static void ioapic_class_init(ObjectClass *klass, void *data) { IOAPICCommonClass *k = IOAPIC_COMMON_CLASS(klass); @@ -321,6 +427,7 @@ static void ioapic_class_init(ObjectClass *klass, void *data) k->realize = ioapic_realize; dc->reset = ioapic_reset_common; + dc->props = ioapic_properties; } static const TypeInfo ioapic_info = { diff --git a/hw/intc/lm32_pic.c b/hw/intc/lm32_pic.c index edc08f184..3dad01c5b 100644 --- a/hw/intc/lm32_pic.c +++ b/hw/intc/lm32_pic.c @@ -152,17 +152,16 @@ static void pic_reset(DeviceState *d) } } -static int lm32_pic_init(SysBusDevice *sbd) +static void lm32_pic_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - LM32PicState *s = LM32_PIC(dev); + DeviceState *dev = DEVICE(obj); + LM32PicState *s = LM32_PIC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); qdev_init_gpio_in(dev, irq_handler, 32); sysbus_init_irq(sbd, &s->parent_irq); pic = s; - - return 0; } static const VMStateDescription vmstate_lm32_pic = { @@ -181,9 +180,7 @@ static const VMStateDescription vmstate_lm32_pic = { static void lm32_pic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = lm32_pic_init; dc->reset = pic_reset; dc->vmsd = &vmstate_lm32_pic; } @@ -192,6 +189,7 @@ static const TypeInfo lm32_pic_info = { .name = TYPE_LM32_PIC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(LM32PicState), + .instance_init = lm32_pic_init, .class_init = lm32_pic_class_init, }; diff --git a/hw/intc/mips_gic.c b/hw/intc/mips_gic.c new file mode 100644 index 000000000..6e257730f --- /dev/null +++ b/hw/intc/mips_gic.c @@ -0,0 +1,460 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved. + * Authors: Sanjay Lal <sanjayl@kymasys.com> + * + * Copyright (C) 2016 Imagination Technologies + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "exec/memory.h" +#include "sysemu/sysemu.h" +#include "sysemu/kvm.h" +#include "kvm_mips.h" +#include "hw/intc/mips_gic.h" + +static void mips_gic_set_vp_irq(MIPSGICState *gic, int vp, int pin, int level) +{ + int ored_level = level; + int i; + + /* ORing pending registers sharing same pin */ + if (!ored_level) { + for (i = 0; i < gic->num_irq; i++) { + if ((gic->irq_state[i].map_pin & GIC_MAP_MSK) == pin && + gic->irq_state[i].map_vp == vp && + gic->irq_state[i].enabled) { + ored_level |= gic->irq_state[i].pending; + } + if (ored_level) { + /* no need to iterate all interrupts */ + break; + } + } + if (((gic->vps[vp].compare_map & GIC_MAP_MSK) == pin) && + (gic->vps[vp].mask & GIC_VP_MASK_CMP_MSK)) { + /* ORing with local pending register (count/compare) */ + ored_level |= (gic->vps[vp].pend & GIC_VP_MASK_CMP_MSK) >> + GIC_VP_MASK_CMP_SHF; + } + } + if (kvm_enabled()) { + kvm_mips_set_ipi_interrupt(mips_env_get_cpu(gic->vps[vp].env), + pin + GIC_CPU_PIN_OFFSET, + ored_level); + } else { + qemu_set_irq(gic->vps[vp].env->irq[pin + GIC_CPU_PIN_OFFSET], + ored_level); + } +} + +static void gic_set_irq(void *opaque, int n_IRQ, int level) +{ + MIPSGICState *gic = (MIPSGICState *) opaque; + int vp = gic->irq_state[n_IRQ].map_vp; + int pin = gic->irq_state[n_IRQ].map_pin & GIC_MAP_MSK; + + gic->irq_state[n_IRQ].pending = (uint8_t) level; + if (!gic->irq_state[n_IRQ].enabled) { + /* GIC interrupt source disabled */ + return; + } + if (vp < 0 || vp >= gic->num_vps) { + return; + } + mips_gic_set_vp_irq(gic, vp, pin, level); +} + +#define OFFSET_CHECK(c) \ + do { \ + if (!(c)) { \ + goto bad_offset; \ + } \ + } while (0) + +/* GIC Read VP Local/Other Registers */ +static uint64_t gic_read_vp(MIPSGICState *gic, uint32_t vp_index, hwaddr addr, + unsigned size) +{ + switch (addr) { + case GIC_VP_CTL_OFS: + return gic->vps[vp_index].ctl; + case GIC_VP_PEND_OFS: + mips_gictimer_get_sh_count(gic->gic_timer); + return gic->vps[vp_index].pend; + case GIC_VP_MASK_OFS: + return gic->vps[vp_index].mask; + case GIC_VP_COMPARE_MAP_OFS: + return gic->vps[vp_index].compare_map; + case GIC_VP_OTHER_ADDR_OFS: + return gic->vps[vp_index].other_addr; + case GIC_VP_IDENT_OFS: + return vp_index; + case GIC_VP_COMPARE_LO_OFS: + return mips_gictimer_get_vp_compare(gic->gic_timer, vp_index); + case GIC_VP_COMPARE_HI_OFS: + return 0; + default: + qemu_log_mask(LOG_UNIMP, "Read %d bytes at GIC offset LOCAL/OTHER 0x%" + PRIx64 "\n", size, addr); + break; + } + return 0; +} + +static uint64_t gic_read(void *opaque, hwaddr addr, unsigned size) +{ + MIPSGICState *gic = (MIPSGICState *) opaque; + uint32_t vp_index = current_cpu->cpu_index; + uint64_t ret = 0; + int i, base, irq_src; + uint32_t other_index; + + switch (addr) { + case GIC_SH_CONFIG_OFS: + ret = gic->sh_config | (mips_gictimer_get_countstop(gic->gic_timer) << + GIC_SH_CONFIG_COUNTSTOP_SHF); + break; + case GIC_SH_COUNTERLO_OFS: + ret = mips_gictimer_get_sh_count(gic->gic_timer); + break; + case GIC_SH_COUNTERHI_OFS: + ret = 0; + break; + case GIC_SH_PEND_OFS ... GIC_SH_PEND_LAST_OFS: + /* each bit represents pending status for an interrupt pin */ + base = (addr - GIC_SH_PEND_OFS) * 8; + OFFSET_CHECK((base + size * 8) <= gic->num_irq); + for (i = 0; i < size * 8; i++) { + ret |= (uint64_t) (gic->irq_state[base + i].pending) << i; + } + break; + case GIC_SH_MASK_OFS ... GIC_SH_MASK_LAST_OFS: + /* each bit represents status for an interrupt pin */ + base = (addr - GIC_SH_MASK_OFS) * 8; + OFFSET_CHECK((base + size * 8) <= gic->num_irq); + for (i = 0; i < size * 8; i++) { + ret |= (uint64_t) (gic->irq_state[base + i].enabled) << i; + } + break; + case GIC_SH_MAP0_PIN_OFS ... GIC_SH_MAP255_PIN_OFS: + /* 32 bits per a pin */ + irq_src = (addr - GIC_SH_MAP0_PIN_OFS) / 4; + OFFSET_CHECK(irq_src < gic->num_irq); + ret = gic->irq_state[irq_src].map_pin; + break; + case GIC_SH_MAP0_VP_OFS ... GIC_SH_MAP255_VP_LAST_OFS: + /* up to 32 bytes per a pin */ + irq_src = (addr - GIC_SH_MAP0_VP_OFS) / 32; + OFFSET_CHECK(irq_src < gic->num_irq); + if ((gic->irq_state[irq_src].map_vp) >= 0) { + ret = (uint64_t) 1 << (gic->irq_state[irq_src].map_vp); + } else { + ret = 0; + } + break; + /* VP-Local Register */ + case VP_LOCAL_SECTION_OFS ... (VP_LOCAL_SECTION_OFS + GIC_VL_BRK_GROUP): + ret = gic_read_vp(gic, vp_index, addr - VP_LOCAL_SECTION_OFS, size); + break; + /* VP-Other Register */ + case VP_OTHER_SECTION_OFS ... (VP_OTHER_SECTION_OFS + GIC_VL_BRK_GROUP): + other_index = gic->vps[vp_index].other_addr; + ret = gic_read_vp(gic, other_index, addr - VP_OTHER_SECTION_OFS, size); + break; + /* User-Mode Visible section */ + case USM_VISIBLE_SECTION_OFS + GIC_USER_MODE_COUNTERLO: + ret = mips_gictimer_get_sh_count(gic->gic_timer); + break; + case USM_VISIBLE_SECTION_OFS + GIC_USER_MODE_COUNTERHI: + ret = 0; + break; + default: + qemu_log_mask(LOG_UNIMP, "Read %d bytes at GIC offset 0x%" PRIx64 "\n", + size, addr); + break; + } + return ret; +bad_offset: + qemu_log_mask(LOG_GUEST_ERROR, "Wrong GIC offset at 0x%" PRIx64 "\n", addr); + return 0; +} + +static void gic_timer_expire_cb(void *opaque, uint32_t vp_index) +{ + MIPSGICState *gic = opaque; + + gic->vps[vp_index].pend |= (1 << GIC_LOCAL_INT_COMPARE); + if (gic->vps[vp_index].pend & + (gic->vps[vp_index].mask & GIC_VP_MASK_CMP_MSK)) { + if (gic->vps[vp_index].compare_map & GIC_MAP_TO_PIN_MSK) { + /* it is safe to set the irq high regardless of other GIC IRQs */ + uint32_t pin = (gic->vps[vp_index].compare_map & GIC_MAP_MSK); + qemu_irq_raise(gic->vps[vp_index].env->irq + [pin + GIC_CPU_PIN_OFFSET]); + } + } +} + +static void gic_timer_store_vp_compare(MIPSGICState *gic, uint32_t vp_index, + uint64_t compare) +{ + gic->vps[vp_index].pend &= ~(1 << GIC_LOCAL_INT_COMPARE); + if (gic->vps[vp_index].compare_map & GIC_MAP_TO_PIN_MSK) { + uint32_t pin = (gic->vps[vp_index].compare_map & GIC_MAP_MSK); + mips_gic_set_vp_irq(gic, vp_index, pin, 0); + } + mips_gictimer_store_vp_compare(gic->gic_timer, vp_index, compare); +} + +/* GIC Write VP Local/Other Registers */ +static void gic_write_vp(MIPSGICState *gic, uint32_t vp_index, hwaddr addr, + uint64_t data, unsigned size) +{ + switch (addr) { + case GIC_VP_CTL_OFS: + /* EIC isn't supported */ + break; + case GIC_VP_RMASK_OFS: + gic->vps[vp_index].mask &= ~(data & GIC_VP_SET_RESET_MSK) & + GIC_VP_SET_RESET_MSK; + break; + case GIC_VP_SMASK_OFS: + gic->vps[vp_index].mask |= data & GIC_VP_SET_RESET_MSK; + break; + case GIC_VP_COMPARE_MAP_OFS: + /* EIC isn't supported */ + OFFSET_CHECK((data & GIC_MAP_MSK) <= GIC_CPU_INT_MAX); + gic->vps[vp_index].compare_map = data & GIC_MAP_TO_PIN_REG_MSK; + break; + case GIC_VP_OTHER_ADDR_OFS: + OFFSET_CHECK(data < gic->num_vps); + gic->vps[vp_index].other_addr = data; + break; + case GIC_VP_COMPARE_LO_OFS: + gic_timer_store_vp_compare(gic, vp_index, data); + break; + default: + qemu_log_mask(LOG_UNIMP, "Write %d bytes at GIC offset LOCAL/OTHER " + "0x%" PRIx64" 0x%08" PRIx64 "\n", size, addr, data); + break; + } + return; +bad_offset: + qemu_log_mask(LOG_GUEST_ERROR, "Wrong GIC offset at 0x%" PRIx64 "\n", addr); + return; +} + +static void gic_write(void *opaque, hwaddr addr, uint64_t data, unsigned size) +{ + int intr; + MIPSGICState *gic = (MIPSGICState *) opaque; + uint32_t vp_index = current_cpu->cpu_index; + int i, base, irq_src; + uint32_t other_index; + + switch (addr) { + case GIC_SH_CONFIG_OFS: + { + uint32_t pre_cntstop = mips_gictimer_get_countstop(gic->gic_timer); + uint32_t new_cntstop = (data & GIC_SH_CONFIG_COUNTSTOP_MSK) >> + GIC_SH_CONFIG_COUNTSTOP_SHF; + if (pre_cntstop != new_cntstop) { + if (new_cntstop == 1) { + mips_gictimer_stop_count(gic->gic_timer); + } else { + mips_gictimer_start_count(gic->gic_timer); + } + } + } + break; + case GIC_SH_COUNTERLO_OFS: + if (mips_gictimer_get_countstop(gic->gic_timer)) { + mips_gictimer_store_sh_count(gic->gic_timer, data); + } + break; + case GIC_SH_RMASK_OFS ... GIC_SH_RMASK_LAST_OFS: + /* up to 64 bits per a pin */ + base = (addr - GIC_SH_RMASK_OFS) * 8; + OFFSET_CHECK((base + size * 8) <= gic->num_irq); + for (i = 0; i < size * 8; i++) { + gic->irq_state[base + i].enabled &= !((data >> i) & 1); + } + break; + case GIC_SH_WEDGE_OFS: + /* Figure out which VP/HW Interrupt this maps to */ + intr = data & ~GIC_SH_WEDGE_RW_MSK; + /* Mask/Enabled Checks */ + OFFSET_CHECK(intr < gic->num_irq); + if (data & GIC_SH_WEDGE_RW_MSK) { + gic_set_irq(gic, intr, 1); + } else { + gic_set_irq(gic, intr, 0); + } + break; + case GIC_SH_SMASK_OFS ... GIC_SH_SMASK_LAST_OFS: + /* up to 64 bits per a pin */ + base = (addr - GIC_SH_SMASK_OFS) * 8; + OFFSET_CHECK((base + size * 8) <= gic->num_irq); + for (i = 0; i < size * 8; i++) { + gic->irq_state[base + i].enabled |= (data >> i) & 1; + } + break; + case GIC_SH_MAP0_PIN_OFS ... GIC_SH_MAP255_PIN_OFS: + /* 32 bits per a pin */ + irq_src = (addr - GIC_SH_MAP0_PIN_OFS) / 4; + OFFSET_CHECK(irq_src < gic->num_irq); + /* EIC isn't supported */ + OFFSET_CHECK((data & GIC_MAP_MSK) <= GIC_CPU_INT_MAX); + gic->irq_state[irq_src].map_pin = data & GIC_MAP_TO_PIN_REG_MSK; + break; + case GIC_SH_MAP0_VP_OFS ... GIC_SH_MAP255_VP_LAST_OFS: + /* up to 32 bytes per a pin */ + irq_src = (addr - GIC_SH_MAP0_VP_OFS) / 32; + OFFSET_CHECK(irq_src < gic->num_irq); + data = data ? ctz64(data) : -1; + OFFSET_CHECK(data < gic->num_vps); + gic->irq_state[irq_src].map_vp = data; + break; + case VP_LOCAL_SECTION_OFS ... (VP_LOCAL_SECTION_OFS + GIC_VL_BRK_GROUP): + gic_write_vp(gic, vp_index, addr - VP_LOCAL_SECTION_OFS, data, size); + break; + case VP_OTHER_SECTION_OFS ... (VP_OTHER_SECTION_OFS + GIC_VL_BRK_GROUP): + other_index = gic->vps[vp_index].other_addr; + gic_write_vp(gic, other_index, addr - VP_OTHER_SECTION_OFS, data, size); + break; + case USM_VISIBLE_SECTION_OFS + GIC_USER_MODE_COUNTERLO: + case USM_VISIBLE_SECTION_OFS + GIC_USER_MODE_COUNTERHI: + /* do nothing. Read-only section */ + break; + default: + qemu_log_mask(LOG_UNIMP, "Write %d bytes at GIC offset 0x%" PRIx64 + " 0x%08" PRIx64 "\n", size, addr, data); + break; + } + return; +bad_offset: + qemu_log_mask(LOG_GUEST_ERROR, "Wrong GIC offset at 0x%" PRIx64 "\n", addr); +} + +static void gic_reset(void *opaque) +{ + int i; + MIPSGICState *gic = (MIPSGICState *) opaque; + int numintrs = (gic->num_irq / 8) - 1; + + gic->sh_config = /* COUNTSTOP = 0 it is accessible via MIPSGICTimer*/ + /* CounterHi not implemented */ + (0 << GIC_SH_CONFIG_COUNTBITS_SHF) | + (numintrs << GIC_SH_CONFIG_NUMINTRS_SHF) | + (gic->num_vps << GIC_SH_CONFIG_PVPS_SHF); + for (i = 0; i < gic->num_vps; i++) { + gic->vps[i].ctl = 0x0; + gic->vps[i].pend = 0x0; + /* PERFCNT, TIMER and WD not implemented */ + gic->vps[i].mask = 0x32; + gic->vps[i].compare_map = GIC_MAP_TO_PIN_MSK; + mips_gictimer_store_vp_compare(gic->gic_timer, i, 0xffffffff); + gic->vps[i].other_addr = 0x0; + } + for (i = 0; i < gic->num_irq; i++) { + gic->irq_state[i].enabled = 0; + gic->irq_state[i].pending = 0; + gic->irq_state[i].map_pin = GIC_MAP_TO_PIN_MSK; + gic->irq_state[i].map_vp = -1; + } + mips_gictimer_store_sh_count(gic->gic_timer, 0); + /* COUNTSTOP = 0 */ + mips_gictimer_start_count(gic->gic_timer); +} + +static const MemoryRegionOps gic_ops = { + .read = gic_read, + .write = gic_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .max_access_size = 8, + }, +}; + +static void mips_gic_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + MIPSGICState *s = MIPS_GIC(obj); + + memory_region_init_io(&s->mr, OBJECT(s), &gic_ops, s, + "mips-gic", GIC_ADDRSPACE_SZ); + sysbus_init_mmio(sbd, &s->mr); + qemu_register_reset(gic_reset, s); +} + +static void mips_gic_realize(DeviceState *dev, Error **errp) +{ + MIPSGICState *s = MIPS_GIC(dev); + CPUState *cs = first_cpu; + int i; + + if (s->num_vps > GIC_MAX_VPS) { + error_setg(errp, "Exceeded maximum CPUs %d", s->num_vps); + return; + } + if ((s->num_irq > GIC_MAX_INTRS) || (s->num_irq % 8) || (s->num_irq <= 0)) { + error_setg(errp, "GIC supports up to %d external interrupts in " + "multiples of 8 : %d", GIC_MAX_INTRS, s->num_irq); + return; + } + s->vps = g_new(MIPSGICVPState, s->num_vps); + s->irq_state = g_new(MIPSGICIRQState, s->num_irq); + /* Register the env for all VPs with the GIC */ + for (i = 0; i < s->num_vps; i++) { + if (cs != NULL) { + s->vps[i].env = cs->env_ptr; + cs = CPU_NEXT(cs); + } else { + error_setg(errp, + "Unable to initialize GIC, CPUState for CPU#%d not valid.", i); + return; + } + } + s->gic_timer = mips_gictimer_init(s, s->num_vps, gic_timer_expire_cb); + qdev_init_gpio_in(dev, gic_set_irq, s->num_irq); + for (i = 0; i < s->num_irq; i++) { + s->irq_state[i].irq = qdev_get_gpio_in(dev, i); + } +} + +static Property mips_gic_properties[] = { + DEFINE_PROP_INT32("num-vp", MIPSGICState, num_vps, 1), + DEFINE_PROP_INT32("num-irq", MIPSGICState, num_irq, 256), + DEFINE_PROP_END_OF_LIST(), +}; + +static void mips_gic_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->props = mips_gic_properties; + dc->realize = mips_gic_realize; +} + +static const TypeInfo mips_gic_info = { + .name = TYPE_MIPS_GIC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MIPSGICState), + .instance_init = mips_gic_init, + .class_init = mips_gic_class_init, +}; + +static void mips_gic_register_types(void) +{ + type_register_static(&mips_gic_info); +} + +type_init(mips_gic_register_types) diff --git a/hw/intc/omap_intc.c b/hw/intc/omap_intc.c index 336882510..877be6797 100644 --- a/hw/intc/omap_intc.c +++ b/hw/intc/omap_intc.c @@ -22,6 +22,7 @@ #include "hw/arm/omap.h" #include "hw/sysbus.h" #include "qemu/error-report.h" +#include "qapi/error.h" /* Interrupt Handlers */ struct omap_intr_handler_bank_s { @@ -363,23 +364,28 @@ static void omap_inth_reset(DeviceState *dev) qemu_set_irq(s->parent_intr[1], 0); } -static int omap_intc_init(SysBusDevice *sbd) +static void omap_intc_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - struct omap_intr_handler_s *s = OMAP_INTC(dev); + DeviceState *dev = DEVICE(obj); + struct omap_intr_handler_s *s = OMAP_INTC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - if (!s->iclk) { - error_report("omap-intc: clk not connected"); - return -1; - } s->nbanks = 1; sysbus_init_irq(sbd, &s->parent_intr[0]); sysbus_init_irq(sbd, &s->parent_intr[1]); qdev_init_gpio_in(dev, omap_set_intr, s->nbanks * 32); - memory_region_init_io(&s->mmio, OBJECT(s), &omap_inth_mem_ops, s, + memory_region_init_io(&s->mmio, obj, &omap_inth_mem_ops, s, "omap-intc", s->size); sysbus_init_mmio(sbd, &s->mmio); - return 0; +} + +static void omap_intc_realize(DeviceState *dev, Error **errp) +{ + struct omap_intr_handler_s *s = OMAP_INTC(dev); + + if (!s->iclk) { + error_setg(errp, "omap-intc: clk not connected"); + } } static Property omap_intc_properties[] = { @@ -391,18 +397,18 @@ static Property omap_intc_properties[] = { static void omap_intc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = omap_intc_init; dc->reset = omap_inth_reset; dc->props = omap_intc_properties; /* Reason: pointer property "clk" */ dc->cannot_instantiate_with_device_add_yet = true; + dc->realize = omap_intc_realize; } static const TypeInfo omap_intc_info = { .name = "omap-intc", .parent = TYPE_OMAP_INTC, + .instance_init = omap_intc_init, .class_init = omap_intc_class_init, }; @@ -605,28 +611,34 @@ static const MemoryRegionOps omap2_inth_mem_ops = { }, }; -static int omap2_intc_init(SysBusDevice *sbd) +static void omap2_intc_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - struct omap_intr_handler_s *s = OMAP_INTC(dev); + DeviceState *dev = DEVICE(obj); + struct omap_intr_handler_s *s = OMAP_INTC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - if (!s->iclk) { - error_report("omap2-intc: iclk not connected"); - return -1; - } - if (!s->fclk) { - error_report("omap2-intc: fclk not connected"); - return -1; - } s->level_only = 1; s->nbanks = 3; sysbus_init_irq(sbd, &s->parent_intr[0]); sysbus_init_irq(sbd, &s->parent_intr[1]); qdev_init_gpio_in(dev, omap_set_intr_noedge, s->nbanks * 32); - memory_region_init_io(&s->mmio, OBJECT(s), &omap2_inth_mem_ops, s, + memory_region_init_io(&s->mmio, obj, &omap2_inth_mem_ops, s, "omap2-intc", 0x1000); sysbus_init_mmio(sbd, &s->mmio); - return 0; +} + +static void omap2_intc_realize(DeviceState *dev, Error **errp) +{ + struct omap_intr_handler_s *s = OMAP_INTC(dev); + + if (!s->iclk) { + error_setg(errp, "omap2-intc: iclk not connected"); + return; + } + if (!s->fclk) { + error_setg(errp, "omap2-intc: fclk not connected"); + return; + } } static Property omap2_intc_properties[] = { @@ -640,18 +652,18 @@ static Property omap2_intc_properties[] = { static void omap2_intc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = omap2_intc_init; dc->reset = omap_inth_reset; dc->props = omap2_intc_properties; /* Reason: pointer property "iclk", "fclk" */ dc->cannot_instantiate_with_device_add_yet = true; + dc->realize = omap2_intc_realize; } static const TypeInfo omap2_intc_info = { .name = "omap2-intc", .parent = TYPE_OMAP_INTC, + .instance_init = omap2_intc_init, .class_init = omap2_intc_class_init, }; diff --git a/hw/intc/openpic.c b/hw/intc/openpic.c index 2d3769310..4349e45e0 100644 --- a/hw/intc/openpic.c +++ b/hw/intc/openpic.c @@ -44,6 +44,7 @@ #include "qapi/error.h" #include "qemu/bitops.h" #include "qapi/qmp/qerror.h" +#include "qemu/log.h" //#define DEBUG_OPENPIC diff --git a/hw/intc/openpic_kvm.c b/hw/intc/openpic_kvm.c index e47e94f2c..0518e017c 100644 --- a/hw/intc/openpic_kvm.c +++ b/hw/intc/openpic_kvm.c @@ -24,6 +24,8 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include <sys/ioctl.h> #include "exec/address-spaces.h" #include "hw/hw.h" diff --git a/hw/intc/pl190.c b/hw/intc/pl190.c index 5ecbc4a48..55ea15de7 100644 --- a/hw/intc/pl190.c +++ b/hw/intc/pl190.c @@ -9,6 +9,7 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" +#include "qemu/log.h" /* The number of virtual priority levels. 16 user vectors plus the unvectored IRQ. Chained interrupts would require an additional level @@ -236,17 +237,17 @@ static void pl190_reset(DeviceState *d) pl190_update_vectors(s); } -static int pl190_init(SysBusDevice *sbd) +static void pl190_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - PL190State *s = PL190(dev); + DeviceState *dev = DEVICE(obj); + PL190State *s = PL190(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - memory_region_init_io(&s->iomem, OBJECT(s), &pl190_ops, s, "pl190", 0x1000); + memory_region_init_io(&s->iomem, obj, &pl190_ops, s, "pl190", 0x1000); sysbus_init_mmio(sbd, &s->iomem); qdev_init_gpio_in(dev, pl190_set_irq, 32); sysbus_init_irq(sbd, &s->irq); sysbus_init_irq(sbd, &s->fiq); - return 0; } static const VMStateDescription vmstate_pl190 = { @@ -271,9 +272,7 @@ static const VMStateDescription vmstate_pl190 = { static void pl190_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = pl190_init; dc->reset = pl190_reset; dc->vmsd = &vmstate_pl190; } @@ -282,6 +281,7 @@ static const TypeInfo pl190_info = { .name = TYPE_PL190, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PL190State), + .instance_init = pl190_init, .class_init = pl190_class_init, }; diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c index bc75fa7d9..6ab29efc6 100644 --- a/hw/intc/s390_flic.c +++ b/hw/intc/s390_flic.c @@ -67,6 +67,13 @@ static void qemu_s390_release_adapter_routes(S390FLICState *fs, { } +static int qemu_s390_clear_io_flic(S390FLICState *fs, uint16_t subchannel_id, + uint16_t subchannel_nr) +{ + /* Fixme TCG */ + return -ENOSYS; +} + static void qemu_s390_flic_class_init(ObjectClass *oc, void *data) { S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc); @@ -75,6 +82,7 @@ static void qemu_s390_flic_class_init(ObjectClass *oc, void *data) fsc->io_adapter_map = qemu_s390_io_adapter_map; fsc->add_adapter_routes = qemu_s390_add_adapter_routes; fsc->release_adapter_routes = qemu_s390_release_adapter_routes; + fsc->clear_io_irq = qemu_s390_clear_io_flic; } static const TypeInfo qemu_s390_flic_info = { diff --git a/hw/intc/s390_flic_kvm.c b/hw/intc/s390_flic_kvm.c index 02449b390..fef808011 100644 --- a/hw/intc/s390_flic_kvm.c +++ b/hw/intc/s390_flic_kvm.c @@ -11,6 +11,8 @@ */ #include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" #include <sys/ioctl.h> #include "qemu/error-report.h" #include "hw/sysbus.h" @@ -28,6 +30,7 @@ typedef struct KVMS390FLICState { S390FLICState parent_obj; uint32_t fd; + bool clear_io_supported; } KVMS390FLICState; DeviceState *s390_flic_kvm_create(void) @@ -128,6 +131,24 @@ int kvm_s390_inject_flic(struct kvm_s390_irq *irq) return flic_enqueue_irqs(irq, sizeof(*irq), flic); } +static int kvm_s390_clear_io_flic(S390FLICState *fs, uint16_t subchannel_id, + uint16_t subchannel_nr) +{ + KVMS390FLICState *flic = KVM_S390_FLIC(fs); + int rc; + uint32_t sid = subchannel_id << 16 | subchannel_nr; + struct kvm_device_attr attr = { + .group = KVM_DEV_FLIC_CLEAR_IO_IRQ, + .addr = (uint64_t) &sid, + .attr = sizeof(sid), + }; + if (unlikely(!flic->clear_io_supported)) { + return -ENOSYS; + } + rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); + return rc ? -errno : 0; +} + /** * __get_all_irqs - store all pending irqs in buffer * @flic: pointer to flic device state @@ -174,7 +195,7 @@ static int kvm_s390_register_io_adapter(S390FLICState *fs, uint32_t id, .swap = swap, }; KVMS390FLICState *flic = KVM_S390_FLIC(fs); - int r, ret; + int r; struct kvm_device_attr attr = { .group = KVM_DEV_FLIC_ADAPTER_REGISTER, .addr = (uint64_t)&adapter, @@ -187,8 +208,7 @@ static int kvm_s390_register_io_adapter(S390FLICState *fs, uint32_t id, r = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); - ret = r ? -errno : 0; - return ret; + return r ? -errno : 0; } static int kvm_s390_io_adapter_map(S390FLICState *fs, uint32_t id, @@ -356,6 +376,7 @@ static void kvm_s390_flic_realize(DeviceState *dev, Error **errp) { KVMS390FLICState *flic_state = KVM_S390_FLIC(dev); struct kvm_create_device cd = {0}; + struct kvm_device_attr test_attr = {0}; int ret; flic_state->fd = -1; @@ -372,6 +393,11 @@ static void kvm_s390_flic_realize(DeviceState *dev, Error **errp) } flic_state->fd = cd.fd; + /* Check clear_io_irq support */ + test_attr.group = KVM_DEV_FLIC_CLEAR_IO_IRQ; + flic_state->clear_io_supported = !ioctl(flic_state->fd, + KVM_HAS_DEVICE_ATTR, test_attr); + /* Register savevm handler for floating interrupts */ register_savevm(NULL, "s390-flic", 0, 1, kvm_flic_save, kvm_flic_load, (void *) flic_state); @@ -418,6 +444,7 @@ static void kvm_s390_flic_class_init(ObjectClass *oc, void *data) fsc->io_adapter_map = kvm_s390_io_adapter_map; fsc->add_adapter_routes = kvm_s390_add_adapter_routes; fsc->release_adapter_routes = kvm_s390_release_adapter_routes; + fsc->clear_io_irq = kvm_s390_clear_io_flic; } static const TypeInfo kvm_s390_flic_info = { diff --git a/hw/intc/slavio_intctl.c b/hw/intc/slavio_intctl.c index c9486ed99..e82e89362 100644 --- a/hw/intc/slavio_intctl.c +++ b/hw/intc/slavio_intctl.c @@ -418,15 +418,16 @@ static void slavio_intctl_reset(DeviceState *d) slavio_check_interrupts(s, 0); } -static int slavio_intctl_init1(SysBusDevice *sbd) +static void slavio_intctl_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - SLAVIO_INTCTLState *s = SLAVIO_INTCTL(dev); + DeviceState *dev = DEVICE(obj); + SLAVIO_INTCTLState *s = SLAVIO_INTCTL(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); unsigned int i, j; char slave_name[45]; qdev_init_gpio_in(dev, slavio_set_irq_all, 32 + MAX_CPUS); - memory_region_init_io(&s->iomem, OBJECT(s), &slavio_intctlm_mem_ops, s, + memory_region_init_io(&s->iomem, obj, &slavio_intctlm_mem_ops, s, "master-interrupt-controller", INTCTLM_SIZE); sysbus_init_mmio(sbd, &s->iomem); @@ -443,16 +444,12 @@ static int slavio_intctl_init1(SysBusDevice *sbd) s->slaves[i].cpu = i; s->slaves[i].master = s; } - - return 0; } static void slavio_intctl_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = slavio_intctl_init1; dc->reset = slavio_intctl_reset; dc->vmsd = &vmstate_intctl; } @@ -461,6 +458,7 @@ static const TypeInfo slavio_intctl_info = { .name = TYPE_SLAVIO_INTCTL, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SLAVIO_INTCTLState), + .instance_init = slavio_intctl_init, .class_init = slavio_intctl_class_init, }; diff --git a/hw/intc/trace-events b/hw/intc/trace-events new file mode 100644 index 000000000..f12192c08 --- /dev/null +++ b/hw/intc/trace-events @@ -0,0 +1,123 @@ +# See docs/tracing.txt for syntax documentation. + +# hw/intc/apic_common.c +cpu_set_apic_base(uint64_t val) "%016"PRIx64 +cpu_get_apic_base(uint64_t val) "%016"PRIx64 +# coalescing +apic_report_irq_delivered(int apic_irq_delivered) "coalescing %d" +apic_reset_irq_delivered(int apic_irq_delivered) "old coalescing %d" +apic_get_irq_delivered(int apic_irq_delivered) "returning coalescing %d" + +# hw/intc/apic.c +apic_local_deliver(int vector, uint32_t lvt) "vector %d delivery mode %d" +apic_deliver_irq(uint8_t dest, uint8_t dest_mode, uint8_t delivery_mode, uint8_t vector_num, uint8_t trigger_mode) "dest %d dest_mode %d delivery_mode %d vector %d trigger_mode %d" +apic_mem_readl(uint64_t addr, uint32_t val) "%"PRIx64" = %08x" +apic_mem_writel(uint64_t addr, uint32_t val) "%"PRIx64" = %08x" + +# hw/intc/slavio_intctl.c +slavio_intctl_mem_readl(uint32_t cpu, uint64_t addr, uint32_t ret) "read cpu %d reg 0x%"PRIx64" = %x" +slavio_intctl_mem_writel(uint32_t cpu, uint64_t addr, uint32_t val) "write cpu %d reg 0x%"PRIx64" = %x" +slavio_intctl_mem_writel_clear(uint32_t cpu, uint32_t val, uint32_t intreg_pending) "Cleared cpu %d irq mask %x, curmask %x" +slavio_intctl_mem_writel_set(uint32_t cpu, uint32_t val, uint32_t intreg_pending) "Set cpu %d irq mask %x, curmask %x" +slavio_intctlm_mem_readl(uint64_t addr, uint32_t ret) "read system reg 0x%"PRIx64" = %x" +slavio_intctlm_mem_writel(uint64_t addr, uint32_t val) "write system reg 0x%"PRIx64" = %x" +slavio_intctlm_mem_writel_enable(uint32_t val, uint32_t intregm_disabled) "Enabled master irq mask %x, curmask %x" +slavio_intctlm_mem_writel_disable(uint32_t val, uint32_t intregm_disabled) "Disabled master irq mask %x, curmask %x" +slavio_intctlm_mem_writel_target(uint32_t cpu) "Set master irq cpu %d" +slavio_check_interrupts(uint32_t pending, uint32_t intregm_disabled) "pending %x disabled %x" +slavio_set_irq(uint32_t target_cpu, int irq, uint32_t pil, int level) "Set cpu %d irq %d -> pil %d level %d" +slavio_set_timer_irq_cpu(int cpu, int level) "Set cpu %d local timer level %d" + +# hw/intc/grlib_irqmp.c +grlib_irqmp_check_irqs(uint32_t pend, uint32_t force, uint32_t mask, uint32_t lvl1, uint32_t lvl2) "pend:0x%04x force:0x%04x mask:0x%04x lvl1:0x%04x lvl0:0x%04x" +grlib_irqmp_ack(int intno) "interrupt:%d" +grlib_irqmp_set_irq(int irq) "Raise CPU IRQ %d" +grlib_irqmp_readl_unknown(uint64_t addr) "addr 0x%"PRIx64 +grlib_irqmp_writel_unknown(uint64_t addr, uint32_t value) "addr 0x%"PRIx64" value 0x%x" + +# hw/intc/lm32_pic.c +lm32_pic_raise_irq(void) "Raise CPU interrupt" +lm32_pic_lower_irq(void) "Lower CPU interrupt" +lm32_pic_interrupt(int irq, int level) "Set IRQ%d %d" +lm32_pic_set_im(uint32_t im) "im 0x%08x" +lm32_pic_set_ip(uint32_t ip) "ip 0x%08x" +lm32_pic_get_im(uint32_t im) "im 0x%08x" +lm32_pic_get_ip(uint32_t ip) "ip 0x%08x" + +# hw/intc/xics.c +xics_icp_check_ipi(int server, uint8_t mfrr) "CPU %d can take IPI mfrr=%#x" +xics_icp_accept(uint32_t old_xirr, uint32_t new_xirr) "icp_accept: XIRR %#"PRIx32"->%#"PRIx32 +xics_icp_eoi(int server, uint32_t xirr, uint32_t new_xirr) "icp_eoi: server %d given XIRR %#"PRIx32" new XIRR %#"PRIx32 +xics_icp_irq(int server, int nr, uint8_t priority) "cpu %d trying to deliver irq %#"PRIx32" priority %#x" +xics_icp_raise(uint32_t xirr, uint8_t pending_priority) "raising IRQ new XIRR=%#x new pending priority=%#x" +xics_set_irq_msi(int srcno, int nr) "set_irq_msi: srcno %d [irq %#x]" +xics_masked_pending(void) "set_irq_msi: masked pending" +xics_set_irq_lsi(int srcno, int nr) "set_irq_lsi: srcno %d [irq %#x]" +xics_ics_write_xive(int nr, int srcno, int server, uint8_t priority) "ics_write_xive: irq %#x [src %d] server %#x prio %#x" +xics_ics_reject(int nr, int srcno) "reject irq %#x [src %d]" +xics_ics_eoi(int nr) "ics_eoi: irq %#x" +xics_alloc(int src, int irq) "source#%d, irq %d" +xics_alloc_block(int src, int first, int num, bool lsi, int align) "source#%d, first irq %d, %d irqs, lsi=%d, alignnum %d" +xics_ics_free(int src, int irq, int num) "Source#%d, first irq %d, %d irqs" +xics_ics_free_warn(int src, int irq) "Source#%d, irq %d is already free" + +# hw/intc/s390_flic_kvm.c +flic_create_device(int err) "flic: create device failed %d" +flic_no_device_api(int err) "flic: no Device Contral API support %d" +flic_reset_failed(int err) "flic: reset failed %d" + +# hw/intc/aspeed_vic.c +aspeed_vic_set_irq(int irq, int level) "Enabling IRQ %d: %d" +aspeed_vic_update_fiq(int flags) "Raising FIQ: %d" +aspeed_vic_update_irq(int flags) "Raising IRQ: %d" +aspeed_vic_read(uint64_t offset, unsigned size, uint32_t value) "From 0x%" PRIx64 " of size %u: 0x%" PRIx32 +aspeed_vic_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 + +# hw/intc/arm_gic.c +gic_enable_irq(int irq) "irq %d enabled" +gic_disable_irq(int irq) "irq %d disabled" +gic_set_irq(int irq, int level, int cpumask, int target) "irq %d level %d cpumask 0x%x target 0x%x" +gic_update_bestirq(int cpu, int irq, int prio, int priority_mask, int running_priority) "cpu %d irq %d priority %d cpu priority mask %d cpu running priority %d" +gic_update_set_irq(int cpu, const char *name, int level) "cpu[%d]: %s = %d" +gic_acknowledge_irq(int cpu, int irq) "cpu %d acknowledged irq %d" + +# hw/intc/arm_gicv3_cpuif.c +gicv3_icc_pmr_read(uint32_t cpu, uint64_t val) "GICv3 ICC_PMR read cpu %x value 0x%" PRIx64 +gicv3_icc_pmr_write(uint32_t cpu, uint64_t val) "GICv3 ICC_PMR write cpu %x value 0x%" PRIx64 +gicv3_icc_bpr_read(uint32_t cpu, uint64_t val) "GICv3 ICC_BPR read cpu %x value 0x%" PRIx64 +gicv3_icc_bpr_write(uint32_t cpu, uint64_t val) "GICv3 ICC_BPR write cpu %x value 0x%" PRIx64 +gicv3_icc_ap_read(int regno, uint32_t cpu, uint64_t val) "GICv3 ICC_AP%dR read cpu %x value 0x%" PRIx64 +gicv3_icc_ap_write(int regno, uint32_t cpu, uint64_t val) "GICv3 ICC_AP%dR write cpu %x value 0x%" PRIx64 +gicv3_icc_igrpen_read(uint32_t cpu, uint64_t val) "GICv3 ICC_IGRPEN read cpu %x value 0x%" PRIx64 +gicv3_icc_igrpen_write(uint32_t cpu, uint64_t val) "GICv3 ICC_IGRPEN write cpu %x value 0x%" PRIx64 +gicv3_icc_igrpen1_el3_read(uint32_t cpu, uint64_t val) "GICv3 ICC_IGRPEN1_EL3 read cpu %x value 0x%" PRIx64 +gicv3_icc_igrpen1_el3_write(uint32_t cpu, uint64_t val) "GICv3 ICC_IGRPEN1_EL3 write cpu %x value 0x%" PRIx64 +gicv3_icc_ctlr_read(uint32_t cpu, uint64_t val) "GICv3 ICC_CTLR read cpu %x value 0x%" PRIx64 +gicv3_icc_ctlr_write(uint32_t cpu, uint64_t val) "GICv3 ICC_CTLR write cpu %x value 0x%" PRIx64 +gicv3_icc_ctlr_el3_read(uint32_t cpu, uint64_t val) "GICv3 ICC_CTLR_EL3 read cpu %x value 0x%" PRIx64 +gicv3_icc_ctlr_el3_write(uint32_t cpu, uint64_t val) "GICv3 ICC_CTLR_EL3 write cpu %x value 0x%" PRIx64 +gicv3_cpuif_update(uint32_t cpuid, int irq, int grp, int prio) "GICv3 CPU i/f %x HPPI update: irq %d group %d prio %d" +gicv3_cpuif_set_irqs(uint32_t cpuid, int fiqlevel, int irqlevel) "GICv3 CPU i/f %x HPPI update: setting FIQ %d IRQ %d" +gicv3_icc_generate_sgi(uint32_t cpuid, int irq, int irm, uint32_t aff, uint32_t targetlist) "GICv3 CPU i/f %x generating SGI %d IRM %d target affinity 0x%xxx targetlist 0x%x" +gicv3_icc_iar0_read(uint32_t cpu, uint64_t val) "GICv3 ICC_IAR0 read cpu %x value 0x%" PRIx64 +gicv3_icc_iar1_read(uint32_t cpu, uint64_t val) "GICv3 ICC_IAR1 read cpu %x value 0x%" PRIx64 +gicv3_icc_eoir_write(uint32_t cpu, uint64_t val) "GICv3 ICC_EOIR write cpu %x value 0x%" PRIx64 +gicv3_icc_hppir0_read(uint32_t cpu, uint64_t val) "GICv3 ICC_HPPIR0 read cpu %x value 0x%" PRIx64 +gicv3_icc_hppir1_read(uint32_t cpu, uint64_t val) "GICv3 ICC_HPPIR1 read cpu %x value 0x%" PRIx64 +gicv3_icc_dir_write(uint32_t cpu, uint64_t val) "GICv3 ICC_DIR write cpu %x value 0x%" PRIx64 +gicv3_icc_rpr_read(uint32_t cpu, uint64_t val) "GICv3 ICC_RPR read cpu %x value 0x%" PRIx64 + +# hw/intc/arm_gicv3_dist.c +gicv3_dist_read(uint64_t offset, uint64_t data, unsigned size, bool secure) "GICv3 distributor read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u secure %d" +gicv3_dist_badread(uint64_t offset, unsigned size, bool secure) "GICv3 distributor read: offset 0x%" PRIx64 " size %u secure %d: error" +gicv3_dist_write(uint64_t offset, uint64_t data, unsigned size, bool secure) "GICv3 distributor write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u secure %d" +gicv3_dist_badwrite(uint64_t offset, uint64_t data, unsigned size, bool secure) "GICv3 distributor write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u secure %d: error" +gicv3_dist_set_irq(int irq, int level) "GICv3 distributor interrupt %d level changed to %d" + +# hw/intc/arm_gicv3_redist.c +gicv3_redist_read(uint32_t cpu, uint64_t offset, uint64_t data, unsigned size, bool secure) "GICv3 redistributor %x read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u secure %d" +gicv3_redist_badread(uint32_t cpu, uint64_t offset, unsigned size, bool secure) "GICv3 redistributor %x read: offset 0x%" PRIx64 " size %u secure %d: error" +gicv3_redist_write(uint32_t cpu, uint64_t offset, uint64_t data, unsigned size, bool secure) "GICv3 redistributor %x write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u secure %d" +gicv3_redist_badwrite(uint32_t cpu, uint64_t offset, uint64_t data, unsigned size, bool secure) "GICv3 redistributor %x write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u secure %d: error" +gicv3_redist_set_irq(uint32_t cpu, int irq, int level) "GICv3 redistributor %x interrupt %d level changed to %d" +gicv3_redist_send_sgi(uint32_t cpu, int irq) "GICv3 redistributor %x pending SGI %d" diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 8659be017..cd48f4204 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,17 +47,31 @@ static int get_cpu_index_by_dt_id(int cpu_dt_id) return -1; } -void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu) +void xics_cpu_destroy(XICSState *xics, PowerPCCPU *cpu) +{ + CPUState *cs = CPU(cpu); + ICPState *ss = &xics->ss[cs->cpu_index]; + + assert(cs->cpu_index < xics->nr_servers); + assert(cs == ss->cs); + + ss->output = NULL; + ss->cs = NULL; +} + +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)) { @@ -82,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); } @@ -104,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; @@ -114,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); } @@ -139,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; @@ -149,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) @@ -199,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; @@ -210,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; @@ -218,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; @@ -243,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; @@ -275,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) +{ + if (mfrr) { + *mfrr = ss->mfrr; + } + return ss->xirr; +} + +void icp_eoi(XICSState *xics, int server, uint32_t xirr) { - ICPState *ss = icp->ss + server; + 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; @@ -376,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; @@ -390,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); } } @@ -404,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); } } @@ -419,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); } } } @@ -458,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) @@ -466,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; @@ -543,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; @@ -644,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; } @@ -660,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)); @@ -680,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 9029d9ee0..edbd62fd1 100644 --- a/hw/intc/xics_kvm.c +++ b/hw/intc/xics_kvm.c @@ -31,6 +31,7 @@ #include "cpu.h" #include "hw/hw.h" #include "trace.h" +#include "sysemu/kvm.h" #include "hw/ppc/spapr.h" #include "hw/ppc/xics.h" #include "kvm_ppc.h" @@ -113,8 +114,10 @@ static void icp_kvm_reset(DeviceState *dev) icp->pending_priority = 0xff; icp->mfrr = 0xff; - /* Make all outputs are deasserted */ - qemu_set_irq(icp->output, 0); + /* Make all outputs as deasserted only if the CPU thread is in use */ + if (icp->output) { + qemu_set_irq(icp->output, 0); + } icp_set_kvm_state(icp, 1); } @@ -142,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, @@ -157,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)); @@ -201,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, @@ -235,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)); @@ -321,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(); } @@ -344,13 +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; - ss->cs = cs; - 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)); @@ -360,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); } } @@ -393,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 = { @@ -444,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; @@ -480,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) @@ -494,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, @@ -504,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 000000000..618826dac --- /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/ipack/ipack.c b/hw/ipack/ipack.c index 5f99ed9a7..6021e6d13 100644 --- a/hw/ipack/ipack.c +++ b/hw/ipack/ipack.c @@ -2,7 +2,7 @@ * QEMU IndustryPack emulation * * Copyright (C) 2012 Igalia, S.L. - * Author: Alberto Garcia <agarcia@igalia.com> + * Author: Alberto Garcia <berto@igalia.com> * * This code is licensed under the GNU GPL v2 or (at your option) any * later version. diff --git a/hw/ipack/tpci200.c b/hw/ipack/tpci200.c index fdda6f414..4dfa6b33f 100644 --- a/hw/ipack/tpci200.c +++ b/hw/ipack/tpci200.c @@ -2,7 +2,7 @@ * QEMU TEWS TPCI200 IndustryPack carrier emulation * * Copyright (C) 2012 Igalia, S.L. - * Author: Alberto Garcia <agarcia@igalia.com> + * Author: Alberto Garcia <berto@igalia.com> * * This code is licensed under the GNU GPL v2 or (at your option) any * later version. diff --git a/hw/ipmi/ipmi.c b/hw/ipmi/ipmi.c index 6adec1e99..f09f217e7 100644 --- a/hw/ipmi/ipmi.c +++ b/hw/ipmi/ipmi.c @@ -30,6 +30,13 @@ #include "qom/object_interfaces.h" #include "qapi/visitor.h" +static uint32_t ipmi_current_uuid = 1; + +uint32_t ipmi_next_uuid(void) +{ + return ipmi_current_uuid++; +} + static int ipmi_do_hw_op(IPMIInterface *s, enum ipmi_op op, int checkonly) { switch (op) { @@ -122,30 +129,3 @@ static void ipmi_register_types(void) } type_init(ipmi_register_types) - -static IPMIFwInfo *ipmi_fw_info; -static unsigned int ipmi_fw_info_len; - -static uint32_t current_uuid = 1; - -void ipmi_add_fwinfo(IPMIFwInfo *info, Error **errp) -{ - info->uuid = current_uuid++; - ipmi_fw_info = g_realloc(ipmi_fw_info, - sizeof(*ipmi_fw_info) * (ipmi_fw_info_len + 1)); - ipmi_fw_info[ipmi_fw_info_len] = *info; -} - -IPMIFwInfo *ipmi_first_fwinfo(void) -{ - return ipmi_fw_info; -} - -IPMIFwInfo *ipmi_next_fwinfo(IPMIFwInfo *current) -{ - current++; - if (current >= &ipmi_fw_info[ipmi_fw_info_len]) { - return NULL; - } - return current; -} diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c index fe12112a2..157879e17 100644 --- a/hw/ipmi/ipmi_bmc_extern.c +++ b/hw/ipmi/ipmi_bmc_extern.c @@ -190,7 +190,7 @@ static void ipmi_bmc_extern_handle_command(IPMIBmc *b, if (ibe->outlen) { /* We already have a command queued. Shouldn't ever happen. */ fprintf(stderr, "IPMI KCS: Got command when not finished with the" - " previous commmand\n"); + " previous command\n"); abort(); } diff --git a/hw/ipmi/isa_ipmi_bt.c b/hw/ipmi/isa_ipmi_bt.c index aaea12ecd..f03661715 100644 --- a/hw/ipmi/isa_ipmi_bt.c +++ b/hw/ipmi/isa_ipmi_bt.c @@ -390,16 +390,6 @@ static void ipmi_bt_init(IPMIInterface *ii, Error **errp) memory_region_init_io(&ib->io, NULL, &ipmi_bt_io_ops, ii, "ipmi-bt", 3); } -static void ipmi_bt_class_init(IPMIInterfaceClass *iic) -{ - iic->init = ipmi_bt_init; - iic->set_atn = ipmi_bt_set_atn; - iic->handle_rsp = ipmi_bt_handle_rsp; - iic->handle_if_event = ipmi_bt_handle_event; - iic->set_irq_enable = ipmi_bt_set_irq_enable; - iic->reset = ipmi_bt_handle_reset; -} - #define TYPE_ISA_IPMI_BT "isa-ipmi-bt" #define ISA_IPMI_BT(obj) OBJECT_CHECK(ISAIPMIBTDevice, (obj), \ @@ -409,9 +399,38 @@ typedef struct ISAIPMIBTDevice { ISADevice dev; int32_t isairq; IPMIBT bt; - IPMIFwInfo fwinfo; + uint32_t uuid; } ISAIPMIBTDevice; +static void ipmi_bt_get_fwinfo(struct IPMIInterface *ii, IPMIFwInfo *info) +{ + ISAIPMIBTDevice *iib = ISA_IPMI_BT(ii); + + info->interface_name = "bt"; + info->interface_type = IPMI_SMBIOS_BT; + info->ipmi_spec_major_revision = 2; + info->ipmi_spec_minor_revision = 0; + info->base_address = iib->bt.io_base; + info->register_length = iib->bt.io_length; + info->register_spacing = 1; + info->memspace = IPMI_MEMSPACE_IO; + info->irq_type = IPMI_LEVEL_IRQ; + info->interrupt_number = iib->isairq; + info->i2c_slave_address = iib->bt.bmc->slave_addr; + info->uuid = iib->uuid; +} + +static void ipmi_bt_class_init(IPMIInterfaceClass *iic) +{ + iic->init = ipmi_bt_init; + iic->set_atn = ipmi_bt_set_atn; + iic->handle_rsp = ipmi_bt_handle_rsp; + iic->handle_if_event = ipmi_bt_handle_event; + iic->set_irq_enable = ipmi_bt_set_irq_enable; + iic->reset = ipmi_bt_handle_reset; + iic->get_fwinfo = ipmi_bt_get_fwinfo; +} + static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp) { ISADevice *isadev = ISA_DEVICE(dev); @@ -424,6 +443,8 @@ static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp) return; } + iib->uuid = ipmi_next_uuid(); + iib->bt.bmc->intf = ii; iic->init(ii, errp); @@ -438,20 +459,6 @@ static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp) qdev_set_legacy_instance_id(dev, iib->bt.io_base, iib->bt.io_length); isa_register_ioport(isadev, &iib->bt.io, iib->bt.io_base); - - iib->fwinfo.interface_name = "bt"; - iib->fwinfo.interface_type = IPMI_SMBIOS_BT; - iib->fwinfo.ipmi_spec_major_revision = 2; - iib->fwinfo.ipmi_spec_minor_revision = 0; - iib->fwinfo.base_address = iib->bt.io_base; - iib->fwinfo.register_length = iib->bt.io_length; - iib->fwinfo.register_spacing = 1; - iib->fwinfo.memspace = IPMI_MEMSPACE_IO; - iib->fwinfo.irq_type = IPMI_LEVEL_IRQ; - iib->fwinfo.interrupt_number = iib->isairq; - iib->fwinfo.acpi_parent = "\\_SB.PCI0.ISA"; - iib->fwinfo.i2c_slave_address = iib->bt.bmc->slave_addr; - ipmi_add_fwinfo(&iib->fwinfo, errp); } static const VMStateDescription vmstate_ISAIPMIBTDevice = { diff --git a/hw/ipmi/isa_ipmi_kcs.c b/hw/ipmi/isa_ipmi_kcs.c index 2742ce06c..9a38f8a28 100644 --- a/hw/ipmi/isa_ipmi_kcs.c +++ b/hw/ipmi/isa_ipmi_kcs.c @@ -354,16 +354,6 @@ static void ipmi_kcs_init(IPMIInterface *ii, Error **errp) memory_region_init_io(&ik->io, NULL, &ipmi_kcs_io_ops, ii, "ipmi-kcs", 2); } -static void ipmi_kcs_class_init(IPMIInterfaceClass *iic) -{ - iic->init = ipmi_kcs_init; - iic->set_atn = ipmi_kcs_set_atn; - iic->handle_rsp = ipmi_kcs_handle_rsp; - iic->handle_if_event = ipmi_kcs_handle_event; - iic->set_irq_enable = ipmi_kcs_set_irq_enable; -} - - #define TYPE_ISA_IPMI_KCS "isa-ipmi-kcs" #define ISA_IPMI_KCS(obj) OBJECT_CHECK(ISAIPMIKCSDevice, (obj), \ TYPE_ISA_IPMI_KCS) @@ -372,9 +362,37 @@ typedef struct ISAIPMIKCSDevice { ISADevice dev; int32_t isairq; IPMIKCS kcs; - IPMIFwInfo fwinfo; + uint32_t uuid; } ISAIPMIKCSDevice; +static void ipmi_kcs_get_fwinfo(IPMIInterface *ii, IPMIFwInfo *info) +{ + ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(ii); + + info->interface_name = "kcs"; + info->interface_type = IPMI_SMBIOS_KCS; + info->ipmi_spec_major_revision = 2; + info->ipmi_spec_minor_revision = 0; + info->base_address = iik->kcs.io_base; + info->i2c_slave_address = iik->kcs.bmc->slave_addr; + info->register_length = iik->kcs.io_length; + info->register_spacing = 1; + info->memspace = IPMI_MEMSPACE_IO; + info->irq_type = IPMI_LEVEL_IRQ; + info->interrupt_number = iik->isairq; + info->uuid = iik->uuid; +} + +static void ipmi_kcs_class_init(IPMIInterfaceClass *iic) +{ + iic->init = ipmi_kcs_init; + iic->set_atn = ipmi_kcs_set_atn; + iic->handle_rsp = ipmi_kcs_handle_rsp; + iic->handle_if_event = ipmi_kcs_handle_event; + iic->set_irq_enable = ipmi_kcs_set_irq_enable; + iic->get_fwinfo = ipmi_kcs_get_fwinfo; +} + static void ipmi_isa_realize(DeviceState *dev, Error **errp) { ISADevice *isadev = ISA_DEVICE(dev); @@ -387,6 +405,8 @@ static void ipmi_isa_realize(DeviceState *dev, Error **errp) return; } + iik->uuid = ipmi_next_uuid(); + iik->kcs.bmc->intf = ii; iic->init(ii, errp); @@ -401,20 +421,6 @@ static void ipmi_isa_realize(DeviceState *dev, Error **errp) qdev_set_legacy_instance_id(dev, iik->kcs.io_base, iik->kcs.io_length); isa_register_ioport(isadev, &iik->kcs.io, iik->kcs.io_base); - - iik->fwinfo.interface_name = "kcs"; - iik->fwinfo.interface_type = IPMI_SMBIOS_KCS; - iik->fwinfo.ipmi_spec_major_revision = 2; - iik->fwinfo.ipmi_spec_minor_revision = 0; - iik->fwinfo.base_address = iik->kcs.io_base; - iik->fwinfo.i2c_slave_address = iik->kcs.bmc->slave_addr; - iik->fwinfo.register_length = iik->kcs.io_length; - iik->fwinfo.register_spacing = 1; - iik->fwinfo.memspace = IPMI_MEMSPACE_IO; - iik->fwinfo.irq_type = IPMI_LEVEL_IRQ; - iik->fwinfo.interrupt_number = iik->isairq; - iik->fwinfo.acpi_parent = "\\_SB.PCI0.ISA"; - ipmi_add_fwinfo(&iik->fwinfo, errp); } const VMStateDescription vmstate_ISAIPMIKCSDevice = { diff --git a/hw/isa/isa-bus.c b/hw/isa/isa-bus.c index 7aa115caf..ce74db232 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 99cd3ba9e..10d1ee8b9 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -47,8 +47,7 @@ #include "hw/pci/pci_bus.h" #include "exec/address-spaces.h" #include "sysemu/sysemu.h" - -static int ich9_lpc_sci_irq(ICH9LPCState *lpc); +#include "qom/cpu.h" /*****************************************************************************/ /* ICH9 LPC PCI to ISA bridge */ @@ -96,8 +95,8 @@ static void ich9_cc_update(ICH9LPCState *lpc) /* * D30: DMI2PCI bridge - * It is arbitrarily decided how INTx lines of PCI devicesbehind the bridge - * are connected to pirq lines. Our choice is PIRQ[E-H]. + * It is arbitrarily decided how INTx lines of PCI devices behind + * the bridge are connected to pirq lines. Our choice is PIRQ[E-H]. * INT[A-D] are connected to PIRQ[E-H] */ for (pci_intx = 0; pci_intx < PCI_NUM_PINS; pci_intx++) { @@ -203,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. */ @@ -251,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 @@ -359,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); } } @@ -402,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 */ @@ -444,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; @@ -457,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); @@ -496,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; @@ -576,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); } @@ -607,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(), @@ -617,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); @@ -634,30 +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); -} - -static void ich9_device_plug_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - ICH9LPCState *lpc = ICH9_LPC_DEVICE(hotplug_dev); - - ich9_pm_device_plug_cb(&lpc->pm, dev, errp); -} -static void ich9_device_unplug_request_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - ICH9LPCState *lpc = ICH9_LPC_DEVICE(hotplug_dev); - - ich9_pm_device_unplug_request_cb(&lpc->pm, dev, errp); -} - -static void ich9_device_unplug_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - ICH9LPCState *lpc = ICH9_LPC_DEVICE(hotplug_dev); + qdev_init_gpio_out_named(dev, lpc->gsi, ICH9_GPIO_GSI, GSI_NUM_PINS); - ich9_pm_device_unplug_cb(&lpc->pm, dev, errp); + isa_bus_irqs(isa_bus, lpc->gsi); } static bool ich9_rst_cnt_needed(void *opaque) @@ -702,6 +692,13 @@ static Property ich9_lpc_properties[] = { DEFINE_PROP_END_OF_LIST(), }; +static void ich9_send_gpe(AcpiDeviceIf *adev, AcpiEventStatusBits ev) +{ + ICH9LPCState *s = ICH9_LPC_DEVICE(adev); + + acpi_send_gpe_event(&s->pm.acpi_regs, s->pm.irq, ev); +} + static void ich9_lpc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -725,10 +722,12 @@ static void ich9_lpc_class_init(ObjectClass *klass, void *data) * pc_q35_init() */ dc->cannot_instantiate_with_device_add_yet = true; - hc->plug = ich9_device_plug_cb; - hc->unplug_request = ich9_device_unplug_request_cb; - hc->unplug = ich9_device_unplug_cb; + hc->plug = ich9_pm_device_plug_cb; + hc->unplug_request = ich9_pm_device_unplug_request_cb; + 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/isa/trace-events b/hw/isa/trace-events new file mode 100644 index 000000000..9faca41a9 --- /dev/null +++ b/hw/isa/trace-events @@ -0,0 +1,9 @@ +# See docs/tracing.txt for syntax documentation. + +# hw/isa/pc87312.c +pc87312_io_read(uint32_t addr, uint32_t val) "read addr=%x val=%x" +pc87312_io_write(uint32_t addr, uint32_t val) "write addr=%x val=%x" +pc87312_info_floppy(uint32_t base) "base 0x%x" +pc87312_info_ide(uint32_t base) "base 0x%x" +pc87312_info_parallel(uint32_t base, uint32_t irq) "base 0x%x, irq %u" +pc87312_info_serial(int n, uint32_t base, uint32_t irq) "id=%d, base 0x%x, irq %u" diff --git a/hw/lm32/lm32.h b/hw/lm32/lm32.h index 18aa6fdc1..db9eb29ea 100644 --- a/hw/lm32/lm32.h +++ b/hw/lm32/lm32.h @@ -1,5 +1,5 @@ #ifndef HW_LM32_H -#define HW_LM32_H 1 +#define HW_LM32_H #include "hw/char/lm32_juart.h" @@ -16,14 +16,31 @@ static inline DeviceState *lm32_pic_init(qemu_irq cpu_irq) return dev; } -static inline DeviceState *lm32_juart_init(void) +static inline DeviceState *lm32_juart_init(CharDriverState *chr) { DeviceState *dev; dev = qdev_create(NULL, TYPE_LM32_JUART); + qdev_prop_set_chr(dev, "chardev", chr); qdev_init_nofail(dev); return dev; } +static inline DeviceState *lm32_uart_create(hwaddr addr, + qemu_irq irq, + CharDriverState *chr) +{ + DeviceState *dev; + SysBusDevice *s; + + dev = qdev_create(NULL, "lm32-uart"); + s = SYS_BUS_DEVICE(dev); + qdev_prop_set_chr(dev, "chardev", chr); + qdev_init_nofail(dev); + sysbus_mmio_map(s, 0, addr); + sysbus_connect_irq(s, 0, irq); + return dev; +} + #endif diff --git a/hw/lm32/lm32_boards.c b/hw/lm32/lm32_boards.c index c0290560f..8f0c3079d 100644 --- a/hw/lm32/lm32_boards.c +++ b/hw/lm32/lm32_boards.c @@ -31,6 +31,7 @@ #include "lm32_hwsetup.h" #include "lm32.h" #include "exec/address-spaces.h" +#include "sysemu/sysemu.h" typedef struct { LM32CPU *cpu; @@ -131,12 +132,12 @@ static void lm32_evr_init(MachineState *machine) irq[i] = qdev_get_gpio_in(env->pic_state, i); } - sysbus_create_simple("lm32-uart", uart0_base, irq[uart0_irq]); + lm32_uart_create(uart0_base, irq[uart0_irq], serial_hds[0]); sysbus_create_simple("lm32-timer", timer0_base, irq[timer0_irq]); sysbus_create_simple("lm32-timer", timer1_base, irq[timer1_irq]); /* make sure juart isn't the first chardev */ - env->juart_state = lm32_juart_init(); + env->juart_state = lm32_juart_init(serial_hds[1]); reset_info->bootstrap_pc = flash_base; @@ -232,13 +233,13 @@ static void lm32_uclinux_init(MachineState *machine) irq[i] = qdev_get_gpio_in(env->pic_state, i); } - sysbus_create_simple("lm32-uart", uart0_base, irq[uart0_irq]); + lm32_uart_create(uart0_base, irq[uart0_irq], serial_hds[0]); sysbus_create_simple("lm32-timer", timer0_base, irq[timer0_irq]); sysbus_create_simple("lm32-timer", timer1_base, irq[timer1_irq]); sysbus_create_simple("lm32-timer", timer2_base, irq[timer2_irq]); /* make sure juart isn't the first chardev */ - env->juart_state = lm32_juart_init(); + env->juart_state = lm32_juart_init(serial_hds[1]); reset_info->bootstrap_pc = flash_base; diff --git a/hw/lm32/milkymist-hw.h b/hw/lm32/milkymist-hw.h index c8dfb4d2d..4418b44ca 100644 --- a/hw/lm32/milkymist-hw.h +++ b/hw/lm32/milkymist-hw.h @@ -1,15 +1,17 @@ -#ifndef QEMU_HW_MILKYMIST_H -#define QEMU_HW_MILKYMIST_H +#ifndef QEMU_HW_MILKYMIST_HW_H +#define QEMU_HW_MILKYMIST_HW_H #include "hw/qdev.h" #include "net/net.h" static inline DeviceState *milkymist_uart_create(hwaddr base, - qemu_irq irq) + qemu_irq irq, + CharDriverState *chr) { DeviceState *dev; dev = qdev_create(NULL, "milkymist-uart"); + qdev_prop_set_chr(dev, "chardev", chr); qdev_init_nofail(dev); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); @@ -108,10 +110,6 @@ static inline DeviceState *milkymist_tmu2_create(hwaddr base, int nelements; int ver_major, ver_minor; - if (display_type == DT_NOGRAPHIC) { - return NULL; - } - /* check that GLX will work */ d = XOpenDisplay(NULL); if (d == NULL) { @@ -205,4 +203,4 @@ static inline DeviceState *milkymist_softusb_create(hwaddr base, return dev; } -#endif /* QEMU_HW_MILKYMIST_H */ +#endif /* QEMU_HW_MILKYMIST_HW_H */ diff --git a/hw/lm32/milkymist.c b/hw/lm32/milkymist.c index 96e6f4dc2..5cae0f19d 100644 --- a/hw/lm32/milkymist.c +++ b/hw/lm32/milkymist.c @@ -159,7 +159,7 @@ milkymist_init(MachineState *machine) } g_free(bios_filename); - milkymist_uart_create(0x60000000, irq[0]); + milkymist_uart_create(0x60000000, irq[0], serial_hds[0]); milkymist_sysctl_create(0x60001000, irq[1], irq[2], irq[3], 80000000, 0x10014d31, 0x0000041f, 0x00000001); milkymist_hpdmc_create(0x60002000); @@ -167,13 +167,15 @@ milkymist_init(MachineState *machine) milkymist_memcard_create(0x60004000); milkymist_ac97_create(0x60005000, irq[4], irq[5], irq[6], irq[7]); milkymist_pfpu_create(0x60006000, irq[8]); - milkymist_tmu2_create(0x60007000, irq[9]); + if (machine->enable_graphics) { + milkymist_tmu2_create(0x60007000, irq[9]); + } milkymist_minimac2_create(0x60008000, 0x30000000, irq[10], irq[11]); milkymist_softusb_create(0x6000f000, irq[15], 0x20000000, 0x1000, 0x20020000, 0x2000); /* make sure juart isn't the first chardev */ - env->juart_state = lm32_juart_init(); + env->juart_state = lm32_juart_init(serial_hds[1]); if (kernel_filename) { uint64_t entry; diff --git a/hw/mem/nvdimm.c b/hw/mem/nvdimm.c index 0a602f28b..7895805a2 100644 --- a/hw/mem/nvdimm.c +++ b/hw/mem/nvdimm.c @@ -23,20 +23,153 @@ */ #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); + g_free(path); + 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 9e7de5682..9e8dab0e8 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) @@ -354,8 +358,9 @@ static void pc_dimm_get_size(Object *obj, Visitor *v, const char *name, int64_t value; MemoryRegion *mr; PCDIMMDevice *dimm = PC_DIMM(obj); + PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(obj); - mr = host_memory_backend_get_memory(dimm->hostmem, errp); + mr = ddc->get_memory_region(dimm); value = memory_region_size(mr); visit_type_int(v, name, &value, errp); @@ -364,14 +369,9 @@ static void pc_dimm_get_size(Object *obj, Visitor *v, const char *name, static void pc_dimm_check_memdev_is_busy(Object *obj, const char *name, Object *val, Error **errp) { - MemoryRegion *mr; Error *local_err = NULL; - mr = host_memory_backend_get_memory(MEMORY_BACKEND(val), &local_err); - if (local_err) { - goto out; - } - if (memory_region_is_mapped(mr)) { + if (host_memory_backend_is_mapped(MEMORY_BACKEND(val))) { char *path = object_get_canonical_path_component(val); error_setg(&local_err, "can't use already busy memdev: %s", path); g_free(path); @@ -379,7 +379,6 @@ static void pc_dimm_check_memdev_is_busy(Object *obj, const char *name, qdev_prop_allow_set_link_before_realize(obj, name, val, &local_err); } -out: error_propagate(errp, local_err); } @@ -399,6 +398,7 @@ static void pc_dimm_init(Object *obj) static void pc_dimm_realize(DeviceState *dev, Error **errp) { PCDIMMDevice *dimm = PC_DIMM(dev); + PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); if (!dimm->hostmem) { error_setg(errp, "'" PC_DIMM_MEMDEV_PROP "' property is not set"); @@ -411,6 +411,19 @@ static void pc_dimm_realize(DeviceState *dev, Error **errp) dimm->node, nb_numa_nodes ? nb_numa_nodes : 1); return; } + + if (ddc->realize) { + ddc->realize(dimm, errp); + } + + host_memory_backend_set_mapped(dimm->hostmem, true); +} + +static void pc_dimm_unrealize(DeviceState *dev, Error **errp) +{ + PCDIMMDevice *dimm = PC_DIMM(dev); + + host_memory_backend_set_mapped(dimm->hostmem, false); } static MemoryRegion *pc_dimm_get_memory_region(PCDIMMDevice *dimm) @@ -418,16 +431,23 @@ 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); PCDIMMDeviceClass *ddc = PC_DIMM_CLASS(oc); dc->realize = pc_dimm_realize; + dc->unrealize = pc_dimm_unrealize; dc->props = pc_dimm_properties; 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/boot.h b/hw/microblaze/boot.h index 0eb7f8e4f..dd1090d8b 100644 --- a/hw/microblaze/boot.h +++ b/hw/microblaze/boot.h @@ -1,5 +1,5 @@ -#ifndef __MICROBLAZE_BOOT__ -#define __MICROBLAZE_BOOT__ +#ifndef MICROBLAZE_BOOT_H +#define MICROBLAZE_BOOT_H #include "hw/hw.h" @@ -9,4 +9,4 @@ void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base, const char *dtb_filename, void (*machine_cpu_reset)(MicroBlazeCPU *)); -#endif /* __MICROBLAZE_BOOT __ */ +#endif /* MICROBLAZE_BOOT_H */ diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c index 07527b677..4968bdbb2 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/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c index f821e1cfe..423bcd7f6 100644 --- a/hw/microblaze/petalogix_s3adsp1800_mmu.c +++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c @@ -36,6 +36,7 @@ #include "hw/boards.h" #include "sysemu/block-backend.h" #include "exec/address-spaces.h" +#include "hw/char/xilinx_uartlite.h" #include "boot.h" @@ -103,8 +104,8 @@ petalogix_s3adsp1800_init(MachineState *machine) irq[i] = qdev_get_gpio_in(dev, i); } - sysbus_create_simple("xlnx.xps-uartlite", UARTLITE_BASEADDR, - irq[UARTLITE_IRQ]); + xilinx_uartlite_create(UARTLITE_BASEADDR, irq[UARTLITE_IRQ], + serial_hds[0]); /* 2 timers at irq 2 @ 62 Mhz. */ dev = qdev_create(NULL, "xlnx.xps-timer"); diff --git a/hw/mips/cps.c b/hw/mips/cps.c index 1bafbbb27..4ef337d5c 100644 --- a/hw/mips/cps.c +++ b/hw/mips/cps.c @@ -26,13 +26,8 @@ qemu_irq get_cps_irq(MIPSCPSState *s, int pin_number) { - MIPSCPU *cpu = MIPS_CPU(first_cpu); - CPUMIPSState *env = &cpu->env; - assert(pin_number < s->num_irq); - - /* TODO: return GIC pins once implemented */ - return env->irq[pin_number]; + return s->gic.irq_state[pin_number].irq; } static void mips_cps_init(Object *obj) @@ -78,14 +73,15 @@ static void mips_cps_realize(DeviceState *dev, Error **errp) for (i = 0; i < s->num_vp; i++) { cpu = cpu_mips_init(s->cpu_model); if (cpu == NULL) { - error_setg(errp, "%s: CPU initialization failed\n", __func__); + error_setg(errp, "%s: CPU initialization failed", __func__); return; } - env = &cpu->env; /* Init internal devices */ - cpu_mips_irq_init_cpu(env); - cpu_mips_clock_init(env); + cpu_mips_irq_init_cpu(cpu); + cpu_mips_clock_init(cpu); + + env = &cpu->env; if (cpu_mips_itu_supported(env)) { itu_present = true; /* Attach ITC Tag to the VP */ @@ -129,6 +125,21 @@ static void mips_cps_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(&s->container, 0, sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->cpc), 0)); + /* Global Interrupt Controller */ + object_initialize(&s->gic, sizeof(s->gic), TYPE_MIPS_GIC); + qdev_set_parent_bus(DEVICE(&s->gic), sysbus_get_default()); + + object_property_set_int(OBJECT(&s->gic), s->num_vp, "num-vp", &err); + object_property_set_int(OBJECT(&s->gic), 128, "num-irq", &err); + object_property_set_bool(OBJECT(&s->gic), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + + memory_region_add_subregion(&s->container, 0, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->gic), 0)); + /* Global Configuration Registers */ gcr_base = env->CP0_CMGCRBase << 4; @@ -138,6 +149,7 @@ static void mips_cps_realize(DeviceState *dev, Error **errp) object_property_set_int(OBJECT(&s->gcr), s->num_vp, "num-vp", &err); object_property_set_int(OBJECT(&s->gcr), 0x800, "gcr-rev", &err); object_property_set_int(OBJECT(&s->gcr), gcr_base, "gcr-base", &err); + object_property_set_link(OBJECT(&s->gcr), OBJECT(&s->gic.mr), "gic", &err); object_property_set_link(OBJECT(&s->gcr), OBJECT(&s->cpc.mr), "cpc", &err); object_property_set_bool(OBJECT(&s->gcr), true, "realized", &err); if (err != NULL) { @@ -151,7 +163,7 @@ static void mips_cps_realize(DeviceState *dev, Error **errp) static Property mips_cps_properties[] = { DEFINE_PROP_UINT32("num-vp", MIPSCPSState, num_vp, 1), - DEFINE_PROP_UINT32("num-irq", MIPSCPSState, num_irq, 8), + DEFINE_PROP_UINT32("num-irq", MIPSCPSState, num_irq, 256), DEFINE_PROP_STRING("cpu-model", MIPSCPSState, cpu_model), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/mips/cputimer.c b/hw/mips/cputimer.c index efb227d06..8a166b3ea 100644 --- a/hw/mips/cputimer.c +++ b/hw/mips/cputimer.c @@ -151,8 +151,10 @@ static void mips_timer_cb (void *opaque) env->CP0_Count--; } -void cpu_mips_clock_init (CPUMIPSState *env) +void cpu_mips_clock_init (MIPSCPU *cpu) { + CPUMIPSState *env = &cpu->env; + /* * If we're in KVM mode, don't create the periodic timer, that is handled in * kernel. diff --git a/hw/mips/gt64xxx_pci.c b/hw/mips/gt64xxx_pci.c index 3f4523df2..4811843ab 100644 --- a/hw/mips/gt64xxx_pci.c +++ b/hw/mips/gt64xxx_pci.c @@ -1167,7 +1167,6 @@ PCIBus *gt64120_register(qemu_irq *pic) DeviceState *dev; dev = qdev_create(NULL, TYPE_GT64120_PCI_HOST_BRIDGE); - qdev_init_nofail(dev); d = GT64120_PCI_HOST_BRIDGE(dev); phb = PCI_HOST_BRIDGE(dev); memory_region_init(&d->pci0_mem, OBJECT(dev), "pci0-mem", UINT32_MAX); @@ -1178,6 +1177,7 @@ PCIBus *gt64120_register(qemu_irq *pic) &d->pci0_mem, get_system_io(), PCI_DEVFN(18, 0), 4, TYPE_PCI_BUS); + qdev_init_nofail(dev); memory_region_init_io(&d->ISD_mem, OBJECT(dev), &isd_mem_ops, d, "isd-mem", 0x1000); pci_create_simple(phb->bus, PCI_DEVFN(0, 0), "gt64120_pci"); diff --git a/hw/mips/mips_fulong2e.c b/hw/mips/mips_fulong2e.c index bdb716e72..889cdc7ca 100644 --- a/hw/mips/mips_fulong2e.c +++ b/hw/mips/mips_fulong2e.c @@ -334,8 +334,8 @@ static void mips_fulong2e_init(MachineState *machine) } /* Init internal devices */ - cpu_mips_irq_init_cpu(env); - cpu_mips_clock_init(env); + cpu_mips_irq_init_cpu(cpu); + cpu_mips_clock_init(cpu); /* North bridge, Bonito --> IP2 */ pci_bus = bonito_init((qemu_irq *)&(env->irq[2])); diff --git a/hw/mips/mips_int.c b/hw/mips/mips_int.c index 59081f9d1..48192d22f 100644 --- a/hw/mips/mips_int.c +++ b/hw/mips/mips_int.c @@ -58,8 +58,9 @@ static void cpu_mips_irq_request(void *opaque, int irq, int level) } } -void cpu_mips_irq_init_cpu(CPUMIPSState *env) +void cpu_mips_irq_init_cpu(MIPSCPU *cpu) { + CPUMIPSState *env = &cpu->env; qemu_irq *qi; int i; diff --git a/hw/mips/mips_jazz.c b/hw/mips/mips_jazz.c index ac7c64125..73f6c9fac 100644 --- a/hw/mips/mips_jazz.c +++ b/hw/mips/mips_jazz.c @@ -201,8 +201,8 @@ static void mips_jazz_init(MachineState *machine, } /* Init CPU internal devices */ - cpu_mips_irq_init_cpu(env); - cpu_mips_clock_init(env); + cpu_mips_irq_init_cpu(cpu); + cpu_mips_clock_init(cpu); /* Chipset */ rc4030 = rc4030_init(&dmas, &rc4030_dma_mr); diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index fa769e5c0..e90857ee0 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -727,7 +727,7 @@ static void write_bootloader(uint8_t *base, int64_t run_addr, stl_p(p++, 0x00000000); /* nop */ stl_p(p++, 0x0ff0021c); /* jal 870 */ stl_p(p++, 0x00000000); /* nop */ - stl_p(p++, 0x08000205); /* j 814 */ + stl_p(p++, 0x1000fff9); /* b 814 */ stl_p(p++, 0x00000000); /* nop */ stl_p(p++, 0x01a00009); /* jalr t5 */ stl_p(p++, 0x01602021); /* move a0,t3 */ @@ -923,11 +923,10 @@ static void create_cpu_without_cps(const char *cpu_model, fprintf(stderr, "Unable to find CPU definition\n"); exit(1); } - env = &cpu->env; /* Init internal devices */ - cpu_mips_irq_init_cpu(env); - cpu_mips_clock_init(env); + cpu_mips_irq_init_cpu(cpu); + cpu_mips_clock_init(cpu); qemu_register_reset(main_cpu_reset, cpu); } @@ -956,9 +955,7 @@ static void create_cps(MaltaState *s, const char *cpu_model, sysbus_mmio_map_overlap(SYS_BUS_DEVICE(s->cps), 0, 0, 1); - /* FIXME: When GIC is present then we should use GIC's IRQ 3. - Until then CPS exposes CPU's IRQs thus use the default IRQ 2. */ - *i8259_irq = get_cps_irq(s->cps, 2); + *i8259_irq = get_cps_irq(s->cps, 3); *cbus_irq = NULL; } diff --git a/hw/mips/mips_mipssim.c b/hw/mips/mips_mipssim.c index a2c2a1646..1b9119500 100644 --- a/hw/mips/mips_mipssim.c +++ b/hw/mips/mips_mipssim.c @@ -216,8 +216,8 @@ mips_mipssim_init(MachineState *machine) } /* Init CPU internal devices. */ - cpu_mips_irq_init_cpu(env); - cpu_mips_clock_init(env); + cpu_mips_irq_init_cpu(cpu); + cpu_mips_clock_init(cpu); /* Register 64 KB of ISA IO space at 0x1fd00000. */ memory_region_init_alias(isa, NULL, "isa_mmio", diff --git a/hw/mips/mips_r4k.c b/hw/mips/mips_r4k.c index 21aca981c..16a59c779 100644 --- a/hw/mips/mips_r4k.c +++ b/hw/mips/mips_r4k.c @@ -267,8 +267,8 @@ void mips_r4k_init(MachineState *machine) } /* Init CPU internal devices */ - cpu_mips_irq_init_cpu(env); - cpu_mips_clock_init(env); + cpu_mips_irq_init_cpu(cpu); + cpu_mips_clock_init(cpu); /* ISA bus: IO space at 0x14000000, mem space at 0x10000000 */ memory_region_init_alias(isa_io, NULL, "isa-io", diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 93f952880..4cfbd1024 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -29,6 +29,7 @@ obj-$(CONFIG_IMX) += imx_ccm.o obj-$(CONFIG_IMX) += imx31_ccm.o obj-$(CONFIG_IMX) += imx25_ccm.o obj-$(CONFIG_IMX) += imx6_ccm.o +obj-$(CONFIG_IMX) += imx6_src.o obj-$(CONFIG_MILKYMIST) += milkymist-hpdmc.o obj-$(CONFIG_MILKYMIST) += milkymist-pfpu.o obj-$(CONFIG_MAINSTONE) += mst_fpga.o @@ -50,3 +51,5 @@ obj-$(CONFIG_MIPS_ITU) += mips_itu.o obj-$(CONFIG_PVPANIC) += pvpanic.o obj-$(CONFIG_EDU) += edu.o obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o +obj-$(CONFIG_AUX) += auxbus.o +obj-$(CONFIG_ASPEED_SOC) += aspeed_scu.o diff --git a/hw/misc/arm11scu.c b/hw/misc/arm11scu.c index 5e54b494b..7042ce11e 100644 --- a/hw/misc/arm11scu.c +++ b/hw/misc/arm11scu.c @@ -10,6 +10,7 @@ #include "qemu/osdep.h" #include "hw/misc/arm11scu.h" +#include "qemu/log.h" static uint64_t mpcore_scu_read(void *opaque, hwaddr offset, unsigned size) diff --git a/hw/misc/arm_integrator_debug.c b/hw/misc/arm_integrator_debug.c index 902605fef..8a5f29559 100644 --- a/hw/misc/arm_integrator_debug.c +++ b/hw/misc/arm_integrator_debug.c @@ -19,6 +19,7 @@ #include "hw/sysbus.h" #include "exec/address-spaces.h" #include "hw/misc/arm_integrator_debug.h" +#include "qemu/log.h" #define INTEGRATOR_DEBUG(obj) \ OBJECT_CHECK(IntegratorDebugState, (obj), TYPE_INTEGRATOR_DEBUG) diff --git a/hw/misc/arm_l2x0.c b/hw/misc/arm_l2x0.c index 7e179f1a4..66a0787c4 100644 --- a/hw/misc/arm_l2x0.c +++ b/hw/misc/arm_l2x0.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" +#include "qemu/log.h" /* L2C-310 r3p2 */ #define CACHE_ID 0x410000c8 @@ -158,14 +159,14 @@ static const MemoryRegionOps l2x0_mem_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int l2x0_priv_init(SysBusDevice *dev) +static void l2x0_priv_init(Object *obj) { - L2x0State *s = ARM_L2X0(dev); + L2x0State *s = ARM_L2X0(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); - memory_region_init_io(&s->iomem, OBJECT(dev), &l2x0_mem_ops, s, + memory_region_init_io(&s->iomem, obj, &l2x0_mem_ops, s, "l2x0_cc", 0x1000); sysbus_init_mmio(dev, &s->iomem); - return 0; } static Property l2x0_properties[] = { @@ -175,10 +176,8 @@ static Property l2x0_properties[] = { static void l2x0_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - k->init = l2x0_priv_init; dc->vmsd = &vmstate_l2x0; dc->props = l2x0_properties; dc->reset = l2x0_priv_reset; @@ -188,6 +187,7 @@ static const TypeInfo l2x0_info = { .name = TYPE_ARM_L2X0, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(L2x0State), + .instance_init = l2x0_priv_init, .class_init = l2x0_class_init, }; diff --git a/hw/misc/arm_sysctl.c b/hw/misc/arm_sysctl.c index 34d90d523..852400870 100644 --- a/hw/misc/arm_sysctl.c +++ b/hw/misc/arm_sysctl.c @@ -14,6 +14,7 @@ #include "hw/sysbus.h" #include "hw/arm/primecell.h" #include "sysemu/sysemu.h" +#include "qemu/log.h" #define LOCK_VALUE 0xa05f diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c new file mode 100644 index 000000000..c7e2c8263 --- /dev/null +++ b/hw/misc/aspeed_scu.c @@ -0,0 +1,282 @@ +/* + * 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 + +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, }; + +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_strap2, 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/auxbus.c b/hw/misc/auxbus.c new file mode 100644 index 000000000..e4a7ba41d --- /dev/null +++ b/hw/misc/auxbus.c @@ -0,0 +1,292 @@ +/* + * auxbus.c + * + * Copyright 2015 : GreenSocs Ltd + * http://www.greensocs.com/ , email: info@greensocs.com + * + * Developed by : + * Frederic Konrad <fred.konrad@greensocs.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option)any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + * + */ + +/* + * This is an implementation of the AUX bus for VESA Display Port v1.1a. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/misc/auxbus.h" +#include "hw/i2c/i2c.h" +#include "monitor/monitor.h" + +#ifndef DEBUG_AUX +#define DEBUG_AUX 0 +#endif + +#define DPRINTF(fmt, ...) do { \ + if (DEBUG_AUX) { \ + qemu_log("aux: " fmt , ## __VA_ARGS__); \ + } \ +} while (0); + +#define TYPE_AUXTOI2C "aux-to-i2c-bridge" +#define AUXTOI2C(obj) OBJECT_CHECK(AUXTOI2CState, (obj), TYPE_AUXTOI2C) + +static void aux_slave_dev_print(Monitor *mon, DeviceState *dev, int indent); +static inline I2CBus *aux_bridge_get_i2c_bus(AUXTOI2CState *bridge); + +/* aux-bus implementation (internal not public) */ +static void aux_bus_class_init(ObjectClass *klass, void *data) +{ + BusClass *k = BUS_CLASS(klass); + + /* AUXSlave has an MMIO so we need to change the way we print information + * in monitor. + */ + k->print_dev = aux_slave_dev_print; +} + +AUXBus *aux_init_bus(DeviceState *parent, const char *name) +{ + AUXBus *bus; + + bus = AUX_BUS(qbus_create(TYPE_AUX_BUS, parent, name)); + bus->bridge = AUXTOI2C(qdev_create(BUS(bus), TYPE_AUXTOI2C)); + + /* Memory related. */ + bus->aux_io = g_malloc(sizeof(*bus->aux_io)); + memory_region_init(bus->aux_io, OBJECT(bus), "aux-io", (1 << 20)); + address_space_init(&bus->aux_addr_space, bus->aux_io, "aux-io"); + return bus; +} + +static void aux_bus_map_device(AUXBus *bus, AUXSlave *dev, hwaddr addr) +{ + memory_region_add_subregion(bus->aux_io, addr, dev->mmio); +} + +static bool aux_bus_is_bridge(AUXBus *bus, DeviceState *dev) +{ + return (dev == DEVICE(bus->bridge)); +} + +I2CBus *aux_get_i2c_bus(AUXBus *bus) +{ + return aux_bridge_get_i2c_bus(bus->bridge); +} + +AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address, + uint8_t len, uint8_t *data) +{ + AUXReply ret = AUX_NACK; + I2CBus *i2c_bus = aux_get_i2c_bus(bus); + size_t i; + bool is_write = false; + + DPRINTF("request at address 0x%" PRIX32 ", command %u, len %u\n", address, + cmd, len); + + switch (cmd) { + /* + * Forward the request on the AUX bus.. + */ + case WRITE_AUX: + case READ_AUX: + is_write = cmd == READ_AUX ? false : true; + for (i = 0; i < len; i++) { + if (!address_space_rw(&bus->aux_addr_space, address++, + MEMTXATTRS_UNSPECIFIED, data++, 1, + is_write)) { + ret = AUX_I2C_ACK; + } else { + ret = AUX_NACK; + break; + } + } + break; + /* + * Classic I2C transactions.. + */ + case READ_I2C: + case WRITE_I2C: + is_write = cmd == READ_I2C ? false : true; + if (i2c_bus_busy(i2c_bus)) { + i2c_end_transfer(i2c_bus); + } + + if (i2c_start_transfer(i2c_bus, address, is_write)) { + ret = AUX_I2C_NACK; + break; + } + + ret = AUX_I2C_ACK; + while (len > 0) { + if (i2c_send_recv(i2c_bus, data++, is_write) < 0) { + ret = AUX_I2C_NACK; + break; + } + len--; + } + i2c_end_transfer(i2c_bus); + break; + /* + * I2C MOT transactions. + * + * Here we send a start when: + * - We didn't start transaction yet. + * - We had a READ and we do a WRITE. + * - We changed the address. + */ + case WRITE_I2C_MOT: + case READ_I2C_MOT: + is_write = cmd == READ_I2C_MOT ? false : true; + ret = AUX_I2C_NACK; + if (!i2c_bus_busy(i2c_bus)) { + /* + * No transactions started.. + */ + if (i2c_start_transfer(i2c_bus, address, is_write)) { + break; + } + } else if ((address != bus->last_i2c_address) || + (bus->last_transaction != cmd)) { + /* + * Transaction started but we need to restart.. + */ + i2c_end_transfer(i2c_bus); + if (i2c_start_transfer(i2c_bus, address, is_write)) { + break; + } + } + + bus->last_transaction = cmd; + bus->last_i2c_address = address; + while (len > 0) { + if (i2c_send_recv(i2c_bus, data++, is_write) < 0) { + i2c_end_transfer(i2c_bus); + break; + } + len--; + } + if (len == 0) { + ret = AUX_I2C_ACK; + } + break; + default: + DPRINTF("Not implemented!\n"); + return AUX_NACK; + } + + DPRINTF("reply: %u\n", ret); + return ret; +} + +static const TypeInfo aux_bus_info = { + .name = TYPE_AUX_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(AUXBus), + .class_init = aux_bus_class_init +}; + +/* aux-i2c implementation (internal not public) */ +struct AUXTOI2CState { + /*< private >*/ + DeviceState parent_obj; + + /*< public >*/ + I2CBus *i2c_bus; +}; + +static void aux_bridge_init(Object *obj) +{ + AUXTOI2CState *s = AUXTOI2C(obj); + + s->i2c_bus = i2c_init_bus(DEVICE(obj), "aux-i2c"); +} + +static inline I2CBus *aux_bridge_get_i2c_bus(AUXTOI2CState *bridge) +{ + return bridge->i2c_bus; +} + +static const TypeInfo aux_to_i2c_type_info = { + .name = TYPE_AUXTOI2C, + .parent = TYPE_DEVICE, + .instance_size = sizeof(AUXTOI2CState), + .instance_init = aux_bridge_init +}; + +/* aux-slave implementation */ +static void aux_slave_dev_print(Monitor *mon, DeviceState *dev, int indent) +{ + AUXBus *bus = AUX_BUS(qdev_get_parent_bus(dev)); + AUXSlave *s; + + /* Don't print anything if the device is I2C "bridge". */ + if (aux_bus_is_bridge(bus, dev)) { + return; + } + + s = AUX_SLAVE(dev); + + monitor_printf(mon, "%*smemory " TARGET_FMT_plx "/" TARGET_FMT_plx "\n", + indent, "", + object_property_get_int(OBJECT(s->mmio), "addr", NULL), + memory_region_size(s->mmio)); +} + +DeviceState *aux_create_slave(AUXBus *bus, const char *type, uint32_t addr) +{ + DeviceState *dev; + + dev = DEVICE(object_new(type)); + assert(dev); + qdev_set_parent_bus(dev, &bus->qbus); + qdev_init_nofail(dev); + aux_bus_map_device(AUX_BUS(qdev_get_parent_bus(dev)), AUX_SLAVE(dev), addr); + return dev; +} + +void aux_init_mmio(AUXSlave *aux_slave, MemoryRegion *mmio) +{ + assert(!aux_slave->mmio); + aux_slave->mmio = mmio; +} + +static void aux_slave_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *k = DEVICE_CLASS(klass); + + set_bit(DEVICE_CATEGORY_MISC, k->categories); + k->bus_type = TYPE_AUX_BUS; +} + +static const TypeInfo aux_slave_type_info = { + .name = TYPE_AUX_SLAVE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(AUXSlave), + .abstract = true, + .class_init = aux_slave_class_init, +}; + +static void aux_register_types(void) +{ + type_register_static(&aux_bus_info); + type_register_static(&aux_slave_type_info); + type_register_static(&aux_to_i2c_type_info); +} + +type_init(aux_register_types) diff --git a/hw/misc/bcm2835_mbox.c b/hw/misc/bcm2835_mbox.c index 263280fd4..e97cc814a 100644 --- a/hw/misc/bcm2835_mbox.c +++ b/hw/misc/bcm2835_mbox.c @@ -11,6 +11,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/misc/bcm2835_mbox.h" +#include "qemu/log.h" #define MAIL0_PEEK 0x90 #define MAIL0_SENDER 0x94 diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c index 530411f84..70eaafd32 100644 --- a/hw/misc/bcm2835_property.c +++ b/hw/misc/bcm2835_property.c @@ -8,6 +8,7 @@ #include "hw/misc/bcm2835_property.h" #include "hw/misc/bcm2835_mbox_defs.h" #include "sysemu/dma.h" +#include "qemu/log.h" /* https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface */ @@ -21,6 +22,8 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) int n; uint32_t offset, length, color; uint32_t xres, yres, xoffset, yoffset, bpp, pixo, alpha; + uint32_t tmp_xres, tmp_yres, tmp_xoffset, tmp_yoffset; + uint32_t tmp_bpp, tmp_pixo, tmp_alpha; uint32_t *newxres = NULL, *newyres = NULL, *newxoffset = NULL, *newyoffset = NULL, *newbpp = NULL, *newpixo = NULL, *newalpha = NULL; @@ -139,7 +142,11 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) case 0x00040001: /* Allocate buffer */ stl_le_phys(&s->dma_as, value + 12, s->fbdev->base); - stl_le_phys(&s->dma_as, value + 16, s->fbdev->size); + tmp_xres = newxres != NULL ? *newxres : s->fbdev->xres; + tmp_yres = newyres != NULL ? *newyres : s->fbdev->yres; + tmp_bpp = newbpp != NULL ? *newbpp : s->fbdev->bpp; + stl_le_phys(&s->dma_as, value + 16, + tmp_xres * tmp_yres * tmp_bpp / 8); resplen = 8; break; case 0x00048001: /* Release buffer */ @@ -150,8 +157,10 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) break; case 0x00040003: /* Get display width/height */ case 0x00040004: - stl_le_phys(&s->dma_as, value + 12, s->fbdev->xres); - stl_le_phys(&s->dma_as, value + 16, s->fbdev->yres); + tmp_xres = newxres != NULL ? *newxres : s->fbdev->xres; + tmp_yres = newyres != NULL ? *newyres : s->fbdev->yres; + stl_le_phys(&s->dma_as, value + 12, tmp_xres); + stl_le_phys(&s->dma_as, value + 16, tmp_yres); resplen = 8; break; case 0x00044003: /* Test display width/height */ @@ -167,7 +176,8 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) resplen = 8; break; case 0x00040005: /* Get depth */ - stl_le_phys(&s->dma_as, value + 12, s->fbdev->bpp); + tmp_bpp = newbpp != NULL ? *newbpp : s->fbdev->bpp; + stl_le_phys(&s->dma_as, value + 12, tmp_bpp); resplen = 4; break; case 0x00044005: /* Test depth */ @@ -179,7 +189,8 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) resplen = 4; break; case 0x00040006: /* Get pixel order */ - stl_le_phys(&s->dma_as, value + 12, s->fbdev->pixo); + tmp_pixo = newpixo != NULL ? *newpixo : s->fbdev->pixo; + stl_le_phys(&s->dma_as, value + 12, tmp_pixo); resplen = 4; break; case 0x00044006: /* Test pixel order */ @@ -191,7 +202,8 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) resplen = 4; break; case 0x00040007: /* Get alpha */ - stl_le_phys(&s->dma_as, value + 12, s->fbdev->alpha); + tmp_alpha = newalpha != NULL ? *newalpha : s->fbdev->alpha; + stl_le_phys(&s->dma_as, value + 12, tmp_alpha); resplen = 4; break; case 0x00044007: /* Test pixel alpha */ @@ -203,12 +215,16 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) resplen = 4; break; case 0x00040008: /* Get pitch */ - stl_le_phys(&s->dma_as, value + 12, s->fbdev->pitch); + tmp_xres = newxres != NULL ? *newxres : s->fbdev->xres; + tmp_bpp = newbpp != NULL ? *newbpp : s->fbdev->bpp; + stl_le_phys(&s->dma_as, value + 12, tmp_xres * tmp_bpp / 8); resplen = 4; break; case 0x00040009: /* Get virtual offset */ - stl_le_phys(&s->dma_as, value + 12, s->fbdev->xoffset); - stl_le_phys(&s->dma_as, value + 16, s->fbdev->yoffset); + tmp_xoffset = newxoffset != NULL ? *newxoffset : s->fbdev->xoffset; + tmp_yoffset = newyoffset != NULL ? *newyoffset : s->fbdev->yoffset; + stl_le_phys(&s->dma_as, value + 12, tmp_xoffset); + stl_le_phys(&s->dma_as, value + 16, tmp_yoffset); resplen = 8; break; case 0x00044009: /* Test virtual offset */ diff --git a/hw/misc/exynos4210_pmu.c b/hw/misc/exynos4210_pmu.c index 889abadfe..e30dbc7d3 100644 --- a/hw/misc/exynos4210_pmu.c +++ b/hw/misc/exynos4210_pmu.c @@ -457,15 +457,15 @@ static void exynos4210_pmu_reset(DeviceState *dev) } } -static int exynos4210_pmu_init(SysBusDevice *dev) +static void exynos4210_pmu_init(Object *obj) { - Exynos4210PmuState *s = EXYNOS4210_PMU(dev); + Exynos4210PmuState *s = EXYNOS4210_PMU(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); /* memory mapping */ - memory_region_init_io(&s->iomem, OBJECT(dev), &exynos4210_pmu_ops, s, + memory_region_init_io(&s->iomem, obj, &exynos4210_pmu_ops, s, "exynos4210.pmu", EXYNOS4210_PMU_REGS_MEM_SIZE); sysbus_init_mmio(dev, &s->iomem); - return 0; } static const VMStateDescription exynos4210_pmu_vmstate = { @@ -481,9 +481,7 @@ static const VMStateDescription exynos4210_pmu_vmstate = { static void exynos4210_pmu_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = exynos4210_pmu_init; dc->reset = exynos4210_pmu_reset; dc->vmsd = &exynos4210_pmu_vmstate; } @@ -492,6 +490,7 @@ static const TypeInfo exynos4210_pmu_info = { .name = TYPE_EXYNOS4210_PMU, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Exynos4210PmuState), + .instance_init = exynos4210_pmu_init, .class_init = exynos4210_pmu_class_init, }; diff --git a/hw/misc/hyperv_testdev.c b/hw/misc/hyperv_testdev.c index 1883fd7f2..6cae9e901 100644 --- a/hw/misc/hyperv_testdev.c +++ b/hw/misc/hyperv_testdev.c @@ -12,11 +12,11 @@ */ #include "qemu/osdep.h" +#include <linux/kvm.h> #include "hw/hw.h" #include "hw/qdev.h" #include "hw/isa/isa.h" #include "sysemu/kvm.h" -#include "linux/kvm.h" #include "target-i386/hyperv.h" #include "kvm_i386.h" diff --git a/hw/misc/imx25_ccm.c b/hw/misc/imx25_ccm.c index 225604d82..5cd8c0a9a 100644 --- a/hw/misc/imx25_ccm.c +++ b/hw/misc/imx25_ccm.c @@ -13,6 +13,7 @@ #include "qemu/osdep.h" #include "hw/misc/imx25_ccm.h" +#include "qemu/log.h" #ifndef DEBUG_IMX25_CCM #define DEBUG_IMX25_CCM 0 diff --git a/hw/misc/imx31_ccm.c b/hw/misc/imx31_ccm.c index 80c164716..1c03e52c4 100644 --- a/hw/misc/imx31_ccm.c +++ b/hw/misc/imx31_ccm.c @@ -13,6 +13,7 @@ #include "qemu/osdep.h" #include "hw/misc/imx31_ccm.h" +#include "qemu/log.h" #define CKIH_FREQ 26000000 /* 26MHz crystal input */ diff --git a/hw/misc/imx6_ccm.c b/hw/misc/imx6_ccm.c index 4e1d49da6..17e15d4c9 100644 --- a/hw/misc/imx6_ccm.c +++ b/hw/misc/imx6_ccm.c @@ -12,6 +12,7 @@ #include "qemu/osdep.h" #include "hw/misc/imx6_ccm.h" +#include "qemu/log.h" #ifndef DEBUG_IMX6_CCM #define DEBUG_IMX6_CCM 0 @@ -370,6 +371,12 @@ static uint32_t imx6_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock) case CLK_32k: freq = CKIL_FREQ; break; + case CLK_HIGH: + freq = 24000000; + break; + case CLK_HIGH_DIV: + freq = 24000000 / 8; + break; default: qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n", TYPE_IMX6_CCM, __func__, clock); diff --git a/hw/misc/imx6_src.c b/hw/misc/imx6_src.c new file mode 100644 index 000000000..8bb682957 --- /dev/null +++ b/hw/misc/imx6_src.c @@ -0,0 +1,265 @@ +/* + * IMX6 System Reset Controller + * + * Copyright (c) 2015 Jean-Christophe Dubois <jcd@tribudubois.net> + * + * 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/misc/imx6_src.h" +#include "sysemu/sysemu.h" +#include "qemu/bitops.h" +#include "qemu/log.h" +#include "arm-powerctl.h" + +#ifndef DEBUG_IMX6_SRC +#define DEBUG_IMX6_SRC 0 +#endif + +#define DPRINTF(fmt, args...) \ + do { \ + if (DEBUG_IMX6_SRC) { \ + fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX6_SRC, \ + __func__, ##args); \ + } \ + } while (0) + +static char const *imx6_src_reg_name(uint32_t reg) +{ + static char unknown[20]; + + switch (reg) { + case SRC_SCR: + return "SRC_SCR"; + case SRC_SBMR1: + return "SRC_SBMR1"; + case SRC_SRSR: + return "SRC_SRSR"; + case SRC_SISR: + return "SRC_SISR"; + case SRC_SIMR: + return "SRC_SIMR"; + case SRC_SBMR2: + return "SRC_SBMR2"; + case SRC_GPR1: + return "SRC_GPR1"; + case SRC_GPR2: + return "SRC_GPR2"; + case SRC_GPR3: + return "SRC_GPR3"; + case SRC_GPR4: + return "SRC_GPR4"; + case SRC_GPR5: + return "SRC_GPR5"; + case SRC_GPR6: + return "SRC_GPR6"; + case SRC_GPR7: + return "SRC_GPR7"; + case SRC_GPR8: + return "SRC_GPR8"; + case SRC_GPR9: + return "SRC_GPR9"; + case SRC_GPR10: + return "SRC_GPR10"; + default: + sprintf(unknown, "%d ?", reg); + return unknown; + } +} + +static const VMStateDescription vmstate_imx6_src = { + .name = TYPE_IMX6_SRC, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, IMX6SRCState, SRC_MAX), + VMSTATE_END_OF_LIST() + }, +}; + +static void imx6_src_reset(DeviceState *dev) +{ + IMX6SRCState *s = IMX6_SRC(dev); + + DPRINTF("\n"); + + memset(s->regs, 0, sizeof(s->regs)); + + /* Set reset values */ + s->regs[SRC_SCR] = 0x521; + s->regs[SRC_SRSR] = 0x1; + s->regs[SRC_SIMR] = 0x1F; +} + +static uint64_t imx6_src_read(void *opaque, hwaddr offset, unsigned size) +{ + uint32_t value = 0; + IMX6SRCState *s = (IMX6SRCState *)opaque; + uint32_t index = offset >> 2; + + if (index < SRC_MAX) { + value = s->regs[index]; + } else { + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX6_SRC, __func__, offset); + + } + + DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx6_src_reg_name(index), value); + + return value; +} + +static void imx6_src_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + IMX6SRCState *s = (IMX6SRCState *)opaque; + uint32_t index = offset >> 2; + unsigned long change_mask; + unsigned long current_value = value; + + if (index >= SRC_MAX) { + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX6_SRC, __func__, offset); + return; + } + + DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx6_src_reg_name(index), + (uint32_t)current_value); + + change_mask = s->regs[index] ^ (uint32_t)current_value; + + switch (index) { + case SRC_SCR: + /* + * On real hardware when the system reset controller starts a + * secondary CPU it runs through some boot ROM code which reads + * the SRC_GPRX registers controlling the start address and branches + * to it. + * Here we are taking a short cut and branching directly to the + * requested address (we don't want to run the boot ROM code inside + * QEMU) + */ + if (EXTRACT(change_mask, CORE3_ENABLE)) { + if (EXTRACT(current_value, CORE3_ENABLE)) { + /* CORE 3 is brought up */ + arm_set_cpu_on(3, s->regs[SRC_GPR7], s->regs[SRC_GPR8], + 3, false); + } else { + /* CORE 3 is shut down */ + arm_set_cpu_off(3); + } + /* We clear the reset bits as the processor changed state */ + clear_bit(CORE3_RST_SHIFT, ¤t_value); + clear_bit(CORE3_RST_SHIFT, &change_mask); + } + if (EXTRACT(change_mask, CORE2_ENABLE)) { + if (EXTRACT(current_value, CORE2_ENABLE)) { + /* CORE 2 is brought up */ + arm_set_cpu_on(2, s->regs[SRC_GPR5], s->regs[SRC_GPR6], + 3, false); + } else { + /* CORE 3 is shut down */ + arm_set_cpu_off(2); + } + /* We clear the reset bits as the processor changed state */ + clear_bit(CORE2_RST_SHIFT, ¤t_value); + clear_bit(CORE2_RST_SHIFT, &change_mask); + } + if (EXTRACT(change_mask, CORE1_ENABLE)) { + if (EXTRACT(current_value, CORE1_ENABLE)) { + /* CORE 1 is brought up */ + arm_set_cpu_on(1, s->regs[SRC_GPR3], s->regs[SRC_GPR4], + 3, false); + } else { + /* CORE 3 is shut down */ + arm_set_cpu_off(1); + } + /* We clear the reset bits as the processor changed state */ + clear_bit(CORE1_RST_SHIFT, ¤t_value); + clear_bit(CORE1_RST_SHIFT, &change_mask); + } + if (EXTRACT(change_mask, CORE0_RST)) { + arm_reset_cpu(0); + clear_bit(CORE0_RST_SHIFT, ¤t_value); + } + if (EXTRACT(change_mask, CORE1_RST)) { + arm_reset_cpu(1); + clear_bit(CORE1_RST_SHIFT, ¤t_value); + } + if (EXTRACT(change_mask, CORE2_RST)) { + arm_reset_cpu(2); + clear_bit(CORE2_RST_SHIFT, ¤t_value); + } + if (EXTRACT(change_mask, CORE3_RST)) { + arm_reset_cpu(3); + clear_bit(CORE3_RST_SHIFT, ¤t_value); + } + if (EXTRACT(change_mask, SW_IPU2_RST)) { + /* We pretend the IPU2 is reset */ + clear_bit(SW_IPU2_RST_SHIFT, ¤t_value); + } + if (EXTRACT(change_mask, SW_IPU1_RST)) { + /* We pretend the IPU1 is reset */ + clear_bit(SW_IPU1_RST_SHIFT, ¤t_value); + } + s->regs[index] = current_value; + break; + default: + s->regs[index] = current_value; + break; + } +} + +static const struct MemoryRegionOps imx6_src_ops = { + .read = imx6_src_read, + .write = imx6_src_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void imx6_src_realize(DeviceState *dev, Error **errp) +{ + IMX6SRCState *s = IMX6_SRC(dev); + + memory_region_init_io(&s->iomem, OBJECT(dev), &imx6_src_ops, s, + TYPE_IMX6_SRC, 0x1000); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); +} + +static void imx6_src_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = imx6_src_realize; + dc->reset = imx6_src_reset; + dc->vmsd = &vmstate_imx6_src; + dc->desc = "i.MX6 System Reset Controller"; +} + +static const TypeInfo imx6_src_info = { + .name = TYPE_IMX6_SRC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IMX6SRCState), + .class_init = imx6_src_class_init, +}; + +static void imx6_src_register_types(void) +{ + type_register_static(&imx6_src_info); +} + +type_init(imx6_src_register_types) diff --git a/hw/misc/imx_ccm.c b/hw/misc/imx_ccm.c index 986d890ca..7f239a41d 100644 --- a/hw/misc/imx_ccm.c +++ b/hw/misc/imx_ccm.c @@ -13,6 +13,7 @@ #include "qemu/osdep.h" #include "hw/misc/imx_ccm.h" +#include "qemu/log.h" #ifndef DEBUG_IMX_CCM #define DEBUG_IMX_CCM 0 diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index e40f23bfc..40a2ebca2 100644 --- a/hw/misc/ivshmem.c +++ b/hw/misc/ivshmem.c @@ -33,12 +33,9 @@ #include "sysemu/hostmem.h" #include "sysemu/qtest.h" #include "qapi/visitor.h" -#include "exec/ram_addr.h" #include "hw/misc/ivshmem.h" -#include <sys/mman.h> - #define PCI_VENDOR_ID_IVSHMEM PCI_VENDOR_ID_REDHAT_QUMRANET #define PCI_DEVICE_ID_IVSHMEM 0x1110 @@ -325,6 +322,7 @@ static int ivshmem_vector_unmask(PCIDevice *dev, unsigned vector, if (ret < 0) { return ret; } + kvm_irqchip_commit_routes(kvm_state); return kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL, v->virq); } @@ -444,13 +442,12 @@ static void ivshmem_add_kvm_msi_virq(IVShmemState *s, int vector, Error **errp) { PCIDevice *pdev = PCI_DEVICE(s); - MSIMessage msg = msix_get_message(pdev, vector); int ret; IVSHMEM_DPRINTF("ivshmem_add_kvm_msi_virq vector:%d\n", vector); assert(!s->msi_vectors[vector].pdev); - ret = kvm_irqchip_add_msi_route(kvm_state, msg, pdev); + ret = kvm_irqchip_add_msi_route(kvm_state, vector, pdev); if (ret < 0) { error_setg(errp, "kvm_irqchip_add_msi_route failed"); return; @@ -533,7 +530,7 @@ static void process_msg_shmem(IVShmemState *s, int fd, Error **errp) } memory_region_init_ram_ptr(&s->server_bar2, OBJECT(s), "ivshmem.bar2", size, ptr); - qemu_set_ram_fd(memory_region_get_ram_addr(&s->server_bar2), fd); + memory_region_set_fd(&s->server_bar2, fd); s->ivshmem_bar2 = &s->server_bar2; } @@ -940,7 +937,7 @@ static void ivshmem_exit(PCIDevice *dev) strerror(errno)); } - fd = qemu_get_ram_fd(memory_region_get_ram_addr(s->ivshmem_bar2)); + fd = memory_region_get_fd(s->ivshmem_bar2); close(fd); } @@ -1011,10 +1008,7 @@ static const TypeInfo ivshmem_common_info = { static void ivshmem_check_memdev_is_busy(Object *obj, const char *name, Object *val, Error **errp) { - MemoryRegion *mr; - - mr = host_memory_backend_get_memory(MEMORY_BACKEND(val), &error_abort); - if (memory_region_is_mapped(mr)) { + if (host_memory_backend_is_mapped(MEMORY_BACKEND(val))) { char *path = object_get_canonical_path_component(val); error_setg(errp, "can't use already busy memdev: %s", path); g_free(path); @@ -1063,6 +1057,14 @@ static void ivshmem_plain_realize(PCIDevice *dev, Error **errp) } ivshmem_common_realize(dev, errp); + host_memory_backend_set_mapped(s->hostmem, true); +} + +static void ivshmem_plain_exit(PCIDevice *pci_dev) +{ + IVShmemState *s = IVSHMEM_COMMON(pci_dev); + + host_memory_backend_set_mapped(s->hostmem, false); } static void ivshmem_plain_class_init(ObjectClass *klass, void *data) @@ -1071,6 +1073,7 @@ static void ivshmem_plain_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); k->realize = ivshmem_plain_realize; + k->exit = ivshmem_plain_exit; dc->props = ivshmem_plain_properties; dc->vmsd = &ivshmem_plain_vmsd; } diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c index f15f30110..05c02fb3a 100644 --- a/hw/misc/macio/cuda.c +++ b/hw/misc/macio/cuda.c @@ -29,6 +29,7 @@ #include "qemu/timer.h" #include "sysemu/sysemu.h" #include "qemu/cutils.h" +#include "qemu/log.h" /* XXX: implement all timer modes */ diff --git a/hw/misc/macio/mac_dbdma.c b/hw/misc/macio/mac_dbdma.c index 6051f17db..15452b9a2 100644 --- a/hw/misc/macio/mac_dbdma.c +++ b/hw/misc/macio/mac_dbdma.c @@ -41,16 +41,26 @@ #include "hw/isa/isa.h" #include "hw/ppc/mac_dbdma.h" #include "qemu/main-loop.h" +#include "qemu/log.h" +#include "sysemu/dma.h" /* debug DBDMA */ -//#define DEBUG_DBDMA - -#ifdef DEBUG_DBDMA -#define DBDMA_DPRINTF(fmt, ...) \ - do { printf("DBDMA: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DBDMA_DPRINTF(fmt, ...) -#endif +#define DEBUG_DBDMA 0 +#define DEBUG_DBDMA_CHANMASK ((1ull << DBDMA_CHANNELS) - 1) + +#define DBDMA_DPRINTF(fmt, ...) do { \ + if (DEBUG_DBDMA) { \ + printf("DBDMA: " fmt , ## __VA_ARGS__); \ + } \ +} while (0); + +#define DBDMA_DPRINTFCH(ch, fmt, ...) do { \ + if (DEBUG_DBDMA) { \ + if ((1ul << (ch)->channel) & DEBUG_DBDMA_CHANMASK) { \ + printf("DBDMA[%02x]: " fmt , (ch)->channel, ## __VA_ARGS__); \ + } \ + } \ +} while (0); /* */ @@ -60,7 +70,7 @@ static DBDMAState *dbdma_from_ch(DBDMA_channel *ch) return container_of(ch, DBDMAState, channels[ch->channel]); } -#ifdef DEBUG_DBDMA +#if DEBUG_DBDMA static void dump_dbdma_cmd(dbdma_cmd *cmd) { printf("dbdma_cmd %p\n", cmd); @@ -78,26 +88,26 @@ static void dump_dbdma_cmd(dbdma_cmd *cmd) #endif static void dbdma_cmdptr_load(DBDMA_channel *ch) { - DBDMA_DPRINTF("dbdma_cmdptr_load 0x%08x\n", - ch->regs[DBDMA_CMDPTR_LO]); - cpu_physical_memory_read(ch->regs[DBDMA_CMDPTR_LO], - &ch->current, sizeof(dbdma_cmd)); + DBDMA_DPRINTFCH(ch, "dbdma_cmdptr_load 0x%08x\n", + ch->regs[DBDMA_CMDPTR_LO]); + dma_memory_read(&address_space_memory, ch->regs[DBDMA_CMDPTR_LO], + &ch->current, sizeof(dbdma_cmd)); } static void dbdma_cmdptr_save(DBDMA_channel *ch) { - DBDMA_DPRINTF("dbdma_cmdptr_save 0x%08x\n", - ch->regs[DBDMA_CMDPTR_LO]); - DBDMA_DPRINTF("xfer_status 0x%08x res_count 0x%04x\n", - le16_to_cpu(ch->current.xfer_status), - le16_to_cpu(ch->current.res_count)); - cpu_physical_memory_write(ch->regs[DBDMA_CMDPTR_LO], - &ch->current, sizeof(dbdma_cmd)); + DBDMA_DPRINTFCH(ch, "dbdma_cmdptr_save 0x%08x\n", + ch->regs[DBDMA_CMDPTR_LO]); + DBDMA_DPRINTFCH(ch, "xfer_status 0x%08x res_count 0x%04x\n", + le16_to_cpu(ch->current.xfer_status), + le16_to_cpu(ch->current.res_count)); + dma_memory_write(&address_space_memory, ch->regs[DBDMA_CMDPTR_LO], + &ch->current, sizeof(dbdma_cmd)); } static void kill_channel(DBDMA_channel *ch) { - DBDMA_DPRINTF("kill_channel\n"); + DBDMA_DPRINTFCH(ch, "kill_channel\n"); ch->regs[DBDMA_STATUS] |= DEAD; ch->regs[DBDMA_STATUS] &= ~ACTIVE; @@ -113,7 +123,7 @@ static void conditional_interrupt(DBDMA_channel *ch) uint32_t status; int cond; - DBDMA_DPRINTF("%s\n", __func__); + DBDMA_DPRINTFCH(ch, "%s\n", __func__); intr = le16_to_cpu(current->command) & INTR_MASK; @@ -122,7 +132,7 @@ static void conditional_interrupt(DBDMA_channel *ch) return; case INTR_ALWAYS: /* always interrupt */ qemu_irq_raise(ch->irq); - DBDMA_DPRINTF("%s: raise\n", __func__); + DBDMA_DPRINTFCH(ch, "%s: raise\n", __func__); return; } @@ -137,13 +147,13 @@ static void conditional_interrupt(DBDMA_channel *ch) case INTR_IFSET: /* intr if condition bit is 1 */ if (cond) { qemu_irq_raise(ch->irq); - DBDMA_DPRINTF("%s: raise\n", __func__); + DBDMA_DPRINTFCH(ch, "%s: raise\n", __func__); } return; case INTR_IFCLR: /* intr if condition bit is 0 */ if (!cond) { qemu_irq_raise(ch->irq); - DBDMA_DPRINTF("%s: raise\n", __func__); + DBDMA_DPRINTFCH(ch, "%s: raise\n", __func__); } return; } @@ -157,7 +167,7 @@ static int conditional_wait(DBDMA_channel *ch) uint32_t status; int cond; - DBDMA_DPRINTF("conditional_wait\n"); + DBDMA_DPRINTFCH(ch, "conditional_wait\n"); wait = le16_to_cpu(current->command) & WAIT_MASK; @@ -203,7 +213,7 @@ static void branch(DBDMA_channel *ch) { dbdma_cmd *current = &ch->current; - ch->regs[DBDMA_CMDPTR_LO] = current->cmd_dep; + ch->regs[DBDMA_CMDPTR_LO] = le32_to_cpu(current->cmd_dep); ch->regs[DBDMA_STATUS] |= BT; dbdma_cmdptr_load(ch); } @@ -216,7 +226,7 @@ static void conditional_branch(DBDMA_channel *ch) uint32_t status; int cond; - DBDMA_DPRINTF("conditional_branch\n"); + DBDMA_DPRINTFCH(ch, "conditional_branch\n"); /* check if we must branch */ @@ -261,7 +271,7 @@ static void dbdma_end(DBDMA_io *io) DBDMA_channel *ch = io->channel; dbdma_cmd *current = &ch->current; - DBDMA_DPRINTF("%s\n", __func__); + DBDMA_DPRINTFCH(ch, "%s\n", __func__); if (conditional_wait(ch)) goto wait; @@ -287,13 +297,13 @@ wait: static void start_output(DBDMA_channel *ch, int key, uint32_t addr, uint16_t req_count, int is_last) { - DBDMA_DPRINTF("start_output\n"); + DBDMA_DPRINTFCH(ch, "start_output\n"); /* KEY_REGS, KEY_DEVICE and KEY_STREAM * are not implemented in the mac-io chip */ - DBDMA_DPRINTF("addr 0x%x key 0x%x\n", addr, key); + DBDMA_DPRINTFCH(ch, "addr 0x%x key 0x%x\n", addr, key); if (!addr || key > KEY_STREAM3) { kill_channel(ch); return; @@ -313,13 +323,13 @@ static void start_output(DBDMA_channel *ch, int key, uint32_t addr, static void start_input(DBDMA_channel *ch, int key, uint32_t addr, uint16_t req_count, int is_last) { - DBDMA_DPRINTF("start_input\n"); + DBDMA_DPRINTFCH(ch, "start_input\n"); /* KEY_REGS, KEY_DEVICE and KEY_STREAM * are not implemented in the mac-io chip */ - DBDMA_DPRINTF("addr 0x%x key 0x%x\n", addr, key); + DBDMA_DPRINTFCH(ch, "addr 0x%x key 0x%x\n", addr, key); if (!addr || key > KEY_STREAM3) { kill_channel(ch); return; @@ -340,9 +350,8 @@ static void load_word(DBDMA_channel *ch, int key, uint32_t addr, uint16_t len) { dbdma_cmd *current = &ch->current; - uint32_t val; - DBDMA_DPRINTF("load_word\n"); + DBDMA_DPRINTFCH(ch, "load_word %d bytes, addr=%08x\n", len, addr); /* only implements KEY_SYSTEM */ @@ -352,14 +361,7 @@ static void load_word(DBDMA_channel *ch, int key, uint32_t addr, return; } - cpu_physical_memory_read(addr, &val, len); - - if (len == 2) - val = (val << 16) | (current->cmd_dep & 0x0000ffff); - else if (len == 1) - val = (val << 24) | (current->cmd_dep & 0x00ffffff); - - current->cmd_dep = val; + dma_memory_read(&address_space_memory, addr, ¤t->cmd_dep, len); if (conditional_wait(ch)) goto wait; @@ -379,9 +381,9 @@ static void store_word(DBDMA_channel *ch, int key, uint32_t addr, uint16_t len) { dbdma_cmd *current = &ch->current; - uint32_t val; - DBDMA_DPRINTF("store_word\n"); + DBDMA_DPRINTFCH(ch, "store_word %d bytes, addr=%08x pa=%x\n", + len, addr, le32_to_cpu(current->cmd_dep)); /* only implements KEY_SYSTEM */ @@ -391,13 +393,7 @@ static void store_word(DBDMA_channel *ch, int key, uint32_t addr, return; } - val = current->cmd_dep; - if (len == 2) - val >>= 16; - else if (len == 1) - val >>= 24; - - cpu_physical_memory_write(addr, &val, len); + dma_memory_write(&address_space_memory, addr, ¤t->cmd_dep, len); if (conditional_wait(ch)) goto wait; @@ -444,7 +440,7 @@ static void channel_run(DBDMA_channel *ch) uint16_t req_count; uint32_t phy_addr; - DBDMA_DPRINTF("channel_run\n"); + DBDMA_DPRINTFCH(ch, "channel_run\n"); dump_dbdma_cmd(current); /* clear WAKE flag at command fetch */ @@ -538,9 +534,9 @@ static void DBDMA_run_bh(void *opaque) { DBDMAState *s = opaque; - DBDMA_DPRINTF("DBDMA_run_bh\n"); - + DBDMA_DPRINTF("-> DBDMA_run_bh\n"); DBDMA_run(s); + DBDMA_DPRINTF("<- DBDMA_run_bh\n"); } void DBDMA_kick(DBDMAState *dbdma) @@ -555,7 +551,7 @@ void DBDMA_register_channel(void *dbdma, int nchan, qemu_irq irq, DBDMAState *s = dbdma; DBDMA_channel *ch = &s->channels[nchan]; - DBDMA_DPRINTF("DBDMA_register_channel 0x%x\n", nchan); + DBDMA_DPRINTFCH(ch, "DBDMA_register_channel 0x%x\n", nchan); assert(rw); assert(flush); @@ -599,7 +595,7 @@ dbdma_control_write(DBDMA_channel *ch) status &= ~FLUSH; } - DBDMA_DPRINTF(" status 0x%08x\n", status); + DBDMA_DPRINTFCH(ch, " status 0x%08x\n", status); ch->regs[DBDMA_STATUS] = status; @@ -616,10 +612,10 @@ static void dbdma_write(void *opaque, hwaddr addr, DBDMA_channel *ch = &s->channels[channel]; int reg = (addr - (channel << DBDMA_CHANNEL_SHIFT)) >> 2; - DBDMA_DPRINTF("writel 0x" TARGET_FMT_plx " <= 0x%08"PRIx64"\n", - addr, value); - DBDMA_DPRINTF("channel 0x%x reg 0x%x\n", - (uint32_t)addr >> DBDMA_CHANNEL_SHIFT, reg); + DBDMA_DPRINTFCH(ch, "writel 0x" TARGET_FMT_plx " <= 0x%08"PRIx64"\n", + addr, value); + DBDMA_DPRINTFCH(ch, "channel 0x%x reg 0x%x\n", + (uint32_t)addr >> DBDMA_CHANNEL_SHIFT, reg); /* cmdptr cannot be modified if channel is ACTIVE */ @@ -670,9 +666,9 @@ static uint64_t dbdma_read(void *opaque, hwaddr addr, value = ch->regs[reg]; - DBDMA_DPRINTF("readl 0x" TARGET_FMT_plx " => 0x%08x\n", addr, value); - DBDMA_DPRINTF("channel 0x%x reg 0x%x\n", - (uint32_t)addr >> DBDMA_CHANNEL_SHIFT, reg); + DBDMA_DPRINTFCH(ch, "readl 0x" TARGET_FMT_plx " => 0x%08x\n", addr, value); + DBDMA_DPRINTFCH(ch, "channel 0x%x reg 0x%x\n", + (uint32_t)addr >> DBDMA_CHANNEL_SHIFT, reg); switch(reg) { case DBDMA_CONTROL: @@ -782,13 +778,24 @@ static void dbdma_unassigned_rw(DBDMA_io *io) DBDMA_channel *ch = io->channel; qemu_log_mask(LOG_GUEST_ERROR, "%s: use of unassigned channel %d\n", __func__, ch->channel); + ch->io.processing = false; } static void dbdma_unassigned_flush(DBDMA_io *io) { DBDMA_channel *ch = io->channel; + dbdma_cmd *current = &ch->current; + uint16_t cmd; qemu_log_mask(LOG_GUEST_ERROR, "%s: use of unassigned channel %d\n", __func__, ch->channel); + + cmd = le16_to_cpu(current->command) & COMMAND_MASK; + if (cmd == OUTPUT_MORE || cmd == OUTPUT_LAST || + cmd == INPUT_MORE || cmd == INPUT_LAST) { + current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS] | FLUSH); + current->res_count = cpu_to_le16(io->len); + dbdma_cmdptr_save(ch); + } } void* DBDMA_init (MemoryRegion **dbdma_mem) diff --git a/hw/misc/max111x.c b/hw/misc/max111x.c index 9014f0f70..2a277bdb8 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/milkymist-hpdmc.c b/hw/misc/milkymist-hpdmc.c index b97000fc4..e6140eec6 100644 --- a/hw/misc/milkymist-hpdmc.c +++ b/hw/misc/milkymist-hpdmc.c @@ -18,7 +18,7 @@ * * * Specification available at: - * http://www.milkymist.org/socdoc/hpdmc.pdf + * http://milkymist.walle.cc/socdoc/hpdmc.pdf */ #include "qemu/osdep.h" diff --git a/hw/misc/milkymist-pfpu.c b/hw/misc/milkymist-pfpu.c index 57acd7b36..1da21a643 100644 --- a/hw/misc/milkymist-pfpu.c +++ b/hw/misc/milkymist-pfpu.c @@ -18,7 +18,7 @@ * * * Specification available at: - * http://www.milkymist.org/socdoc/pfpu.pdf + * http://milkymist.walle.cc/socdoc/pfpu.pdf * */ diff --git a/hw/misc/mips_cmgcr.c b/hw/misc/mips_cmgcr.c index 37be23995..b3ba16694 100644 --- a/hw/misc/mips_cmgcr.c +++ b/hw/misc/mips_cmgcr.c @@ -11,17 +11,24 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "qemu/log.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "sysemu/sysemu.h" #include "hw/misc/mips_cmgcr.h" #include "hw/misc/mips_cpc.h" +#include "hw/intc/mips_gic.h" static inline bool is_cpc_connected(MIPSGCRState *s) { return s->cpc_mr != NULL; } +static inline bool is_gic_connected(MIPSGCRState *s) +{ + return s->gic_mr != NULL; +} + static inline void update_cpc_base(MIPSGCRState *gcr, uint64_t val) { if (is_cpc_connected(gcr)) { @@ -35,10 +42,25 @@ static inline void update_cpc_base(MIPSGCRState *gcr, uint64_t val) } } +static inline void update_gic_base(MIPSGCRState *gcr, uint64_t val) +{ + if (is_gic_connected(gcr)) { + gcr->gic_base = val & GCR_GIC_BASE_MSK; + memory_region_transaction_begin(); + memory_region_set_address(gcr->gic_mr, + gcr->gic_base & GCR_GIC_BASE_GICBASE_MSK); + memory_region_set_enabled(gcr->gic_mr, + gcr->gic_base & GCR_GIC_BASE_GICEN_MSK); + memory_region_transaction_commit(); + } +} + /* Read GCR registers */ static uint64_t gcr_read(void *opaque, hwaddr addr, unsigned size) { MIPSGCRState *gcr = (MIPSGCRState *) opaque; + MIPSGCRVPState *current_vps = &gcr->vps[current_cpu->cpu_index]; + MIPSGCRVPState *other_vps = &gcr->vps[current_vps->other]; switch (addr) { /* Global Control Block Register */ @@ -49,8 +71,12 @@ static uint64_t gcr_read(void *opaque, hwaddr addr, unsigned size) return gcr->gcr_base; case GCR_REV_OFS: return gcr->gcr_rev; + case GCR_GIC_BASE_OFS: + return gcr->gic_base; case GCR_CPC_BASE_OFS: return gcr->cpc_base; + case GCR_GIC_STATUS_OFS: + return is_gic_connected(gcr); case GCR_CPC_STATUS_OFS: return is_cpc_connected(gcr); case GCR_L2_CONFIG_OFS: @@ -61,8 +87,14 @@ static uint64_t gcr_read(void *opaque, hwaddr addr, unsigned size) case MIPS_COCB_OFS + GCR_CL_CONFIG_OFS: /* Set PVP to # of VPs - 1 */ return gcr->num_vps - 1; + case MIPS_CLCB_OFS + GCR_CL_RESETBASE_OFS: + return current_vps->reset_base; + case MIPS_COCB_OFS + GCR_CL_RESETBASE_OFS: + return other_vps->reset_base; case MIPS_CLCB_OFS + GCR_CL_OTHER_OFS: - return 0; + return current_vps->other; + case MIPS_COCB_OFS + GCR_CL_OTHER_OFS: + return other_vps->other; default: qemu_log_mask(LOG_UNIMP, "Read %d bytes at GCR offset 0x%" HWADDR_PRIx "\n", size, addr); @@ -71,15 +103,46 @@ static uint64_t gcr_read(void *opaque, hwaddr addr, unsigned size) return 0; } +static inline target_ulong get_exception_base(MIPSGCRVPState *vps) +{ + /* TODO: BEV_BASE and SELECT_BEV */ + return (int32_t)(vps->reset_base & GCR_CL_RESET_BASE_RESETBASE_MSK); +} + /* Write GCR registers */ static void gcr_write(void *opaque, hwaddr addr, uint64_t data, unsigned size) { MIPSGCRState *gcr = (MIPSGCRState *)opaque; + MIPSGCRVPState *current_vps = &gcr->vps[current_cpu->cpu_index]; + MIPSGCRVPState *other_vps = &gcr->vps[current_vps->other]; switch (addr) { + case GCR_GIC_BASE_OFS: + update_gic_base(gcr, data); + break; case GCR_CPC_BASE_OFS: update_cpc_base(gcr, data); break; + case MIPS_CLCB_OFS + GCR_CL_RESETBASE_OFS: + current_vps->reset_base = data & GCR_CL_RESET_BASE_MSK; + cpu_set_exception_base(current_cpu->cpu_index, + get_exception_base(current_vps)); + break; + case MIPS_COCB_OFS + GCR_CL_RESETBASE_OFS: + other_vps->reset_base = data & GCR_CL_RESET_BASE_MSK; + cpu_set_exception_base(current_vps->other, + get_exception_base(other_vps)); + break; + case MIPS_CLCB_OFS + GCR_CL_OTHER_OFS: + if ((data & GCR_CL_OTHER_MSK) < gcr->num_vps) { + current_vps->other = data & GCR_CL_OTHER_MSK; + } + break; + case MIPS_COCB_OFS + GCR_CL_OTHER_OFS: + if ((data & GCR_CL_OTHER_MSK) < gcr->num_vps) { + other_vps->other = data & GCR_CL_OTHER_MSK; + } + break; default: qemu_log_mask(LOG_UNIMP, "Write %d bytes at GCR offset 0x%" HWADDR_PRIx " 0x%" PRIx64 "\n", size, addr, data); @@ -101,6 +164,12 @@ static void mips_gcr_init(Object *obj) SysBusDevice *sbd = SYS_BUS_DEVICE(obj); MIPSGCRState *s = MIPS_GCR(obj); + object_property_add_link(obj, "gic", TYPE_MEMORY_REGION, + (Object **)&s->gic_mr, + qdev_prop_allow_set_link_before_realize, + OBJ_PROP_LINK_UNREF_ON_RELEASE, + &error_abort); + object_property_add_link(obj, "cpc", TYPE_MEMORY_REGION, (Object **)&s->cpc_mr, qdev_prop_allow_set_link_before_realize, @@ -115,8 +184,16 @@ static void mips_gcr_init(Object *obj) static void mips_gcr_reset(DeviceState *dev) { MIPSGCRState *s = MIPS_GCR(dev); + int i; + update_gic_base(s, 0); update_cpc_base(s, 0); + + for (i = 0; i < s->num_vps; i++) { + s->vps[i].other = 0; + s->vps[i].reset_base = 0xBFC00000 & GCR_CL_RESET_BASE_MSK; + cpu_set_exception_base(i, get_exception_base(&s->vps[i])); + } } static const VMStateDescription vmstate_mips_gcr = { @@ -136,12 +213,21 @@ static Property mips_gcr_properties[] = { DEFINE_PROP_END_OF_LIST(), }; +static void mips_gcr_realize(DeviceState *dev, Error **errp) +{ + MIPSGCRState *s = MIPS_GCR(dev); + + /* Create local set of registers for each VP */ + s->vps = g_new(MIPSGCRVPState, s->num_vps); +} + static void mips_gcr_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->props = mips_gcr_properties; dc->vmsd = &vmstate_mips_gcr; dc->reset = mips_gcr_reset; + dc->realize = mips_gcr_realize; } static const TypeInfo mips_gcr_info = { diff --git a/hw/misc/mips_cpc.c b/hw/misc/mips_cpc.c index d2b8e42da..6d345745f 100644 --- a/hw/misc/mips_cpc.c +++ b/hw/misc/mips_cpc.c @@ -19,6 +19,8 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "cpu.h" +#include "qemu/log.h" #include "hw/sysbus.h" #include "hw/misc/mips_cpc.h" @@ -35,7 +37,7 @@ static void cpc_run_vp(MIPSCPCState *cpc, uint64_t vp_run) CPU_FOREACH(cs) { uint64_t i = 1ULL << cs->cpu_index; if (i & vp_run & ~cpc->vp_running) { - cpu_interrupt(cs, CPU_INTERRUPT_WAKE); + cpu_reset(cs); cpc->vp_running |= i; } } @@ -48,8 +50,7 @@ static void cpc_stop_vp(MIPSCPCState *cpc, uint64_t vp_stop) CPU_FOREACH(cs) { uint64_t i = 1ULL << cs->cpu_index; if (i & vp_stop & cpc->vp_running) { - cs->halted = 1; - cpu_reset_interrupt(cs, CPU_INTERRUPT_WAKE); + cpu_interrupt(cs, CPU_INTERRUPT_HALT); cpc->vp_running &= ~i; } } diff --git a/hw/misc/mips_itu.c b/hw/misc/mips_itu.c index da5455062..ef935b51a 100644 --- a/hw/misc/mips_itu.c +++ b/hw/misc/mips_itu.c @@ -19,6 +19,9 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "cpu.h" +#include "qemu/log.h" +#include "exec/exec-all.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "sysemu/sysemu.h" diff --git a/hw/misc/mst_fpga.c b/hw/misc/mst_fpga.c index 48d7dfb2d..a10f0496f 100644 --- a/hw/misc/mst_fpga.c +++ b/hw/misc/mst_fpga.c @@ -200,10 +200,11 @@ static int mst_fpga_post_load(void *opaque, int version_id) return 0; } -static int mst_fpga_init(SysBusDevice *sbd) +static void mst_fpga_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - mst_irq_state *s = MAINSTONE_FPGA(dev); + DeviceState *dev = DEVICE(obj); + mst_irq_state *s = MAINSTONE_FPGA(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); s->pcmcia0 = MST_PCMCIAx_READY | MST_PCMCIAx_nCD; s->pcmcia1 = MST_PCMCIAx_READY | MST_PCMCIAx_nCD; @@ -213,10 +214,9 @@ static int mst_fpga_init(SysBusDevice *sbd) /* alloc the external 16 irqs */ qdev_init_gpio_in(dev, mst_fpga_set_irq, MST_NUM_IRQS); - memory_region_init_io(&s->iomem, OBJECT(s), &mst_fpga_ops, s, + memory_region_init_io(&s->iomem, obj, &mst_fpga_ops, s, "fpga", 0x00100000); sysbus_init_mmio(sbd, &s->iomem); - return 0; } static VMStateDescription vmstate_mst_fpga_regs = { @@ -245,9 +245,7 @@ static VMStateDescription vmstate_mst_fpga_regs = { static void mst_fpga_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = mst_fpga_init; dc->desc = "Mainstone II FPGA"; dc->vmsd = &vmstate_mst_fpga_regs; } @@ -256,6 +254,7 @@ static const TypeInfo mst_fpga_info = { .name = TYPE_MAINSTONE_FPGA, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(mst_irq_state), + .instance_init = mst_fpga_init, .class_init = mst_fpga_class_init, }; diff --git a/hw/misc/pc-testdev.c b/hw/misc/pc-testdev.c index 086893dcc..b81d82008 100644 --- a/hw/misc/pc-testdev.c +++ b/hw/misc/pc-testdev.c @@ -36,9 +36,6 @@ */ #include "qemu/osdep.h" -#if defined(CONFIG_POSIX) -#include <sys/mman.h> -#endif #include "hw/hw.h" #include "hw/qdev.h" #include "hw/isa/isa.h" diff --git a/hw/misc/pci-testdev.c b/hw/misc/pci-testdev.c index 2f2e98977..7d5990213 100644 --- a/hw/misc/pci-testdev.c +++ b/hw/misc/pci-testdev.c @@ -21,6 +21,7 @@ #include "hw/hw.h" #include "hw/pci/pci.h" #include "qemu/event_notifier.h" +#include "sysemu/kvm.h" typedef struct PCITestDevHdr { uint8_t test; diff --git a/hw/misc/stm32f2xx_syscfg.c b/hw/misc/stm32f2xx_syscfg.c index d0d7076ef..7c45833d0 100644 --- a/hw/misc/stm32f2xx_syscfg.c +++ b/hw/misc/stm32f2xx_syscfg.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" #include "hw/misc/stm32f2xx_syscfg.h" +#include "qemu/log.h" #ifndef STM_SYSCFG_ERR_DEBUG #define STM_SYSCFG_ERR_DEBUG 0 diff --git a/hw/misc/trace-events b/hw/misc/trace-events new file mode 100644 index 000000000..0cc556ca9 --- /dev/null +++ b/hw/misc/trace-events @@ -0,0 +1,55 @@ +# See docs/tracing.txt for syntax documentation. + +# hw/misc/eccmemctl.c +ecc_mem_writel_mer(uint32_t val) "Write memory enable %08x" +ecc_mem_writel_mdr(uint32_t val) "Write memory delay %08x" +ecc_mem_writel_mfsr(uint32_t val) "Write memory fault status %08x" +ecc_mem_writel_vcr(uint32_t val) "Write slot configuration %08x" +ecc_mem_writel_dr(uint32_t val) "Write diagnostic %08x" +ecc_mem_writel_ecr0(uint32_t val) "Write event count 1 %08x" +ecc_mem_writel_ecr1(uint32_t val) "Write event count 2 %08x" +ecc_mem_readl_mer(uint32_t ret) "Read memory enable %08x" +ecc_mem_readl_mdr(uint32_t ret) "Read memory delay %08x" +ecc_mem_readl_mfsr(uint32_t ret) "Read memory fault status %08x" +ecc_mem_readl_vcr(uint32_t ret) "Read slot configuration %08x" +ecc_mem_readl_mfar0(uint32_t ret) "Read memory fault address 0 %08x" +ecc_mem_readl_mfar1(uint32_t ret) "Read memory fault address 1 %08x" +ecc_mem_readl_dr(uint32_t ret) "Read diagnostic %08x" +ecc_mem_readl_ecr0(uint32_t ret) "Read event count 1 %08x" +ecc_mem_readl_ecr1(uint32_t ret) "Read event count 2 %08x" +ecc_diag_mem_writeb(uint64_t addr, uint32_t val) "Write diagnostic %"PRId64" = %02x" +ecc_diag_mem_readb(uint64_t addr, uint32_t ret) "Read diagnostic %"PRId64"= %02x" + +# hw/misc/slavio_misc.c +slavio_misc_update_irq_raise(void) "Raise IRQ" +slavio_misc_update_irq_lower(void) "Lower IRQ" +slavio_set_power_fail(int power_failing, uint8_t config) "Power fail: %d, config: %d" +slavio_cfg_mem_writeb(uint32_t val) "Write config %02x" +slavio_cfg_mem_readb(uint32_t ret) "Read config %02x" +slavio_diag_mem_writeb(uint32_t val) "Write diag %02x" +slavio_diag_mem_readb(uint32_t ret) "Read diag %02x" +slavio_mdm_mem_writeb(uint32_t val) "Write modem control %02x" +slavio_mdm_mem_readb(uint32_t ret) "Read modem control %02x" +slavio_aux1_mem_writeb(uint32_t val) "Write aux1 %02x" +slavio_aux1_mem_readb(uint32_t ret) "Read aux1 %02x" +slavio_aux2_mem_writeb(uint32_t val) "Write aux2 %02x" +slavio_aux2_mem_readb(uint32_t ret) "Read aux2 %02x" +apc_mem_writeb(uint32_t val) "Write power management %02x" +apc_mem_readb(uint32_t ret) "Read power management %02x" +slavio_sysctrl_mem_writel(uint32_t val) "Write system control %08x" +slavio_sysctrl_mem_readl(uint32_t ret) "Read system control %08x" +slavio_led_mem_writew(uint32_t val) "Write diagnostic LED %04x" +slavio_led_mem_readw(uint32_t ret) "Read diagnostic LED %04x" + +# hw/misc/milkymist-hpdmc.c +milkymist_hpdmc_memory_read(uint32_t addr, uint32_t value) "addr=%08x value=%08x" +milkymist_hpdmc_memory_write(uint32_t addr, uint32_t value) "addr=%08x value=%08x" + +# hw/misc/milkymist-pfpu.c +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 689678980..c763811a9 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/misc/zynq-xadc.c b/hw/misc/zynq-xadc.c index 71fbccd79..14906103c 100644 --- a/hw/misc/zynq-xadc.c +++ b/hw/misc/zynq-xadc.c @@ -18,6 +18,7 @@ #include "hw/misc/zynq-xadc.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" +#include "qemu/log.h" enum { CFG = 0x000 / 4, diff --git a/hw/misc/zynq_slcr.c b/hw/misc/zynq_slcr.c index b1b7591ef..789121900 100644 --- a/hw/misc/zynq_slcr.c +++ b/hw/misc/zynq_slcr.c @@ -19,6 +19,7 @@ #include "qemu/timer.h" #include "hw/sysbus.h" #include "sysemu/sysemu.h" +#include "qemu/log.h" #ifndef ZYNQ_SLCR_ERR_DEBUG #define ZYNQ_SLCR_ERR_DEBUG 0 diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs index 64d044923..610ed3e7a 100644 --- a/hw/net/Makefile.objs +++ b/hw/net/Makefile.objs @@ -6,9 +6,11 @@ common-obj-$(CONFIG_NE2000_PCI) += ne2000.o common-obj-$(CONFIG_EEPRO100_PCI) += eepro100.o common-obj-$(CONFIG_PCNET_PCI) += pcnet-pci.o common-obj-$(CONFIG_PCNET_COMMON) += pcnet.o -common-obj-$(CONFIG_E1000_PCI) += e1000.o +common-obj-$(CONFIG_E1000_PCI) += e1000.o e1000x_common.o +common-obj-$(CONFIG_E1000E_PCI) += net_tx_pkt.o net_rx_pkt.o +common-obj-$(CONFIG_E1000E_PCI) += e1000e.o e1000e_core.o e1000x_common.o common-obj-$(CONFIG_RTL8139_PCI) += rtl8139.o -common-obj-$(CONFIG_VMXNET3_PCI) += vmxnet_tx_pkt.o vmxnet_rx_pkt.o +common-obj-$(CONFIG_VMXNET3_PCI) += net_tx_pkt.o net_rx_pkt.o common-obj-$(CONFIG_VMXNET3_PCI) += vmxnet3.o common-obj-$(CONFIG_SMC91C111) += smc91c111.o diff --git a/hw/net/allwinner_emac.c b/hw/net/allwinner_emac.c index 16d4b63ba..50e8361e5 100644 --- a/hw/net/allwinner_emac.c +++ b/hw/net/allwinner_emac.c @@ -21,6 +21,7 @@ #include "net/net.h" #include "qemu/fifo8.h" #include "hw/net/allwinner_emac.h" +#include "qemu/log.h" #include <zlib.h> static uint8_t padding[60]; @@ -423,7 +424,7 @@ static const MemoryRegionOps aw_emac_mem_ops = { }; static NetClientInfo net_aw_emac_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, + .type = NET_CLIENT_DRIVER_NIC, .size = sizeof(NICState), .can_receive = aw_emac_can_receive, .receive = aw_emac_receive, diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index 0346f3e33..db1b301e7 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; @@ -1194,7 +1207,7 @@ static void gem_set_link(NetClientState *nc) } static NetClientInfo net_gem_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, + .type = NET_CLIENT_DRIVER_NIC, .size = sizeof(NICState), .can_receive = gem_can_receive, .receive = gem_receive, diff --git a/hw/net/dp8393x.c b/hw/net/dp8393x.c index 0fa652c39..17f0338d1 100644 --- a/hw/net/dp8393x.c +++ b/hw/net/dp8393x.c @@ -812,7 +812,7 @@ static void dp8393x_reset(DeviceState *dev) } static NetClientInfo net_dp83932_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, + .type = NET_CLIENT_DRIVER_NIC, .size = sizeof(NICState), .can_receive = dp8393x_can_receive, .receive = dp8393x_receive, diff --git a/hw/net/e1000.c b/hw/net/e1000.c index 8e79b550e..93249497f 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -36,7 +36,7 @@ #include "qemu/iov.h" #include "qemu/range.h" -#include "e1000_regs.h" +#include "e1000x_common.h" static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; @@ -64,11 +64,6 @@ static int debugflags = DBGBIT(TXERR) | DBGBIT(GENERAL); #define PNPMMIO_SIZE 0x20000 #define MIN_BUF_SIZE 60 /* Min. octets in an ethernet frame sans FCS */ -/* this is the size past which hardware will drop packets when setting LPE=0 */ -#define MAXIMUM_ETHERNET_VLAN_SIZE 1522 -/* this is the size past which hardware will drop packets when setting LPE=1 */ -#define MAXIMUM_ETHERNET_LPE_SIZE 16384 - #define MAXIMUM_ETHERNET_HDR_LEN (14+4) /* @@ -102,22 +97,9 @@ typedef struct E1000State_st { unsigned char vlan[4]; unsigned char data[0x10000]; uint16_t size; - unsigned char sum_needed; unsigned char vlan_needed; - uint8_t ipcss; - uint8_t ipcso; - uint16_t ipcse; - uint8_t tucss; - uint8_t tucso; - uint16_t tucse; - uint8_t hdr_len; - uint16_t mss; - uint32_t paylen; + e1000x_txd_props props; uint16_t tso_frames; - char tse; - int8_t ip; - int8_t tcp; - char cptse; // current packet tse bit } tx; struct { @@ -162,52 +144,19 @@ typedef struct E1000BaseClass { #define E1000_DEVICE_GET_CLASS(obj) \ OBJECT_GET_CLASS(E1000BaseClass, (obj), TYPE_E1000_BASE) -#define defreg(x) x = (E1000_##x>>2) -enum { - defreg(CTRL), defreg(EECD), defreg(EERD), defreg(GPRC), - defreg(GPTC), defreg(ICR), defreg(ICS), defreg(IMC), - defreg(IMS), defreg(LEDCTL), defreg(MANC), defreg(MDIC), - defreg(MPC), defreg(PBA), defreg(RCTL), defreg(RDBAH), - defreg(RDBAL), defreg(RDH), defreg(RDLEN), defreg(RDT), - defreg(STATUS), defreg(SWSM), defreg(TCTL), defreg(TDBAH), - defreg(TDBAL), defreg(TDH), defreg(TDLEN), defreg(TDT), - defreg(TORH), defreg(TORL), defreg(TOTH), defreg(TOTL), - defreg(TPR), defreg(TPT), defreg(TXDCTL), defreg(WUFC), - defreg(RA), defreg(MTA), defreg(CRCERRS), defreg(VFTA), - defreg(VET), defreg(RDTR), defreg(RADV), defreg(TADV), - defreg(ITR), defreg(FCRUC), defreg(TDFH), defreg(TDFT), - defreg(TDFHS), defreg(TDFTS), defreg(TDFPC), defreg(RDFH), - defreg(RDFT), defreg(RDFHS), defreg(RDFTS), defreg(RDFPC), - defreg(IPAV), defreg(WUC), defreg(WUS), defreg(AIT), - defreg(IP6AT), defreg(IP4AT), defreg(FFLT), defreg(FFMT), - defreg(FFVT), defreg(WUPM), defreg(PBM), defreg(SCC), - defreg(ECOL), defreg(MCC), defreg(LATECOL), defreg(COLC), - defreg(DC), defreg(TNCRS), defreg(SEC), defreg(CEXTERR), - defreg(RLEC), defreg(XONRXC), defreg(XONTXC), defreg(XOFFRXC), - defreg(XOFFTXC), defreg(RFC), defreg(RJC), defreg(RNBC), - defreg(TSCTFC), defreg(MGTPRC), defreg(MGTPDC), defreg(MGTPTC), - defreg(RUC), defreg(ROC), defreg(GORCL), defreg(GORCH), - defreg(GOTCL), defreg(GOTCH), defreg(BPRC), defreg(MPRC), - defreg(TSCTC), defreg(PRC64), defreg(PRC127), defreg(PRC255), - defreg(PRC511), defreg(PRC1023), defreg(PRC1522), defreg(PTC64), - defreg(PTC127), defreg(PTC255), defreg(PTC511), defreg(PTC1023), - defreg(PTC1522), defreg(MPTC), defreg(BPTC) -}; - static void -e1000_link_down(E1000State *s) +e1000_link_up(E1000State *s) { - s->mac_reg[STATUS] &= ~E1000_STATUS_LU; - s->phy_reg[PHY_STATUS] &= ~MII_SR_LINK_STATUS; - s->phy_reg[PHY_STATUS] &= ~MII_SR_AUTONEG_COMPLETE; - s->phy_reg[PHY_LP_ABILITY] &= ~MII_LPAR_LPACK; + e1000x_update_regs_on_link_up(s->mac_reg, s->phy_reg); + + /* E1000_STATUS_LU is tested by e1000_can_receive() */ + qemu_flush_queued_packets(qemu_get_queue(s->nic)); } static void -e1000_link_up(E1000State *s) +e1000_autoneg_done(E1000State *s) { - s->mac_reg[STATUS] |= E1000_STATUS_LU; - s->phy_reg[PHY_STATUS] |= MII_SR_LINK_STATUS; + e1000x_update_regs_on_autoneg_done(s->mac_reg, s->phy_reg); /* E1000_STATUS_LU is tested by e1000_can_receive() */ qemu_flush_queued_packets(qemu_get_queue(s->nic)); @@ -233,10 +182,7 @@ set_phy_ctrl(E1000State *s, int index, uint16_t val) * down. */ if (have_autoneg(s) && (val & MII_CR_RESTART_AUTO_NEG)) { - e1000_link_down(s); - DBGOUT(PHY, "Start link auto negotiation\n"); - timer_mod(s->autoneg_timer, - qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 500); + e1000x_restart_autoneg(s->mac_reg, s->phy_reg, s->autoneg_timer); } } @@ -365,11 +311,9 @@ set_interrupt_cause(E1000State *s, int index, uint32_t val) */ mit_delay = (mit_delay < 500) ? 500 : mit_delay; - if (mit_delay) { - s->mit_timer_on = 1; - timer_mod(s->mit_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - mit_delay * 256); - } + s->mit_timer_on = 1; + timer_mod(s->mit_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + mit_delay * 256); s->mit_ide = 0; } } @@ -401,43 +345,16 @@ e1000_autoneg_timer(void *opaque) { E1000State *s = opaque; if (!qemu_get_queue(s->nic)->link_down) { - e1000_link_up(s); - s->phy_reg[PHY_LP_ABILITY] |= MII_LPAR_LPACK; - s->phy_reg[PHY_STATUS] |= MII_SR_AUTONEG_COMPLETE; - DBGOUT(PHY, "Auto negotiation is completed\n"); + e1000_autoneg_done(s); set_ics(s, 0, E1000_ICS_LSC); /* signal link status change to guest */ } } -static int -rxbufsize(uint32_t v) -{ - v &= E1000_RCTL_BSEX | E1000_RCTL_SZ_16384 | E1000_RCTL_SZ_8192 | - E1000_RCTL_SZ_4096 | E1000_RCTL_SZ_2048 | E1000_RCTL_SZ_1024 | - E1000_RCTL_SZ_512 | E1000_RCTL_SZ_256; - switch (v) { - case E1000_RCTL_BSEX | E1000_RCTL_SZ_16384: - return 16384; - case E1000_RCTL_BSEX | E1000_RCTL_SZ_8192: - return 8192; - case E1000_RCTL_BSEX | E1000_RCTL_SZ_4096: - return 4096; - case E1000_RCTL_SZ_1024: - return 1024; - case E1000_RCTL_SZ_512: - return 512; - case E1000_RCTL_SZ_256: - return 256; - } - return 2048; -} - static void e1000_reset(void *opaque) { E1000State *d = opaque; E1000BaseClass *edc = E1000_DEVICE_GET_CLASS(d); uint8_t *macaddr = d->conf.macaddr.a; - int i; timer_del(d->autoneg_timer); timer_del(d->mit_timer); @@ -453,17 +370,10 @@ static void e1000_reset(void *opaque) memset(&d->tx, 0, sizeof d->tx); if (qemu_get_queue(d->nic)->link_down) { - e1000_link_down(d); + e1000x_update_regs_on_link_down(d->mac_reg, d->phy_reg); } - /* Some guests expect pre-initialized RAH/RAL (AddrValid flag + MACaddr) */ - d->mac_reg[RA] = 0; - d->mac_reg[RA + 1] = E1000_RAH_AV; - for (i = 0; i < 4; i++) { - d->mac_reg[RA] |= macaddr[i] << (8 * i); - d->mac_reg[RA + 1] |= (i < 2) ? macaddr[i + 4] << (8 * i) : 0; - } - qemu_format_nic_info_str(qemu_get_queue(d->nic), macaddr); + e1000x_reset_mac_addr(d->nic, d->mac_reg, macaddr); } static void @@ -477,7 +387,7 @@ static void set_rx_control(E1000State *s, int index, uint32_t val) { s->mac_reg[RCTL] = val; - s->rxbuf_size = rxbufsize(val); + s->rxbuf_size = e1000x_rxbufsize(val); s->rxbuf_min_shift = ((val / E1000_RCTL_RDMTS_QUAT) & 3) + 1; DBGOUT(RX, "RCTL: %d, mac_reg[RCTL] = 0x%x\n", s->mac_reg[RDT], s->mac_reg[RCTL]); @@ -598,89 +508,15 @@ putsum(uint8_t *data, uint32_t n, uint32_t sloc, uint32_t css, uint32_t cse) } static inline void -inc_reg_if_not_full(E1000State *s, int index) -{ - if (s->mac_reg[index] != 0xffffffff) { - s->mac_reg[index]++; - } -} - -static inline void inc_tx_bcast_or_mcast_count(E1000State *s, const unsigned char *arr) { if (!memcmp(arr, bcast, sizeof bcast)) { - inc_reg_if_not_full(s, BPTC); + e1000x_inc_reg_if_not_full(s->mac_reg, BPTC); } else if (arr[0] & 1) { - inc_reg_if_not_full(s, MPTC); - } -} - -static void -grow_8reg_if_not_full(E1000State *s, int index, int size) -{ - uint64_t sum = s->mac_reg[index] | (uint64_t)s->mac_reg[index+1] << 32; - - if (sum + size < sum) { - sum = ~0ULL; - } else { - sum += size; - } - s->mac_reg[index] = sum; - s->mac_reg[index+1] = sum >> 32; -} - -static void -increase_size_stats(E1000State *s, const int *size_regs, int size) -{ - if (size > 1023) { - inc_reg_if_not_full(s, size_regs[5]); - } else if (size > 511) { - inc_reg_if_not_full(s, size_regs[4]); - } else if (size > 255) { - inc_reg_if_not_full(s, size_regs[3]); - } else if (size > 127) { - inc_reg_if_not_full(s, size_regs[2]); - } else if (size > 64) { - inc_reg_if_not_full(s, size_regs[1]); - } else if (size == 64) { - inc_reg_if_not_full(s, size_regs[0]); + e1000x_inc_reg_if_not_full(s->mac_reg, MPTC); } } -static inline int -vlan_enabled(E1000State *s) -{ - return ((s->mac_reg[CTRL] & E1000_CTRL_VME) != 0); -} - -static inline int -vlan_rx_filter_enabled(E1000State *s) -{ - return ((s->mac_reg[RCTL] & E1000_RCTL_VFE) != 0); -} - -static inline int -is_vlan_packet(E1000State *s, const uint8_t *buf) -{ - return (be16_to_cpup((uint16_t *)(buf + 12)) == - le16_to_cpu(s->mac_reg[VET])); -} - -static inline int -is_vlan_txd(uint32_t txd_lower) -{ - return ((txd_lower & E1000_TXD_CMD_VLE) != 0); -} - -/* FCS aka Ethernet CRC-32. We don't get it from backends and can't - * fill it in, just pad descriptor length by 4 bytes unless guest - * told us to strip it off the packet. */ -static inline int -fcs_len(E1000State *s) -{ - return (s->mac_reg[RCTL] & E1000_RCTL_SECRC) ? 0 : 4; -} - static void e1000_send_packet(E1000State *s, const uint8_t *buf, int size) { @@ -694,55 +530,60 @@ e1000_send_packet(E1000State *s, const uint8_t *buf, int size) qemu_send_packet(nc, buf, size); } inc_tx_bcast_or_mcast_count(s, buf); - increase_size_stats(s, PTCregs, size); + e1000x_increase_size_stats(s->mac_reg, PTCregs, 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; - if (tp->tse && tp->cptse) { - css = tp->ipcss; + if (tp->props.tse && tp->props.cptse) { + css = tp->props.ipcss; DBGOUT(TXSUM, "frames %d size %d ipcss %d\n", frames, tp->size, css); - if (tp->ip) { /* IPv4 */ + 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); } - css = tp->tucss; + css = tp->props.tucss; len = tp->size - css; - DBGOUT(TXSUM, "tcp %d tucss %d len %d\n", tp->tcp, css, len); - if (tp->tcp) { - sofar = frames * tp->mss; + DBGOUT(TXSUM, "tcp %d tucss %d len %d\n", tp->props.tcp, css, len); + if (tp->props.tcp) { + sofar = frames * tp->props.mss; stl_be_p(tp->data+css+4, ldl_be_p(tp->data+css+4)+sofar); /* seq */ - if (tp->paylen - sofar > tp->mss) { + if (tp->props.paylen - sofar > tp->props.mss) { tp->data[css + 13] &= ~9; /* PSH, FIN */ } else if (frames) { - inc_reg_if_not_full(s, TSCTC); + e1000x_inc_reg_if_not_full(s->mac_reg, TSCTC); } } else /* UDP */ stw_be_p(tp->data+css+4, len); - if (tp->sum_needed & E1000_TXD_POPTS_TXSM) { + 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->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); } tp->tso_frames++; } - if (tp->sum_needed & E1000_TXD_POPTS_TXSM) - putsum(tp->data, tp->size, tp->tucso, tp->tucss, tp->tucse); - if (tp->sum_needed & E1000_TXD_POPTS_IXSM) - putsum(tp->data, tp->size, tp->ipcso, tp->ipcss, tp->ipcse); + if (tp->props.sum_needed & E1000_TXD_POPTS_TXSM) { + putsum(tp->data, tp->size, tp->props.tucso, + tp->props.tucss, tp->props.tucse); + } + if (tp->props.sum_needed & E1000_TXD_POPTS_IXSM) { + putsum(tp->data, tp->size, tp->props.ipcso, + tp->props.ipcss, tp->props.ipcse); + } if (tp->vlan_needed) { memmove(tp->vlan, tp->data, 4); memmove(tp->data, tp->data + 4, 8); @@ -752,8 +593,8 @@ xmit_seg(E1000State *s) e1000_send_packet(s, tp->data, tp->size); } - inc_reg_if_not_full(s, TPT); - grow_8reg_if_not_full(s, TOTL, s->tx.size); + e1000x_inc_reg_if_not_full(s->mac_reg, TPT); + e1000x_grow_8reg_if_not_full(s->mac_reg, TOTL, s->tx.size); s->mac_reg[GPTC] = s->mac_reg[TPT]; s->mac_reg[GOTCL] = s->mac_reg[TOTL]; s->mac_reg[GOTCH] = s->mac_reg[TOTH]; @@ -765,7 +606,7 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp) PCIDevice *d = PCI_DEVICE(s); uint32_t txd_lower = le32_to_cpu(dp->lower.data); uint32_t dtype = txd_lower & (E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D); - unsigned int split_size = txd_lower & 0xffff, bytes, sz, op; + unsigned int split_size = txd_lower & 0xffff, bytes, sz; unsigned int msh = 0xfffff; uint64_t addr; struct e1000_context_desc *xp = (struct e1000_context_desc *)dp; @@ -773,38 +614,27 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp) s->mit_ide |= (txd_lower & E1000_TXD_CMD_IDE); if (dtype == E1000_TXD_CMD_DEXT) { /* context descriptor */ - op = le32_to_cpu(xp->cmd_and_length); - tp->ipcss = xp->lower_setup.ip_fields.ipcss; - tp->ipcso = xp->lower_setup.ip_fields.ipcso; - tp->ipcse = le16_to_cpu(xp->lower_setup.ip_fields.ipcse); - tp->tucss = xp->upper_setup.tcp_fields.tucss; - tp->tucso = xp->upper_setup.tcp_fields.tucso; - tp->tucse = le16_to_cpu(xp->upper_setup.tcp_fields.tucse); - tp->paylen = op & 0xfffff; - tp->hdr_len = xp->tcp_seg_setup.fields.hdr_len; - tp->mss = le16_to_cpu(xp->tcp_seg_setup.fields.mss); - tp->ip = (op & E1000_TXD_CMD_IP) ? 1 : 0; - tp->tcp = (op & E1000_TXD_CMD_TCP) ? 1 : 0; - tp->tse = (op & E1000_TXD_CMD_TSE) ? 1 : 0; + e1000x_read_tx_ctx_descr(xp, &tp->props); tp->tso_frames = 0; - if (tp->tucso == 0) { /* this is probably wrong */ + if (tp->props.tucso == 0) { /* this is probably wrong */ DBGOUT(TXSUM, "TCP/UDP: cso 0!\n"); - tp->tucso = tp->tucss + (tp->tcp ? 16 : 6); + tp->props.tucso = tp->props.tucss + (tp->props.tcp ? 16 : 6); } return; } else if (dtype == (E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D)) { // data descriptor if (tp->size == 0) { - tp->sum_needed = le32_to_cpu(dp->upper.data) >> 8; + tp->props.sum_needed = le32_to_cpu(dp->upper.data) >> 8; } - tp->cptse = ( txd_lower & E1000_TXD_CMD_TSE ) ? 1 : 0; + tp->props.cptse = (txd_lower & E1000_TXD_CMD_TSE) ? 1 : 0; } else { // legacy descriptor - tp->cptse = 0; + tp->props.cptse = 0; } - if (vlan_enabled(s) && is_vlan_txd(txd_lower) && - (tp->cptse || txd_lower & E1000_TXD_CMD_EOP)) { + if (e1000x_vlan_enabled(s->mac_reg) && + e1000x_is_vlan_txd(txd_lower) && + (tp->props.cptse || txd_lower & E1000_TXD_CMD_EOP)) { tp->vlan_needed = 1; stw_be_p(tp->vlan_header, le16_to_cpu(s->mac_reg[VET])); @@ -813,8 +643,8 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp) } addr = le64_to_cpu(dp->buffer_addr); - if (tp->tse && tp->cptse) { - msh = tp->hdr_len + tp->mss; + if (tp->props.tse && tp->props.cptse) { + msh = tp->props.hdr_len + tp->props.mss; do { bytes = split_size; if (tp->size + bytes > msh) @@ -823,19 +653,19 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp) bytes = MIN(sizeof(tp->data) - tp->size, bytes); pci_dma_read(d, addr, tp->data + tp->size, bytes); sz = tp->size + bytes; - if (sz >= tp->hdr_len && tp->size < tp->hdr_len) { - memmove(tp->header, tp->data, tp->hdr_len); + if (sz >= tp->props.hdr_len && tp->size < tp->props.hdr_len) { + memmove(tp->header, tp->data, tp->props.hdr_len); } tp->size = sz; addr += bytes; if (sz == msh) { xmit_seg(s); - memmove(tp->data, tp->header, tp->hdr_len); - tp->size = tp->hdr_len; + memmove(tp->data, tp->header, tp->props.hdr_len); + tp->size = tp->props.hdr_len; } split_size -= bytes; } while (bytes && split_size); - } else if (!tp->tse && tp->cptse) { + } else if (!tp->props.tse && tp->props.cptse) { // context descriptor TSE is not set, while data descriptor TSE is set DBGOUT(TXERR, "TCP segmentation error\n"); } else { @@ -846,14 +676,14 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp) if (!(txd_lower & E1000_TXD_CMD_EOP)) return; - if (!(tp->tse && tp->cptse && tp->size < tp->hdr_len)) { + if (!(tp->props.tse && tp->props.cptse && tp->size < tp->props.hdr_len)) { xmit_seg(s); } tp->tso_frames = 0; - tp->sum_needed = 0; + tp->props.sum_needed = 0; tp->vlan_needed = 0; tp->size = 0; - tp->cptse = 0; + tp->props.cptse = 0; } static uint32_t @@ -925,14 +755,14 @@ start_xmit(E1000State *s) static int receive_filter(E1000State *s, const uint8_t *buf, int size) { - static const int mta_shift[] = {4, 3, 2, 0}; - uint32_t f, rctl = s->mac_reg[RCTL], ra[2], *rp; + uint32_t rctl = s->mac_reg[RCTL]; int isbcast = !memcmp(buf, bcast, sizeof bcast), ismcast = (buf[0] & 1); - if (is_vlan_packet(s, buf) && vlan_rx_filter_enabled(s)) { - 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)); + if (e1000x_is_vlan_packet(buf, le16_to_cpu(s->mac_reg[VET])) && + e1000x_vlan_rx_filter_enabled(s->mac_reg)) { + 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; } @@ -942,44 +772,16 @@ receive_filter(E1000State *s, const uint8_t *buf, int size) } if (ismcast && (rctl & E1000_RCTL_MPE)) { /* promiscuous mcast */ - inc_reg_if_not_full(s, MPRC); + e1000x_inc_reg_if_not_full(s->mac_reg, MPRC); return 1; } if (isbcast && (rctl & E1000_RCTL_BAM)) { /* broadcast enabled */ - inc_reg_if_not_full(s, BPRC); - return 1; - } - - for (rp = s->mac_reg + RA; rp < s->mac_reg + RA + 32; rp += 2) { - if (!(rp[1] & E1000_RAH_AV)) - continue; - ra[0] = cpu_to_le32(rp[0]); - ra[1] = cpu_to_le32(rp[1]); - if (!memcmp(buf, (uint8_t *)ra, 6)) { - DBGOUT(RXFILTER, - "unicast match[%d]: %02x:%02x:%02x:%02x:%02x:%02x\n", - (int)(rp - s->mac_reg - RA)/2, - buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); - return 1; - } - } - DBGOUT(RXFILTER, "unicast mismatch: %02x:%02x:%02x:%02x:%02x:%02x\n", - buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); - - f = mta_shift[(rctl >> E1000_RCTL_MO_SHIFT) & 3]; - f = (((buf[5] << 8) | buf[4]) >> f) & 0xfff; - if (s->mac_reg[MTA + (f >> 5)] & (1 << (f & 0x1f))) { - inc_reg_if_not_full(s, MPRC); + e1000x_inc_reg_if_not_full(s->mac_reg, BPRC); return 1; } - DBGOUT(RXFILTER, - "dropping, inexact filter mismatch: %02x:%02x:%02x:%02x:%02x:%02x MO %d MTA[%d] %x\n", - buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], - (rctl >> E1000_RCTL_MO_SHIFT) & 3, f >> 5, - s->mac_reg[MTA + (f >> 5)]); - return 0; + return e1000x_rx_group_filter(s->mac_reg, buf); } static void @@ -989,13 +791,11 @@ e1000_set_link_status(NetClientState *nc) uint32_t old_status = s->mac_reg[STATUS]; if (nc->link_down) { - e1000_link_down(s); + e1000x_update_regs_on_link_down(s->mac_reg, s->phy_reg); } else { if (have_autoneg(s) && !(s->phy_reg[PHY_STATUS] & MII_SR_AUTONEG_COMPLETE)) { - /* emulate auto-negotiation if supported */ - timer_mod(s->autoneg_timer, - qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 500); + e1000x_restart_autoneg(s->mac_reg, s->phy_reg, s->autoneg_timer); } else { e1000_link_up(s); } @@ -1028,9 +828,7 @@ e1000_can_receive(NetClientState *nc) { E1000State *s = qemu_get_nic_opaque(nc); - return (s->mac_reg[STATUS] & E1000_STATUS_LU) && - (s->mac_reg[RCTL] & E1000_RCTL_EN) && - (s->parent_obj.config[PCI_COMMAND] & PCI_COMMAND_MASTER) && + return e1000x_rx_ready(&s->parent_obj, s->mac_reg) && e1000_has_rxbufs(s, 1); } @@ -1061,14 +859,8 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) size_t desc_offset; size_t desc_size; size_t total_size; - static const int PRCregs[6] = { PRC64, PRC127, PRC255, PRC511, - PRC1023, PRC1522 }; - - if (!(s->mac_reg[STATUS] & E1000_STATUS_LU)) { - return -1; - } - if (!(s->mac_reg[RCTL] & E1000_RCTL_EN)) { + if (!e1000x_hw_rx_enabled(s->mac_reg)) { return -1; } @@ -1076,7 +868,7 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) if (size < sizeof(min_buf)) { iov_to_buf(iov, iovcnt, 0, min_buf, size); memset(&min_buf[size], 0, sizeof(min_buf) - size); - inc_reg_if_not_full(s, RUC); + e1000x_inc_reg_if_not_full(s->mac_reg, RUC); min_iov.iov_base = filter_buf = min_buf; min_iov.iov_len = size = sizeof(min_buf); iovcnt = 1; @@ -1088,11 +880,7 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) } /* Discard oversized packets if !LPE and !SBP. */ - if ((size > MAXIMUM_ETHERNET_LPE_SIZE || - (size > MAXIMUM_ETHERNET_VLAN_SIZE - && !(s->mac_reg[RCTL] & E1000_RCTL_LPE))) - && !(s->mac_reg[RCTL] & E1000_RCTL_SBP)) { - inc_reg_if_not_full(s, ROC); + if (e1000x_is_oversized(s->mac_reg, size)) { return size; } @@ -1100,9 +888,9 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) return size; } - if (vlan_enabled(s) && is_vlan_packet(s, filter_buf)) { - vlan_special = cpu_to_le16(be16_to_cpup((uint16_t *)(filter_buf - + 14))); + 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(lduw_be_p(filter_buf + 14)); iov_ofs = 4; if (filter_buf == iov->iov_base) { memmove(filter_buf + 4, filter_buf, 12); @@ -1119,7 +907,7 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) rdh_start = s->mac_reg[RDH]; desc_offset = 0; - total_size = size + fcs_len(s); + total_size = size + e1000x_fcs_len(s->mac_reg); if (!e1000_has_rxbufs(s, total_size)) { set_ics(s, 0, E1000_ICS_RXO); return -1; @@ -1179,17 +967,7 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) } } while (desc_offset < total_size); - increase_size_stats(s, PRCregs, total_size); - inc_reg_if_not_full(s, TPR); - s->mac_reg[GPRC] = s->mac_reg[TPR]; - /* TOR - Total Octets Received: - * This register includes bytes received in a packet from the <Destination - * Address> field through the <CRC> field, inclusively. - * Always include FCS length (4) in size. - */ - grow_8reg_if_not_full(s, TORL, size+4); - s->mac_reg[GORCL] = s->mac_reg[TORL]; - s->mac_reg[GORCH] = s->mac_reg[TORH]; + e1000x_update_rx_total_stats(s->mac_reg, size, total_size); n = E1000_ICS_RXT0; if ((rdt = s->mac_reg[RDT]) < s->mac_reg[RDH]) @@ -1670,20 +1448,20 @@ static const VMStateDescription vmstate_e1000 = { VMSTATE_UINT16(eecd_state.bitnum_out, E1000State), VMSTATE_UINT16(eecd_state.reading, E1000State), VMSTATE_UINT32(eecd_state.old_eecd, E1000State), - VMSTATE_UINT8(tx.ipcss, E1000State), - VMSTATE_UINT8(tx.ipcso, E1000State), - VMSTATE_UINT16(tx.ipcse, E1000State), - VMSTATE_UINT8(tx.tucss, E1000State), - VMSTATE_UINT8(tx.tucso, E1000State), - VMSTATE_UINT16(tx.tucse, E1000State), - VMSTATE_UINT32(tx.paylen, E1000State), - VMSTATE_UINT8(tx.hdr_len, E1000State), - VMSTATE_UINT16(tx.mss, E1000State), + VMSTATE_UINT8(tx.props.ipcss, E1000State), + VMSTATE_UINT8(tx.props.ipcso, E1000State), + VMSTATE_UINT16(tx.props.ipcse, E1000State), + VMSTATE_UINT8(tx.props.tucss, E1000State), + VMSTATE_UINT8(tx.props.tucso, E1000State), + VMSTATE_UINT16(tx.props.tucse, E1000State), + VMSTATE_UINT32(tx.props.paylen, E1000State), + VMSTATE_UINT8(tx.props.hdr_len, E1000State), + VMSTATE_UINT16(tx.props.mss, E1000State), VMSTATE_UINT16(tx.size, E1000State), VMSTATE_UINT16(tx.tso_frames, E1000State), - VMSTATE_UINT8(tx.sum_needed, E1000State), - VMSTATE_INT8(tx.ip, E1000State), - VMSTATE_INT8(tx.tcp, E1000State), + VMSTATE_UINT8(tx.props.sum_needed, E1000State), + VMSTATE_INT8(tx.props.ip, E1000State), + VMSTATE_INT8(tx.props.tcp, E1000State), VMSTATE_BUFFER(tx.header, E1000State), VMSTATE_BUFFER(tx.data, E1000State), VMSTATE_UINT16_ARRAY(eeprom_data, E1000State, 64), @@ -1785,7 +1563,7 @@ pci_e1000_uninit(PCIDevice *dev) } static NetClientInfo net_e1000_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, + .type = NET_CLIENT_DRIVER_NIC, .size = sizeof(NICState), .can_receive = e1000_can_receive, .receive = e1000_receive, @@ -1806,15 +1584,11 @@ static void e1000_write_config(PCIDevice *pci_dev, uint32_t address, } } - static void pci_e1000_realize(PCIDevice *pci_dev, Error **errp) { DeviceState *dev = DEVICE(pci_dev); E1000State *d = E1000(pci_dev); - PCIDeviceClass *pdc = PCI_DEVICE_GET_CLASS(pci_dev); uint8_t *pci_conf; - uint16_t checksum = 0; - int i; uint8_t *macaddr; pci_dev->config_write = e1000_write_config; @@ -1832,17 +1606,14 @@ static void pci_e1000_realize(PCIDevice *pci_dev, Error **errp) pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &d->io); - memmove(d->eeprom_data, e1000_eeprom_template, - sizeof e1000_eeprom_template); qemu_macaddr_default_if_unset(&d->conf.macaddr); macaddr = d->conf.macaddr.a; - for (i = 0; i < 3; i++) - d->eeprom_data[i] = (macaddr[2*i+1]<<8) | macaddr[2*i]; - d->eeprom_data[11] = d->eeprom_data[13] = pdc->device_id; - for (i = 0; i < EEPROM_CHECKSUM_REG; i++) - checksum += d->eeprom_data[i]; - checksum = (uint16_t) EEPROM_SUM - checksum; - d->eeprom_data[EEPROM_CHECKSUM_REG] = checksum; + + e1000x_core_prepare_eeprom(d->eeprom_data, + e1000_eeprom_template, + sizeof(e1000_eeprom_template), + PCI_DEVICE_GET_CLASS(pci_dev)->device_id, + macaddr); d->nic = qemu_new_nic(&net_e1000_info, &d->conf, object_get_typename(OBJECT(d)), dev->id, d); diff --git a/hw/net/e1000_regs.h b/hw/net/e1000_regs.h index 1c40244ab..23eed50b9 100644 --- a/hw/net/e1000_regs.h +++ b/hw/net/e1000_regs.h @@ -29,9 +29,8 @@ * Structures, enums, and macros for the MAC */ -#ifndef _E1000_HW_H_ -#define _E1000_HW_H_ - +#ifndef HW_E1000_REGS_H +#define HW_E1000_REGS_H /* PCI Device IDs */ #define E1000_DEV_ID_82542 0x1000 @@ -85,6 +84,7 @@ #define E1000_DEV_ID_82573E 0x108B #define E1000_DEV_ID_82573E_IAMT 0x108C #define E1000_DEV_ID_82573L 0x109A +#define E1000_DEV_ID_82574L 0x10D3 #define E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3 0x10B5 #define E1000_DEV_ID_80003ES2LAN_COPPER_DPT 0x1096 #define E1000_DEV_ID_80003ES2LAN_SERDES_DPT 0x1098 @@ -104,6 +104,7 @@ #define E1000_PHY_ID2_82544x 0xC30 #define E1000_PHY_ID2_8254xx_DEFAULT 0xC20 /* 82540x, 82545x, and 82546x */ #define E1000_PHY_ID2_82573x 0xCC0 +#define E1000_PHY_ID2_82574x 0xCB1 /* Register Set. (82543, 82544) * @@ -135,8 +136,11 @@ #define E1000_ITR 0x000C4 /* Interrupt Throttling Rate - RW */ #define E1000_ICS 0x000C8 /* Interrupt Cause Set - WO */ #define E1000_IMS 0x000D0 /* Interrupt Mask Set - RW */ +#define E1000_EIAC 0x000DC /* Ext. Interrupt Auto Clear - RW */ #define E1000_IMC 0x000D8 /* Interrupt Mask Clear - WO */ #define E1000_IAM 0x000E0 /* Interrupt Acknowledge Auto Mask */ +#define E1000_IVAR 0x000E4 /* Interrupt Vector Allocation Register - RW */ +#define E1000_EITR 0x000E8 /* Extended Interrupt Throttling Rate - RW */ #define E1000_RCTL 0x00100 /* RX Control - RW */ #define E1000_RDTR1 0x02820 /* RX Delay Timer (1) - RW */ #define E1000_RDBAL1 0x02900 /* RX Descriptor Base Address Low (1) - RW */ @@ -145,6 +149,7 @@ #define E1000_RDH1 0x02910 /* RX Descriptor Head (1) - RW */ #define E1000_RDT1 0x02918 /* RX Descriptor Tail (1) - RW */ #define E1000_FCTTV 0x00170 /* Flow Control Transmit Timer Value - RW */ +#define E1000_FCRTV 0x05F40 /* Flow Control Refresh Timer Value - RW */ #define E1000_TXCW 0x00178 /* TX Configuration Word - RW */ #define E1000_RXCW 0x00180 /* RX Configuration Word - RO */ #define E1000_TCTL 0x00400 /* TX Control - RW */ @@ -161,6 +166,10 @@ #define E1000_PBM 0x10000 /* Packet Buffer Memory - RW */ #define E1000_PBS 0x01008 /* Packet Buffer Size - RW */ #define E1000_EEMNGCTL 0x01010 /* MNG EEprom Control */ +#define E1000_EEMNGDATA 0x01014 /* MNG EEPROM Read/Write data */ +#define E1000_FLMNGCTL 0x01018 /* MNG Flash Control */ +#define E1000_FLMNGDATA 0x0101C /* MNG FLASH Read data */ +#define E1000_FLMNGCNT 0x01020 /* MNG FLASH Read Counter */ #define E1000_FLASH_UPDATES 1000 #define E1000_EEARBC 0x01024 /* EEPROM Auto Read Bus Control */ #define E1000_FLASHT 0x01028 /* FLASH Timer Register */ @@ -169,9 +178,12 @@ #define E1000_FLSWDATA 0x01034 /* FLASH data register */ #define E1000_FLSWCNT 0x01038 /* FLASH Access Counter */ #define E1000_FLOP 0x0103C /* FLASH Opcode Register */ +#define E1000_FLOL 0x01050 /* FEEP Auto Load */ #define E1000_ERT 0x02008 /* Early Rx Threshold - RW */ #define E1000_FCRTL 0x02160 /* Flow Control Receive Threshold Low - RW */ +#define E1000_FCRTL_A 0x00168 /* Alias to FCRTL */ #define E1000_FCRTH 0x02168 /* Flow Control Receive Threshold High - RW */ +#define E1000_FCRTH_A 0x00160 /* Alias to FCRTH */ #define E1000_PSRCTL 0x02170 /* Packet Split Receive Control - RW */ #define E1000_RDBAL 0x02800 /* RX Descriptor Base Address Low - RW */ #define E1000_RDBAH 0x02804 /* RX Descriptor Base Address High - RW */ @@ -179,11 +191,17 @@ #define E1000_RDH 0x02810 /* RX Descriptor Head - RW */ #define E1000_RDT 0x02818 /* RX Descriptor Tail - RW */ #define E1000_RDTR 0x02820 /* RX Delay Timer - RW */ +#define E1000_RDTR_A 0x00108 /* Alias to RDTR */ #define E1000_RDBAL0 E1000_RDBAL /* RX Desc Base Address Low (0) - RW */ +#define E1000_RDBAL0_A 0x00110 /* Alias to RDBAL0 */ #define E1000_RDBAH0 E1000_RDBAH /* RX Desc Base Address High (0) - RW */ +#define E1000_RDBAH0_A 0x00114 /* Alias to RDBAH0 */ #define E1000_RDLEN0 E1000_RDLEN /* RX Desc Length (0) - RW */ +#define E1000_RDLEN0_A 0x00118 /* Alias to RDLEN0 */ #define E1000_RDH0 E1000_RDH /* RX Desc Head (0) - RW */ +#define E1000_RDH0_A 0x00120 /* Alias to RDH0 */ #define E1000_RDT0 E1000_RDT /* RX Desc Tail (0) - RW */ +#define E1000_RDT0_A 0x00128 /* Alias to RDT0 */ #define E1000_RDTR0 E1000_RDTR /* RX Delay Timer (0) - RW */ #define E1000_RXDCTL 0x02828 /* RX Descriptor Control queue 0 - RW */ #define E1000_RXDCTL1 0x02928 /* RX Descriptor Control queue 1 - RW */ @@ -192,22 +210,33 @@ #define E1000_RAID 0x02C08 /* Receive Ack Interrupt Delay - RW */ #define E1000_TXDMAC 0x03000 /* TX DMA Control - RW */ #define E1000_KABGTXD 0x03004 /* AFE Band Gap Transmit Ref Data */ +#define E1000_POEMB 0x00F10 /* PHY OEM Bits Register - RW */ #define E1000_RDFH 0x02410 /* Receive Data FIFO Head Register - RW */ +#define E1000_RDFH_A 0x08000 /* Alias to RDFH */ #define E1000_RDFT 0x02418 /* Receive Data FIFO Tail Register - RW */ +#define E1000_RDFT_A 0x08008 /* Alias to RDFT */ #define E1000_RDFHS 0x02420 /* Receive Data FIFO Head Saved Register - RW */ #define E1000_RDFTS 0x02428 /* Receive Data FIFO Tail Saved Register - RW */ #define E1000_RDFPC 0x02430 /* Receive Data FIFO Packet Count - RW */ #define E1000_TDFH 0x03410 /* TX Data FIFO Head - RW */ +#define E1000_TDFH_A 0x08010 /* Alias to TDFH */ #define E1000_TDFT 0x03418 /* TX Data FIFO Tail - RW */ +#define E1000_TDFT_A 0x08018 /* Alias to TDFT */ #define E1000_TDFHS 0x03420 /* TX Data FIFO Head Saved - RW */ #define E1000_TDFTS 0x03428 /* TX Data FIFO Tail Saved - RW */ #define E1000_TDFPC 0x03430 /* TX Data FIFO Packet Count - RW */ #define E1000_TDBAL 0x03800 /* TX Descriptor Base Address Low - RW */ +#define E1000_TDBAL_A 0x00420 /* Alias to TDBAL */ #define E1000_TDBAH 0x03804 /* TX Descriptor Base Address High - RW */ +#define E1000_TDBAH_A 0x00424 /* Alias to TDBAH */ #define E1000_TDLEN 0x03808 /* TX Descriptor Length - RW */ +#define E1000_TDLEN_A 0x00428 /* Alias to TDLEN */ #define E1000_TDH 0x03810 /* TX Descriptor Head - RW */ +#define E1000_TDH_A 0x00430 /* Alias to TDH */ #define E1000_TDT 0x03818 /* TX Descripotr Tail - RW */ +#define E1000_TDT_A 0x00438 /* Alias to TDT */ #define E1000_TIDV 0x03820 /* TX Interrupt Delay Value - RW */ +#define E1000_TIDV_A 0x00440 /* Alias to TIDV */ #define E1000_TXDCTL 0x03828 /* TX Descriptor Control - RW */ #define E1000_TADV 0x0382C /* TX Interrupt Absolute Delay Val - RW */ #define E1000_TSPMT 0x03830 /* TCP Segmentation PAD & Min Threshold - RW */ @@ -288,9 +317,15 @@ #define E1000_ICRXOC 0x04124 /* Interrupt Cause Receiver Overrun Count */ #define E1000_RXCSUM 0x05000 /* RX Checksum Control - RW */ #define E1000_RFCTL 0x05008 /* Receive Filter Control*/ +#define E1000_MAVTV0 0x05010 /* Management VLAN TAG Value 0 */ +#define E1000_MAVTV1 0x05014 /* Management VLAN TAG Value 1 */ +#define E1000_MAVTV2 0x05018 /* Management VLAN TAG Value 2 */ +#define E1000_MAVTV3 0x0501c /* Management VLAN TAG Value 3 */ #define E1000_MTA 0x05200 /* Multicast Table Array - RW Array */ #define E1000_RA 0x05400 /* Receive Address - RW Array */ +#define E1000_RA_A 0x00040 /* Alias to RA */ #define E1000_VFTA 0x05600 /* VLAN Filter Table Array - RW Array */ +#define E1000_VFTA_A 0x00600 /* Alias to VFTA */ #define E1000_WUC 0x05800 /* Wakeup Control - RW */ #define E1000_WUFC 0x05808 /* Wakeup Filter Control - RW */ #define E1000_WUS 0x05810 /* Wakeup Status - RO */ @@ -300,27 +335,57 @@ #define E1000_IP6AT 0x05880 /* IPv6 Address Table - RW Array */ #define E1000_WUPL 0x05900 /* Wakeup Packet Length - RW */ #define E1000_WUPM 0x05A00 /* Wakeup Packet Memory - RO A */ +#define E1000_MFUTP01 0x05828 /* Management Flex UDP/TCP Ports 0/1 - RW */ +#define E1000_MFUTP23 0x05830 /* Management Flex UDP/TCP Ports 2/3 - RW */ +#define E1000_MFVAL 0x05824 /* Manageability Filters Valid - RW */ +#define E1000_MDEF 0x05890 /* Manageability Decision Filters - RW Array */ #define E1000_FFLT 0x05F00 /* Flexible Filter Length Table - RW Array */ #define E1000_HOST_IF 0x08800 /* Host Interface */ #define E1000_FFMT 0x09000 /* Flexible Filter Mask Table - RW Array */ +#define E1000_FTFT 0x09400 /* Flexible TCO Filter Table - RW Array */ #define E1000_FFVT 0x09800 /* Flexible Filter Value Table - RW Array */ #define E1000_KUMCTRLSTA 0x00034 /* MAC-PHY interface - RW */ -#define E1000_MDPHYA 0x0003C /* PHY address - RW */ -#define E1000_MANC2H 0x05860 /* Management Control To Host - RW */ +#define E1000_MDPHYA 0x0003C /* PHY address - RW */ +#define E1000_MANC2H 0x05860 /* Management Control To Host - RW */ #define E1000_SW_FW_SYNC 0x05B5C /* Software-Firmware Synchronization - RW */ #define E1000_GCR 0x05B00 /* PCI-Ex Control */ +#define E1000_FUNCTAG 0x05B08 /* Function-Tag Register */ #define E1000_GSCL_1 0x05B10 /* PCI-Ex Statistic Control #1 */ #define E1000_GSCL_2 0x05B14 /* PCI-Ex Statistic Control #2 */ #define E1000_GSCL_3 0x05B18 /* PCI-Ex Statistic Control #3 */ #define E1000_GSCL_4 0x05B1C /* PCI-Ex Statistic Control #4 */ +#define E1000_GSCN_0 0x05B20 /* 3GIO Statistic Counter Register #0 */ +#define E1000_GSCN_1 0x05B24 /* 3GIO Statistic Counter Register #1 */ +#define E1000_GSCN_2 0x05B28 /* 3GIO Statistic Counter Register #2 */ +#define E1000_GSCN_3 0x05B2C /* 3GIO Statistic Counter Register #3 */ #define E1000_FACTPS 0x05B30 /* Function Active and Power State to MNG */ #define E1000_SWSM 0x05B50 /* SW Semaphore */ +#define E1000_GCR2 0x05B64 /* 3GIO Control Register 2 */ #define E1000_FWSM 0x05B54 /* FW Semaphore */ +#define E1000_PBACLR 0x05B68 /* MSI-X PBA Clear */ #define E1000_FFLT_DBG 0x05F04 /* Debug Register */ #define E1000_HICR 0x08F00 /* Host Inteface Control */ +#define E1000_TSYNCRXCTL 0x0B620 /* Rx Time Sync Control register - RW */ +#define E1000_TSYNCTXCTL 0x0B614 /* Tx Time Sync Control register - RW */ +#define E1000_TIMINCA 0x0B608 /* Increment attributes register - RW */ +#define E1000_RXSTMPL 0x0B624 /* Rx timestamp Low - RO */ +#define E1000_RXSTMPH 0x0B628 /* Rx timestamp High - RO */ +#define E1000_TXSTMPL 0x0B618 /* Tx timestamp value Low - RO */ +#define E1000_TXSTMPH 0x0B61C /* Tx timestamp value High - RO */ +#define E1000_SYSTIML 0x0B600 /* System time register Low - RO */ +#define E1000_SYSTIMH 0x0B604 /* System time register High - RO */ +#define E1000_TIMINCA 0x0B608 /* Increment attributes register - RW */ +#define E1000_RXMTRL 0x0B634 /* Time sync Rx EtherType and Msg Type - RW */ +#define E1000_RXUDP 0x0B638 /* Time Sync Rx UDP Port - RW */ +#define E1000_RXSATRL 0x0B62C /* Rx timestamp attribute low - RO */ +#define E1000_RXSATRH 0x0B630 /* Rx timestamp attribute high - RO */ +#define E1000_TIMADJL 0x0B60C /* Time Adjustment Offset register Low - RW */ +#define E1000_TIMADJH 0x0B610 /* Time Adjustment Offset register High - RW */ +#define E1000_RXCFGL 0x0B634 /* RX Ethertype and Message Type - RW*/ + /* RSS registers */ #define E1000_CPUVEC 0x02C10 /* CPU Vector Register - RW */ #define E1000_MRQC 0x05818 /* Multiple Receive Control - RW */ @@ -329,6 +394,85 @@ #define E1000_RSSIM 0x05864 /* RSS Interrupt Mask */ #define E1000_RSSIR 0x05868 /* RSS Interrupt Request */ +#define E1000_MRQC_ENABLED(mrqc) (((mrqc) & (BIT(0) | BIT(1))) == BIT(0)) + +#define E1000_RETA_IDX(hash) ((hash) & (BIT(7) - 1)) +#define E1000_RETA_VAL(reta, hash) (((uint8_t *)(reta))[E1000_RETA_IDX(hash)]) +#define E1000_RSS_QUEUE(reta, hash) ((E1000_RETA_VAL(reta, hash) & BIT(7)) >> 7) + +#define E1000_MRQC_EN_TCPIPV4(mrqc) ((mrqc) & BIT(16)) +#define E1000_MRQC_EN_IPV4(mrqc) ((mrqc) & BIT(17)) +#define E1000_MRQC_EN_TCPIPV6(mrqc) ((mrqc) & BIT(18)) +#define E1000_MRQC_EN_IPV6EX(mrqc) ((mrqc) & BIT(19)) +#define E1000_MRQC_EN_IPV6(mrqc) ((mrqc) & BIT(20)) + +#define E1000_MRQ_RSS_TYPE_NONE (0) +#define E1000_MRQ_RSS_TYPE_IPV4TCP (1) +#define E1000_MRQ_RSS_TYPE_IPV4 (2) +#define E1000_MRQ_RSS_TYPE_IPV6TCP (3) +#define E1000_MRQ_RSS_TYPE_IPV6EX (4) +#define E1000_MRQ_RSS_TYPE_IPV6 (5) + +#define E1000_ICR_ASSERTED BIT(31) +#define E1000_EIAC_MASK 0x01F00000 + +/* [TR]DBAL and [TR]DLEN masks */ +#define E1000_XDBAL_MASK (~(BIT(4) - 1)) +#define E1000_XDLEN_MASK ((BIT(20) - 1) & (~(BIT(7) - 1))) + +/* IVAR register parsing helpers */ +#define E1000_IVAR_INT_ALLOC_VALID (0x8) + +#define E1000_IVAR_RXQ0_SHIFT (0) +#define E1000_IVAR_RXQ1_SHIFT (4) +#define E1000_IVAR_TXQ0_SHIFT (8) +#define E1000_IVAR_TXQ1_SHIFT (12) +#define E1000_IVAR_OTHER_SHIFT (16) + +#define E1000_IVAR_ENTRY_MASK (0xF) +#define E1000_IVAR_ENTRY_VALID_MASK E1000_IVAR_INT_ALLOC_VALID +#define E1000_IVAR_ENTRY_VEC_MASK (0x7) + +#define E1000_IVAR_RXQ0(x) ((x) >> E1000_IVAR_RXQ0_SHIFT) +#define E1000_IVAR_RXQ1(x) ((x) >> E1000_IVAR_RXQ1_SHIFT) +#define E1000_IVAR_TXQ0(x) ((x) >> E1000_IVAR_TXQ0_SHIFT) +#define E1000_IVAR_TXQ1(x) ((x) >> E1000_IVAR_TXQ1_SHIFT) +#define E1000_IVAR_OTHER(x) ((x) >> E1000_IVAR_OTHER_SHIFT) + +#define E1000_IVAR_ENTRY_VALID(x) ((x) & E1000_IVAR_ENTRY_VALID_MASK) +#define E1000_IVAR_ENTRY_VEC(x) ((x) & E1000_IVAR_ENTRY_VEC_MASK) + +#define E1000_IVAR_TX_INT_EVERY_WB BIT(31) + +/* RFCTL register bits */ +#define E1000_RFCTL_ISCSI_DIS 0x00000001 +#define E1000_RFCTL_NFSW_DIS 0x00000040 +#define E1000_RFCTL_NFSR_DIS 0x00000080 +#define E1000_RFCTL_IPV6_DIS 0x00000400 +#define E1000_RFCTL_IPV6_XSUM_DIS 0x00000800 +#define E1000_RFCTL_ACK_DIS 0x00001000 +#define E1000_RFCTL_ACK_DATA_DIS 0x00002000 +#define E1000_RFCTL_IPFRSP_DIS 0x00004000 +#define E1000_RFCTL_EXTEN 0x00008000 +#define E1000_RFCTL_IPV6_EX_DIS 0x00010000 +#define E1000_RFCTL_NEW_IPV6_EXT_DIS 0x00020000 + +/* PSRCTL parsing */ +#define E1000_PSRCTL_BSIZE0_MASK 0x0000007F +#define E1000_PSRCTL_BSIZE1_MASK 0x00003F00 +#define E1000_PSRCTL_BSIZE2_MASK 0x003F0000 +#define E1000_PSRCTL_BSIZE3_MASK 0x3F000000 + +#define E1000_PSRCTL_BSIZE0_SHIFT 0 +#define E1000_PSRCTL_BSIZE1_SHIFT 8 +#define E1000_PSRCTL_BSIZE2_SHIFT 16 +#define E1000_PSRCTL_BSIZE3_SHIFT 24 + +#define E1000_PSRCTL_BUFFS_PER_DESC 4 + +/* TARC* parsing */ +#define E1000_TARC_ENABLE BIT(10) + /* PHY 1000 MII Register/Bit Definitions */ /* PHY Registers defined by IEEE */ #define PHY_CTRL 0x00 /* Control Register */ @@ -344,6 +488,40 @@ #define PHY_1000T_STATUS 0x0A /* 1000Base-T Status Reg */ #define PHY_EXT_STATUS 0x0F /* Extended Status Reg */ +/* 82574-specific registers */ +#define PHY_COPPER_CTRL1 0x10 /* Copper Specific Control Register 1 */ +#define PHY_COPPER_STAT1 0x11 /* Copper Specific Status Register 1 */ +#define PHY_COPPER_INT_ENABLE 0x12 /* Interrupt Enable Register */ +#define PHY_COPPER_STAT2 0x13 /* Copper Specific Status Register 2 */ +#define PHY_COPPER_CTRL3 0x14 /* Copper Specific Control Register 3 */ +#define PHY_COPPER_CTRL2 0x1A /* Copper Specific Control Register 2 */ +#define PHY_RX_ERR_CNTR 0x15 /* Receive Error Counter */ +#define PHY_PAGE 0x16 /* Page Address (Any page) */ +#define PHY_OEM_BITS 0x19 /* OEM Bits (Page 0) */ +#define PHY_BIAS_1 0x1d /* Bias Setting Register */ +#define PHY_BIAS_2 0x1e /* Bias Setting Register */ + +/* 82574-specific registers - page 2 */ +#define PHY_MAC_CTRL1 0x10 /* MAC Specific Control Register 1 */ +#define PHY_MAC_INT_ENABLE 0x12 /* MAC Interrupt Enable Register */ +#define PHY_MAC_STAT 0x13 /* MAC Specific Status Register */ +#define PHY_MAC_CTRL2 0x15 /* MAC Specific Control Register 2 */ + +/* 82574-specific registers - page 3 */ +#define PHY_LED_03_FUNC_CTRL1 0x10 /* LED[3:0] Function Control */ +#define PHY_LED_03_POL_CTRL 0x11 /* LED[3:0] Polarity Control */ +#define PHY_LED_TIMER_CTRL 0x12 /* LED Timer Control */ +#define PHY_LED_45_CTRL 0x13 /* LED[5:4] Function Control and Polarity */ + +/* 82574-specific registers - page 5 */ +#define PHY_1000T_SKEW 0x14 /* 1000 BASE - T Pair Skew Register */ +#define PHY_1000T_SWAP 0x15 /* 1000 BASE - T Pair Swap and Polarity */ + +/* 82574-specific registers - page 6 */ +#define PHY_CRC_COUNTERS 0x11 /* CRC Counters */ + +#define PHY_PAGE_RW_MASK 0x7F /* R/W part of page address register */ + #define MAX_PHY_REG_ADDRESS 0x1F /* 5 bit address bus (0-0x1F) */ #define MAX_PHY_MULTI_PAGE_REG 0xF /* Registers equal on all pages */ @@ -423,6 +601,18 @@ #define E1000_ICR_DSW 0x00000020 /* FW changed the status of DISSW bit in the FWSM */ #define E1000_ICR_PHYINT 0x00001000 /* LAN connected device generates an interrupt */ #define E1000_ICR_EPRST 0x00100000 /* ME handware reset occurs */ +#define E1000_ICR_RXQ0 0x00100000 /* Rx Queue 0 Interrupt */ +#define E1000_ICR_RXQ1 0x00200000 /* Rx Queue 1 Interrupt */ +#define E1000_ICR_TXQ0 0x00400000 /* Tx Queue 0 Interrupt */ +#define E1000_ICR_TXQ1 0x00800000 /* Tx Queue 1 Interrupt */ +#define E1000_ICR_OTHER 0x01000000 /* Other Interrupts */ + +#define E1000_ICR_OTHER_CAUSES (E1000_ICR_LSC | \ + E1000_ICR_RXO | \ + E1000_ICR_MDAC | \ + E1000_ICR_SRPD | \ + E1000_ICR_ACK | \ + E1000_ICR_MNG) /* Interrupt Cause Set */ #define E1000_ICS_TXDW E1000_ICR_TXDW /* Transmit desc written back */ @@ -471,6 +661,11 @@ #define E1000_IMS_SRPD E1000_ICR_SRPD #define E1000_IMS_ACK E1000_ICR_ACK /* Receive Ack frame */ #define E1000_IMS_MNG E1000_ICR_MNG /* Manageability event */ +#define E1000_IMS_RXQ0 E1000_ICR_RXQ0 +#define E1000_IMS_RXQ1 E1000_ICR_RXQ1 +#define E1000_IMS_TXQ0 E1000_ICR_TXQ0 +#define E1000_IMS_TXQ1 E1000_ICR_TXQ1 +#define E1000_IMS_OTHER E1000_ICR_OTHER #define E1000_IMS_DOCK E1000_ICR_DOCK /* Dock/Undock */ #define E1000_IMS_RXD_FIFO_PAR0 E1000_ICR_RXD_FIFO_PAR0 /* queue 0 Rx descriptor FIFO parity error */ #define E1000_IMS_TXD_FIFO_PAR0 E1000_ICR_TXD_FIFO_PAR0 /* queue 0 Tx descriptor FIFO parity error */ @@ -562,6 +757,15 @@ #define E1000_EEPROM_RW_ADDR_SHIFT 8 /* Shift to the address bits */ #define E1000_EEPROM_POLL_WRITE 1 /* Flag for polling for write complete */ #define E1000_EEPROM_POLL_READ 0 /* Flag for polling for read complete */ + +/* 82574 EERD/EEWR registers layout */ +#define E1000_EERW_START BIT(0) +#define E1000_EERW_DONE BIT(1) +#define E1000_EERW_ADDR_SHIFT 2 +#define E1000_EERW_ADDR_MASK ((1L << 14) - 1) +#define E1000_EERW_DATA_SHIFT 16 +#define E1000_EERW_DATA_MASK ((1L << 16) - 1) + /* Register Bit Masks */ /* Device Control */ #define E1000_CTRL_FD 0x00000001 /* Full duplex.0=half; 1=full */ @@ -584,7 +788,17 @@ #define E1000_CTRL_D_UD_EN 0x00002000 /* Dock/Undock enable */ #define E1000_CTRL_D_UD_POLARITY 0x00004000 /* Defined polarity of Dock/Undock indication in SDP[0] */ #define E1000_CTRL_FORCE_PHY_RESET 0x00008000 /* Reset both PHY ports, through PHYRST_N pin */ +#define E1000_CTRL_SPD_SHIFT 8 /* Speed Select Shift */ + +#define E1000_CTRL_EXT_ASDCHK 0x00001000 /* auto speed detection check */ +#define E1000_CTRL_EXT_EE_RST 0x00002000 /* EEPROM reset */ #define E1000_CTRL_EXT_LINK_EN 0x00010000 /* enable link status from external LINK_0 and LINK_1 pins */ +#define E1000_CTRL_EXT_EIAME 0x01000000 +#define E1000_CTRL_EXT_IAME 0x08000000 /* Int ACK Auto-mask */ +#define E1000_CTRL_EXT_PBA_CLR 0x80000000 /* PBA Clear */ +#define E1000_CTRL_EXT_INT_TIMERS_CLEAR_ENA 0x20000000 +#define E1000_CTRL_EXT_SPD_BYPS 0x00008000 /* Speed Select Bypass */ + #define E1000_CTRL_SWDPIN0 0x00040000 /* SWDPIN 0 value */ #define E1000_CTRL_SWDPIN1 0x00080000 /* SWDPIN 1 value */ #define E1000_CTRL_SWDPIN2 0x00100000 /* SWDPIN 2 value */ @@ -593,6 +807,7 @@ #define E1000_CTRL_SWDPIO1 0x00800000 /* SWDPIN 1 input or output */ #define E1000_CTRL_SWDPIO2 0x01000000 /* SWDPIN 2 input or output */ #define E1000_CTRL_SWDPIO3 0x02000000 /* SWDPIN 3 input or output */ +#define E1000_CTRL_ADVD3WUC 0x00100000 /* D3 WUC */ #define E1000_CTRL_RST 0x04000000 /* Global reset */ #define E1000_CTRL_RFCE 0x08000000 /* Receive Flow Control enable */ #define E1000_CTRL_TFCE 0x10000000 /* Transmit flow control enable */ @@ -617,9 +832,13 @@ #define E1000_STATUS_LAN_INIT_DONE 0x00000200 /* Lan Init Completion by EEPROM/Flash */ #define E1000_STATUS_ASDV 0x00000300 /* Auto speed detect value */ +#define E1000_STATUS_ASDV_10 0x00000000 /* ASDV 10Mb */ +#define E1000_STATUS_ASDV_100 0x00000100 /* ASDV 100Mb */ +#define E1000_STATUS_ASDV_1000 0x00000200 /* ASDV 1Gb */ #define E1000_STATUS_DOCK_CI 0x00000800 /* Change in Dock/Undock state. Clear on write '0'. */ #define E1000_STATUS_GIO_MASTER_ENABLE 0x00080000 /* Status of Master requests. */ #define E1000_STATUS_MTXCKOK 0x00000400 /* MTX clock running OK */ +#define E1000_STATUS_PHYRA 0x00000400 /* PHY Reset Asserted */ #define E1000_STATUS_PCI66 0x00000800 /* In 66Mhz slot */ #define E1000_STATUS_BUS64 0x00001000 /* In 64 bit slot */ #define E1000_STATUS_PCIX_MODE 0x00002000 /* PCI-X mode */ @@ -634,6 +853,8 @@ #define E1000_STATUS_FUSE_9 0x08000000 #define E1000_STATUS_SERDES0_DIS 0x10000000 /* SERDES disabled on port 0 */ #define E1000_STATUS_SERDES1_DIS 0x20000000 /* SERDES disabled on port 1 */ +#define E1000_STATUS_SPEED_SHIFT 6 +#define E1000_STATUS_ASDV_SHIFT 8 /* EEPROM/Flash Control */ #define E1000_EECD_SK 0x00000001 /* EEPROM Clock */ @@ -664,6 +885,8 @@ #define E1000_EECD_AUPDEN 0x00100000 /* Enable Autonomous FLASH update */ #define E1000_EECD_SHADV 0x00200000 /* Shadow RAM Data Valid */ #define E1000_EECD_SEC1VAL 0x00400000 /* Sector One Valid */ + + #define E1000_EECD_SECVAL_SHIFT 22 #define E1000_STM_OPCODE 0xDB00 #define E1000_HICR_FW_RESET 0xC0 @@ -684,6 +907,18 @@ #define E1000_MDIC_INT_EN 0x20000000 #define E1000_MDIC_ERROR 0x40000000 +/* Rx Interrupt Delay Timer */ +#define E1000_RDTR_FPD BIT(31) + +/* Tx Interrupt Delay Timer */ +#define E1000_TIDV_FPD BIT(31) + +/* Delay increments in nanoseconds for delayed interrupts registers */ +#define E1000_INTR_DELAY_NS_RES (1024) + +/* Delay increments in nanoseconds for interrupt throttling registers */ +#define E1000_INTR_THROTTLING_NS_RES (256) + /* EEPROM Commands - Microwire */ #define EEPROM_READ_OPCODE_MICROWIRE 0x6 /* EEPROM read opcode */ #define EEPROM_WRITE_OPCODE_MICROWIRE 0x5 /* EEPROM write opcode */ @@ -711,6 +946,21 @@ #define E1000_EEPROM_CFG_DONE 0x00040000 /* MNG config cycle done */ #define E1000_EEPROM_CFG_DONE_PORT_1 0x00080000 /* ...for second port */ +/* PCI Express Control */ +/* 3GIO Control Register - GCR (0x05B00; RW) */ +#define E1000_L0S_ADJUST (1 << 9) +#define E1000_L1_ENTRY_LATENCY_MSB (1 << 23) +#define E1000_L1_ENTRY_LATENCY_LSB (1 << 25 | 1 << 26) + +#define E1000_L0S_ADJUST (1 << 9) +#define E1000_L1_ENTRY_LATENCY_MSB (1 << 23) +#define E1000_L1_ENTRY_LATENCY_LSB (1 << 25 | 1 << 26) + +#define E1000_GCR_RO_BITS (1 << 23 | 1 << 25 | 1 << 26) + +/* MSI-X PBA Clear register */ +#define E1000_PBACLR_VALID_MASK (BIT(5) - 1) + /* Transmit Descriptor */ struct e1000_tx_desc { uint64_t buffer_addr; /* Address of the descriptor's data buffer */ @@ -752,7 +1002,9 @@ struct e1000_tx_desc { #define E1000_TXD_CMD_TCP 0x01000000 /* TCP packet */ #define E1000_TXD_CMD_IP 0x02000000 /* IP packet */ #define E1000_TXD_CMD_TSE 0x04000000 /* TCP Seg enable */ +#define E1000_TXD_CMD_SNAP 0x40000000 /* Update SNAP header */ #define E1000_TXD_STAT_TC 0x00000004 /* Tx Underrun */ +#define E1000_TXD_EXTCMD_TSTAMP 0x00000010 /* IEEE1588 Timestamp packet */ /* Transmit Control */ #define E1000_TCTL_RST 0x00000001 /* software reset */ @@ -767,7 +1019,7 @@ struct e1000_tx_desc { #define E1000_TCTL_NRTU 0x02000000 /* No Re-transmit on underrun */ #define E1000_TCTL_MULR 0x10000000 /* Multiple request support */ -/* Receive Descriptor */ +/* Legacy Receive Descriptor */ struct e1000_rx_desc { uint64_t buffer_addr; /* Address of the descriptor's data buffer */ uint16_t length; /* Length of data DMAed into data buffer */ @@ -777,6 +1029,78 @@ struct e1000_rx_desc { uint16_t special; }; +/* Extended Receive Descriptor */ +union e1000_rx_desc_extended { + struct { + uint64_t buffer_addr; + uint64_t reserved; + } read; + struct { + struct { + uint32_t mrq; /* Multiple Rx Queues */ + union { + uint32_t rss; /* RSS Hash */ + struct { + uint16_t ip_id; /* IP id */ + uint16_t csum; /* Packet Checksum */ + } csum_ip; + } hi_dword; + } lower; + struct { + uint32_t status_error; /* ext status/error */ + uint16_t length; + uint16_t vlan; /* VLAN tag */ + } upper; + } wb; /* writeback */ +}; + +#define MAX_PS_BUFFERS 4 + +/* Number of packet split data buffers (not including the header buffer) */ +#define PS_PAGE_BUFFERS (MAX_PS_BUFFERS - 1) + +/* Receive Descriptor - Packet Split */ +union e1000_rx_desc_packet_split { + struct { + /* one buffer for protocol header(s), three data buffers */ + uint64_t buffer_addr[MAX_PS_BUFFERS]; + } read; + struct { + struct { + uint32_t mrq; /* Multiple Rx Queues */ + union { + uint32_t rss; /* RSS Hash */ + struct { + uint16_t ip_id; /* IP id */ + uint16_t csum; /* Packet Checksum */ + } csum_ip; + } hi_dword; + } lower; + struct { + uint32_t status_error; /* ext status/error */ + uint16_t length0; /* length of buffer 0 */ + uint16_t vlan; /* VLAN tag */ + } middle; + struct { + uint16_t header_status; + /* length of buffers 1-3 */ + uint16_t length[PS_PAGE_BUFFERS]; + } upper; + uint64_t reserved; + } wb; /* writeback */ +}; + +/* Receive Checksum Control bits */ +#define E1000_RXCSUM_IPOFLD 0x100 /* IP Checksum Offload Enable */ +#define E1000_RXCSUM_TUOFLD 0x200 /* TCP/UDP Checksum Offload Enable */ +#define E1000_RXCSUM_PCSD 0x2000 /* Packet Checksum Disable */ + +#define E1000_RING_DESC_LEN (16) +#define E1000_RING_DESC_LEN_SHIFT (4) + +#define E1000_MIN_RX_DESC_LEN E1000_RING_DESC_LEN +#define E1000_MAX_RX_DESC_LEN (sizeof(union e1000_rx_desc_packet_split)) + /* Receive Descriptor bit definitions */ #define E1000_RXD_STAT_DD 0x01 /* Descriptor Done */ #define E1000_RXD_STAT_EOP 0x02 /* End of Packet */ @@ -802,6 +1126,15 @@ struct e1000_rx_desc { #define E1000_RXD_SPC_CFI_MASK 0x1000 /* CFI is bit 12 */ #define E1000_RXD_SPC_CFI_SHIFT 12 +/* RX packet types */ +#define E1000_RXD_PKT_MAC (0) +#define E1000_RXD_PKT_IP4 (1) +#define E1000_RXD_PKT_IP4_XDP (2) +#define E1000_RXD_PKT_IP6 (5) +#define E1000_RXD_PKT_IP6_XDP (6) + +#define E1000_RXD_PKT_TYPE(t) ((t) << 16) + #define E1000_RXDEXT_STATERR_CE 0x01000000 #define E1000_RXDEXT_STATERR_SE 0x02000000 #define E1000_RXDEXT_STATERR_SEQ 0x04000000 @@ -879,6 +1212,8 @@ struct e1000_data_desc { #define E1000_MANC_NEIGHBOR_EN 0x00004000 /* Enable Neighbor Discovery * Filtering */ #define E1000_MANC_ARP_RES_EN 0x00008000 /* Enable ARP response Filtering */ +#define E1000_MANC_DIS_IP_CHK_ARP 0x10000000 /* Disable IP address chacking */ + /*for ARP packets - in 82574 */ #define E1000_MANC_TCO_RESET 0x00010000 /* TCO Reset Occurred */ #define E1000_MANC_RCV_TCO_EN 0x00020000 /* Receive TCO Packets Enabled */ #define E1000_MANC_REPORT_STATUS 0x00040000 /* Status Reporting Enabled */ @@ -902,7 +1237,14 @@ struct e1000_data_desc { #define E1000_MANC_SMB_DATA_OUT_SHIFT 28 /* SMBus Data Out Shift */ #define E1000_MANC_SMB_CLK_OUT_SHIFT 29 /* SMBus Clock Out Shift */ +/* FACTPS Control */ +#define E1000_FACTPS_LAN0_ON 0x00000004 /* Lan 0 enable */ + /* For checksumming, the sum of all words in the EEPROM should equal 0xBABA. */ #define EEPROM_SUM 0xBABA -#endif /* _E1000_HW_H_ */ +/* I/O-Mapped Access to Internal Registers, Memories, and Flash */ +#define E1000_IOADDR 0x00 +#define E1000_IODATA 0x04 + +#endif /* HW_E1000_REGS_H */ diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c new file mode 100644 index 000000000..bad43f474 --- /dev/null +++ b/hw/net/e1000e.c @@ -0,0 +1,711 @@ +/* +* QEMU INTEL 82574 GbE NIC emulation +* +* Software developer's manuals: +* http://www.intel.com/content/dam/doc/datasheet/82574l-gbe-controller-datasheet.pdf +* +* Copyright (c) 2015 Ravello Systems LTD (http://ravellosystems.com) +* Developed by Daynix Computing LTD (http://www.daynix.com) +* +* Authors: +* Dmitry Fleytman <dmitry@daynix.com> +* Leonid Bloch <leonid@daynix.com> +* Yan Vugenfirer <yan@daynix.com> +* +* Based on work done by: +* Nir Peleg, Tutis Systems Ltd. for Qumranet Inc. +* Copyright (c) 2008 Qumranet +* Based on work done by: +* Copyright (c) 2007 Dan Aloni +* Copyright (c) 2004 Antony T Curtis +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ + +#include "qemu/osdep.h" +#include "net/net.h" +#include "net/tap.h" +#include "qemu/range.h" +#include "sysemu/sysemu.h" +#include "hw/pci/msi.h" +#include "hw/pci/msix.h" + +#include "hw/net/e1000_regs.h" + +#include "e1000x_common.h" +#include "e1000e_core.h" + +#include "trace.h" + +#define TYPE_E1000E "e1000e" +#define E1000E(obj) OBJECT_CHECK(E1000EState, (obj), TYPE_E1000E) + +typedef struct E1000EState { + PCIDevice parent_obj; + NICState *nic; + NICConf conf; + + MemoryRegion mmio; + MemoryRegion flash; + MemoryRegion io; + MemoryRegion msix; + + uint32_t ioaddr; + + uint16_t subsys_ven; + uint16_t subsys; + + uint16_t subsys_ven_used; + uint16_t subsys_used; + + bool disable_vnet; + + E1000ECore core; + +} E1000EState; + +#define E1000E_MMIO_IDX 0 +#define E1000E_FLASH_IDX 1 +#define E1000E_IO_IDX 2 +#define E1000E_MSIX_IDX 3 + +#define E1000E_MMIO_SIZE (128 * 1024) +#define E1000E_FLASH_SIZE (128 * 1024) +#define E1000E_IO_SIZE (32) +#define E1000E_MSIX_SIZE (16 * 1024) + +#define E1000E_MSIX_TABLE (0x0000) +#define E1000E_MSIX_PBA (0x2000) + +static uint64_t +e1000e_mmio_read(void *opaque, hwaddr addr, unsigned size) +{ + E1000EState *s = opaque; + return e1000e_core_read(&s->core, addr, size); +} + +static void +e1000e_mmio_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + E1000EState *s = opaque; + e1000e_core_write(&s->core, addr, val, size); +} + +static bool +e1000e_io_get_reg_index(E1000EState *s, uint32_t *idx) +{ + if (s->ioaddr < 0x1FFFF) { + *idx = s->ioaddr; + return true; + } + + if (s->ioaddr < 0x7FFFF) { + trace_e1000e_wrn_io_addr_undefined(s->ioaddr); + return false; + } + + if (s->ioaddr < 0xFFFFF) { + trace_e1000e_wrn_io_addr_flash(s->ioaddr); + return false; + } + + trace_e1000e_wrn_io_addr_unknown(s->ioaddr); + return false; +} + +static uint64_t +e1000e_io_read(void *opaque, hwaddr addr, unsigned size) +{ + E1000EState *s = opaque; + uint32_t idx = 0; + uint64_t val; + + switch (addr) { + case E1000_IOADDR: + trace_e1000e_io_read_addr(s->ioaddr); + return s->ioaddr; + case E1000_IODATA: + if (e1000e_io_get_reg_index(s, &idx)) { + val = e1000e_core_read(&s->core, idx, sizeof(val)); + trace_e1000e_io_read_data(idx, val); + return val; + } + return 0; + default: + trace_e1000e_wrn_io_read_unknown(addr); + return 0; + } +} + +static void +e1000e_io_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + E1000EState *s = opaque; + uint32_t idx = 0; + + switch (addr) { + case E1000_IOADDR: + trace_e1000e_io_write_addr(val); + s->ioaddr = (uint32_t) val; + return; + case E1000_IODATA: + if (e1000e_io_get_reg_index(s, &idx)) { + trace_e1000e_io_write_data(idx, val); + e1000e_core_write(&s->core, idx, val, sizeof(val)); + } + return; + default: + trace_e1000e_wrn_io_write_unknown(addr); + return; + } +} + +static const MemoryRegionOps mmio_ops = { + .read = e1000e_mmio_read, + .write = e1000e_mmio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static const MemoryRegionOps io_ops = { + .read = e1000e_io_read, + .write = e1000e_io_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static int +e1000e_nc_can_receive(NetClientState *nc) +{ + E1000EState *s = qemu_get_nic_opaque(nc); + return e1000e_can_receive(&s->core); +} + +static ssize_t +e1000e_nc_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) +{ + E1000EState *s = qemu_get_nic_opaque(nc); + return e1000e_receive_iov(&s->core, iov, iovcnt); +} + +static ssize_t +e1000e_nc_receive(NetClientState *nc, const uint8_t *buf, size_t size) +{ + E1000EState *s = qemu_get_nic_opaque(nc); + return e1000e_receive(&s->core, buf, size); +} + +static void +e1000e_set_link_status(NetClientState *nc) +{ + E1000EState *s = qemu_get_nic_opaque(nc); + e1000e_core_set_link_status(&s->core); +} + +static NetClientInfo net_e1000e_info = { + .type = NET_CLIENT_DRIVER_NIC, + .size = sizeof(NICState), + .can_receive = e1000e_nc_can_receive, + .receive = e1000e_nc_receive, + .receive_iov = e1000e_nc_receive_iov, + .link_status_changed = e1000e_set_link_status, +}; + +/* +* EEPROM (NVM) contents documented in Table 36, section 6.1 +* and generally 6.1.2 Software accessed words. +*/ +static const uint16_t e1000e_eeprom_template[64] = { + /* Address | Compat. | ImVer | Compat. */ + 0x0000, 0x0000, 0x0000, 0x0420, 0xf746, 0x2010, 0xffff, 0xffff, + /* PBA |ICtrl1 | SSID | SVID | DevID |-------|ICtrl2 */ + 0x0000, 0x0000, 0x026b, 0x0000, 0x8086, 0x0000, 0x0000, 0x8058, + /* NVM words 1,2,3 |-------------------------------|PCI-EID*/ + 0x0000, 0x2001, 0x7e7c, 0xffff, 0x1000, 0x00c8, 0x0000, 0x2704, + /* PCIe Init. Conf 1,2,3 |PCICtrl|PHY|LD1|-------| RevID | LD0,2 */ + 0x6cc9, 0x3150, 0x070e, 0x460b, 0x2d84, 0x0100, 0xf000, 0x0706, + /* FLPAR |FLANADD|LAN-PWR|FlVndr |ICtrl3 |APTSMBA|APTRxEP|APTSMBC*/ + 0x6000, 0x0080, 0x0f04, 0x7fff, 0x4f01, 0xc600, 0x0000, 0x20ff, + /* APTIF | APTMC |APTuCP |LSWFWID|MSWFWID|NC-SIMC|NC-SIC | VPDP */ + 0x0028, 0x0003, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0xffff, + /* SW Section */ + 0x0100, 0xc000, 0x121c, 0xc007, 0xffff, 0xffff, 0xffff, 0xffff, + /* SW Section |CHKSUM */ + 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0x0120, 0xffff, 0x0000, +}; + +static void e1000e_core_realize(E1000EState *s) +{ + s->core.owner = &s->parent_obj; + s->core.owner_nic = s->nic; +} + +static void +e1000e_unuse_msix_vectors(E1000EState *s, int num_vectors) +{ + int i; + for (i = 0; i < num_vectors; i++) { + msix_vector_unuse(PCI_DEVICE(s), i); + } +} + +static bool +e1000e_use_msix_vectors(E1000EState *s, int num_vectors) +{ + int i; + for (i = 0; i < num_vectors; i++) { + int res = msix_vector_use(PCI_DEVICE(s), i); + if (res < 0) { + trace_e1000e_msix_use_vector_fail(i, res); + e1000e_unuse_msix_vectors(s, i); + return false; + } + } + return true; +} + +static void +e1000e_init_msix(E1000EState *s) +{ + PCIDevice *d = PCI_DEVICE(s); + int res = msix_init(PCI_DEVICE(s), E1000E_MSIX_VEC_NUM, + &s->msix, + E1000E_MSIX_IDX, E1000E_MSIX_TABLE, + &s->msix, + E1000E_MSIX_IDX, E1000E_MSIX_PBA, + 0xA0); + + if (res < 0) { + trace_e1000e_msix_init_fail(res); + } else { + if (!e1000e_use_msix_vectors(s, E1000E_MSIX_VEC_NUM)) { + msix_uninit(d, &s->msix, &s->msix); + } + } +} + +static void +e1000e_cleanup_msix(E1000EState *s) +{ + if (msix_enabled(PCI_DEVICE(s))) { + e1000e_unuse_msix_vectors(s, E1000E_MSIX_VEC_NUM); + msix_uninit(PCI_DEVICE(s), &s->msix, &s->msix); + } +} + +static void +e1000e_init_net_peer(E1000EState *s, PCIDevice *pci_dev, uint8_t *macaddr) +{ + DeviceState *dev = DEVICE(pci_dev); + NetClientState *nc; + int i; + + s->nic = qemu_new_nic(&net_e1000e_info, &s->conf, + object_get_typename(OBJECT(s)), dev->id, s); + + s->core.max_queue_num = s->conf.peers.queues - 1; + + trace_e1000e_mac_set_permanent(MAC_ARG(macaddr)); + memcpy(s->core.permanent_mac, macaddr, sizeof(s->core.permanent_mac)); + + qemu_format_nic_info_str(qemu_get_queue(s->nic), macaddr); + + /* Setup virtio headers */ + if (s->disable_vnet) { + s->core.has_vnet = false; + trace_e1000e_cfg_support_virtio(false); + return; + } else { + s->core.has_vnet = true; + } + + for (i = 0; i < s->conf.peers.queues; i++) { + nc = qemu_get_subqueue(s->nic, i); + if (!nc->peer || !qemu_has_vnet_hdr(nc->peer)) { + s->core.has_vnet = false; + trace_e1000e_cfg_support_virtio(false); + return; + } + } + + trace_e1000e_cfg_support_virtio(true); + + for (i = 0; i < s->conf.peers.queues; i++) { + nc = qemu_get_subqueue(s->nic, i); + qemu_set_vnet_hdr_len(nc->peer, sizeof(struct virtio_net_hdr)); + qemu_using_vnet_hdr(nc->peer, true); + } +} + +static inline uint64_t +e1000e_gen_dsn(uint8_t *mac) +{ + return (uint64_t)(mac[5]) | + (uint64_t)(mac[4]) << 8 | + (uint64_t)(mac[3]) << 16 | + (uint64_t)(0x00FF) << 24 | + (uint64_t)(0x00FF) << 32 | + (uint64_t)(mac[2]) << 40 | + (uint64_t)(mac[1]) << 48 | + (uint64_t)(mac[0]) << 56; +} + +static int +e1000e_add_pm_capability(PCIDevice *pdev, uint8_t offset, uint16_t pmc) +{ + int ret = pci_add_capability(pdev, PCI_CAP_ID_PM, offset, PCI_PM_SIZEOF); + + if (ret >= 0) { + pci_set_word(pdev->config + offset + PCI_PM_PMC, + PCI_PM_CAP_VER_1_1 | + pmc); + + pci_set_word(pdev->wmask + offset + PCI_PM_CTRL, + PCI_PM_CTRL_STATE_MASK | + PCI_PM_CTRL_PME_ENABLE | + PCI_PM_CTRL_DATA_SEL_MASK); + + pci_set_word(pdev->w1cmask + offset + PCI_PM_CTRL, + PCI_PM_CTRL_PME_STATUS); + } + + return ret; +} + +static void e1000e_write_config(PCIDevice *pci_dev, uint32_t address, + uint32_t val, int len) +{ + E1000EState *s = E1000E(pci_dev); + + pci_default_write_config(pci_dev, address, val, len); + + if (range_covers_byte(address, len, PCI_COMMAND) && + (pci_dev->config[PCI_COMMAND] & PCI_COMMAND_MASTER)) { + qemu_flush_queued_packets(qemu_get_queue(s->nic)); + } +} + +static void e1000e_pci_realize(PCIDevice *pci_dev, Error **errp) +{ + static const uint16_t e1000e_pmrb_offset = 0x0C8; + static const uint16_t e1000e_pcie_offset = 0x0E0; + static const uint16_t e1000e_aer_offset = 0x100; + static const uint16_t e1000e_dsn_offset = 0x140; + E1000EState *s = E1000E(pci_dev); + uint8_t *macaddr; + int ret; + + trace_e1000e_cb_pci_realize(); + + pci_dev->config_write = e1000e_write_config; + + pci_dev->config[PCI_CACHE_LINE_SIZE] = 0x10; + pci_dev->config[PCI_INTERRUPT_PIN] = 1; + + pci_set_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID, s->subsys_ven); + pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID, s->subsys); + + s->subsys_ven_used = s->subsys_ven; + s->subsys_used = s->subsys; + + /* Define IO/MMIO regions */ + memory_region_init_io(&s->mmio, OBJECT(s), &mmio_ops, s, + "e1000e-mmio", E1000E_MMIO_SIZE); + pci_register_bar(pci_dev, E1000E_MMIO_IDX, + PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio); + + /* + * We provide a dummy implementation for the flash BAR + * for drivers that may theoretically probe for its presence. + */ + memory_region_init(&s->flash, OBJECT(s), + "e1000e-flash", E1000E_FLASH_SIZE); + pci_register_bar(pci_dev, E1000E_FLASH_IDX, + PCI_BASE_ADDRESS_SPACE_MEMORY, &s->flash); + + memory_region_init_io(&s->io, OBJECT(s), &io_ops, s, + "e1000e-io", E1000E_IO_SIZE); + pci_register_bar(pci_dev, E1000E_IO_IDX, + PCI_BASE_ADDRESS_SPACE_IO, &s->io); + + memory_region_init(&s->msix, OBJECT(s), "e1000e-msix", + E1000E_MSIX_SIZE); + pci_register_bar(pci_dev, E1000E_MSIX_IDX, + PCI_BASE_ADDRESS_SPACE_MEMORY, &s->msix); + + /* Create networking backend */ + qemu_macaddr_default_if_unset(&s->conf.macaddr); + macaddr = s->conf.macaddr.a; + + e1000e_init_msix(s); + + if (pcie_endpoint_cap_v1_init(pci_dev, e1000e_pcie_offset) < 0) { + hw_error("Failed to initialize PCIe capability"); + } + + ret = msi_init(PCI_DEVICE(s), 0xD0, 1, true, false, NULL); + if (ret) { + trace_e1000e_msi_init_fail(ret); + } + + if (e1000e_add_pm_capability(pci_dev, e1000e_pmrb_offset, + PCI_PM_CAP_DSI) < 0) { + hw_error("Failed to initialize PM capability"); + } + + if (pcie_aer_init(pci_dev, e1000e_aer_offset, PCI_ERR_SIZEOF) < 0) { + hw_error("Failed to initialize AER capability"); + } + + pcie_dev_ser_num_init(pci_dev, e1000e_dsn_offset, + e1000e_gen_dsn(macaddr)); + + e1000e_init_net_peer(s, pci_dev, macaddr); + + /* Initialize core */ + e1000e_core_realize(s); + + e1000e_core_pci_realize(&s->core, + e1000e_eeprom_template, + sizeof(e1000e_eeprom_template), + macaddr); +} + +static void e1000e_pci_uninit(PCIDevice *pci_dev) +{ + E1000EState *s = E1000E(pci_dev); + + trace_e1000e_cb_pci_uninit(); + + e1000e_core_pci_uninit(&s->core); + + pcie_aer_exit(pci_dev); + pcie_cap_exit(pci_dev); + + qemu_del_nic(s->nic); + + e1000e_cleanup_msix(s); + msi_uninit(pci_dev); +} + +static void e1000e_qdev_reset(DeviceState *dev) +{ + E1000EState *s = E1000E(dev); + + trace_e1000e_cb_qdev_reset(); + + e1000e_core_reset(&s->core); +} + +static void e1000e_pre_save(void *opaque) +{ + E1000EState *s = opaque; + + trace_e1000e_cb_pre_save(); + + e1000e_core_pre_save(&s->core); +} + +static int e1000e_post_load(void *opaque, int version_id) +{ + E1000EState *s = opaque; + + trace_e1000e_cb_post_load(); + + if ((s->subsys != s->subsys_used) || + (s->subsys_ven != s->subsys_ven_used)) { + fprintf(stderr, + "ERROR: Cannot migrate while device properties " + "(subsys/subsys_ven) differ"); + return -1; + } + + return e1000e_core_post_load(&s->core); +} + +static const VMStateDescription e1000e_vmstate_tx = { + .name = "e1000e-tx", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(props.sum_needed, struct e1000e_tx), + VMSTATE_UINT8(props.ipcss, struct e1000e_tx), + VMSTATE_UINT8(props.ipcso, struct e1000e_tx), + VMSTATE_UINT16(props.ipcse, struct e1000e_tx), + VMSTATE_UINT8(props.tucss, struct e1000e_tx), + VMSTATE_UINT8(props.tucso, struct e1000e_tx), + VMSTATE_UINT16(props.tucse, struct e1000e_tx), + VMSTATE_UINT8(props.hdr_len, struct e1000e_tx), + VMSTATE_UINT16(props.mss, struct e1000e_tx), + VMSTATE_UINT32(props.paylen, struct e1000e_tx), + VMSTATE_INT8(props.ip, struct e1000e_tx), + VMSTATE_INT8(props.tcp, struct e1000e_tx), + VMSTATE_BOOL(props.tse, struct e1000e_tx), + VMSTATE_BOOL(props.cptse, struct e1000e_tx), + VMSTATE_BOOL(skip_cp, struct e1000e_tx), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription e1000e_vmstate_intr_timer = { + .name = "e1000e-intr-timer", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_TIMER_PTR(timer, E1000IntrDelayTimer), + VMSTATE_BOOL(running, E1000IntrDelayTimer), + VMSTATE_END_OF_LIST() + } +}; + +#define VMSTATE_E1000E_INTR_DELAY_TIMER(_f, _s) \ + VMSTATE_STRUCT(_f, _s, 0, \ + e1000e_vmstate_intr_timer, E1000IntrDelayTimer) + +#define VMSTATE_E1000E_INTR_DELAY_TIMER_ARRAY(_f, _s, _num) \ + VMSTATE_STRUCT_ARRAY(_f, _s, _num, 0, \ + e1000e_vmstate_intr_timer, E1000IntrDelayTimer) + +static const VMStateDescription e1000e_vmstate = { + .name = "e1000e", + .version_id = 1, + .minimum_version_id = 1, + .pre_save = e1000e_pre_save, + .post_load = e1000e_post_load, + .fields = (VMStateField[]) { + VMSTATE_PCIE_DEVICE(parent_obj, E1000EState), + VMSTATE_MSIX(parent_obj, E1000EState), + + VMSTATE_UINT32(ioaddr, E1000EState), + VMSTATE_UINT32(core.rxbuf_min_shift, E1000EState), + VMSTATE_UINT8(core.rx_desc_len, E1000EState), + VMSTATE_UINT32_ARRAY(core.rxbuf_sizes, E1000EState, + E1000_PSRCTL_BUFFS_PER_DESC), + VMSTATE_UINT32(core.rx_desc_buf_size, E1000EState), + VMSTATE_UINT16_ARRAY(core.eeprom, E1000EState, E1000E_EEPROM_SIZE), + VMSTATE_UINT16_2DARRAY(core.phy, E1000EState, + E1000E_PHY_PAGES, E1000E_PHY_PAGE_SIZE), + VMSTATE_UINT32_ARRAY(core.mac, E1000EState, E1000E_MAC_SIZE), + VMSTATE_UINT8_ARRAY(core.permanent_mac, E1000EState, ETH_ALEN), + + VMSTATE_UINT32(core.delayed_causes, E1000EState), + + VMSTATE_UINT16(subsys, E1000EState), + VMSTATE_UINT16(subsys_ven, E1000EState), + + VMSTATE_E1000E_INTR_DELAY_TIMER(core.rdtr, E1000EState), + VMSTATE_E1000E_INTR_DELAY_TIMER(core.radv, E1000EState), + VMSTATE_E1000E_INTR_DELAY_TIMER(core.raid, E1000EState), + VMSTATE_E1000E_INTR_DELAY_TIMER(core.tadv, E1000EState), + VMSTATE_E1000E_INTR_DELAY_TIMER(core.tidv, E1000EState), + + VMSTATE_E1000E_INTR_DELAY_TIMER(core.itr, E1000EState), + VMSTATE_BOOL(core.itr_intr_pending, E1000EState), + + VMSTATE_E1000E_INTR_DELAY_TIMER_ARRAY(core.eitr, E1000EState, + E1000E_MSIX_VEC_NUM), + VMSTATE_BOOL_ARRAY(core.eitr_intr_pending, E1000EState, + E1000E_MSIX_VEC_NUM), + + VMSTATE_UINT32(core.itr_guest_value, E1000EState), + VMSTATE_UINT32_ARRAY(core.eitr_guest_value, E1000EState, + E1000E_MSIX_VEC_NUM), + + VMSTATE_UINT16(core.vet, E1000EState), + + VMSTATE_STRUCT_ARRAY(core.tx, E1000EState, E1000E_NUM_QUEUES, 0, + e1000e_vmstate_tx, struct e1000e_tx), + VMSTATE_END_OF_LIST() + } +}; + +static PropertyInfo e1000e_prop_disable_vnet, + e1000e_prop_subsys_ven, + e1000e_prop_subsys; + +static Property e1000e_properties[] = { + DEFINE_NIC_PROPERTIES(E1000EState, conf), + DEFINE_PROP_DEFAULT("disable_vnet_hdr", E1000EState, disable_vnet, false, + e1000e_prop_disable_vnet, bool), + DEFINE_PROP_DEFAULT("subsys_ven", E1000EState, subsys_ven, + PCI_VENDOR_ID_INTEL, + e1000e_prop_subsys_ven, uint16_t), + DEFINE_PROP_DEFAULT("subsys", E1000EState, subsys, 0, + e1000e_prop_subsys, uint16_t), + DEFINE_PROP_END_OF_LIST(), +}; + +static void e1000e_class_init(ObjectClass *class, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(class); + PCIDeviceClass *c = PCI_DEVICE_CLASS(class); + + c->realize = e1000e_pci_realize; + c->exit = e1000e_pci_uninit; + c->vendor_id = PCI_VENDOR_ID_INTEL; + c->device_id = E1000_DEV_ID_82574L; + c->revision = 0; + c->romfile = "efi-e1000e.rom"; + c->class_id = PCI_CLASS_NETWORK_ETHERNET; + c->is_express = 1; + + dc->desc = "Intel 82574L GbE Controller"; + dc->reset = e1000e_qdev_reset; + dc->vmsd = &e1000e_vmstate; + dc->props = e1000e_properties; + + e1000e_prop_disable_vnet = qdev_prop_uint8; + e1000e_prop_disable_vnet.description = "Do not use virtio headers, " + "perform SW offloads emulation " + "instead"; + + e1000e_prop_subsys_ven = qdev_prop_uint16; + e1000e_prop_subsys_ven.description = "PCI device Subsystem Vendor ID"; + + e1000e_prop_subsys = qdev_prop_uint16; + e1000e_prop_subsys.description = "PCI device Subsystem ID"; + + set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); +} + +static void e1000e_instance_init(Object *obj) +{ + E1000EState *s = E1000E(obj); + device_add_bootindex_property(obj, &s->conf.bootindex, + "bootindex", "/ethernet-phy@0", + DEVICE(obj), NULL); +} + +static const TypeInfo e1000e_info = { + .name = TYPE_E1000E, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(E1000EState), + .class_init = e1000e_class_init, + .instance_init = e1000e_instance_init, +}; + +static void e1000e_register_types(void) +{ + type_register_static(&e1000e_info); +} + +type_init(e1000e_register_types) diff --git a/hw/net/e1000e_core.c b/hw/net/e1000e_core.c new file mode 100644 index 000000000..badb1feb7 --- /dev/null +++ b/hw/net/e1000e_core.c @@ -0,0 +1,3483 @@ +/* +* Core code for QEMU e1000e emulation +* +* Software developer's manuals: +* http://www.intel.com/content/dam/doc/datasheet/82574l-gbe-controller-datasheet.pdf +* +* Copyright (c) 2015 Ravello Systems LTD (http://ravellosystems.com) +* Developed by Daynix Computing LTD (http://www.daynix.com) +* +* Authors: +* Dmitry Fleytman <dmitry@daynix.com> +* Leonid Bloch <leonid@daynix.com> +* Yan Vugenfirer <yan@daynix.com> +* +* Based on work done by: +* Nir Peleg, Tutis Systems Ltd. for Qumranet Inc. +* Copyright (c) 2008 Qumranet +* Based on work done by: +* Copyright (c) 2007 Dan Aloni +* Copyright (c) 2004 Antony T Curtis +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ + +#include "qemu/osdep.h" +#include "sysemu/sysemu.h" +#include "net/net.h" +#include "net/tap.h" +#include "hw/pci/msi.h" +#include "hw/pci/msix.h" + +#include "net_tx_pkt.h" +#include "net_rx_pkt.h" + +#include "e1000x_common.h" +#include "e1000e_core.h" + +#include "trace.h" + +#define E1000E_MIN_XITR (500) /* No more then 7813 interrupts per + second according to spec 10.2.4.2 */ +#define E1000E_MAX_TX_FRAGS (64) + +static void +e1000e_set_interrupt_cause(E1000ECore *core, uint32_t val); + +static inline void +e1000e_process_ts_option(E1000ECore *core, struct e1000_tx_desc *dp) +{ + if (le32_to_cpu(dp->upper.data) & E1000_TXD_EXTCMD_TSTAMP) { + trace_e1000e_wrn_no_ts_support(); + } +} + +static inline void +e1000e_process_snap_option(E1000ECore *core, uint32_t cmd_and_length) +{ + if (cmd_and_length & E1000_TXD_CMD_SNAP) { + trace_e1000e_wrn_no_snap_support(); + } +} + +static inline void +e1000e_raise_legacy_irq(E1000ECore *core) +{ + trace_e1000e_irq_legacy_notify(true); + e1000x_inc_reg_if_not_full(core->mac, IAC); + pci_set_irq(core->owner, 1); +} + +static inline void +e1000e_lower_legacy_irq(E1000ECore *core) +{ + trace_e1000e_irq_legacy_notify(false); + pci_set_irq(core->owner, 0); +} + +static inline void +e1000e_intrmgr_rearm_timer(E1000IntrDelayTimer *timer) +{ + int64_t delay_ns = (int64_t) timer->core->mac[timer->delay_reg] * + timer->delay_resolution_ns; + + trace_e1000e_irq_rearm_timer(timer->delay_reg << 2, delay_ns); + + timer_mod(timer->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + delay_ns); + + timer->running = true; +} + +static void +e1000e_intmgr_timer_resume(E1000IntrDelayTimer *timer) +{ + if (timer->running) { + e1000e_intrmgr_rearm_timer(timer); + } +} + +static void +e1000e_intmgr_timer_pause(E1000IntrDelayTimer *timer) +{ + if (timer->running) { + timer_del(timer->timer); + } +} + +static inline void +e1000e_intrmgr_stop_timer(E1000IntrDelayTimer *timer) +{ + if (timer->running) { + timer_del(timer->timer); + timer->running = false; + } +} + +static inline void +e1000e_intrmgr_fire_delayed_interrupts(E1000ECore *core) +{ + trace_e1000e_irq_fire_delayed_interrupts(); + e1000e_set_interrupt_cause(core, 0); +} + +static void +e1000e_intrmgr_on_timer(void *opaque) +{ + E1000IntrDelayTimer *timer = opaque; + + trace_e1000e_irq_throttling_timer(timer->delay_reg << 2); + + timer->running = false; + e1000e_intrmgr_fire_delayed_interrupts(timer->core); +} + +static void +e1000e_intrmgr_on_throttling_timer(void *opaque) +{ + E1000IntrDelayTimer *timer = opaque; + + assert(!msix_enabled(timer->core->owner)); + + timer->running = false; + + if (!timer->core->itr_intr_pending) { + trace_e1000e_irq_throttling_no_pending_interrupts(); + return; + } + + if (msi_enabled(timer->core->owner)) { + trace_e1000e_irq_msi_notify_postponed(); + e1000e_set_interrupt_cause(timer->core, 0); + } else { + trace_e1000e_irq_legacy_notify_postponed(); + e1000e_set_interrupt_cause(timer->core, 0); + } +} + +static void +e1000e_intrmgr_on_msix_throttling_timer(void *opaque) +{ + E1000IntrDelayTimer *timer = opaque; + int idx = timer - &timer->core->eitr[0]; + + assert(msix_enabled(timer->core->owner)); + + timer->running = false; + + if (!timer->core->eitr_intr_pending[idx]) { + trace_e1000e_irq_throttling_no_pending_vec(idx); + return; + } + + trace_e1000e_irq_msix_notify_postponed_vec(idx); + msix_notify(timer->core->owner, idx); +} + +static void +e1000e_intrmgr_initialize_all_timers(E1000ECore *core, bool create) +{ + int i; + + core->radv.delay_reg = RADV; + core->rdtr.delay_reg = RDTR; + core->raid.delay_reg = RAID; + core->tadv.delay_reg = TADV; + core->tidv.delay_reg = TIDV; + + core->radv.delay_resolution_ns = E1000_INTR_DELAY_NS_RES; + core->rdtr.delay_resolution_ns = E1000_INTR_DELAY_NS_RES; + core->raid.delay_resolution_ns = E1000_INTR_DELAY_NS_RES; + core->tadv.delay_resolution_ns = E1000_INTR_DELAY_NS_RES; + core->tidv.delay_resolution_ns = E1000_INTR_DELAY_NS_RES; + + core->radv.core = core; + core->rdtr.core = core; + core->raid.core = core; + core->tadv.core = core; + core->tidv.core = core; + + core->itr.core = core; + core->itr.delay_reg = ITR; + core->itr.delay_resolution_ns = E1000_INTR_THROTTLING_NS_RES; + + for (i = 0; i < E1000E_MSIX_VEC_NUM; i++) { + core->eitr[i].core = core; + core->eitr[i].delay_reg = EITR + i; + core->eitr[i].delay_resolution_ns = E1000_INTR_THROTTLING_NS_RES; + } + + if (!create) { + return; + } + + core->radv.timer = + timer_new_ns(QEMU_CLOCK_VIRTUAL, e1000e_intrmgr_on_timer, &core->radv); + core->rdtr.timer = + timer_new_ns(QEMU_CLOCK_VIRTUAL, e1000e_intrmgr_on_timer, &core->rdtr); + core->raid.timer = + timer_new_ns(QEMU_CLOCK_VIRTUAL, e1000e_intrmgr_on_timer, &core->raid); + + core->tadv.timer = + timer_new_ns(QEMU_CLOCK_VIRTUAL, e1000e_intrmgr_on_timer, &core->tadv); + core->tidv.timer = + timer_new_ns(QEMU_CLOCK_VIRTUAL, e1000e_intrmgr_on_timer, &core->tidv); + + core->itr.timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + e1000e_intrmgr_on_throttling_timer, + &core->itr); + + for (i = 0; i < E1000E_MSIX_VEC_NUM; i++) { + core->eitr[i].timer = + timer_new_ns(QEMU_CLOCK_VIRTUAL, + e1000e_intrmgr_on_msix_throttling_timer, + &core->eitr[i]); + } +} + +static inline void +e1000e_intrmgr_stop_delay_timers(E1000ECore *core) +{ + e1000e_intrmgr_stop_timer(&core->radv); + e1000e_intrmgr_stop_timer(&core->rdtr); + e1000e_intrmgr_stop_timer(&core->raid); + e1000e_intrmgr_stop_timer(&core->tidv); + e1000e_intrmgr_stop_timer(&core->tadv); +} + +static bool +e1000e_intrmgr_delay_rx_causes(E1000ECore *core, uint32_t *causes) +{ + uint32_t delayable_causes; + uint32_t rdtr = core->mac[RDTR]; + uint32_t radv = core->mac[RADV]; + uint32_t raid = core->mac[RAID]; + + if (msix_enabled(core->owner)) { + return false; + } + + delayable_causes = E1000_ICR_RXQ0 | + E1000_ICR_RXQ1 | + E1000_ICR_RXT0; + + if (!(core->mac[RFCTL] & E1000_RFCTL_ACK_DIS)) { + delayable_causes |= E1000_ICR_ACK; + } + + /* Clean up all causes that may be delayed */ + core->delayed_causes |= *causes & delayable_causes; + *causes &= ~delayable_causes; + + /* Check if delayed RX interrupts disabled by client + or if there are causes that cannot be delayed */ + if ((rdtr == 0) || (*causes != 0)) { + return false; + } + + /* Check if delayed RX ACK interrupts disabled by client + and there is an ACK packet received */ + if ((raid == 0) && (core->delayed_causes & E1000_ICR_ACK)) { + return false; + } + + /* All causes delayed */ + e1000e_intrmgr_rearm_timer(&core->rdtr); + + if (!core->radv.running && (radv != 0)) { + e1000e_intrmgr_rearm_timer(&core->radv); + } + + if (!core->raid.running && (core->delayed_causes & E1000_ICR_ACK)) { + e1000e_intrmgr_rearm_timer(&core->raid); + } + + return true; +} + +static bool +e1000e_intrmgr_delay_tx_causes(E1000ECore *core, uint32_t *causes) +{ + static const uint32_t delayable_causes = E1000_ICR_TXQ0 | + E1000_ICR_TXQ1 | + E1000_ICR_TXQE | + E1000_ICR_TXDW; + + if (msix_enabled(core->owner)) { + return false; + } + + /* Clean up all causes that may be delayed */ + core->delayed_causes |= *causes & delayable_causes; + *causes &= ~delayable_causes; + + /* If there are causes that cannot be delayed */ + if (*causes != 0) { + return false; + } + + /* All causes delayed */ + e1000e_intrmgr_rearm_timer(&core->tidv); + + if (!core->tadv.running && (core->mac[TADV] != 0)) { + e1000e_intrmgr_rearm_timer(&core->tadv); + } + + return true; +} + +static uint32_t +e1000e_intmgr_collect_delayed_causes(E1000ECore *core) +{ + uint32_t res; + + if (msix_enabled(core->owner)) { + assert(core->delayed_causes == 0); + return 0; + } + + res = core->delayed_causes; + core->delayed_causes = 0; + + e1000e_intrmgr_stop_delay_timers(core); + + return res; +} + +static void +e1000e_intrmgr_fire_all_timers(E1000ECore *core) +{ + int i; + uint32_t val = e1000e_intmgr_collect_delayed_causes(core); + + trace_e1000e_irq_adding_delayed_causes(val, core->mac[ICR]); + core->mac[ICR] |= val; + + if (core->itr.running) { + timer_del(core->itr.timer); + e1000e_intrmgr_on_throttling_timer(&core->itr); + } + + for (i = 0; i < E1000E_MSIX_VEC_NUM; i++) { + if (core->eitr[i].running) { + timer_del(core->eitr[i].timer); + e1000e_intrmgr_on_msix_throttling_timer(&core->eitr[i]); + } + } +} + +static void +e1000e_intrmgr_resume(E1000ECore *core) +{ + int i; + + e1000e_intmgr_timer_resume(&core->radv); + e1000e_intmgr_timer_resume(&core->rdtr); + e1000e_intmgr_timer_resume(&core->raid); + e1000e_intmgr_timer_resume(&core->tidv); + e1000e_intmgr_timer_resume(&core->tadv); + + e1000e_intmgr_timer_resume(&core->itr); + + for (i = 0; i < E1000E_MSIX_VEC_NUM; i++) { + e1000e_intmgr_timer_resume(&core->eitr[i]); + } +} + +static void +e1000e_intrmgr_pause(E1000ECore *core) +{ + int i; + + e1000e_intmgr_timer_pause(&core->radv); + e1000e_intmgr_timer_pause(&core->rdtr); + e1000e_intmgr_timer_pause(&core->raid); + e1000e_intmgr_timer_pause(&core->tidv); + e1000e_intmgr_timer_pause(&core->tadv); + + e1000e_intmgr_timer_pause(&core->itr); + + for (i = 0; i < E1000E_MSIX_VEC_NUM; i++) { + e1000e_intmgr_timer_pause(&core->eitr[i]); + } +} + +static void +e1000e_intrmgr_reset(E1000ECore *core) +{ + int i; + + core->delayed_causes = 0; + + e1000e_intrmgr_stop_delay_timers(core); + + e1000e_intrmgr_stop_timer(&core->itr); + + for (i = 0; i < E1000E_MSIX_VEC_NUM; i++) { + e1000e_intrmgr_stop_timer(&core->eitr[i]); + } +} + +static void +e1000e_intrmgr_pci_unint(E1000ECore *core) +{ + int i; + + timer_del(core->radv.timer); + timer_free(core->radv.timer); + timer_del(core->rdtr.timer); + timer_free(core->rdtr.timer); + timer_del(core->raid.timer); + timer_free(core->raid.timer); + + timer_del(core->tadv.timer); + timer_free(core->tadv.timer); + timer_del(core->tidv.timer); + timer_free(core->tidv.timer); + + timer_del(core->itr.timer); + timer_free(core->itr.timer); + + for (i = 0; i < E1000E_MSIX_VEC_NUM; i++) { + timer_del(core->eitr[i].timer); + timer_free(core->eitr[i].timer); + } +} + +static void +e1000e_intrmgr_pci_realize(E1000ECore *core) +{ + e1000e_intrmgr_initialize_all_timers(core, true); +} + +static inline bool +e1000e_rx_csum_enabled(E1000ECore *core) +{ + return (core->mac[RXCSUM] & E1000_RXCSUM_PCSD) ? false : true; +} + +static inline bool +e1000e_rx_use_legacy_descriptor(E1000ECore *core) +{ + return (core->mac[RFCTL] & E1000_RFCTL_EXTEN) ? false : true; +} + +static inline bool +e1000e_rx_use_ps_descriptor(E1000ECore *core) +{ + return !e1000e_rx_use_legacy_descriptor(core) && + (core->mac[RCTL] & E1000_RCTL_DTYP_PS); +} + +static inline bool +e1000e_rss_enabled(E1000ECore *core) +{ + return E1000_MRQC_ENABLED(core->mac[MRQC]) && + !e1000e_rx_csum_enabled(core) && + !e1000e_rx_use_legacy_descriptor(core); +} + +typedef struct E1000E_RSSInfo_st { + bool enabled; + uint32_t hash; + uint32_t queue; + uint32_t type; +} E1000E_RSSInfo; + +static uint32_t +e1000e_rss_get_hash_type(E1000ECore *core, struct NetRxPkt *pkt) +{ + bool isip4, isip6, isudp, istcp; + + assert(e1000e_rss_enabled(core)); + + net_rx_pkt_get_protocols(pkt, &isip4, &isip6, &isudp, &istcp); + + if (isip4) { + bool fragment = net_rx_pkt_get_ip4_info(pkt)->fragment; + + trace_e1000e_rx_rss_ip4(fragment, istcp, core->mac[MRQC], + E1000_MRQC_EN_TCPIPV4(core->mac[MRQC]), + E1000_MRQC_EN_IPV4(core->mac[MRQC])); + + if (!fragment && istcp && E1000_MRQC_EN_TCPIPV4(core->mac[MRQC])) { + return E1000_MRQ_RSS_TYPE_IPV4TCP; + } + + if (E1000_MRQC_EN_IPV4(core->mac[MRQC])) { + return E1000_MRQ_RSS_TYPE_IPV4; + } + } else if (isip6) { + eth_ip6_hdr_info *ip6info = net_rx_pkt_get_ip6_info(pkt); + + bool ex_dis = core->mac[RFCTL] & E1000_RFCTL_IPV6_EX_DIS; + bool new_ex_dis = core->mac[RFCTL] & E1000_RFCTL_NEW_IPV6_EXT_DIS; + + /* + * Following two traces must not be combined because resulting + * event will have 11 arguments totally and some trace backends + * (at least "ust") have limitation of maximum 10 arguments per + * event. Events with more arguments fail to compile for + * backends like these. + */ + trace_e1000e_rx_rss_ip6_rfctl(core->mac[RFCTL]); + trace_e1000e_rx_rss_ip6(ex_dis, new_ex_dis, istcp, + ip6info->has_ext_hdrs, + ip6info->rss_ex_dst_valid, + ip6info->rss_ex_src_valid, + core->mac[MRQC], + E1000_MRQC_EN_TCPIPV6(core->mac[MRQC]), + E1000_MRQC_EN_IPV6EX(core->mac[MRQC]), + E1000_MRQC_EN_IPV6(core->mac[MRQC])); + + if ((!ex_dis || !ip6info->has_ext_hdrs) && + (!new_ex_dis || !(ip6info->rss_ex_dst_valid || + ip6info->rss_ex_src_valid))) { + + if (istcp && !ip6info->fragment && + E1000_MRQC_EN_TCPIPV6(core->mac[MRQC])) { + return E1000_MRQ_RSS_TYPE_IPV6TCP; + } + + if (E1000_MRQC_EN_IPV6EX(core->mac[MRQC])) { + return E1000_MRQ_RSS_TYPE_IPV6EX; + } + + } + + if (E1000_MRQC_EN_IPV6(core->mac[MRQC])) { + return E1000_MRQ_RSS_TYPE_IPV6; + } + + } + + return E1000_MRQ_RSS_TYPE_NONE; +} + +static uint32_t +e1000e_rss_calc_hash(E1000ECore *core, + struct NetRxPkt *pkt, + E1000E_RSSInfo *info) +{ + NetRxPktRssType type; + + assert(e1000e_rss_enabled(core)); + + switch (info->type) { + case E1000_MRQ_RSS_TYPE_IPV4: + type = NetPktRssIpV4; + break; + case E1000_MRQ_RSS_TYPE_IPV4TCP: + type = NetPktRssIpV4Tcp; + break; + case E1000_MRQ_RSS_TYPE_IPV6TCP: + type = NetPktRssIpV6Tcp; + break; + case E1000_MRQ_RSS_TYPE_IPV6: + type = NetPktRssIpV6; + break; + case E1000_MRQ_RSS_TYPE_IPV6EX: + type = NetPktRssIpV6Ex; + break; + default: + assert(false); + return 0; + } + + return net_rx_pkt_calc_rss_hash(pkt, type, (uint8_t *) &core->mac[RSSRK]); +} + +static void +e1000e_rss_parse_packet(E1000ECore *core, + struct NetRxPkt *pkt, + E1000E_RSSInfo *info) +{ + trace_e1000e_rx_rss_started(); + + if (!e1000e_rss_enabled(core)) { + info->enabled = false; + info->hash = 0; + info->queue = 0; + info->type = 0; + trace_e1000e_rx_rss_disabled(); + return; + } + + info->enabled = true; + + info->type = e1000e_rss_get_hash_type(core, pkt); + + trace_e1000e_rx_rss_type(info->type); + + if (info->type == E1000_MRQ_RSS_TYPE_NONE) { + info->hash = 0; + info->queue = 0; + return; + } + + info->hash = e1000e_rss_calc_hash(core, pkt, info); + info->queue = E1000_RSS_QUEUE(&core->mac[RETA], info->hash); +} + +static void +e1000e_setup_tx_offloads(E1000ECore *core, struct e1000e_tx *tx) +{ + if (tx->props.tse && tx->props.cptse) { + net_tx_pkt_build_vheader(tx->tx_pkt, true, true, tx->props.mss); + net_tx_pkt_update_ip_checksums(tx->tx_pkt); + e1000x_inc_reg_if_not_full(core->mac, TSCTC); + return; + } + + if (tx->props.sum_needed & E1000_TXD_POPTS_TXSM) { + net_tx_pkt_build_vheader(tx->tx_pkt, false, true, 0); + } + + if (tx->props.sum_needed & E1000_TXD_POPTS_IXSM) { + net_tx_pkt_update_ip_hdr_checksum(tx->tx_pkt); + } +} + +static bool +e1000e_tx_pkt_send(E1000ECore *core, struct e1000e_tx *tx, int queue_index) +{ + int target_queue = MIN(core->max_queue_num, queue_index); + NetClientState *queue = qemu_get_subqueue(core->owner_nic, target_queue); + + e1000e_setup_tx_offloads(core, tx); + + net_tx_pkt_dump(tx->tx_pkt); + + if ((core->phy[0][PHY_CTRL] & MII_CR_LOOPBACK) || + ((core->mac[RCTL] & E1000_RCTL_LBM_MAC) == E1000_RCTL_LBM_MAC)) { + return net_tx_pkt_send_loopback(tx->tx_pkt, queue); + } else { + return net_tx_pkt_send(tx->tx_pkt, queue); + } +} + +static void +e1000e_on_tx_done_update_stats(E1000ECore *core, struct NetTxPkt *tx_pkt) +{ + static const int PTCregs[6] = { PTC64, PTC127, PTC255, PTC511, + PTC1023, PTC1522 }; + + size_t tot_len = net_tx_pkt_get_total_len(tx_pkt); + + e1000x_increase_size_stats(core->mac, PTCregs, tot_len); + e1000x_inc_reg_if_not_full(core->mac, TPT); + e1000x_grow_8reg_if_not_full(core->mac, TOTL, tot_len); + + switch (net_tx_pkt_get_packet_type(tx_pkt)) { + case ETH_PKT_BCAST: + e1000x_inc_reg_if_not_full(core->mac, BPTC); + break; + case ETH_PKT_MCAST: + e1000x_inc_reg_if_not_full(core->mac, MPTC); + break; + case ETH_PKT_UCAST: + break; + default: + g_assert_not_reached(); + } + + core->mac[GPTC] = core->mac[TPT]; + core->mac[GOTCL] = core->mac[TOTL]; + core->mac[GOTCH] = core->mac[TOTH]; +} + +static void +e1000e_process_tx_desc(E1000ECore *core, + struct e1000e_tx *tx, + struct e1000_tx_desc *dp, + int queue_index) +{ + uint32_t txd_lower = le32_to_cpu(dp->lower.data); + uint32_t dtype = txd_lower & (E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D); + unsigned int split_size = txd_lower & 0xffff; + uint64_t addr; + struct e1000_context_desc *xp = (struct e1000_context_desc *)dp; + bool eop = txd_lower & E1000_TXD_CMD_EOP; + + if (dtype == E1000_TXD_CMD_DEXT) { /* context descriptor */ + e1000x_read_tx_ctx_descr(xp, &tx->props); + e1000e_process_snap_option(core, le32_to_cpu(xp->cmd_and_length)); + return; + } else if (dtype == (E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D)) { + /* data descriptor */ + tx->props.sum_needed = le32_to_cpu(dp->upper.data) >> 8; + tx->props.cptse = (txd_lower & E1000_TXD_CMD_TSE) ? 1 : 0; + e1000e_process_ts_option(core, dp); + } else { + /* legacy descriptor */ + e1000e_process_ts_option(core, dp); + tx->props.cptse = 0; + } + + addr = le64_to_cpu(dp->buffer_addr); + + if (!tx->skip_cp) { + if (!net_tx_pkt_add_raw_fragment(tx->tx_pkt, addr, split_size)) { + tx->skip_cp = true; + } + } + + if (eop) { + if (!tx->skip_cp && net_tx_pkt_parse(tx->tx_pkt)) { + if (e1000x_vlan_enabled(core->mac) && + e1000x_is_vlan_txd(txd_lower)) { + net_tx_pkt_setup_vlan_header_ex(tx->tx_pkt, + le16_to_cpu(dp->upper.fields.special), core->vet); + } + if (e1000e_tx_pkt_send(core, tx, queue_index)) { + e1000e_on_tx_done_update_stats(core, tx->tx_pkt); + } + } + + tx->skip_cp = false; + net_tx_pkt_reset(tx->tx_pkt); + + tx->props.sum_needed = 0; + tx->props.cptse = 0; + } +} + +static inline uint32_t +e1000e_tx_wb_interrupt_cause(E1000ECore *core, int queue_idx) +{ + if (!msix_enabled(core->owner)) { + return E1000_ICR_TXDW; + } + + return (queue_idx == 0) ? E1000_ICR_TXQ0 : E1000_ICR_TXQ1; +} + +static inline uint32_t +e1000e_rx_wb_interrupt_cause(E1000ECore *core, int queue_idx, + bool min_threshold_hit) +{ + if (!msix_enabled(core->owner)) { + return E1000_ICS_RXT0 | (min_threshold_hit ? E1000_ICS_RXDMT0 : 0); + } + + return (queue_idx == 0) ? E1000_ICR_RXQ0 : E1000_ICR_RXQ1; +} + +static uint32_t +e1000e_txdesc_writeback(E1000ECore *core, dma_addr_t base, + struct e1000_tx_desc *dp, bool *ide, int queue_idx) +{ + uint32_t txd_upper, txd_lower = le32_to_cpu(dp->lower.data); + + if (!(txd_lower & E1000_TXD_CMD_RS) && + !(core->mac[IVAR] & E1000_IVAR_TX_INT_EVERY_WB)) { + return 0; + } + + *ide = (txd_lower & E1000_TXD_CMD_IDE) ? true : false; + + txd_upper = le32_to_cpu(dp->upper.data) | E1000_TXD_STAT_DD; + + dp->upper.data = cpu_to_le32(txd_upper); + pci_dma_write(core->owner, base + ((char *)&dp->upper - (char *)dp), + &dp->upper, sizeof(dp->upper)); + return e1000e_tx_wb_interrupt_cause(core, queue_idx); +} + +typedef struct E1000E_RingInfo_st { + int dbah; + int dbal; + int dlen; + int dh; + int dt; + int idx; +} E1000E_RingInfo; + +static inline bool +e1000e_ring_empty(E1000ECore *core, const E1000E_RingInfo *r) +{ + return core->mac[r->dh] == core->mac[r->dt]; +} + +static inline uint64_t +e1000e_ring_base(E1000ECore *core, const E1000E_RingInfo *r) +{ + uint64_t bah = core->mac[r->dbah]; + uint64_t bal = core->mac[r->dbal]; + + return (bah << 32) + bal; +} + +static inline uint64_t +e1000e_ring_head_descr(E1000ECore *core, const E1000E_RingInfo *r) +{ + return e1000e_ring_base(core, r) + E1000_RING_DESC_LEN * core->mac[r->dh]; +} + +static inline void +e1000e_ring_advance(E1000ECore *core, const E1000E_RingInfo *r, uint32_t count) +{ + core->mac[r->dh] += count; + + if (core->mac[r->dh] * E1000_RING_DESC_LEN >= core->mac[r->dlen]) { + core->mac[r->dh] = 0; + } +} + +static inline uint32_t +e1000e_ring_free_descr_num(E1000ECore *core, const E1000E_RingInfo *r) +{ + trace_e1000e_ring_free_space(r->idx, core->mac[r->dlen], + core->mac[r->dh], core->mac[r->dt]); + + if (core->mac[r->dh] <= core->mac[r->dt]) { + return core->mac[r->dt] - core->mac[r->dh]; + } + + if (core->mac[r->dh] > core->mac[r->dt]) { + return core->mac[r->dlen] / E1000_RING_DESC_LEN + + core->mac[r->dt] - core->mac[r->dh]; + } + + g_assert_not_reached(); + return 0; +} + +static inline bool +e1000e_ring_enabled(E1000ECore *core, const E1000E_RingInfo *r) +{ + return core->mac[r->dlen] > 0; +} + +static inline uint32_t +e1000e_ring_len(E1000ECore *core, const E1000E_RingInfo *r) +{ + return core->mac[r->dlen]; +} + +typedef struct E1000E_TxRing_st { + const E1000E_RingInfo *i; + struct e1000e_tx *tx; +} E1000E_TxRing; + +static inline int +e1000e_mq_queue_idx(int base_reg_idx, int reg_idx) +{ + return (reg_idx - base_reg_idx) / (0x100 >> 2); +} + +static inline void +e1000e_tx_ring_init(E1000ECore *core, E1000E_TxRing *txr, int idx) +{ + static const E1000E_RingInfo i[E1000E_NUM_QUEUES] = { + { TDBAH, TDBAL, TDLEN, TDH, TDT, 0 }, + { TDBAH1, TDBAL1, TDLEN1, TDH1, TDT1, 1 } + }; + + assert(idx < ARRAY_SIZE(i)); + + txr->i = &i[idx]; + txr->tx = &core->tx[idx]; +} + +typedef struct E1000E_RxRing_st { + const E1000E_RingInfo *i; +} E1000E_RxRing; + +static inline void +e1000e_rx_ring_init(E1000ECore *core, E1000E_RxRing *rxr, int idx) +{ + static const E1000E_RingInfo i[E1000E_NUM_QUEUES] = { + { RDBAH0, RDBAL0, RDLEN0, RDH0, RDT0, 0 }, + { RDBAH1, RDBAL1, RDLEN1, RDH1, RDT1, 1 } + }; + + assert(idx < ARRAY_SIZE(i)); + + rxr->i = &i[idx]; +} + +static void +e1000e_start_xmit(E1000ECore *core, const E1000E_TxRing *txr) +{ + dma_addr_t base; + struct e1000_tx_desc desc; + bool ide = false; + const E1000E_RingInfo *txi = txr->i; + uint32_t cause = E1000_ICS_TXQE; + + if (!(core->mac[TCTL] & E1000_TCTL_EN)) { + trace_e1000e_tx_disabled(); + return; + } + + while (!e1000e_ring_empty(core, txi)) { + base = e1000e_ring_head_descr(core, txi); + + pci_dma_read(core->owner, base, &desc, sizeof(desc)); + + trace_e1000e_tx_descr((void *)(intptr_t)desc.buffer_addr, + desc.lower.data, desc.upper.data); + + e1000e_process_tx_desc(core, txr->tx, &desc, txi->idx); + cause |= e1000e_txdesc_writeback(core, base, &desc, &ide, txi->idx); + + e1000e_ring_advance(core, txi, 1); + } + + if (!ide || !e1000e_intrmgr_delay_tx_causes(core, &cause)) { + e1000e_set_interrupt_cause(core, cause); + } +} + +static bool +e1000e_has_rxbufs(E1000ECore *core, const E1000E_RingInfo *r, + size_t total_size) +{ + uint32_t bufs = e1000e_ring_free_descr_num(core, r); + + trace_e1000e_rx_has_buffers(r->idx, bufs, total_size, + core->rx_desc_buf_size); + + return total_size <= bufs / (core->rx_desc_len / E1000_MIN_RX_DESC_LEN) * + core->rx_desc_buf_size; +} + +static inline void +e1000e_start_recv(E1000ECore *core) +{ + int i; + + trace_e1000e_rx_start_recv(); + + for (i = 0; i <= core->max_queue_num; i++) { + qemu_flush_queued_packets(qemu_get_subqueue(core->owner_nic, i)); + } +} + +int +e1000e_can_receive(E1000ECore *core) +{ + int i; + + if (!e1000x_rx_ready(core->owner, core->mac)) { + return false; + } + + for (i = 0; i < E1000E_NUM_QUEUES; i++) { + E1000E_RxRing rxr; + + e1000e_rx_ring_init(core, &rxr, i); + if (e1000e_ring_enabled(core, rxr.i) && + e1000e_has_rxbufs(core, rxr.i, 1)) { + trace_e1000e_rx_can_recv(); + return true; + } + } + + trace_e1000e_rx_can_recv_rings_full(); + return false; +} + +ssize_t +e1000e_receive(E1000ECore *core, const uint8_t *buf, size_t size) +{ + const struct iovec iov = { + .iov_base = (uint8_t *)buf, + .iov_len = size + }; + + return e1000e_receive_iov(core, &iov, 1); +} + +static inline bool +e1000e_rx_l3_cso_enabled(E1000ECore *core) +{ + return !!(core->mac[RXCSUM] & E1000_RXCSUM_IPOFLD); +} + +static inline bool +e1000e_rx_l4_cso_enabled(E1000ECore *core) +{ + return !!(core->mac[RXCSUM] & E1000_RXCSUM_TUOFLD); +} + +static bool +e1000e_receive_filter(E1000ECore *core, const uint8_t *buf, int size) +{ + uint32_t rctl = core->mac[RCTL]; + + if (e1000x_is_vlan_packet(buf, core->vet) && + e1000x_vlan_rx_filter_enabled(core->mac)) { + 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; + } else { + trace_e1000e_rx_flt_vlan_match(vid); + } + } + + switch (net_rx_pkt_get_packet_type(core->rx_pkt)) { + case ETH_PKT_UCAST: + if (rctl & E1000_RCTL_UPE) { + return true; /* promiscuous ucast */ + } + break; + + case ETH_PKT_BCAST: + if (rctl & E1000_RCTL_BAM) { + return true; /* broadcast enabled */ + } + break; + + case ETH_PKT_MCAST: + if (rctl & E1000_RCTL_MPE) { + return true; /* promiscuous mcast */ + } + break; + + default: + g_assert_not_reached(); + } + + return e1000x_rx_group_filter(core->mac, buf); +} + +static inline void +e1000e_read_lgcy_rx_descr(E1000ECore *core, uint8_t *desc, hwaddr *buff_addr) +{ + struct e1000_rx_desc *d = (struct e1000_rx_desc *) desc; + *buff_addr = le64_to_cpu(d->buffer_addr); +} + +static inline void +e1000e_read_ext_rx_descr(E1000ECore *core, uint8_t *desc, hwaddr *buff_addr) +{ + union e1000_rx_desc_extended *d = (union e1000_rx_desc_extended *) desc; + *buff_addr = le64_to_cpu(d->read.buffer_addr); +} + +static inline void +e1000e_read_ps_rx_descr(E1000ECore *core, uint8_t *desc, + hwaddr (*buff_addr)[MAX_PS_BUFFERS]) +{ + int i; + union e1000_rx_desc_packet_split *d = + (union e1000_rx_desc_packet_split *) desc; + + for (i = 0; i < MAX_PS_BUFFERS; i++) { + (*buff_addr)[i] = le64_to_cpu(d->read.buffer_addr[i]); + } + + trace_e1000e_rx_desc_ps_read((*buff_addr)[0], (*buff_addr)[1], + (*buff_addr)[2], (*buff_addr)[3]); +} + +static inline void +e1000e_read_rx_descr(E1000ECore *core, uint8_t *desc, + hwaddr (*buff_addr)[MAX_PS_BUFFERS]) +{ + if (e1000e_rx_use_legacy_descriptor(core)) { + e1000e_read_lgcy_rx_descr(core, desc, &(*buff_addr)[0]); + (*buff_addr)[1] = (*buff_addr)[2] = (*buff_addr)[3] = 0; + } else { + if (core->mac[RCTL] & E1000_RCTL_DTYP_PS) { + e1000e_read_ps_rx_descr(core, desc, buff_addr); + } else { + e1000e_read_ext_rx_descr(core, desc, &(*buff_addr)[0]); + (*buff_addr)[1] = (*buff_addr)[2] = (*buff_addr)[3] = 0; + } + } +} + +static void +e1000e_verify_csum_in_sw(E1000ECore *core, + struct NetRxPkt *pkt, + uint32_t *status_flags, + bool istcp, bool isudp) +{ + bool csum_valid; + uint32_t csum_error; + + if (e1000e_rx_l3_cso_enabled(core)) { + if (!net_rx_pkt_validate_l3_csum(pkt, &csum_valid)) { + trace_e1000e_rx_metadata_l3_csum_validation_failed(); + } else { + csum_error = csum_valid ? 0 : E1000_RXDEXT_STATERR_IPE; + *status_flags |= E1000_RXD_STAT_IPCS | csum_error; + } + } else { + trace_e1000e_rx_metadata_l3_cso_disabled(); + } + + if (!e1000e_rx_l4_cso_enabled(core)) { + trace_e1000e_rx_metadata_l4_cso_disabled(); + return; + } + + if (!net_rx_pkt_validate_l4_csum(pkt, &csum_valid)) { + trace_e1000e_rx_metadata_l4_csum_validation_failed(); + return; + } + + csum_error = csum_valid ? 0 : E1000_RXDEXT_STATERR_TCPE; + + if (istcp) { + *status_flags |= E1000_RXD_STAT_TCPCS | + csum_error; + } else if (isudp) { + *status_flags |= E1000_RXD_STAT_TCPCS | + E1000_RXD_STAT_UDPCS | + csum_error; + } +} + +static inline bool +e1000e_is_tcp_ack(E1000ECore *core, struct NetRxPkt *rx_pkt) +{ + if (!net_rx_pkt_is_tcp_ack(rx_pkt)) { + return false; + } + + if (core->mac[RFCTL] & E1000_RFCTL_ACK_DATA_DIS) { + return !net_rx_pkt_has_tcp_data(rx_pkt); + } + + return true; +} + +static void +e1000e_build_rx_metadata(E1000ECore *core, + struct NetRxPkt *pkt, + bool is_eop, + const E1000E_RSSInfo *rss_info, + uint32_t *rss, uint32_t *mrq, + uint32_t *status_flags, + uint16_t *ip_id, + uint16_t *vlan_tag) +{ + struct virtio_net_hdr *vhdr; + bool isip4, isip6, istcp, isudp; + uint32_t pkt_type; + + *status_flags = E1000_RXD_STAT_DD; + + /* No additional metadata needed for non-EOP descriptors */ + if (!is_eop) { + goto func_exit; + } + + *status_flags |= E1000_RXD_STAT_EOP; + + net_rx_pkt_get_protocols(pkt, &isip4, &isip6, &isudp, &istcp); + trace_e1000e_rx_metadata_protocols(isip4, isip6, isudp, istcp); + + /* VLAN state */ + if (net_rx_pkt_is_vlan_stripped(pkt)) { + *status_flags |= E1000_RXD_STAT_VP; + *vlan_tag = cpu_to_le16(net_rx_pkt_get_vlan_tag(pkt)); + trace_e1000e_rx_metadata_vlan(*vlan_tag); + } + + /* Packet parsing results */ + if ((core->mac[RXCSUM] & E1000_RXCSUM_PCSD) != 0) { + if (rss_info->enabled) { + *rss = cpu_to_le32(rss_info->hash); + *mrq = cpu_to_le32(rss_info->type | (rss_info->queue << 8)); + trace_e1000e_rx_metadata_rss(*rss, *mrq); + } + } else if (isip4) { + *status_flags |= E1000_RXD_STAT_IPIDV; + *ip_id = cpu_to_le16(net_rx_pkt_get_ip_id(pkt)); + trace_e1000e_rx_metadata_ip_id(*ip_id); + } + + if (istcp && e1000e_is_tcp_ack(core, pkt)) { + *status_flags |= E1000_RXD_STAT_ACK; + trace_e1000e_rx_metadata_ack(); + } + + if (isip6 && (core->mac[RFCTL] & E1000_RFCTL_IPV6_DIS)) { + trace_e1000e_rx_metadata_ipv6_filtering_disabled(); + pkt_type = E1000_RXD_PKT_MAC; + } else if (istcp || isudp) { + pkt_type = isip4 ? E1000_RXD_PKT_IP4_XDP : E1000_RXD_PKT_IP6_XDP; + } else if (isip4 || isip6) { + pkt_type = isip4 ? E1000_RXD_PKT_IP4 : E1000_RXD_PKT_IP6; + } else { + pkt_type = E1000_RXD_PKT_MAC; + } + + *status_flags |= E1000_RXD_PKT_TYPE(pkt_type); + trace_e1000e_rx_metadata_pkt_type(pkt_type); + + /* RX CSO information */ + if (isip6 && (core->mac[RFCTL] & E1000_RFCTL_IPV6_XSUM_DIS)) { + trace_e1000e_rx_metadata_ipv6_sum_disabled(); + goto func_exit; + } + + if (!net_rx_pkt_has_virt_hdr(pkt)) { + trace_e1000e_rx_metadata_no_virthdr(); + e1000e_verify_csum_in_sw(core, pkt, status_flags, istcp, isudp); + goto func_exit; + } + + vhdr = net_rx_pkt_get_vhdr(pkt); + + if (!(vhdr->flags & VIRTIO_NET_HDR_F_DATA_VALID) && + !(vhdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM)) { + trace_e1000e_rx_metadata_virthdr_no_csum_info(); + e1000e_verify_csum_in_sw(core, pkt, status_flags, istcp, isudp); + goto func_exit; + } + + if (e1000e_rx_l3_cso_enabled(core)) { + *status_flags |= isip4 ? E1000_RXD_STAT_IPCS : 0; + } else { + trace_e1000e_rx_metadata_l3_cso_disabled(); + } + + if (e1000e_rx_l4_cso_enabled(core)) { + if (istcp) { + *status_flags |= E1000_RXD_STAT_TCPCS; + } else if (isudp) { + *status_flags |= E1000_RXD_STAT_TCPCS | E1000_RXD_STAT_UDPCS; + } + } else { + trace_e1000e_rx_metadata_l4_cso_disabled(); + } + + trace_e1000e_rx_metadata_status_flags(*status_flags); + +func_exit: + *status_flags = cpu_to_le32(*status_flags); +} + +static inline void +e1000e_write_lgcy_rx_descr(E1000ECore *core, uint8_t *desc, + struct NetRxPkt *pkt, + const E1000E_RSSInfo *rss_info, + uint16_t length) +{ + uint32_t status_flags, rss, mrq; + uint16_t ip_id; + + struct e1000_rx_desc *d = (struct e1000_rx_desc *) desc; + + memset(d, 0, sizeof(*d)); + + assert(!rss_info->enabled); + + d->length = cpu_to_le16(length); + + e1000e_build_rx_metadata(core, pkt, pkt != NULL, + rss_info, + &rss, &mrq, + &status_flags, &ip_id, + &d->special); + d->errors = (uint8_t) (le32_to_cpu(status_flags) >> 24); + d->status = (uint8_t) le32_to_cpu(status_flags); +} + +static inline void +e1000e_write_ext_rx_descr(E1000ECore *core, uint8_t *desc, + struct NetRxPkt *pkt, + const E1000E_RSSInfo *rss_info, + uint16_t length) +{ + union e1000_rx_desc_extended *d = (union e1000_rx_desc_extended *) desc; + + memset(d, 0, sizeof(*d)); + + d->wb.upper.length = cpu_to_le16(length); + + e1000e_build_rx_metadata(core, pkt, pkt != NULL, + rss_info, + &d->wb.lower.hi_dword.rss, + &d->wb.lower.mrq, + &d->wb.upper.status_error, + &d->wb.lower.hi_dword.csum_ip.ip_id, + &d->wb.upper.vlan); +} + +static inline void +e1000e_write_ps_rx_descr(E1000ECore *core, uint8_t *desc, + struct NetRxPkt *pkt, + const E1000E_RSSInfo *rss_info, + size_t ps_hdr_len, + uint16_t(*written)[MAX_PS_BUFFERS]) +{ + int i; + union e1000_rx_desc_packet_split *d = + (union e1000_rx_desc_packet_split *) desc; + + memset(d, 0, sizeof(*d)); + + d->wb.middle.length0 = cpu_to_le16((*written)[0]); + + for (i = 0; i < PS_PAGE_BUFFERS; i++) { + d->wb.upper.length[i] = cpu_to_le16((*written)[i + 1]); + } + + e1000e_build_rx_metadata(core, pkt, pkt != NULL, + rss_info, + &d->wb.lower.hi_dword.rss, + &d->wb.lower.mrq, + &d->wb.middle.status_error, + &d->wb.lower.hi_dword.csum_ip.ip_id, + &d->wb.middle.vlan); + + d->wb.upper.header_status = + cpu_to_le16(ps_hdr_len | (ps_hdr_len ? E1000_RXDPS_HDRSTAT_HDRSP : 0)); + + trace_e1000e_rx_desc_ps_write((*written)[0], (*written)[1], + (*written)[2], (*written)[3]); +} + +static inline void +e1000e_write_rx_descr(E1000ECore *core, uint8_t *desc, +struct NetRxPkt *pkt, const E1000E_RSSInfo *rss_info, + size_t ps_hdr_len, uint16_t(*written)[MAX_PS_BUFFERS]) +{ + if (e1000e_rx_use_legacy_descriptor(core)) { + assert(ps_hdr_len == 0); + e1000e_write_lgcy_rx_descr(core, desc, pkt, rss_info, (*written)[0]); + } else { + if (core->mac[RCTL] & E1000_RCTL_DTYP_PS) { + e1000e_write_ps_rx_descr(core, desc, pkt, rss_info, + ps_hdr_len, written); + } else { + assert(ps_hdr_len == 0); + e1000e_write_ext_rx_descr(core, desc, pkt, rss_info, + (*written)[0]); + } + } +} + +typedef struct e1000e_ba_state_st { + uint16_t written[MAX_PS_BUFFERS]; + uint8_t cur_idx; +} e1000e_ba_state; + +static inline void +e1000e_write_hdr_to_rx_buffers(E1000ECore *core, + hwaddr (*ba)[MAX_PS_BUFFERS], + e1000e_ba_state *bastate, + const char *data, + dma_addr_t data_len) +{ + assert(data_len <= core->rxbuf_sizes[0] - bastate->written[0]); + + pci_dma_write(core->owner, (*ba)[0] + bastate->written[0], data, data_len); + bastate->written[0] += data_len; + + bastate->cur_idx = 1; +} + +static void +e1000e_write_to_rx_buffers(E1000ECore *core, + hwaddr (*ba)[MAX_PS_BUFFERS], + e1000e_ba_state *bastate, + const char *data, + dma_addr_t data_len) +{ + while (data_len > 0) { + uint32_t cur_buf_len = core->rxbuf_sizes[bastate->cur_idx]; + uint32_t cur_buf_bytes_left = cur_buf_len - + bastate->written[bastate->cur_idx]; + uint32_t bytes_to_write = MIN(data_len, cur_buf_bytes_left); + + trace_e1000e_rx_desc_buff_write(bastate->cur_idx, + (*ba)[bastate->cur_idx], + bastate->written[bastate->cur_idx], + data, + bytes_to_write); + + pci_dma_write(core->owner, + (*ba)[bastate->cur_idx] + bastate->written[bastate->cur_idx], + data, bytes_to_write); + + bastate->written[bastate->cur_idx] += bytes_to_write; + data += bytes_to_write; + data_len -= bytes_to_write; + + if (bastate->written[bastate->cur_idx] == cur_buf_len) { + bastate->cur_idx++; + } + + assert(bastate->cur_idx < MAX_PS_BUFFERS); + } +} + +static void +e1000e_update_rx_stats(E1000ECore *core, + size_t data_size, + size_t data_fcs_size) +{ + e1000x_update_rx_total_stats(core->mac, data_size, data_fcs_size); + + switch (net_rx_pkt_get_packet_type(core->rx_pkt)) { + case ETH_PKT_BCAST: + e1000x_inc_reg_if_not_full(core->mac, BPRC); + break; + + case ETH_PKT_MCAST: + e1000x_inc_reg_if_not_full(core->mac, MPRC); + break; + + default: + break; + } +} + +static inline bool +e1000e_rx_descr_threshold_hit(E1000ECore *core, const E1000E_RingInfo *rxi) +{ + return e1000e_ring_free_descr_num(core, rxi) == + e1000e_ring_len(core, rxi) >> core->rxbuf_min_shift; +} + +static bool +e1000e_do_ps(E1000ECore *core, struct NetRxPkt *pkt, size_t *hdr_len) +{ + bool isip4, isip6, isudp, istcp; + bool fragment; + + if (!e1000e_rx_use_ps_descriptor(core)) { + return false; + } + + net_rx_pkt_get_protocols(pkt, &isip4, &isip6, &isudp, &istcp); + + if (isip4) { + fragment = net_rx_pkt_get_ip4_info(pkt)->fragment; + } else if (isip6) { + fragment = net_rx_pkt_get_ip6_info(pkt)->fragment; + } else { + return false; + } + + if (fragment && (core->mac[RFCTL] & E1000_RFCTL_IPFRSP_DIS)) { + return false; + } + + if (!fragment && (isudp || istcp)) { + *hdr_len = net_rx_pkt_get_l5_hdr_offset(pkt); + } else { + *hdr_len = net_rx_pkt_get_l4_hdr_offset(pkt); + } + + if ((*hdr_len > core->rxbuf_sizes[0]) || + (*hdr_len > net_rx_pkt_get_total_len(pkt))) { + return false; + } + + return true; +} + +static void +e1000e_write_packet_to_guest(E1000ECore *core, struct NetRxPkt *pkt, + const E1000E_RxRing *rxr, + const E1000E_RSSInfo *rss_info) +{ + PCIDevice *d = core->owner; + dma_addr_t base; + uint8_t desc[E1000_MAX_RX_DESC_LEN]; + size_t desc_size; + size_t desc_offset = 0; + size_t iov_ofs = 0; + + struct iovec *iov = net_rx_pkt_get_iovec(pkt); + size_t size = net_rx_pkt_get_total_len(pkt); + size_t total_size = size + e1000x_fcs_len(core->mac); + const E1000E_RingInfo *rxi; + size_t ps_hdr_len = 0; + bool do_ps = e1000e_do_ps(core, pkt, &ps_hdr_len); + + rxi = rxr->i; + + do { + hwaddr ba[MAX_PS_BUFFERS]; + e1000e_ba_state bastate = { { 0 } }; + bool is_last = false; + bool is_first = true; + + desc_size = total_size - desc_offset; + + if (desc_size > core->rx_desc_buf_size) { + desc_size = core->rx_desc_buf_size; + } + + base = e1000e_ring_head_descr(core, rxi); + + pci_dma_read(d, base, &desc, core->rx_desc_len); + + trace_e1000e_rx_descr(rxi->idx, base, core->rx_desc_len); + + e1000e_read_rx_descr(core, desc, &ba); + + if (ba[0]) { + if (desc_offset < size) { + static const uint32_t fcs_pad; + size_t iov_copy; + size_t copy_size = size - desc_offset; + if (copy_size > core->rx_desc_buf_size) { + copy_size = core->rx_desc_buf_size; + } + + /* For PS mode copy the packet header first */ + if (do_ps) { + if (is_first) { + size_t ps_hdr_copied = 0; + do { + iov_copy = MIN(ps_hdr_len - ps_hdr_copied, + iov->iov_len - iov_ofs); + + e1000e_write_hdr_to_rx_buffers(core, &ba, &bastate, + iov->iov_base, iov_copy); + + copy_size -= iov_copy; + ps_hdr_copied += iov_copy; + + iov_ofs += iov_copy; + if (iov_ofs == iov->iov_len) { + iov++; + iov_ofs = 0; + } + } while (ps_hdr_copied < ps_hdr_len); + + is_first = false; + } else { + /* Leave buffer 0 of each descriptor except first */ + /* empty as per spec 7.1.5.1 */ + e1000e_write_hdr_to_rx_buffers(core, &ba, &bastate, + NULL, 0); + } + } + + /* Copy packet payload */ + while (copy_size) { + iov_copy = MIN(copy_size, iov->iov_len - iov_ofs); + + e1000e_write_to_rx_buffers(core, &ba, &bastate, + iov->iov_base + iov_ofs, iov_copy); + + copy_size -= iov_copy; + iov_ofs += iov_copy; + if (iov_ofs == iov->iov_len) { + iov++; + iov_ofs = 0; + } + } + + if (desc_offset + desc_size >= total_size) { + /* Simulate FCS checksum presence in the last descriptor */ + e1000e_write_to_rx_buffers(core, &ba, &bastate, + (const char *) &fcs_pad, e1000x_fcs_len(core->mac)); + } + } + desc_offset += desc_size; + if (desc_offset >= total_size) { + is_last = true; + } + } else { /* as per intel docs; skip descriptors with null buf addr */ + trace_e1000e_rx_null_descriptor(); + } + + e1000e_write_rx_descr(core, desc, is_last ? core->rx_pkt : NULL, + rss_info, do_ps ? ps_hdr_len : 0, &bastate.written); + pci_dma_write(d, base, &desc, core->rx_desc_len); + + e1000e_ring_advance(core, rxi, + core->rx_desc_len / E1000_MIN_RX_DESC_LEN); + + } while (desc_offset < total_size); + + e1000e_update_rx_stats(core, size, total_size); +} + +static inline void +e1000e_rx_fix_l4_csum(E1000ECore *core, struct NetRxPkt *pkt) +{ + if (net_rx_pkt_has_virt_hdr(pkt)) { + struct virtio_net_hdr *vhdr = net_rx_pkt_get_vhdr(pkt); + + if (vhdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { + net_rx_pkt_fix_l4_csum(pkt); + } + } +} + +ssize_t +e1000e_receive_iov(E1000ECore *core, const struct iovec *iov, int iovcnt) +{ + static const int maximum_ethernet_hdr_len = (14 + 4); + /* Min. octets in an ethernet frame sans FCS */ + static const int min_buf_size = 60; + + uint32_t n = 0; + uint8_t min_buf[min_buf_size]; + struct iovec min_iov; + uint8_t *filter_buf; + size_t size, orig_size; + size_t iov_ofs = 0; + E1000E_RxRing rxr; + E1000E_RSSInfo rss_info; + size_t total_size; + ssize_t retval; + bool rdmts_hit; + + trace_e1000e_rx_receive_iov(iovcnt); + + if (!e1000x_hw_rx_enabled(core->mac)) { + return -1; + } + + /* Pull virtio header in */ + if (core->has_vnet) { + net_rx_pkt_set_vhdr_iovec(core->rx_pkt, iov, iovcnt); + iov_ofs = sizeof(struct virtio_net_hdr); + } + + filter_buf = iov->iov_base + iov_ofs; + orig_size = iov_size(iov, iovcnt); + size = orig_size - iov_ofs; + + /* Pad to minimum Ethernet frame length */ + if (size < sizeof(min_buf)) { + iov_to_buf(iov, iovcnt, iov_ofs, min_buf, size); + memset(&min_buf[size], 0, sizeof(min_buf) - size); + e1000x_inc_reg_if_not_full(core->mac, RUC); + min_iov.iov_base = filter_buf = min_buf; + min_iov.iov_len = size = sizeof(min_buf); + iovcnt = 1; + iov = &min_iov; + iov_ofs = 0; + } else if (iov->iov_len < maximum_ethernet_hdr_len) { + /* This is very unlikely, but may happen. */ + iov_to_buf(iov, iovcnt, iov_ofs, min_buf, maximum_ethernet_hdr_len); + filter_buf = min_buf; + } + + /* Discard oversized packets if !LPE and !SBP. */ + if (e1000x_is_oversized(core->mac, size)) { + return orig_size; + } + + net_rx_pkt_set_packet_type(core->rx_pkt, + get_eth_packet_type(PKT_GET_ETH_HDR(filter_buf))); + + if (!e1000e_receive_filter(core, filter_buf, size)) { + trace_e1000e_rx_flt_dropped(); + return orig_size; + } + + net_rx_pkt_attach_iovec_ex(core->rx_pkt, iov, iovcnt, iov_ofs, + e1000x_vlan_enabled(core->mac), core->vet); + + e1000e_rss_parse_packet(core, core->rx_pkt, &rss_info); + e1000e_rx_ring_init(core, &rxr, rss_info.queue); + + trace_e1000e_rx_rss_dispatched_to_queue(rxr.i->idx); + + total_size = net_rx_pkt_get_total_len(core->rx_pkt) + + e1000x_fcs_len(core->mac); + + if (e1000e_has_rxbufs(core, rxr.i, total_size)) { + e1000e_rx_fix_l4_csum(core, core->rx_pkt); + + e1000e_write_packet_to_guest(core, core->rx_pkt, &rxr, &rss_info); + + retval = orig_size; + + /* Perform small receive detection (RSRPD) */ + if (total_size < core->mac[RSRPD]) { + n |= E1000_ICS_SRPD; + } + + /* Perform ACK receive detection */ + if (e1000e_is_tcp_ack(core, core->rx_pkt)) { + n |= E1000_ICS_ACK; + } + + /* Check if receive descriptor minimum threshold hit */ + rdmts_hit = e1000e_rx_descr_threshold_hit(core, rxr.i); + n |= e1000e_rx_wb_interrupt_cause(core, rxr.i->idx, rdmts_hit); + + trace_e1000e_rx_written_to_guest(n); + } else { + n |= E1000_ICS_RXO; + retval = 0; + + trace_e1000e_rx_not_written_to_guest(n); + } + + if (!e1000e_intrmgr_delay_rx_causes(core, &n)) { + trace_e1000e_rx_interrupt_set(n); + e1000e_set_interrupt_cause(core, n); + } else { + trace_e1000e_rx_interrupt_delayed(n); + } + + return retval; +} + +static inline bool +e1000e_have_autoneg(E1000ECore *core) +{ + return core->phy[0][PHY_CTRL] & MII_CR_AUTO_NEG_EN; +} + +static void e1000e_update_flowctl_status(E1000ECore *core) +{ + if (e1000e_have_autoneg(core) && + core->phy[0][PHY_STATUS] & MII_SR_AUTONEG_COMPLETE) { + trace_e1000e_link_autoneg_flowctl(true); + core->mac[CTRL] |= E1000_CTRL_TFCE | E1000_CTRL_RFCE; + } else { + trace_e1000e_link_autoneg_flowctl(false); + } +} + +static inline void +e1000e_link_down(E1000ECore *core) +{ + e1000x_update_regs_on_link_down(core->mac, core->phy[0]); + e1000e_update_flowctl_status(core); +} + +static inline void +e1000e_set_phy_ctrl(E1000ECore *core, int index, uint16_t val) +{ + /* bits 0-5 reserved; MII_CR_[RESTART_AUTO_NEG,RESET] are self clearing */ + core->phy[0][PHY_CTRL] = val & ~(0x3f | + MII_CR_RESET | + MII_CR_RESTART_AUTO_NEG); + + if ((val & MII_CR_RESTART_AUTO_NEG) && + e1000e_have_autoneg(core)) { + e1000x_restart_autoneg(core->mac, core->phy[0], core->autoneg_timer); + } +} + +static void +e1000e_set_phy_oem_bits(E1000ECore *core, int index, uint16_t val) +{ + core->phy[0][PHY_OEM_BITS] = val & ~BIT(10); + + if (val & BIT(10)) { + e1000x_restart_autoneg(core->mac, core->phy[0], core->autoneg_timer); + } +} + +static void +e1000e_set_phy_page(E1000ECore *core, int index, uint16_t val) +{ + core->phy[0][PHY_PAGE] = val & PHY_PAGE_RW_MASK; +} + +void +e1000e_core_set_link_status(E1000ECore *core) +{ + NetClientState *nc = qemu_get_queue(core->owner_nic); + uint32_t old_status = core->mac[STATUS]; + + trace_e1000e_link_status_changed(nc->link_down ? false : true); + + if (nc->link_down) { + e1000x_update_regs_on_link_down(core->mac, core->phy[0]); + } else { + if (e1000e_have_autoneg(core) && + !(core->phy[0][PHY_STATUS] & MII_SR_AUTONEG_COMPLETE)) { + e1000x_restart_autoneg(core->mac, core->phy[0], + core->autoneg_timer); + } else { + e1000x_update_regs_on_link_up(core->mac, core->phy[0]); + } + } + + if (core->mac[STATUS] != old_status) { + e1000e_set_interrupt_cause(core, E1000_ICR_LSC); + } +} + +static void +e1000e_set_ctrl(E1000ECore *core, int index, uint32_t val) +{ + trace_e1000e_core_ctrl_write(index, val); + + /* RST is self clearing */ + core->mac[CTRL] = val & ~E1000_CTRL_RST; + core->mac[CTRL_DUP] = core->mac[CTRL]; + + trace_e1000e_link_set_params( + !!(val & E1000_CTRL_ASDE), + (val & E1000_CTRL_SPD_SEL) >> E1000_CTRL_SPD_SHIFT, + !!(val & E1000_CTRL_FRCSPD), + !!(val & E1000_CTRL_FRCDPX), + !!(val & E1000_CTRL_RFCE), + !!(val & E1000_CTRL_TFCE)); + + if (val & E1000_CTRL_RST) { + trace_e1000e_core_ctrl_sw_reset(); + e1000x_reset_mac_addr(core->owner_nic, core->mac, core->permanent_mac); + } + + if (val & E1000_CTRL_PHY_RST) { + trace_e1000e_core_ctrl_phy_reset(); + core->mac[STATUS] |= E1000_STATUS_PHYRA; + } +} + +static void +e1000e_set_rfctl(E1000ECore *core, int index, uint32_t val) +{ + trace_e1000e_rx_set_rfctl(val); + + if (!(val & E1000_RFCTL_ISCSI_DIS)) { + trace_e1000e_wrn_iscsi_filtering_not_supported(); + } + + if (!(val & E1000_RFCTL_NFSW_DIS)) { + trace_e1000e_wrn_nfsw_filtering_not_supported(); + } + + if (!(val & E1000_RFCTL_NFSR_DIS)) { + trace_e1000e_wrn_nfsr_filtering_not_supported(); + } + + core->mac[RFCTL] = val; +} + +static void +e1000e_calc_per_desc_buf_size(E1000ECore *core) +{ + int i; + core->rx_desc_buf_size = 0; + + for (i = 0; i < ARRAY_SIZE(core->rxbuf_sizes); i++) { + core->rx_desc_buf_size += core->rxbuf_sizes[i]; + } +} + +static void +e1000e_parse_rxbufsize(E1000ECore *core) +{ + uint32_t rctl = core->mac[RCTL]; + + memset(core->rxbuf_sizes, 0, sizeof(core->rxbuf_sizes)); + + if (rctl & E1000_RCTL_DTYP_MASK) { + uint32_t bsize; + + bsize = core->mac[PSRCTL] & E1000_PSRCTL_BSIZE0_MASK; + core->rxbuf_sizes[0] = (bsize >> E1000_PSRCTL_BSIZE0_SHIFT) * 128; + + bsize = core->mac[PSRCTL] & E1000_PSRCTL_BSIZE1_MASK; + core->rxbuf_sizes[1] = (bsize >> E1000_PSRCTL_BSIZE1_SHIFT) * 1024; + + bsize = core->mac[PSRCTL] & E1000_PSRCTL_BSIZE2_MASK; + core->rxbuf_sizes[2] = (bsize >> E1000_PSRCTL_BSIZE2_SHIFT) * 1024; + + bsize = core->mac[PSRCTL] & E1000_PSRCTL_BSIZE3_MASK; + core->rxbuf_sizes[3] = (bsize >> E1000_PSRCTL_BSIZE3_SHIFT) * 1024; + } else if (rctl & E1000_RCTL_FLXBUF_MASK) { + int flxbuf = rctl & E1000_RCTL_FLXBUF_MASK; + core->rxbuf_sizes[0] = (flxbuf >> E1000_RCTL_FLXBUF_SHIFT) * 1024; + } else { + core->rxbuf_sizes[0] = e1000x_rxbufsize(rctl); + } + + trace_e1000e_rx_desc_buff_sizes(core->rxbuf_sizes[0], core->rxbuf_sizes[1], + core->rxbuf_sizes[2], core->rxbuf_sizes[3]); + + e1000e_calc_per_desc_buf_size(core); +} + +static void +e1000e_calc_rxdesclen(E1000ECore *core) +{ + if (e1000e_rx_use_legacy_descriptor(core)) { + core->rx_desc_len = sizeof(struct e1000_rx_desc); + } else { + if (core->mac[RCTL] & E1000_RCTL_DTYP_PS) { + core->rx_desc_len = sizeof(union e1000_rx_desc_packet_split); + } else { + core->rx_desc_len = sizeof(union e1000_rx_desc_extended); + } + } + trace_e1000e_rx_desc_len(core->rx_desc_len); +} + +static void +e1000e_set_rx_control(E1000ECore *core, int index, uint32_t val) +{ + core->mac[RCTL] = val; + trace_e1000e_rx_set_rctl(core->mac[RCTL]); + + if (val & E1000_RCTL_EN) { + e1000e_parse_rxbufsize(core); + e1000e_calc_rxdesclen(core); + core->rxbuf_min_shift = ((val / E1000_RCTL_RDMTS_QUAT) & 3) + 1 + + E1000_RING_DESC_LEN_SHIFT; + + e1000e_start_recv(core); + } +} + +static +void(*e1000e_phyreg_writeops[E1000E_PHY_PAGES][E1000E_PHY_PAGE_SIZE]) +(E1000ECore *, int, uint16_t) = { + [0] = { + [PHY_CTRL] = e1000e_set_phy_ctrl, + [PHY_PAGE] = e1000e_set_phy_page, + [PHY_OEM_BITS] = e1000e_set_phy_oem_bits + } +}; + +static inline void +e1000e_clear_ims_bits(E1000ECore *core, uint32_t bits) +{ + trace_e1000e_irq_clear_ims(bits, core->mac[IMS], core->mac[IMS] & ~bits); + core->mac[IMS] &= ~bits; +} + +static inline bool +e1000e_postpone_interrupt(bool *interrupt_pending, + E1000IntrDelayTimer *timer) +{ + if (timer->running) { + trace_e1000e_irq_postponed_by_xitr(timer->delay_reg << 2); + + *interrupt_pending = true; + return true; + } + + if (timer->core->mac[timer->delay_reg] != 0) { + e1000e_intrmgr_rearm_timer(timer); + } + + return false; +} + +static inline bool +e1000e_itr_should_postpone(E1000ECore *core) +{ + return e1000e_postpone_interrupt(&core->itr_intr_pending, &core->itr); +} + +static inline bool +e1000e_eitr_should_postpone(E1000ECore *core, int idx) +{ + return e1000e_postpone_interrupt(&core->eitr_intr_pending[idx], + &core->eitr[idx]); +} + +static void +e1000e_msix_notify_one(E1000ECore *core, uint32_t cause, uint32_t int_cfg) +{ + uint32_t effective_eiac; + + if (E1000_IVAR_ENTRY_VALID(int_cfg)) { + uint32_t vec = E1000_IVAR_ENTRY_VEC(int_cfg); + if (vec < E1000E_MSIX_VEC_NUM) { + if (!e1000e_eitr_should_postpone(core, vec)) { + trace_e1000e_irq_msix_notify_vec(vec); + msix_notify(core->owner, vec); + } + } else { + trace_e1000e_wrn_msix_vec_wrong(cause, int_cfg); + } + } else { + trace_e1000e_wrn_msix_invalid(cause, int_cfg); + } + + if (core->mac[CTRL_EXT] & E1000_CTRL_EXT_EIAME) { + trace_e1000e_irq_ims_clear_eiame(core->mac[IAM], cause); + e1000e_clear_ims_bits(core, core->mac[IAM] & cause); + } + + trace_e1000e_irq_icr_clear_eiac(core->mac[ICR], core->mac[EIAC]); + + if (core->mac[EIAC] & E1000_ICR_OTHER) { + effective_eiac = (core->mac[EIAC] & E1000_EIAC_MASK) | + E1000_ICR_OTHER_CAUSES; + } else { + effective_eiac = core->mac[EIAC] & E1000_EIAC_MASK; + } + core->mac[ICR] &= ~effective_eiac; +} + +static void +e1000e_msix_notify(E1000ECore *core, uint32_t causes) +{ + if (causes & E1000_ICR_RXQ0) { + e1000e_msix_notify_one(core, E1000_ICR_RXQ0, + E1000_IVAR_RXQ0(core->mac[IVAR])); + } + + if (causes & E1000_ICR_RXQ1) { + e1000e_msix_notify_one(core, E1000_ICR_RXQ1, + E1000_IVAR_RXQ1(core->mac[IVAR])); + } + + if (causes & E1000_ICR_TXQ0) { + e1000e_msix_notify_one(core, E1000_ICR_TXQ0, + E1000_IVAR_TXQ0(core->mac[IVAR])); + } + + if (causes & E1000_ICR_TXQ1) { + e1000e_msix_notify_one(core, E1000_ICR_TXQ1, + E1000_IVAR_TXQ1(core->mac[IVAR])); + } + + if (causes & E1000_ICR_OTHER) { + e1000e_msix_notify_one(core, E1000_ICR_OTHER, + E1000_IVAR_OTHER(core->mac[IVAR])); + } +} + +static void +e1000e_msix_clear_one(E1000ECore *core, uint32_t cause, uint32_t int_cfg) +{ + if (E1000_IVAR_ENTRY_VALID(int_cfg)) { + uint32_t vec = E1000_IVAR_ENTRY_VEC(int_cfg); + if (vec < E1000E_MSIX_VEC_NUM) { + trace_e1000e_irq_msix_pending_clearing(cause, int_cfg, vec); + msix_clr_pending(core->owner, vec); + } else { + trace_e1000e_wrn_msix_vec_wrong(cause, int_cfg); + } + } else { + trace_e1000e_wrn_msix_invalid(cause, int_cfg); + } +} + +static void +e1000e_msix_clear(E1000ECore *core, uint32_t causes) +{ + if (causes & E1000_ICR_RXQ0) { + e1000e_msix_clear_one(core, E1000_ICR_RXQ0, + E1000_IVAR_RXQ0(core->mac[IVAR])); + } + + if (causes & E1000_ICR_RXQ1) { + e1000e_msix_clear_one(core, E1000_ICR_RXQ1, + E1000_IVAR_RXQ1(core->mac[IVAR])); + } + + if (causes & E1000_ICR_TXQ0) { + e1000e_msix_clear_one(core, E1000_ICR_TXQ0, + E1000_IVAR_TXQ0(core->mac[IVAR])); + } + + if (causes & E1000_ICR_TXQ1) { + e1000e_msix_clear_one(core, E1000_ICR_TXQ1, + E1000_IVAR_TXQ1(core->mac[IVAR])); + } + + if (causes & E1000_ICR_OTHER) { + e1000e_msix_clear_one(core, E1000_ICR_OTHER, + E1000_IVAR_OTHER(core->mac[IVAR])); + } +} + +static inline void +e1000e_fix_icr_asserted(E1000ECore *core) +{ + core->mac[ICR] &= ~E1000_ICR_ASSERTED; + if (core->mac[ICR]) { + core->mac[ICR] |= E1000_ICR_ASSERTED; + } + + trace_e1000e_irq_fix_icr_asserted(core->mac[ICR]); +} + +static void +e1000e_send_msi(E1000ECore *core, bool msix) +{ + uint32_t causes = core->mac[ICR] & core->mac[IMS] & ~E1000_ICR_ASSERTED; + + if (msix) { + e1000e_msix_notify(core, causes); + } else { + if (!e1000e_itr_should_postpone(core)) { + trace_e1000e_irq_msi_notify(causes); + msi_notify(core->owner, 0); + } + } +} + +static void +e1000e_update_interrupt_state(E1000ECore *core) +{ + bool interrupts_pending; + bool is_msix = msix_enabled(core->owner); + + /* Set ICR[OTHER] for MSI-X */ + if (is_msix) { + if (core->mac[ICR] & core->mac[IMS] & E1000_ICR_OTHER_CAUSES) { + core->mac[ICR] |= E1000_ICR_OTHER; + trace_e1000e_irq_add_msi_other(core->mac[ICR]); + } + } + + e1000e_fix_icr_asserted(core); + + /* + * Make sure ICR and ICS registers have the same value. + * The spec says that the ICS register is write-only. However in practice, + * on real hardware ICS is readable, and for reads it has the same value as + * ICR (except that ICS does not have the clear on read behaviour of ICR). + * + * The VxWorks PRO/1000 driver uses this behaviour. + */ + core->mac[ICS] = core->mac[ICR]; + + interrupts_pending = (core->mac[IMS] & core->mac[ICR]) ? true : false; + + trace_e1000e_irq_pending_interrupts(core->mac[ICR] & core->mac[IMS], + core->mac[ICR], core->mac[IMS]); + + if (is_msix || msi_enabled(core->owner)) { + if (interrupts_pending) { + e1000e_send_msi(core, is_msix); + } + } else { + if (interrupts_pending) { + if (!e1000e_itr_should_postpone(core)) { + e1000e_raise_legacy_irq(core); + } + } else { + e1000e_lower_legacy_irq(core); + } + } +} + +static inline void +e1000e_set_interrupt_cause(E1000ECore *core, uint32_t val) +{ + trace_e1000e_irq_set_cause_entry(val, core->mac[ICR]); + + val |= e1000e_intmgr_collect_delayed_causes(core); + core->mac[ICR] |= val; + + trace_e1000e_irq_set_cause_exit(val, core->mac[ICR]); + + e1000e_update_interrupt_state(core); +} + +static inline void +e1000e_autoneg_timer(void *opaque) +{ + E1000ECore *core = opaque; + if (!qemu_get_queue(core->owner_nic)->link_down) { + e1000x_update_regs_on_autoneg_done(core->mac, core->phy[0]); + e1000e_update_flowctl_status(core); + /* signal link status change to the guest */ + e1000e_set_interrupt_cause(core, E1000_ICR_LSC); + } +} + +static inline uint16_t +e1000e_get_reg_index_with_offset(const uint16_t *mac_reg_access, hwaddr addr) +{ + uint16_t index = (addr & 0x1ffff) >> 2; + return index + (mac_reg_access[index] & 0xfffe); +} + +static const char e1000e_phy_regcap[E1000E_PHY_PAGES][0x20] = { + [0] = { + [PHY_CTRL] = PHY_ANYPAGE | PHY_RW, + [PHY_STATUS] = PHY_ANYPAGE | PHY_R, + [PHY_ID1] = PHY_ANYPAGE | PHY_R, + [PHY_ID2] = PHY_ANYPAGE | PHY_R, + [PHY_AUTONEG_ADV] = PHY_ANYPAGE | PHY_RW, + [PHY_LP_ABILITY] = PHY_ANYPAGE | PHY_R, + [PHY_AUTONEG_EXP] = PHY_ANYPAGE | PHY_R, + [PHY_NEXT_PAGE_TX] = PHY_ANYPAGE | PHY_RW, + [PHY_LP_NEXT_PAGE] = PHY_ANYPAGE | PHY_R, + [PHY_1000T_CTRL] = PHY_ANYPAGE | PHY_RW, + [PHY_1000T_STATUS] = PHY_ANYPAGE | PHY_R, + [PHY_EXT_STATUS] = PHY_ANYPAGE | PHY_R, + [PHY_PAGE] = PHY_ANYPAGE | PHY_RW, + + [PHY_COPPER_CTRL1] = PHY_RW, + [PHY_COPPER_STAT1] = PHY_R, + [PHY_COPPER_CTRL3] = PHY_RW, + [PHY_RX_ERR_CNTR] = PHY_R, + [PHY_OEM_BITS] = PHY_RW, + [PHY_BIAS_1] = PHY_RW, + [PHY_BIAS_2] = PHY_RW, + [PHY_COPPER_INT_ENABLE] = PHY_RW, + [PHY_COPPER_STAT2] = PHY_R, + [PHY_COPPER_CTRL2] = PHY_RW + }, + [2] = { + [PHY_MAC_CTRL1] = PHY_RW, + [PHY_MAC_INT_ENABLE] = PHY_RW, + [PHY_MAC_STAT] = PHY_R, + [PHY_MAC_CTRL2] = PHY_RW + }, + [3] = { + [PHY_LED_03_FUNC_CTRL1] = PHY_RW, + [PHY_LED_03_POL_CTRL] = PHY_RW, + [PHY_LED_TIMER_CTRL] = PHY_RW, + [PHY_LED_45_CTRL] = PHY_RW + }, + [5] = { + [PHY_1000T_SKEW] = PHY_R, + [PHY_1000T_SWAP] = PHY_R + }, + [6] = { + [PHY_CRC_COUNTERS] = PHY_R + } +}; + +static bool +e1000e_phy_reg_check_cap(E1000ECore *core, uint32_t addr, + char cap, uint8_t *page) +{ + *page = + (e1000e_phy_regcap[0][addr] & PHY_ANYPAGE) ? 0 + : core->phy[0][PHY_PAGE]; + + if (*page >= E1000E_PHY_PAGES) { + return false; + } + + return e1000e_phy_regcap[*page][addr] & cap; +} + +static void +e1000e_phy_reg_write(E1000ECore *core, uint8_t page, + uint32_t addr, uint16_t data) +{ + assert(page < E1000E_PHY_PAGES); + assert(addr < E1000E_PHY_PAGE_SIZE); + + if (e1000e_phyreg_writeops[page][addr]) { + e1000e_phyreg_writeops[page][addr](core, addr, data); + } else { + core->phy[page][addr] = data; + } +} + +static void +e1000e_set_mdic(E1000ECore *core, int index, uint32_t val) +{ + uint32_t data = val & E1000_MDIC_DATA_MASK; + uint32_t addr = ((val & E1000_MDIC_REG_MASK) >> E1000_MDIC_REG_SHIFT); + uint8_t page; + + if ((val & E1000_MDIC_PHY_MASK) >> E1000_MDIC_PHY_SHIFT != 1) { /* phy # */ + val = core->mac[MDIC] | E1000_MDIC_ERROR; + } else if (val & E1000_MDIC_OP_READ) { + if (!e1000e_phy_reg_check_cap(core, addr, PHY_R, &page)) { + trace_e1000e_core_mdic_read_unhandled(page, addr); + val |= E1000_MDIC_ERROR; + } else { + val = (val ^ data) | core->phy[page][addr]; + trace_e1000e_core_mdic_read(page, addr, val); + } + } else if (val & E1000_MDIC_OP_WRITE) { + if (!e1000e_phy_reg_check_cap(core, addr, PHY_W, &page)) { + trace_e1000e_core_mdic_write_unhandled(page, addr); + val |= E1000_MDIC_ERROR; + } else { + trace_e1000e_core_mdic_write(page, addr, data); + e1000e_phy_reg_write(core, page, addr, data); + } + } + core->mac[MDIC] = val | E1000_MDIC_READY; + + if (val & E1000_MDIC_INT_EN) { + e1000e_set_interrupt_cause(core, E1000_ICR_MDAC); + } +} + +static void +e1000e_set_rdt(E1000ECore *core, int index, uint32_t val) +{ + core->mac[index] = val & 0xffff; + trace_e1000e_rx_set_rdt(e1000e_mq_queue_idx(RDT0, index), val); + e1000e_start_recv(core); +} + +static void +e1000e_set_status(E1000ECore *core, int index, uint32_t val) +{ + if ((val & E1000_STATUS_PHYRA) == 0) { + core->mac[index] &= ~E1000_STATUS_PHYRA; + } +} + +static void +e1000e_set_ctrlext(E1000ECore *core, int index, uint32_t val) +{ + trace_e1000e_link_set_ext_params(!!(val & E1000_CTRL_EXT_ASDCHK), + !!(val & E1000_CTRL_EXT_SPD_BYPS)); + + /* Zero self-clearing bits */ + val &= ~(E1000_CTRL_EXT_ASDCHK | E1000_CTRL_EXT_EE_RST); + core->mac[CTRL_EXT] = val; +} + +static void +e1000e_set_pbaclr(E1000ECore *core, int index, uint32_t val) +{ + int i; + + core->mac[PBACLR] = val & E1000_PBACLR_VALID_MASK; + + if (msix_enabled(core->owner)) { + return; + } + + for (i = 0; i < E1000E_MSIX_VEC_NUM; i++) { + if (core->mac[PBACLR] & BIT(i)) { + msix_clr_pending(core->owner, i); + } + } +} + +static void +e1000e_set_fcrth(E1000ECore *core, int index, uint32_t val) +{ + core->mac[FCRTH] = val & 0xFFF8; +} + +static void +e1000e_set_fcrtl(E1000ECore *core, int index, uint32_t val) +{ + core->mac[FCRTL] = val & 0x8000FFF8; +} + +static inline void +e1000e_set_16bit(E1000ECore *core, int index, uint32_t val) +{ + core->mac[index] = val & 0xffff; +} + +static void +e1000e_set_12bit(E1000ECore *core, int index, uint32_t val) +{ + core->mac[index] = val & 0xfff; +} + +static void +e1000e_set_vet(E1000ECore *core, int index, uint32_t val) +{ + core->mac[VET] = val & 0xffff; + core->vet = le16_to_cpu(core->mac[VET]); + trace_e1000e_vlan_vet(core->vet); +} + +static void +e1000e_set_dlen(E1000ECore *core, int index, uint32_t val) +{ + core->mac[index] = val & E1000_XDLEN_MASK; +} + +static void +e1000e_set_dbal(E1000ECore *core, int index, uint32_t val) +{ + core->mac[index] = val & E1000_XDBAL_MASK; +} + +static void +e1000e_set_tctl(E1000ECore *core, int index, uint32_t val) +{ + E1000E_TxRing txr; + core->mac[index] = val; + + if (core->mac[TARC0] & E1000_TARC_ENABLE) { + e1000e_tx_ring_init(core, &txr, 0); + e1000e_start_xmit(core, &txr); + } + + if (core->mac[TARC1] & E1000_TARC_ENABLE) { + e1000e_tx_ring_init(core, &txr, 1); + e1000e_start_xmit(core, &txr); + } +} + +static void +e1000e_set_tdt(E1000ECore *core, int index, uint32_t val) +{ + E1000E_TxRing txr; + int qidx = e1000e_mq_queue_idx(TDT, index); + uint32_t tarc_reg = (qidx == 0) ? TARC0 : TARC1; + + core->mac[index] = val & 0xffff; + + if (core->mac[tarc_reg] & E1000_TARC_ENABLE) { + e1000e_tx_ring_init(core, &txr, qidx); + e1000e_start_xmit(core, &txr); + } +} + +static void +e1000e_set_ics(E1000ECore *core, int index, uint32_t val) +{ + trace_e1000e_irq_write_ics(val); + e1000e_set_interrupt_cause(core, val); +} + +static void +e1000e_set_icr(E1000ECore *core, int index, uint32_t val) +{ + if ((core->mac[ICR] & E1000_ICR_ASSERTED) && + (core->mac[CTRL_EXT] & E1000_CTRL_EXT_IAME)) { + trace_e1000e_irq_icr_process_iame(); + e1000e_clear_ims_bits(core, core->mac[IAM]); + } + + trace_e1000e_irq_icr_write(val, core->mac[ICR], core->mac[ICR] & ~val); + core->mac[ICR] &= ~val; + e1000e_update_interrupt_state(core); +} + +static void +e1000e_set_imc(E1000ECore *core, int index, uint32_t val) +{ + trace_e1000e_irq_ims_clear_set_imc(val); + e1000e_clear_ims_bits(core, val); + e1000e_update_interrupt_state(core); +} + +static void +e1000e_set_ims(E1000ECore *core, int index, uint32_t val) +{ + static const uint32_t ims_ext_mask = + E1000_IMS_RXQ0 | E1000_IMS_RXQ1 | + E1000_IMS_TXQ0 | E1000_IMS_TXQ1 | + E1000_IMS_OTHER; + + static const uint32_t ims_valid_mask = + E1000_IMS_TXDW | E1000_IMS_TXQE | E1000_IMS_LSC | + E1000_IMS_RXDMT0 | E1000_IMS_RXO | E1000_IMS_RXT0 | + E1000_IMS_MDAC | E1000_IMS_TXD_LOW | E1000_IMS_SRPD | + E1000_IMS_ACK | E1000_IMS_MNG | E1000_IMS_RXQ0 | + E1000_IMS_RXQ1 | E1000_IMS_TXQ0 | E1000_IMS_TXQ1 | + E1000_IMS_OTHER; + + uint32_t valid_val = val & ims_valid_mask; + + trace_e1000e_irq_set_ims(val, core->mac[IMS], core->mac[IMS] | valid_val); + core->mac[IMS] |= valid_val; + + if ((valid_val & ims_ext_mask) && + (core->mac[CTRL_EXT] & E1000_CTRL_EXT_PBA_CLR) && + msix_enabled(core->owner)) { + e1000e_msix_clear(core, valid_val); + } + + if ((valid_val == ims_valid_mask) && + (core->mac[CTRL_EXT] & E1000_CTRL_EXT_INT_TIMERS_CLEAR_ENA)) { + trace_e1000e_irq_fire_all_timers(val); + e1000e_intrmgr_fire_all_timers(core); + } + + e1000e_update_interrupt_state(core); +} + +static void +e1000e_set_rdtr(E1000ECore *core, int index, uint32_t val) +{ + e1000e_set_16bit(core, index, val); + + if ((val & E1000_RDTR_FPD) && (core->rdtr.running)) { + trace_e1000e_irq_rdtr_fpd_running(); + e1000e_intrmgr_fire_delayed_interrupts(core); + } else { + trace_e1000e_irq_rdtr_fpd_not_running(); + } +} + +static void +e1000e_set_tidv(E1000ECore *core, int index, uint32_t val) +{ + e1000e_set_16bit(core, index, val); + + if ((val & E1000_TIDV_FPD) && (core->tidv.running)) { + trace_e1000e_irq_tidv_fpd_running(); + e1000e_intrmgr_fire_delayed_interrupts(core); + } else { + trace_e1000e_irq_tidv_fpd_not_running(); + } +} + +static uint32_t +e1000e_mac_readreg(E1000ECore *core, int index) +{ + return core->mac[index]; +} + +static uint32_t +e1000e_mac_ics_read(E1000ECore *core, int index) +{ + trace_e1000e_irq_read_ics(core->mac[ICS]); + return core->mac[ICS]; +} + +static uint32_t +e1000e_mac_ims_read(E1000ECore *core, int index) +{ + trace_e1000e_irq_read_ims(core->mac[IMS]); + return core->mac[IMS]; +} + +#define E1000E_LOW_BITS_READ_FUNC(num) \ + static uint32_t \ + e1000e_mac_low##num##_read(E1000ECore *core, int index) \ + { \ + return core->mac[index] & (BIT(num) - 1); \ + } \ + +#define E1000E_LOW_BITS_READ(num) \ + e1000e_mac_low##num##_read + +E1000E_LOW_BITS_READ_FUNC(4); +E1000E_LOW_BITS_READ_FUNC(6); +E1000E_LOW_BITS_READ_FUNC(11); +E1000E_LOW_BITS_READ_FUNC(13); +E1000E_LOW_BITS_READ_FUNC(16); + +static uint32_t +e1000e_mac_swsm_read(E1000ECore *core, int index) +{ + uint32_t val = core->mac[SWSM]; + core->mac[SWSM] = val | 1; + return val; +} + +static uint32_t +e1000e_mac_itr_read(E1000ECore *core, int index) +{ + return core->itr_guest_value; +} + +static uint32_t +e1000e_mac_eitr_read(E1000ECore *core, int index) +{ + return core->eitr_guest_value[index - EITR]; +} + +static uint32_t +e1000e_mac_icr_read(E1000ECore *core, int index) +{ + uint32_t ret = core->mac[ICR]; + trace_e1000e_irq_icr_read_entry(ret); + + if (core->mac[IMS] == 0) { + trace_e1000e_irq_icr_clear_zero_ims(); + core->mac[ICR] = 0; + } + + if ((core->mac[ICR] & E1000_ICR_ASSERTED) && + (core->mac[CTRL_EXT] & E1000_CTRL_EXT_IAME)) { + trace_e1000e_irq_icr_clear_iame(); + core->mac[ICR] = 0; + trace_e1000e_irq_icr_process_iame(); + e1000e_clear_ims_bits(core, core->mac[IAM]); + } + + trace_e1000e_irq_icr_read_exit(core->mac[ICR]); + e1000e_update_interrupt_state(core); + return ret; +} + +static uint32_t +e1000e_mac_read_clr4(E1000ECore *core, int index) +{ + uint32_t ret = core->mac[index]; + + core->mac[index] = 0; + return ret; +} + +static uint32_t +e1000e_mac_read_clr8(E1000ECore *core, int index) +{ + uint32_t ret = core->mac[index]; + + core->mac[index] = 0; + core->mac[index - 1] = 0; + return ret; +} + +static uint32_t +e1000e_get_ctrl(E1000ECore *core, int index) +{ + uint32_t val = core->mac[CTRL]; + + trace_e1000e_link_read_params( + !!(val & E1000_CTRL_ASDE), + (val & E1000_CTRL_SPD_SEL) >> E1000_CTRL_SPD_SHIFT, + !!(val & E1000_CTRL_FRCSPD), + !!(val & E1000_CTRL_FRCDPX), + !!(val & E1000_CTRL_RFCE), + !!(val & E1000_CTRL_TFCE)); + + return val; +} + +static uint32_t +e1000e_get_status(E1000ECore *core, int index) +{ + uint32_t res = core->mac[STATUS]; + + if (!(core->mac[CTRL] & E1000_CTRL_GIO_MASTER_DISABLE)) { + res |= E1000_STATUS_GIO_MASTER_ENABLE; + } + + if (core->mac[CTRL] & E1000_CTRL_FRCDPX) { + res |= (core->mac[CTRL] & E1000_CTRL_FD) ? E1000_STATUS_FD : 0; + } else { + res |= E1000_STATUS_FD; + } + + if ((core->mac[CTRL] & E1000_CTRL_FRCSPD) || + (core->mac[CTRL_EXT] & E1000_CTRL_EXT_SPD_BYPS)) { + switch (core->mac[CTRL] & E1000_CTRL_SPD_SEL) { + case E1000_CTRL_SPD_10: + res |= E1000_STATUS_SPEED_10; + break; + case E1000_CTRL_SPD_100: + res |= E1000_STATUS_SPEED_100; + break; + case E1000_CTRL_SPD_1000: + default: + res |= E1000_STATUS_SPEED_1000; + break; + } + } else { + res |= E1000_STATUS_SPEED_1000; + } + + trace_e1000e_link_status( + !!(res & E1000_STATUS_LU), + !!(res & E1000_STATUS_FD), + (res & E1000_STATUS_SPEED_MASK) >> E1000_STATUS_SPEED_SHIFT, + (res & E1000_STATUS_ASDV) >> E1000_STATUS_ASDV_SHIFT); + + return res; +} + +static uint32_t +e1000e_get_tarc(E1000ECore *core, int index) +{ + return core->mac[index] & ((BIT(11) - 1) | + BIT(27) | + BIT(28) | + BIT(29) | + BIT(30)); +} + +static void +e1000e_mac_writereg(E1000ECore *core, int index, uint32_t val) +{ + core->mac[index] = val; +} + +static void +e1000e_mac_setmacaddr(E1000ECore *core, int index, uint32_t val) +{ + uint32_t macaddr[2]; + + core->mac[index] = val; + + macaddr[0] = cpu_to_le32(core->mac[RA]); + macaddr[1] = cpu_to_le32(core->mac[RA + 1]); + qemu_format_nic_info_str(qemu_get_queue(core->owner_nic), + (uint8_t *) macaddr); + + trace_e1000e_mac_set_sw(MAC_ARG(macaddr)); +} + +static void +e1000e_set_eecd(E1000ECore *core, int index, uint32_t val) +{ + static const uint32_t ro_bits = E1000_EECD_PRES | + E1000_EECD_AUTO_RD | + E1000_EECD_SIZE_EX_MASK; + + core->mac[EECD] = (core->mac[EECD] & ro_bits) | (val & ~ro_bits); +} + +static void +e1000e_set_eerd(E1000ECore *core, int index, uint32_t val) +{ + uint32_t addr = (val >> E1000_EERW_ADDR_SHIFT) & E1000_EERW_ADDR_MASK; + uint32_t flags = 0; + uint32_t data = 0; + + if ((addr < E1000E_EEPROM_SIZE) && (val & E1000_EERW_START)) { + data = core->eeprom[addr]; + flags = E1000_EERW_DONE; + } + + core->mac[EERD] = flags | + (addr << E1000_EERW_ADDR_SHIFT) | + (data << E1000_EERW_DATA_SHIFT); +} + +static void +e1000e_set_eewr(E1000ECore *core, int index, uint32_t val) +{ + uint32_t addr = (val >> E1000_EERW_ADDR_SHIFT) & E1000_EERW_ADDR_MASK; + uint32_t data = (val >> E1000_EERW_DATA_SHIFT) & E1000_EERW_DATA_MASK; + uint32_t flags = 0; + + if ((addr < E1000E_EEPROM_SIZE) && (val & E1000_EERW_START)) { + core->eeprom[addr] = data; + flags = E1000_EERW_DONE; + } + + core->mac[EERD] = flags | + (addr << E1000_EERW_ADDR_SHIFT) | + (data << E1000_EERW_DATA_SHIFT); +} + +static void +e1000e_set_rxdctl(E1000ECore *core, int index, uint32_t val) +{ + core->mac[RXDCTL] = core->mac[RXDCTL1] = val; +} + +static void +e1000e_set_itr(E1000ECore *core, int index, uint32_t val) +{ + uint32_t interval = val & 0xffff; + + trace_e1000e_irq_itr_set(val); + + core->itr_guest_value = interval; + core->mac[index] = MAX(interval, E1000E_MIN_XITR); +} + +static void +e1000e_set_eitr(E1000ECore *core, int index, uint32_t val) +{ + uint32_t interval = val & 0xffff; + uint32_t eitr_num = index - EITR; + + trace_e1000e_irq_eitr_set(eitr_num, val); + + core->eitr_guest_value[eitr_num] = interval; + core->mac[index] = MAX(interval, E1000E_MIN_XITR); +} + +static void +e1000e_set_psrctl(E1000ECore *core, int index, uint32_t val) +{ + if ((val & E1000_PSRCTL_BSIZE0_MASK) == 0) { + hw_error("e1000e: PSRCTL.BSIZE0 cannot be zero"); + } + + if ((val & E1000_PSRCTL_BSIZE1_MASK) == 0) { + hw_error("e1000e: PSRCTL.BSIZE1 cannot be zero"); + } + + core->mac[PSRCTL] = val; +} + +static void +e1000e_update_rx_offloads(E1000ECore *core) +{ + int cso_state = e1000e_rx_l4_cso_enabled(core); + + trace_e1000e_rx_set_cso(cso_state); + + if (core->has_vnet) { + qemu_set_offload(qemu_get_queue(core->owner_nic)->peer, + cso_state, 0, 0, 0, 0); + } +} + +static void +e1000e_set_rxcsum(E1000ECore *core, int index, uint32_t val) +{ + core->mac[RXCSUM] = val; + e1000e_update_rx_offloads(core); +} + +static void +e1000e_set_gcr(E1000ECore *core, int index, uint32_t val) +{ + uint32_t ro_bits = core->mac[GCR] & E1000_GCR_RO_BITS; + core->mac[GCR] = (val & ~E1000_GCR_RO_BITS) | ro_bits; +} + +#define e1000e_getreg(x) [x] = e1000e_mac_readreg +static uint32_t (*e1000e_macreg_readops[])(E1000ECore *, int) = { + e1000e_getreg(PBA), + e1000e_getreg(WUFC), + e1000e_getreg(MANC), + e1000e_getreg(TOTL), + e1000e_getreg(RDT0), + e1000e_getreg(RDBAH0), + e1000e_getreg(TDBAL1), + e1000e_getreg(RDLEN0), + e1000e_getreg(RDH1), + e1000e_getreg(LATECOL), + e1000e_getreg(SEC), + e1000e_getreg(XONTXC), + e1000e_getreg(WUS), + e1000e_getreg(GORCL), + e1000e_getreg(MGTPRC), + e1000e_getreg(EERD), + e1000e_getreg(EIAC), + e1000e_getreg(PSRCTL), + e1000e_getreg(MANC2H), + e1000e_getreg(RXCSUM), + e1000e_getreg(GSCL_3), + e1000e_getreg(GSCN_2), + e1000e_getreg(RSRPD), + e1000e_getreg(RDBAL1), + e1000e_getreg(FCAH), + e1000e_getreg(FCRTH), + e1000e_getreg(FLOP), + e1000e_getreg(FLASHT), + e1000e_getreg(RXSTMPH), + e1000e_getreg(TXSTMPL), + e1000e_getreg(TIMADJL), + e1000e_getreg(TXDCTL), + e1000e_getreg(RDH0), + e1000e_getreg(TDT1), + e1000e_getreg(TNCRS), + e1000e_getreg(RJC), + e1000e_getreg(IAM), + e1000e_getreg(GSCL_2), + e1000e_getreg(RDBAH1), + e1000e_getreg(FLSWDATA), + e1000e_getreg(RXSATRH), + e1000e_getreg(TIPG), + e1000e_getreg(FLMNGCTL), + e1000e_getreg(FLMNGCNT), + e1000e_getreg(TSYNCTXCTL), + e1000e_getreg(EXTCNF_SIZE), + e1000e_getreg(EXTCNF_CTRL), + e1000e_getreg(EEMNGDATA), + e1000e_getreg(CTRL_EXT), + e1000e_getreg(SYSTIMH), + e1000e_getreg(EEMNGCTL), + e1000e_getreg(FLMNGDATA), + e1000e_getreg(TSYNCRXCTL), + e1000e_getreg(TDH), + e1000e_getreg(LEDCTL), + e1000e_getreg(STATUS), + e1000e_getreg(TCTL), + e1000e_getreg(TDBAL), + e1000e_getreg(TDLEN), + e1000e_getreg(TDH1), + e1000e_getreg(RADV), + e1000e_getreg(ECOL), + e1000e_getreg(DC), + e1000e_getreg(RLEC), + e1000e_getreg(XOFFTXC), + e1000e_getreg(RFC), + e1000e_getreg(RNBC), + e1000e_getreg(MGTPTC), + e1000e_getreg(TIMINCA), + e1000e_getreg(RXCFGL), + e1000e_getreg(MFUTP01), + e1000e_getreg(FACTPS), + e1000e_getreg(GSCL_1), + e1000e_getreg(GSCN_0), + e1000e_getreg(GCR2), + e1000e_getreg(RDT1), + e1000e_getreg(PBACLR), + e1000e_getreg(FCTTV), + e1000e_getreg(EEWR), + e1000e_getreg(FLSWCTL), + e1000e_getreg(RXDCTL1), + e1000e_getreg(RXSATRL), + e1000e_getreg(SYSTIML), + e1000e_getreg(RXUDP), + e1000e_getreg(TORL), + e1000e_getreg(TDLEN1), + e1000e_getreg(MCC), + e1000e_getreg(WUC), + e1000e_getreg(EECD), + e1000e_getreg(MFUTP23), + e1000e_getreg(RAID), + e1000e_getreg(FCRTV), + e1000e_getreg(TXDCTL1), + e1000e_getreg(RCTL), + e1000e_getreg(TDT), + e1000e_getreg(MDIC), + e1000e_getreg(FCRUC), + e1000e_getreg(VET), + e1000e_getreg(RDBAL0), + e1000e_getreg(TDBAH1), + e1000e_getreg(RDTR), + e1000e_getreg(SCC), + e1000e_getreg(COLC), + e1000e_getreg(CEXTERR), + e1000e_getreg(XOFFRXC), + e1000e_getreg(IPAV), + e1000e_getreg(GOTCL), + e1000e_getreg(MGTPDC), + e1000e_getreg(GCR), + e1000e_getreg(IVAR), + e1000e_getreg(POEMB), + e1000e_getreg(MFVAL), + e1000e_getreg(FUNCTAG), + e1000e_getreg(GSCL_4), + e1000e_getreg(GSCN_3), + e1000e_getreg(MRQC), + e1000e_getreg(RDLEN1), + e1000e_getreg(FCT), + e1000e_getreg(FLA), + e1000e_getreg(FLOL), + e1000e_getreg(RXDCTL), + e1000e_getreg(RXSTMPL), + e1000e_getreg(TXSTMPH), + e1000e_getreg(TIMADJH), + e1000e_getreg(FCRTL), + e1000e_getreg(TDBAH), + e1000e_getreg(TADV), + e1000e_getreg(XONRXC), + e1000e_getreg(TSCTFC), + e1000e_getreg(RFCTL), + e1000e_getreg(GSCN_1), + e1000e_getreg(FCAL), + e1000e_getreg(FLSWCNT), + + [TOTH] = e1000e_mac_read_clr8, + [GOTCH] = e1000e_mac_read_clr8, + [PRC64] = e1000e_mac_read_clr4, + [PRC255] = e1000e_mac_read_clr4, + [PRC1023] = e1000e_mac_read_clr4, + [PTC64] = e1000e_mac_read_clr4, + [PTC255] = e1000e_mac_read_clr4, + [PTC1023] = e1000e_mac_read_clr4, + [GPRC] = e1000e_mac_read_clr4, + [TPT] = e1000e_mac_read_clr4, + [RUC] = e1000e_mac_read_clr4, + [BPRC] = e1000e_mac_read_clr4, + [MPTC] = e1000e_mac_read_clr4, + [IAC] = e1000e_mac_read_clr4, + [ICR] = e1000e_mac_icr_read, + [RDFH] = E1000E_LOW_BITS_READ(13), + [RDFHS] = E1000E_LOW_BITS_READ(13), + [RDFPC] = E1000E_LOW_BITS_READ(13), + [TDFH] = E1000E_LOW_BITS_READ(13), + [TDFHS] = E1000E_LOW_BITS_READ(13), + [STATUS] = e1000e_get_status, + [TARC0] = e1000e_get_tarc, + [PBS] = E1000E_LOW_BITS_READ(6), + [ICS] = e1000e_mac_ics_read, + [AIT] = E1000E_LOW_BITS_READ(16), + [TORH] = e1000e_mac_read_clr8, + [GORCH] = e1000e_mac_read_clr8, + [PRC127] = e1000e_mac_read_clr4, + [PRC511] = e1000e_mac_read_clr4, + [PRC1522] = e1000e_mac_read_clr4, + [PTC127] = e1000e_mac_read_clr4, + [PTC511] = e1000e_mac_read_clr4, + [PTC1522] = e1000e_mac_read_clr4, + [GPTC] = e1000e_mac_read_clr4, + [TPR] = e1000e_mac_read_clr4, + [ROC] = e1000e_mac_read_clr4, + [MPRC] = e1000e_mac_read_clr4, + [BPTC] = e1000e_mac_read_clr4, + [TSCTC] = e1000e_mac_read_clr4, + [ITR] = e1000e_mac_itr_read, + [RDFT] = E1000E_LOW_BITS_READ(13), + [RDFTS] = E1000E_LOW_BITS_READ(13), + [TDFPC] = E1000E_LOW_BITS_READ(13), + [TDFT] = E1000E_LOW_BITS_READ(13), + [TDFTS] = E1000E_LOW_BITS_READ(13), + [CTRL] = e1000e_get_ctrl, + [TARC1] = e1000e_get_tarc, + [SWSM] = e1000e_mac_swsm_read, + [IMS] = e1000e_mac_ims_read, + + [CRCERRS ... MPC] = e1000e_mac_readreg, + [IP6AT ... IP6AT + 3] = e1000e_mac_readreg, + [IP4AT ... IP4AT + 6] = e1000e_mac_readreg, + [RA ... RA + 31] = e1000e_mac_readreg, + [WUPM ... WUPM + 31] = e1000e_mac_readreg, + [MTA ... MTA + 127] = e1000e_mac_readreg, + [VFTA ... VFTA + 127] = e1000e_mac_readreg, + [FFMT ... FFMT + 254] = E1000E_LOW_BITS_READ(4), + [FFVT ... FFVT + 254] = e1000e_mac_readreg, + [MDEF ... MDEF + 7] = e1000e_mac_readreg, + [FFLT ... FFLT + 10] = E1000E_LOW_BITS_READ(11), + [FTFT ... FTFT + 254] = e1000e_mac_readreg, + [PBM ... PBM + 10239] = e1000e_mac_readreg, + [RETA ... RETA + 31] = e1000e_mac_readreg, + [RSSRK ... RSSRK + 31] = e1000e_mac_readreg, + [MAVTV0 ... MAVTV3] = e1000e_mac_readreg, + [EITR...EITR + E1000E_MSIX_VEC_NUM - 1] = e1000e_mac_eitr_read +}; +enum { E1000E_NREADOPS = ARRAY_SIZE(e1000e_macreg_readops) }; + +#define e1000e_putreg(x) [x] = e1000e_mac_writereg +static void (*e1000e_macreg_writeops[])(E1000ECore *, int, uint32_t) = { + e1000e_putreg(PBA), + e1000e_putreg(SWSM), + e1000e_putreg(WUFC), + e1000e_putreg(RDBAH1), + e1000e_putreg(TDBAH), + e1000e_putreg(TXDCTL), + e1000e_putreg(RDBAH0), + e1000e_putreg(LEDCTL), + e1000e_putreg(FCAL), + e1000e_putreg(FCRUC), + e1000e_putreg(AIT), + e1000e_putreg(TDFH), + e1000e_putreg(TDFT), + e1000e_putreg(TDFHS), + e1000e_putreg(TDFTS), + e1000e_putreg(TDFPC), + e1000e_putreg(WUC), + e1000e_putreg(WUS), + e1000e_putreg(RDFH), + e1000e_putreg(RDFT), + e1000e_putreg(RDFHS), + e1000e_putreg(RDFTS), + e1000e_putreg(RDFPC), + e1000e_putreg(IPAV), + e1000e_putreg(TDBAH1), + e1000e_putreg(TIMINCA), + e1000e_putreg(IAM), + e1000e_putreg(EIAC), + e1000e_putreg(IVAR), + e1000e_putreg(TARC0), + e1000e_putreg(TARC1), + e1000e_putreg(FLSWDATA), + e1000e_putreg(POEMB), + e1000e_putreg(PBS), + e1000e_putreg(MFUTP01), + e1000e_putreg(MFUTP23), + e1000e_putreg(MANC), + e1000e_putreg(MANC2H), + e1000e_putreg(MFVAL), + e1000e_putreg(EXTCNF_CTRL), + e1000e_putreg(FACTPS), + e1000e_putreg(FUNCTAG), + e1000e_putreg(GSCL_1), + e1000e_putreg(GSCL_2), + e1000e_putreg(GSCL_3), + e1000e_putreg(GSCL_4), + e1000e_putreg(GSCN_0), + e1000e_putreg(GSCN_1), + e1000e_putreg(GSCN_2), + e1000e_putreg(GSCN_3), + e1000e_putreg(GCR2), + e1000e_putreg(MRQC), + e1000e_putreg(FLOP), + e1000e_putreg(FLOL), + e1000e_putreg(FLSWCTL), + e1000e_putreg(FLSWCNT), + e1000e_putreg(FLA), + e1000e_putreg(RXDCTL1), + e1000e_putreg(TXDCTL1), + e1000e_putreg(TIPG), + e1000e_putreg(RXSTMPH), + e1000e_putreg(RXSTMPL), + e1000e_putreg(RXSATRL), + e1000e_putreg(RXSATRH), + e1000e_putreg(TXSTMPL), + e1000e_putreg(TXSTMPH), + e1000e_putreg(SYSTIML), + e1000e_putreg(SYSTIMH), + e1000e_putreg(TIMADJL), + e1000e_putreg(TIMADJH), + e1000e_putreg(RXUDP), + e1000e_putreg(RXCFGL), + e1000e_putreg(TSYNCRXCTL), + e1000e_putreg(TSYNCTXCTL), + e1000e_putreg(FLSWDATA), + e1000e_putreg(EXTCNF_SIZE), + e1000e_putreg(EEMNGCTL), + e1000e_putreg(RA), + + [TDH1] = e1000e_set_16bit, + [TDT1] = e1000e_set_tdt, + [TCTL] = e1000e_set_tctl, + [TDT] = e1000e_set_tdt, + [MDIC] = e1000e_set_mdic, + [ICS] = e1000e_set_ics, + [TDH] = e1000e_set_16bit, + [RDH0] = e1000e_set_16bit, + [RDT0] = e1000e_set_rdt, + [IMC] = e1000e_set_imc, + [IMS] = e1000e_set_ims, + [ICR] = e1000e_set_icr, + [EECD] = e1000e_set_eecd, + [RCTL] = e1000e_set_rx_control, + [CTRL] = e1000e_set_ctrl, + [RDTR] = e1000e_set_rdtr, + [RADV] = e1000e_set_16bit, + [TADV] = e1000e_set_16bit, + [ITR] = e1000e_set_itr, + [EERD] = e1000e_set_eerd, + [GCR] = e1000e_set_gcr, + [PSRCTL] = e1000e_set_psrctl, + [RXCSUM] = e1000e_set_rxcsum, + [RAID] = e1000e_set_16bit, + [RSRPD] = e1000e_set_12bit, + [TIDV] = e1000e_set_tidv, + [TDLEN1] = e1000e_set_dlen, + [TDLEN] = e1000e_set_dlen, + [RDLEN0] = e1000e_set_dlen, + [RDLEN1] = e1000e_set_dlen, + [TDBAL] = e1000e_set_dbal, + [TDBAL1] = e1000e_set_dbal, + [RDBAL0] = e1000e_set_dbal, + [RDBAL1] = e1000e_set_dbal, + [RDH1] = e1000e_set_16bit, + [RDT1] = e1000e_set_rdt, + [STATUS] = e1000e_set_status, + [PBACLR] = e1000e_set_pbaclr, + [CTRL_EXT] = e1000e_set_ctrlext, + [FCAH] = e1000e_set_16bit, + [FCT] = e1000e_set_16bit, + [FCTTV] = e1000e_set_16bit, + [FCRTV] = e1000e_set_16bit, + [FCRTH] = e1000e_set_fcrth, + [FCRTL] = e1000e_set_fcrtl, + [VET] = e1000e_set_vet, + [RXDCTL] = e1000e_set_rxdctl, + [FLASHT] = e1000e_set_16bit, + [EEWR] = e1000e_set_eewr, + [CTRL_DUP] = e1000e_set_ctrl, + [RFCTL] = e1000e_set_rfctl, + [RA + 1] = e1000e_mac_setmacaddr, + + [IP6AT ... IP6AT + 3] = e1000e_mac_writereg, + [IP4AT ... IP4AT + 6] = e1000e_mac_writereg, + [RA + 2 ... RA + 31] = e1000e_mac_writereg, + [WUPM ... WUPM + 31] = e1000e_mac_writereg, + [MTA ... MTA + 127] = e1000e_mac_writereg, + [VFTA ... VFTA + 127] = e1000e_mac_writereg, + [FFMT ... FFMT + 254] = e1000e_mac_writereg, + [FFVT ... FFVT + 254] = e1000e_mac_writereg, + [PBM ... PBM + 10239] = e1000e_mac_writereg, + [MDEF ... MDEF + 7] = e1000e_mac_writereg, + [FFLT ... FFLT + 10] = e1000e_mac_writereg, + [FTFT ... FTFT + 254] = e1000e_mac_writereg, + [RETA ... RETA + 31] = e1000e_mac_writereg, + [RSSRK ... RSSRK + 31] = e1000e_mac_writereg, + [MAVTV0 ... MAVTV3] = e1000e_mac_writereg, + [EITR...EITR + E1000E_MSIX_VEC_NUM - 1] = e1000e_set_eitr +}; +enum { E1000E_NWRITEOPS = ARRAY_SIZE(e1000e_macreg_writeops) }; + +enum { MAC_ACCESS_PARTIAL = 1 }; + +/* The array below combines alias offsets of the index values for the + * MAC registers that have aliases, with the indication of not fully + * implemented registers (lowest bit). This combination is possible + * because all of the offsets are even. */ +static const uint16_t mac_reg_access[E1000E_MAC_SIZE] = { + /* Alias index offsets */ + [FCRTL_A] = 0x07fe, [FCRTH_A] = 0x0802, + [RDH0_A] = 0x09bc, [RDT0_A] = 0x09bc, [RDTR_A] = 0x09c6, + [RDFH_A] = 0xe904, [RDFT_A] = 0xe904, + [TDH_A] = 0x0cf8, [TDT_A] = 0x0cf8, [TIDV_A] = 0x0cf8, + [TDFH_A] = 0xed00, [TDFT_A] = 0xed00, + [RA_A ... RA_A + 31] = 0x14f0, + [VFTA_A ... VFTA_A + 127] = 0x1400, + [RDBAL0_A ... RDLEN0_A] = 0x09bc, + [TDBAL_A ... TDLEN_A] = 0x0cf8, + /* Access options */ + [RDFH] = MAC_ACCESS_PARTIAL, [RDFT] = MAC_ACCESS_PARTIAL, + [RDFHS] = MAC_ACCESS_PARTIAL, [RDFTS] = MAC_ACCESS_PARTIAL, + [RDFPC] = MAC_ACCESS_PARTIAL, + [TDFH] = MAC_ACCESS_PARTIAL, [TDFT] = MAC_ACCESS_PARTIAL, + [TDFHS] = MAC_ACCESS_PARTIAL, [TDFTS] = MAC_ACCESS_PARTIAL, + [TDFPC] = MAC_ACCESS_PARTIAL, [EECD] = MAC_ACCESS_PARTIAL, + [PBM] = MAC_ACCESS_PARTIAL, [FLA] = MAC_ACCESS_PARTIAL, + [FCAL] = MAC_ACCESS_PARTIAL, [FCAH] = MAC_ACCESS_PARTIAL, + [FCT] = MAC_ACCESS_PARTIAL, [FCTTV] = MAC_ACCESS_PARTIAL, + [FCRTV] = MAC_ACCESS_PARTIAL, [FCRTL] = MAC_ACCESS_PARTIAL, + [FCRTH] = MAC_ACCESS_PARTIAL, [TXDCTL] = MAC_ACCESS_PARTIAL, + [TXDCTL1] = MAC_ACCESS_PARTIAL, + [MAVTV0 ... MAVTV3] = MAC_ACCESS_PARTIAL +}; + +void +e1000e_core_write(E1000ECore *core, hwaddr addr, uint64_t val, unsigned size) +{ + uint16_t index = e1000e_get_reg_index_with_offset(mac_reg_access, addr); + + if (index < E1000E_NWRITEOPS && e1000e_macreg_writeops[index]) { + if (mac_reg_access[index] & MAC_ACCESS_PARTIAL) { + trace_e1000e_wrn_regs_write_trivial(index << 2); + } + trace_e1000e_core_write(index << 2, size, val); + e1000e_macreg_writeops[index](core, index, val); + } else if (index < E1000E_NREADOPS && e1000e_macreg_readops[index]) { + trace_e1000e_wrn_regs_write_ro(index << 2, size, val); + } else { + trace_e1000e_wrn_regs_write_unknown(index << 2, size, val); + } +} + +uint64_t +e1000e_core_read(E1000ECore *core, hwaddr addr, unsigned size) +{ + uint64_t val; + uint16_t index = e1000e_get_reg_index_with_offset(mac_reg_access, addr); + + if (index < E1000E_NREADOPS && e1000e_macreg_readops[index]) { + if (mac_reg_access[index] & MAC_ACCESS_PARTIAL) { + trace_e1000e_wrn_regs_read_trivial(index << 2); + } + val = e1000e_macreg_readops[index](core, index); + trace_e1000e_core_read(index << 2, size, val); + return val; + } else { + trace_e1000e_wrn_regs_read_unknown(index << 2, size); + } + return 0; +} + +static inline void +e1000e_autoneg_pause(E1000ECore *core) +{ + timer_del(core->autoneg_timer); +} + +static void +e1000e_autoneg_resume(E1000ECore *core) +{ + if (e1000e_have_autoneg(core) && + !(core->phy[0][PHY_STATUS] & MII_SR_AUTONEG_COMPLETE)) { + qemu_get_queue(core->owner_nic)->link_down = false; + timer_mod(core->autoneg_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 500); + } +} + +static void +e1000e_vm_state_change(void *opaque, int running, RunState state) +{ + E1000ECore *core = opaque; + + if (running) { + trace_e1000e_vm_state_running(); + e1000e_intrmgr_resume(core); + e1000e_autoneg_resume(core); + } else { + trace_e1000e_vm_state_stopped(); + e1000e_autoneg_pause(core); + e1000e_intrmgr_pause(core); + } +} + +void +e1000e_core_pci_realize(E1000ECore *core, + const uint16_t *eeprom_templ, + uint32_t eeprom_size, + const uint8_t *macaddr) +{ + int i; + + core->autoneg_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, + e1000e_autoneg_timer, core); + e1000e_intrmgr_pci_realize(core); + + core->vmstate = + qemu_add_vm_change_state_handler(e1000e_vm_state_change, core); + + for (i = 0; i < E1000E_NUM_QUEUES; i++) { + net_tx_pkt_init(&core->tx[i].tx_pkt, core->owner, + E1000E_MAX_TX_FRAGS, core->has_vnet); + } + + net_rx_pkt_init(&core->rx_pkt, core->has_vnet); + + e1000x_core_prepare_eeprom(core->eeprom, + eeprom_templ, + eeprom_size, + PCI_DEVICE_GET_CLASS(core->owner)->device_id, + macaddr); + e1000e_update_rx_offloads(core); +} + +void +e1000e_core_pci_uninit(E1000ECore *core) +{ + int i; + + timer_del(core->autoneg_timer); + timer_free(core->autoneg_timer); + + e1000e_intrmgr_pci_unint(core); + + qemu_del_vm_change_state_handler(core->vmstate); + + for (i = 0; i < E1000E_NUM_QUEUES; i++) { + net_tx_pkt_reset(core->tx[i].tx_pkt); + net_tx_pkt_uninit(core->tx[i].tx_pkt); + } + + net_rx_pkt_uninit(core->rx_pkt); +} + +static const uint16_t +e1000e_phy_reg_init[E1000E_PHY_PAGES][E1000E_PHY_PAGE_SIZE] = { + [0] = { + [PHY_CTRL] = MII_CR_SPEED_SELECT_MSB | + MII_CR_FULL_DUPLEX | + MII_CR_AUTO_NEG_EN, + + [PHY_STATUS] = MII_SR_EXTENDED_CAPS | + MII_SR_LINK_STATUS | + MII_SR_AUTONEG_CAPS | + MII_SR_PREAMBLE_SUPPRESS | + MII_SR_EXTENDED_STATUS | + MII_SR_10T_HD_CAPS | + MII_SR_10T_FD_CAPS | + MII_SR_100X_HD_CAPS | + MII_SR_100X_FD_CAPS, + + [PHY_ID1] = 0x141, + [PHY_ID2] = E1000_PHY_ID2_82574x, + [PHY_AUTONEG_ADV] = 0xde1, + [PHY_LP_ABILITY] = 0x7e0, + [PHY_AUTONEG_EXP] = BIT(2), + [PHY_NEXT_PAGE_TX] = BIT(0) | BIT(13), + [PHY_1000T_CTRL] = BIT(8) | BIT(9) | BIT(10) | BIT(11), + [PHY_1000T_STATUS] = 0x3c00, + [PHY_EXT_STATUS] = BIT(12) | BIT(13), + + [PHY_COPPER_CTRL1] = BIT(5) | BIT(6) | BIT(8) | BIT(9) | + BIT(12) | BIT(13), + [PHY_COPPER_STAT1] = BIT(3) | BIT(10) | BIT(11) | BIT(13) | BIT(15) + }, + [2] = { + [PHY_MAC_CTRL1] = BIT(3) | BIT(7), + [PHY_MAC_CTRL2] = BIT(1) | BIT(2) | BIT(6) | BIT(12) + }, + [3] = { + [PHY_LED_TIMER_CTRL] = BIT(0) | BIT(2) | BIT(14) + } +}; + +static const uint32_t e1000e_mac_reg_init[] = { + [PBA] = 0x00140014, + [LEDCTL] = BIT(1) | BIT(8) | BIT(9) | BIT(15) | BIT(17) | BIT(18), + [EXTCNF_CTRL] = BIT(3), + [EEMNGCTL] = BIT(31), + [FLASHT] = 0x2, + [FLSWCTL] = BIT(30) | BIT(31), + [FLOL] = BIT(0), + [RXDCTL] = BIT(16), + [RXDCTL1] = BIT(16), + [TIPG] = 0x8 | (0x8 << 10) | (0x6 << 20), + [RXCFGL] = 0x88F7, + [RXUDP] = 0x319, + [CTRL] = E1000_CTRL_FD | E1000_CTRL_SWDPIN2 | E1000_CTRL_SWDPIN0 | + E1000_CTRL_SPD_1000 | E1000_CTRL_SLU | + E1000_CTRL_ADVD3WUC, + [STATUS] = E1000_STATUS_ASDV_1000 | E1000_STATUS_LU, + [PSRCTL] = (2 << E1000_PSRCTL_BSIZE0_SHIFT) | + (4 << E1000_PSRCTL_BSIZE1_SHIFT) | + (4 << E1000_PSRCTL_BSIZE2_SHIFT), + [TARC0] = 0x3 | E1000_TARC_ENABLE, + [TARC1] = 0x3 | E1000_TARC_ENABLE, + [EECD] = E1000_EECD_AUTO_RD | E1000_EECD_PRES, + [EERD] = E1000_EERW_DONE, + [EEWR] = E1000_EERW_DONE, + [GCR] = E1000_L0S_ADJUST | + E1000_L1_ENTRY_LATENCY_MSB | + E1000_L1_ENTRY_LATENCY_LSB, + [TDFH] = 0x600, + [TDFT] = 0x600, + [TDFHS] = 0x600, + [TDFTS] = 0x600, + [POEMB] = 0x30D, + [PBS] = 0x028, + [MANC] = E1000_MANC_DIS_IP_CHK_ARP, + [FACTPS] = E1000_FACTPS_LAN0_ON | 0x20000000, + [SWSM] = 1, + [RXCSUM] = E1000_RXCSUM_IPOFLD | E1000_RXCSUM_TUOFLD, + [ITR] = E1000E_MIN_XITR, + [EITR...EITR + E1000E_MSIX_VEC_NUM - 1] = E1000E_MIN_XITR, +}; + +void +e1000e_core_reset(E1000ECore *core) +{ + int i; + + timer_del(core->autoneg_timer); + + e1000e_intrmgr_reset(core); + + memset(core->phy, 0, sizeof core->phy); + memmove(core->phy, e1000e_phy_reg_init, sizeof e1000e_phy_reg_init); + memset(core->mac, 0, sizeof core->mac); + memmove(core->mac, e1000e_mac_reg_init, sizeof e1000e_mac_reg_init); + + core->rxbuf_min_shift = 1 + E1000_RING_DESC_LEN_SHIFT; + + if (qemu_get_queue(core->owner_nic)->link_down) { + e1000e_link_down(core); + } + + e1000x_reset_mac_addr(core->owner_nic, core->mac, core->permanent_mac); + + for (i = 0; i < ARRAY_SIZE(core->tx); i++) { + net_tx_pkt_reset(core->tx[i].tx_pkt); + memset(&core->tx[i].props, 0, sizeof(core->tx[i].props)); + core->tx[i].skip_cp = false; + } +} + +void e1000e_core_pre_save(E1000ECore *core) +{ + int i; + NetClientState *nc = qemu_get_queue(core->owner_nic); + + /* + * If link is down and auto-negotiation is supported and ongoing, + * complete auto-negotiation immediately. This allows us to look + * at MII_SR_AUTONEG_COMPLETE to infer link status on load. + */ + if (nc->link_down && e1000e_have_autoneg(core)) { + core->phy[0][PHY_STATUS] |= MII_SR_AUTONEG_COMPLETE; + e1000e_update_flowctl_status(core); + } + + for (i = 0; i < ARRAY_SIZE(core->tx); i++) { + if (net_tx_pkt_has_fragments(core->tx[i].tx_pkt)) { + core->tx[i].skip_cp = true; + } + } +} + +int +e1000e_core_post_load(E1000ECore *core) +{ + NetClientState *nc = qemu_get_queue(core->owner_nic); + + /* nc.link_down can't be migrated, so infer link_down according + * to link status bit in core.mac[STATUS]. + */ + nc->link_down = (core->mac[STATUS] & E1000_STATUS_LU) == 0; + + return 0; +} diff --git a/hw/net/e1000e_core.h b/hw/net/e1000e_core.h new file mode 100644 index 000000000..5f413a9e0 --- /dev/null +++ b/hw/net/e1000e_core.h @@ -0,0 +1,146 @@ +/* +* Core code for QEMU e1000e emulation +* +* Software developer's manuals: +* http://www.intel.com/content/dam/doc/datasheet/82574l-gbe-controller-datasheet.pdf +* +* Copyright (c) 2015 Ravello Systems LTD (http://ravellosystems.com) +* Developed by Daynix Computing LTD (http://www.daynix.com) +* +* Authors: +* Dmitry Fleytman <dmitry@daynix.com> +* Leonid Bloch <leonid@daynix.com> +* Yan Vugenfirer <yan@daynix.com> +* +* Based on work done by: +* Nir Peleg, Tutis Systems Ltd. for Qumranet Inc. +* Copyright (c) 2008 Qumranet +* Based on work done by: +* Copyright (c) 2007 Dan Aloni +* Copyright (c) 2004 Antony T Curtis +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ + +#define E1000E_PHY_PAGE_SIZE (0x20) +#define E1000E_PHY_PAGES (0x07) +#define E1000E_MAC_SIZE (0x8000) +#define E1000E_EEPROM_SIZE (64) +#define E1000E_MSIX_VEC_NUM (5) +#define E1000E_NUM_QUEUES (2) + +typedef struct E1000Core E1000ECore; + +enum { PHY_R = BIT(0), + PHY_W = BIT(1), + PHY_RW = PHY_R | PHY_W, + PHY_ANYPAGE = BIT(2) }; + +typedef struct E1000IntrDelayTimer_st { + QEMUTimer *timer; + bool running; + uint32_t delay_reg; + uint32_t delay_resolution_ns; + E1000ECore *core; +} E1000IntrDelayTimer; + +struct E1000Core { + uint32_t mac[E1000E_MAC_SIZE]; + uint16_t phy[E1000E_PHY_PAGES][E1000E_PHY_PAGE_SIZE]; + uint16_t eeprom[E1000E_EEPROM_SIZE]; + + uint32_t rxbuf_sizes[E1000_PSRCTL_BUFFS_PER_DESC]; + uint32_t rx_desc_buf_size; + uint32_t rxbuf_min_shift; + uint8_t rx_desc_len; + + QEMUTimer *autoneg_timer; + + struct e1000e_tx { + e1000x_txd_props props; + + bool skip_cp; + struct NetTxPkt *tx_pkt; + } tx[E1000E_NUM_QUEUES]; + + struct NetRxPkt *rx_pkt; + + bool has_vnet; + int max_queue_num; + + /* Interrupt moderation management */ + uint32_t delayed_causes; + + E1000IntrDelayTimer radv; + E1000IntrDelayTimer rdtr; + E1000IntrDelayTimer raid; + + E1000IntrDelayTimer tadv; + E1000IntrDelayTimer tidv; + + E1000IntrDelayTimer itr; + bool itr_intr_pending; + + E1000IntrDelayTimer eitr[E1000E_MSIX_VEC_NUM]; + bool eitr_intr_pending[E1000E_MSIX_VEC_NUM]; + + VMChangeStateEntry *vmstate; + + uint32_t itr_guest_value; + uint32_t eitr_guest_value[E1000E_MSIX_VEC_NUM]; + + uint16_t vet; + + uint8_t permanent_mac[ETH_ALEN]; + + NICState *owner_nic; + PCIDevice *owner; + void (*owner_start_recv)(PCIDevice *d); +}; + +void +e1000e_core_write(E1000ECore *core, hwaddr addr, uint64_t val, unsigned size); + +uint64_t +e1000e_core_read(E1000ECore *core, hwaddr addr, unsigned size); + +void +e1000e_core_pci_realize(E1000ECore *regs, + const uint16_t *eeprom_templ, + uint32_t eeprom_size, + const uint8_t *macaddr); + +void +e1000e_core_reset(E1000ECore *core); + +void +e1000e_core_pre_save(E1000ECore *core); + +int +e1000e_core_post_load(E1000ECore *core); + +void +e1000e_core_set_link_status(E1000ECore *core); + +void +e1000e_core_pci_uninit(E1000ECore *core); + +int +e1000e_can_receive(E1000ECore *core); + +ssize_t +e1000e_receive(E1000ECore *core, const uint8_t *buf, size_t size); + +ssize_t +e1000e_receive_iov(E1000ECore *core, const struct iovec *iov, int iovcnt); diff --git a/hw/net/e1000x_common.c b/hw/net/e1000x_common.c new file mode 100644 index 000000000..eb0e09713 --- /dev/null +++ b/hw/net/e1000x_common.c @@ -0,0 +1,267 @@ +/* +* QEMU e1000(e) emulation - shared code +* +* Copyright (c) 2008 Qumranet +* +* Based on work done by: +* Nir Peleg, Tutis Systems Ltd. for Qumranet Inc. +* Copyright (c) 2007 Dan Aloni +* Copyright (c) 2004 Antony T Curtis +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ + +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "hw/pci/pci.h" +#include "net/net.h" + +#include "e1000x_common.h" + +#include "trace.h" + +bool e1000x_rx_ready(PCIDevice *d, uint32_t *mac) +{ + bool link_up = mac[STATUS] & E1000_STATUS_LU; + bool rx_enabled = mac[RCTL] & E1000_RCTL_EN; + bool pci_master = d->config[PCI_COMMAND] & PCI_COMMAND_MASTER; + + if (!link_up || !rx_enabled || !pci_master) { + trace_e1000x_rx_can_recv_disabled(link_up, rx_enabled, pci_master); + return false; + } + + return true; +} + +bool e1000x_is_vlan_packet(const uint8_t *buf, uint16_t vet) +{ + uint16_t eth_proto = lduw_be_p(buf + 12); + bool res = (eth_proto == vet); + + trace_e1000x_vlan_is_vlan_pkt(res, eth_proto, vet); + + return res; +} + +bool e1000x_rx_group_filter(uint32_t *mac, const uint8_t *buf) +{ + static const int mta_shift[] = { 4, 3, 2, 0 }; + uint32_t f, ra[2], *rp, rctl = mac[RCTL]; + + for (rp = mac + RA; rp < mac + RA + 32; rp += 2) { + if (!(rp[1] & E1000_RAH_AV)) { + continue; + } + ra[0] = cpu_to_le32(rp[0]); + ra[1] = cpu_to_le32(rp[1]); + if (!memcmp(buf, (uint8_t *)ra, 6)) { + trace_e1000x_rx_flt_ucast_match((int)(rp - mac - RA) / 2, + MAC_ARG(buf)); + return true; + } + } + trace_e1000x_rx_flt_ucast_mismatch(MAC_ARG(buf)); + + f = mta_shift[(rctl >> E1000_RCTL_MO_SHIFT) & 3]; + f = (((buf[5] << 8) | buf[4]) >> f) & 0xfff; + if (mac[MTA + (f >> 5)] & (1 << (f & 0x1f))) { + e1000x_inc_reg_if_not_full(mac, MPRC); + return true; + } + + trace_e1000x_rx_flt_inexact_mismatch(MAC_ARG(buf), + (rctl >> E1000_RCTL_MO_SHIFT) & 3, + f >> 5, + mac[MTA + (f >> 5)]); + + return false; +} + +bool e1000x_hw_rx_enabled(uint32_t *mac) +{ + if (!(mac[STATUS] & E1000_STATUS_LU)) { + trace_e1000x_rx_link_down(mac[STATUS]); + return false; + } + + if (!(mac[RCTL] & E1000_RCTL_EN)) { + trace_e1000x_rx_disabled(mac[RCTL]); + return false; + } + + return true; +} + +bool e1000x_is_oversized(uint32_t *mac, size_t size) +{ + /* this is the size past which hardware will + drop packets when setting LPE=0 */ + static const int maximum_ethernet_vlan_size = 1522; + /* this is the size past which hardware will + drop packets when setting LPE=1 */ + static const int maximum_ethernet_lpe_size = 16384; + + if ((size > maximum_ethernet_lpe_size || + (size > maximum_ethernet_vlan_size + && !(mac[RCTL] & E1000_RCTL_LPE))) + && !(mac[RCTL] & E1000_RCTL_SBP)) { + e1000x_inc_reg_if_not_full(mac, ROC); + trace_e1000x_rx_oversized(size); + return true; + } + + return false; +} + +void e1000x_restart_autoneg(uint32_t *mac, uint16_t *phy, QEMUTimer *timer) +{ + e1000x_update_regs_on_link_down(mac, phy); + trace_e1000x_link_negotiation_start(); + timer_mod(timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 500); +} + +void e1000x_reset_mac_addr(NICState *nic, uint32_t *mac_regs, + uint8_t *mac_addr) +{ + int i; + + mac_regs[RA] = 0; + mac_regs[RA + 1] = E1000_RAH_AV; + for (i = 0; i < 4; i++) { + mac_regs[RA] |= mac_addr[i] << (8 * i); + mac_regs[RA + 1] |= + (i < 2) ? mac_addr[i + 4] << (8 * i) : 0; + } + + qemu_format_nic_info_str(qemu_get_queue(nic), mac_addr); + trace_e1000x_mac_indicate(MAC_ARG(mac_addr)); +} + +void e1000x_update_regs_on_autoneg_done(uint32_t *mac, uint16_t *phy) +{ + e1000x_update_regs_on_link_up(mac, phy); + phy[PHY_LP_ABILITY] |= MII_LPAR_LPACK; + phy[PHY_STATUS] |= MII_SR_AUTONEG_COMPLETE; + trace_e1000x_link_negotiation_done(); +} + +void +e1000x_core_prepare_eeprom(uint16_t *eeprom, + const uint16_t *templ, + uint32_t templ_size, + uint16_t dev_id, + const uint8_t *macaddr) +{ + uint16_t checksum = 0; + int i; + + memmove(eeprom, templ, templ_size); + + for (i = 0; i < 3; i++) { + eeprom[i] = (macaddr[2 * i + 1] << 8) | macaddr[2 * i]; + } + + eeprom[11] = eeprom[13] = dev_id; + + for (i = 0; i < EEPROM_CHECKSUM_REG; i++) { + checksum += eeprom[i]; + } + + checksum = (uint16_t) EEPROM_SUM - checksum; + + eeprom[EEPROM_CHECKSUM_REG] = checksum; +} + +uint32_t +e1000x_rxbufsize(uint32_t rctl) +{ + rctl &= E1000_RCTL_BSEX | E1000_RCTL_SZ_16384 | E1000_RCTL_SZ_8192 | + E1000_RCTL_SZ_4096 | E1000_RCTL_SZ_2048 | E1000_RCTL_SZ_1024 | + E1000_RCTL_SZ_512 | E1000_RCTL_SZ_256; + switch (rctl) { + case E1000_RCTL_BSEX | E1000_RCTL_SZ_16384: + return 16384; + case E1000_RCTL_BSEX | E1000_RCTL_SZ_8192: + return 8192; + case E1000_RCTL_BSEX | E1000_RCTL_SZ_4096: + return 4096; + case E1000_RCTL_SZ_1024: + return 1024; + case E1000_RCTL_SZ_512: + return 512; + case E1000_RCTL_SZ_256: + return 256; + } + return 2048; +} + +void +e1000x_update_rx_total_stats(uint32_t *mac, + size_t data_size, + size_t data_fcs_size) +{ + static const int PRCregs[6] = { PRC64, PRC127, PRC255, PRC511, + PRC1023, PRC1522 }; + + e1000x_increase_size_stats(mac, PRCregs, data_fcs_size); + e1000x_inc_reg_if_not_full(mac, TPR); + mac[GPRC] = mac[TPR]; + /* TOR - Total Octets Received: + * This register includes bytes received in a packet from the <Destination + * Address> field through the <CRC> field, inclusively. + * Always include FCS length (4) in size. + */ + e1000x_grow_8reg_if_not_full(mac, TORL, data_size + 4); + mac[GORCL] = mac[TORL]; + mac[GORCH] = mac[TORH]; +} + +void +e1000x_increase_size_stats(uint32_t *mac, const int *size_regs, int size) +{ + if (size > 1023) { + e1000x_inc_reg_if_not_full(mac, size_regs[5]); + } else if (size > 511) { + e1000x_inc_reg_if_not_full(mac, size_regs[4]); + } else if (size > 255) { + e1000x_inc_reg_if_not_full(mac, size_regs[3]); + } else if (size > 127) { + e1000x_inc_reg_if_not_full(mac, size_regs[2]); + } else if (size > 64) { + e1000x_inc_reg_if_not_full(mac, size_regs[1]); + } else if (size == 64) { + e1000x_inc_reg_if_not_full(mac, size_regs[0]); + } +} + +void +e1000x_read_tx_ctx_descr(struct e1000_context_desc *d, + e1000x_txd_props *props) +{ + uint32_t op = le32_to_cpu(d->cmd_and_length); + + props->ipcss = d->lower_setup.ip_fields.ipcss; + props->ipcso = d->lower_setup.ip_fields.ipcso; + props->ipcse = le16_to_cpu(d->lower_setup.ip_fields.ipcse); + props->tucss = d->upper_setup.tcp_fields.tucss; + props->tucso = d->upper_setup.tcp_fields.tucso; + props->tucse = le16_to_cpu(d->upper_setup.tcp_fields.tucse); + props->paylen = op & 0xfffff; + props->hdr_len = d->tcp_seg_setup.fields.hdr_len; + props->mss = le16_to_cpu(d->tcp_seg_setup.fields.mss); + props->ip = (op & E1000_TXD_CMD_IP) ? 1 : 0; + props->tcp = (op & E1000_TXD_CMD_TCP) ? 1 : 0; + props->tse = (op & E1000_TXD_CMD_TSE) ? 1 : 0; +} diff --git a/hw/net/e1000x_common.h b/hw/net/e1000x_common.h new file mode 100644 index 000000000..21bf28e0c --- /dev/null +++ b/hw/net/e1000x_common.h @@ -0,0 +1,213 @@ +/* +* QEMU e1000(e) emulation - shared code +* +* Copyright (c) 2008 Qumranet +* +* Based on work done by: +* Nir Peleg, Tutis Systems Ltd. for Qumranet Inc. +* Copyright (c) 2007 Dan Aloni +* Copyright (c) 2004 Antony T Curtis +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ + +#include "e1000_regs.h" + +#define defreg(x) x = (E1000_##x >> 2) +enum { + defreg(CTRL), defreg(EECD), defreg(EERD), defreg(GPRC), + defreg(GPTC), defreg(ICR), defreg(ICS), defreg(IMC), + defreg(IMS), defreg(LEDCTL), defreg(MANC), defreg(MDIC), + defreg(MPC), defreg(PBA), defreg(RCTL), defreg(RDBAH0), + defreg(RDBAL0), defreg(RDH0), defreg(RDLEN0), defreg(RDT0), + defreg(STATUS), defreg(SWSM), defreg(TCTL), defreg(TDBAH), + defreg(TDBAL), defreg(TDH), defreg(TDLEN), defreg(TDT), + defreg(TDLEN1), defreg(TDBAL1), defreg(TDBAH1), defreg(TDH1), + defreg(TDT1), defreg(TORH), defreg(TORL), defreg(TOTH), + defreg(TOTL), defreg(TPR), defreg(TPT), defreg(TXDCTL), + defreg(WUFC), defreg(RA), defreg(MTA), defreg(CRCERRS), + defreg(VFTA), defreg(VET), defreg(RDTR), defreg(RADV), + defreg(TADV), defreg(ITR), defreg(SCC), defreg(ECOL), + defreg(MCC), defreg(LATECOL), defreg(COLC), defreg(DC), + defreg(TNCRS), defreg(SEC), defreg(CEXTERR), defreg(RLEC), + defreg(XONRXC), defreg(XONTXC), defreg(XOFFRXC), defreg(XOFFTXC), + defreg(FCRUC), defreg(AIT), defreg(TDFH), defreg(TDFT), + defreg(TDFHS), defreg(TDFTS), defreg(TDFPC), defreg(WUC), + defreg(WUS), defreg(POEMB), defreg(PBS), defreg(RDFH), + defreg(RDFT), defreg(RDFHS), defreg(RDFTS), defreg(RDFPC), + defreg(PBM), defreg(IPAV), defreg(IP4AT), defreg(IP6AT), + defreg(WUPM), defreg(FFLT), defreg(FFMT), defreg(FFVT), + defreg(TARC0), defreg(TARC1), defreg(IAM), defreg(EXTCNF_CTRL), + defreg(GCR), defreg(TIMINCA), defreg(EIAC), defreg(CTRL_EXT), + defreg(IVAR), defreg(MFUTP01), defreg(MFUTP23), defreg(MANC2H), + defreg(MFVAL), defreg(MDEF), defreg(FACTPS), defreg(FTFT), + defreg(RUC), defreg(ROC), defreg(RFC), defreg(RJC), + defreg(PRC64), defreg(PRC127), defreg(PRC255), defreg(PRC511), + defreg(PRC1023), defreg(PRC1522), defreg(PTC64), defreg(PTC127), + defreg(PTC255), defreg(PTC511), defreg(PTC1023), defreg(PTC1522), + defreg(GORCL), defreg(GORCH), defreg(GOTCL), defreg(GOTCH), + defreg(RNBC), defreg(BPRC), defreg(MPRC), defreg(RFCTL), + defreg(PSRCTL), defreg(MPTC), defreg(BPTC), defreg(TSCTFC), + defreg(IAC), defreg(MGTPRC), defreg(MGTPDC), defreg(MGTPTC), + defreg(TSCTC), defreg(RXCSUM), defreg(FUNCTAG), defreg(GSCL_1), + defreg(GSCL_2), defreg(GSCL_3), defreg(GSCL_4), defreg(GSCN_0), + defreg(GSCN_1), defreg(GSCN_2), defreg(GSCN_3), defreg(GCR2), + defreg(RAID), defreg(RSRPD), defreg(TIDV), defreg(EITR), + defreg(MRQC), defreg(RETA), defreg(RSSRK), defreg(RDBAH1), + defreg(RDBAL1), defreg(RDLEN1), defreg(RDH1), defreg(RDT1), + defreg(PBACLR), defreg(FCAL), defreg(FCAH), defreg(FCT), + defreg(FCRTH), defreg(FCRTL), defreg(FCTTV), defreg(FCRTV), + defreg(FLA), defreg(EEWR), defreg(FLOP), defreg(FLOL), + defreg(FLSWCTL), defreg(FLSWCNT), defreg(RXDCTL), defreg(RXDCTL1), + defreg(MAVTV0), defreg(MAVTV1), defreg(MAVTV2), defreg(MAVTV3), + defreg(TXSTMPL), defreg(TXSTMPH), defreg(SYSTIML), defreg(SYSTIMH), + defreg(RXCFGL), defreg(RXUDP), defreg(TIMADJL), defreg(TIMADJH), + defreg(RXSTMPH), defreg(RXSTMPL), defreg(RXSATRL), defreg(RXSATRH), + defreg(FLASHT), defreg(TIPG), defreg(RDH), defreg(RDT), + defreg(RDLEN), defreg(RDBAH), defreg(RDBAL), + defreg(TXDCTL1), + defreg(FLSWDATA), + defreg(CTRL_DUP), + defreg(EXTCNF_SIZE), + defreg(EEMNGCTL), + defreg(EEMNGDATA), + defreg(FLMNGCTL), + defreg(FLMNGDATA), + defreg(FLMNGCNT), + defreg(TSYNCRXCTL), + defreg(TSYNCTXCTL), + + /* Aliases */ + defreg(RDH0_A), defreg(RDT0_A), defreg(RDTR_A), defreg(RDFH_A), + defreg(RDFT_A), defreg(TDH_A), defreg(TDT_A), defreg(TIDV_A), + defreg(TDFH_A), defreg(TDFT_A), defreg(RA_A), defreg(RDBAL0_A), + defreg(TDBAL_A), defreg(TDLEN_A), defreg(VFTA_A), defreg(RDLEN0_A), + defreg(FCRTL_A), defreg(FCRTH_A) +}; + +static inline void +e1000x_inc_reg_if_not_full(uint32_t *mac, int index) +{ + if (mac[index] != 0xffffffff) { + mac[index]++; + } +} + +static inline void +e1000x_grow_8reg_if_not_full(uint32_t *mac, int index, int size) +{ + uint64_t sum = mac[index] | (uint64_t)mac[index + 1] << 32; + + if (sum + size < sum) { + sum = ~0ULL; + } else { + sum += size; + } + mac[index] = sum; + mac[index + 1] = sum >> 32; +} + +static inline int +e1000x_vlan_enabled(uint32_t *mac) +{ + return ((mac[CTRL] & E1000_CTRL_VME) != 0); +} + +static inline int +e1000x_is_vlan_txd(uint32_t txd_lower) +{ + return ((txd_lower & E1000_TXD_CMD_VLE) != 0); +} + +static inline int +e1000x_vlan_rx_filter_enabled(uint32_t *mac) +{ + return ((mac[RCTL] & E1000_RCTL_VFE) != 0); +} + +static inline int +e1000x_fcs_len(uint32_t *mac) +{ + /* FCS aka Ethernet CRC-32. We don't get it from backends and can't + * fill it in, just pad descriptor length by 4 bytes unless guest + * told us to strip it off the packet. */ + return (mac[RCTL] & E1000_RCTL_SECRC) ? 0 : 4; +} + +static inline void +e1000x_update_regs_on_link_down(uint32_t *mac, uint16_t *phy) +{ + mac[STATUS] &= ~E1000_STATUS_LU; + phy[PHY_STATUS] &= ~MII_SR_LINK_STATUS; + phy[PHY_STATUS] &= ~MII_SR_AUTONEG_COMPLETE; + phy[PHY_LP_ABILITY] &= ~MII_LPAR_LPACK; +} + +static inline void +e1000x_update_regs_on_link_up(uint32_t *mac, uint16_t *phy) +{ + mac[STATUS] |= E1000_STATUS_LU; + phy[PHY_STATUS] |= MII_SR_LINK_STATUS; +} + +void e1000x_update_rx_total_stats(uint32_t *mac, + size_t data_size, + size_t data_fcs_size); + +void e1000x_core_prepare_eeprom(uint16_t *eeprom, + const uint16_t *templ, + uint32_t templ_size, + uint16_t dev_id, + const uint8_t *macaddr); + +uint32_t e1000x_rxbufsize(uint32_t rctl); + +bool e1000x_rx_ready(PCIDevice *d, uint32_t *mac); + +bool e1000x_is_vlan_packet(const uint8_t *buf, uint16_t vet); + +bool e1000x_rx_group_filter(uint32_t *mac, const uint8_t *buf); + +bool e1000x_hw_rx_enabled(uint32_t *mac); + +bool e1000x_is_oversized(uint32_t *mac, size_t size); + +void e1000x_restart_autoneg(uint32_t *mac, uint16_t *phy, QEMUTimer *timer); + +void e1000x_reset_mac_addr(NICState *nic, uint32_t *mac_regs, + uint8_t *mac_addr); + +void e1000x_update_regs_on_autoneg_done(uint32_t *mac, uint16_t *phy); + +void e1000x_increase_size_stats(uint32_t *mac, const int *size_regs, int size); + +typedef struct e1000x_txd_props { + unsigned char sum_needed; + uint8_t ipcss; + uint8_t ipcso; + uint16_t ipcse; + uint8_t tucss; + uint8_t tucso; + uint16_t tucse; + uint32_t paylen; + uint8_t hdr_len; + uint16_t mss; + int8_t ip; + int8_t tcp; + bool tse; + bool cptse; +} e1000x_txd_props; + +void e1000x_read_tx_ctx_descr(struct e1000_context_desc *d, + e1000x_txd_props *props); diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c index 9b4b9b59d..bab4dbfc9 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) @@ -1848,7 +1848,7 @@ static void pci_nic_uninit(PCIDevice *pci_dev) } static NetClientInfo net_eepro100_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, + .type = NET_CLIENT_DRIVER_NIC, .size = sizeof(NICState), .receive = nic_receive, }; diff --git a/hw/net/etraxfs_eth.c b/hw/net/etraxfs_eth.c index 05495ec40..efaa49faa 100644 --- a/hw/net/etraxfs_eth.c +++ b/hw/net/etraxfs_eth.c @@ -578,7 +578,7 @@ static const MemoryRegionOps eth_ops = { }; static NetClientInfo net_etraxfs_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, + .type = NET_CLIENT_DRIVER_NIC, .size = sizeof(NICState), .receive = eth_receive, .link_status_changed = eth_set_link, diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c index 1e35f7f8c..b5c777fbf 100644 --- a/hw/net/fsl_etsec/etsec.c +++ b/hw/net/fsl_etsec/etsec.c @@ -33,6 +33,7 @@ #include "hw/ptimer.h" #include "etsec.h" #include "registers.h" +#include "qemu/log.h" /* #define HEX_DUMP */ /* #define DEBUG_REGISTER */ @@ -370,7 +371,7 @@ static void etsec_set_link_status(NetClientState *nc) } static NetClientInfo net_etsec_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, + .type = NET_CLIENT_DRIVER_NIC, .size = sizeof(NICState), .receive = etsec_receive, .link_status_changed = etsec_set_link_status, diff --git a/hw/net/fsl_etsec/etsec.h b/hw/net/fsl_etsec/etsec.h index e7dc0a4b9..30c828e24 100644 --- a/hw/net/fsl_etsec/etsec.h +++ b/hw/net/fsl_etsec/etsec.h @@ -21,8 +21,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#ifndef _ETSEC_H_ -#define _ETSEC_H_ + +#ifndef ETSEC_H +#define ETSEC_H #include "hw/qdev.h" #include "hw/sysbus.h" @@ -173,4 +174,4 @@ void etsec_write_miim(eTSEC *etsec, void etsec_miim_link_status(eTSEC *etsec, NetClientState *nc); -#endif /* ! _ETSEC_H_ */ +#endif /* ETSEC_H */ diff --git a/hw/net/fsl_etsec/registers.h b/hw/net/fsl_etsec/registers.h index 6fb96842b..c4ed2b9d6 100644 --- a/hw/net/fsl_etsec/registers.h +++ b/hw/net/fsl_etsec/registers.h @@ -21,9 +21,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#ifndef _ETSEC_REGISTERS_H_ -#define _ETSEC_REGISTERS_H_ +#ifndef ETSEC_REGISTERS_H +#define ETSEC_REGISTERS_H enum eTSEC_Register_Access_Type { ACC_RW = 1, /* Read/Write */ @@ -316,4 +316,4 @@ extern const eTSEC_Register_Definition eTSEC_registers_def[]; #define TMR_ETTS2_H (0xEA8 / 4) #define TMR_ETTS2_L (0xEAC / 4) -#endif /* ! _ETSEC_REGISTERS_H_ */ +#endif /* ETSEC_REGISTERS_H */ diff --git a/hw/net/fsl_etsec/rings.c b/hw/net/fsl_etsec/rings.c index ed1de7da9..79d2f14dd 100644 --- a/hw/net/fsl_etsec/rings.c +++ b/hw/net/fsl_etsec/rings.c @@ -23,7 +23,7 @@ */ #include "qemu/osdep.h" #include "net/checksum.h" - +#include "qemu/log.h" #include "etsec.h" #include "registers.h" diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c index e60e3380e..1c415ab3b 100644 --- a/hw/net/imx_fec.c +++ b/hw/net/imx_fec.c @@ -24,6 +24,9 @@ #include "qemu/osdep.h" #include "hw/net/imx_fec.h" #include "sysemu/dma.h" +#include "qemu/log.h" +#include "net/checksum.h" +#include "net/eth.h" /* For crc32 */ #include <zlib.h> @@ -52,30 +55,153 @@ } \ } while (0) -static const VMStateDescription vmstate_imx_fec = { +static const char *imx_default_reg_name(IMXFECState *s, uint32_t index) +{ + static char tmp[20]; + sprintf(tmp, "index %d", index); + return tmp; +} + +static const char *imx_fec_reg_name(IMXFECState *s, uint32_t index) +{ + switch (index) { + case ENET_FRBR: + return "FRBR"; + case ENET_FRSR: + return "FRSR"; + case ENET_MIIGSK_CFGR: + return "MIIGSK_CFGR"; + case ENET_MIIGSK_ENR: + return "MIIGSK_ENR"; + default: + return imx_default_reg_name(s, index); + } +} + +static const char *imx_enet_reg_name(IMXFECState *s, uint32_t index) +{ + switch (index) { + case ENET_RSFL: + return "RSFL"; + case ENET_RSEM: + return "RSEM"; + case ENET_RAEM: + return "RAEM"; + case ENET_RAFL: + return "RAFL"; + case ENET_TSEM: + return "TSEM"; + case ENET_TAEM: + return "TAEM"; + case ENET_TAFL: + return "TAFL"; + case ENET_TIPG: + return "TIPG"; + case ENET_FTRL: + return "FTRL"; + case ENET_TACC: + return "TACC"; + case ENET_RACC: + return "RACC"; + case ENET_ATCR: + return "ATCR"; + case ENET_ATVR: + return "ATVR"; + case ENET_ATOFF: + return "ATOFF"; + case ENET_ATPER: + return "ATPER"; + case ENET_ATCOR: + return "ATCOR"; + case ENET_ATINC: + return "ATINC"; + case ENET_ATSTMP: + return "ATSTMP"; + case ENET_TGSR: + return "TGSR"; + case ENET_TCSR0: + return "TCSR0"; + case ENET_TCCR0: + return "TCCR0"; + case ENET_TCSR1: + return "TCSR1"; + case ENET_TCCR1: + return "TCCR1"; + case ENET_TCSR2: + return "TCSR2"; + case ENET_TCCR2: + return "TCCR2"; + case ENET_TCSR3: + return "TCSR3"; + case ENET_TCCR3: + return "TCCR3"; + default: + return imx_default_reg_name(s, index); + } +} + +static const char *imx_eth_reg_name(IMXFECState *s, uint32_t index) +{ + switch (index) { + case ENET_EIR: + return "EIR"; + case ENET_EIMR: + return "EIMR"; + case ENET_RDAR: + return "RDAR"; + case ENET_TDAR: + return "TDAR"; + case ENET_ECR: + return "ECR"; + case ENET_MMFR: + return "MMFR"; + case ENET_MSCR: + return "MSCR"; + case ENET_MIBC: + return "MIBC"; + case ENET_RCR: + return "RCR"; + case ENET_TCR: + return "TCR"; + case ENET_PALR: + return "PALR"; + case ENET_PAUR: + return "PAUR"; + case ENET_OPD: + return "OPD"; + case ENET_IAUR: + return "IAUR"; + case ENET_IALR: + return "IALR"; + case ENET_GAUR: + return "GAUR"; + case ENET_GALR: + return "GALR"; + case ENET_TFWR: + return "TFWR"; + case ENET_RDSR: + return "RDSR"; + case ENET_TDSR: + return "TDSR"; + case ENET_MRBR: + return "MRBR"; + default: + if (s->is_fec) { + return imx_fec_reg_name(s, index); + } else { + return imx_enet_reg_name(s, index); + } + } +} + +static const VMStateDescription vmstate_imx_eth = { .name = TYPE_IMX_FEC, - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (VMStateField[]) { - VMSTATE_UINT32(irq_state, IMXFECState), - VMSTATE_UINT32(eir, IMXFECState), - VMSTATE_UINT32(eimr, IMXFECState), - VMSTATE_UINT32(rx_enabled, IMXFECState), + VMSTATE_UINT32_ARRAY(regs, IMXFECState, ENET_MAX), VMSTATE_UINT32(rx_descriptor, IMXFECState), VMSTATE_UINT32(tx_descriptor, IMXFECState), - VMSTATE_UINT32(ecr, IMXFECState), - VMSTATE_UINT32(mmfr, IMXFECState), - VMSTATE_UINT32(mscr, IMXFECState), - VMSTATE_UINT32(mibc, IMXFECState), - VMSTATE_UINT32(rcr, IMXFECState), - VMSTATE_UINT32(tcr, IMXFECState), - VMSTATE_UINT32(tfwr, IMXFECState), - VMSTATE_UINT32(frsr, IMXFECState), - VMSTATE_UINT32(erdsr, IMXFECState), - VMSTATE_UINT32(etdsr, IMXFECState), - VMSTATE_UINT32(emrbr, IMXFECState), - VMSTATE_UINT32(miigsk_cfgr, IMXFECState), - VMSTATE_UINT32(miigsk_enr, IMXFECState), VMSTATE_UINT32(phy_status, IMXFECState), VMSTATE_UINT32(phy_control, IMXFECState), @@ -94,7 +220,7 @@ static const VMStateDescription vmstate_imx_fec = { #define PHY_INT_PARFAULT (1 << 2) #define PHY_INT_AUTONEG_PAGE (1 << 1) -static void imx_fec_update(IMXFECState *s); +static void imx_eth_update(IMXFECState *s); /* * The MII phy could raise a GPIO to the processor which in turn @@ -104,7 +230,7 @@ static void imx_fec_update(IMXFECState *s); */ static void phy_update_irq(IMXFECState *s) { - imx_fec_update(s); + imx_eth_update(s); } static void phy_update_link(IMXFECState *s) @@ -123,7 +249,7 @@ static void phy_update_link(IMXFECState *s) phy_update_irq(s); } -static void imx_fec_set_link(NetClientState *nc) +static void imx_eth_set_link(NetClientState *nc) { phy_update_link(IMX_FEC(qemu_get_nic_opaque(nc))); } @@ -249,23 +375,35 @@ static void imx_fec_write_bd(IMXFECBufDesc *bd, dma_addr_t addr) dma_memory_write(&address_space_memory, addr, bd, sizeof(*bd)); } -static void imx_fec_update(IMXFECState *s) +static void imx_enet_read_bd(IMXENETBufDesc *bd, dma_addr_t addr) { - uint32_t active; - uint32_t changed; + dma_memory_read(&address_space_memory, addr, bd, sizeof(*bd)); +} - active = s->eir & s->eimr; - changed = active ^ s->irq_state; - if (changed) { - qemu_set_irq(s->irq, active); +static void imx_enet_write_bd(IMXENETBufDesc *bd, dma_addr_t addr) +{ + dma_memory_write(&address_space_memory, addr, bd, sizeof(*bd)); +} + +static void imx_eth_update(IMXFECState *s) +{ + if (s->regs[ENET_EIR] & s->regs[ENET_EIMR] & ENET_INT_TS_TIMER) { + qemu_set_irq(s->irq[1], 1); + } else { + qemu_set_irq(s->irq[1], 0); + } + + if (s->regs[ENET_EIR] & s->regs[ENET_EIMR] & ENET_INT_MAC) { + qemu_set_irq(s->irq[0], 1); + } else { + qemu_set_irq(s->irq[0], 0); } - s->irq_state = active; } static void imx_fec_do_tx(IMXFECState *s) { int frame_size = 0; - uint8_t frame[FEC_MAX_FRAME_SIZE]; + uint8_t frame[ENET_MAX_FRAME_SIZE]; uint8_t *ptr = frame; uint32_t addr = s->tx_descriptor; @@ -276,272 +414,521 @@ static void imx_fec_do_tx(IMXFECState *s) imx_fec_read_bd(&bd, addr); FEC_PRINTF("tx_bd %x flags %04x len %d data %08x\n", addr, bd.flags, bd.length, bd.data); - if ((bd.flags & FEC_BD_R) == 0) { + if ((bd.flags & ENET_BD_R) == 0) { /* Run out of descriptors to transmit. */ + FEC_PRINTF("tx_bd ran out of descriptors to transmit\n"); break; } len = bd.length; - if (frame_size + len > FEC_MAX_FRAME_SIZE) { - len = FEC_MAX_FRAME_SIZE - frame_size; - s->eir |= FEC_INT_BABT; + if (frame_size + len > ENET_MAX_FRAME_SIZE) { + len = ENET_MAX_FRAME_SIZE - frame_size; + s->regs[ENET_EIR] |= ENET_INT_BABT; } dma_memory_read(&address_space_memory, bd.data, ptr, len); ptr += len; frame_size += len; - if (bd.flags & FEC_BD_L) { + if (bd.flags & ENET_BD_L) { /* Last buffer in frame. */ qemu_send_packet(qemu_get_queue(s->nic), frame, len); ptr = frame; frame_size = 0; - s->eir |= FEC_INT_TXF; + s->regs[ENET_EIR] |= ENET_INT_TXF; } - s->eir |= FEC_INT_TXB; - bd.flags &= ~FEC_BD_R; + s->regs[ENET_EIR] |= ENET_INT_TXB; + bd.flags &= ~ENET_BD_R; /* Write back the modified descriptor. */ imx_fec_write_bd(&bd, addr); /* Advance to the next descriptor. */ - if ((bd.flags & FEC_BD_W) != 0) { - addr = s->etdsr; + if ((bd.flags & ENET_BD_W) != 0) { + addr = s->regs[ENET_TDSR]; } else { - addr += 8; + addr += sizeof(bd); } } s->tx_descriptor = addr; - imx_fec_update(s); + imx_eth_update(s); } -static void imx_fec_enable_rx(IMXFECState *s) +static void imx_enet_do_tx(IMXFECState *s) +{ + int frame_size = 0; + uint8_t frame[ENET_MAX_FRAME_SIZE]; + uint8_t *ptr = frame; + uint32_t addr = s->tx_descriptor; + + while (1) { + IMXENETBufDesc bd; + int len; + + imx_enet_read_bd(&bd, addr); + FEC_PRINTF("tx_bd %x flags %04x len %d data %08x option %04x " + "status %04x\n", addr, bd.flags, bd.length, bd.data, + bd.option, bd.status); + if ((bd.flags & ENET_BD_R) == 0) { + /* Run out of descriptors to transmit. */ + break; + } + len = bd.length; + if (frame_size + len > ENET_MAX_FRAME_SIZE) { + len = ENET_MAX_FRAME_SIZE - frame_size; + s->regs[ENET_EIR] |= ENET_INT_BABT; + } + dma_memory_read(&address_space_memory, bd.data, ptr, len); + ptr += len; + frame_size += len; + if (bd.flags & ENET_BD_L) { + if (bd.option & ENET_BD_PINS) { + struct ip_header *ip_hd = PKT_GET_IP_HDR(frame); + if (IP_HEADER_VERSION(ip_hd) == 4) { + net_checksum_calculate(frame, frame_size); + } + } + if (bd.option & ENET_BD_IINS) { + struct ip_header *ip_hd = PKT_GET_IP_HDR(frame); + /* We compute checksum only for IPv4 frames */ + if (IP_HEADER_VERSION(ip_hd) == 4) { + uint16_t csum; + ip_hd->ip_sum = 0; + csum = net_raw_checksum((uint8_t *)ip_hd, sizeof(*ip_hd)); + ip_hd->ip_sum = cpu_to_be16(csum); + } + } + /* Last buffer in frame. */ + qemu_send_packet(qemu_get_queue(s->nic), frame, len); + ptr = frame; + frame_size = 0; + if (bd.option & ENET_BD_TX_INT) { + s->regs[ENET_EIR] |= ENET_INT_TXF; + } + } + if (bd.option & ENET_BD_TX_INT) { + s->regs[ENET_EIR] |= ENET_INT_TXB; + } + bd.flags &= ~ENET_BD_R; + /* Write back the modified descriptor. */ + imx_enet_write_bd(&bd, addr); + /* Advance to the next descriptor. */ + if ((bd.flags & ENET_BD_W) != 0) { + addr = s->regs[ENET_TDSR]; + } else { + addr += sizeof(bd); + } + } + + s->tx_descriptor = addr; + + imx_eth_update(s); +} + +static void imx_eth_do_tx(IMXFECState *s) +{ + if (!s->is_fec && (s->regs[ENET_ECR] & ENET_ECR_EN1588)) { + imx_enet_do_tx(s); + } else { + imx_fec_do_tx(s); + } +} + +static void imx_eth_enable_rx(IMXFECState *s) { IMXFECBufDesc bd; - uint32_t tmp; + bool tmp; imx_fec_read_bd(&bd, s->rx_descriptor); - tmp = ((bd.flags & FEC_BD_E) != 0); + tmp = ((bd.flags & ENET_BD_E) != 0); if (!tmp) { FEC_PRINTF("RX buffer full\n"); - } else if (!s->rx_enabled) { + } else if (!s->regs[ENET_RDAR]) { qemu_flush_queued_packets(qemu_get_queue(s->nic)); } - s->rx_enabled = tmp; + s->regs[ENET_RDAR] = tmp ? ENET_RDAR_RDAR : 0; } -static void imx_fec_reset(DeviceState *d) +static void imx_eth_reset(DeviceState *d) { IMXFECState *s = IMX_FEC(d); - /* Reset the FEC */ - s->eir = 0; - s->eimr = 0; - s->rx_enabled = 0; - s->ecr = 0; - s->mscr = 0; - s->mibc = 0xc0000000; - s->rcr = 0x05ee0001; - s->tcr = 0; - s->tfwr = 0; - s->frsr = 0x500; - s->miigsk_cfgr = 0; - s->miigsk_enr = 0x6; + /* Reset the Device */ + memset(s->regs, 0, sizeof(s->regs)); + s->regs[ENET_ECR] = 0xf0000000; + s->regs[ENET_MIBC] = 0xc0000000; + s->regs[ENET_RCR] = 0x05ee0001; + s->regs[ENET_OPD] = 0x00010000; + + s->regs[ENET_PALR] = (s->conf.macaddr.a[0] << 24) + | (s->conf.macaddr.a[1] << 16) + | (s->conf.macaddr.a[2] << 8) + | s->conf.macaddr.a[3]; + s->regs[ENET_PAUR] = (s->conf.macaddr.a[4] << 24) + | (s->conf.macaddr.a[5] << 16) + | 0x8808; + + if (s->is_fec) { + s->regs[ENET_FRBR] = 0x00000600; + s->regs[ENET_FRSR] = 0x00000500; + s->regs[ENET_MIIGSK_ENR] = 0x00000006; + } else { + s->regs[ENET_RAEM] = 0x00000004; + s->regs[ENET_RAFL] = 0x00000004; + s->regs[ENET_TAEM] = 0x00000004; + s->regs[ENET_TAFL] = 0x00000008; + s->regs[ENET_TIPG] = 0x0000000c; + s->regs[ENET_FTRL] = 0x000007ff; + s->regs[ENET_ATPER] = 0x3b9aca00; + } + + s->rx_descriptor = 0; + s->tx_descriptor = 0; /* We also reset the PHY */ phy_reset(s); } -static uint64_t imx_fec_read(void *opaque, hwaddr addr, unsigned size) +static uint32_t imx_default_read(IMXFECState *s, uint32_t index) +{ + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + PRIx32 "\n", TYPE_IMX_FEC, __func__, index * 4); + return 0; +} + +static uint32_t imx_fec_read(IMXFECState *s, uint32_t index) +{ + switch (index) { + case ENET_FRBR: + case ENET_FRSR: + case ENET_MIIGSK_CFGR: + case ENET_MIIGSK_ENR: + return s->regs[index]; + default: + return imx_default_read(s, index); + } +} + +static uint32_t imx_enet_read(IMXFECState *s, uint32_t index) { + switch (index) { + case ENET_RSFL: + case ENET_RSEM: + case ENET_RAEM: + case ENET_RAFL: + case ENET_TSEM: + case ENET_TAEM: + case ENET_TAFL: + case ENET_TIPG: + case ENET_FTRL: + case ENET_TACC: + case ENET_RACC: + case ENET_ATCR: + case ENET_ATVR: + case ENET_ATOFF: + case ENET_ATPER: + case ENET_ATCOR: + case ENET_ATINC: + case ENET_ATSTMP: + case ENET_TGSR: + case ENET_TCSR0: + case ENET_TCCR0: + case ENET_TCSR1: + case ENET_TCCR1: + case ENET_TCSR2: + case ENET_TCCR2: + case ENET_TCSR3: + case ENET_TCCR3: + return s->regs[index]; + default: + return imx_default_read(s, index); + } +} + +static uint64_t imx_eth_read(void *opaque, hwaddr offset, unsigned size) +{ + uint32_t value = 0; IMXFECState *s = IMX_FEC(opaque); + uint32_t index = offset >> 2; + + switch (index) { + case ENET_EIR: + case ENET_EIMR: + case ENET_RDAR: + case ENET_TDAR: + case ENET_ECR: + case ENET_MMFR: + case ENET_MSCR: + case ENET_MIBC: + case ENET_RCR: + case ENET_TCR: + case ENET_PALR: + case ENET_PAUR: + case ENET_OPD: + case ENET_IAUR: + case ENET_IALR: + case ENET_GAUR: + case ENET_GALR: + case ENET_TFWR: + case ENET_RDSR: + case ENET_TDSR: + case ENET_MRBR: + value = s->regs[index]; + break; + default: + if (s->is_fec) { + value = imx_fec_read(s, index); + } else { + value = imx_enet_read(s, index); + } + break; + } - FEC_PRINTF("reading from @ 0x%" HWADDR_PRIx "\n", addr); - - switch (addr & 0x3ff) { - case 0x004: - return s->eir; - case 0x008: - return s->eimr; - case 0x010: - return s->rx_enabled ? (1 << 24) : 0; /* RDAR */ - case 0x014: - return 0; /* TDAR */ - case 0x024: - return s->ecr; - case 0x040: - return s->mmfr; - case 0x044: - return s->mscr; - case 0x064: - return s->mibc; /* MIBC */ - case 0x084: - return s->rcr; - case 0x0c4: - return s->tcr; - case 0x0e4: /* PALR */ - return (s->conf.macaddr.a[0] << 24) - | (s->conf.macaddr.a[1] << 16) - | (s->conf.macaddr.a[2] << 8) - | s->conf.macaddr.a[3]; - break; - case 0x0e8: /* PAUR */ - return (s->conf.macaddr.a[4] << 24) - | (s->conf.macaddr.a[5] << 16) - | 0x8808; - case 0x0ec: - return 0x10000; /* OPD */ - case 0x118: - return 0; - case 0x11c: - return 0; - case 0x120: - return 0; - case 0x124: - return 0; - case 0x144: - return s->tfwr; - case 0x14c: - return 0x600; - case 0x150: - return s->frsr; - case 0x180: - return s->erdsr; - case 0x184: - return s->etdsr; - case 0x188: - return s->emrbr; - case 0x300: - return s->miigsk_cfgr; - case 0x308: - return s->miigsk_enr; + FEC_PRINTF("reg[%s] => 0x%" PRIx32 "\n", imx_eth_reg_name(s, index), + value); + + return value; +} + +static void imx_default_write(IMXFECState *s, uint32_t index, uint32_t value) +{ + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%" + PRIx32 "\n", TYPE_IMX_FEC, __func__, index * 4); + return; +} + +static void imx_fec_write(IMXFECState *s, uint32_t index, uint32_t value) +{ + switch (index) { + case ENET_FRBR: + /* FRBR is read only */ + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Register FRBR is read only\n", + TYPE_IMX_FEC, __func__); + break; + case ENET_FRSR: + s->regs[index] = (value & 0x000003fc) | 0x00000400; + break; + case ENET_MIIGSK_CFGR: + s->regs[index] = value & 0x00000053; + break; + case ENET_MIIGSK_ENR: + s->regs[index] = (value & 0x00000002) ? 0x00000006 : 0; + break; default: - qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%" - HWADDR_PRIx "\n", TYPE_IMX_FEC, __func__, addr); - return 0; + imx_default_write(s, index, value); + break; } } -static void imx_fec_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) +static void imx_enet_write(IMXFECState *s, uint32_t index, uint32_t value) +{ + switch (index) { + case ENET_RSFL: + case ENET_RSEM: + case ENET_RAEM: + case ENET_RAFL: + case ENET_TSEM: + case ENET_TAEM: + case ENET_TAFL: + s->regs[index] = value & 0x000001ff; + break; + case ENET_TIPG: + s->regs[index] = value & 0x0000001f; + break; + case ENET_FTRL: + s->regs[index] = value & 0x00003fff; + break; + case ENET_TACC: + s->regs[index] = value & 0x00000019; + break; + case ENET_RACC: + s->regs[index] = value & 0x000000C7; + break; + case ENET_ATCR: + s->regs[index] = value & 0x00002a9d; + break; + case ENET_ATVR: + case ENET_ATOFF: + case ENET_ATPER: + s->regs[index] = value; + break; + case ENET_ATSTMP: + /* ATSTMP is read only */ + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Register ATSTMP is read only\n", + TYPE_IMX_FEC, __func__); + break; + case ENET_ATCOR: + s->regs[index] = value & 0x7fffffff; + break; + case ENET_ATINC: + s->regs[index] = value & 0x00007f7f; + break; + case ENET_TGSR: + /* implement clear timer flag */ + value = value & 0x0000000f; + break; + case ENET_TCSR0: + case ENET_TCSR1: + case ENET_TCSR2: + case ENET_TCSR3: + value = value & 0x000000fd; + break; + case ENET_TCCR0: + case ENET_TCCR1: + case ENET_TCCR2: + case ENET_TCCR3: + s->regs[index] = value; + break; + default: + imx_default_write(s, index, value); + break; + } +} + +static void imx_eth_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) { IMXFECState *s = IMX_FEC(opaque); + uint32_t index = offset >> 2; - FEC_PRINTF("writing 0x%08x @ 0x%" HWADDR_PRIx "\n", (int)value, addr); + FEC_PRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx_eth_reg_name(s, index), + (uint32_t)value); - switch (addr & 0x3ff) { - case 0x004: /* EIR */ - s->eir &= ~value; + switch (index) { + case ENET_EIR: + s->regs[index] &= ~value; break; - case 0x008: /* EIMR */ - s->eimr = value; + case ENET_EIMR: + s->regs[index] = value; break; - case 0x010: /* RDAR */ - if ((s->ecr & FEC_EN) && !s->rx_enabled) { - imx_fec_enable_rx(s); + case ENET_RDAR: + if (s->regs[ENET_ECR] & ENET_ECR_ETHEREN) { + if (!s->regs[index]) { + s->regs[index] = ENET_RDAR_RDAR; + imx_eth_enable_rx(s); + } + } else { + s->regs[index] = 0; } break; - case 0x014: /* TDAR */ - if (s->ecr & FEC_EN) { - imx_fec_do_tx(s); + case ENET_TDAR: + if (s->regs[ENET_ECR] & ENET_ECR_ETHEREN) { + s->regs[index] = ENET_TDAR_TDAR; + imx_eth_do_tx(s); } + s->regs[index] = 0; break; - case 0x024: /* ECR */ - s->ecr = value; - if (value & FEC_RESET) { - imx_fec_reset(DEVICE(s)); + case ENET_ECR: + if (value & ENET_ECR_RESET) { + return imx_eth_reset(DEVICE(s)); } - if ((s->ecr & FEC_EN) == 0) { - s->rx_enabled = 0; + s->regs[index] = value; + if ((s->regs[index] & ENET_ECR_ETHEREN) == 0) { + s->regs[ENET_RDAR] = 0; + s->rx_descriptor = s->regs[ENET_RDSR]; + s->regs[ENET_TDAR] = 0; + s->tx_descriptor = s->regs[ENET_TDSR]; } break; - case 0x040: /* MMFR */ - /* store the value */ - s->mmfr = value; - if (extract32(value, 28, 1)) { - do_phy_write(s, extract32(value, 18, 9), extract32(value, 0, 16)); + case ENET_MMFR: + s->regs[index] = value; + if (extract32(value, 29, 1)) { + /* This is a read operation */ + s->regs[ENET_MMFR] = deposit32(s->regs[ENET_MMFR], 0, 16, + do_phy_read(s, + extract32(value, + 18, 10))); } else { - s->mmfr = do_phy_read(s, extract32(value, 18, 9)); + /* This a write operation */ + do_phy_write(s, extract32(value, 18, 10), extract32(value, 0, 16)); } /* raise the interrupt as the PHY operation is done */ - s->eir |= FEC_INT_MII; + s->regs[ENET_EIR] |= ENET_INT_MII; break; - case 0x044: /* MSCR */ - s->mscr = value & 0xfe; + case ENET_MSCR: + s->regs[index] = value & 0xfe; break; - case 0x064: /* MIBC */ + case ENET_MIBC: /* TODO: Implement MIB. */ - s->mibc = (value & 0x80000000) ? 0xc0000000 : 0; + s->regs[index] = (value & 0x80000000) ? 0xc0000000 : 0; break; - case 0x084: /* RCR */ - s->rcr = value & 0x07ff003f; + case ENET_RCR: + s->regs[index] = value & 0x07ff003f; /* TODO: Implement LOOP mode. */ break; - case 0x0c4: /* TCR */ + case ENET_TCR: /* We transmit immediately, so raise GRA immediately. */ - s->tcr = value; + s->regs[index] = value; if (value & 1) { - s->eir |= FEC_INT_GRA; + s->regs[ENET_EIR] |= ENET_INT_GRA; } break; - case 0x0e4: /* PALR */ + case ENET_PALR: + s->regs[index] = value; s->conf.macaddr.a[0] = value >> 24; s->conf.macaddr.a[1] = value >> 16; s->conf.macaddr.a[2] = value >> 8; s->conf.macaddr.a[3] = value; break; - case 0x0e8: /* PAUR */ + case ENET_PAUR: + s->regs[index] = (value | 0x0000ffff) & 0xffff8808; s->conf.macaddr.a[4] = value >> 24; s->conf.macaddr.a[5] = value >> 16; break; - case 0x0ec: /* OPDR */ + case ENET_OPD: + s->regs[index] = (value & 0x0000ffff) | 0x00010000; break; - case 0x118: /* IAUR */ - case 0x11c: /* IALR */ - case 0x120: /* GAUR */ - case 0x124: /* GALR */ + case ENET_IAUR: + case ENET_IALR: + case ENET_GAUR: + case ENET_GALR: /* TODO: implement MAC hash filtering. */ break; - case 0x144: /* TFWR */ - s->tfwr = value & 3; - break; - case 0x14c: /* FRBR */ - /* FRBR writes ignored. */ - break; - case 0x150: /* FRSR */ - s->frsr = (value & 0x3fc) | 0x400; - break; - case 0x180: /* ERDSR */ - s->erdsr = value & ~3; - s->rx_descriptor = s->erdsr; - break; - case 0x184: /* ETDSR */ - s->etdsr = value & ~3; - s->tx_descriptor = s->etdsr; + case ENET_TFWR: + if (s->is_fec) { + s->regs[index] = value & 0x3; + } else { + s->regs[index] = value & 0x13f; + } break; - case 0x188: /* EMRBR */ - s->emrbr = value & 0x7f0; + case ENET_RDSR: + if (s->is_fec) { + s->regs[index] = value & ~3; + } else { + s->regs[index] = value & ~7; + } + s->rx_descriptor = s->regs[index]; break; - case 0x300: /* MIIGSK_CFGR */ - s->miigsk_cfgr = value & 0x53; + case ENET_TDSR: + if (s->is_fec) { + s->regs[index] = value & ~3; + } else { + s->regs[index] = value & ~7; + } + s->tx_descriptor = s->regs[index]; break; - case 0x308: /* MIIGSK_ENR */ - s->miigsk_enr = (value & 0x2) ? 0x6 : 0; + case ENET_MRBR: + s->regs[index] = value & 0x00003ff0; break; default: - qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%" - HWADDR_PRIx "\n", TYPE_IMX_FEC, __func__, addr); - break; + if (s->is_fec) { + imx_fec_write(s, index, value); + } else { + imx_enet_write(s, index, value); + } + return; } - imx_fec_update(s); + imx_eth_update(s); } -static int imx_fec_can_receive(NetClientState *nc) +static int imx_eth_can_receive(NetClientState *nc) { IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc)); - return s->rx_enabled; + FEC_PRINTF("\n"); + + return s->regs[ENET_RDAR] ? 1 : 0; } static ssize_t imx_fec_receive(NetClientState *nc, const uint8_t *buf, @@ -559,7 +946,7 @@ static ssize_t imx_fec_receive(NetClientState *nc, const uint8_t *buf, FEC_PRINTF("len %d\n", (int)size); - if (!s->rx_enabled) { + if (!s->regs[ENET_RDAR]) { qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Unexpected packet\n", TYPE_IMX_FEC, __func__); return 0; @@ -570,21 +957,21 @@ static ssize_t imx_fec_receive(NetClientState *nc, const uint8_t *buf, crc = cpu_to_be32(crc32(~0, buf, size)); crc_ptr = (uint8_t *) &crc; - /* Huge frames are truncted. */ - if (size > FEC_MAX_FRAME_SIZE) { - size = FEC_MAX_FRAME_SIZE; - flags |= FEC_BD_TR | FEC_BD_LG; + /* Huge frames are truncated. */ + if (size > ENET_MAX_FRAME_SIZE) { + size = ENET_MAX_FRAME_SIZE; + flags |= ENET_BD_TR | ENET_BD_LG; } /* Frames larger than the user limit just set error flags. */ - if (size > (s->rcr >> 16)) { - flags |= FEC_BD_LG; + if (size > (s->regs[ENET_RCR] >> 16)) { + flags |= ENET_BD_LG; } addr = s->rx_descriptor; while (size > 0) { imx_fec_read_bd(&bd, addr); - if ((bd.flags & FEC_BD_E) == 0) { + if ((bd.flags & ENET_BD_E) == 0) { /* No descriptors available. Bail out. */ /* * FIXME: This is wrong. We should probably either @@ -595,7 +982,7 @@ static ssize_t imx_fec_receive(NetClientState *nc, const uint8_t *buf, TYPE_IMX_FEC, __func__); break; } - buf_len = (size <= s->emrbr) ? size : s->emrbr; + buf_len = (size <= s->regs[ENET_MRBR]) ? size : s->regs[ENET_MRBR]; bd.length = buf_len; size -= buf_len; @@ -613,99 +1000,232 @@ static ssize_t imx_fec_receive(NetClientState *nc, const uint8_t *buf, crc_ptr, 4 - size); crc_ptr += 4 - size; } - bd.flags &= ~FEC_BD_E; + bd.flags &= ~ENET_BD_E; if (size == 0) { /* Last buffer in frame. */ - bd.flags |= flags | FEC_BD_L; + bd.flags |= flags | ENET_BD_L; FEC_PRINTF("rx frame flags %04x\n", bd.flags); - s->eir |= FEC_INT_RXF; + s->regs[ENET_EIR] |= ENET_INT_RXF; } else { - s->eir |= FEC_INT_RXB; + s->regs[ENET_EIR] |= ENET_INT_RXB; } imx_fec_write_bd(&bd, addr); /* Advance to the next descriptor. */ - if ((bd.flags & FEC_BD_W) != 0) { - addr = s->erdsr; + if ((bd.flags & ENET_BD_W) != 0) { + addr = s->regs[ENET_RDSR]; + } else { + addr += sizeof(bd); + } + } + s->rx_descriptor = addr; + imx_eth_enable_rx(s); + imx_eth_update(s); + return len; +} + +static ssize_t imx_enet_receive(NetClientState *nc, const uint8_t *buf, + size_t len) +{ + IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc)); + IMXENETBufDesc bd; + uint32_t flags = 0; + uint32_t addr; + uint32_t crc; + uint32_t buf_addr; + uint8_t *crc_ptr; + unsigned int buf_len; + size_t size = len; + + FEC_PRINTF("len %d\n", (int)size); + + if (!s->regs[ENET_RDAR]) { + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Unexpected packet\n", + TYPE_IMX_FEC, __func__); + return 0; + } + + /* 4 bytes for the CRC. */ + size += 4; + crc = cpu_to_be32(crc32(~0, buf, size)); + crc_ptr = (uint8_t *) &crc; + + /* Huge frames are truncted. */ + if (size > ENET_MAX_FRAME_SIZE) { + size = ENET_MAX_FRAME_SIZE; + flags |= ENET_BD_TR | ENET_BD_LG; + } + + /* Frames larger than the user limit just set error flags. */ + if (size > (s->regs[ENET_RCR] >> 16)) { + flags |= ENET_BD_LG; + } + + addr = s->rx_descriptor; + while (size > 0) { + imx_enet_read_bd(&bd, addr); + if ((bd.flags & ENET_BD_E) == 0) { + /* No descriptors available. Bail out. */ + /* + * FIXME: This is wrong. We should probably either + * save the remainder for when more RX buffers are + * available, or flag an error. + */ + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Lost end of frame\n", + TYPE_IMX_FEC, __func__); + break; + } + buf_len = (size <= s->regs[ENET_MRBR]) ? size : s->regs[ENET_MRBR]; + bd.length = buf_len; + size -= buf_len; + + FEC_PRINTF("rx_bd 0x%x length %d\n", addr, bd.length); + + /* The last 4 bytes are the CRC. */ + if (size < 4) { + buf_len += size - 4; + } + buf_addr = bd.data; + dma_memory_write(&address_space_memory, buf_addr, buf, buf_len); + buf += buf_len; + if (size < 4) { + dma_memory_write(&address_space_memory, buf_addr + buf_len, + crc_ptr, 4 - size); + crc_ptr += 4 - size; + } + bd.flags &= ~ENET_BD_E; + if (size == 0) { + /* Last buffer in frame. */ + bd.flags |= flags | ENET_BD_L; + FEC_PRINTF("rx frame flags %04x\n", bd.flags); + if (bd.option & ENET_BD_RX_INT) { + s->regs[ENET_EIR] |= ENET_INT_RXF; + } + } else { + if (bd.option & ENET_BD_RX_INT) { + s->regs[ENET_EIR] |= ENET_INT_RXB; + } + } + imx_enet_write_bd(&bd, addr); + /* Advance to the next descriptor. */ + if ((bd.flags & ENET_BD_W) != 0) { + addr = s->regs[ENET_RDSR]; } else { - addr += 8; + addr += sizeof(bd); } } s->rx_descriptor = addr; - imx_fec_enable_rx(s); - imx_fec_update(s); + imx_eth_enable_rx(s); + imx_eth_update(s); return len; } -static const MemoryRegionOps imx_fec_ops = { - .read = imx_fec_read, - .write = imx_fec_write, +static ssize_t imx_eth_receive(NetClientState *nc, const uint8_t *buf, + size_t len) +{ + IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc)); + + if (!s->is_fec && (s->regs[ENET_ECR] & ENET_ECR_EN1588)) { + return imx_enet_receive(nc, buf, len); + } else { + return imx_fec_receive(nc, buf, len); + } +} + +static const MemoryRegionOps imx_eth_ops = { + .read = imx_eth_read, + .write = imx_eth_write, .valid.min_access_size = 4, .valid.max_access_size = 4, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_NATIVE_ENDIAN, }; -static void imx_fec_cleanup(NetClientState *nc) +static void imx_eth_cleanup(NetClientState *nc) { IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc)); s->nic = NULL; } -static NetClientInfo net_imx_fec_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .can_receive = imx_fec_can_receive, - .receive = imx_fec_receive, - .cleanup = imx_fec_cleanup, - .link_status_changed = imx_fec_set_link, +static NetClientInfo imx_eth_net_info = { + .type = NET_CLIENT_DRIVER_NIC, + .size = sizeof(NICState), + .can_receive = imx_eth_can_receive, + .receive = imx_eth_receive, + .cleanup = imx_eth_cleanup, + .link_status_changed = imx_eth_set_link, }; -static void imx_fec_realize(DeviceState *dev, Error **errp) +static void imx_eth_realize(DeviceState *dev, Error **errp) { IMXFECState *s = IMX_FEC(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - memory_region_init_io(&s->iomem, OBJECT(dev), &imx_fec_ops, s, + memory_region_init_io(&s->iomem, OBJECT(dev), &imx_eth_ops, s, TYPE_IMX_FEC, 0x400); sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq); + sysbus_init_irq(sbd, &s->irq[0]); + sysbus_init_irq(sbd, &s->irq[1]); + qemu_macaddr_default_if_unset(&s->conf.macaddr); s->conf.peers.ncs[0] = nd_table[0].netdev; - s->nic = qemu_new_nic(&net_imx_fec_info, &s->conf, - object_get_typename(OBJECT(dev)), DEVICE(dev)->id, - s); + s->nic = qemu_new_nic(&imx_eth_net_info, &s->conf, + object_get_typename(OBJECT(dev)), + DEVICE(dev)->id, s); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); } -static Property imx_fec_properties[] = { +static Property imx_eth_properties[] = { DEFINE_NIC_PROPERTIES(IMXFECState, conf), DEFINE_PROP_END_OF_LIST(), }; -static void imx_fec_class_init(ObjectClass *klass, void *data) +static void imx_eth_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->vmsd = &vmstate_imx_fec; - dc->reset = imx_fec_reset; - dc->props = imx_fec_properties; - dc->realize = imx_fec_realize; - dc->desc = "i.MX FEC Ethernet Controller"; + dc->vmsd = &vmstate_imx_eth; + dc->reset = imx_eth_reset; + dc->props = imx_eth_properties; + dc->realize = imx_eth_realize; + dc->desc = "i.MX FEC/ENET Ethernet Controller"; +} + +static void imx_fec_init(Object *obj) +{ + IMXFECState *s = IMX_FEC(obj); + + s->is_fec = true; +} + +static void imx_enet_init(Object *obj) +{ + IMXFECState *s = IMX_FEC(obj); + + s->is_fec = false; } static const TypeInfo imx_fec_info = { - .name = TYPE_IMX_FEC, - .parent = TYPE_SYS_BUS_DEVICE, + .name = TYPE_IMX_FEC, + .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(IMXFECState), - .class_init = imx_fec_class_init, + .instance_init = imx_fec_init, + .class_init = imx_eth_class_init, +}; + +static const TypeInfo imx_enet_info = { + .name = TYPE_IMX_ENET, + .parent = TYPE_IMX_FEC, + .instance_init = imx_enet_init, }; -static void imx_fec_register_types(void) +static void imx_eth_register_types(void) { type_register_static(&imx_fec_info); + type_register_static(&imx_enet_info); } -type_init(imx_fec_register_types) +type_init(imx_eth_register_types) diff --git a/hw/net/lan9118.c b/hw/net/lan9118.c index 08dc474d6..4615d873b 100644 --- a/hw/net/lan9118.c +++ b/hw/net/lan9118.c @@ -16,6 +16,7 @@ #include "hw/devices.h" #include "sysemu/sysemu.h" #include "hw/ptimer.h" +#include "qemu/log.h" /* For crc32 */ #include <zlib.h> @@ -1312,7 +1313,7 @@ static const MemoryRegionOps lan9118_16bit_mem_ops = { }; static NetClientInfo net_lan9118_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, + .type = NET_CLIENT_DRIVER_NIC, .size = sizeof(NICState), .receive = lan9118_receive, .link_status_changed = lan9118_set_link, diff --git a/hw/net/lance.c b/hw/net/lance.c index 6253d2103..573d724bc 100644 --- a/hw/net/lance.c +++ b/hw/net/lance.c @@ -93,7 +93,7 @@ static const MemoryRegionOps lance_mem_ops = { }; static NetClientInfo net_lance_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, + .type = NET_CLIENT_DRIVER_NIC, .size = sizeof(NICState), .receive = pcnet_receive, .link_status_changed = pcnet_set_link_status, diff --git a/hw/net/mcf_fec.c b/hw/net/mcf_fec.c index 7c0398ed9..0ee8ad9d6 100644 --- a/hw/net/mcf_fec.c +++ b/hw/net/mcf_fec.c @@ -507,7 +507,7 @@ static const MemoryRegionOps mcf_fec_ops = { }; static NetClientInfo net_mcf_fec_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, + .type = NET_CLIENT_DRIVER_NIC, .size = sizeof(NICState), .receive = mcf_fec_receive, }; diff --git a/hw/net/milkymist-minimac2.c b/hw/net/milkymist-minimac2.c index 1e147c33c..c3a12e119 100644 --- a/hw/net/milkymist-minimac2.c +++ b/hw/net/milkymist-minimac2.c @@ -447,7 +447,7 @@ static void milkymist_minimac2_reset(DeviceState *d) } static NetClientInfo net_milkymist_minimac2_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, + .type = NET_CLIENT_DRIVER_NIC, .size = sizeof(NICState), .receive = minimac2_rx, }; diff --git a/hw/net/mipsnet.c b/hw/net/mipsnet.c index 740cd98ff..5a63df7cc 100644 --- a/hw/net/mipsnet.c +++ b/hw/net/mipsnet.c @@ -83,6 +83,9 @@ static ssize_t mipsnet_receive(NetClientState *nc, const uint8_t *buf, size_t si if (!mipsnet_can_receive(nc)) return 0; + if (size >= sizeof(s->rx_buffer)) { + return 0; + } s->busy = 1; /* Just accept everything. */ @@ -180,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; @@ -219,7 +224,7 @@ static const VMStateDescription vmstate_mipsnet = { }; static NetClientInfo net_mipsnet_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, + .type = NET_CLIENT_DRIVER_NIC, .size = sizeof(NICState), .receive = mipsnet_receive, }; diff --git a/hw/net/ne2000-isa.c b/hw/net/ne2000-isa.c index a7f5a9464..f3455339e 100644 --- a/hw/net/ne2000-isa.c +++ b/hw/net/ne2000-isa.c @@ -44,7 +44,7 @@ typedef struct ISANE2000State { } ISANE2000State; static NetClientInfo net_ne2000_isa_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, + .type = NET_CLIENT_DRIVER_NIC, .size = sizeof(NICState), .receive = ne2000_receive, }; @@ -127,9 +127,7 @@ static void isa_ne2000_set_bootindex(Object *obj, Visitor *v, s->c.bootindex = boot_index; out: - if (local_err) { - error_propagate(errp, local_err); - } + error_propagate(errp, local_err); } static void isa_ne2000_instance_init(Object *obj) diff --git a/hw/net/ne2000.c b/hw/net/ne2000.c index f0feaf96b..798d681e2 100644 --- a/hw/net/ne2000.c +++ b/hw/net/ne2000.c @@ -712,7 +712,7 @@ void ne2000_setup_io(NE2000State *s, DeviceState *dev, unsigned size) } static NetClientInfo net_ne2000_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, + .type = NET_CLIENT_DRIVER_NIC, .size = sizeof(NICState), .receive = ne2000_receive, }; diff --git a/hw/net/ne2000.h b/hw/net/ne2000.h index d022b28fc..d213dccae 100644 --- a/hw/net/ne2000.h +++ b/hw/net/ne2000.h @@ -1,5 +1,5 @@ #ifndef HW_NE2000_H -#define HW_NE2000_H 1 +#define HW_NE2000_H #define NE2000_PMEM_SIZE (32*1024) #define NE2000_PMEM_START (16*1024) diff --git a/hw/net/net_rx_pkt.c b/hw/net/net_rx_pkt.c new file mode 100644 index 000000000..1019b50c1 --- /dev/null +++ b/hw/net/net_rx_pkt.c @@ -0,0 +1,600 @@ +/* + * QEMU RX packets abstractions + * + * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) + * + * Developed by Daynix Computing LTD (http://www.daynix.com) + * + * Authors: + * Dmitry Fleytman <dmitry@daynix.com> + * Tamir Shomer <tamirs@daynix.com> + * Yan Vugenfirer <yan@daynix.com> + * + * 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 "trace.h" +#include "net_rx_pkt.h" +#include "net/checksum.h" +#include "net/tap.h" + +struct NetRxPkt { + struct virtio_net_hdr virt_hdr; + uint8_t ehdr_buf[sizeof(struct eth_header)]; + struct iovec *vec; + uint16_t vec_len_total; + uint16_t vec_len; + uint32_t tot_len; + uint16_t tci; + bool vlan_stripped; + bool has_virt_hdr; + eth_pkt_types_e packet_type; + + /* Analysis results */ + bool isip4; + bool isip6; + bool isudp; + bool istcp; + + size_t l3hdr_off; + size_t l4hdr_off; + size_t l5hdr_off; + + eth_ip6_hdr_info ip6hdr_info; + eth_ip4_hdr_info ip4hdr_info; + eth_l4_hdr_info l4hdr_info; +}; + +void net_rx_pkt_init(struct NetRxPkt **pkt, bool has_virt_hdr) +{ + struct NetRxPkt *p = g_malloc0(sizeof *p); + p->has_virt_hdr = has_virt_hdr; + p->vec = NULL; + p->vec_len_total = 0; + *pkt = p; +} + +void net_rx_pkt_uninit(struct NetRxPkt *pkt) +{ + if (pkt->vec_len_total != 0) { + g_free(pkt->vec); + } + + g_free(pkt); +} + +struct virtio_net_hdr *net_rx_pkt_get_vhdr(struct NetRxPkt *pkt) +{ + assert(pkt); + return &pkt->virt_hdr; +} + +static inline void +net_rx_pkt_iovec_realloc(struct NetRxPkt *pkt, + int new_iov_len) +{ + if (pkt->vec_len_total < new_iov_len) { + g_free(pkt->vec); + pkt->vec = g_malloc(sizeof(*pkt->vec) * new_iov_len); + pkt->vec_len_total = new_iov_len; + } +} + +static void +net_rx_pkt_pull_data(struct NetRxPkt *pkt, + const struct iovec *iov, int iovcnt, + size_t ploff) +{ + if (pkt->vlan_stripped) { + net_rx_pkt_iovec_realloc(pkt, iovcnt + 1); + + pkt->vec[0].iov_base = pkt->ehdr_buf; + pkt->vec[0].iov_len = sizeof(pkt->ehdr_buf); + + pkt->tot_len = + iov_size(iov, iovcnt) - ploff + sizeof(struct eth_header); + + pkt->vec_len = iov_copy(pkt->vec + 1, pkt->vec_len_total - 1, + iov, iovcnt, ploff, pkt->tot_len); + } else { + net_rx_pkt_iovec_realloc(pkt, iovcnt); + + pkt->tot_len = iov_size(iov, iovcnt) - ploff; + pkt->vec_len = iov_copy(pkt->vec, pkt->vec_len_total, + iov, iovcnt, ploff, pkt->tot_len); + } + + eth_get_protocols(pkt->vec, pkt->vec_len, &pkt->isip4, &pkt->isip6, + &pkt->isudp, &pkt->istcp, + &pkt->l3hdr_off, &pkt->l4hdr_off, &pkt->l5hdr_off, + &pkt->ip6hdr_info, &pkt->ip4hdr_info, &pkt->l4hdr_info); + + trace_net_rx_pkt_parsed(pkt->isip4, pkt->isip6, pkt->isudp, pkt->istcp, + pkt->l3hdr_off, pkt->l4hdr_off, pkt->l5hdr_off); +} + +void net_rx_pkt_attach_iovec(struct NetRxPkt *pkt, + const struct iovec *iov, int iovcnt, + size_t iovoff, bool strip_vlan) +{ + uint16_t tci = 0; + uint16_t ploff = iovoff; + assert(pkt); + pkt->vlan_stripped = false; + + if (strip_vlan) { + pkt->vlan_stripped = eth_strip_vlan(iov, iovcnt, iovoff, pkt->ehdr_buf, + &ploff, &tci); + } + + pkt->tci = tci; + + net_rx_pkt_pull_data(pkt, iov, iovcnt, ploff); +} + +void net_rx_pkt_attach_iovec_ex(struct NetRxPkt *pkt, + const struct iovec *iov, int iovcnt, + size_t iovoff, bool strip_vlan, + uint16_t vet) +{ + uint16_t tci = 0; + uint16_t ploff = iovoff; + assert(pkt); + pkt->vlan_stripped = false; + + if (strip_vlan) { + pkt->vlan_stripped = eth_strip_vlan_ex(iov, iovcnt, iovoff, vet, + pkt->ehdr_buf, + &ploff, &tci); + } + + pkt->tci = tci; + + net_rx_pkt_pull_data(pkt, iov, iovcnt, ploff); +} + +void net_rx_pkt_dump(struct NetRxPkt *pkt) +{ +#ifdef NET_RX_PKT_DEBUG + NetRxPkt *pkt = (NetRxPkt *)pkt; + assert(pkt); + + printf("RX PKT: tot_len: %d, vlan_stripped: %d, vlan_tag: %d\n", + pkt->tot_len, pkt->vlan_stripped, pkt->tci); +#endif +} + +void net_rx_pkt_set_packet_type(struct NetRxPkt *pkt, + eth_pkt_types_e packet_type) +{ + assert(pkt); + + pkt->packet_type = packet_type; + +} + +eth_pkt_types_e net_rx_pkt_get_packet_type(struct NetRxPkt *pkt) +{ + assert(pkt); + + return pkt->packet_type; +} + +size_t net_rx_pkt_get_total_len(struct NetRxPkt *pkt) +{ + assert(pkt); + + return pkt->tot_len; +} + +void net_rx_pkt_set_protocols(struct NetRxPkt *pkt, const void *data, + size_t len) +{ + const struct iovec iov = { + .iov_base = (void *)data, + .iov_len = len + }; + + assert(pkt); + + eth_get_protocols(&iov, 1, &pkt->isip4, &pkt->isip6, + &pkt->isudp, &pkt->istcp, + &pkt->l3hdr_off, &pkt->l4hdr_off, &pkt->l5hdr_off, + &pkt->ip6hdr_info, &pkt->ip4hdr_info, &pkt->l4hdr_info); +} + +void net_rx_pkt_get_protocols(struct NetRxPkt *pkt, + bool *isip4, bool *isip6, + bool *isudp, bool *istcp) +{ + assert(pkt); + + *isip4 = pkt->isip4; + *isip6 = pkt->isip6; + *isudp = pkt->isudp; + *istcp = pkt->istcp; +} + +size_t net_rx_pkt_get_l3_hdr_offset(struct NetRxPkt *pkt) +{ + assert(pkt); + return pkt->l3hdr_off; +} + +size_t net_rx_pkt_get_l4_hdr_offset(struct NetRxPkt *pkt) +{ + assert(pkt); + return pkt->l4hdr_off; +} + +size_t net_rx_pkt_get_l5_hdr_offset(struct NetRxPkt *pkt) +{ + assert(pkt); + return pkt->l5hdr_off; +} + +eth_ip6_hdr_info *net_rx_pkt_get_ip6_info(struct NetRxPkt *pkt) +{ + return &pkt->ip6hdr_info; +} + +eth_ip4_hdr_info *net_rx_pkt_get_ip4_info(struct NetRxPkt *pkt) +{ + return &pkt->ip4hdr_info; +} + +eth_l4_hdr_info *net_rx_pkt_get_l4_info(struct NetRxPkt *pkt) +{ + return &pkt->l4hdr_info; +} + +static inline void +_net_rx_rss_add_chunk(uint8_t *rss_input, size_t *bytes_written, + void *ptr, size_t size) +{ + memcpy(&rss_input[*bytes_written], ptr, size); + trace_net_rx_pkt_rss_add_chunk(ptr, size, *bytes_written); + *bytes_written += size; +} + +static inline void +_net_rx_rss_prepare_ip4(uint8_t *rss_input, + struct NetRxPkt *pkt, + size_t *bytes_written) +{ + struct ip_header *ip4_hdr = &pkt->ip4hdr_info.ip4_hdr; + + _net_rx_rss_add_chunk(rss_input, bytes_written, + &ip4_hdr->ip_src, sizeof(uint32_t)); + + _net_rx_rss_add_chunk(rss_input, bytes_written, + &ip4_hdr->ip_dst, sizeof(uint32_t)); +} + +static inline void +_net_rx_rss_prepare_ip6(uint8_t *rss_input, + struct NetRxPkt *pkt, + bool ipv6ex, size_t *bytes_written) +{ + eth_ip6_hdr_info *ip6info = &pkt->ip6hdr_info; + + _net_rx_rss_add_chunk(rss_input, bytes_written, + (ipv6ex && ip6info->rss_ex_src_valid) ? &ip6info->rss_ex_src + : &ip6info->ip6_hdr.ip6_src, + sizeof(struct in6_address)); + + _net_rx_rss_add_chunk(rss_input, bytes_written, + (ipv6ex && ip6info->rss_ex_dst_valid) ? &ip6info->rss_ex_dst + : &ip6info->ip6_hdr.ip6_dst, + sizeof(struct in6_address)); +} + +static inline void +_net_rx_rss_prepare_tcp(uint8_t *rss_input, + struct NetRxPkt *pkt, + size_t *bytes_written) +{ + struct tcp_header *tcphdr = &pkt->l4hdr_info.hdr.tcp; + + _net_rx_rss_add_chunk(rss_input, bytes_written, + &tcphdr->th_sport, sizeof(uint16_t)); + + _net_rx_rss_add_chunk(rss_input, bytes_written, + &tcphdr->th_dport, sizeof(uint16_t)); +} + +uint32_t +net_rx_pkt_calc_rss_hash(struct NetRxPkt *pkt, + NetRxPktRssType type, + uint8_t *key) +{ + uint8_t rss_input[36]; + size_t rss_length = 0; + uint32_t rss_hash = 0; + net_toeplitz_key key_data; + + switch (type) { + case NetPktRssIpV4: + assert(pkt->isip4); + trace_net_rx_pkt_rss_ip4(); + _net_rx_rss_prepare_ip4(&rss_input[0], pkt, &rss_length); + break; + case NetPktRssIpV4Tcp: + assert(pkt->isip4); + assert(pkt->istcp); + trace_net_rx_pkt_rss_ip4_tcp(); + _net_rx_rss_prepare_ip4(&rss_input[0], pkt, &rss_length); + _net_rx_rss_prepare_tcp(&rss_input[0], pkt, &rss_length); + break; + case NetPktRssIpV6Tcp: + assert(pkt->isip6); + assert(pkt->istcp); + trace_net_rx_pkt_rss_ip6_tcp(); + _net_rx_rss_prepare_ip6(&rss_input[0], pkt, true, &rss_length); + _net_rx_rss_prepare_tcp(&rss_input[0], pkt, &rss_length); + break; + case NetPktRssIpV6: + assert(pkt->isip6); + trace_net_rx_pkt_rss_ip6(); + _net_rx_rss_prepare_ip6(&rss_input[0], pkt, false, &rss_length); + break; + case NetPktRssIpV6Ex: + assert(pkt->isip6); + trace_net_rx_pkt_rss_ip6_ex(); + _net_rx_rss_prepare_ip6(&rss_input[0], pkt, true, &rss_length); + break; + default: + assert(false); + break; + } + + net_toeplitz_key_init(&key_data, key); + net_toeplitz_add(&rss_hash, rss_input, rss_length, &key_data); + + trace_net_rx_pkt_rss_hash(rss_length, rss_hash); + + return rss_hash; +} + +uint16_t net_rx_pkt_get_ip_id(struct NetRxPkt *pkt) +{ + assert(pkt); + + if (pkt->isip4) { + return be16_to_cpu(pkt->ip4hdr_info.ip4_hdr.ip_id); + } + + return 0; +} + +bool net_rx_pkt_is_tcp_ack(struct NetRxPkt *pkt) +{ + assert(pkt); + + if (pkt->istcp) { + return TCP_HEADER_FLAGS(&pkt->l4hdr_info.hdr.tcp) & TCP_FLAG_ACK; + } + + return false; +} + +bool net_rx_pkt_has_tcp_data(struct NetRxPkt *pkt) +{ + assert(pkt); + + if (pkt->istcp) { + return pkt->l4hdr_info.has_tcp_data; + } + + return false; +} + +struct iovec *net_rx_pkt_get_iovec(struct NetRxPkt *pkt) +{ + assert(pkt); + + return pkt->vec; +} + +uint16_t net_rx_pkt_get_iovec_len(struct NetRxPkt *pkt) +{ + assert(pkt); + + return pkt->vec_len; +} + +void net_rx_pkt_set_vhdr(struct NetRxPkt *pkt, + struct virtio_net_hdr *vhdr) +{ + assert(pkt); + + memcpy(&pkt->virt_hdr, vhdr, sizeof pkt->virt_hdr); +} + +void net_rx_pkt_set_vhdr_iovec(struct NetRxPkt *pkt, + const struct iovec *iov, int iovcnt) +{ + assert(pkt); + + iov_to_buf(iov, iovcnt, 0, &pkt->virt_hdr, sizeof pkt->virt_hdr); +} + +bool net_rx_pkt_is_vlan_stripped(struct NetRxPkt *pkt) +{ + assert(pkt); + + return pkt->vlan_stripped; +} + +bool net_rx_pkt_has_virt_hdr(struct NetRxPkt *pkt) +{ + assert(pkt); + + return pkt->has_virt_hdr; +} + +uint16_t net_rx_pkt_get_vlan_tag(struct NetRxPkt *pkt) +{ + assert(pkt); + + return pkt->tci; +} + +bool net_rx_pkt_validate_l3_csum(struct NetRxPkt *pkt, bool *csum_valid) +{ + uint32_t cntr; + uint16_t csum; + uint32_t csl; + + trace_net_rx_pkt_l3_csum_validate_entry(); + + if (!pkt->isip4) { + trace_net_rx_pkt_l3_csum_validate_not_ip4(); + return false; + } + + csl = pkt->l4hdr_off - pkt->l3hdr_off; + + cntr = net_checksum_add_iov(pkt->vec, pkt->vec_len, + pkt->l3hdr_off, + csl, 0); + + csum = net_checksum_finish(cntr); + + *csum_valid = (csum == 0); + + trace_net_rx_pkt_l3_csum_validate_csum(pkt->l3hdr_off, csl, + cntr, csum, *csum_valid); + + return true; +} + +static uint16_t +_net_rx_pkt_calc_l4_csum(struct NetRxPkt *pkt) +{ + uint32_t cntr; + uint16_t csum; + uint16_t csl; + uint32_t cso; + + trace_net_rx_pkt_l4_csum_calc_entry(); + + if (pkt->isip4) { + if (pkt->isudp) { + csl = be16_to_cpu(pkt->l4hdr_info.hdr.udp.uh_ulen); + trace_net_rx_pkt_l4_csum_calc_ip4_udp(); + } else { + csl = be16_to_cpu(pkt->ip4hdr_info.ip4_hdr.ip_len) - + IP_HDR_GET_LEN(&pkt->ip4hdr_info.ip4_hdr); + trace_net_rx_pkt_l4_csum_calc_ip4_tcp(); + } + + cntr = eth_calc_ip4_pseudo_hdr_csum(&pkt->ip4hdr_info.ip4_hdr, + csl, &cso); + trace_net_rx_pkt_l4_csum_calc_ph_csum(cntr, csl); + } else { + if (pkt->isudp) { + csl = be16_to_cpu(pkt->l4hdr_info.hdr.udp.uh_ulen); + trace_net_rx_pkt_l4_csum_calc_ip6_udp(); + } else { + struct ip6_header *ip6hdr = &pkt->ip6hdr_info.ip6_hdr; + size_t full_ip6hdr_len = pkt->l4hdr_off - pkt->l3hdr_off; + size_t ip6opts_len = full_ip6hdr_len - sizeof(struct ip6_header); + + csl = be16_to_cpu(ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_plen) - + ip6opts_len; + trace_net_rx_pkt_l4_csum_calc_ip6_tcp(); + } + + cntr = eth_calc_ip6_pseudo_hdr_csum(&pkt->ip6hdr_info.ip6_hdr, csl, + pkt->ip6hdr_info.l4proto, &cso); + trace_net_rx_pkt_l4_csum_calc_ph_csum(cntr, csl); + } + + cntr += net_checksum_add_iov(pkt->vec, pkt->vec_len, + pkt->l4hdr_off, csl, cso); + + csum = net_checksum_finish(cntr); + + trace_net_rx_pkt_l4_csum_calc_csum(pkt->l4hdr_off, csl, cntr, csum); + + return csum; +} + +bool net_rx_pkt_validate_l4_csum(struct NetRxPkt *pkt, bool *csum_valid) +{ + uint16_t csum; + + trace_net_rx_pkt_l4_csum_validate_entry(); + + if (!pkt->istcp && !pkt->isudp) { + trace_net_rx_pkt_l4_csum_validate_not_xxp(); + return false; + } + + if (pkt->isudp && (pkt->l4hdr_info.hdr.udp.uh_sum == 0)) { + trace_net_rx_pkt_l4_csum_validate_udp_with_no_checksum(); + return false; + } + + if (pkt->isip4 && pkt->ip4hdr_info.fragment) { + trace_net_rx_pkt_l4_csum_validate_ip4_fragment(); + return false; + } + + csum = _net_rx_pkt_calc_l4_csum(pkt); + + *csum_valid = ((csum == 0) || (csum == 0xFFFF)); + + trace_net_rx_pkt_l4_csum_validate_csum(*csum_valid); + + return true; +} + +bool net_rx_pkt_fix_l4_csum(struct NetRxPkt *pkt) +{ + uint16_t csum = 0; + uint32_t l4_cso; + + trace_net_rx_pkt_l4_csum_fix_entry(); + + if (pkt->istcp) { + l4_cso = offsetof(struct tcp_header, th_sum); + trace_net_rx_pkt_l4_csum_fix_tcp(l4_cso); + } else if (pkt->isudp) { + if (pkt->l4hdr_info.hdr.udp.uh_sum == 0) { + trace_net_rx_pkt_l4_csum_fix_udp_with_no_checksum(); + return false; + } + l4_cso = offsetof(struct udp_header, uh_sum); + trace_net_rx_pkt_l4_csum_fix_udp(l4_cso); + } else { + trace_net_rx_pkt_l4_csum_fix_not_xxp(); + return false; + } + + if (pkt->isip4 && pkt->ip4hdr_info.fragment) { + trace_net_rx_pkt_l4_csum_fix_ip4_fragment(); + return false; + } + + /* Set zero to checksum word */ + iov_from_buf(pkt->vec, pkt->vec_len, + pkt->l4hdr_off + l4_cso, + &csum, sizeof(csum)); + + /* Calculate L4 checksum */ + csum = cpu_to_be16(_net_rx_pkt_calc_l4_csum(pkt)); + + /* Set calculated checksum to checksum word */ + iov_from_buf(pkt->vec, pkt->vec_len, + pkt->l4hdr_off + l4_cso, + &csum, sizeof(csum)); + + trace_net_rx_pkt_l4_csum_fix_csum(pkt->l4hdr_off + l4_cso, csum); + + return true; +} diff --git a/hw/net/net_rx_pkt.h b/hw/net/net_rx_pkt.h new file mode 100644 index 000000000..7adf0fad5 --- /dev/null +++ b/hw/net/net_rx_pkt.h @@ -0,0 +1,363 @@ +/* + * QEMU RX packets abstraction + * + * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) + * + * Developed by Daynix Computing LTD (http://www.daynix.com) + * + * Authors: + * Dmitry Fleytman <dmitry@daynix.com> + * Tamir Shomer <tamirs@daynix.com> + * Yan Vugenfirer <yan@daynix.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef NET_RX_PKT_H +#define NET_RX_PKT_H + +#include "net/eth.h" + +/* defines to enable packet dump functions */ +/*#define NET_RX_PKT_DEBUG*/ + +struct NetRxPkt; + +/** + * Clean all rx packet resources + * + * @pkt: packet + * + */ +void net_rx_pkt_uninit(struct NetRxPkt *pkt); + +/** + * Init function for rx packet functionality + * + * @pkt: packet pointer + * @has_virt_hdr: device uses virtio header + * + */ +void net_rx_pkt_init(struct NetRxPkt **pkt, bool has_virt_hdr); + +/** + * returns total length of data attached to rx context + * + * @pkt: packet + * + * Return: nothing + * + */ +size_t net_rx_pkt_get_total_len(struct NetRxPkt *pkt); + +/** + * parse and set packet analysis results + * + * @pkt: packet + * @data: pointer to the data buffer to be parsed + * @len: data length + * + */ +void net_rx_pkt_set_protocols(struct NetRxPkt *pkt, const void *data, + size_t len); + +/** + * fetches packet analysis results + * + * @pkt: packet + * @isip4: whether the packet given is IPv4 + * @isip6: whether the packet given is IPv6 + * @isudp: whether the packet given is UDP + * @istcp: whether the packet given is TCP + * + */ +void net_rx_pkt_get_protocols(struct NetRxPkt *pkt, + bool *isip4, bool *isip6, + bool *isudp, bool *istcp); + +/** +* fetches L3 header offset +* +* @pkt: packet +* +*/ +size_t net_rx_pkt_get_l3_hdr_offset(struct NetRxPkt *pkt); + +/** +* fetches L4 header offset +* +* @pkt: packet +* +*/ +size_t net_rx_pkt_get_l4_hdr_offset(struct NetRxPkt *pkt); + +/** +* fetches L5 header offset +* +* @pkt: packet +* +*/ +size_t net_rx_pkt_get_l5_hdr_offset(struct NetRxPkt *pkt); + +/** + * fetches IP6 header analysis results + * + * Return: pointer to analysis results structure which is stored in internal + * packet area. + * + */ +eth_ip6_hdr_info *net_rx_pkt_get_ip6_info(struct NetRxPkt *pkt); + +/** + * fetches IP4 header analysis results + * + * Return: pointer to analysis results structure which is stored in internal + * packet area. + * + */ +eth_ip4_hdr_info *net_rx_pkt_get_ip4_info(struct NetRxPkt *pkt); + +/** + * fetches L4 header analysis results + * + * Return: pointer to analysis results structure which is stored in internal + * packet area. + * + */ +eth_l4_hdr_info *net_rx_pkt_get_l4_info(struct NetRxPkt *pkt); + +typedef enum { + NetPktRssIpV4, + NetPktRssIpV4Tcp, + NetPktRssIpV6Tcp, + NetPktRssIpV6, + NetPktRssIpV6Ex +} NetRxPktRssType; + +/** +* calculates RSS hash for packet +* +* @pkt: packet +* @type: RSS hash type +* +* Return: Toeplitz RSS hash. +* +*/ +uint32_t +net_rx_pkt_calc_rss_hash(struct NetRxPkt *pkt, + NetRxPktRssType type, + uint8_t *key); + +/** +* fetches IP identification for the packet +* +* @pkt: packet +* +*/ +uint16_t net_rx_pkt_get_ip_id(struct NetRxPkt *pkt); + +/** +* check if given packet is a TCP ACK packet +* +* @pkt: packet +* +*/ +bool net_rx_pkt_is_tcp_ack(struct NetRxPkt *pkt); + +/** +* check if given packet contains TCP data +* +* @pkt: packet +* +*/ +bool net_rx_pkt_has_tcp_data(struct NetRxPkt *pkt); + +/** + * returns virtio header stored in rx context + * + * @pkt: packet + * @ret: virtio header + * + */ +struct virtio_net_hdr *net_rx_pkt_get_vhdr(struct NetRxPkt *pkt); + +/** + * returns packet type + * + * @pkt: packet + * @ret: packet type + * + */ +eth_pkt_types_e net_rx_pkt_get_packet_type(struct NetRxPkt *pkt); + +/** + * returns vlan tag + * + * @pkt: packet + * @ret: VLAN tag + * + */ +uint16_t net_rx_pkt_get_vlan_tag(struct NetRxPkt *pkt); + +/** + * tells whether vlan was stripped from the packet + * + * @pkt: packet + * @ret: VLAN stripped sign + * + */ +bool net_rx_pkt_is_vlan_stripped(struct NetRxPkt *pkt); + +/** + * notifies caller if the packet has virtio header + * + * @pkt: packet + * @ret: true if packet has virtio header, false otherwize + * + */ +bool net_rx_pkt_has_virt_hdr(struct NetRxPkt *pkt); + +/** +* attach scatter-gather data to rx packet +* +* @pkt: packet +* @iov: received data scatter-gather list +* @iovcnt number of elements in iov +* @iovoff data start offset in the iov +* @strip_vlan: should the module strip vlan from data +* +*/ +void net_rx_pkt_attach_iovec(struct NetRxPkt *pkt, + const struct iovec *iov, + int iovcnt, size_t iovoff, + bool strip_vlan); + +/** +* attach scatter-gather data to rx packet +* +* @pkt: packet +* @iov: received data scatter-gather list +* @iovcnt number of elements in iov +* @iovoff data start offset in the iov +* @strip_vlan: should the module strip vlan from data +* @vet: VLAN tag Ethernet type +* +*/ +void net_rx_pkt_attach_iovec_ex(struct NetRxPkt *pkt, + const struct iovec *iov, int iovcnt, + size_t iovoff, bool strip_vlan, + uint16_t vet); + +/** + * attach data to rx packet + * + * @pkt: packet + * @data: pointer to the data buffer + * @len: data length + * @strip_vlan: should the module strip vlan from data + * + */ +static inline void +net_rx_pkt_attach_data(struct NetRxPkt *pkt, const void *data, + size_t len, bool strip_vlan) +{ + const struct iovec iov = { + .iov_base = (void *) data, + .iov_len = len + }; + + net_rx_pkt_attach_iovec(pkt, &iov, 1, 0, strip_vlan); +} + +/** + * returns io vector that holds the attached data + * + * @pkt: packet + * @ret: pointer to IOVec + * + */ +struct iovec *net_rx_pkt_get_iovec(struct NetRxPkt *pkt); + +/** +* returns io vector length that holds the attached data +* +* @pkt: packet +* @ret: IOVec length +* +*/ +uint16_t net_rx_pkt_get_iovec_len(struct NetRxPkt *pkt); + +/** + * prints rx packet data if debug is enabled + * + * @pkt: packet + * + */ +void net_rx_pkt_dump(struct NetRxPkt *pkt); + +/** + * copy passed vhdr data to packet context + * + * @pkt: packet + * @vhdr: VHDR buffer + * + */ +void net_rx_pkt_set_vhdr(struct NetRxPkt *pkt, + struct virtio_net_hdr *vhdr); + +/** +* copy passed vhdr data to packet context +* +* @pkt: packet +* @iov: VHDR iov +* @iovcnt: VHDR iov array size +* +*/ +void net_rx_pkt_set_vhdr_iovec(struct NetRxPkt *pkt, + const struct iovec *iov, int iovcnt); + +/** + * save packet type in packet context + * + * @pkt: packet + * @packet_type: the packet type + * + */ +void net_rx_pkt_set_packet_type(struct NetRxPkt *pkt, + eth_pkt_types_e packet_type); + +/** +* validate TCP/UDP checksum of the packet +* +* @pkt: packet +* @csum_valid: checksum validation result +* @ret: true if validation was performed, false in case packet is +* not TCP/UDP or checksum validation is not possible +* +*/ +bool net_rx_pkt_validate_l4_csum(struct NetRxPkt *pkt, bool *csum_valid); + +/** +* validate IPv4 checksum of the packet +* +* @pkt: packet +* @csum_valid: checksum validation result +* @ret: true if validation was performed, false in case packet is +* not TCP/UDP or checksum validation is not possible +* +*/ +bool net_rx_pkt_validate_l3_csum(struct NetRxPkt *pkt, bool *csum_valid); + +/** +* fix IPv4 checksum of the packet +* +* @pkt: packet +* @ret: true if checksum was fixed, false in case packet is +* not TCP/UDP or checksum correction is not possible +* +*/ +bool net_rx_pkt_fix_l4_csum(struct NetRxPkt *pkt); + +#endif diff --git a/hw/net/vmxnet_tx_pkt.c b/hw/net/net_tx_pkt.c index 91e1e08fd..20b25496e 100644 --- a/hw/net/vmxnet_tx_pkt.c +++ b/hw/net/net_tx_pkt.c @@ -1,5 +1,5 @@ /* - * QEMU VMWARE VMXNET* paravirtual NICs - TX packets abstractions + * QEMU TX packets abstractions * * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) * @@ -16,24 +16,24 @@ */ #include "qemu/osdep.h" -#include "hw/hw.h" -#include "vmxnet_tx_pkt.h" +#include "net_tx_pkt.h" #include "net/eth.h" -#include "qemu-common.h" -#include "qemu/iov.h" #include "net/checksum.h" #include "net/tap.h" #include "net/net.h" +#include "hw/pci/pci.h" enum { - VMXNET_TX_PKT_VHDR_FRAG = 0, - VMXNET_TX_PKT_L2HDR_FRAG, - VMXNET_TX_PKT_L3HDR_FRAG, - VMXNET_TX_PKT_PL_START_FRAG + NET_TX_PKT_VHDR_FRAG = 0, + NET_TX_PKT_L2HDR_FRAG, + NET_TX_PKT_L3HDR_FRAG, + NET_TX_PKT_PL_START_FRAG }; /* TX packet private context */ -struct VmxnetTxPkt { +struct NetTxPkt { + PCIDevice *pci_dev; + struct virtio_net_hdr virt_hdr; bool has_virt_hdr; @@ -44,6 +44,7 @@ struct VmxnetTxPkt { struct iovec *vec; uint8_t l2_hdr[ETH_MAX_L2_HDR_LEN]; + uint8_t l3_hdr[ETH_MAX_IP_DGRAM_LEN]; uint32_t payload_len; @@ -53,32 +54,34 @@ struct VmxnetTxPkt { uint16_t hdr_len; eth_pkt_types_e packet_type; uint8_t l4proto; + + bool is_loopback; }; -void vmxnet_tx_pkt_init(struct VmxnetTxPkt **pkt, uint32_t max_frags, - bool has_virt_hdr) +void net_tx_pkt_init(struct NetTxPkt **pkt, PCIDevice *pci_dev, + uint32_t max_frags, bool has_virt_hdr) { - struct VmxnetTxPkt *p = g_malloc0(sizeof *p); + struct NetTxPkt *p = g_malloc0(sizeof *p); + + p->pci_dev = pci_dev; - p->vec = g_malloc((sizeof *p->vec) * - (max_frags + VMXNET_TX_PKT_PL_START_FRAG)); + p->vec = g_new(struct iovec, max_frags + NET_TX_PKT_PL_START_FRAG); - p->raw = g_malloc((sizeof *p->raw) * max_frags); + p->raw = g_new(struct iovec, max_frags); p->max_payload_frags = max_frags; p->max_raw_frags = max_frags; p->has_virt_hdr = has_virt_hdr; - p->vec[VMXNET_TX_PKT_VHDR_FRAG].iov_base = &p->virt_hdr; - p->vec[VMXNET_TX_PKT_VHDR_FRAG].iov_len = + p->vec[NET_TX_PKT_VHDR_FRAG].iov_base = &p->virt_hdr; + p->vec[NET_TX_PKT_VHDR_FRAG].iov_len = p->has_virt_hdr ? sizeof p->virt_hdr : 0; - p->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_base = &p->l2_hdr; - p->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base = NULL; - p->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len = 0; + p->vec[NET_TX_PKT_L2HDR_FRAG].iov_base = &p->l2_hdr; + p->vec[NET_TX_PKT_L3HDR_FRAG].iov_base = &p->l3_hdr; *pkt = p; } -void vmxnet_tx_pkt_uninit(struct VmxnetTxPkt *pkt) +void net_tx_pkt_uninit(struct NetTxPkt *pkt) { if (pkt) { g_free(pkt->vec); @@ -87,49 +90,63 @@ void vmxnet_tx_pkt_uninit(struct VmxnetTxPkt *pkt) } } -void vmxnet_tx_pkt_update_ip_checksums(struct VmxnetTxPkt *pkt) +void net_tx_pkt_update_ip_hdr_checksum(struct NetTxPkt *pkt) { uint16_t csum; - uint32_t ph_raw_csum; assert(pkt); - uint8_t gso_type = pkt->virt_hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN; struct ip_header *ip_hdr; + ip_hdr = pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base; - if (VIRTIO_NET_HDR_GSO_TCPV4 != gso_type && - VIRTIO_NET_HDR_GSO_UDP != gso_type) { - return; - } + ip_hdr->ip_len = cpu_to_be16(pkt->payload_len + + pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len); - ip_hdr = pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base; + ip_hdr->ip_sum = 0; + csum = net_raw_checksum((uint8_t *)ip_hdr, + pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len); + ip_hdr->ip_sum = cpu_to_be16(csum); +} + +void net_tx_pkt_update_ip_checksums(struct NetTxPkt *pkt) +{ + uint16_t csum; + uint32_t cntr, cso; + assert(pkt); + uint8_t gso_type = pkt->virt_hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN; + void *ip_hdr = pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base; - if (pkt->payload_len + pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len > + if (pkt->payload_len + pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len > ETH_MAX_IP_DGRAM_LEN) { return; } - ip_hdr->ip_len = cpu_to_be16(pkt->payload_len + - pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len); - - /* Calculate IP header checksum */ - ip_hdr->ip_sum = 0; - csum = net_raw_checksum((uint8_t *)ip_hdr, - pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len); - ip_hdr->ip_sum = cpu_to_be16(csum); + if (gso_type == VIRTIO_NET_HDR_GSO_TCPV4 || + gso_type == VIRTIO_NET_HDR_GSO_UDP) { + /* Calculate IP header checksum */ + net_tx_pkt_update_ip_hdr_checksum(pkt); + + /* Calculate IP pseudo header checksum */ + cntr = eth_calc_ip4_pseudo_hdr_csum(ip_hdr, pkt->payload_len, &cso); + csum = cpu_to_be16(~net_checksum_finish(cntr)); + } else if (gso_type == VIRTIO_NET_HDR_GSO_TCPV6) { + /* Calculate IP pseudo header checksum */ + cntr = eth_calc_ip6_pseudo_hdr_csum(ip_hdr, pkt->payload_len, + IP_PROTO_TCP, &cso); + csum = cpu_to_be16(~net_checksum_finish(cntr)); + } else { + return; + } - /* Calculate IP pseudo header checksum */ - ph_raw_csum = eth_calc_pseudo_hdr_csum(ip_hdr, pkt->payload_len); - csum = cpu_to_be16(~net_checksum_finish(ph_raw_csum)); - iov_from_buf(&pkt->vec[VMXNET_TX_PKT_PL_START_FRAG], pkt->payload_frags, + iov_from_buf(&pkt->vec[NET_TX_PKT_PL_START_FRAG], pkt->payload_frags, pkt->virt_hdr.csum_offset, &csum, sizeof(csum)); } -static void vmxnet_tx_pkt_calculate_hdr_len(struct VmxnetTxPkt *pkt) +static void net_tx_pkt_calculate_hdr_len(struct NetTxPkt *pkt) { - pkt->hdr_len = pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_len + - pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len; + pkt->hdr_len = pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len + + pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len; } -static bool vmxnet_tx_pkt_parse_headers(struct VmxnetTxPkt *pkt) +static bool net_tx_pkt_parse_headers(struct NetTxPkt *pkt) { struct iovec *l2_hdr, *l3_hdr; size_t bytes_read; @@ -138,8 +155,8 @@ static bool vmxnet_tx_pkt_parse_headers(struct VmxnetTxPkt *pkt) assert(pkt); - l2_hdr = &pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG]; - l3_hdr = &pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG]; + l2_hdr = &pkt->vec[NET_TX_PKT_L2HDR_FRAG]; + l3_hdr = &pkt->vec[NET_TX_PKT_L3HDR_FRAG]; bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, 0, l2_hdr->iov_base, ETH_MAX_L2_HDR_LEN); @@ -160,15 +177,19 @@ static bool vmxnet_tx_pkt_parse_headers(struct VmxnetTxPkt *pkt) if (bytes_read < l2_hdr->iov_len) { l2_hdr->iov_len = 0; + l3_hdr->iov_len = 0; + pkt->packet_type = ETH_PKT_UCAST; return false; + } else { + l2_hdr->iov_len = ETH_MAX_L2_HDR_LEN; + l2_hdr->iov_len = eth_get_l2_hdr_length(l2_hdr->iov_base); + pkt->packet_type = get_eth_packet_type(l2_hdr->iov_base); } - l3_proto = eth_get_l3_proto(l2_hdr->iov_base, l2_hdr->iov_len); + l3_proto = eth_get_l3_proto(l2_hdr, 1, l2_hdr->iov_len); switch (l3_proto) { case ETH_P_IP: - l3_hdr->iov_base = g_malloc(ETH_MAX_IP4_HDR_LEN); - bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, l2_hdr->iov_len, l3_hdr->iov_base, sizeof(struct ip_header)); @@ -178,27 +199,45 @@ static bool vmxnet_tx_pkt_parse_headers(struct VmxnetTxPkt *pkt) } l3_hdr->iov_len = IP_HDR_GET_LEN(l3_hdr->iov_base); - pkt->l4proto = ((struct ip_header *) l3_hdr->iov_base)->ip_p; - /* copy optional IPv4 header data */ - bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, - l2_hdr->iov_len + sizeof(struct ip_header), - l3_hdr->iov_base + sizeof(struct ip_header), - l3_hdr->iov_len - sizeof(struct ip_header)); - if (bytes_read < l3_hdr->iov_len - sizeof(struct ip_header)) { + if (l3_hdr->iov_len < sizeof(struct ip_header)) { l3_hdr->iov_len = 0; return false; } + + pkt->l4proto = ((struct ip_header *) l3_hdr->iov_base)->ip_p; + + if (IP_HDR_GET_LEN(l3_hdr->iov_base) != sizeof(struct ip_header)) { + /* copy optional IPv4 header data if any*/ + bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, + l2_hdr->iov_len + sizeof(struct ip_header), + l3_hdr->iov_base + sizeof(struct ip_header), + l3_hdr->iov_len - sizeof(struct ip_header)); + if (bytes_read < l3_hdr->iov_len - sizeof(struct ip_header)) { + l3_hdr->iov_len = 0; + return false; + } + } + break; case ETH_P_IPV6: + { + eth_ip6_hdr_info hdrinfo; + if (!eth_parse_ipv6_hdr(pkt->raw, pkt->raw_frags, l2_hdr->iov_len, - &pkt->l4proto, &full_ip6hdr_len)) { + &hdrinfo)) { l3_hdr->iov_len = 0; return false; } - l3_hdr->iov_base = g_malloc(full_ip6hdr_len); + pkt->l4proto = hdrinfo.l4proto; + full_ip6hdr_len = hdrinfo.full_hdr_len; + + if (full_ip6hdr_len > ETH_MAX_IP_DGRAM_LEN) { + l3_hdr->iov_len = 0; + return false; + } bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, l2_hdr->iov_len, l3_hdr->iov_base, full_ip6hdr_len); @@ -210,67 +249,62 @@ static bool vmxnet_tx_pkt_parse_headers(struct VmxnetTxPkt *pkt) l3_hdr->iov_len = full_ip6hdr_len; } break; - + } default: l3_hdr->iov_len = 0; break; } - vmxnet_tx_pkt_calculate_hdr_len(pkt); - pkt->packet_type = get_eth_packet_type(l2_hdr->iov_base); + net_tx_pkt_calculate_hdr_len(pkt); return true; } -static bool vmxnet_tx_pkt_rebuild_payload(struct VmxnetTxPkt *pkt) +static void net_tx_pkt_rebuild_payload(struct NetTxPkt *pkt) { - size_t payload_len = iov_size(pkt->raw, pkt->raw_frags) - pkt->hdr_len; - - pkt->payload_frags = iov_copy(&pkt->vec[VMXNET_TX_PKT_PL_START_FRAG], + pkt->payload_len = iov_size(pkt->raw, pkt->raw_frags) - pkt->hdr_len; + pkt->payload_frags = iov_copy(&pkt->vec[NET_TX_PKT_PL_START_FRAG], pkt->max_payload_frags, pkt->raw, pkt->raw_frags, - pkt->hdr_len, payload_len); + pkt->hdr_len, pkt->payload_len); +} - if (pkt->payload_frags != (uint32_t) -1) { - pkt->payload_len = payload_len; +bool net_tx_pkt_parse(struct NetTxPkt *pkt) +{ + if (net_tx_pkt_parse_headers(pkt)) { + net_tx_pkt_rebuild_payload(pkt); return true; } else { return false; } } -bool vmxnet_tx_pkt_parse(struct VmxnetTxPkt *pkt) -{ - return vmxnet_tx_pkt_parse_headers(pkt) && - vmxnet_tx_pkt_rebuild_payload(pkt); -} - -struct virtio_net_hdr *vmxnet_tx_pkt_get_vhdr(struct VmxnetTxPkt *pkt) +struct virtio_net_hdr *net_tx_pkt_get_vhdr(struct NetTxPkt *pkt) { assert(pkt); return &pkt->virt_hdr; } -static uint8_t vmxnet_tx_pkt_get_gso_type(struct VmxnetTxPkt *pkt, +static uint8_t net_tx_pkt_get_gso_type(struct NetTxPkt *pkt, bool tso_enable) { uint8_t rc = VIRTIO_NET_HDR_GSO_NONE; uint16_t l3_proto; - l3_proto = eth_get_l3_proto(pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_base, - pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_len); + l3_proto = eth_get_l3_proto(&pkt->vec[NET_TX_PKT_L2HDR_FRAG], 1, + pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len); if (!tso_enable) { goto func_exit; } - rc = eth_get_gso_type(l3_proto, pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base, + rc = eth_get_gso_type(l3_proto, pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base, pkt->l4proto); func_exit: return rc; } -void vmxnet_tx_pkt_build_vheader(struct VmxnetTxPkt *pkt, bool tso_enable, +void net_tx_pkt_build_vheader(struct NetTxPkt *pkt, bool tso_enable, bool csum_enable, uint32_t gso_size) { struct tcp_hdr l4hdr; @@ -279,7 +313,7 @@ void vmxnet_tx_pkt_build_vheader(struct VmxnetTxPkt *pkt, bool tso_enable, /* csum has to be enabled if tso is. */ assert(csum_enable || !tso_enable); - pkt->virt_hdr.gso_type = vmxnet_tx_pkt_get_gso_type(pkt, tso_enable); + pkt->virt_hdr.gso_type = net_tx_pkt_get_gso_type(pkt, tso_enable); switch (pkt->virt_hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { case VIRTIO_NET_HDR_GSO_NONE: @@ -288,16 +322,16 @@ void vmxnet_tx_pkt_build_vheader(struct VmxnetTxPkt *pkt, bool tso_enable, break; case VIRTIO_NET_HDR_GSO_UDP: - pkt->virt_hdr.gso_size = IP_FRAG_ALIGN_SIZE(gso_size); + pkt->virt_hdr.gso_size = gso_size; pkt->virt_hdr.hdr_len = pkt->hdr_len + sizeof(struct udp_header); break; case VIRTIO_NET_HDR_GSO_TCPV4: case VIRTIO_NET_HDR_GSO_TCPV6: - iov_to_buf(&pkt->vec[VMXNET_TX_PKT_PL_START_FRAG], pkt->payload_frags, + iov_to_buf(&pkt->vec[NET_TX_PKT_PL_START_FRAG], pkt->payload_frags, 0, &l4hdr, sizeof(l4hdr)); pkt->virt_hdr.hdr_len = pkt->hdr_len + l4hdr.th_off * sizeof(uint32_t); - pkt->virt_hdr.gso_size = IP_FRAG_ALIGN_SIZE(gso_size); + pkt->virt_hdr.gso_size = gso_size; break; default: @@ -322,23 +356,24 @@ void vmxnet_tx_pkt_build_vheader(struct VmxnetTxPkt *pkt, bool tso_enable, } } -void vmxnet_tx_pkt_setup_vlan_header(struct VmxnetTxPkt *pkt, uint16_t vlan) +void net_tx_pkt_setup_vlan_header_ex(struct NetTxPkt *pkt, + uint16_t vlan, uint16_t vlan_ethtype) { bool is_new; assert(pkt); - eth_setup_vlan_headers(pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_base, - vlan, &is_new); + eth_setup_vlan_headers_ex(pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_base, + vlan, vlan_ethtype, &is_new); /* update l2hdrlen */ if (is_new) { pkt->hdr_len += sizeof(struct vlan_header); - pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_len += + pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len += sizeof(struct vlan_header); } } -bool vmxnet_tx_pkt_add_raw_fragment(struct VmxnetTxPkt *pkt, hwaddr pa, +bool net_tx_pkt_add_raw_fragment(struct NetTxPkt *pkt, hwaddr pa, size_t len) { hwaddr mapped_len = 0; @@ -353,44 +388,50 @@ bool vmxnet_tx_pkt_add_raw_fragment(struct VmxnetTxPkt *pkt, hwaddr pa, ventry = &pkt->raw[pkt->raw_frags]; mapped_len = len; - ventry->iov_base = cpu_physical_memory_map(pa, &mapped_len, false); - ventry->iov_len = mapped_len; - pkt->raw_frags += !!ventry->iov_base; + ventry->iov_base = pci_dma_map(pkt->pci_dev, pa, + &mapped_len, DMA_DIRECTION_TO_DEVICE); - if ((ventry->iov_base == NULL) || (len != mapped_len)) { + if ((ventry->iov_base != NULL) && (len == mapped_len)) { + ventry->iov_len = mapped_len; + pkt->raw_frags++; + return true; + } else { return false; } +} - return true; +bool net_tx_pkt_has_fragments(struct NetTxPkt *pkt) +{ + return pkt->raw_frags > 0; } -eth_pkt_types_e vmxnet_tx_pkt_get_packet_type(struct VmxnetTxPkt *pkt) +eth_pkt_types_e net_tx_pkt_get_packet_type(struct NetTxPkt *pkt) { assert(pkt); return pkt->packet_type; } -size_t vmxnet_tx_pkt_get_total_len(struct VmxnetTxPkt *pkt) +size_t net_tx_pkt_get_total_len(struct NetTxPkt *pkt) { assert(pkt); return pkt->hdr_len + pkt->payload_len; } -void vmxnet_tx_pkt_dump(struct VmxnetTxPkt *pkt) +void net_tx_pkt_dump(struct NetTxPkt *pkt) { -#ifdef VMXNET_TX_PKT_DEBUG +#ifdef NET_TX_PKT_DEBUG assert(pkt); printf("TX PKT: hdr_len: %d, pkt_type: 0x%X, l2hdr_len: %lu, " "l3hdr_len: %lu, payload_len: %u\n", pkt->hdr_len, pkt->packet_type, - pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_len, - pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len, pkt->payload_len); + pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len, + pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len, pkt->payload_len); #endif } -void vmxnet_tx_pkt_reset(struct VmxnetTxPkt *pkt) +void net_tx_pkt_reset(struct NetTxPkt *pkt) { int i; @@ -401,38 +442,31 @@ void vmxnet_tx_pkt_reset(struct VmxnetTxPkt *pkt) memset(&pkt->virt_hdr, 0, sizeof(pkt->virt_hdr)); - g_free(pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base); - pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base = NULL; - assert(pkt->vec); - for (i = VMXNET_TX_PKT_L2HDR_FRAG; - i < pkt->payload_frags + VMXNET_TX_PKT_PL_START_FRAG; i++) { - pkt->vec[i].iov_len = 0; - } + pkt->payload_len = 0; pkt->payload_frags = 0; assert(pkt->raw); for (i = 0; i < pkt->raw_frags; i++) { assert(pkt->raw[i].iov_base); - cpu_physical_memory_unmap(pkt->raw[i].iov_base, pkt->raw[i].iov_len, - false, pkt->raw[i].iov_len); - pkt->raw[i].iov_len = 0; + pci_dma_unmap(pkt->pci_dev, pkt->raw[i].iov_base, pkt->raw[i].iov_len, + DMA_DIRECTION_TO_DEVICE, 0); } pkt->raw_frags = 0; pkt->hdr_len = 0; - pkt->packet_type = 0; pkt->l4proto = 0; } -static void vmxnet_tx_pkt_do_sw_csum(struct VmxnetTxPkt *pkt) +static void net_tx_pkt_do_sw_csum(struct NetTxPkt *pkt) { - struct iovec *iov = &pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG]; + struct iovec *iov = &pkt->vec[NET_TX_PKT_L2HDR_FRAG]; uint32_t csum_cntr; uint16_t csum = 0; + uint32_t cso; /* num of iovec without vhdr */ - uint32_t iov_len = pkt->payload_frags + VMXNET_TX_PKT_PL_START_FRAG - 1; + uint32_t iov_len = pkt->payload_frags + NET_TX_PKT_PL_START_FRAG - 1; uint16_t csl; struct ip_header *iphdr; size_t csum_offset = pkt->virt_hdr.csum_start + pkt->virt_hdr.csum_offset; @@ -443,12 +477,13 @@ static void vmxnet_tx_pkt_do_sw_csum(struct VmxnetTxPkt *pkt) /* Calculate L4 TCP/UDP checksum */ csl = pkt->payload_len; - /* data checksum */ - csum_cntr = - net_checksum_add_iov(iov, iov_len, pkt->virt_hdr.csum_start, csl); /* add pseudo header to csum */ - iphdr = pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base; - csum_cntr += eth_calc_pseudo_hdr_csum(iphdr, csl); + iphdr = pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base; + csum_cntr = eth_calc_ip4_pseudo_hdr_csum(iphdr, csl, &cso); + + /* data checksum */ + csum_cntr += + net_checksum_add_iov(iov, iov_len, pkt->virt_hdr.csum_start, csl, cso); /* Put the checksum obtained into the packet */ csum = cpu_to_be16(net_checksum_finish(csum_cntr)); @@ -456,37 +491,37 @@ static void vmxnet_tx_pkt_do_sw_csum(struct VmxnetTxPkt *pkt) } enum { - VMXNET_TX_PKT_FRAGMENT_L2_HDR_POS = 0, - VMXNET_TX_PKT_FRAGMENT_L3_HDR_POS, - VMXNET_TX_PKT_FRAGMENT_HEADER_NUM + NET_TX_PKT_FRAGMENT_L2_HDR_POS = 0, + NET_TX_PKT_FRAGMENT_L3_HDR_POS, + NET_TX_PKT_FRAGMENT_HEADER_NUM }; -#define VMXNET_MAX_FRAG_SG_LIST (64) +#define NET_MAX_FRAG_SG_LIST (64) -static size_t vmxnet_tx_pkt_fetch_fragment(struct VmxnetTxPkt *pkt, +static size_t net_tx_pkt_fetch_fragment(struct NetTxPkt *pkt, int *src_idx, size_t *src_offset, struct iovec *dst, int *dst_idx) { size_t fetched = 0; struct iovec *src = pkt->vec; - *dst_idx = VMXNET_TX_PKT_FRAGMENT_HEADER_NUM; + *dst_idx = NET_TX_PKT_FRAGMENT_HEADER_NUM; - while (fetched < pkt->virt_hdr.gso_size) { + while (fetched < IP_FRAG_ALIGN_SIZE(pkt->virt_hdr.gso_size)) { /* no more place in fragment iov */ - if (*dst_idx == VMXNET_MAX_FRAG_SG_LIST) { + if (*dst_idx == NET_MAX_FRAG_SG_LIST) { break; } /* no more data in iovec */ - if (*src_idx == (pkt->payload_frags + VMXNET_TX_PKT_PL_START_FRAG)) { + if (*src_idx == (pkt->payload_frags + NET_TX_PKT_PL_START_FRAG)) { break; } dst[*dst_idx].iov_base = src[*src_idx].iov_base + *src_offset; dst[*dst_idx].iov_len = MIN(src[*src_idx].iov_len - *src_offset, - pkt->virt_hdr.gso_size - fetched); + IP_FRAG_ALIGN_SIZE(pkt->virt_hdr.gso_size) - fetched); *src_offset += dst[*dst_idx].iov_len; fetched += dst[*dst_idx].iov_len; @@ -502,35 +537,45 @@ static size_t vmxnet_tx_pkt_fetch_fragment(struct VmxnetTxPkt *pkt, return fetched; } -static bool vmxnet_tx_pkt_do_sw_fragmentation(struct VmxnetTxPkt *pkt, +static inline void net_tx_pkt_sendv(struct NetTxPkt *pkt, + NetClientState *nc, const struct iovec *iov, int iov_cnt) +{ + if (pkt->is_loopback) { + nc->info->receive_iov(nc, iov, iov_cnt); + } else { + qemu_sendv_packet(nc, iov, iov_cnt); + } +} + +static bool net_tx_pkt_do_sw_fragmentation(struct NetTxPkt *pkt, NetClientState *nc) { - struct iovec fragment[VMXNET_MAX_FRAG_SG_LIST]; + struct iovec fragment[NET_MAX_FRAG_SG_LIST]; size_t fragment_len = 0; bool more_frags = false; /* some pointers for shorter code */ void *l2_iov_base, *l3_iov_base; size_t l2_iov_len, l3_iov_len; - int src_idx = VMXNET_TX_PKT_PL_START_FRAG, dst_idx; + int src_idx = NET_TX_PKT_PL_START_FRAG, dst_idx; size_t src_offset = 0; size_t fragment_offset = 0; - l2_iov_base = pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_base; - l2_iov_len = pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_len; - l3_iov_base = pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base; - l3_iov_len = pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len; + l2_iov_base = pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_base; + l2_iov_len = pkt->vec[NET_TX_PKT_L2HDR_FRAG].iov_len; + l3_iov_base = pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base; + l3_iov_len = pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len; /* Copy headers */ - fragment[VMXNET_TX_PKT_FRAGMENT_L2_HDR_POS].iov_base = l2_iov_base; - fragment[VMXNET_TX_PKT_FRAGMENT_L2_HDR_POS].iov_len = l2_iov_len; - fragment[VMXNET_TX_PKT_FRAGMENT_L3_HDR_POS].iov_base = l3_iov_base; - fragment[VMXNET_TX_PKT_FRAGMENT_L3_HDR_POS].iov_len = l3_iov_len; + fragment[NET_TX_PKT_FRAGMENT_L2_HDR_POS].iov_base = l2_iov_base; + fragment[NET_TX_PKT_FRAGMENT_L2_HDR_POS].iov_len = l2_iov_len; + fragment[NET_TX_PKT_FRAGMENT_L3_HDR_POS].iov_base = l3_iov_base; + fragment[NET_TX_PKT_FRAGMENT_L3_HDR_POS].iov_len = l3_iov_len; /* Put as much data as possible and send */ do { - fragment_len = vmxnet_tx_pkt_fetch_fragment(pkt, &src_idx, &src_offset, + fragment_len = net_tx_pkt_fetch_fragment(pkt, &src_idx, &src_offset, fragment, &dst_idx); more_frags = (fragment_offset + fragment_len < pkt->payload_len); @@ -540,22 +585,22 @@ static bool vmxnet_tx_pkt_do_sw_fragmentation(struct VmxnetTxPkt *pkt, eth_fix_ip4_checksum(l3_iov_base, l3_iov_len); - qemu_sendv_packet(nc, fragment, dst_idx); + net_tx_pkt_sendv(pkt, nc, fragment, dst_idx); fragment_offset += fragment_len; - } while (more_frags); + } while (fragment_len && more_frags); return true; } -bool vmxnet_tx_pkt_send(struct VmxnetTxPkt *pkt, NetClientState *nc) +bool net_tx_pkt_send(struct NetTxPkt *pkt, NetClientState *nc) { assert(pkt); if (!pkt->has_virt_hdr && pkt->virt_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { - vmxnet_tx_pkt_do_sw_csum(pkt); + net_tx_pkt_do_sw_csum(pkt); } /* @@ -565,17 +610,28 @@ bool vmxnet_tx_pkt_send(struct VmxnetTxPkt *pkt, NetClientState *nc) if (VIRTIO_NET_HDR_GSO_NONE != pkt->virt_hdr.gso_type) { if (pkt->payload_len > ETH_MAX_IP_DGRAM_LEN - - pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len) { + pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len) { return false; } } if (pkt->has_virt_hdr || pkt->virt_hdr.gso_type == VIRTIO_NET_HDR_GSO_NONE) { - qemu_sendv_packet(nc, pkt->vec, - pkt->payload_frags + VMXNET_TX_PKT_PL_START_FRAG); + net_tx_pkt_sendv(pkt, nc, pkt->vec, + pkt->payload_frags + NET_TX_PKT_PL_START_FRAG); return true; } - return vmxnet_tx_pkt_do_sw_fragmentation(pkt, nc); + return net_tx_pkt_do_sw_fragmentation(pkt, nc); +} + +bool net_tx_pkt_send_loopback(struct NetTxPkt *pkt, NetClientState *nc) +{ + bool res; + + pkt->is_loopback = true; + res = net_tx_pkt_send(pkt, nc); + pkt->is_loopback = false; + + return res; } diff --git a/hw/net/net_tx_pkt.h b/hw/net/net_tx_pkt.h new file mode 100644 index 000000000..212ecc62f --- /dev/null +++ b/hw/net/net_tx_pkt.h @@ -0,0 +1,190 @@ +/* + * QEMU TX packets abstraction + * + * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) + * + * Developed by Daynix Computing LTD (http://www.daynix.com) + * + * Authors: + * Dmitry Fleytman <dmitry@daynix.com> + * Tamir Shomer <tamirs@daynix.com> + * Yan Vugenfirer <yan@daynix.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef NET_TX_PKT_H +#define NET_TX_PKT_H + +#include "net/eth.h" +#include "exec/hwaddr.h" + +/* define to enable packet dump functions */ +/*#define NET_TX_PKT_DEBUG*/ + +struct NetTxPkt; + +/** + * Init function for tx packet functionality + * + * @pkt: packet pointer + * @pci_dev: PCI device processing this packet + * @max_frags: max tx ip fragments + * @has_virt_hdr: device uses virtio header. + */ +void net_tx_pkt_init(struct NetTxPkt **pkt, PCIDevice *pci_dev, + uint32_t max_frags, bool has_virt_hdr); + +/** + * Clean all tx packet resources. + * + * @pkt: packet. + */ +void net_tx_pkt_uninit(struct NetTxPkt *pkt); + +/** + * get virtio header + * + * @pkt: packet + * @ret: virtio header + */ +struct virtio_net_hdr *net_tx_pkt_get_vhdr(struct NetTxPkt *pkt); + +/** + * build virtio header (will be stored in module context) + * + * @pkt: packet + * @tso_enable: TSO enabled + * @csum_enable: CSO enabled + * @gso_size: MSS size for TSO + * + */ +void net_tx_pkt_build_vheader(struct NetTxPkt *pkt, bool tso_enable, + bool csum_enable, uint32_t gso_size); + +/** +* updates vlan tag, and adds vlan header with custom ethernet type +* in case it is missing. +* +* @pkt: packet +* @vlan: VLAN tag +* @vlan_ethtype: VLAN header Ethernet type +* +*/ +void net_tx_pkt_setup_vlan_header_ex(struct NetTxPkt *pkt, + uint16_t vlan, uint16_t vlan_ethtype); + +/** +* updates vlan tag, and adds vlan header in case it is missing +* +* @pkt: packet +* @vlan: VLAN tag +* +*/ +static inline void +net_tx_pkt_setup_vlan_header(struct NetTxPkt *pkt, uint16_t vlan) +{ + net_tx_pkt_setup_vlan_header_ex(pkt, vlan, ETH_P_VLAN); +} + +/** + * populate data fragment into pkt context. + * + * @pkt: packet + * @pa: physical address of fragment + * @len: length of fragment + * + */ +bool net_tx_pkt_add_raw_fragment(struct NetTxPkt *pkt, hwaddr pa, + size_t len); + +/** + * Fix ip header fields and calculate IP header and pseudo header checksums. + * + * @pkt: packet + * + */ +void net_tx_pkt_update_ip_checksums(struct NetTxPkt *pkt); + +/** + * Calculate the IP header checksum. + * + * @pkt: packet + * + */ +void net_tx_pkt_update_ip_hdr_checksum(struct NetTxPkt *pkt); + +/** + * get length of all populated data. + * + * @pkt: packet + * @ret: total data length + * + */ +size_t net_tx_pkt_get_total_len(struct NetTxPkt *pkt); + +/** + * get packet type + * + * @pkt: packet + * @ret: packet type + * + */ +eth_pkt_types_e net_tx_pkt_get_packet_type(struct NetTxPkt *pkt); + +/** + * prints packet data if debug is enabled + * + * @pkt: packet + * + */ +void net_tx_pkt_dump(struct NetTxPkt *pkt); + +/** + * reset tx packet private context (needed to be called between packets) + * + * @pkt: packet + * + */ +void net_tx_pkt_reset(struct NetTxPkt *pkt); + +/** + * Send packet to qemu. handles sw offloads if vhdr is not supported. + * + * @pkt: packet + * @nc: NetClientState + * @ret: operation result + * + */ +bool net_tx_pkt_send(struct NetTxPkt *pkt, NetClientState *nc); + +/** +* Redirect packet directly to receive path (emulate loopback phy). +* Handles sw offloads if vhdr is not supported. +* +* @pkt: packet +* @nc: NetClientState +* @ret: operation result +* +*/ +bool net_tx_pkt_send_loopback(struct NetTxPkt *pkt, NetClientState *nc); + +/** + * parse raw packet data and analyze offload requirements. + * + * @pkt: packet + * + */ +bool net_tx_pkt_parse(struct NetTxPkt *pkt); + +/** +* indicates if there are data fragments held by this packet object. +* +* @pkt: packet +* +*/ +bool net_tx_pkt_has_fragments(struct NetTxPkt *pkt); + +#endif diff --git a/hw/net/opencores_eth.c b/hw/net/opencores_eth.c index c6094fbb5..268d6a789 100644 --- a/hw/net/opencores_eth.c +++ b/hw/net/opencores_eth.c @@ -33,6 +33,7 @@ #include "qemu/osdep.h" #include "hw/hw.h" +#include "hw/net/mii.h" #include "hw/sysbus.h" #include "net/net.h" #include "sysemu/sysemu.h" @@ -55,12 +56,6 @@ /* PHY MII registers */ enum { - MII_BMCR, - MII_BMSR, - MII_PHYIDR1, - MII_PHYIDR2, - MII_ANAR, - MII_ANLPAR, MII_REG_MAX = 16, }; @@ -72,10 +67,11 @@ typedef struct Mii { static void mii_set_link(Mii *s, bool link_ok) { if (link_ok) { - s->regs[MII_BMSR] |= 0x4; - s->regs[MII_ANLPAR] |= 0x01e1; + s->regs[MII_BMSR] |= MII_BMSR_LINK_ST; + s->regs[MII_ANLPAR] |= MII_ANLPAR_TXFD | MII_ANLPAR_TX | + MII_ANLPAR_10FD | MII_ANLPAR_10 | MII_ANLPAR_CSMACD; } else { - s->regs[MII_BMSR] &= ~0x4; + s->regs[MII_BMSR] &= ~MII_BMSR_LINK_ST; s->regs[MII_ANLPAR] &= 0x01ff; } s->link_ok = link_ok; @@ -84,11 +80,14 @@ static void mii_set_link(Mii *s, bool link_ok) static void mii_reset(Mii *s) { memset(s->regs, 0, sizeof(s->regs)); - s->regs[MII_BMCR] = 0x1000; - s->regs[MII_BMSR] = 0x7868; /* no ext regs */ - s->regs[MII_PHYIDR1] = 0x2000; - s->regs[MII_PHYIDR2] = 0x5c90; - s->regs[MII_ANAR] = 0x01e1; + s->regs[MII_BMCR] = MII_BMCR_AUTOEN; + s->regs[MII_BMSR] = MII_BMSR_100TX_FD | MII_BMSR_100TX_HD | + MII_BMSR_10T_FD | MII_BMSR_10T_HD | MII_BMSR_MFPS | + MII_BMSR_AN_COMP | MII_BMSR_AUTONEG; + s->regs[MII_PHYID1] = 0x2000; + s->regs[MII_PHYID2] = 0x5c90; + s->regs[MII_ANAR] = MII_ANAR_TXFD | MII_ANAR_TX | + MII_ANAR_10FD | MII_ANAR_10 | MII_ANAR_CSMACD; mii_set_link(s, s->link_ok); } @@ -98,7 +97,7 @@ static void mii_ro(Mii *s, uint16_t v) static void mii_write_bmcr(Mii *s, uint16_t v) { - if (v & 0x8000) { + if (v & MII_BMCR_RESET) { mii_reset(s); } else { s->regs[MII_BMCR] = v; @@ -110,8 +109,8 @@ static void mii_write_host(Mii *s, unsigned idx, uint16_t v) static void (*reg_write[MII_REG_MAX])(Mii *s, uint16_t v) = { [MII_BMCR] = mii_write_bmcr, [MII_BMSR] = mii_ro, - [MII_PHYIDR1] = mii_ro, - [MII_PHYIDR2] = mii_ro, + [MII_PHYID1] = mii_ro, + [MII_PHYID2] = mii_ro, }; if (idx < MII_REG_MAX) { @@ -474,7 +473,7 @@ static ssize_t open_eth_receive(NetClientState *nc, } static NetClientInfo net_open_eth_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, + .type = NET_CLIENT_DRIVER_NIC, .size = sizeof(NICState), .can_receive = open_eth_can_receive, .receive = open_eth_receive, @@ -483,7 +482,8 @@ static NetClientInfo net_open_eth_info = { static void open_eth_start_xmit(OpenEthState *s, desc *tx) { - uint8_t buf[65536]; + uint8_t *buf = NULL; + uint8_t buffer[0x600]; unsigned len = GET_FIELD(tx->len_flags, TXD_LEN); unsigned tx_len = len; @@ -498,6 +498,11 @@ static void open_eth_start_xmit(OpenEthState *s, desc *tx) trace_open_eth_start_xmit(tx->buf_ptr, len, tx_len); + if (tx_len > sizeof(buffer)) { + buf = g_new(uint8_t, tx_len); + } else { + buf = buffer; + } if (len > tx_len) { len = tx_len; } @@ -506,6 +511,9 @@ static void open_eth_start_xmit(OpenEthState *s, desc *tx) memset(buf + len, 0, tx_len - len); } qemu_send_packet(qemu_get_queue(s->nic), buf, tx_len); + if (tx_len > sizeof(buffer)) { + g_free(buf); + } if (tx->len_flags & TXD_WR) { s->tx_desc = 0; diff --git a/hw/net/pcnet-pci.c b/hw/net/pcnet-pci.c index 595439a65..0acf8a487 100644 --- a/hw/net/pcnet-pci.c +++ b/hw/net/pcnet-pci.c @@ -272,7 +272,7 @@ static void pci_pcnet_uninit(PCIDevice *dev) } static NetClientInfo net_pci_pcnet_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, + .type = NET_CLIENT_DRIVER_NIC, .size = sizeof(NICState), .receive = pcnet_receive, .link_status_changed = pcnet_set_link_status, diff --git a/hw/net/pcnet.h b/hw/net/pcnet.h index dec8de834..40831a784 100644 --- a/hw/net/pcnet.h +++ b/hw/net/pcnet.h @@ -1,5 +1,5 @@ #ifndef HW_PCNET_H -#define HW_PCNET_H 1 +#define HW_PCNET_H #define PCNET_IOPORT_SIZE 0x20 #define PCNET_PNPMMIO_SIZE 0x20 diff --git a/hw/net/rocker/rocker.h b/hw/net/rocker/rocker.h index f9c80f801..7ae0495d9 100644 --- a/hw/net/rocker/rocker.h +++ b/hw/net/rocker/rocker.h @@ -16,8 +16,8 @@ * GNU General Public License for more details. */ -#ifndef _ROCKER_H_ -#define _ROCKER_H_ +#ifndef ROCKER_H +#define ROCKER_H #include "qemu/sockets.h" @@ -81,4 +81,4 @@ int rx_produce(World *world, uint32_t pport, int rocker_port_eg(Rocker *r, uint32_t pport, const struct iovec *iov, int iovcnt); -#endif /* _ROCKER_H_ */ +#endif /* ROCKER_H */ diff --git a/hw/net/rocker/rocker_desc.h b/hw/net/rocker/rocker_desc.h index d4041f5c4..1dec33561 100644 --- a/hw/net/rocker/rocker_desc.h +++ b/hw/net/rocker/rocker_desc.h @@ -14,9 +14,8 @@ * GNU General Public License for more details. */ - -#ifndef _ROCKER_DESC_H_ -#define _ROCKER_DESC_H_ +#ifndef ROCKER_DESC_H +#define ROCKER_DESC_H #include "rocker_hw.h" diff --git a/hw/net/rocker/rocker_fp.c b/hw/net/rocker/rocker_fp.c index 0149899c6..1305ac36c 100644 --- a/hw/net/rocker/rocker_fp.c +++ b/hw/net/rocker/rocker_fp.c @@ -167,7 +167,7 @@ static void fp_port_set_link_status(NetClientState *nc) } static NetClientInfo fp_port_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, + .type = NET_CLIENT_DRIVER_NIC, .size = sizeof(NICState), .receive = fp_port_receive, .receive_iov = fp_port_receive_iov, diff --git a/hw/net/rocker/rocker_fp.h b/hw/net/rocker/rocker_fp.h index 04592bbfd..dbe1dd329 100644 --- a/hw/net/rocker/rocker_fp.h +++ b/hw/net/rocker/rocker_fp.h @@ -14,8 +14,8 @@ * GNU General Public License for more details. */ -#ifndef _ROCKER_FP_H_ -#define _ROCKER_FP_H_ +#ifndef ROCKER_FP_H +#define ROCKER_FP_H #include "net/net.h" #include "qemu/iov.h" @@ -51,4 +51,4 @@ FpPort *fp_port_alloc(Rocker *r, char *sw_name, void fp_port_free(FpPort *port); void fp_port_reset(FpPort *port); -#endif /* _ROCKER_FP_H_ */ +#endif /* ROCKER_FP_H */ diff --git a/hw/net/rocker/rocker_hw.h b/hw/net/rocker/rocker_hw.h index 8c5083032..1786323fa 100644 --- a/hw/net/rocker/rocker_hw.h +++ b/hw/net/rocker/rocker_hw.h @@ -6,8 +6,8 @@ * */ -#ifndef _ROCKER_HW_ -#define _ROCKER_HW_ +#ifndef ROCKER_HW_H +#define ROCKER_HW_H #define __le16 uint16_t #define __le32 uint32_t @@ -490,4 +490,4 @@ enum rocker_of_dpa_overlay_type { */ #define ROCKER_CONTROL_RESET (1 << 0) -#endif /* _ROCKER_HW_ */ +#endif /* ROCKER_HW_H */ diff --git a/hw/net/rocker/rocker_of_dpa.c b/hw/net/rocker/rocker_of_dpa.c index 0a134ebca..9b1e0d244 100644 --- a/hw/net/rocker/rocker_of_dpa.c +++ b/hw/net/rocker/rocker_of_dpa.c @@ -103,9 +103,8 @@ typedef struct of_dpa_flow_key { /* Width of key which includes field 'f' in u64s, rounded up */ #define FLOW_KEY_WIDTH(f) \ - ((offsetof(OfDpaFlowKey, f) + \ - sizeof(((OfDpaFlowKey *)0)->f) + \ - sizeof(uint64_t) - 1) / sizeof(uint64_t)) + DIV_ROUND_UP(offsetof(OfDpaFlowKey, f) + sizeof(((OfDpaFlowKey *)0)->f), \ + sizeof(uint64_t)) typedef struct of_dpa_flow_action { uint32_t goto_tbl; diff --git a/hw/net/rocker/rocker_of_dpa.h b/hw/net/rocker/rocker_of_dpa.h index f3f6d7780..01c7a97d0 100644 --- a/hw/net/rocker/rocker_of_dpa.h +++ b/hw/net/rocker/rocker_of_dpa.h @@ -14,9 +14,9 @@ * GNU General Public License for more details. */ -#ifndef _ROCKER_OF_DPA_H_ -#define _ROCKER_OF_DPA_H_ +#ifndef ROCKER_OF_DPA_H +#define ROCKER_OF_DPA_H World *of_dpa_world_alloc(Rocker *r); -#endif /* _ROCKER_OF_DPA_H_ */ +#endif /* ROCKER_OF_DPA_H */ diff --git a/hw/net/rocker/rocker_tlv.h b/hw/net/rocker/rocker_tlv.h index e3c4ab679..dd28d0844 100644 --- a/hw/net/rocker/rocker_tlv.h +++ b/hw/net/rocker/rocker_tlv.h @@ -14,8 +14,8 @@ * GNU General Public License for more details. */ -#ifndef _ROCKER_TLV_H_ -#define _ROCKER_TLV_H_ +#ifndef ROCKER_TLV_H +#define ROCKER_TLV_H #define ROCKER_TLV_ALIGNTO 8U #define ROCKER_TLV_ALIGN(len) \ @@ -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/rocker/rocker_world.h b/hw/net/rocker/rocker_world.h index 58ade4733..44f1fe3e1 100644 --- a/hw/net/rocker/rocker_world.h +++ b/hw/net/rocker/rocker_world.h @@ -14,8 +14,8 @@ * GNU General Public License for more details. */ -#ifndef _ROCKER_WORLD_H_ -#define _ROCKER_WORLD_H_ +#ifndef ROCKER_WORLD_H +#define ROCKER_WORLD_H #include "rocker_hw.h" @@ -58,4 +58,4 @@ const char *world_name(World *world); World *rocker_get_world(Rocker *r, enum rocker_world_type type); -#endif /* _ROCKER_WORLD_H_ */ +#endif /* ROCKER_WORLD_H */ diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c index 1e5ec149f..3345bc6b5 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); @@ -1867,11 +1843,6 @@ static int rtl8139_transmit_one(RTL8139State *s, int descriptor) return 1; } -/* structures and macros for task offloading */ -#define TCP_HEADER_DATA_OFFSET(tcp) (((be16_to_cpu(tcp->th_offset_flags) >> 12)&0xf) << 2) -#define TCP_FLAGS_ONLY(flags) ((flags)&0x3f) -#define TCP_HEADER_FLAGS(tcp) TCP_FLAGS_ONLY(be16_to_cpu(tcp->th_offset_flags)) - #define TCP_HEADER_CLEAR_FLAGS(tcp, off) ((tcp)->th_offset_flags &= cpu_to_be16(~TCP_FLAGS_ONLY(off))) /* produces ones' complement sum of data */ @@ -3227,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, @@ -3298,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() @@ -3411,7 +3393,7 @@ static void rtl8139_set_link_status(NetClientState *nc) } static NetClientInfo net_rtl8139_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, + .type = NET_CLIENT_DRIVER_NIC, .size = sizeof(NICState), .can_receive = rtl8139_can_receive, .receive = rtl8139_receive, diff --git a/hw/net/smc91c111.c b/hw/net/smc91c111.c index 21c1b8f54..3b16dcf5a 100644 --- a/hw/net/smc91c111.c +++ b/hw/net/smc91c111.c @@ -755,7 +755,7 @@ static const MemoryRegionOps smc91c111_mem_ops = { }; static NetClientInfo net_smc91c111_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, + .type = NET_CLIENT_DRIVER_NIC, .size = sizeof(NICState), .can_receive = smc91c111_can_receive_nc, .receive = smc91c111_receive, diff --git a/hw/net/spapr_llan.c b/hw/net/spapr_llan.c index a647f25d9..b273eda93 100644 --- a/hw/net/spapr_llan.c +++ b/hw/net/spapr_llan.c @@ -28,6 +28,7 @@ #include "qemu-common.h" #include "cpu.h" #include "hw/hw.h" +#include "qemu/log.h" #include "net/net.h" #include "hw/qdev.h" #include "hw/ppc/spapr.h" @@ -106,9 +107,10 @@ typedef struct VIOsPAPRVLANDevice { NICConf nicconf; NICState *nic; bool isopen; - target_ulong buf_list; + hwaddr buf_list; uint32_t add_buf_ptr, use_buf_ptr, rx_bufs; - target_ulong rxq_ptr; + hwaddr rxq_ptr; + QEMUTimer *rxp_timer; uint32_t compat_flags; /* Compatability flags for migration */ RxBufPool *rx_pool[RX_MAX_POOLS]; /* Receive buffer descriptor pools */ } VIOsPAPRVLANDevice; @@ -121,6 +123,21 @@ static int spapr_vlan_can_receive(NetClientState *nc) } /** + * The last 8 bytes of the receive buffer list page (that has been + * supplied by the guest with the H_REGISTER_LOGICAL_LAN call) contain + * a counter for frames that have been dropped because there was no + * suitable receive buffer available. This function is used to increase + * this counter by one. + */ +static void spapr_vlan_record_dropped_rx_frame(VIOsPAPRVLANDevice *dev) +{ + uint64_t cnt; + + cnt = vio_ldq(&dev->sdev, dev->buf_list + 4096 - 8); + vio_stq(&dev->sdev, dev->buf_list + 4096 - 8, cnt + 1); +} + +/** * Get buffer descriptor from one of our receive buffer pools */ static vlan_bd_t spapr_vlan_get_rx_bd_from_pool(VIOsPAPRVLANDevice *dev, @@ -205,7 +222,8 @@ static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf, } if (!dev->rx_bufs) { - return -1; + spapr_vlan_record_dropped_rx_frame(dev); + return 0; } if (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) { @@ -214,7 +232,8 @@ static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf, bd = spapr_vlan_get_rx_bd_from_page(dev, size); } if (!bd) { - return -1; + spapr_vlan_record_dropped_rx_frame(dev); + return 0; } dev->rx_bufs--; @@ -259,12 +278,19 @@ static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf, } static NetClientInfo net_spapr_vlan_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, + .type = NET_CLIENT_DRIVER_NIC, .size = sizeof(NICState), .can_receive = spapr_vlan_can_receive, .receive = spapr_vlan_receive, }; +static void spapr_vlan_flush_rx_queue(void *opaque) +{ + VIOsPAPRVLANDevice *dev = opaque; + + qemu_flush_queued_packets(qemu_get_queue(dev->nic)); +} + static void spapr_vlan_reset_rx_pool(RxBufPool *rxp) { /* @@ -301,6 +327,9 @@ static void spapr_vlan_realize(VIOsPAPRDevice *sdev, Error **errp) dev->nic = qemu_new_nic(&net_spapr_vlan_info, &dev->nicconf, object_get_typename(OBJECT(sdev)), sdev->qdev.id, dev); qemu_format_nic_info_str(qemu_get_queue(dev->nic), dev->nicconf.macaddr.a); + + dev->rxp_timer = timer_new_us(QEMU_CLOCK_VIRTUAL, spapr_vlan_flush_rx_queue, + dev); } static void spapr_vlan_instance_init(Object *obj) @@ -331,6 +360,11 @@ static void spapr_vlan_instance_finalize(Object *obj) dev->rx_pool[i] = NULL; } } + + if (dev->rxp_timer) { + timer_del(dev->rxp_timer); + timer_free(dev->rxp_timer); + } } void spapr_vlan_create(VIOsPAPRBus *bus, NICInfo *nd) @@ -628,7 +662,13 @@ static target_ulong h_add_logical_lan_buffer(PowerPCCPU *cpu, dev->rx_bufs++; - qemu_flush_queued_packets(qemu_get_queue(dev->nic)); + /* + * Give guest some more time to add additional RX buffers before we + * flush the receive queue, so that e.g. fragmented IP packets can + * be passed to the guest in one go later (instead of passing single + * fragments if there is only one receive buffer available). + */ + timer_mod(dev->rxp_timer, qemu_clock_get_us(QEMU_CLOCK_VIRTUAL) + 500); return H_SUCCESS; } @@ -765,11 +805,11 @@ static const VMStateDescription vmstate_spapr_llan = { VMSTATE_SPAPR_VIO(sdev, VIOsPAPRVLANDevice), /* LLAN state */ VMSTATE_BOOL(isopen, VIOsPAPRVLANDevice), - VMSTATE_UINTTL(buf_list, VIOsPAPRVLANDevice), + VMSTATE_UINT64(buf_list, VIOsPAPRVLANDevice), VMSTATE_UINT32(add_buf_ptr, VIOsPAPRVLANDevice), VMSTATE_UINT32(use_buf_ptr, VIOsPAPRVLANDevice), VMSTATE_UINT32(rx_bufs, VIOsPAPRVLANDevice), - VMSTATE_UINTTL(rxq_ptr, VIOsPAPRVLANDevice), + VMSTATE_UINT64(rxq_ptr, VIOsPAPRVLANDevice), VMSTATE_END_OF_LIST() }, diff --git a/hw/net/stellaris_enet.c b/hw/net/stellaris_enet.c index 688089494..957730e02 100644 --- a/hw/net/stellaris_enet.c +++ b/hw/net/stellaris_enet.c @@ -460,7 +460,7 @@ static void stellaris_enet_reset(stellaris_enet_state *s) } static NetClientInfo net_stellaris_enet_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, + .type = NET_CLIENT_DRIVER_NIC, .size = sizeof(NICState), .receive = stellaris_enet_receive, }; diff --git a/hw/net/trace-events b/hw/net/trace-events new file mode 100644 index 000000000..8d38d7724 --- /dev/null +++ b/hw/net/trace-events @@ -0,0 +1,272 @@ +# See docs/tracing.txt for syntax documentation. + +# hw/net/lance.c +lance_mem_readw(uint64_t addr, uint32_t ret) "addr=%"PRIx64"val=0x%04x" +lance_mem_writew(uint64_t addr, uint32_t val) "addr=%"PRIx64"val=0x%04x" + +# hw/net/milkymist-minimac2.c +milkymist_minimac2_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x" +milkymist_minimac2_memory_write(uint32_t addr, uint32_t value) "addr %08x value %08x" +milkymist_minimac2_mdio_write(uint8_t phy_addr, uint8_t addr, uint16_t value) "phy_addr %02x addr %02x value %04x" +milkymist_minimac2_mdio_read(uint8_t phy_addr, uint8_t addr, uint16_t value) "phy_addr %02x addr %02x value %04x" +milkymist_minimac2_tx_frame(uint32_t length) "length %u" +milkymist_minimac2_rx_frame(const void *buf, uint32_t length) "buf %p length %u" +milkymist_minimac2_rx_transfer(const void *buf, uint32_t length) "buf %p length %d" +milkymist_minimac2_raise_irq_rx(void) "Raise IRQ RX" +milkymist_minimac2_lower_irq_rx(void) "Lower IRQ RX" +milkymist_minimac2_pulse_irq_tx(void) "Pulse IRQ TX" + +# hw/net/mipsnet.c +mipsnet_send(uint32_t size) "sending len=%u" +mipsnet_receive(uint32_t size) "receiving len=%u" +mipsnet_read(uint64_t addr, uint32_t val) "read addr=0x%" PRIx64 " val=0x%x" +mipsnet_write(uint64_t addr, uint64_t val) "write addr=0x%" PRIx64 " val=0x%" PRIx64 +mipsnet_irq(uint32_t isr, uint32_t intctl) "set irq to %d (%02x)" + +# hw/net/opencores_eth.c +open_eth_mii_write(unsigned idx, uint16_t v) "MII[%02x] <- %04x" +open_eth_mii_read(unsigned idx, uint16_t v) "MII[%02x] -> %04x" +open_eth_update_irq(uint32_t v) "IRQ <- %x" +open_eth_receive(unsigned len) "RX: len: %u" +open_eth_receive_mcast(unsigned idx, uint32_t h0, uint32_t h1) "MCAST: idx = %u, hash: %08x:%08x" +open_eth_receive_reject(void) "RX: rejected" +open_eth_receive_desc(uint32_t addr, uint32_t len_flags) "RX: %08x, len_flags: %08x" +open_eth_start_xmit(uint32_t addr, unsigned len, unsigned tx_len) "TX: %08x, len: %u, tx_len: %u" +open_eth_reg_read(uint32_t addr, uint32_t v) "MAC[%02x] -> %08x" +open_eth_reg_write(uint32_t addr, uint32_t v) "MAC[%02x] <- %08x" +open_eth_desc_read(uint32_t addr, uint32_t v) "DESC[%04x] -> %08x" +open_eth_desc_write(uint32_t addr, uint32_t v) "DESC[%04x] <- %08x" + +# hw/net/pcnet.c +pcnet_s_reset(void *s) "s=%p" +pcnet_user_int(void *s) "s=%p" +pcnet_isr_change(void *s, uint32_t isr, uint32_t isr_old) "s=%p INTA=%d<=%d" +pcnet_init(void *s, uint64_t init_addr) "s=%p init_addr=%#"PRIx64 +pcnet_rlen_tlen(void *s, uint32_t rlen, uint32_t tlen) "s=%p rlen=%d tlen=%d" +pcnet_ss32_rdra_tdra(void *s, uint32_t ss32, uint32_t rdra, uint32_t rcvrl, uint32_t tdra, uint32_t xmtrl) "s=%p ss32=%d rdra=0x%08x[%d] tdra=0x%08x[%d]" + +# hw/net/pcnet-pci.c +pcnet_aprom_writeb(void *opaque, uint32_t addr, uint32_t val) "opaque=%p addr=0x%08x val=0x%02x" +pcnet_aprom_readb(void *opaque, uint32_t addr, uint32_t val) "opaque=%p addr=0x%08x val=0x%02x" +pcnet_ioport_read(void *opaque, uint64_t addr, unsigned size) "opaque=%p addr=%#"PRIx64" size=%d" +pcnet_ioport_write(void *opaque, uint64_t addr, uint64_t data, unsigned size) "opaque=%p addr=%#"PRIx64" data=%#"PRIx64" size=%d" +pcnet_mmio_writeb(void *opaque, uint64_t addr, uint32_t val) "opaque=%p addr=%#"PRIx64" val=0x%x" +pcnet_mmio_writew(void *opaque, uint64_t addr, uint32_t val) "opaque=%p addr=%#"PRIx64" val=0x%x" +pcnet_mmio_writel(void *opaque, uint64_t addr, uint32_t val) "opaque=%p addr=%#"PRIx64" val=0x%x" +pcnet_mmio_readb(void *opaque, uint64_t addr, uint32_t val) "opaque=%p addr=%#"PRIx64" val=0x%x" +pcnet_mmio_readw(void *opaque, uint64_t addr, uint32_t val) "opaque=%p addr=%#"PRIx64" val=0x%x" +pcnet_mmio_readl(void *opaque, uint64_t addr, uint32_t val) "opaque=%p addr=%#"PRIx64" val=0x%x" + +# hw/net/net_rx_pkt.c +net_rx_pkt_parsed(bool ip4, bool ip6, bool udp, bool tcp, size_t l3o, size_t l4o, size_t l5o) "RX packet parsed: ip4: %d, ip6: %d, udp: %d, tcp: %d, l3 offset: %zu, l4 offset: %zu, l5 offset: %zu" +net_rx_pkt_l4_csum_validate_entry(void) "Starting L4 checksum validation" +net_rx_pkt_l4_csum_validate_not_xxp(void) "Not a TCP/UDP packet" +net_rx_pkt_l4_csum_validate_udp_with_no_checksum(void) "UDP packet without checksum" +net_rx_pkt_l4_csum_validate_ip4_fragment(void) "IP4 fragment" +net_rx_pkt_l4_csum_validate_ip4_udp(void) "IP4/UDP packet" +net_rx_pkt_l4_csum_validate_ip4_tcp(void) "IP4/TCP packet" +net_rx_pkt_l4_csum_validate_ip6_udp(void) "IP6/UDP packet" +net_rx_pkt_l4_csum_validate_ip6_tcp(void) "IP6/TCP packet" +net_rx_pkt_l4_csum_validate_csum(bool csum_valid) "Checksum valid: %d" + +net_rx_pkt_l4_csum_calc_entry(void) "Starting L4 checksum calculation" +net_rx_pkt_l4_csum_calc_ip4_udp(void) "IP4/UDP packet" +net_rx_pkt_l4_csum_calc_ip4_tcp(void) "IP4/TCP packet" +net_rx_pkt_l4_csum_calc_ip6_udp(void) "IP6/UDP packet" +net_rx_pkt_l4_csum_calc_ip6_tcp(void) "IP6/TCP packet" +net_rx_pkt_l4_csum_calc_ph_csum(uint32_t cntr, uint16_t csl) "Pseudo-header: checksum counter %u, length %u" +net_rx_pkt_l4_csum_calc_csum(size_t l4hdr_off, uint16_t csl, uint32_t cntr, uint16_t csum) "L4 Checksum: L4 header offset: %zu, length: %u, counter: 0x%X, final checksum: 0x%X" + +net_rx_pkt_l4_csum_fix_entry(void) "Starting L4 checksum correction" +net_rx_pkt_l4_csum_fix_tcp(uint32_t l4_cso) "TCP packet, L4 cso: %u" +net_rx_pkt_l4_csum_fix_udp(uint32_t l4_cso) "UDP packet, L4 cso: %u" +net_rx_pkt_l4_csum_fix_not_xxp(void) "Not an IP4 packet" +net_rx_pkt_l4_csum_fix_ip4_fragment(void) "IP4 fragment" +net_rx_pkt_l4_csum_fix_udp_with_no_checksum(void) "UDP packet without checksum" +net_rx_pkt_l4_csum_fix_csum(uint32_t cso, uint16_t csum) "L4 Checksum: Offset: %u, value 0x%X" + +net_rx_pkt_l3_csum_validate_entry(void) "Starting L3 checksum validation" +net_rx_pkt_l3_csum_validate_not_ip4(void) "Not an IP4 packet" +net_rx_pkt_l3_csum_validate_csum(size_t l3hdr_off, uint32_t csl, uint32_t cntr, uint16_t csum, bool csum_valid) "L3 Checksum: L3 header offset: %zu, length: %u, counter: 0x%X, final checksum: 0x%X, valid: %d" + +net_rx_pkt_rss_ip4(void) "Calculating IPv4 RSS hash" +net_rx_pkt_rss_ip4_tcp(void) "Calculating IPv4/TCP RSS hash" +net_rx_pkt_rss_ip6_tcp(void) "Calculating IPv6/TCP RSS hash" +net_rx_pkt_rss_ip6(void) "Calculating IPv6 RSS hash" +net_rx_pkt_rss_ip6_ex(void) "Calculating IPv6/EX RSS hash" +net_rx_pkt_rss_hash(size_t rss_length, uint32_t rss_hash) "RSS hash for %zu bytes: 0x%X" +net_rx_pkt_rss_add_chunk(void* ptr, size_t size, size_t input_offset) "Add RSS chunk %p, %zu bytes, RSS input offset %zu bytes" + +# hw/net/e1000x_common.c +e1000x_rx_can_recv_disabled(bool link_up, bool rx_enabled, bool pci_master) "link_up: %d, rx_enabled %d, pci_master %d" +e1000x_vlan_is_vlan_pkt(bool is_vlan_pkt, uint16_t eth_proto, uint16_t vet) "Is VLAN packet: %d, ETH proto: 0x%X, VET: 0x%X" +e1000x_rx_flt_ucast_match(uint32_t idx, uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5) "unicast match[%d]: %02x:%02x:%02x:%02x:%02x:%02x" +e1000x_rx_flt_ucast_mismatch(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5) "unicast mismatch: %02x:%02x:%02x:%02x:%02x:%02x" +e1000x_rx_flt_inexact_mismatch(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5, uint32_t mo, uint32_t mta, uint32_t mta_val) "inexact mismatch: %02x:%02x:%02x:%02x:%02x:%02x MO %d MTA[%d] %x" +e1000x_rx_link_down(uint32_t status_reg) "Received packet dropped because the link is down STATUS = %u" +e1000x_rx_disabled(uint32_t rctl_reg) "Received packet dropped because receive is disabled RCTL = %u" +e1000x_rx_oversized(size_t size) "Received packet dropped because it was oversized (%zu bytes)" +e1000x_mac_indicate(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5) "Indicating MAC to guest: %02x:%02x:%02x:%02x:%02x:%02x" +e1000x_link_negotiation_start(void) "Start link auto negotiation" +e1000x_link_negotiation_done(void) "Auto negotiation is completed" + +# hw/net/e1000e_core.c +e1000e_core_write(uint64_t index, uint32_t size, uint64_t val) "Write to register 0x%"PRIx64", %d byte(s), value: 0x%"PRIx64 +e1000e_core_read(uint64_t index, uint32_t size, uint64_t val) "Read from register 0x%"PRIx64", %d byte(s), value: 0x%"PRIx64 +e1000e_core_mdic_read(uint8_t page, uint32_t addr, uint32_t data) "MDIC READ: PHY[%u][%u] = 0x%x" +e1000e_core_mdic_read_unhandled(uint8_t page, uint32_t addr) "MDIC READ: PHY[%u][%u] UNHANDLED" +e1000e_core_mdic_write(uint8_t page, uint32_t addr, uint32_t data) "MDIC WRITE: PHY[%u][%u] = 0x%x" +e1000e_core_mdic_write_unhandled(uint8_t page, uint32_t addr) "MDIC WRITE: PHY[%u][%u] UNHANDLED" +e1000e_core_eeeprom_write(uint16_t bit_in, uint16_t bit_out, uint16_t reading) "eeprom bitnum in %d out %d, reading %d" +e1000e_core_ctrl_write(uint64_t index, uint32_t val) "Write CTRL register 0x%"PRIx64", value: 0x%X" +e1000e_core_ctrl_sw_reset(void) "Doing SW reset" +e1000e_core_ctrl_phy_reset(void) "Doing PHY reset" + +e1000e_link_autoneg_flowctl(bool enabled) "Auto-negotiated flow control state is %d" +e1000e_link_set_params(bool autodetect, uint32_t speed, bool force_spd, bool force_dplx, bool rx_fctl, bool tx_fctl) "Set link params: Autodetect: %d, Speed: %d, Force speed: %d, Force duplex: %d, RX flow control %d, TX flow control %d" +e1000e_link_read_params(bool autodetect, uint32_t speed, bool force_spd, bool force_dplx, bool rx_fctl, bool tx_fctl) "Get link params: Autodetect: %d, Speed: %d, Force speed: %d, Force duplex: %d, RX flow control %d, TX flow control %d" +e1000e_link_set_ext_params(bool asd_check, bool speed_select_bypass) "Set extended link params: ASD check: %d, Speed select bypass: %d" +e1000e_link_status(bool link_up, bool full_dplx, uint32_t speed, uint32_t asdv) "Link up: %d, Duplex: %d, Speed: %d, ASDV: %d" +e1000e_link_status_changed(bool status) "New link status: %d" + +e1000e_wrn_regs_write_ro(uint64_t index, uint32_t size, uint64_t val) "WARNING: Write to RO register 0x%"PRIx64", %d byte(s), value: 0x%"PRIx64 +e1000e_wrn_regs_write_unknown(uint64_t index, uint32_t size, uint64_t val) "WARNING: Write to unknown register 0x%"PRIx64", %d byte(s), value: 0x%"PRIx64 +e1000e_wrn_regs_read_unknown(uint64_t index, uint32_t size) "WARNING: Read from unknown register 0x%"PRIx64", %d byte(s)" +e1000e_wrn_regs_read_trivial(uint32_t index) "WARNING: Reading register at offset: 0x%05x. It is not fully implemented." +e1000e_wrn_regs_write_trivial(uint32_t index) "WARNING: Writing to register at offset: 0x%05x. It is not fully implemented." +e1000e_wrn_no_ts_support(void) "WARNING: Guest requested TX timestamping which is not supported" +e1000e_wrn_no_snap_support(void) "WARNING: Guest requested TX SNAP header update which is not supported" +e1000e_wrn_iscsi_filtering_not_supported(void) "WARNING: Guest requested iSCSI filtering which is not supported" +e1000e_wrn_nfsw_filtering_not_supported(void) "WARNING: Guest requested NFS write filtering which is not supported" +e1000e_wrn_nfsr_filtering_not_supported(void) "WARNING: Guest requested NFS read filtering which is not supported" + +e1000e_tx_disabled(void) "TX Disabled" +e1000e_tx_descr(void *addr, uint32_t lower, uint32_t upper) "%p : %x %x" + +e1000e_ring_free_space(int ridx, uint32_t rdlen, uint32_t rdh, uint32_t rdt) "ring #%d: LEN: %u, DH: %u, DT: %u" + +e1000e_rx_can_recv_rings_full(void) "Cannot receive: all rings are full" +e1000e_rx_can_recv(void) "Can receive" +e1000e_rx_has_buffers(int ridx, uint32_t free_desc, size_t total_size, uint32_t desc_buf_size) "ring #%d: free descr: %u, packet size %zu, descr buffer size %u" +e1000e_rx_null_descriptor(void) "Null RX descriptor!!" +e1000e_rx_flt_vlan_mismatch(uint16_t vid) "VID mismatch: 0x%X" +e1000e_rx_flt_vlan_match(uint16_t vid) "VID match: 0x%X" +e1000e_rx_desc_ps_read(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3) "buffers: [0x%"PRIx64", 0x%"PRIx64", 0x%"PRIx64", 0x%"PRIx64"]" +e1000e_rx_desc_ps_write(uint16_t a0, uint16_t a1, uint16_t a2, uint16_t a3) "bytes written: [%u, %u, %u, %u]" +e1000e_rx_desc_buff_sizes(uint32_t b0, uint32_t b1, uint32_t b2, uint32_t b3) "buffer sizes: [%u, %u, %u, %u]" +e1000e_rx_desc_len(uint8_t rx_desc_len) "RX descriptor length: %u" +e1000e_rx_desc_buff_write(uint8_t idx, uint64_t addr, uint16_t offset, const void* source, uint32_t len) "buffer #%u, addr: 0x%"PRIx64", offset: %u, from: %p, length: %u" +e1000e_rx_descr(int ridx, uint64_t base, uint8_t len) "Next RX descriptor: ring #%d, PA: 0x%"PRIx64", length: %u" +e1000e_rx_set_rctl(uint32_t rctl) "RCTL = 0x%x" +e1000e_rx_receive_iov(int iovcnt) "Received vector of %d fragments" +e1000e_rx_packet_size(size_t full, size_t vhdr, size_t data) "Received packet of %zu bytes total, %zu virt header, %zu data" +e1000e_rx_flt_dropped(void) "Received packet dropped by RX filter" +e1000e_rx_written_to_guest(uint32_t causes) "Received packet written to guest (ICR causes %u)" +e1000e_rx_not_written_to_guest(uint32_t causes) "Received packet NOT written to guest (ICR causes %u)" +e1000e_rx_interrupt_set(uint32_t causes) "Receive interrupt set (ICR causes %u)" +e1000e_rx_interrupt_delayed(uint32_t causes) "Receive interrupt delayed (ICR causes %u)" +e1000e_rx_set_cso(int cso_state) "RX CSO state set to %d" +e1000e_rx_set_rdt(int queue_idx, uint32_t val) "Setting RDT[%d] = %u" +e1000e_rx_set_rfctl(uint32_t val) "Setting RFCTL = 0x%X" +e1000e_rx_start_recv(void) + +e1000e_rx_rss_started(void) "Starting RSS processing" +e1000e_rx_rss_disabled(void) "RSS is disabled" +e1000e_rx_rss_type(uint32_t type) "RSS type is %u" +e1000e_rx_rss_ip4(bool isfragment, bool istcp, uint32_t mrqc, bool tcpipv4_enabled, bool ipv4_enabled) "RSS IPv4: fragment %d, tcp %d, mrqc 0x%X, tcpipv4 enabled %d, ipv4 enabled %d" +e1000e_rx_rss_ip6_rfctl(uint32_t rfctl) "RSS IPv6: rfctl 0x%X" +e1000e_rx_rss_ip6(bool ex_dis, bool new_ex_dis, bool istcp, bool has_ext_headers, bool ex_dst_valid, bool ex_src_valid, uint32_t mrqc, bool tcpipv6_enabled, bool ipv6ex_enabled, bool ipv6_enabled) "RSS IPv6: ex_dis: %d, new_ex_dis: %d, tcp %d, has_ext_headers %d, ex_dst_valid %d, ex_src_valid %d, mrqc 0x%X, tcpipv6 enabled %d, ipv6ex enabled %d, ipv6 enabled %d" +e1000e_rx_rss_dispatched_to_queue(int queue_idx) "Packet being dispatched to queue %d" + +e1000e_rx_metadata_protocols(bool isip4, bool isip6, bool isudp, bool istcp) "protocols: ip4: %d, ip6: %d, udp: %d, tcp: %d" +e1000e_rx_metadata_vlan(uint16_t vlan_tag) "VLAN tag is 0x%X" +e1000e_rx_metadata_rss(uint32_t rss, uint32_t mrq) "RSS data: rss: 0x%X, mrq: 0x%X" +e1000e_rx_metadata_ip_id(uint16_t ip_id) "the IPv4 ID is 0x%X" +e1000e_rx_metadata_ack(void) "the packet is TCP ACK" +e1000e_rx_metadata_pkt_type(uint32_t pkt_type) "the packet type is %u" +e1000e_rx_metadata_no_virthdr(void) "the packet has no virt-header" +e1000e_rx_metadata_virthdr_no_csum_info(void) "virt-header does not contain checksum info" +e1000e_rx_metadata_l3_cso_disabled(void) "IP4 CSO is disabled" +e1000e_rx_metadata_l4_cso_disabled(void) "TCP/UDP CSO is disabled" +e1000e_rx_metadata_l3_csum_validation_failed(void) "Cannot validate L3 checksum" +e1000e_rx_metadata_l4_csum_validation_failed(void) "Cannot validate L4 checksum" +e1000e_rx_metadata_status_flags(uint32_t status_flags) "status_flags is 0x%X" +e1000e_rx_metadata_ipv6_sum_disabled(void) "IPv6 RX checksummimg disabled by RFCTL" +e1000e_rx_metadata_ipv6_filtering_disabled(void) "IPv6 RX filtering disabled by RFCTL" + +e1000e_vlan_vet(uint16_t vet) "Setting VLAN ethernet type 0x%X" + +e1000e_irq_set_cause(uint32_t cause) "IRQ cause set 0x%x" +e1000e_irq_msi_notify(uint32_t cause) "MSI notify 0x%x" +e1000e_irq_throttling_no_pending_interrupts(void) "No pending interrupts to notify" +e1000e_irq_msi_notify_postponed(void) "Sending MSI postponed by ITR" +e1000e_irq_legacy_notify_postponed(void) "Raising legacy IRQ postponed by ITR" +e1000e_irq_throttling_no_pending_vec(int idx) "No pending interrupts for vector %d" +e1000e_irq_msix_notify_postponed_vec(int idx) "Sending MSI-X postponed by EITR[%d]" +e1000e_irq_msix_notify(uint32_t cause) "MSI-X notify 0x%x" +e1000e_irq_legacy_notify(bool level) "IRQ line state: %d" +e1000e_irq_msix_notify_vec(uint32_t vector) "MSI-X notify vector 0x%x" +e1000e_irq_postponed_by_xitr(uint32_t reg) "Interrupt postponed by [E]ITR register 0x%x" +e1000e_irq_clear_ims(uint32_t bits, uint32_t old_ims, uint32_t new_ims) "Clearing IMS bits 0x%x: 0x%x --> 0x%x" +e1000e_irq_set_ims(uint32_t bits, uint32_t old_ims, uint32_t new_ims) "Setting IMS bits 0x%x: 0x%x --> 0x%x" +e1000e_irq_fix_icr_asserted(uint32_t new_val) "ICR_ASSERTED bit fixed: 0x%x" +e1000e_irq_add_msi_other(uint32_t new_val) "ICR_OTHER bit added: 0x%x" +e1000e_irq_pending_interrupts(uint32_t pending, uint32_t icr, uint32_t ims) "ICR PENDING: 0x%x (ICR: 0x%x, IMS: 0x%x)" +e1000e_irq_set_cause_entry(uint32_t val, uint32_t icr) "Going to set IRQ cause 0x%x, ICR: 0x%x" +e1000e_irq_set_cause_exit(uint32_t val, uint32_t icr) "Set IRQ cause 0x%x, ICR: 0x%x" +e1000e_irq_icr_write(uint32_t bits, uint32_t old_icr, uint32_t new_icr) "Clearing ICR bits 0x%x: 0x%x --> 0x%x" +e1000e_irq_write_ics(uint32_t val) "Adding ICR bits 0x%x" +e1000e_irq_icr_process_iame(void) "Clearing IMS bits due to IAME" +e1000e_irq_read_ics(uint32_t ics) "Current ICS: 0x%x" +e1000e_irq_read_ims(uint32_t ims) "Current IMS: 0x%x" +e1000e_irq_icr_read_entry(uint32_t icr) "Starting ICR read. Current ICR: 0x%x" +e1000e_irq_icr_read_exit(uint32_t icr) "Ending ICR read. Current ICR: 0x%x" +e1000e_irq_icr_clear_zero_ims(void) "Clearing ICR on read due to zero IMS" +e1000e_irq_icr_clear_iame(void) "Clearing ICR on read due to IAME" +e1000e_irq_ims_clear_eiame(uint32_t iam, uint32_t cause) "Clearing IMS due to EIAME, IAM: 0x%X, cause: 0x%X" +e1000e_irq_icr_clear_eiac(uint32_t icr, uint32_t eiac) "Clearing ICR bits due to EIAC, ICR: 0x%X, EIAC: 0x%X" +e1000e_irq_ims_clear_set_imc(uint32_t val) "Clearing IMS bits due to IMC write 0x%x" +e1000e_irq_fire_delayed_interrupts(void) "Firing delayed interrupts" +e1000e_irq_rearm_timer(uint32_t reg, int64_t delay_ns) "Mitigation timer armed for register 0x%X, delay %"PRId64" ns" +e1000e_irq_throttling_timer(uint32_t reg) "Mitigation timer shot for register 0x%X" +e1000e_irq_rdtr_fpd_running(void) "FPD written while RDTR was running" +e1000e_irq_rdtr_fpd_not_running(void) "FPD written while RDTR was not running" +e1000e_irq_tidv_fpd_running(void) "FPD written while TIDV was running" +e1000e_irq_tidv_fpd_not_running(void) "FPD written while TIDV was not running" +e1000e_irq_eitr_set(uint32_t eitr_num, uint32_t val) "EITR[%u] = %u" +e1000e_irq_itr_set(uint32_t val) "ITR = %u" +e1000e_irq_fire_all_timers(uint32_t val) "Firing all delay/throttling timers on all interrupts enable (0x%X written to IMS)" +e1000e_irq_adding_delayed_causes(uint32_t val, uint32_t icr) "Merging delayed causes 0x%X to ICR 0x%X" +e1000e_irq_msix_pending_clearing(uint32_t cause, uint32_t int_cfg, uint32_t vec) "Clearing MSI-X pending bit for cause 0x%x, IVAR config 0x%x, vector %u" + +e1000e_wrn_msix_vec_wrong(uint32_t cause, uint32_t cfg) "Invalid configuration for cause 0x%x: 0x%x" +e1000e_wrn_msix_invalid(uint32_t cause, uint32_t cfg) "Invalid entry for cause 0x%x: 0x%x" + +e1000e_mac_set_permanent(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5) "Set permanent MAC: %02x:%02x:%02x:%02x:%02x:%02x" +e1000e_mac_set_sw(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5) "Set SW MAC: %02x:%02x:%02x:%02x:%02x:%02x" + +# hw/net/e1000e.c +e1000e_cb_pci_realize(void) "E1000E PCI realize entry" +e1000e_cb_pci_uninit(void) "E1000E PCI unit entry" +e1000e_cb_qdev_reset(void) "E1000E qdev reset entry" +e1000e_cb_pre_save(void) "E1000E pre save entry" +e1000e_cb_post_load(void) "E1000E post load entry" + +e1000e_io_write_addr(uint64_t addr) "IOADDR write 0x%"PRIx64 +e1000e_io_write_data(uint64_t addr, uint64_t val) "IODATA write 0x%"PRIx64", value: 0x%"PRIx64 +e1000e_io_read_addr(uint64_t addr) "IOADDR read 0x%"PRIx64 +e1000e_io_read_data(uint64_t addr, uint64_t val) "IODATA read 0x%"PRIx64", value: 0x%"PRIx64 +e1000e_wrn_io_write_unknown(uint64_t addr) "IO write unknown address 0x%"PRIx64 +e1000e_wrn_io_read_unknown(uint64_t addr) "IO read unknown address 0x%"PRIx64 +e1000e_wrn_io_addr_undefined(uint64_t addr) "IO undefined register 0x%"PRIx64 +e1000e_wrn_io_addr_flash(uint64_t addr) "IO flash access (0x%"PRIx64") not implemented" +e1000e_wrn_io_addr_unknown(uint64_t addr) "IO unknown register 0x%"PRIx64 + +e1000e_msi_init_fail(int32_t res) "Failed to initialize MSI, error %d" +e1000e_msix_init_fail(int32_t res) "Failed to initialize MSI-X, error %d" +e1000e_msix_use_vector_fail(uint32_t vec, int32_t res) "Failed to use MSI-X vector %d, error %d" + +e1000e_cfg_support_virtio(bool support) "Virtio header supported: %d" + +e1000e_vm_state_running(void) "VM state is running" +e1000e_vm_state_stopped(void) "VM state is stopped" diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 6e1032fc1..f2d49ad7e 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -88,10 +88,10 @@ static const int *vhost_net_get_feature_bits(struct vhost_net *net) const int *feature_bits = 0; switch (net->nc->info->type) { - case NET_CLIENT_OPTIONS_KIND_TAP: + case NET_CLIENT_DRIVER_TAP: feature_bits = kernel_feature_bits; break; - case NET_CLIENT_OPTIONS_KIND_VHOST_USER: + case NET_CLIENT_DRIVER_VHOST_USER: feature_bits = user_feature_bits; break; default: @@ -120,10 +120,15 @@ uint64_t vhost_net_get_max_queues(VHostNetState *net) return net->dev.max_queues; } +uint64_t vhost_net_get_acked_features(VHostNetState *net) +{ + return net->dev.acked_features; +} + static int vhost_net_get_fd(NetClientState *backend) { switch (backend->info->type) { - case NET_CLIENT_OPTIONS_KIND_TAP: + case NET_CLIENT_DRIVER_TAP: return tap_get_fd(backend); default: fprintf(stderr, "vhost-net requires tap backend\n"); @@ -135,7 +140,8 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) { int r; bool backend_kernel = options->backend_type == VHOST_BACKEND_TYPE_KERNEL; - struct vhost_net *net = g_malloc(sizeof *net); + struct vhost_net *net = g_new0(struct vhost_net, 1); + uint64_t features = 0; if (!options->net_backend) { fprintf(stderr, "vhost-net requires net backend to be setup\n"); @@ -166,7 +172,7 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) } r = vhost_dev_init(&net->dev, options->opaque, - options->backend_type); + options->backend_type, options->busyloop_timeout); if (r < 0) { goto fail; } @@ -179,14 +185,27 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) fprintf(stderr, "vhost lacks feature mask %" PRIu64 " for backend\n", (uint64_t)(~net->dev.features & net->dev.backend_features)); - vhost_dev_cleanup(&net->dev); goto fail; } } + /* Set sane init value. Override when guest acks. */ - vhost_net_ack_features(net, 0); + if (net->nc->info->type == NET_CLIENT_DRIVER_VHOST_USER) { + features = vhost_user_get_acked_features(net->nc); + if (~net->dev.features & features) { + fprintf(stderr, "vhost lacks feature mask %" PRIu64 + " for backend\n", + (uint64_t)(~net->dev.features & features)); + goto fail; + } + } + + vhost_net_ack_features(net, features); + return net; + fail: + vhost_dev_cleanup(&net->dev); g_free(net); return NULL; } @@ -219,12 +238,11 @@ static int vhost_net_start_one(struct vhost_net *net, net->nc->info->poll(net->nc, false); } - if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP) { + if (net->nc->info->type == NET_CLIENT_DRIVER_TAP) { qemu_set_fd_handler(net->backend, NULL, NULL, NULL); file.fd = net->backend; for (file.index = 0; file.index < net->dev.nvqs; ++file.index) { - const VhostOps *vhost_ops = net->dev.vhost_ops; - r = vhost_ops->vhost_net_set_backend(&net->dev, &file); + r = vhost_net_set_backend(&net->dev, &file); if (r < 0) { r = -errno; goto fail; @@ -234,10 +252,9 @@ static int vhost_net_start_one(struct vhost_net *net, return 0; fail: file.fd = -1; - if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP) { + if (net->nc->info->type == NET_CLIENT_DRIVER_TAP) { while (file.index-- > 0) { - const VhostOps *vhost_ops = net->dev.vhost_ops; - int r = vhost_ops->vhost_net_set_backend(&net->dev, &file); + int r = vhost_net_set_backend(&net->dev, &file); assert(r >= 0); } } @@ -256,10 +273,9 @@ static void vhost_net_stop_one(struct vhost_net *net, { struct vhost_vring_file file = { .fd = -1 }; - if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP) { + if (net->nc->info->type == NET_CLIENT_DRIVER_TAP) { for (file.index = 0; file.index < net->dev.nvqs; ++file.index) { - const VhostOps *vhost_ops = net->dev.vhost_ops; - int r = vhost_ops->vhost_net_set_backend(&net->dev, &file); + int r = vhost_net_set_backend(&net->dev, &file); assert(r >= 0); } } @@ -293,8 +309,8 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, * because vhost user doesn't interrupt masking/unmasking * properly. */ - if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER) { - dev->use_guest_notifier_mask = false; + if (net->nc->info->type == NET_CLIENT_DRIVER_VHOST_USER) { + dev->use_guest_notifier_mask = false; } } @@ -310,6 +326,15 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, if (r < 0) { goto err_start; } + + if (ncs[i].peer->vring_enable) { + /* restore vring enable state */ + r = vhost_set_vring_enable(ncs[i].peer, ncs[i].peer->vring_enable); + + if (r < 0) { + goto err_start; + } + } } return 0; @@ -350,19 +375,16 @@ void vhost_net_stop(VirtIODevice *dev, NetClientState *ncs, void vhost_net_cleanup(struct vhost_net *net) { vhost_dev_cleanup(&net->dev); - g_free(net); } int vhost_net_notify_migration_done(struct vhost_net *net, char* mac_addr) { const VhostOps *vhost_ops = net->dev.vhost_ops; - int r = -1; - if (vhost_ops->vhost_migration_done) { - r = vhost_ops->vhost_migration_done(&net->dev, mac_addr); - } + assert(vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); + assert(vhost_ops->vhost_migration_done); - return r; + return vhost_ops->vhost_migration_done(&net->dev, mac_addr); } bool vhost_net_virtqueue_pending(VHostNetState *net, int idx) @@ -385,11 +407,12 @@ VHostNetState *get_vhost_net(NetClientState *nc) } switch (nc->info->type) { - case NET_CLIENT_OPTIONS_KIND_TAP: + case NET_CLIENT_DRIVER_TAP: vhost_net = tap_get_vhost_net(nc); break; - case NET_CLIENT_OPTIONS_KIND_VHOST_USER: + case NET_CLIENT_DRIVER_VHOST_USER: vhost_net = vhost_user_get_vhost_net(nc); + assert(vhost_net); break; default: break; @@ -403,7 +426,9 @@ int vhost_set_vring_enable(NetClientState *nc, int enable) VHostNetState *net = get_vhost_net(nc); const VhostOps *vhost_ops = net->dev.vhost_ops; - if (vhost_ops->vhost_set_vring_enable) { + nc->vring_enable = enable; + + if (vhost_ops && vhost_ops->vhost_set_vring_enable) { return vhost_ops->vhost_set_vring_enable(&net->dev, enable); } @@ -442,10 +467,16 @@ uint64_t vhost_net_get_features(struct vhost_net *net, uint64_t features) { return features; } + void vhost_net_ack_features(struct vhost_net *net, uint64_t features) { } +uint64_t vhost_net_get_acked_features(VHostNetState *net) +{ + return 0; +} + bool vhost_net_virtqueue_pending(VHostNetState *net, int idx) { return false; diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 5798f87d8..01f135155 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -468,11 +468,11 @@ static int peer_attach(VirtIONet *n, int index) return 0; } - if (nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER) { + if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) { vhost_set_vring_enable(nc->peer, 1); } - if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { + if (nc->peer->info->type != NET_CLIENT_DRIVER_TAP) { return 0; } @@ -487,11 +487,11 @@ static int peer_detach(VirtIONet *n, int index) return 0; } - if (nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER) { + if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) { vhost_set_vring_enable(nc->peer, 0); } - if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { + if (nc->peer->info->type != NET_CLIENT_DRIVER_TAP) { return 0; } @@ -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; } @@ -1492,7 +1492,7 @@ static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue) virtio_net_set_queues(n); } -static void virtio_net_save(QEMUFile *f, void *opaque) +static void virtio_net_save(QEMUFile *f, void *opaque, size_t size) { VirtIONet *n = opaque; VirtIODevice *vdev = VIRTIO_DEVICE(n); @@ -1538,37 +1538,12 @@ static void virtio_net_save_device(VirtIODevice *vdev, QEMUFile *f) } } -static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) +static int virtio_net_load(QEMUFile *f, void *opaque, size_t size) { VirtIONet *n = opaque; VirtIODevice *vdev = VIRTIO_DEVICE(n); - int ret; - if (version_id < 2 || version_id > VIRTIO_NET_VM_VERSION) - return -EINVAL; - - ret = virtio_load(vdev, f, version_id); - if (ret) { - return ret; - } - - if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { - n->curr_guest_offloads = qemu_get_be64(f); - } else { - n->curr_guest_offloads = virtio_net_supported_guest_offloads(n); - } - - if (peer_has_vnet_hdr(n)) { - virtio_net_apply_guest_offloads(n); - } - - if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE) && - virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) { - n->announce_counter = SELF_ANNOUNCE_ROUNDS; - timer_mod(n->announce_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL)); - } - - return 0; + return virtio_load(vdev, f, VIRTIO_NET_VM_VERSION); } static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f, @@ -1584,68 +1559,49 @@ static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f, virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)); - if (version_id >= 3) - n->status = qemu_get_be16(f); + n->status = qemu_get_be16(f); - if (version_id >= 4) { - if (version_id < 8) { - n->promisc = qemu_get_be32(f); - n->allmulti = qemu_get_be32(f); - } else { - n->promisc = qemu_get_byte(f); - n->allmulti = qemu_get_byte(f); - } - } + n->promisc = qemu_get_byte(f); + n->allmulti = qemu_get_byte(f); - if (version_id >= 5) { - n->mac_table.in_use = qemu_get_be32(f); - /* MAC_TABLE_ENTRIES may be different from the saved image */ - if (n->mac_table.in_use <= MAC_TABLE_ENTRIES) { - qemu_get_buffer(f, n->mac_table.macs, - n->mac_table.in_use * ETH_ALEN); - } else { - int64_t i; - - /* Overflow detected - can happen if source has a larger MAC table. - * We simply set overflow flag so there's no need to maintain the - * table of addresses, discard them all. - * Note: 64 bit math to avoid integer overflow. - */ - for (i = 0; i < (int64_t)n->mac_table.in_use * ETH_ALEN; ++i) { - qemu_get_byte(f); - } - n->mac_table.multi_overflow = n->mac_table.uni_overflow = 1; - n->mac_table.in_use = 0; + n->mac_table.in_use = qemu_get_be32(f); + /* MAC_TABLE_ENTRIES may be different from the saved image */ + if (n->mac_table.in_use <= MAC_TABLE_ENTRIES) { + qemu_get_buffer(f, n->mac_table.macs, + n->mac_table.in_use * ETH_ALEN); + } else { + int64_t i; + + /* Overflow detected - can happen if source has a larger MAC table. + * We simply set overflow flag so there's no need to maintain the + * table of addresses, discard them all. + * Note: 64 bit math to avoid integer overflow. + */ + for (i = 0; i < (int64_t)n->mac_table.in_use * ETH_ALEN; ++i) { + qemu_get_byte(f); } + n->mac_table.multi_overflow = n->mac_table.uni_overflow = 1; + n->mac_table.in_use = 0; } - if (version_id >= 6) - qemu_get_buffer(f, (uint8_t *)n->vlans, MAX_VLAN >> 3); + qemu_get_buffer(f, (uint8_t *)n->vlans, MAX_VLAN >> 3); - if (version_id >= 7) { - if (qemu_get_be32(f) && !peer_has_vnet_hdr(n)) { - error_report("virtio-net: saved image requires vnet_hdr=on"); - return -1; - } + if (qemu_get_be32(f) && !peer_has_vnet_hdr(n)) { + error_report("virtio-net: saved image requires vnet_hdr=on"); + return -1; } - if (version_id >= 9) { - n->mac_table.multi_overflow = qemu_get_byte(f); - n->mac_table.uni_overflow = qemu_get_byte(f); - } + n->mac_table.multi_overflow = qemu_get_byte(f); + n->mac_table.uni_overflow = qemu_get_byte(f); - if (version_id >= 10) { - n->alluni = qemu_get_byte(f); - n->nomulti = qemu_get_byte(f); - n->nouni = qemu_get_byte(f); - n->nobcast = qemu_get_byte(f); - } + n->alluni = qemu_get_byte(f); + n->nomulti = qemu_get_byte(f); + n->nouni = qemu_get_byte(f); + n->nobcast = qemu_get_byte(f); - if (version_id >= 11) { - if (qemu_get_byte(f) && !peer_has_ufo(n)) { - error_report("virtio-net: saved image requires TUN_F_UFO support"); - return -1; - } + if (qemu_get_byte(f) && !peer_has_ufo(n)) { + error_report("virtio-net: saved image requires TUN_F_UFO support"); + return -1; } if (n->max_queues > 1) { @@ -1665,6 +1621,16 @@ static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f, } } + if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { + n->curr_guest_offloads = qemu_get_be64(f); + } else { + n->curr_guest_offloads = virtio_net_supported_guest_offloads(n); + } + + if (peer_has_vnet_hdr(n)) { + virtio_net_apply_guest_offloads(n); + } + virtio_net_set_queues(n); /* Find the first multicast entry in the saved MAC filter */ @@ -1682,11 +1648,17 @@ static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f, qemu_get_subqueue(n->nic, i)->link_down = link_down; } + if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE) && + virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) { + n->announce_counter = SELF_ANNOUNCE_ROUNDS; + timer_mod(n->announce_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL)); + } + return 0; } static NetClientInfo net_virtio_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, + .type = NET_CLIENT_DRIVER_NIC, .size = sizeof(NICState), .can_receive = virtio_net_can_receive, .receive = virtio_net_receive, @@ -1815,8 +1787,6 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) nc->rxfilter_notify_enabled = 1; n->qdev = dev; - register_savevm(dev, "virtio-net", -1, VIRTIO_NET_VM_VERSION, - virtio_net_save, virtio_net_load, n); } static void virtio_net_device_unrealize(DeviceState *dev, Error **errp) @@ -1828,8 +1798,6 @@ static void virtio_net_device_unrealize(DeviceState *dev, Error **errp) /* This will stop vhost backend if appropriate. */ virtio_net_set_status(vdev, 0); - unregister_savevm(dev, "virtio-net", n); - g_free(n->netclient_name); n->netclient_name = NULL; g_free(n->netclient_type); @@ -1864,6 +1832,9 @@ static void virtio_net_instance_init(Object *obj) DEVICE(n), NULL); } +VMSTATE_VIRTIO_DEVICE(net, VIRTIO_NET_VM_VERSION, virtio_net_load, + virtio_net_save); + static Property virtio_net_properties[] = { DEFINE_PROP_BIT("csum", VirtIONet, host_features, VIRTIO_NET_F_CSUM, true), DEFINE_PROP_BIT("guest_csum", VirtIONet, host_features, @@ -1918,6 +1889,7 @@ static void virtio_net_class_init(ObjectClass *klass, void *data) VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); dc->props = virtio_net_properties; + dc->vmsd = &vmstate_virtio_net; set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); vdc->realize = virtio_net_device_realize; vdc->unrealize = virtio_net_device_unrealize; diff --git a/hw/net/vmware_utils.h b/hw/net/vmware_utils.h index c0dbb2ff4..550060170 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 093a71e12..90f694366 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -30,14 +30,14 @@ #include "vmxnet3.h" #include "vmxnet_debug.h" #include "vmware_utils.h" -#include "vmxnet_tx_pkt.h" -#include "vmxnet_rx_pkt.h" +#include "net_tx_pkt.h" +#include "net_rx_pkt.h" #define PCI_DEVICE_ID_VMWARE_VMXNET3_REVISION 0x1 #define VMXNET3_MSIX_BAR_SIZE 0x2000 #define MIN_BUF_SIZE 60 -/* Compatability flags for migration */ +/* Compatibility flags for migration */ #define VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS_BIT 0 #define VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS \ (1 << VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS_BIT) @@ -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) @@ -280,8 +283,6 @@ typedef struct { /* Whether MSI-X support was installed successfully */ bool msix_used; - /* Whether MSI support was installed successfully */ - bool msi_used; hwaddr drv_shmem; hwaddr temp_shared_guest_driver_memory; @@ -314,13 +315,13 @@ typedef struct { bool peer_has_vhdr; /* TX packets to QEMU interface */ - struct VmxnetTxPkt *tx_pkt; + struct NetTxPkt *tx_pkt; uint32_t offload_mode; uint32_t cso_or_gso_size; uint16_t tci; bool needs_vlan; - struct VmxnetRxPkt *rx_pkt; + struct NetRxPkt *rx_pkt; bool tx_sop; bool skip_current_tx_pkt; @@ -341,14 +342,14 @@ typedef struct { uint32_t mcast_list_len; uint32_t mcast_list_buff_size; /* needed for live migration. */ - /* Compatability flags for migration */ + /* Compatibility flags for migration */ uint32_t compat_flags; } VMXNET3State; /* Interrupt management */ /* - *This function returns sign whether interrupt line is in asserted state + * This function returns sign whether interrupt line is in asserted state * This depends on the type of interrupt used. For INTX interrupt line will * be asserted until explicit deassertion, for MSI(X) interrupt line will * be deasserted automatically due to notification semantics of the MSI(X) @@ -363,7 +364,7 @@ static bool _vmxnet3_assert_interrupt_line(VMXNET3State *s, uint32_t int_idx) msix_notify(d, int_idx); return false; } - if (s->msi_used && msi_enabled(d)) { + if (msi_enabled(d)) { VMW_IRPRN("Sending MSI notification for vector %u", int_idx); msi_notify(d, int_idx); return false; @@ -387,7 +388,7 @@ static void _vmxnet3_deassert_interrupt_line(VMXNET3State *s, int lidx) * This function should never be called for MSI(X) interrupts * because deassertion never required for message interrupts */ - assert(!s->msi_used || !msi_enabled(d)); + assert(!msi_enabled(d)); VMW_IRPRN("Deasserting line for interrupt %u", lidx); pci_irq_deassert(d); @@ -424,7 +425,7 @@ static void vmxnet3_trigger_interrupt(VMXNET3State *s, int lidx) goto do_automask; } - if (s->msi_used && msi_enabled(d) && s->auto_int_masking) { + if (msi_enabled(d) && s->auto_int_masking) { goto do_automask; } @@ -456,9 +457,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) @@ -474,7 +475,7 @@ static void vmxnet3_set_variable_mac(VMXNET3State *s, uint32_t h, uint32_t l) s->conf.macaddr.a[4] = VMXNET3_GET_BYTE(h, 0); s->conf.macaddr.a[5] = VMXNET3_GET_BYTE(h, 1); - VMW_CFPRN("Variable MAC: " VMXNET_MF, VMXNET_MA(s->conf.macaddr.a)); + VMW_CFPRN("Variable MAC: " MAC_FMT, MAC_ARG(s->conf.macaddr.a)); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); } @@ -526,13 +527,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(); @@ -546,18 +548,18 @@ vmxnet3_setup_tx_offloads(VMXNET3State *s) { switch (s->offload_mode) { case VMXNET3_OM_NONE: - vmxnet_tx_pkt_build_vheader(s->tx_pkt, false, false, 0); + net_tx_pkt_build_vheader(s->tx_pkt, false, false, 0); break; case VMXNET3_OM_CSUM: - vmxnet_tx_pkt_build_vheader(s->tx_pkt, false, true, 0); + net_tx_pkt_build_vheader(s->tx_pkt, false, true, 0); VMW_PKPRN("L4 CSO requested\n"); break; case VMXNET3_OM_TSO: - vmxnet_tx_pkt_build_vheader(s->tx_pkt, true, true, + net_tx_pkt_build_vheader(s->tx_pkt, true, true, s->cso_or_gso_size); - vmxnet_tx_pkt_update_ip_checksums(s->tx_pkt); + net_tx_pkt_update_ip_checksums(s->tx_pkt); VMW_PKPRN("GSO offload requested."); break; @@ -590,12 +592,12 @@ static void vmxnet3_on_tx_done_update_stats(VMXNET3State *s, int qidx, Vmxnet3PktStatus status) { - size_t tot_len = vmxnet_tx_pkt_get_total_len(s->tx_pkt); + size_t tot_len = net_tx_pkt_get_total_len(s->tx_pkt); struct UPT1_TxStats *stats = &s->txq_descr[qidx].txq_stats; switch (status) { case VMXNET3_PKT_STATUS_OK: - switch (vmxnet_tx_pkt_get_packet_type(s->tx_pkt)) { + switch (net_tx_pkt_get_packet_type(s->tx_pkt)) { case ETH_PKT_BCAST: stats->bcastPktsTxOK++; stats->bcastBytesTxOK += tot_len; @@ -643,7 +645,7 @@ vmxnet3_on_rx_done_update_stats(VMXNET3State *s, Vmxnet3PktStatus status) { struct UPT1_RxStats *stats = &s->rxq_descr[qidx].rxq_stats; - size_t tot_len = vmxnet_rx_pkt_get_total_len(s->rx_pkt); + size_t tot_len = net_rx_pkt_get_total_len(s->rx_pkt); switch (status) { case VMXNET3_PKT_STATUS_OUT_OF_BUF: @@ -654,7 +656,7 @@ vmxnet3_on_rx_done_update_stats(VMXNET3State *s, stats->pktsRxError++; break; case VMXNET3_PKT_STATUS_OK: - switch (vmxnet_rx_pkt_get_packet_type(s->rx_pkt)) { + switch (net_rx_pkt_get_packet_type(s->rx_pkt)) { case ETH_PKT_BCAST: stats->bcastPktsRxOK++; stats->bcastBytesRxOK += tot_len; @@ -688,13 +690,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); @@ -715,10 +718,10 @@ vmxnet3_send_packet(VMXNET3State *s, uint32_t qidx) } /* debug prints */ - vmxnet3_dump_virt_hdr(vmxnet_tx_pkt_get_vhdr(s->tx_pkt)); - vmxnet_tx_pkt_dump(s->tx_pkt); + vmxnet3_dump_virt_hdr(net_tx_pkt_get_vhdr(s->tx_pkt)); + net_tx_pkt_dump(s->tx_pkt); - if (!vmxnet_tx_pkt_send(s->tx_pkt, qemu_get_queue(s->nic))) { + if (!net_tx_pkt_send(s->tx_pkt, qemu_get_queue(s->nic))) { status = VMXNET3_PKT_STATUS_DISCARD; goto func_exit; } @@ -746,7 +749,7 @@ static void vmxnet3_process_tx_queue(VMXNET3State *s, int qidx) data_len = (txd.len > 0) ? txd.len : VMXNET3_MAX_TX_BUF_SIZE; data_pa = le64_to_cpu(txd.addr); - if (!vmxnet_tx_pkt_add_raw_fragment(s->tx_pkt, + if (!net_tx_pkt_add_raw_fragment(s->tx_pkt, data_pa, data_len)) { s->skip_current_tx_pkt = true; @@ -759,9 +762,9 @@ static void vmxnet3_process_tx_queue(VMXNET3State *s, int qidx) } if (txd.eop) { - if (!s->skip_current_tx_pkt && vmxnet_tx_pkt_parse(s->tx_pkt)) { + if (!s->skip_current_tx_pkt && net_tx_pkt_parse(s->tx_pkt)) { if (s->needs_vlan) { - vmxnet_tx_pkt_setup_vlan_header(s->tx_pkt, s->tci); + net_tx_pkt_setup_vlan_header(s->tx_pkt, s->tci); } vmxnet3_send_packet(s, qidx); @@ -773,7 +776,7 @@ static void vmxnet3_process_tx_queue(VMXNET3State *s, int qidx) vmxnet3_complete_packet(s, qidx, txd_idx); s->tx_sop = true; s->skip_current_tx_pkt = false; - vmxnet_tx_pkt_reset(s->tx_pkt); + net_tx_pkt_reset(s->tx_pkt); } } } @@ -782,9 +785,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,7 +807,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); - cpu_physical_memory_read(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) { @@ -928,7 +934,7 @@ vmxnet3_get_next_rx_descr(VMXNET3State *s, bool is_head, * in the case the host OS performs forwarding, it will forward an * incorrectly checksummed packet. */ -static void vmxnet3_rx_need_csum_calculate(struct VmxnetRxPkt *pkt, +static void vmxnet3_rx_need_csum_calculate(struct NetRxPkt *pkt, const void *pkt_data, size_t pkt_len) { @@ -937,16 +943,16 @@ static void vmxnet3_rx_need_csum_calculate(struct VmxnetRxPkt *pkt, uint8_t *data; int len; - if (!vmxnet_rx_pkt_has_virt_hdr(pkt)) { + if (!net_rx_pkt_has_virt_hdr(pkt)) { return; } - vhdr = vmxnet_rx_pkt_get_vhdr(pkt); + vhdr = net_rx_pkt_get_vhdr(pkt); if (!VMXNET_FLAG_IS_SET(vhdr->flags, VIRTIO_NET_HDR_F_NEEDS_CSUM)) { return; } - vmxnet_rx_pkt_get_protocols(pkt, &isip4, &isip6, &isudp, &istcp); + net_rx_pkt_get_protocols(pkt, &isip4, &isip6, &isudp, &istcp); if (!(isip4 || isip6) || !(istcp || isudp)) { return; } @@ -970,7 +976,7 @@ static void vmxnet3_rx_need_csum_calculate(struct VmxnetRxPkt *pkt, vhdr->flags |= VIRTIO_NET_HDR_F_DATA_VALID; } -static void vmxnet3_rx_update_descr(struct VmxnetRxPkt *pkt, +static void vmxnet3_rx_update_descr(struct NetRxPkt *pkt, struct Vmxnet3_RxCompDesc *rxcd) { int csum_ok, is_gso; @@ -978,16 +984,16 @@ static void vmxnet3_rx_update_descr(struct VmxnetRxPkt *pkt, struct virtio_net_hdr *vhdr; uint8_t offload_type; - if (vmxnet_rx_pkt_is_vlan_stripped(pkt)) { + if (net_rx_pkt_is_vlan_stripped(pkt)) { rxcd->ts = 1; - rxcd->tci = vmxnet_rx_pkt_get_vlan_tag(pkt); + rxcd->tci = net_rx_pkt_get_vlan_tag(pkt); } - if (!vmxnet_rx_pkt_has_virt_hdr(pkt)) { + if (!net_rx_pkt_has_virt_hdr(pkt)) { goto nocsum; } - vhdr = vmxnet_rx_pkt_get_vhdr(pkt); + vhdr = net_rx_pkt_get_vhdr(pkt); /* * Checksum is valid when lower level tell so or when lower level * requires checksum offload telling that packet produced/bridged @@ -1004,7 +1010,7 @@ static void vmxnet3_rx_update_descr(struct VmxnetRxPkt *pkt, goto nocsum; } - vmxnet_rx_pkt_get_protocols(pkt, &isip4, &isip6, &isudp, &istcp); + net_rx_pkt_get_protocols(pkt, &isip4, &isip6, &isudp, &istcp); if ((!istcp && !isudp) || (!isip4 && !isip6)) { goto nocsum; } @@ -1023,10 +1029,11 @@ nocsum: } static void -vmxnet3_physical_memory_writev(const struct iovec *iov, - size_t start_iov_off, - hwaddr target_addr, - size_t bytes_to_copy) +vmxnet3_pci_dma_writev(PCIDevice *pci_dev, + const struct iovec *iov, + size_t start_iov_off, + hwaddr target_addr, + size_t bytes_to_copy) { size_t curr_off = 0; size_t copied = 0; @@ -1036,9 +1043,9 @@ vmxnet3_physical_memory_writev(const struct iovec *iov, size_t chunk_len = MIN((curr_off + iov->iov_len) - start_iov_off, bytes_to_copy); - cpu_physical_memory_write(target_addr + copied, - iov->iov_base + start_iov_off - curr_off, - chunk_len); + pci_dma_write(pci_dev, target_addr + copied, + iov->iov_base + start_iov_off - curr_off, + chunk_len); copied += chunk_len; start_iov_off += chunk_len; @@ -1055,6 +1062,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; @@ -1063,13 +1071,13 @@ vmxnet3_indicate_packet(VMXNET3State *s) uint32_t new_rxcd_gen = VMXNET3_INIT_GEN; hwaddr new_rxcd_pa = 0; hwaddr ready_rxcd_pa = 0; - struct iovec *data = vmxnet_rx_pkt_get_iovec(s->rx_pkt); + struct iovec *data = net_rx_pkt_get_iovec(s->rx_pkt); size_t bytes_copied = 0; - size_t bytes_left = vmxnet_rx_pkt_get_total_len(s->rx_pkt); + size_t bytes_left = net_rx_pkt_get_total_len(s->rx_pkt); uint16_t num_frags = 0; size_t chunk_size; - vmxnet_rx_pkt_dump(s->rx_pkt); + net_rx_pkt_dump(s->rx_pkt); while (bytes_left > 0) { @@ -1088,15 +1096,15 @@ vmxnet3_indicate_packet(VMXNET3State *s) } chunk_size = MIN(bytes_left, rxd.len); - vmxnet3_physical_memory_writev(data, bytes_copied, - le64_to_cpu(rxd.addr), chunk_size); + vmxnet3_pci_dma_writev(d, data, bytes_copied, + le64_to_cpu(rxd.addr), chunk_size); bytes_copied += chunk_size; bytes_left -= chunk_size; vmxnet3_dump_rx_descr(&rxd); if (ready_rxcd_pa != 0) { - cpu_physical_memory_write(ready_rxcd_pa, &rxcd, sizeof(rxcd)); + pci_dma_write(d, ready_rxcd_pa, &rxcd, sizeof(rxcd)); } memset(&rxcd, 0, sizeof(struct Vmxnet3_RxCompDesc)); @@ -1127,7 +1135,8 @@ vmxnet3_indicate_packet(VMXNET3State *s) if (ready_rxcd_pa != 0) { rxcd.eop = 1; rxcd.err = (bytes_left != 0); - cpu_physical_memory_write(ready_rxcd_pa, &rxcd, sizeof(rxcd)); + + pci_dma_write(d, ready_rxcd_pa, &rxcd, sizeof(rxcd)); /* Flush RX descriptor changes */ smp_wmb(); @@ -1158,6 +1167,10 @@ vmxnet3_io_bar0_write(void *opaque, hwaddr addr, { VMXNET3State *s = opaque; + if (!s->device_active) { + return; + } + if (VMW_IS_MULTIREG_ADDR(addr, VMXNET3_REG_TXPROD, VMXNET3_DEVICE_MAX_TX_QUEUES, VMXNET3_REG_ALIGN)) { int tx_queue_idx = @@ -1219,16 +1232,16 @@ static void vmxnet3_reset_interrupt_states(VMXNET3State *s) static void vmxnet3_reset_mac(VMXNET3State *s) { memcpy(&s->conf.macaddr.a, &s->perm_mac.a, sizeof(s->perm_mac.a)); - VMW_CFPRN("MAC address set to: " VMXNET_MF, VMXNET_MA(s->conf.macaddr.a)); + VMW_CFPRN("MAC address set to: " MAC_FMT, MAC_ARG(s->conf.macaddr.a)); } static void vmxnet3_deactivate_device(VMXNET3State *s) { if (s->device_active) { VMW_CBPRN("Deactivating vmxnet3..."); - vmxnet_tx_pkt_reset(s->tx_pkt); - vmxnet_tx_pkt_uninit(s->tx_pkt); - vmxnet_rx_pkt_uninit(s->rx_pkt); + net_tx_pkt_reset(s->tx_pkt); + net_tx_pkt_uninit(s->tx_pkt); + net_rx_pkt_uninit(s->rx_pkt); s->device_active = false; } } @@ -1246,7 +1259,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); } @@ -1254,9 +1269,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)); @@ -1277,8 +1293,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]); @@ -1295,13 +1313,14 @@ 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); - cpu_physical_memory_read(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++) { - VMW_CFPRN("\t" VMXNET_MF, VMXNET_MA(s->mcast_list[i].a)); + VMW_CFPRN("\t" MAC_FMT, MAC_ARG(s->mcast_list[i].a)); } } } @@ -1323,28 +1342,32 @@ 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++) { - cpu_physical_memory_write(s->txq_descr[i].tx_stats_pa, - &s->txq_descr[i].txq_stats, - sizeof(s->txq_descr[i].txq_stats)); + 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++) { - cpu_physical_memory_write(s->rxq_descr[i].rx_stats_pa, - &s->rxq_descr[i].rxq_stats, - sizeof(s->rxq_descr[i].rxq_stats)); + pci_dma_write(d, + s->rxq_descr[i].rx_stats_pa, + &s->rxq_descr[i].rxq_stats, + sizeof(s->rxq_descr[i].rxq_stats)); } } 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; @@ -1364,13 +1387,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); } @@ -1379,8 +1403,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); @@ -1402,8 +1427,8 @@ static void vmxnet3_update_features(VMXNET3State *s) static bool vmxnet3_verify_intx(VMXNET3State *s, int intx) { - return s->msix_used || s->msi_used || (intx == - (pci_get_byte(s->parent_obj.config + PCI_INTERRUPT_PIN) - 1)); + return s->msix_used || msi_enabled(PCI_DEVICE(s)) + || intx == pci_get_byte(s->parent_obj.config + PCI_INTERRUPT_PIN) - 1; } static void vmxnet3_validate_interrupt_idx(bool is_msix, int idx) @@ -1455,12 +1480,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; } @@ -1476,11 +1502,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; @@ -1489,24 +1515,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); /* @@ -1522,25 +1548,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); @@ -1551,15 +1577,16 @@ 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); } /* Preallocate TX packet wrapper */ VMW_CFPRN("Max TX fragments is %u", s->max_tx_frags); - vmxnet_tx_pkt_init(&s->tx_pkt, s->max_tx_frags, s->peer_has_vhdr); - vmxnet_rx_pkt_init(&s->rx_pkt, s->peer_has_vhdr); + net_tx_pkt_init(&s->tx_pkt, PCI_DEVICE(s), + s->max_tx_frags, s->peer_has_vhdr); + net_rx_pkt_init(&s->rx_pkt, s->peer_has_vhdr); /* Read rings memory locations for RX queues */ for (i = 0; i < s->rxq_num; i++) { @@ -1570,7 +1597,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); @@ -1578,18 +1605,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); @@ -1756,19 +1783,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 @@ -1965,7 +1994,7 @@ vmxnet3_rx_filter_may_indicate(VMXNET3State *s, const void *data, return false; } - switch (vmxnet_rx_pkt_get_packet_type(s->rx_pkt)) { + switch (net_rx_pkt_get_packet_type(s->rx_pkt)) { case ETH_PKT_UCAST: if (!VMXNET_FLAG_IS_SET(s->rx_mode, VMXNET3_RXM_UCAST)) { return false; @@ -2013,7 +2042,7 @@ vmxnet3_receive(NetClientState *nc, const uint8_t *buf, size_t size) } if (s->peer_has_vhdr) { - vmxnet_rx_pkt_set_vhdr(s->rx_pkt, (struct virtio_net_hdr *)buf); + net_rx_pkt_set_vhdr(s->rx_pkt, (struct virtio_net_hdr *)buf); buf += sizeof(struct virtio_net_hdr); size -= sizeof(struct virtio_net_hdr); } @@ -2026,13 +2055,13 @@ vmxnet3_receive(NetClientState *nc, const uint8_t *buf, size_t size) size = sizeof(min_buf); } - vmxnet_rx_pkt_set_packet_type(s->rx_pkt, + net_rx_pkt_set_packet_type(s->rx_pkt, get_eth_packet_type(PKT_GET_ETH_HDR(buf))); if (vmxnet3_rx_filter_may_indicate(s, buf, size)) { - vmxnet_rx_pkt_set_protocols(s->rx_pkt, buf, size); + net_rx_pkt_set_protocols(s->rx_pkt, buf, size); vmxnet3_rx_need_csum_calculate(s->rx_pkt, buf, size); - vmxnet_rx_pkt_attach_data(s->rx_pkt, buf, size, s->rx_vlan_stripping); + net_rx_pkt_attach_data(s->rx_pkt, buf, size, s->rx_vlan_stripping); bytes_indicated = vmxnet3_indicate_packet(s) ? size : -1; if (bytes_indicated < size) { VMW_PKPRN("RX: %zu of %zu bytes indicated", bytes_indicated, size); @@ -2062,7 +2091,7 @@ static void vmxnet3_set_link_status(NetClientState *nc) } static NetClientInfo net_vmxnet3_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, + .type = NET_CLIENT_DRIVER_NIC, .size = sizeof(NICState), .receive = vmxnet3_receive, .link_status_changed = vmxnet3_set_link_status, @@ -2102,7 +2131,7 @@ static void vmxnet3_net_init(VMXNET3State *s) s->link_status_and_speed = VMXNET3_LINK_SPEED | VMXNET3_LINK_STATUS_UP; - VMW_CFPRN("Permanent MAC: " VMXNET_MF, VMXNET_MA(s->perm_mac.a)); + VMW_CFPRN("Permanent MAC: " MAC_FMT, MAC_ARG(s->perm_mac.a)); s->nic = qemu_new_nic(&net_vmxnet3_info, &s->conf, object_get_typename(OBJECT(s)), @@ -2189,35 +2218,12 @@ vmxnet3_cleanup_msix(VMXNET3State *s) } } -#define VMXNET3_USE_64BIT (true) -#define VMXNET3_PER_VECTOR_MASK (false) - -static bool -vmxnet3_init_msi(VMXNET3State *s) -{ - PCIDevice *d = PCI_DEVICE(s); - int res; - - res = msi_init(d, VMXNET3_MSI_OFFSET(s), VMXNET3_MAX_NMSIX_INTRS, - VMXNET3_USE_64BIT, VMXNET3_PER_VECTOR_MASK); - if (0 > res) { - VMW_WRPRN("Failed to initialize MSI, error %d", res); - s->msi_used = false; - } else { - s->msi_used = true; - } - - return s->msi_used; -} - static void vmxnet3_cleanup_msi(VMXNET3State *s) { PCIDevice *d = PCI_DEVICE(s); - if (s->msi_used) { - msi_uninit(d); - } + msi_uninit(d); } static void @@ -2255,9 +2261,9 @@ static const MemoryRegionOps b1_ops = { }, }; -static uint8_t *vmxnet3_device_serial_num(VMXNET3State *s) +static uint64_t vmxnet3_device_serial_num(VMXNET3State *s) { - static uint64_t dsn_payload; + uint64_t dsn_payload; uint8_t *dsnp = (uint8_t *)&dsn_payload; dsnp[0] = 0xfe; @@ -2268,13 +2274,18 @@ static uint8_t *vmxnet3_device_serial_num(VMXNET3State *s) dsnp[5] = s->conf.macaddr.a[1]; dsnp[6] = s->conf.macaddr.a[2]; dsnp[7] = 0xff; - return dsnp; + return dsn_payload; } + +#define VMXNET3_USE_64BIT (true) +#define VMXNET3_PER_VECTOR_MASK (false) + static void vmxnet3_pci_realize(PCIDevice *pci_dev, Error **errp) { DeviceState *dev = DEVICE(pci_dev); VMXNET3State *s = VMXNET3(pci_dev); + int ret; VMW_CBPRN("Starting init..."); @@ -2298,14 +2309,16 @@ static void vmxnet3_pci_realize(PCIDevice *pci_dev, Error **errp) /* Interrupt pin A */ pci_dev->config[PCI_INTERRUPT_PIN] = 0x01; + ret = msi_init(pci_dev, VMXNET3_MSI_OFFSET(s), VMXNET3_MAX_NMSIX_INTRS, + VMXNET3_USE_64BIT, VMXNET3_PER_VECTOR_MASK, NULL); + /* Any error other than -ENOTSUP(board's MSI support is broken) + * is a programming error. Fall back to INTx silently on -ENOTSUP */ + assert(!ret || ret == -ENOTSUP); + if (!vmxnet3_init_msix(s)) { VMW_WRPRN("Failed to initialize MSI-X, configuration is inconsistent."); } - if (!vmxnet3_init_msi(s)) { - VMW_WRPRN("Failed to initialize MSI, configuration is inconsistent."); - } - vmxnet3_net_init(s); if (pci_is_express(pci_dev)) { @@ -2313,10 +2326,8 @@ static void vmxnet3_pci_realize(PCIDevice *pci_dev, Error **errp) pcie_endpoint_cap_init(pci_dev, VMXNET3_EXP_EP_OFFSET); } - pcie_add_capability(pci_dev, PCI_EXT_CAP_ID_DSN, 0x1, - VMXNET3_DSN_OFFSET, PCI_EXT_CAP_DSN_SIZEOF); - memcpy(pci_dev->config + VMXNET3_DSN_OFFSET + 4, - vmxnet3_device_serial_num(s), sizeof(uint64_t)); + pcie_dev_ser_num_init(pci_dev, VMXNET3_DSN_OFFSET, + vmxnet3_device_serial_num(s)); } register_savevm(dev, "vmxnet3-msix", -1, 1, @@ -2538,8 +2549,9 @@ static int vmxnet3_post_load(void *opaque, int version_id) VMXNET3State *s = opaque; PCIDevice *d = PCI_DEVICE(s); - vmxnet_tx_pkt_init(&s->tx_pkt, s->max_tx_frags, s->peer_has_vhdr); - vmxnet_rx_pkt_init(&s->rx_pkt, s->peer_has_vhdr); + net_tx_pkt_init(&s->tx_pkt, PCI_DEVICE(s), + s->max_tx_frags, s->peer_has_vhdr); + net_rx_pkt_init(&s->rx_pkt, s->peer_has_vhdr); if (s->msix_used) { if (!vmxnet3_use_msix_vectors(s, VMXNET3_MAX_INTRS)) { @@ -2693,6 +2705,7 @@ static void vmxnet3_class_init(ObjectClass *class, void *data) c->vendor_id = PCI_VENDOR_ID_VMWARE; c->device_id = PCI_DEVICE_ID_VMWARE_VMXNET3; c->revision = PCI_DEVICE_ID_VMWARE_VMXNET3_REVISION; + c->romfile = "efi-vmxnet3.rom"; c->class_id = PCI_CLASS_NETWORK_ETHERNET; c->subsystem_vendor_id = PCI_VENDOR_ID_VMWARE; c->subsystem_id = PCI_DEVICE_ID_VMWARE_VMXNET3; diff --git a/hw/net/vmxnet3.h b/hw/net/vmxnet3.h index f7006afe9..f9352c4a2 100644 --- a/hw/net/vmxnet3.h +++ b/hw/net/vmxnet3.h @@ -15,8 +15,8 @@ * */ -#ifndef _QEMU_VMXNET3_H -#define _QEMU_VMXNET3_H +#ifndef QEMU_VMXNET3_H +#define QEMU_VMXNET3_H #define VMXNET3_DEVICE_MAX_TX_QUEUES 8 #define VMXNET3_DEVICE_MAX_RX_QUEUES 8 /* Keep this value as a power of 2 */ diff --git a/hw/net/vmxnet_debug.h b/hw/net/vmxnet_debug.h index 96495dbb1..cb50aa95d 100644 --- a/hw/net/vmxnet_debug.h +++ b/hw/net/vmxnet_debug.h @@ -15,8 +15,8 @@ * */ -#ifndef _QEMU_VMXNET_DEBUG_H -#define _QEMU_VMXNET_DEBUG_H +#ifndef QEMU_VMXNET_DEBUG_H +#define QEMU_VMXNET_DEBUG_H #define VMXNET_DEVICE_NAME "vmxnet3" @@ -142,7 +142,4 @@ } \ } while (0) -#define VMXNET_MF "%02X:%02X:%02X:%02X:%02X:%02X" -#define VMXNET_MA(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] - -#endif /* _QEMU_VMXNET3_DEBUG_H */ +#endif /* QEMU_VMXNET_DEBUG_H */ diff --git a/hw/net/vmxnet_rx_pkt.c b/hw/net/vmxnet_rx_pkt.c deleted file mode 100644 index 21bb46e68..000000000 --- a/hw/net/vmxnet_rx_pkt.c +++ /dev/null @@ -1,187 +0,0 @@ -/* - * QEMU VMWARE VMXNET* paravirtual NICs - RX packets abstractions - * - * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) - * - * Developed by Daynix Computing LTD (http://www.daynix.com) - * - * Authors: - * Dmitry Fleytman <dmitry@daynix.com> - * Tamir Shomer <tamirs@daynix.com> - * Yan Vugenfirer <yan@daynix.com> - * - * 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 "vmxnet_rx_pkt.h" -#include "net/eth.h" -#include "qemu-common.h" -#include "qemu/iov.h" -#include "net/checksum.h" -#include "net/tap.h" - -/* - * RX packet may contain up to 2 fragments - rebuilt eth header - * in case of VLAN tag stripping - * and payload received from QEMU - in any case - */ -#define VMXNET_MAX_RX_PACKET_FRAGMENTS (2) - -struct VmxnetRxPkt { - struct virtio_net_hdr virt_hdr; - uint8_t ehdr_buf[ETH_MAX_L2_HDR_LEN]; - struct iovec vec[VMXNET_MAX_RX_PACKET_FRAGMENTS]; - uint16_t vec_len; - uint32_t tot_len; - uint16_t tci; - bool vlan_stripped; - bool has_virt_hdr; - eth_pkt_types_e packet_type; - - /* Analysis results */ - bool isip4; - bool isip6; - bool isudp; - bool istcp; -}; - -void vmxnet_rx_pkt_init(struct VmxnetRxPkt **pkt, bool has_virt_hdr) -{ - struct VmxnetRxPkt *p = g_malloc0(sizeof *p); - p->has_virt_hdr = has_virt_hdr; - *pkt = p; -} - -void vmxnet_rx_pkt_uninit(struct VmxnetRxPkt *pkt) -{ - g_free(pkt); -} - -struct virtio_net_hdr *vmxnet_rx_pkt_get_vhdr(struct VmxnetRxPkt *pkt) -{ - assert(pkt); - return &pkt->virt_hdr; -} - -void vmxnet_rx_pkt_attach_data(struct VmxnetRxPkt *pkt, const void *data, - size_t len, bool strip_vlan) -{ - uint16_t tci = 0; - uint16_t ploff; - assert(pkt); - pkt->vlan_stripped = false; - - if (strip_vlan) { - pkt->vlan_stripped = eth_strip_vlan(data, pkt->ehdr_buf, &ploff, &tci); - } - - if (pkt->vlan_stripped) { - pkt->vec[0].iov_base = pkt->ehdr_buf; - pkt->vec[0].iov_len = ploff - sizeof(struct vlan_header); - pkt->vec[1].iov_base = (uint8_t *) data + ploff; - pkt->vec[1].iov_len = len - ploff; - pkt->vec_len = 2; - pkt->tot_len = len - ploff + sizeof(struct eth_header); - } else { - pkt->vec[0].iov_base = (void *)data; - pkt->vec[0].iov_len = len; - pkt->vec_len = 1; - pkt->tot_len = len; - } - - pkt->tci = tci; -} - -void vmxnet_rx_pkt_dump(struct VmxnetRxPkt *pkt) -{ -#ifdef VMXNET_RX_PKT_DEBUG - VmxnetRxPkt *pkt = (VmxnetRxPkt *)pkt; - assert(pkt); - - printf("RX PKT: tot_len: %d, vlan_stripped: %d, vlan_tag: %d\n", - pkt->tot_len, pkt->vlan_stripped, pkt->tci); -#endif -} - -void vmxnet_rx_pkt_set_packet_type(struct VmxnetRxPkt *pkt, - eth_pkt_types_e packet_type) -{ - assert(pkt); - - pkt->packet_type = packet_type; - -} - -eth_pkt_types_e vmxnet_rx_pkt_get_packet_type(struct VmxnetRxPkt *pkt) -{ - assert(pkt); - - return pkt->packet_type; -} - -size_t vmxnet_rx_pkt_get_total_len(struct VmxnetRxPkt *pkt) -{ - assert(pkt); - - return pkt->tot_len; -} - -void vmxnet_rx_pkt_set_protocols(struct VmxnetRxPkt *pkt, const void *data, - size_t len) -{ - assert(pkt); - - eth_get_protocols(data, len, &pkt->isip4, &pkt->isip6, - &pkt->isudp, &pkt->istcp); -} - -void vmxnet_rx_pkt_get_protocols(struct VmxnetRxPkt *pkt, - bool *isip4, bool *isip6, - bool *isudp, bool *istcp) -{ - assert(pkt); - - *isip4 = pkt->isip4; - *isip6 = pkt->isip6; - *isudp = pkt->isudp; - *istcp = pkt->istcp; -} - -struct iovec *vmxnet_rx_pkt_get_iovec(struct VmxnetRxPkt *pkt) -{ - assert(pkt); - - return pkt->vec; -} - -void vmxnet_rx_pkt_set_vhdr(struct VmxnetRxPkt *pkt, - struct virtio_net_hdr *vhdr) -{ - assert(pkt); - - memcpy(&pkt->virt_hdr, vhdr, sizeof pkt->virt_hdr); -} - -bool vmxnet_rx_pkt_is_vlan_stripped(struct VmxnetRxPkt *pkt) -{ - assert(pkt); - - return pkt->vlan_stripped; -} - -bool vmxnet_rx_pkt_has_virt_hdr(struct VmxnetRxPkt *pkt) -{ - assert(pkt); - - return pkt->has_virt_hdr; -} - -uint16_t vmxnet_rx_pkt_get_vlan_tag(struct VmxnetRxPkt *pkt) -{ - assert(pkt); - - return pkt->tci; -} diff --git a/hw/net/vmxnet_rx_pkt.h b/hw/net/vmxnet_rx_pkt.h deleted file mode 100644 index 0a45c1ba0..000000000 --- a/hw/net/vmxnet_rx_pkt.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - * QEMU VMWARE VMXNET* paravirtual NICs - RX packets abstraction - * - * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) - * - * Developed by Daynix Computing LTD (http://www.daynix.com) - * - * Authors: - * Dmitry Fleytman <dmitry@daynix.com> - * Tamir Shomer <tamirs@daynix.com> - * Yan Vugenfirer <yan@daynix.com> - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#ifndef VMXNET_RX_PKT_H -#define VMXNET_RX_PKT_H - -#include "net/eth.h" - -/* defines to enable packet dump functions */ -/*#define VMXNET_RX_PKT_DEBUG*/ - -struct VmxnetRxPkt; - -/** - * Clean all rx packet resources - * - * @pkt: packet - * - */ -void vmxnet_rx_pkt_uninit(struct VmxnetRxPkt *pkt); - -/** - * Init function for rx packet functionality - * - * @pkt: packet pointer - * @has_virt_hdr: device uses virtio header - * - */ -void vmxnet_rx_pkt_init(struct VmxnetRxPkt **pkt, bool has_virt_hdr); - -/** - * returns total length of data attached to rx context - * - * @pkt: packet - * - * Return: nothing - * - */ -size_t vmxnet_rx_pkt_get_total_len(struct VmxnetRxPkt *pkt); - -/** - * parse and set packet analysis results - * - * @pkt: packet - * @data: pointer to the data buffer to be parsed - * @len: data length - * - */ -void vmxnet_rx_pkt_set_protocols(struct VmxnetRxPkt *pkt, const void *data, - size_t len); - -/** - * fetches packet analysis results - * - * @pkt: packet - * @isip4: whether the packet given is IPv4 - * @isip6: whether the packet given is IPv6 - * @isudp: whether the packet given is UDP - * @istcp: whether the packet given is TCP - * - */ -void vmxnet_rx_pkt_get_protocols(struct VmxnetRxPkt *pkt, - bool *isip4, bool *isip6, - bool *isudp, bool *istcp); - -/** - * returns virtio header stored in rx context - * - * @pkt: packet - * @ret: virtio header - * - */ -struct virtio_net_hdr *vmxnet_rx_pkt_get_vhdr(struct VmxnetRxPkt *pkt); - -/** - * returns packet type - * - * @pkt: packet - * @ret: packet type - * - */ -eth_pkt_types_e vmxnet_rx_pkt_get_packet_type(struct VmxnetRxPkt *pkt); - -/** - * returns vlan tag - * - * @pkt: packet - * @ret: VLAN tag - * - */ -uint16_t vmxnet_rx_pkt_get_vlan_tag(struct VmxnetRxPkt *pkt); - -/** - * tells whether vlan was stripped from the packet - * - * @pkt: packet - * @ret: VLAN stripped sign - * - */ -bool vmxnet_rx_pkt_is_vlan_stripped(struct VmxnetRxPkt *pkt); - -/** - * notifies caller if the packet has virtio header - * - * @pkt: packet - * @ret: true if packet has virtio header, false otherwize - * - */ -bool vmxnet_rx_pkt_has_virt_hdr(struct VmxnetRxPkt *pkt); - -/** - * attach data to rx packet - * - * @pkt: packet - * @data: pointer to the data buffer - * @len: data length - * @strip_vlan: should the module strip vlan from data - * - */ -void vmxnet_rx_pkt_attach_data(struct VmxnetRxPkt *pkt, const void *data, - size_t len, bool strip_vlan); - -/** - * returns io vector that holds the attached data - * - * @pkt: packet - * @ret: pointer to IOVec - * - */ -struct iovec *vmxnet_rx_pkt_get_iovec(struct VmxnetRxPkt *pkt); - -/** - * prints rx packet data if debug is enabled - * - * @pkt: packet - * - */ -void vmxnet_rx_pkt_dump(struct VmxnetRxPkt *pkt); - -/** - * copy passed vhdr data to packet context - * - * @pkt: packet - * @vhdr: VHDR buffer - * - */ -void vmxnet_rx_pkt_set_vhdr(struct VmxnetRxPkt *pkt, - struct virtio_net_hdr *vhdr); - -/** - * save packet type in packet context - * - * @pkt: packet - * @packet_type: the packet type - * - */ -void vmxnet_rx_pkt_set_packet_type(struct VmxnetRxPkt *pkt, - eth_pkt_types_e packet_type); - -#endif diff --git a/hw/net/vmxnet_tx_pkt.h b/hw/net/vmxnet_tx_pkt.h deleted file mode 100644 index f51e98ad9..000000000 --- a/hw/net/vmxnet_tx_pkt.h +++ /dev/null @@ -1,146 +0,0 @@ -/* - * QEMU VMWARE VMXNET* paravirtual NICs - TX packets abstraction - * - * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) - * - * Developed by Daynix Computing LTD (http://www.daynix.com) - * - * Authors: - * Dmitry Fleytman <dmitry@daynix.com> - * Tamir Shomer <tamirs@daynix.com> - * Yan Vugenfirer <yan@daynix.com> - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#ifndef VMXNET_TX_PKT_H -#define VMXNET_TX_PKT_H - -#include "net/eth.h" -#include "exec/hwaddr.h" - -/* define to enable packet dump functions */ -/*#define VMXNET_TX_PKT_DEBUG*/ - -struct VmxnetTxPkt; - -/** - * Init function for tx packet functionality - * - * @pkt: packet pointer - * @max_frags: max tx ip fragments - * @has_virt_hdr: device uses virtio header. - */ -void vmxnet_tx_pkt_init(struct VmxnetTxPkt **pkt, uint32_t max_frags, - bool has_virt_hdr); - -/** - * Clean all tx packet resources. - * - * @pkt: packet. - */ -void vmxnet_tx_pkt_uninit(struct VmxnetTxPkt *pkt); - -/** - * get virtio header - * - * @pkt: packet - * @ret: virtio header - */ -struct virtio_net_hdr *vmxnet_tx_pkt_get_vhdr(struct VmxnetTxPkt *pkt); - -/** - * build virtio header (will be stored in module context) - * - * @pkt: packet - * @tso_enable: TSO enabled - * @csum_enable: CSO enabled - * @gso_size: MSS size for TSO - * - */ -void vmxnet_tx_pkt_build_vheader(struct VmxnetTxPkt *pkt, bool tso_enable, - bool csum_enable, uint32_t gso_size); - -/** - * updates vlan tag, and adds vlan header in case it is missing - * - * @pkt: packet - * @vlan: VLAN tag - * - */ -void vmxnet_tx_pkt_setup_vlan_header(struct VmxnetTxPkt *pkt, uint16_t vlan); - -/** - * populate data fragment into pkt context. - * - * @pkt: packet - * @pa: physical address of fragment - * @len: length of fragment - * - */ -bool vmxnet_tx_pkt_add_raw_fragment(struct VmxnetTxPkt *pkt, hwaddr pa, - size_t len); - -/** - * fix ip header fields and calculate checksums needed. - * - * @pkt: packet - * - */ -void vmxnet_tx_pkt_update_ip_checksums(struct VmxnetTxPkt *pkt); - -/** - * get length of all populated data. - * - * @pkt: packet - * @ret: total data length - * - */ -size_t vmxnet_tx_pkt_get_total_len(struct VmxnetTxPkt *pkt); - -/** - * get packet type - * - * @pkt: packet - * @ret: packet type - * - */ -eth_pkt_types_e vmxnet_tx_pkt_get_packet_type(struct VmxnetTxPkt *pkt); - -/** - * prints packet data if debug is enabled - * - * @pkt: packet - * - */ -void vmxnet_tx_pkt_dump(struct VmxnetTxPkt *pkt); - -/** - * reset tx packet private context (needed to be called between packets) - * - * @pkt: packet - * - */ -void vmxnet_tx_pkt_reset(struct VmxnetTxPkt *pkt); - -/** - * Send packet to qemu. handles sw offloads if vhdr is not supported. - * - * @pkt: packet - * @nc: NetClientState - * @ret: operation result - * - */ -bool vmxnet_tx_pkt_send(struct VmxnetTxPkt *pkt, NetClientState *nc); - -/** - * parse raw packet data and analyze offload requirements. - * - * @pkt: packet - * - */ -bool vmxnet_tx_pkt_parse(struct VmxnetTxPkt *pkt); - -#endif diff --git a/hw/net/xen_nic.c b/hw/net/xen_nic.c index 7281730d9..6856b5299 100644 --- a/hw/net/xen_nic.c +++ b/hw/net/xen_nic.c @@ -22,7 +22,6 @@ #include "qemu/osdep.h" #include <sys/socket.h> #include <sys/ioctl.h> -#include <sys/mman.h> #include <sys/wait.h> #include "hw/hw.h" @@ -270,7 +269,7 @@ static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size /* ------------------------------------------------------------- */ static NetClientInfo net_xen_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, + .type = NET_CLIENT_DRIVER_NIC, .size = sizeof(NICState), .receive = net_rx_packet, }; diff --git a/hw/net/xgmac.c b/hw/net/xgmac.c index 0c5f793bd..46b1aa17f 100644 --- a/hw/net/xgmac.c +++ b/hw/net/xgmac.c @@ -371,7 +371,7 @@ out: } static NetClientInfo net_xgmac_enet_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, + .type = NET_CLIENT_DRIVER_NIC, .size = sizeof(NICState), .receive = eth_rx, }; diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c index de23ab5dc..b6701844d 100644 --- a/hw/net/xilinx_axienet.c +++ b/hw/net/xilinx_axienet.c @@ -935,7 +935,7 @@ xilinx_axienet_data_stream_push(StreamSlave *obj, uint8_t *buf, size_t size) } static NetClientInfo net_xilinx_enet_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, + .type = NET_CLIENT_DRIVER_NIC, .size = sizeof(NICState), .receive = eth_rx, }; diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index bc846e709..35de353b7 100644 --- a/hw/net/xilinx_ethlite.c +++ b/hw/net/xilinx_ethlite.c @@ -197,6 +197,10 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) } D(qemu_log("%s %zd rxbase=%x\n", __func__, size, rxbase)); + if (size > (R_MAX - R_RX_BUF0 - rxbase) * 4) { + D(qemu_log("ethlite packet is too big, size=%x\n", size)); + return -1; + } memcpy(&s->regs[rxbase + R_RX_BUF0], buf, size); s->regs[rxbase + R_RX_CTRL0] |= CTRL_S; @@ -217,7 +221,7 @@ static void xilinx_ethlite_reset(DeviceState *dev) } static NetClientInfo net_xilinx_ethlite_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, + .type = NET_CLIENT_DRIVER_NIC, .size = sizeof(NICState), .can_receive = eth_can_rx, .receive = eth_rx, diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index 999f48028..6a68e594d 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -25,6 +25,7 @@ #include "hw/hw.h" #include "sysemu/sysemu.h" #include "sysemu/dma.h" +#include "hw/boards.h" #include "hw/isa/isa.h" #include "hw/nvram/fw_cfg.h" #include "hw/sysbus.h" @@ -551,7 +552,7 @@ static bool is_version_1(void *opaque, int version_id) return version_id == 1; } -static bool fw_cfg_dma_enabled(void *opaque) +bool fw_cfg_dma_enabled(void *opaque) { FWCfgState *s = opaque; @@ -728,17 +729,22 @@ static int get_fw_cfg_order(FWCfgState *s, const char *name) { int i; - if (s->fw_cfg_order_override > 0) - return s->fw_cfg_order_override; + if (s->fw_cfg_order_override > 0) { + return s->fw_cfg_order_override; + } for (i = 0; i < ARRAY_SIZE(fw_cfg_order); i++) { - if (fw_cfg_order[i].name == NULL) - continue; - if (strcmp(name, fw_cfg_order[i].name) == 0) - return fw_cfg_order[i].order; + if (fw_cfg_order[i].name == NULL) { + continue; + } + + if (strcmp(name, fw_cfg_order[i].name) == 0) { + return fw_cfg_order[i].order; + } } + /* Stick unknown stuff at the end. */ - error_report("warning: Unknown firmware file in legacy mode: %s\n", name); + error_report("warning: Unknown firmware file in legacy mode: %s", name); return FW_CFG_ORDER_OVERRIDE_LAST; } @@ -868,16 +874,17 @@ static void fw_cfg_machine_ready(struct Notifier *n, void *data) static void fw_cfg_init1(DeviceState *dev) { FWCfgState *s = FW_CFG(dev); + MachineState *machine = MACHINE(qdev_get_machine()); assert(!object_resolve_path(FW_CFG_PATH, NULL)); - object_property_add_child(qdev_get_machine(), FW_CFG_NAME, OBJECT(s), NULL); + object_property_add_child(OBJECT(machine), FW_CFG_NAME, OBJECT(s), NULL); qdev_init_nofail(dev); fw_cfg_add_bytes(s, FW_CFG_SIGNATURE, (char *)"QEMU", 4); fw_cfg_add_bytes(s, FW_CFG_UUID, qemu_uuid, 16); - fw_cfg_add_i16(s, FW_CFG_NOGRAPHIC, (uint16_t)(display_type == DT_NOGRAPHIC)); + fw_cfg_add_i16(s, FW_CFG_NOGRAPHIC, (uint16_t)!machine->enable_graphics); fw_cfg_add_i16(s, FW_CFG_NB_CPUS, (uint16_t)smp_cpus); fw_cfg_add_i16(s, FW_CFG_BOOT_MENU, (uint16_t)boot_menu); fw_cfg_bootsplash(s); @@ -983,6 +990,7 @@ static void fw_cfg_class_init(ObjectClass *klass, void *data) static const TypeInfo fw_cfg_info = { .name = TYPE_FW_CFG, .parent = TYPE_SYS_BUS_DEVICE, + .abstract = true, .instance_size = sizeof(FWCfgState), .class_init = fw_cfg_class_init, }; diff --git a/hw/nvram/spapr_nvram.c b/hw/nvram/spapr_nvram.c index 802636ef3..4de5f705d 100644 --- a/hw/nvram/spapr_nvram.c +++ b/hw/nvram/spapr_nvram.c @@ -39,6 +39,7 @@ typedef struct sPAPRNVRAM { uint32_t size; uint8_t *buf; BlockBackend *blk; + VMChangeStateEntry *vmstate; } sPAPRNVRAM; #define TYPE_VIO_SPAPR_NVRAM "spapr-nvram" @@ -124,7 +125,7 @@ static void rtas_nvram_store(PowerPCCPU *cpu, sPAPRMachineState *spapr, alen = len; if (nvram->blk) { - alen = blk_pwrite(nvram->blk, offset, membuf, len); + alen = blk_pwrite(nvram->blk, offset, membuf, len, 0); } assert(nvram->buf); @@ -185,19 +186,25 @@ static int spapr_nvram_pre_load(void *opaque) return 0; } +static void postload_update_cb(void *opaque, int running, RunState state) +{ + sPAPRNVRAM *nvram = opaque; + + /* This is called after bdrv_invalidate_cache_all. */ + + qemu_del_vm_change_state_handler(nvram->vmstate); + nvram->vmstate = NULL; + + blk_pwrite(nvram->blk, 0, nvram->buf, nvram->size, 0); +} + static int spapr_nvram_post_load(void *opaque, int version_id) { sPAPRNVRAM *nvram = VIO_SPAPR_NVRAM(opaque); if (nvram->blk) { - int alen = blk_pwrite(nvram->blk, 0, nvram->buf, nvram->size); - - if (alen < 0) { - return alen; - } - if (alen != nvram->size) { - return -1; - } + nvram->vmstate = qemu_add_vm_change_state_handler(postload_update_cb, + nvram); } return 0; diff --git a/hw/nvram/trace-events b/hw/nvram/trace-events new file mode 100644 index 000000000..1f1e05ab6 --- /dev/null +++ b/hw/nvram/trace-events @@ -0,0 +1,10 @@ +# See docs/tracing.txt for syntax documentation. + +# hw/nvram/ds1225y.c +nvram_read(uint32_t addr, uint32_t ret) "read addr %d: 0x%02x" +nvram_write(uint32_t addr, uint32_t old, uint32_t val) "write addr %d: 0x%02x -> 0x%02x" + +# hw/nvram/fw_cfg.c +fw_cfg_select(void *s, uint16_t key, int ret) "%p key %d = %d" +fw_cfg_read(void *s, uint64_t ret) "%p = %"PRIx64 +fw_cfg_add_file(void *s, int index, char *name, size_t len) "%p #%d: %s (%zd bytes)" diff --git a/hw/pci-bridge/dec.h b/hw/pci-bridge/dec.h index 17dc0c2b0..ae17ca736 100644 --- a/hw/pci-bridge/dec.h +++ b/hw/pci-bridge/dec.h @@ -1,5 +1,5 @@ -#ifndef DEC_PCI_H -#define DEC_PCI_H +#ifndef HW_PCI_BRIDGE_DEC_H +#define HW_PCI_BRIDGE_DEC_H #include "qemu-common.h" diff --git a/hw/pci-bridge/ioh3420.c b/hw/pci-bridge/ioh3420.c index 0937fa34b..c8b5ac420 100644 --- a/hw/pci-bridge/ioh3420.c +++ b/hw/pci-bridge/ioh3420.c @@ -25,6 +25,7 @@ #include "hw/pci/msi.h" #include "hw/pci/pcie.h" #include "ioh3420.h" +#include "qapi/error.h" #define PCI_DEVICE_ID_IOH_EPORT 0x3420 /* D0:F0 express mode */ #define PCI_DEVICE_ID_IOH_REV 0x2 @@ -97,7 +98,9 @@ static int ioh3420_initfn(PCIDevice *d) PCIEPort *p = PCIE_PORT(d); PCIESlot *s = PCIE_SLOT(d); int rc; + Error *err = NULL; + pci_config_set_interrupt_pin(d->config, 1); pci_bridge_initfn(d, TYPE_PCIE_BUS); pcie_port_init_reg(d); @@ -106,12 +109,16 @@ static int ioh3420_initfn(PCIDevice *d) if (rc < 0) { goto err_bridge; } + rc = msi_init(d, IOH_EP_MSI_OFFSET, IOH_EP_MSI_NR_VECTOR, IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT, - IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT); + IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT, &err); if (rc < 0) { + assert(rc == -ENOTSUP); + error_report_err(err); goto err_bridge; } + rc = pcie_cap_init(d, IOH_EP_EXP_OFFSET, PCI_EXP_TYPE_ROOT_PORT, p->port); if (rc < 0) { goto err_msi; @@ -120,18 +127,21 @@ static int ioh3420_initfn(PCIDevice *d) pcie_cap_arifwd_init(d); pcie_cap_deverr_init(d); pcie_cap_slot_init(d, s->slot); + pcie_cap_root_init(d); + pcie_chassis_create(s->chassis); rc = pcie_chassis_add_slot(s); if (rc < 0) { goto err_pcie_cap; } - pcie_cap_root_init(d); + rc = pcie_aer_init(d, IOH_EP_AER_OFFSET, PCI_ERR_SIZEOF); if (rc < 0) { goto err; } pcie_aer_root_init(d); ioh3420_aer_vector_update(d); + return 0; err: @@ -207,12 +217,3 @@ static void ioh3420_register_types(void) } type_init(ioh3420_register_types) - -/* - * Local variables: - * c-indent-level: 4 - * c-basic-offset: 4 - * tab-width: 8 - * indent-tab-mode: nil - * End: - */ diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c index 7b582e96a..5dbd933cc 100644 --- a/hw/pci-bridge/pci_bridge_dev.c +++ b/hw/pci-bridge/pci_bridge_dev.c @@ -42,9 +42,10 @@ struct PCIBridgeDev { MemoryRegion bar; uint8_t chassis_nr; -#define PCI_BRIDGE_DEV_F_MSI_REQ 0 -#define PCI_BRIDGE_DEV_F_SHPC_REQ 1 +#define PCI_BRIDGE_DEV_F_SHPC_REQ 0 uint32_t flags; + + OnOffAuto msi; }; typedef struct PCIBridgeDev PCIBridgeDev; @@ -53,6 +54,7 @@ static int pci_bridge_dev_initfn(PCIDevice *dev) PCIBridge *br = PCI_BRIDGE(dev); PCIBridgeDev *bridge_dev = PCI_BRIDGE_DEV(dev); int err; + Error *local_err = NULL; pci_bridge_initfn(dev, TYPE_PCI_BUS); @@ -66,19 +68,33 @@ static int pci_bridge_dev_initfn(PCIDevice *dev) } } else { /* MSI is not applicable without SHPC */ - bridge_dev->flags &= ~(1 << PCI_BRIDGE_DEV_F_MSI_REQ); + bridge_dev->msi = ON_OFF_AUTO_OFF; } + err = slotid_cap_init(dev, 0, bridge_dev->chassis_nr, 0); if (err) { goto slotid_error; } - if ((bridge_dev->flags & (1 << PCI_BRIDGE_DEV_F_MSI_REQ)) && - msi_nonbroken) { - err = msi_init(dev, 0, 1, true, true); - if (err < 0) { + + if (bridge_dev->msi != ON_OFF_AUTO_OFF) { + /* it means SHPC exists, because MSI is needed by SHPC */ + + err = msi_init(dev, 0, 1, true, true, &local_err); + /* Any error other than -ENOTSUP(board's MSI support is broken) + * is a programming error */ + assert(!err || err == -ENOTSUP); + if (err && bridge_dev->msi == ON_OFF_AUTO_ON) { + /* Can't satisfy user's explicit msi=on request, fail */ + error_append_hint(&local_err, "You have to use msi=auto (default) " + "or msi=off with this machine type.\n"); + error_report_err(local_err); goto msi_error; } + assert(!local_err || bridge_dev->msi == ON_OFF_AUTO_AUTO); + /* With msi=auto, we fall back to MSI off silently */ + error_free(local_err); } + if (shpc_present(dev)) { /* TODO: spec recommends using 64 bit prefetcheable BAR. * Check whether that works well. */ @@ -86,6 +102,7 @@ static int pci_bridge_dev_initfn(PCIDevice *dev) PCI_BASE_ADDRESS_MEM_TYPE_64, &bridge_dev->bar); } return 0; + msi_error: slotid_cap_cleanup(dev); slotid_error: @@ -143,8 +160,8 @@ static Property pci_bridge_dev_properties[] = { /* Note: 0 is not a legal chassis number. */ DEFINE_PROP_UINT8(PCI_BRIDGE_DEV_PROP_CHASSIS_NR, PCIBridgeDev, chassis_nr, 0), - DEFINE_PROP_BIT(PCI_BRIDGE_DEV_PROP_MSI, PCIBridgeDev, flags, - PCI_BRIDGE_DEV_F_MSI_REQ, true), + DEFINE_PROP_ON_OFF_AUTO(PCI_BRIDGE_DEV_PROP_MSI, PCIBridgeDev, msi, + ON_OFF_AUTO_AUTO), DEFINE_PROP_BIT(PCI_BRIDGE_DEV_PROP_SHPC, PCIBridgeDev, flags, PCI_BRIDGE_DEV_F_SHPC_REQ, true), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/pci-bridge/pci_expander_bridge.c b/hw/pci-bridge/pci_expander_bridge.c index ba320bd85..1cc598f7e 100644 --- a/hw/pci-bridge/pci_expander_bridge.c +++ b/hw/pci-bridge/pci_expander_bridge.c @@ -11,6 +11,7 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/pci/pci.h" #include "hw/pci/pci_bus.h" #include "hw/pci/pci_host.h" @@ -149,6 +150,8 @@ static void pxb_host_class_init(ObjectClass *class, void *data) PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(class); dc->fw_name = "pci"; + /* Reason: Internal part of the pxb/pxb-pcie device, not usable by itself */ + dc->cannot_instantiate_with_device_add_yet = true; sbc->explicit_ofw_unit_address = pxb_host_ofw_unit_address; hc->root_bus_path = pxb_host_root_bus_path; } @@ -160,30 +163,25 @@ static const TypeInfo pxb_host_info = { }; /* - * Registers the PXB bus as a child of the i440fx root bus. - * - * Returns 0 on successs, -1 if i440fx host was not - * found or the bus number is already in use. + * Registers the PXB bus as a child of pci host root bus. */ -static int pxb_register_bus(PCIDevice *dev, PCIBus *pxb_bus) +static void pxb_register_bus(PCIDevice *dev, PCIBus *pxb_bus, Error **errp) { PCIBus *bus = dev->bus; int pxb_bus_num = pci_bus_num(pxb_bus); if (bus->parent_dev) { - error_report("PXB devices can be attached only to root bus."); - return -1; + error_setg(errp, "PXB devices can be attached only to root bus"); + return; } QLIST_FOREACH(bus, &bus->child, sibling) { if (pci_bus_num(bus) == pxb_bus_num) { - error_report("Bus %d is already in use.", pxb_bus_num); - return -1; + error_setg(errp, "Bus %d is already in use", pxb_bus_num); + return; } } QLIST_INSERT_HEAD(&dev->bus->child, pxb_bus, sibling); - - return 0; } static int pxb_map_irq_fn(PCIDevice *pci_dev, int pin) @@ -213,17 +211,18 @@ static gint pxb_compare(gconstpointer a, gconstpointer b) 0; } -static int pxb_dev_init_common(PCIDevice *dev, bool pcie) +static void pxb_dev_realize_common(PCIDevice *dev, bool pcie, Error **errp) { PXBDev *pxb = convert_to_pxb(dev); DeviceState *ds, *bds = NULL; PCIBus *bus; const char *dev_name = NULL; + Error *local_err = NULL; if (pxb->numa_node != NUMA_NODE_UNASSIGNED && pxb->numa_node >= nb_numa_nodes) { - error_report("Illegal numa node %d.", pxb->numa_node); - return -EINVAL; + error_setg(errp, "Illegal numa node %d", pxb->numa_node); + return; } if (dev->qdev.id && *dev->qdev.id) { @@ -248,7 +247,9 @@ static int pxb_dev_init_common(PCIDevice *dev, bool pcie) PCI_HOST_BRIDGE(ds)->bus = bus; - if (pxb_register_bus(dev, bus)) { + pxb_register_bus(dev, bus, &local_err); + if (local_err) { + error_propagate(errp, local_err); goto err_register_bus; } @@ -262,23 +263,22 @@ static int pxb_dev_init_common(PCIDevice *dev, bool pcie) pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_HOST); pxb_dev_list = g_list_insert_sorted(pxb_dev_list, pxb, pxb_compare); - return 0; + return; err_register_bus: object_unref(OBJECT(bds)); object_unparent(OBJECT(bus)); object_unref(OBJECT(ds)); - return -EINVAL; } -static int pxb_dev_initfn(PCIDevice *dev) +static void pxb_dev_realize(PCIDevice *dev, Error **errp) { if (pci_bus_is_express(dev->bus)) { - error_report("pxb devices cannot reside on a PCIe bus!"); - return -EINVAL; + error_setg(errp, "pxb devices cannot reside on a PCIe bus"); + return; } - return pxb_dev_init_common(dev, false); + pxb_dev_realize_common(dev, false, errp); } static void pxb_dev_exitfn(PCIDevice *pci_dev) @@ -300,7 +300,7 @@ static void pxb_dev_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = pxb_dev_initfn; + k->realize = pxb_dev_realize; k->exit = pxb_dev_exitfn; k->vendor_id = PCI_VENDOR_ID_REDHAT; k->device_id = PCI_DEVICE_ID_REDHAT_PXB; @@ -308,6 +308,7 @@ static void pxb_dev_class_init(ObjectClass *klass, void *data) dc->desc = "PCI Expander Bridge"; dc->props = pxb_dev_properties; + dc->hotpluggable = false; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } @@ -318,14 +319,14 @@ static const TypeInfo pxb_dev_info = { .class_init = pxb_dev_class_init, }; -static int pxb_pcie_dev_initfn(PCIDevice *dev) +static void pxb_pcie_dev_realize(PCIDevice *dev, Error **errp) { if (!pci_bus_is_express(dev->bus)) { - error_report("pxb-pcie devices cannot reside on a PCI bus!"); - return -EINVAL; + error_setg(errp, "pxb-pcie devices cannot reside on a PCI bus"); + return; } - return pxb_dev_init_common(dev, true); + pxb_dev_realize_common(dev, true, errp); } static void pxb_pcie_dev_class_init(ObjectClass *klass, void *data) @@ -333,7 +334,7 @@ static void pxb_pcie_dev_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = pxb_pcie_dev_initfn; + k->realize = pxb_pcie_dev_realize; k->exit = pxb_dev_exitfn; k->vendor_id = PCI_VENDOR_ID_REDHAT; k->device_id = PCI_DEVICE_ID_REDHAT_PXB_PCIE; @@ -341,6 +342,7 @@ static void pxb_pcie_dev_class_init(ObjectClass *klass, void *data) dc->desc = "PCI Express Expander Bridge"; dc->props = pxb_dev_properties; + dc->hotpluggable = false; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } diff --git a/hw/pci-bridge/xio3130_downstream.c b/hw/pci-bridge/xio3130_downstream.c index cf1ee63ab..cef6e1325 100644 --- a/hw/pci-bridge/xio3130_downstream.c +++ b/hw/pci-bridge/xio3130_downstream.c @@ -24,6 +24,7 @@ #include "hw/pci/msi.h" #include "hw/pci/pcie.h" #include "xio3130_downstream.h" +#include "qapi/error.h" #define PCI_DEVICE_ID_TI_XIO3130D 0x8233 /* downstream port */ #define XIO3130_REVISION 0x1 @@ -60,21 +61,26 @@ static int xio3130_downstream_initfn(PCIDevice *d) PCIEPort *p = PCIE_PORT(d); PCIESlot *s = PCIE_SLOT(d); int rc; + Error *err = NULL; pci_bridge_initfn(d, TYPE_PCIE_BUS); pcie_port_init_reg(d); rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR, XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT, - XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT); + XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT, &err); if (rc < 0) { + assert(rc == -ENOTSUP); + error_report_err(err); goto err_bridge; } + rc = pci_bridge_ssvid_init(d, XIO3130_SSVID_OFFSET, XIO3130_SSVID_SVID, XIO3130_SSVID_SSID); if (rc < 0) { goto err_bridge; } + rc = pcie_cap_init(d, XIO3130_EXP_OFFSET, PCI_EXP_TYPE_DOWNSTREAM, p->port); if (rc < 0) { @@ -83,12 +89,14 @@ static int xio3130_downstream_initfn(PCIDevice *d) pcie_cap_flr_init(d); pcie_cap_deverr_init(d); pcie_cap_slot_init(d, s->slot); + pcie_cap_arifwd_init(d); + pcie_chassis_create(s->chassis); rc = pcie_chassis_add_slot(s); if (rc < 0) { goto err_pcie_cap; } - pcie_cap_arifwd_init(d); + rc = pcie_aer_init(d, XIO3130_AER_OFFSET, PCI_ERR_SIZEOF); if (rc < 0) { goto err; @@ -195,12 +203,3 @@ static void xio3130_downstream_register_types(void) } type_init(xio3130_downstream_register_types) - -/* - * Local variables: - * c-indent-level: 4 - * c-basic-offset: 4 - * tab-width: 8 - * indent-tab-mode: nil - * End: - */ diff --git a/hw/pci-bridge/xio3130_upstream.c b/hw/pci-bridge/xio3130_upstream.c index 164ef58c4..4ad0440aa 100644 --- a/hw/pci-bridge/xio3130_upstream.c +++ b/hw/pci-bridge/xio3130_upstream.c @@ -24,6 +24,7 @@ #include "hw/pci/msi.h" #include "hw/pci/pcie.h" #include "xio3130_upstream.h" +#include "qapi/error.h" #define PCI_DEVICE_ID_TI_XIO3130U 0x8232 /* upstream port */ #define XIO3130_REVISION 0x2 @@ -56,21 +57,26 @@ static int xio3130_upstream_initfn(PCIDevice *d) { PCIEPort *p = PCIE_PORT(d); int rc; + Error *err = NULL; pci_bridge_initfn(d, TYPE_PCIE_BUS); pcie_port_init_reg(d); rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR, XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT, - XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT); + XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT, &err); if (rc < 0) { + assert(rc == -ENOTSUP); + error_report_err(err); goto err_bridge; } + rc = pci_bridge_ssvid_init(d, XIO3130_SSVID_OFFSET, XIO3130_SSVID_SVID, XIO3130_SSVID_SSID); if (rc < 0) { goto err_bridge; } + rc = pcie_cap_init(d, XIO3130_EXP_OFFSET, PCI_EXP_TYPE_UPSTREAM, p->port); if (rc < 0) { @@ -78,6 +84,7 @@ static int xio3130_upstream_initfn(PCIDevice *d) } pcie_cap_flr_init(d); pcie_cap_deverr_init(d); + rc = pcie_aer_init(d, XIO3130_AER_OFFSET, PCI_ERR_SIZEOF); if (rc < 0) { goto err; @@ -167,13 +174,3 @@ static void xio3130_upstream_register_types(void) } type_init(xio3130_upstream_register_types) - - -/* - * Local variables: - * c-indent-level: 4 - * c-basic-offset: 4 - * tab-width: 8 - * indent-tab-mode: nil - * End: - */ diff --git a/hw/pci-bridge/xio3130_upstream.h b/hw/pci-bridge/xio3130_upstream.h index 08c1d5f75..d0ab7577e 100644 --- a/hw/pci-bridge/xio3130_upstream.h +++ b/hw/pci-bridge/xio3130_upstream.h @@ -7,4 +7,4 @@ PCIEPort *xio3130_upstream_init(PCIBus *bus, int devfn, bool multifunction, const char *bus_name, pci_map_irq_fn map_irq, uint8_t port); -#endif /* QEMU_XIO3130_H */ +#endif /* QEMU_XIO3130_UPSTREAM_H */ diff --git a/hw/pci-host/apb.c b/hw/pci-host/apb.c index aaef7bb3a..653e71112 100644 --- a/hw/pci-host/apb.c +++ b/hw/pci-host/apb.c @@ -36,6 +36,7 @@ #include "hw/pci-host/apb.h" #include "sysemu/sysemu.h" #include "exec/address-spaces.h" +#include "qemu/log.h" /* debug APB */ //#define DEBUG_APB @@ -633,7 +634,7 @@ static void pci_apb_set_irq(void *opaque, int irq_num, int level) } } -static int apb_pci_bridge_initfn(PCIDevice *dev) +static void apb_pci_bridge_realize(PCIDevice *dev, Error **errp) { pci_bridge_initfn(dev, TYPE_PCI_BUS); @@ -651,7 +652,6 @@ static int apb_pci_bridge_initfn(PCIDevice *dev) pci_set_word(dev->config + PCI_STATUS, PCI_STATUS_FAST_BACK | PCI_STATUS_66MHZ | PCI_STATUS_DEVSEL_MEDIUM); - return 0; } PCIBus *pci_apb_init(hwaddr special_base, @@ -669,6 +669,13 @@ PCIBus *pci_apb_init(hwaddr special_base, /* Ultrasparc PBM main bus */ dev = qdev_create(NULL, TYPE_APB); + d = APB_DEVICE(dev); + phb = PCI_HOST_BRIDGE(dev); + phb->bus = pci_register_bus(DEVICE(phb), "pci", + pci_apb_set_irq, pci_pbm_map_irq, d, + &d->pci_mmio, + get_system_io(), + 0, 32, TYPE_PCI_BUS); qdev_init_nofail(dev); s = SYS_BUS_DEVICE(dev); /* apb_config */ @@ -677,18 +684,10 @@ PCIBus *pci_apb_init(hwaddr special_base, sysbus_mmio_map(s, 1, special_base + 0x1000000ULL); /* pci_ioport */ sysbus_mmio_map(s, 2, special_base + 0x2000000ULL); - d = APB_DEVICE(dev); memory_region_init(&d->pci_mmio, OBJECT(s), "pci-mmio", 0x100000000ULL); memory_region_add_subregion(get_system_memory(), mem_base, &d->pci_mmio); - phb = PCI_HOST_BRIDGE(dev); - phb->bus = pci_register_bus(DEVICE(phb), "pci", - pci_apb_set_irq, pci_pbm_map_irq, d, - &d->pci_mmio, - get_system_io(), - 0, 32, TYPE_PCI_BUS); - *pbm_irqs = d->pbm_irqs; d->ivec_irqs = ivec_irqs; @@ -843,7 +842,7 @@ static void pbm_pci_bridge_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = apb_pci_bridge_initfn; + k->realize = apb_pci_bridge_realize; k->exit = pci_bridge_exitfn; k->vendor_id = PCI_VENDOR_ID_SUN; k->device_id = PCI_DEVICE_ID_SUN_SIMBA; diff --git a/hw/pci-host/grackle.c b/hw/pci-host/grackle.c index 8f9121615..2c8acdaac 100644 --- a/hw/pci-host/grackle.c +++ b/hw/pci-host/grackle.c @@ -72,7 +72,6 @@ PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic, GrackleState *d; dev = qdev_create(NULL, TYPE_GRACKLE_PCI_HOST_BRIDGE); - qdev_init_nofail(dev); s = SYS_BUS_DEVICE(dev); phb = PCI_HOST_BRIDGE(dev); d = GRACKLE_PCI_HOST_BRIDGE(dev); @@ -92,6 +91,7 @@ PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic, 0, 4, TYPE_PCI_BUS); pci_create_simple(phb->bus, 0, "grackle"); + qdev_init_nofail(dev); sysbus_mmio_map(s, 0, base); sysbus_mmio_map(s, 1, base + 0x00200000); diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c index df2b0e26f..f9218aa95 100644 --- a/hw/pci-host/piix.c +++ b/hw/pci-host/piix.c @@ -48,7 +48,7 @@ typedef struct I440FXState { PCIHostState parent_obj; - PcPciInfo pci_info; + Range pci_hole; uint64_t pci_hole64_size; uint32_t short_root_bus; } I440FXState; @@ -221,8 +221,12 @@ static void i440fx_pcihost_get_pci_hole_start(Object *obj, Visitor *v, Error **errp) { I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj); - uint32_t value = s->pci_info.w32.begin; + uint64_t val64; + uint32_t value; + val64 = range_is_empty(&s->pci_hole) ? 0 : range_lob(&s->pci_hole); + value = val64; + assert(value == val64); visit_type_uint32(v, name, &value, errp); } @@ -231,8 +235,12 @@ static void i440fx_pcihost_get_pci_hole_end(Object *obj, Visitor *v, Error **errp) { I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj); - uint32_t value = s->pci_info.w32.end; + uint64_t val64; + uint32_t value; + val64 = range_is_empty(&s->pci_hole) ? 0 : range_upb(&s->pci_hole) + 1; + value = val64; + assert(value == val64); visit_type_uint32(v, name, &value, errp); } @@ -242,10 +250,11 @@ static void i440fx_pcihost_get_pci_hole64_start(Object *obj, Visitor *v, { PCIHostState *h = PCI_HOST_BRIDGE(obj); Range w64; + uint64_t value; pci_bus_get_w64_range(h->bus, &w64); - - visit_type_uint64(v, name, &w64.begin, errp); + value = range_is_empty(&w64) ? 0 : range_lob(&w64); + visit_type_uint64(v, name, &value, errp); } static void i440fx_pcihost_get_pci_hole64_end(Object *obj, Visitor *v, @@ -254,16 +263,16 @@ static void i440fx_pcihost_get_pci_hole64_end(Object *obj, Visitor *v, { PCIHostState *h = PCI_HOST_BRIDGE(obj); Range w64; + uint64_t value; pci_bus_get_w64_range(h->bus, &w64); - - visit_type_uint64(v, name, &w64.end, errp); + value = range_is_empty(&w64) ? 0 : range_upb(&w64) + 1; + visit_type_uint64(v, name, &value, errp); } static void i440fx_pcihost_initfn(Object *obj) { PCIHostState *s = PCI_HOST_BRIDGE(obj); - I440FXState *d = I440FX_PCI_HOST_BRIDGE(obj); memory_region_init_io(&s->conf_mem, obj, &pci_host_conf_le_ops, s, "pci-conf-idx", 4); @@ -285,8 +294,6 @@ static void i440fx_pcihost_initfn(Object *obj) object_property_add(obj, PCI_HOST_PROP_PCI_HOLE64_END, "int", i440fx_pcihost_get_pci_hole64_end, NULL, NULL, NULL, NULL); - - d->pci_info.w32.end = IO_APIC_DEFAULT_ADDRESS; } static void i440fx_pcihost_realize(DeviceState *dev, Error **errp) @@ -347,7 +354,8 @@ PCIBus *i440fx_init(const char *host_type, const char *pci_type, f->ram_memory = ram_memory; i440fx = I440FX_PCI_HOST_BRIDGE(dev); - i440fx->pci_info.w32.begin = below_4g_mem_size; + range_set_bounds(&i440fx->pci_hole, below_4g_mem_size, + IO_APIC_DEFAULT_ADDRESS - 1); /* setup pci memory mapping */ pc_pci_as_mapping_init(OBJECT(f), f->system_memory, @@ -865,6 +873,8 @@ static void i440fx_pcihost_class_init(ObjectClass *klass, void *data) dc->realize = i440fx_pcihost_realize; dc->fw_name = "pci"; dc->props = i440fx_props; + /* Reason: needs to be wired up by pc_init1 */ + dc->cannot_instantiate_with_device_add_yet = true; } static const TypeInfo i440fx_pcihost_info = { diff --git a/hw/pci-host/prep.c b/hw/pci-host/prep.c index 487e32ecb..5580293f9 100644 --- a/hw/pci-host/prep.c +++ b/hw/pci-host/prep.c @@ -247,6 +247,7 @@ static void raven_pcihost_realizefn(DeviceState *d, Error **errp) memory_region_add_subregion(address_space_mem, 0xbffffff0, &s->pci_intack); /* TODO Remove once realize propagates to child devices. */ + object_property_set_bool(OBJECT(&s->pci_bus), true, "realized", errp); object_property_set_bool(OBJECT(&s->pci_dev), true, "realized", errp); } diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index 70f897e3a..344f77b10 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -52,6 +52,7 @@ static void q35_host_realize(DeviceState *dev, Error **errp) pci->bus = pci_bus_new(DEVICE(s), "pcie.0", s->mch.pci_address_space, s->mch.address_space_io, 0, TYPE_PCIE_BUS); + PC_MACHINE(qdev_get_machine())->bus = pci->bus; qdev_set_parent_bus(DEVICE(&s->mch), BUS(pci->bus)); qdev_init_nofail(DEVICE(&s->mch)); } @@ -73,8 +74,13 @@ static void q35_host_get_pci_hole_start(Object *obj, Visitor *v, Error **errp) { Q35PCIHost *s = Q35_HOST_DEVICE(obj); - uint32_t value = s->mch.pci_info.w32.begin; + uint64_t val64; + uint32_t value; + val64 = range_is_empty(&s->mch.pci_hole) + ? 0 : range_lob(&s->mch.pci_hole); + value = val64; + assert(value == val64); visit_type_uint32(v, name, &value, errp); } @@ -83,8 +89,13 @@ static void q35_host_get_pci_hole_end(Object *obj, Visitor *v, Error **errp) { Q35PCIHost *s = Q35_HOST_DEVICE(obj); - uint32_t value = s->mch.pci_info.w32.end; + uint64_t val64; + uint32_t value; + val64 = range_is_empty(&s->mch.pci_hole) + ? 0 : range_upb(&s->mch.pci_hole) + 1; + value = val64; + assert(value == val64); visit_type_uint32(v, name, &value, errp); } @@ -94,10 +105,11 @@ static void q35_host_get_pci_hole64_start(Object *obj, Visitor *v, { PCIHostState *h = PCI_HOST_BRIDGE(obj); Range w64; + uint64_t value; pci_bus_get_w64_range(h->bus, &w64); - - visit_type_uint64(v, name, &w64.begin, errp); + value = range_is_empty(&w64) ? 0 : range_lob(&w64); + visit_type_uint64(v, name, &value, errp); } static void q35_host_get_pci_hole64_end(Object *obj, Visitor *v, @@ -106,10 +118,11 @@ static void q35_host_get_pci_hole64_end(Object *obj, Visitor *v, { PCIHostState *h = PCI_HOST_BRIDGE(obj); Range w64; + uint64_t value; pci_bus_get_w64_range(h->bus, &w64); - - visit_type_uint64(v, name, &w64.end, errp); + value = range_is_empty(&w64) ? 0 : range_upb(&w64) + 1; + visit_type_uint64(v, name, &value, errp); } static void q35_host_get_mmcfg_size(Object *obj, Visitor *v, const char *name, @@ -127,6 +140,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(), }; @@ -138,6 +155,8 @@ static void q35_host_class_init(ObjectClass *klass, void *data) hc->root_bus_path = q35_host_root_bus_path; dc->realize = q35_host_realize; dc->props = mch_props; + /* Reason: needs to be wired up by pc_q35_init */ + dc->cannot_instantiate_with_device_add_yet = true; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->fw_name = "pci"; } @@ -177,14 +196,30 @@ 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 * can't cover it exactly. */ - s->mch.pci_info.w32.begin = MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT + - MCH_HOST_BRIDGE_PCIEXBAR_MAX; - s->mch.pci_info.w32.end = IO_APIC_DEFAULT_ADDRESS; + range_set_bounds(&s->mch.pci_hole, + MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT + MCH_HOST_BRIDGE_PCIEXBAR_MAX, + IO_APIC_DEFAULT_ADDRESS - 1); } static const TypeInfo q35_host_info = { @@ -252,10 +287,7 @@ static void mch_update_pciexbar(MCHPCIState *mch) break; case MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_RVD: default: - enable = 0; - length = 0; abort(); - break; } addr = pciexbar & addr_mask; pcie_host_mmcfg_update(pehb, enable, addr, length); @@ -265,9 +297,13 @@ static void mch_update_pciexbar(MCHPCIState *mch) * which means an MTRR can't cover it exactly. */ if (enable) { - mch->pci_info.w32.begin = addr + length; + range_set_bounds(&mch->pci_hole, + addr + length, + IO_APIC_DEFAULT_ADDRESS - 1); } else { - mch->pci_info.w32.begin = MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT; + range_set_bounds(&mch->pci_hole, + MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT, + IO_APIC_DEFAULT_ADDRESS - 1); } } @@ -424,30 +460,6 @@ static void mch_reset(DeviceState *qdev) mch_update(mch); } -static AddressSpace *q35_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) -{ - IntelIOMMUState *s = opaque; - VTDAddressSpace *vtd_as; - - assert(0 <= devfn && devfn <= VTD_PCI_DEVFN_MAX); - - vtd_as = vtd_find_add_as(s, bus, devfn); - return &vtd_as->as; -} - -static void mch_init_dmar(MCHPCIState *mch) -{ - PCIBus *pci_bus = PCI_BUS(qdev_get_parent_bus(DEVICE(mch))); - - mch->iommu = INTEL_IOMMU_DEVICE(qdev_create(NULL, TYPE_INTEL_IOMMU_DEVICE)); - object_property_add_child(OBJECT(mch), "intel-iommu", - OBJECT(mch->iommu), NULL); - qdev_init_nofail(DEVICE(mch->iommu)); - sysbus_mmio_map(SYS_BUS_DEVICE(mch->iommu), 0, Q35_HOST_BRIDGE_IOMMU_ADDR); - - pci_setup_iommu(pci_bus, q35_host_dma_iommu, mch->iommu); -} - static void mch_realize(PCIDevice *d, Error **errp) { int i; @@ -506,10 +518,6 @@ static void mch_realize(PCIDevice *d, Error **errp) mch->pci_address_space, &mch->pam_regions[i+1], PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE, PAM_EXPAN_SIZE); } - /* Intel IOMMU (VT-d) */ - if (object_property_get_bool(qdev_get_machine(), "iommu", NULL)) { - mch_init_dmar(mch); - } } uint64_t mch_mcfg_base(void) diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index 15b105423..7aac4d67a 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -62,12 +62,9 @@ typedef struct UNINState { static int pci_unin_map_irq(PCIDevice *pci_dev, int irq_num) { - int retval; int devfn = pci_dev->devfn & 0x00FFFFFF; - retval = (((devfn >> 11) & 0x1F) + irq_num) & 3; - - return retval; + return (((devfn >> 11) & 0x1F) + irq_num) & 3; } static void pci_unin_set_irq(void *opaque, int irq_num, int level) diff --git a/hw/pci-host/versatile.c b/hw/pci-host/versatile.c index 339ec2c50..467cbb9cb 100644 --- a/hw/pci-host/versatile.c +++ b/hw/pci-host/versatile.c @@ -13,6 +13,7 @@ #include "hw/pci/pci_bus.h" #include "hw/pci/pci_host.h" #include "exec/address-spaces.h" +#include "qemu/log.h" /* Old and buggy versions of QEMU used the wrong mapping from * PCI IRQs to system interrupt lines. Unfortunately the Linux @@ -454,6 +455,7 @@ static void pci_vpb_realize(DeviceState *dev, Error **errp) } /* TODO Remove once realize propagates to child devices. */ + object_property_set_bool(OBJECT(&s->pci_bus), true, "realized", errp); object_property_set_bool(OBJECT(&s->pci_dev), true, "realized", errp); } diff --git a/hw/pci/msi.c b/hw/pci/msi.c index e0e64c2d9..a87b2278a 100644 --- a/hw/pci/msi.c +++ b/hw/pci/msi.c @@ -22,6 +22,7 @@ #include "hw/pci/msi.h" #include "hw/xen/xen.h" #include "qemu/range.h" +#include "qapi/error.h" /* PCI_MSI_ADDRESS_LO */ #define PCI_MSI_ADDRESS_LO_MASK (~0x3) @@ -40,7 +41,7 @@ * * Setting this flag to false will remove MSI/MSI-X capability from all devices. * - * It is preferrable for controllers to set this to true (non-broken) even if + * It is preferable for controllers to set this to true (non-broken) even if * they do not actually support MSI/MSI-X: guests normally probe the controller * type and do not attempt to enable MSI/MSI-X with interrupt controllers not * supporting such, so removing the capability is not required, and @@ -165,8 +166,25 @@ bool msi_enabled(const PCIDevice *dev) PCI_MSI_FLAGS_ENABLE); } +/* + * Make PCI device @dev MSI-capable. + * Non-zero @offset puts capability MSI at that offset in PCI config + * space. + * @nr_vectors is the number of MSI vectors (1, 2, 4, 8, 16 or 32). + * If @msi64bit, make the device capable of sending a 64-bit message + * address. + * If @msi_per_vector_mask, make the device support per-vector masking. + * @errp is for returning errors. + * Return 0 on success; set @errp and return -errno on error. + * + * -ENOTSUP means lacking msi support for a msi-capable platform. + * -EINVAL means capability overlap, happens when @offset is non-zero, + * also means a programming error, except device assignment, which can check + * if a real HW is broken. + */ int msi_init(struct PCIDevice *dev, uint8_t offset, - unsigned int nr_vectors, bool msi64bit, bool msi_per_vector_mask) + unsigned int nr_vectors, bool msi64bit, + bool msi_per_vector_mask, Error **errp) { unsigned int vectors_order; uint16_t flags; @@ -174,6 +192,7 @@ int msi_init(struct PCIDevice *dev, uint8_t offset, int config_offset; if (!msi_nonbroken) { + error_setg(errp, "MSI is not supported by interrupt controller"); return -ENOTSUP; } @@ -197,7 +216,8 @@ int msi_init(struct PCIDevice *dev, uint8_t offset, } cap_size = msi_cap_sizeof(flags); - config_offset = pci_add_capability(dev, PCI_CAP_ID_MSI, offset, cap_size); + config_offset = pci_add_capability2(dev, PCI_CAP_ID_MSI, offset, + cap_size, errp); if (config_offset < 0) { return config_offset; } @@ -220,7 +240,8 @@ int msi_init(struct PCIDevice *dev, uint8_t offset, pci_set_long(dev->wmask + msi_mask_off(dev, msi64bit), 0xffffffff >> (PCI_MSI_VECTORS_MAX - nr_vectors)); } - return config_offset; + + return 0; } void msi_uninit(struct PCIDevice *dev) diff --git a/hw/pci/msix.c b/hw/pci/msix.c index b75f0e9c4..0ec1cb14f 100644 --- a/hw/pci/msix.c +++ b/hw/pci/msix.c @@ -72,7 +72,7 @@ void msix_set_pending(PCIDevice *dev, unsigned int vector) *msix_pending_byte(dev, vector) |= msix_pending_mask(vector); } -static void msix_clr_pending(PCIDevice *dev, int vector) +void msix_clr_pending(PCIDevice *dev, int vector) { *msix_pending_byte(dev, vector) &= ~msix_pending_mask(vector); } diff --git a/hw/pci/pci.c b/hw/pci/pci.c index bb605efae..24fae1689 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -62,6 +62,8 @@ static Property pci_props[] = { QEMU_PCI_CAP_MULTIFUNCTION_BITNR, false), DEFINE_PROP_BIT("command_serr_enable", PCIDevice, cap_present, QEMU_PCI_CAP_SERR_BITNR, true), + DEFINE_PROP_BIT("x-pcie-lnksta-dllla", PCIDevice, cap_present, + QEMU_PCIE_LNKSTA_DLLLA_BITNR, true), DEFINE_PROP_END_OF_LIST() }; @@ -78,10 +80,37 @@ static const VMStateDescription vmstate_pcibus = { } }; +static void pci_init_bus_master(PCIDevice *pci_dev) +{ + AddressSpace *dma_as = pci_device_iommu_address_space(pci_dev); + + memory_region_init_alias(&pci_dev->bus_master_enable_region, + OBJECT(pci_dev), "bus master", + dma_as->root, 0, memory_region_size(dma_as->root)); + memory_region_set_enabled(&pci_dev->bus_master_enable_region, false); + address_space_init(&pci_dev->bus_master_as, + &pci_dev->bus_master_enable_region, pci_dev->name); +} + +static void pcibus_machine_done(Notifier *notifier, void *data) +{ + PCIBus *bus = container_of(notifier, PCIBus, machine_done); + int i; + + for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) { + if (bus->devices[i]) { + pci_init_bus_master(bus->devices[i]); + } + } +} + static void pci_bus_realize(BusState *qbus, Error **errp) { PCIBus *bus = PCI_BUS(qbus); + bus->machine_done.notify = pcibus_machine_done; + qemu_add_machine_init_done_notifier(&bus->machine_done); + vmstate_register(NULL, -1, &vmstate_pcibus, bus); } @@ -89,6 +118,8 @@ static void pci_bus_unrealize(BusState *qbus, Error **errp) { PCIBus *bus = PCI_BUS(qbus); + qemu_remove_machine_init_done_notifier(&bus->machine_done); + vmstate_unregister(NULL, &vmstate_pcibus, bus); } @@ -836,6 +867,81 @@ static void do_pci_unregister_device(PCIDevice *pci_dev) address_space_destroy(&pci_dev->bus_master_as); } +/* Extract PCIReqIDCache into BDF format */ +static uint16_t pci_req_id_cache_extract(PCIReqIDCache *cache) +{ + uint8_t bus_n; + uint16_t result; + + switch (cache->type) { + case PCI_REQ_ID_BDF: + result = pci_get_bdf(cache->dev); + break; + case PCI_REQ_ID_SECONDARY_BUS: + bus_n = pci_bus_num(cache->dev->bus); + result = PCI_BUILD_BDF(bus_n, 0); + break; + default: + error_printf("Invalid PCI requester ID cache type: %d\n", + cache->type); + exit(1); + break; + } + + return result; +} + +/* Parse bridges up to the root complex and return requester ID + * cache for specific device. For full PCIe topology, the cache + * result would be exactly the same as getting BDF of the device. + * However, several tricks are required when system mixed up with + * legacy PCI devices and PCIe-to-PCI bridges. + * + * Here we cache the proxy device (and type) not requester ID since + * bus number might change from time to time. + */ +static PCIReqIDCache pci_req_id_cache_get(PCIDevice *dev) +{ + PCIDevice *parent; + PCIReqIDCache cache = { + .dev = dev, + .type = PCI_REQ_ID_BDF, + }; + + while (!pci_bus_is_root(dev->bus)) { + /* We are under PCI/PCIe bridges */ + parent = dev->bus->parent_dev; + if (pci_is_express(parent)) { + if (pcie_cap_get_type(parent) == PCI_EXP_TYPE_PCI_BRIDGE) { + /* When we pass through PCIe-to-PCI/PCIX bridges, we + * override the requester ID using secondary bus + * number of parent bridge with zeroed devfn + * (pcie-to-pci bridge spec chap 2.3). */ + cache.type = PCI_REQ_ID_SECONDARY_BUS; + cache.dev = dev; + } + } else { + /* Legacy PCI, override requester ID with the bridge's + * BDF upstream. When the root complex connects to + * legacy PCI devices (including buses), it can only + * obtain requester ID info from directly attached + * devices. If devices are attached under bridges, only + * the requester ID of the bridge that is directly + * attached to the root complex can be recognized. */ + cache.type = PCI_REQ_ID_BDF; + cache.dev = parent; + } + dev = parent; + } + + return cache; +} + +uint16_t pci_requester_id(PCIDevice *dev) +{ + return pci_req_id_cache_extract(&dev->requester_id_cache); +} + /* -1 for devfn means auto assign */ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, const char *name, int devfn, @@ -845,7 +951,6 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, PCIConfigReadFunc *config_read = pc->config_read; PCIConfigWriteFunc *config_write = pc->config_write; Error *local_err = NULL; - AddressSpace *dma_as; DeviceState *dev = DEVICE(pci_dev); pci_dev->bus = bus; @@ -885,15 +990,11 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, } pci_dev->devfn = devfn; - dma_as = pci_device_iommu_address_space(pci_dev); - - memory_region_init_alias(&pci_dev->bus_master_enable_region, - OBJECT(pci_dev), "bus master", - dma_as->root, 0, memory_region_size(dma_as->root)); - memory_region_set_enabled(&pci_dev->bus_master_enable_region, false); - address_space_init(&pci_dev->bus_master_as, &pci_dev->bus_master_enable_region, - name); + pci_dev->requester_id_cache = pci_req_id_cache_get(pci_dev); + if (qdev_hotplug) { + pci_init_bus_master(pci_dev); + } pstrcpy(pci_dev->name, sizeof(pci_dev->name), name); pci_dev->irq_state = 0; pci_config_alloc(pci_dev); @@ -975,7 +1076,7 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, uint8_t type, MemoryRegion *memory) { PCIIORegion *r; - uint32_t addr; + uint32_t addr; /* offset in pci config space */ uint64_t wmask; pcibus_t size = memory_region_size(memory); @@ -991,15 +1092,20 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, r->addr = PCI_BAR_UNMAPPED; r->size = size; r->type = type; - r->memory = NULL; + r->memory = memory; + r->address_space = type & PCI_BASE_ADDRESS_SPACE_IO + ? pci_dev->bus->address_space_io + : pci_dev->bus->address_space_mem; wmask = ~(size - 1); - addr = pci_bar(pci_dev, region_num); if (region_num == PCI_ROM_SLOT) { /* ROM enable bit is writable */ wmask |= PCI_ROM_ADDRESS_ENABLE; } + + addr = pci_bar(pci_dev, region_num); pci_set_long(pci_dev->config + addr, type); + if (!(r->type & PCI_BASE_ADDRESS_SPACE_IO) && r->type & PCI_BASE_ADDRESS_MEM_TYPE_64) { pci_set_quad(pci_dev->wmask + addr, wmask); @@ -1008,11 +1114,6 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, pci_set_long(pci_dev->wmask + addr, wmask & 0xffffffff); pci_set_long(pci_dev->cmask + addr, 0xffffffff); } - pci_dev->io_regions[region_num].memory = memory; - pci_dev->io_regions[region_num].address_space - = type & PCI_BASE_ADDRESS_SPACE_IO - ? pci_dev->bus->address_space_io - : pci_dev->bus->address_space_mem; } static void pci_update_vga(PCIDevice *pci_dev) @@ -2152,10 +2253,8 @@ int pci_add_capability2(PCIDevice *pdev, uint8_t cap_id, if (!offset) { offset = pci_find_space(pdev, size); - if (!offset) { - error_setg(errp, "out of PCI config space"); - return -ENOSPC; - } + /* out of PCI config space is programming error */ + assert(offset); } else { /* Verify that capabilities don't overlap. Note: device assignment * depends on this check to verify that the device is not broken. @@ -2436,13 +2535,13 @@ static void pci_dev_get_w64(PCIBus *b, PCIDevice *dev, void *opaque) if (limit >= base) { Range pref_range; - pref_range.begin = base; - pref_range.end = limit + 1; + range_set_bounds(&pref_range, base, limit); range_extend(range, &pref_range); } } for (i = 0; i < PCI_NUM_REGIONS; ++i) { PCIIORegion *r = &dev->io_regions[i]; + pcibus_t lob, upb; Range region_range; if (!r->size || @@ -2450,16 +2549,17 @@ static void pci_dev_get_w64(PCIBus *b, PCIDevice *dev, void *opaque) !(r->type & PCI_BASE_ADDRESS_MEM_TYPE_64)) { continue; } - region_range.begin = pci_bar_address(dev, i, r->type, r->size); - region_range.end = region_range.begin + r->size; - if (region_range.begin == PCI_BAR_UNMAPPED) { + lob = pci_bar_address(dev, i, r->type, r->size); + upb = lob + r->size - 1; + if (lob == PCI_BAR_UNMAPPED) { continue; } - region_range.begin = MAX(region_range.begin, 0x1ULL << 32); + lob = MAX(lob, 0x1ULL << 32); - if (region_range.end - 1 >= region_range.begin) { + if (upb >= lob) { + range_set_bounds(®ion_range, lob, upb); range_extend(range, ®ion_range); } } @@ -2467,7 +2567,7 @@ static void pci_dev_get_w64(PCIBus *b, PCIDevice *dev, void *opaque) void pci_bus_get_w64_range(PCIBus *bus, Range *range) { - range->begin = range->end = 0; + range_make_empty(range); pci_for_each_device_under_bus(bus, pci_dev_get_w64, range); } @@ -2498,6 +2598,21 @@ PCIDevice *pci_get_function_0(PCIDevice *pci_dev) } } +MSIMessage pci_get_msi_message(PCIDevice *dev, int vector) +{ + MSIMessage msg; + if (msix_enabled(dev)) { + msg = msix_get_message(dev, vector); + } else if (msi_enabled(dev)) { + msg = msi_get_message(dev, vector); + } else { + /* Should never happen */ + error_report("%s: unknown interrupt type", __func__); + abort(); + } + return msg; +} + static const TypeInfo pci_device_type_info = { .name = TYPE_PCI_DEVICE, .parent = TYPE_DEVICE, diff --git a/hw/pci/pci_bridge.c b/hw/pci/pci_bridge.c index 3cf30bd33..5118ef404 100644 --- a/hw/pci/pci_bridge.c +++ b/hw/pci/pci_bridge.c @@ -116,7 +116,7 @@ pcibus_t pci_bridge_get_base(const PCIDevice *bridge, uint8_t type) return base; } -/* accessor funciton to get bridge filtering limit */ +/* accessor function to get bridge filtering limit */ pcibus_t pci_bridge_get_limit(const PCIDevice *bridge, uint8_t type) { pcibus_t limit; diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index 728386ada..99cfb4561 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -43,26 +43,18 @@ /*************************************************************************** * pci express capability helper functions */ -int pcie_cap_init(PCIDevice *dev, uint8_t offset, uint8_t type, uint8_t port) -{ - int pos; - uint8_t *exp_cap; - assert(pci_is_express(dev)); - - pos = pci_add_capability(dev, PCI_CAP_ID_EXP, offset, - PCI_EXP_VER2_SIZEOF); - if (pos < 0) { - return pos; - } - dev->exp.exp_cap = pos; - exp_cap = dev->config + pos; +static void +pcie_cap_v1_fill(PCIDevice *dev, uint8_t port, uint8_t type, uint8_t version) +{ + uint8_t *exp_cap = dev->config + dev->exp.exp_cap; + uint8_t *cmask = dev->cmask + dev->exp.exp_cap; /* capability register - interrupt message number defaults to 0 */ + interrupt message number defaults to 0 */ pci_set_word(exp_cap + PCI_EXP_FLAGS, ((type << PCI_EXP_FLAGS_TYPE_SHIFT) & PCI_EXP_FLAGS_TYPE) | - PCI_EXP_FLAGS_VER2); + version); /* device capability register * table 7-12: @@ -80,8 +72,39 @@ int pcie_cap_init(PCIDevice *dev, uint8_t offset, uint8_t type, uint8_t port) PCI_EXP_LNK_LS_25); pci_set_word(exp_cap + PCI_EXP_LNKSTA, - PCI_EXP_LNK_MLW_1 | PCI_EXP_LNK_LS_25 |PCI_EXP_LNKSTA_DLLLA); + PCI_EXP_LNK_MLW_1 | PCI_EXP_LNK_LS_25); + + if (dev->cap_present & QEMU_PCIE_LNKSTA_DLLLA) { + pci_word_test_and_set_mask(exp_cap + PCI_EXP_LNKSTA, + PCI_EXP_LNKSTA_DLLLA); + } + + /* We changed link status bits over time, and changing them across + * migrations is generally fine as hardware changes them too. + * Let's not bother checking. + */ + pci_set_word(cmask + PCI_EXP_LNKSTA, 0); +} + +int pcie_cap_init(PCIDevice *dev, uint8_t offset, uint8_t type, uint8_t port) +{ + /* PCIe cap v2 init */ + int pos; + uint8_t *exp_cap; + + assert(pci_is_express(dev)); + + pos = pci_add_capability(dev, PCI_CAP_ID_EXP, offset, PCI_EXP_VER2_SIZEOF); + if (pos < 0) { + return pos; + } + dev->exp.exp_cap = pos; + exp_cap = dev->config + pos; + /* Filling values common with v1 */ + pcie_cap_v1_fill(dev, port, type, PCI_EXP_FLAGS_VER2); + + /* Filling v2 specific values */ pci_set_long(exp_cap + PCI_EXP_DEVCAP2, PCI_EXP_DEVCAP2_EFF | PCI_EXP_DEVCAP2_EETLPP); @@ -89,7 +112,27 @@ int pcie_cap_init(PCIDevice *dev, uint8_t offset, uint8_t type, uint8_t port) return pos; } -int pcie_endpoint_cap_init(PCIDevice *dev, uint8_t offset) +int pcie_cap_v1_init(PCIDevice *dev, uint8_t offset, uint8_t type, + uint8_t port) +{ + /* PCIe cap v1 init */ + int pos; + + assert(pci_is_express(dev)); + + pos = pci_add_capability(dev, PCI_CAP_ID_EXP, offset, PCI_EXP_VER1_SIZEOF); + if (pos < 0) { + return pos; + } + dev->exp.exp_cap = pos; + + pcie_cap_v1_fill(dev, port, type, PCI_EXP_FLAGS_VER1); + + return pos; +} + +static int +pcie_endpoint_cap_common_init(PCIDevice *dev, uint8_t offset, uint8_t cap_size) { uint8_t type = PCI_EXP_TYPE_ENDPOINT; @@ -102,7 +145,19 @@ int pcie_endpoint_cap_init(PCIDevice *dev, uint8_t offset) type = PCI_EXP_TYPE_RC_END; } - return pcie_cap_init(dev, offset, type, 0); + return (cap_size == PCI_EXP_VER1_SIZEOF) + ? pcie_cap_v1_init(dev, offset, type, 0) + : pcie_cap_init(dev, offset, type, 0); +} + +int pcie_endpoint_cap_init(PCIDevice *dev, uint8_t offset) +{ + return pcie_endpoint_cap_common_init(dev, offset, PCI_EXP_VER2_SIZEOF); +} + +int pcie_endpoint_cap_v1_init(PCIDevice *dev, uint8_t offset) +{ + return pcie_endpoint_cap_common_init(dev, offset, PCI_EXP_VER1_SIZEOF); } void pcie_cap_exit(PCIDevice *dev) @@ -110,6 +165,11 @@ void pcie_cap_exit(PCIDevice *dev) pci_del_capability(dev, PCI_CAP_ID_EXP, PCI_EXP_VER2_SIZEOF); } +void pcie_cap_v1_exit(PCIDevice *dev) +{ + pci_del_capability(dev, PCI_CAP_ID_EXP, PCI_EXP_VER1_SIZEOF); +} + uint8_t pcie_cap_get_type(const PCIDevice *dev) { uint32_t pos = dev->exp.exp_cap; @@ -647,3 +707,13 @@ void pcie_ari_init(PCIDevice *dev, uint16_t offset, uint16_t nextfn) offset, PCI_ARI_SIZEOF); pci_set_long(dev->config + offset + PCI_ARI_CAP, (nextfn & 0xff) << 8); } + +void pcie_dev_ser_num_init(PCIDevice *dev, uint16_t offset, uint64_t ser_num) +{ + static const int pci_dsn_ver = 1; + static const int pci_dsn_cap = 4; + + pcie_add_capability(dev, PCI_EXT_CAP_ID_DSN, pci_dsn_ver, offset, + PCI_EXT_CAP_DSN_SIZEOF); + pci_set_quad(dev->config + offset + pci_dsn_cap, ser_num); +} diff --git a/hw/pci/pcie_aer.c b/hw/pci/pcie_aer.c index e2d4e68ba..048ce6a42 100644 --- a/hw/pci/pcie_aer.c +++ b/hw/pci/pcie_aer.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "sysemu/sysemu.h" #include "qapi/qmp/types.h" +#include "qapi/qmp/qjson.h" #include "monitor/monitor.h" #include "hw/pci/pci_bridge.h" #include "hw/pci/pcie.h" diff --git a/hw/pci/trace-events b/hw/pci/trace-events new file mode 100644 index 000000000..2b9cf2440 --- /dev/null +++ b/hw/pci/trace-events @@ -0,0 +1,9 @@ +# See docs/tracing.txt for syntax documentation. + +# hw/pci/pci.c +pci_update_mappings_del(void *d, uint32_t bus, uint32_t slot, uint32_t func, int bar, uint64_t addr, uint64_t size) "d=%p %02x:%02x.%x %d,%#"PRIx64"+%#"PRIx64 +pci_update_mappings_add(void *d, uint32_t bus, uint32_t slot, uint32_t func, int bar, uint64_t addr, uint64_t size) "d=%p %02x:%02x.%x %d,%#"PRIx64"+%#"PRIx64 + +# hw/pci/pci_host.c +pci_cfg_read(const char *dev, unsigned devid, unsigned fnid, unsigned offs, unsigned val) "%s %02u:%u @0x%x -> 0x%x" +pci_cfg_write(const char *dev, unsigned devid, unsigned fnid, unsigned offs, unsigned val) "%s %02u:%u @0x%x <- 0x%x" diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index c1ffc7771..91a3420f4 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -4,9 +4,11 @@ obj-y += ppc.o ppc_booke.o obj-$(CONFIG_PSERIES) += spapr.o spapr_vio.o spapr_events.o obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o +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 ee1c60b82..0cd534df5 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 ef224ea5e..70ba1d8f4 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/e500plat.c b/hw/ppc/e500plat.c index b00565c3d..94b454551 100644 --- a/hw/ppc/e500plat.c +++ b/hw/ppc/e500plat.c @@ -14,6 +14,7 @@ #include "e500.h" #include "hw/boards.h" #include "sysemu/device_tree.h" +#include "sysemu/kvm.h" #include "hw/pci/pci.h" #include "hw/ppc/openpic.h" #include "kvm_ppc.h" diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index 5764b86c2..20cbddb4e 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -22,8 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#if !defined(__PPC_MAC_H__) -#define __PPC_MAC_H__ + +#ifndef PPC_MAC_H +#define PPC_MAC_H #include "exec/memory.h" #include "hw/sysbus.h" @@ -184,4 +185,4 @@ typedef struct MacIONVRAMState { } MacIONVRAMState; void pmac_format_nvram_partition (MacIONVRAMState *nvr, int len); -#endif /* !defined(__PPC_MAC_H__) */ +#endif /* PPC_MAC_H */ diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 32e88b378..7d2510658 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -380,6 +380,7 @@ static void ppc_core99_init(MachineState *machine) pci_bus = pci_pmac_init(pic, get_system_memory(), get_system_io()); machine_arch = ARCH_MAC99; } + object_property_set_bool(OBJECT(pci_bus), true, "realized", &error_abort); machine->usb |= defaults_enabled() && !machine->usb_disabled; diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index a9bb1c27d..447948746 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -309,7 +309,7 @@ static void ppc_heathrow_init(MachineState *machine) dev = qdev_create(adb_bus, TYPE_ADB_MOUSE); qdev_init_nofail(dev); - if (usb_enabled()) { + if (machine_usb(machine)) { pci_create_simple(pci_bus, -1, "pci-ohci"); } diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index 38ff2e159..894586900 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -33,6 +33,7 @@ #include "hw/timer/m48t59.h" #include "qemu/log.h" #include "qemu/error-report.h" +#include "qapi/error.h" #include "hw/loader.h" #include "sysemu/kvm.h" #include "kvm_ppc.h" @@ -164,9 +165,9 @@ static void ppc6xx_set_irq(void *opaque, int pin, int level) } } -void ppc6xx_irq_init(CPUPPCState *env) +void ppc6xx_irq_init(PowerPCCPU *cpu) { - PowerPCCPU *cpu = ppc_env_get_cpu(env); + CPUPPCState *env = &cpu->env; env->irq_inputs = (void **)qemu_allocate_irqs(&ppc6xx_set_irq, cpu, PPC6xx_INPUT_NB); @@ -251,9 +252,9 @@ static void ppc970_set_irq(void *opaque, int pin, int level) } } -void ppc970_irq_init(CPUPPCState *env) +void ppc970_irq_init(PowerPCCPU *cpu) { - PowerPCCPU *cpu = ppc_env_get_cpu(env); + CPUPPCState *env = &cpu->env; env->irq_inputs = (void **)qemu_allocate_irqs(&ppc970_set_irq, cpu, PPC970_INPUT_NB); @@ -287,9 +288,9 @@ static void power7_set_irq(void *opaque, int pin, int level) } } -void ppcPOWER7_irq_init(CPUPPCState *env) +void ppcPOWER7_irq_init(PowerPCCPU *cpu) { - PowerPCCPU *cpu = ppc_env_get_cpu(env); + CPUPPCState *env = &cpu->env; env->irq_inputs = (void **)qemu_allocate_irqs(&power7_set_irq, cpu, POWER7_INPUT_NB); @@ -372,9 +373,9 @@ static void ppc40x_set_irq(void *opaque, int pin, int level) } } -void ppc40x_irq_init(CPUPPCState *env) +void ppc40x_irq_init(PowerPCCPU *cpu) { - PowerPCCPU *cpu = ppc_env_get_cpu(env); + CPUPPCState *env = &cpu->env; env->irq_inputs = (void **)qemu_allocate_irqs(&ppc40x_set_irq, cpu, PPC40x_INPUT_NB); @@ -436,9 +437,9 @@ static void ppce500_set_irq(void *opaque, int pin, int level) } } -void ppce500_irq_init(CPUPPCState *env) +void ppce500_irq_init(PowerPCCPU *cpu) { - PowerPCCPU *cpu = ppc_env_get_cpu(env); + CPUPPCState *env = &cpu->env; env->irq_inputs = (void **)qemu_allocate_irqs(&ppce500_set_irq, cpu, PPCE500_INPUT_NB); @@ -699,9 +700,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) @@ -880,7 +890,7 @@ static int timebase_post_load(void *opaque, int version_id) host_ns = qemu_clock_get_ns(QEMU_CLOCK_HOST); ns_diff = MAX(0, host_ns - tb_remote->time_of_the_day_ns); migration_duration_ns = MIN(NANOSECONDS_PER_SECOND, ns_diff); - migration_duration_tb = muldiv64(migration_duration_ns, freq, + migration_duration_tb = muldiv64(freq, migration_duration_ns, NANOSECONDS_PER_SECOND); guest_tb = tb_remote->guest_timebase + MIN(0, migration_duration_tb); @@ -928,9 +938,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 { @@ -1343,3 +1351,28 @@ PowerPCCPU *ppc_get_vcpu_by_dt_id(int cpu_dt_id) return NULL; } + +void ppc_cpu_parse_features(const char *cpu_model) +{ + CPUClass *cc; + ObjectClass *oc; + const char *typename; + gchar **model_pieces; + + model_pieces = g_strsplit(cpu_model, ",", 2); + if (!model_pieces[0]) { + error_report("Invalid/empty CPU model name"); + exit(1); + } + + oc = cpu_class_by_name(TYPE_POWERPC_CPU, model_pieces[0]); + if (oc == NULL) { + error_report("Unable to find CPU definition: %s", model_pieces[0]); + exit(1); + } + + typename = object_class_get_name(oc); + cc = CPU_CLASS(oc); + cc->parse_features(typename, model_pieces[1], &error_fatal); + g_strfreev(model_pieces); +} diff --git a/hw/ppc/ppc405.h b/hw/ppc/ppc405.h index 1c5f04fae..c67febca2 100644 --- a/hw/ppc/ppc405.h +++ b/hw/ppc/ppc405.h @@ -22,8 +22,8 @@ * THE SOFTWARE. */ -#if !defined(PPC_405_H) -#define PPC_405_H +#ifndef PPC405_H +#define PPC405_H #include "hw/ppc/ppc4xx.h" @@ -78,4 +78,4 @@ CPUPPCState *ppc_stb025_init (MemoryRegion ram_memories[2], uint32_t sysclk, qemu_irq **picp, ram_addr_t *offsetp); -#endif /* !defined(PPC_405_H) */ +#endif /* PPC405_H */ diff --git a/hw/ppc/ppc4xx_devs.c b/hw/ppc/ppc4xx_devs.c index 7d59018fc..e7f413e49 100644 --- a/hw/ppc/ppc4xx_devs.c +++ b/hw/ppc/ppc4xx_devs.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "cpu.h" #include "hw/hw.h" #include "hw/ppc/ppc.h" #include "hw/ppc/ppc4xx.h" diff --git a/hw/ppc/ppce500_spin.c b/hw/ppc/ppce500_spin.c index 76bd78bfd..22c584eb8 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/prep.c b/hw/ppc/prep.c index 3ffb85e60..054af1e8b 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "cpu.h" #include "hw/hw.h" #include "hw/timer/m48t59.h" #include "hw/i386/pc.h" @@ -648,7 +649,7 @@ static void ppc_prep_init(MachineState *machine) memory_region_add_subregion(sysmem, 0xFEFF0000, xcsr); #endif - if (usb_enabled()) { + if (machine_usb(machine)) { pci_create_simple(pci_bus, -1, "pci-ohci"); } diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index b69995e0d..30d6800ab 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -29,6 +29,7 @@ #include "sysemu/sysemu.h" #include "sysemu/numa.h" #include "hw/hw.h" +#include "qemu/log.h" #include "hw/fw-path-provider.h" #include "elf.h" #include "net/net.h" @@ -65,6 +66,8 @@ #include "hw/compat.h" #include "qemu/cutils.h" +#include "hw/ppc/spapr_cpu_core.h" +#include "qmp-commands.h" #include <libfdt.h> @@ -88,8 +91,6 @@ #define MIN_RMA_SLOF 128UL -#define TIMEBASE_FREQ 512000000ULL - #define PHANDLE_XICP 0x00001111 #define HTAB_SIZE(spapr) (1ULL << ((spapr)->htab_shift)) @@ -115,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 { @@ -131,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, @@ -338,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); @@ -598,12 +603,23 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, int index = ppc_get_vcpu_dt_id(cpu); uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40), 0xffffffff, 0xffffffff}; - uint32_t tbfreq = kvm_enabled() ? kvmppc_get_tbfreq() : TIMEBASE_FREQ; + uint32_t tbfreq = kvm_enabled() ? kvmppc_get_tbfreq() + : SPAPR_TIMEBASE_FREQ; uint32_t cpufreq = kvm_enabled() ? kvmppc_get_clockfreq() : 1000000000; uint32_t page_sizes_prop[64]; size_t page_sizes_prop_size; uint32_t vcpus_per_socket = smp_threads * smp_cores; uint32_t pft_size_prop[] = {0, cpu_to_be32(spapr->htab_shift)}; + sPAPRDRConnector *drc; + sPAPRDRConnectorClass *drck; + int drc_index; + + drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_CPU, index); + if (drc) { + drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + drc_index = drck->get_index(drc); + _FDT((fdt_setprop_cell(fdt, offset, "ibm,my-drc-index", drc_index))); + } /* Note: we keep CI large pages off for now because a 64K capable guest * provisioned with large pages might otherwise try to map a qemu @@ -761,14 +777,17 @@ static int spapr_populate_drconf_memory(sPAPRMachineState *spapr, void *fdt) int ret, i, offset; uint64_t lmb_size = SPAPR_MEMORY_BLOCK_SIZE; uint32_t prop_lmb_size[] = {0, cpu_to_be32(lmb_size)}; - uint32_t nr_lmbs = (machine->maxram_size - machine->ram_size)/lmb_size; + uint32_t hotplug_lmb_start = spapr->hotplug_memory.base / lmb_size; + uint32_t nr_lmbs = (spapr->hotplug_memory.base + + memory_region_size(&spapr->hotplug_memory.mr)) / + lmb_size; uint32_t *int_buf, *cur_index, buf_len; int nr_nodes = nb_numa_nodes ? nb_numa_nodes : 1; /* - * Don't create the node if there are no DR LMBs. + * Don't create the node if there is no hotpluggable memory */ - if (!nr_lmbs) { + if (machine->ram_size == machine->maxram_size) { return 0; } @@ -802,26 +821,40 @@ static int spapr_populate_drconf_memory(sPAPRMachineState *spapr, void *fdt) int_buf[0] = cpu_to_be32(nr_lmbs); cur_index++; for (i = 0; i < nr_lmbs; i++) { - sPAPRDRConnector *drc; - sPAPRDRConnectorClass *drck; - uint64_t addr = i * lmb_size + spapr->hotplug_memory.base;; + uint64_t addr = i * lmb_size; uint32_t *dynamic_memory = cur_index; - drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_LMB, - addr/lmb_size); - g_assert(drc); - drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - - dynamic_memory[0] = cpu_to_be32(addr >> 32); - dynamic_memory[1] = cpu_to_be32(addr & 0xffffffff); - dynamic_memory[2] = cpu_to_be32(drck->get_index(drc)); - dynamic_memory[3] = cpu_to_be32(0); /* reserved */ - dynamic_memory[4] = cpu_to_be32(numa_get_node(addr, NULL)); - if (addr < machine->ram_size || - memory_region_present(get_system_memory(), addr)) { - dynamic_memory[5] = cpu_to_be32(SPAPR_LMB_FLAGS_ASSIGNED); + if (i >= hotplug_lmb_start) { + sPAPRDRConnector *drc; + sPAPRDRConnectorClass *drck; + + drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_LMB, i); + g_assert(drc); + drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + + dynamic_memory[0] = cpu_to_be32(addr >> 32); + dynamic_memory[1] = cpu_to_be32(addr & 0xffffffff); + dynamic_memory[2] = cpu_to_be32(drck->get_index(drc)); + dynamic_memory[3] = cpu_to_be32(0); /* reserved */ + dynamic_memory[4] = cpu_to_be32(numa_get_node(addr, NULL)); + if (memory_region_present(get_system_memory(), addr)) { + dynamic_memory[5] = cpu_to_be32(SPAPR_LMB_FLAGS_ASSIGNED); + } else { + dynamic_memory[5] = cpu_to_be32(0); + } } else { - dynamic_memory[5] = cpu_to_be32(0); + /* + * LMB information for RMA, boot time RAM and gap b/n RAM and + * hotplug memory region -- all these are marked as reserved + * and as having no valid DRC. + */ + dynamic_memory[0] = cpu_to_be32(addr >> 32); + dynamic_memory[1] = cpu_to_be32(addr & 0xffffffff); + dynamic_memory[2] = cpu_to_be32(0); + dynamic_memory[3] = cpu_to_be32(0); /* reserved */ + dynamic_memory[4] = cpu_to_be32(-1); + dynamic_memory[5] = cpu_to_be32(SPAPR_LMB_FLAGS_RESERVED | + SPAPR_LMB_FLAGS_DRC_INVALID); } cur_index += SPAPR_DR_LMB_LIST_ENTRY_SIZE; @@ -905,6 +938,7 @@ static void spapr_finalize_fdt(sPAPRMachineState *spapr, hwaddr rtas_size) { MachineState *machine = MACHINE(qdev_get_machine()); + MachineClass *mc = MACHINE_GET_CLASS(machine); sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); const char *boot_device = machine->boot_order; int ret, i; @@ -987,6 +1021,16 @@ static void spapr_finalize_fdt(sPAPRMachineState *spapr, _FDT(spapr_drc_populate_dt(fdt, 0, NULL, SPAPR_DR_CONNECTOR_TYPE_LMB)); } + if (mc->query_hotpluggable_cpus) { + int offset = fdt_path_offset(fdt, "/cpus"); + ret = spapr_drc_populate_dt(fdt, offset, NULL, + SPAPR_DR_CONNECTOR_TYPE_CPU); + if (ret < 0) { + error_report("Couldn't set up CPU DR device tree properties"); + exit(1); + } + } + _FDT((fdt_pack(fdt))); if (fdt_totalsize(fdt) > FDT_MAX_SIZE) { @@ -1180,26 +1224,6 @@ static void ppc_spapr_reset(void) } -static void spapr_cpu_reset(void *opaque) -{ - sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); - PowerPCCPU *cpu = opaque; - CPUState *cs = CPU(cpu); - CPUPPCState *env = &cpu->env; - - cpu_reset(cs); - - /* All CPUs start halted. CPU0 is unhalted from the machine level - * reset code and the rest are explicitly started up by the guest - * using an RTAS call */ - cs->halted = 1; - - env->spr[SPR_HIOR] = 0; - - ppc_hash64_set_external_hpt(cpu, spapr->htab, spapr->htab_shift, - &error_fatal); -} - static void spapr_create_nvram(sPAPRMachineState *spapr) { DeviceState *dev = qdev_create(&spapr->vio_bus->bus, "spapr-nvram"); @@ -1489,7 +1513,6 @@ static int htab_save_complete(QEMUFile *f, void *opaque) if (rc < 0) { return rc; } - close_htab_fd(spapr); } else { if (spapr->htab_first_pass) { htab_save_first_pass(f, spapr, -1); @@ -1591,10 +1614,18 @@ static int htab_load(QEMUFile *f, void *opaque, int version_id) return 0; } +static void htab_cleanup(void *opaque) +{ + sPAPRMachineState *spapr = opaque; + + close_htab_fd(spapr); +} + static SaveVMHandlers savevm_htab_handlers = { .save_live_setup = htab_save_setup, .save_live_iterate = htab_save_iterate, .save_live_complete_precopy = htab_save_complete, + .cleanup = htab_cleanup, .load_state = htab_load, }; @@ -1605,32 +1636,6 @@ static void spapr_boot_set(void *opaque, const char *boot_device, machine->boot_order = g_strdup(boot_device); } -static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, - Error **errp) -{ - CPUPPCState *env = &cpu->env; - - /* Set time-base frequency to 512 MHz */ - cpu_ppc_tb_init(env, TIMEBASE_FREQ); - - /* Enable PAPR mode in TCG or KVM */ - cpu_ppc_set_papr(cpu); - - if (cpu->max_compat) { - Error *local_err = NULL; - - ppc_set_compat(cpu, cpu->max_compat, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - } - - xics_cpu_setup(spapr->icp, cpu); - - qemu_register_reset(spapr_cpu_reset, cpu); -} - /* * Reset routine for LMB DR devices. * @@ -1708,11 +1713,11 @@ static void spapr_validate_node_memory(MachineState *machine, Error **errp) static void ppc_spapr_init(MachineState *machine) { sPAPRMachineState *spapr = SPAPR_MACHINE(machine); + MachineClass *mc = MACHINE_GET_CLASS(machine); sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); const char *kernel_filename = machine->kernel_filename; const char *kernel_cmdline = machine->kernel_cmdline; const char *initrd_filename = machine->initrd_filename; - PowerPCCPU *cpu; PCIHostState *phb; int i; MemoryRegion *sysmem = get_system_memory(); @@ -1726,6 +1731,22 @@ static void ppc_spapr_init(MachineState *machine) long load_limit, fw_size; bool kernel_le = false; char *filename; + int smt = kvmppc_smt_threads(); + int spapr_cores = smp_cpus / smp_threads; + int spapr_max_cores = max_cpus / smp_threads; + + if (mc->query_hotpluggable_cpus) { + if (smp_cpus % smp_threads) { + error_report("smp_cpus (%u) must be multiple of threads (%u)", + smp_cpus, smp_threads); + exit(1); + } + if (max_cpus % smp_threads) { + error_report("max_cpus (%u) must be multiple of threads (%u)", + max_cpus, smp_threads); + exit(1); + } + } msi_nonbroken = true; @@ -1759,6 +1780,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) { @@ -1771,10 +1799,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 * kvmppc_smt_threads(), - 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); @@ -1784,13 +1811,46 @@ static void ppc_spapr_init(MachineState *machine) if (machine->cpu_model == NULL) { machine->cpu_model = kvm_enabled() ? "host" : "POWER7"; } - for (i = 0; i < smp_cpus; i++) { - cpu = cpu_ppc_init(machine->cpu_model); - if (cpu == NULL) { - error_report("Unable to find PowerPC CPU definition"); + + ppc_cpu_parse_features(machine->cpu_model); + + if (mc->query_hotpluggable_cpus) { + char *type = spapr_get_cpu_core_type(machine->cpu_model); + + if (type == NULL) { + error_report("Unable to find sPAPR CPU Core definition"); exit(1); } - spapr_cpu_init(spapr, cpu, &error_fatal); + + spapr->cores = g_new0(Object *, spapr_max_cores); + for (i = 0; i < spapr_max_cores; i++) { + int core_id = i * smp_threads; + sPAPRDRConnector *drc = + spapr_dr_connector_new(OBJECT(spapr), + SPAPR_DR_CONNECTOR_TYPE_CPU, + (core_id / smp_threads) * smt); + + qemu_register_reset(spapr_drc_reset, drc); + + if (i < spapr_cores) { + Object *core = object_new(type); + object_property_set_int(core, smp_threads, "nr-threads", + &error_fatal); + object_property_set_int(core, core_id, CPU_CORE_PROP_CORE_ID, + &error_fatal); + object_property_set_bool(core, true, "realized", &error_fatal); + } + } + g_free(type); + } else { + for (i = 0; i < smp_cpus; i++) { + PowerPCCPU *cpu = cpu_ppc_init(machine->cpu_model); + if (cpu == NULL) { + error_report("Unable to find PowerPC CPU definition"); + exit(1); + } + spapr_cpu_init(spapr, cpu, &error_fatal); + } } if (kvm_enabled()) { @@ -1815,11 +1875,21 @@ static void ppc_spapr_init(MachineState *machine) /* initialize hotplug memory address space */ if (machine->ram_size < machine->maxram_size) { ram_addr_t hotplug_mem_size = machine->maxram_size - machine->ram_size; + /* + * Limit the number of hotpluggable memory slots to half the number + * slots that KVM supports, leaving the other half for PCI and other + * devices. However ensure that number of slots doesn't drop below 32. + */ + int max_memslots = kvm_enabled() ? kvm_get_max_memslots() / 2 : + SPAPR_MAX_RAM_SLOTS; - if (machine->ram_slots > SPAPR_MAX_RAM_SLOTS) { + if (max_memslots < SPAPR_MAX_RAM_SLOTS) { + max_memslots = SPAPR_MAX_RAM_SLOTS; + } + if (machine->ram_slots > max_memslots) { error_report("Specified number of memory slots %" PRIu64" exceeds max supported %d", - machine->ram_slots, SPAPR_MAX_RAM_SLOTS); + machine->ram_slots, max_memslots); exit(1); } @@ -1841,6 +1911,10 @@ static void ppc_spapr_init(MachineState *machine) exit(1); } spapr->rtas_size = get_image_size(filename); + if (spapr->rtas_size < 0) { + error_report("Could not get size of LPAR rtas '%s'", filename); + exit(1); + } spapr->rtas_blob = g_malloc(spapr->rtas_size); if (load_image_size(filename, spapr->rtas_blob, spapr->rtas_size) < 0) { error_report("Could not load LPAR rtas '%s'", filename); @@ -2131,15 +2205,6 @@ static void spapr_add_lmbs(DeviceState *dev, uint64_t addr, uint64_t size, int i, fdt_offset, fdt_size; void *fdt; - /* - * Check for DRC connectors and send hotplug notification to the - * guest only in case of hotplugged memory. This allows cold plugged - * memory to be specified at boot time. - */ - if (!dev->hotplugged) { - return; - } - for (i = 0; i < nr_lmbs; i++) { drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_LMB, addr/SPAPR_MEMORY_BLOCK_SIZE); @@ -2153,7 +2218,12 @@ static void spapr_add_lmbs(DeviceState *dev, uint64_t addr, uint64_t size, drck->attach(drc, dev, fdt, fdt_offset, !dev->hotplugged, errp); addr += SPAPR_MEMORY_BLOCK_SIZE; } - spapr_hotplug_req_add_by_count(SPAPR_DR_CONNECTOR_TYPE_LMB, nr_lmbs); + /* send hotplug notification to the + * guest only in case of hotplugged memory + */ + if (dev->hotplugged) { + spapr_hotplug_req_add_by_count(SPAPR_DR_CONNECTOR_TYPE_LMB, nr_lmbs); + } } static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, @@ -2191,6 +2261,27 @@ out: error_propagate(errp, local_err); } +void *spapr_populate_hotplug_cpu_dt(CPUState *cs, int *fdt_offset, + sPAPRMachineState *spapr) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + DeviceClass *dc = DEVICE_GET_CLASS(cs); + int id = ppc_get_vcpu_dt_id(cpu); + void *fdt; + int offset, fdt_size; + char *nodename; + + fdt = create_device_tree(&fdt_size); + nodename = g_strdup_printf("%s@%x", dc->fw_name, id); + offset = fdt_add_subnode(fdt, 0, nodename); + + spapr_populate_cpu_dt(cs, fdt, offset, spapr); + g_free(nodename); + + *fdt_offset = offset; + return fdt; +} + static void spapr_machine_device_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { @@ -2235,21 +2326,40 @@ static void spapr_machine_device_plug(HotplugHandler *hotplug_dev, } spapr_memory_plug(hotplug_dev, dev, node, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) { + spapr_core_plug(hotplug_dev, dev, errp); } } static void spapr_machine_device_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { + MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { error_setg(errp, "Memory hot unplug not supported by sPAPR"); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) { + if (!mc->query_hotpluggable_cpus) { + error_setg(errp, "CPU hot unplug not supported on this machine"); + return; + } + spapr_core_unplug(hotplug_dev, dev, errp); + } +} + +static void spapr_machine_device_pre_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) { + spapr_core_pre_plug(hotplug_dev, dev, errp); } } static HotplugHandler *spapr_get_hotpug_handler(MachineState *machine, DeviceState *dev) { - if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) || + object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) { return HOTPLUG_HANDLER(machine); } return NULL; @@ -2262,6 +2372,37 @@ static unsigned spapr_cpu_index_to_socket_id(unsigned cpu_index) return cpu_index / smp_threads / smp_cores; } +static HotpluggableCPUList *spapr_query_hotpluggable_cpus(MachineState *machine) +{ + int i; + HotpluggableCPUList *head = NULL; + sPAPRMachineState *spapr = SPAPR_MACHINE(machine); + int spapr_max_cores = max_cpus / smp_threads; + + for (i = 0; i < spapr_max_cores; i++) { + HotpluggableCPUList *list_item = g_new0(typeof(*list_item), 1); + HotpluggableCPU *cpu_item = g_new0(typeof(*cpu_item), 1); + CpuInstanceProperties *cpu_props = g_new0(typeof(*cpu_props), 1); + + cpu_item->type = spapr_get_cpu_core_type(machine->cpu_model); + cpu_item->vcpus_count = smp_threads; + cpu_props->has_core_id = true; + cpu_props->core_id = i * smp_threads; + /* TODO: add 'has_node/node' here to describe + to which node core belongs */ + + cpu_item->props = cpu_props; + if (spapr->cores[i]) { + cpu_item->has_qom_path = true; + cpu_item->qom_path = object_get_canonical_path(spapr->cores[i]); + } + list_item->value = cpu_item; + list_item->next = head; + head = list_item; + } + return head; +} + static void spapr_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -2288,11 +2429,13 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) mc->has_dynamic_sysbus = true; mc->pci_allow_0_address = true; mc->get_hotplug_handler = spapr_get_hotpug_handler; + hc->pre_plug = spapr_machine_device_pre_plug; hc->plug = spapr_machine_device_plug; hc->unplug = spapr_machine_device_unplug; mc->cpu_index_to_socket_id = spapr_cpu_index_to_socket_id; smc->dr_lmb_enabled = true; + mc->query_hotpluggable_cpus = spapr_query_hotpluggable_cpus; fwc->get_dev_path = spapr_get_fw_dev_path; nc->nmi_monitor_handler = spapr_nmi; } @@ -2343,18 +2486,42 @@ static const TypeInfo spapr_machine_info = { type_init(spapr_machine_register_##suffix) /* + * pseries-2.7 + */ +static void spapr_machine_2_7_instance_options(MachineState *machine) +{ +} + +static void spapr_machine_2_7_class_options(MachineClass *mc) +{ + /* Defaults for the latest behaviour inherited from the base class */ +} + +DEFINE_SPAPR_MACHINE(2_7, "2.7", true); + +/* * pseries-2.6 */ +#define SPAPR_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) { } static void spapr_machine_2_6_class_options(MachineClass *mc) { - /* Defaults for the latest behaviour inherited from the base class */ + spapr_machine_2_7_class_options(mc); + mc->query_hotpluggable_cpus = NULL; + SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_6); } -DEFINE_SPAPR_MACHINE(2_6, "2.6", true); +DEFINE_SPAPR_MACHINE(2_6, "2.6", false); /* * pseries-2.5 @@ -2386,7 +2553,6 @@ DEFINE_SPAPR_MACHINE(2_5, "2.5", false); * pseries-2.4 */ #define SPAPR_COMPAT_2_4 \ - SPAPR_COMPAT_2_5 \ HW_COMPAT_2_4 static void spapr_machine_2_4_instance_options(MachineState *machine) @@ -2409,7 +2575,6 @@ DEFINE_SPAPR_MACHINE(2_4, "2.4", false); * pseries-2.3 */ #define SPAPR_COMPAT_2_3 \ - SPAPR_COMPAT_2_4 \ HW_COMPAT_2_3 \ {\ .driver = "spapr-pci-host-bridge",\ @@ -2437,7 +2602,6 @@ DEFINE_SPAPR_MACHINE(2_3, "2.3", false); */ #define SPAPR_COMPAT_2_2 \ - SPAPR_COMPAT_2_3 \ HW_COMPAT_2_2 \ {\ .driver = TYPE_SPAPR_PCI_HOST_BRIDGE,\ @@ -2462,7 +2626,6 @@ DEFINE_SPAPR_MACHINE(2_2, "2.2", false); * pseries-2.1 */ #define SPAPR_COMPAT_2_1 \ - SPAPR_COMPAT_2_2 \ HW_COMPAT_2_1 static void spapr_machine_2_1_instance_options(MachineState *machine) diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c new file mode 100644 index 000000000..bcb483dbe --- /dev/null +++ b/hw/ppc/spapr_cpu_core.c @@ -0,0 +1,432 @@ +/* + * sPAPR CPU core device, acts as container of CPU thread devices. + * + * Copyright (C) 2016 Bharata B Rao <bharata@linux.vnet.ibm.com> + * + * 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 "hw/cpu/core.h" +#include "hw/ppc/spapr_cpu_core.h" +#include "target-ppc/cpu.h" +#include "hw/ppc/spapr.h" +#include "hw/boards.h" +#include "qapi/error.h" +#include "sysemu/cpus.h" +#include "target-ppc/kvm_ppc.h" +#include "hw/ppc/ppc.h" +#include "target-ppc/mmu-hash64.h" +#include "sysemu/numa.h" + +static void spapr_cpu_reset(void *opaque) +{ + sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); + PowerPCCPU *cpu = opaque; + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + + cpu_reset(cs); + + /* All CPUs start halted. CPU0 is unhalted from the machine level + * reset code and the rest are explicitly started up by the guest + * using an RTAS call */ + cs->halted = 1; + + env->spr[SPR_HIOR] = 0; + + ppc_hash64_set_external_hpt(cpu, spapr->htab, spapr->htab_shift, + &error_fatal); +} + +static void spapr_cpu_destroy(PowerPCCPU *cpu) +{ + sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); + + xics_cpu_destroy(spapr->xics, cpu); + qemu_unregister_reset(spapr_cpu_reset, cpu); +} + +void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, Error **errp) +{ + CPUPPCState *env = &cpu->env; + CPUState *cs = CPU(cpu); + int i; + + /* Set time-base frequency to 512 MHz */ + cpu_ppc_tb_init(env, SPAPR_TIMEBASE_FREQ); + + /* Enable PAPR mode in TCG or KVM */ + cpu_ppc_set_papr(cpu); + + if (cpu->max_compat) { + Error *local_err = NULL; + + ppc_set_compat(cpu, cpu->max_compat, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + } + + /* Set NUMA node for the added CPUs */ + for (i = 0; i < nb_numa_nodes; i++) { + if (test_bit(cs->cpu_index, numa_info[i].node_cpu)) { + cs->numa_node = i; + break; + } + } + + xics_cpu_setup(spapr->xics, cpu); + + qemu_register_reset(spapr_cpu_reset, cpu); + spapr_cpu_reset(cpu); +} + +/* + * Return the sPAPR CPU core type for @model which essentially is the CPU + * model specified with -cpu cmdline option. + */ +char *spapr_get_cpu_core_type(const char *model) +{ + char *core_type; + gchar **model_pieces = g_strsplit(model, ",", 2); + + core_type = g_strdup_printf("%s-%s", model_pieces[0], TYPE_SPAPR_CPU_CORE); + g_strfreev(model_pieces); + + /* Check whether it exists or whether we have to look up an alias name */ + if (!object_class_by_name(core_type)) { + const char *realmodel; + + g_free(core_type); + realmodel = ppc_cpu_lookup_alias(model); + if (realmodel) { + return spapr_get_cpu_core_type(realmodel); + } + return NULL; + } + + return core_type; +} + +static void spapr_core_release(DeviceState *dev, void *opaque) +{ + sPAPRCPUCore *sc = SPAPR_CPU_CORE(OBJECT(dev)); + 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()); + CPUCore *cc = CPU_CORE(dev); + int i; + + for (i = 0; i < cc->nr_threads; i++) { + void *obj = sc->threads + i * size; + DeviceState *dev = DEVICE(obj); + CPUState *cs = CPU(dev); + PowerPCCPU *cpu = POWERPC_CPU(cs); + + spapr_cpu_destroy(cpu); + cpu_remove_sync(cs); + object_unparent(obj); + } + + spapr->cores[cc->core_id / smp_threads] = NULL; + + g_free(sc->threads); + object_unparent(OBJECT(dev)); +} + +void spapr_core_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) +{ + CPUCore *cc = CPU_CORE(dev); + int smt = kvmppc_smt_threads(); + int index = cc->core_id / smp_threads; + sPAPRDRConnector *drc = + spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_CPU, index * smt); + sPAPRDRConnectorClass *drck; + Error *local_err = NULL; + + if (index == 0) { + error_setg(errp, "Boot CPU core may not be unplugged"); + return; + } + + g_assert(drc); + + drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + drck->detach(drc, dev, spapr_core_release, NULL, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + spapr_hotplug_req_remove_by_index(drc); +} + +void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) +{ + sPAPRMachineState *spapr = SPAPR_MACHINE(OBJECT(hotplug_dev)); + sPAPRCPUCore *core = SPAPR_CPU_CORE(OBJECT(dev)); + CPUCore *cc = CPU_CORE(dev); + CPUState *cs = CPU(core->threads); + sPAPRDRConnector *drc; + sPAPRDRConnectorClass *drck; + Error *local_err = NULL; + void *fdt = NULL; + int fdt_offset = 0; + int index = cc->core_id / smp_threads; + int smt = kvmppc_smt_threads(); + + drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_CPU, index * smt); + spapr->cores[index] = OBJECT(dev); + + g_assert(drc); + + /* + * Setup CPU DT entries only for hotplugged CPUs. For boot time or + * coldplugged CPUs DT entries are setup in spapr_finalize_fdt(). + */ + if (dev->hotplugged) { + fdt = spapr_populate_hotplug_cpu_dt(cs, &fdt_offset, spapr); + } + + drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + drck->attach(drc, dev, fdt, fdt_offset, !dev->hotplugged, &local_err); + if (local_err) { + g_free(fdt); + spapr->cores[index] = NULL; + error_propagate(errp, local_err); + return; + } + + if (dev->hotplugged) { + /* + * Send hotplug notification interrupt to the guest only in case + * of hotplugged CPUs. + */ + spapr_hotplug_req_add_by_index(drc); + } else { + /* + * Set the right DRC states for cold plugged CPU. + */ + drck->set_allocation_state(drc, SPAPR_DR_ALLOCATION_STATE_USABLE); + drck->set_isolation_state(drc, SPAPR_DR_ISOLATION_STATE_UNISOLATED); + } +} + +void spapr_core_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) +{ + MachineState *machine = MACHINE(OBJECT(hotplug_dev)); + MachineClass *mc = MACHINE_GET_CLASS(hotplug_dev); + sPAPRMachineState *spapr = SPAPR_MACHINE(OBJECT(hotplug_dev)); + int spapr_max_cores = max_cpus / smp_threads; + int index; + Error *local_err = NULL; + CPUCore *cc = CPU_CORE(dev); + char *base_core_type = spapr_get_cpu_core_type(machine->cpu_model); + const char *type = object_get_typename(OBJECT(dev)); + + if (!mc->query_hotpluggable_cpus) { + error_setg(&local_err, "CPU hotplug not supported for this machine"); + goto out; + } + + if (strcmp(base_core_type, type)) { + error_setg(&local_err, "CPU core type should be %s", base_core_type); + goto out; + } + + if (cc->nr_threads != smp_threads) { + error_setg(&local_err, "threads must be %d", smp_threads); + goto out; + } + + if (cc->core_id % smp_threads) { + error_setg(&local_err, "invalid core id %d", cc->core_id); + goto out; + } + + index = cc->core_id / smp_threads; + if (index < 0 || index >= spapr_max_cores) { + error_setg(&local_err, "core id %d out of range", cc->core_id); + goto out; + } + + if (spapr->cores[index]) { + error_setg(&local_err, "core %d already populated", cc->core_id); + goto out; + } + +out: + g_free(base_core_type); + error_propagate(errp, local_err); +} + +static void spapr_cpu_core_realize_child(Object *child, Error **errp) +{ + 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", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + spapr_cpu_init(spapr, cpu, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } +} + +static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) +{ + sPAPRCPUCore *sc = SPAPR_CPU_CORE(OBJECT(dev)); + CPUCore *cc = CPU_CORE(OBJECT(dev)); + const char *typename = object_class_get_name(sc->cpu_class); + size_t size = object_type_get_instance_size(typename); + Error *local_err = NULL; + void *obj; + int i, j; + + sc->threads = g_malloc0(size * cc->nr_threads); + for (i = 0; i < cc->nr_threads; i++) { + char id[32]; + CPUState *cs; + + obj = sc->threads + i * size; + + object_initialize(obj, size, typename); + cs = CPU(obj); + cs->cpu_index = cc->core_id + i; + snprintf(id, sizeof(id), "thread[%d]", i); + object_property_add_child(OBJECT(sc), id, obj, &local_err); + if (local_err) { + goto err; + } + object_unref(obj); + } + + 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) { + obj = sc->threads + i * size; + object_unparent(obj); + } + g_free(sc->threads); + error_propagate(errp, local_err); +} + +static void spapr_cpu_core_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + dc->realize = spapr_cpu_core_realize; +} + +/* + * instance_init routines from different flavours of sPAPR CPU cores. + */ +#define SPAPR_CPU_CORE_INITFN(_type, _fname) \ +static void glue(glue(spapr_cpu_core_, _fname), _initfn(Object *obj)) \ +{ \ + sPAPRCPUCore *core = SPAPR_CPU_CORE(obj); \ + char *name = g_strdup_printf("%s-" TYPE_POWERPC_CPU, stringify(_type)); \ + ObjectClass *oc = object_class_by_name(name); \ + g_assert(oc); \ + g_free((void *)name); \ + 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; + void (*initfn)(Object *obj); +} SPAPRCoreInfo; + +static const SPAPRCoreInfo spapr_cores[] = { + /* 970 */ + { .name = "970_v2.2", .initfn = spapr_cpu_core_970_initfn }, + + /* 970MP variants */ + { .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 }, + + /* POWER5+ */ + { .name = "POWER5+_v2.1", .initfn = spapr_cpu_core_POWER5plus_initfn }, + + /* POWER7 */ + { .name = "POWER7_v2.3", .initfn = spapr_cpu_core_POWER7_initfn }, + + /* POWER7+ */ + { .name = "POWER7+_v2.1", .initfn = spapr_cpu_core_POWER7plus_initfn }, + + /* POWER8 */ + { .name = "POWER8_v2.0", .initfn = spapr_cpu_core_POWER8_initfn }, + + /* POWER8E */ + { .name = "POWER8E_v2.1", .initfn = spapr_cpu_core_POWER8E_initfn }, + + /* POWER8NVL */ + { .name = "POWER8NVL_v1.0", .initfn = spapr_cpu_core_POWER8NVL_initfn }, + + { .name = NULL } +}; + +static void spapr_cpu_core_register(const SPAPRCoreInfo *info) +{ + TypeInfo type_info = { + .parent = TYPE_SPAPR_CPU_CORE, + .instance_size = sizeof(sPAPRCPUCore), + .instance_init = info->initfn, + }; + + type_info.name = g_strdup_printf("%s-" TYPE_SPAPR_CPU_CORE, info->name); + type_register(&type_info); + g_free((void *)type_info.name); +} + +static const TypeInfo spapr_cpu_core_type_info = { + .name = TYPE_SPAPR_CPU_CORE, + .parent = TYPE_CPU_CORE, + .abstract = true, + .instance_size = sizeof(sPAPRCPUCore), + .class_init = spapr_cpu_core_class_init, +}; + +static void spapr_cpu_core_register_types(void) +{ + const SPAPRCoreInfo *info = spapr_cores; + + type_register_static(&spapr_cpu_core_type_info); + while (info->name) { + spapr_cpu_core_register(info); + info++; + } +} + +type_init(spapr_cpu_core_register_types) diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c index 1f5f1d790..26a067951 100644 --- a/hw/ppc/spapr_drc.c +++ b/hw/ppc/spapr_drc.c @@ -140,6 +140,8 @@ static uint32_t set_allocation_state(sPAPRDRConnector *drc, DPRINTFN("finalizing device removal"); drck->detach(drc, DEVICE(drc->dev), drc->detach_cb, drc->detach_cb_opaque, NULL); + } else if (drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_USABLE) { + drc->awaiting_allocation = false; } } return RTAS_OUT_SUCCESS; @@ -269,11 +271,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name, void *fdt; if (!drc->fdt) { - visit_start_struct(v, name, NULL, 0, &err); - if (!err) { - visit_end_struct(v, &err); - } - error_propagate(errp, err); + visit_type_null(v, NULL, errp); return; } @@ -301,7 +299,8 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name, case FDT_END_NODE: /* shouldn't ever see an FDT_END_NODE before FDT_BEGIN_NODE */ g_assert(fdt_depth > 0); - visit_end_struct(v, &err); + visit_check_struct(v, &err); + visit_end_struct(v, NULL); if (err) { error_propagate(errp, err); return; @@ -312,7 +311,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name, int i; prop = fdt_get_property_by_offset(fdt, fdt_offset, &prop_len); name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); - visit_start_list(v, name, &err); + visit_start_list(v, name, NULL, 0, &err); if (err) { error_propagate(errp, err); return; @@ -324,7 +323,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name, return; } } - visit_end_list(v); + visit_end_list(v, NULL); break; } default: @@ -376,6 +375,10 @@ static void attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt, drc->signalled = (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI) ? true : coldplug; + if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI) { + drc->awaiting_allocation = true; + } + object_property_add_link(OBJECT(drc), "device", object_get_typename(OBJECT(drc->dev)), (Object **)(&drc->dev), @@ -424,6 +427,12 @@ static void detach(sPAPRDRConnector *drc, DeviceState *d, return; } + if (drc->awaiting_allocation) { + drc->awaiting_release = true; + DPRINTFN("awaiting allocation to complete before removal"); + return; + } + drc->indicator_state = SPAPR_DR_INDICATOR_STATE_INACTIVE; if (drc->detach_cb) { diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c index 049fb1b32..b0668b34a 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) @@ -449,6 +449,9 @@ static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action, case SPAPR_DR_CONNECTOR_TYPE_LMB: hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_MEMORY; break; + case SPAPR_DR_CONNECTOR_TYPE_CPU: + hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_CPU; + break; default: /* we shouldn't be signaling hotplug events for resources * that don't support them @@ -465,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) @@ -548,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; @@ -600,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 8f40602a5..73af112e1 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -1,12 +1,15 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "sysemu/sysemu.h" +#include "qemu/log.h" #include "cpu.h" +#include "exec/exec-all.h" #include "helper_regs.h" #include "hw/ppc/spapr.h" #include "mmu-hash64.h" #include "cpu-models.h" #include "trace.h" +#include "sysemu/kvm.h" #include "kvm_ppc.h" struct SPRSyncState { @@ -80,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; @@ -99,11 +102,15 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPRMachineState *spapr, return H_PARAMETER; } } else { + target_ulong wimg_flags; /* Looks like an IO address */ /* FIXME: What WIMG combinations could be sensible for IO? * For now we allow WIMG=010x, but are there others? */ /* FIXME: Should we check against registered IO addresses? */ - if ((ptel & (HPTE64_R_W | HPTE64_R_I | HPTE64_R_M)) != HPTE64_R_I) { + wimg_flags = (ptel & (HPTE64_R_W | HPTE64_R_I | HPTE64_R_M)); + + if (wimg_flags != HPTE64_R_I && + wimg_flags != (HPTE64_R_I | HPTE64_R_M)) { return H_PARAMETER; } } @@ -183,6 +190,7 @@ static RemoveResult remove_hpte(PowerPCCPU *cpu, target_ulong ptex, static target_ulong h_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { + CPUPPCState *env = &cpu->env; target_ulong flags = args[0]; target_ulong pte_index = args[1]; target_ulong avpn = args[2]; @@ -193,6 +201,7 @@ static target_ulong h_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr, switch (ret) { case REMOVE_SUCCESS: + check_tlb_flush(env); return H_SUCCESS; case REMOVE_NOT_FOUND: @@ -229,7 +238,9 @@ static target_ulong h_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr, static target_ulong h_bulk_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { + CPUPPCState *env = &cpu->env; int i; + target_ulong rc = H_SUCCESS; for (i = 0; i < H_BULK_REMOVE_MAX_BATCH; i++) { target_ulong *tsh = &args[i*2]; @@ -262,14 +273,18 @@ static target_ulong h_bulk_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr, break; case REMOVE_PARM: - return H_PARAMETER; + rc = H_PARAMETER; + goto exit; case REMOVE_HW: - return H_HARDWARE; + rc = H_HARDWARE; + goto exit; } } + exit: + check_tlb_flush(env); - return H_SUCCESS; + return rc; } static target_ulong h_protect(PowerPCCPU *cpu, sPAPRMachineState *spapr, @@ -911,6 +926,41 @@ static void do_set_compat(void *arg) ((cpuver) == CPU_POWERPC_LOGICAL_2_06_PLUS) ? 2061 : \ ((cpuver) == CPU_POWERPC_LOGICAL_2_07) ? 2070 : 0) +static void cas_handle_compat_cpu(PowerPCCPUClass *pcc, uint32_t pvr, + unsigned max_lvl, unsigned *compat_lvl, + unsigned *cpu_version) +{ + unsigned lvl = get_compat_level(pvr); + bool is205, is206, is207; + + if (!lvl) { + return; + } + + /* If it is a logical PVR, try to determine the highest level */ + is205 = (pcc->pcr_supported & PCR_COMPAT_2_05) && + (lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_05)); + is206 = (pcc->pcr_supported & PCR_COMPAT_2_06) && + ((lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_06)) || + (lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_06_PLUS))); + is207 = (pcc->pcr_supported & PCR_COMPAT_2_07) && + (lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_07)); + + if (is205 || is206 || is207) { + if (!max_lvl) { + /* User did not set the level, choose the highest */ + if (*compat_lvl <= lvl) { + *compat_lvl = lvl; + *cpu_version = pvr; + } + } else if (max_lvl >= lvl) { + /* User chose the level, don't set higher than this */ + *compat_lvl = lvl; + *cpu_version = pvr; + } + } +} + #define OV5_DRCONF_MEMORY 0x20 static target_ulong h_client_architecture_support(PowerPCCPU *cpu_, @@ -920,7 +970,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu_, { target_ulong list = ppc64_phys_to_real(args[0]); target_ulong ov_table, ov5; - PowerPCCPUClass *pcc_ = POWERPC_CPU_GET_CLASS(cpu_); + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu_); CPUState *cs; bool cpu_match = false, cpu_update = true, memory_update = false; unsigned old_cpu_version = cpu_->cpu_version; @@ -947,29 +997,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu_, cpu_match = true; cpu_version = cpu_->cpu_version; } else if (!cpu_match) { - /* If it is a logical PVR, try to determine the highest level */ - unsigned lvl = get_compat_level(pvr); - if (lvl) { - bool is205 = (pcc_->pcr_mask & PCR_COMPAT_2_05) && - (lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_05)); - bool is206 = (pcc_->pcr_mask & PCR_COMPAT_2_06) && - ((lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_06)) || - (lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_06_PLUS))); - - if (is205 || is206) { - if (!max_lvl) { - /* User did not set the level, choose the highest */ - if (compat_lvl <= lvl) { - compat_lvl = lvl; - cpu_version = pvr; - } - } else if (max_lvl >= lvl) { - /* User chose the level, don't set higher than this */ - compat_lvl = lvl; - cpu_version = pvr; - } - } - } + cas_handle_compat_cpu(pcc, pvr, max_lvl, &compat_lvl, &cpu_version); } /* Terminator record */ if (~pvr_mask & pvr) { @@ -979,7 +1007,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu_, /* Parsing finished */ trace_spapr_cas_pvr(cpu_->cpu_version, cpu_match, - cpu_version, pcc_->pcr_mask); + cpu_version, pcc->pcr_mask); /* Update CPUs */ if (old_cpu_version != cpu_version) { diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index 7dd458846..6bc4d4db3 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -17,7 +17,9 @@ * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "hw/hw.h" +#include "qemu/log.h" #include "sysemu/kvm.h" #include "hw/qdev.h" #include "kvm_ppc.h" @@ -75,6 +77,37 @@ static IOMMUAccessFlags spapr_tce_iommu_access_flags(uint64_t tce) } } +static uint64_t *spapr_tce_alloc_table(uint32_t liobn, + uint32_t page_shift, + uint32_t nb_table, + int *fd, + bool need_vfio) +{ + uint64_t *table = NULL; + uint64_t window_size = (uint64_t)nb_table << page_shift; + + if (kvm_enabled() && !(window_size >> 32)) { + table = kvmppc_create_spapr_tce(liobn, window_size, fd, need_vfio); + } + + if (!table) { + *fd = -1; + table = g_malloc0(nb_table * sizeof(uint64_t)); + } + + trace_spapr_iommu_new_table(liobn, table, *fd); + + return table; +} + +static void spapr_tce_free_table(uint64_t *table, int fd, uint32_t nb_table) +{ + if (!kvm_enabled() || + (kvmppc_remove_spapr_tce(table, fd, nb_table) != 0)) { + g_free(table); + } +} + /* Called from RCU critical section */ static IOMMUTLBEntry spapr_tce_translate_iommu(MemoryRegion *iommu, hwaddr addr, bool is_write) @@ -105,61 +138,131 @@ static IOMMUTLBEntry spapr_tce_translate_iommu(MemoryRegion *iommu, hwaddr addr, return ret; } +static void spapr_tce_table_pre_save(void *opaque) +{ + sPAPRTCETable *tcet = SPAPR_TCE_TABLE(opaque); + + tcet->mig_table = tcet->table; + tcet->mig_nb_table = tcet->nb_table; + + trace_spapr_iommu_pre_save(tcet->liobn, tcet->mig_nb_table, + tcet->bus_offset, tcet->page_shift); +} + +static uint64_t spapr_tce_get_min_page_size(MemoryRegion *iommu) +{ + sPAPRTCETable *tcet = container_of(iommu, sPAPRTCETable, 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); + uint32_t old_nb_table = tcet->nb_table; + uint64_t old_bus_offset = tcet->bus_offset; + uint32_t old_page_shift = tcet->page_shift; if (tcet->vdev) { spapr_vio_set_bypass(tcet->vdev, tcet->bypass); } + if (tcet->mig_nb_table != tcet->nb_table) { + spapr_tce_table_disable(tcet); + } + + if (tcet->mig_nb_table) { + if (!tcet->nb_table) { + spapr_tce_table_enable(tcet, old_page_shift, old_bus_offset, + tcet->mig_nb_table); + } + + memcpy(tcet->table, tcet->mig_table, + tcet->nb_table * sizeof(tcet->table[0])); + + free(tcet->mig_table); + tcet->mig_table = NULL; + } + + trace_spapr_iommu_post_load(tcet->liobn, old_nb_table, tcet->nb_table, + tcet->bus_offset, tcet->page_shift); + return 0; } +static bool spapr_tce_table_ex_needed(void *opaque) +{ + sPAPRTCETable *tcet = opaque; + + return tcet->bus_offset || tcet->page_shift != 0xC; +} + +static const VMStateDescription vmstate_spapr_tce_table_ex = { + .name = "spapr_iommu_ex", + .version_id = 1, + .minimum_version_id = 1, + .needed = spapr_tce_table_ex_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT64(bus_offset, sPAPRTCETable), + VMSTATE_UINT32(page_shift, sPAPRTCETable), + VMSTATE_END_OF_LIST() + }, +}; + static const VMStateDescription vmstate_spapr_tce_table = { .name = "spapr_iommu", .version_id = 2, .minimum_version_id = 2, + .pre_save = spapr_tce_table_pre_save, .post_load = spapr_tce_table_post_load, .fields = (VMStateField []) { /* Sanity check */ VMSTATE_UINT32_EQUAL(liobn, sPAPRTCETable), - VMSTATE_UINT32_EQUAL(nb_table, sPAPRTCETable), /* IOMMU state */ + VMSTATE_UINT32(mig_nb_table, sPAPRTCETable), VMSTATE_BOOL(bypass, sPAPRTCETable), - VMSTATE_VARRAY_UINT32(table, sPAPRTCETable, nb_table, 0, vmstate_info_uint64, uint64_t), + VMSTATE_VARRAY_UINT32_ALLOC(mig_table, sPAPRTCETable, mig_nb_table, 0, + vmstate_info_uint64, uint64_t), VMSTATE_END_OF_LIST() }, + .subsections = (const VMStateDescription*[]) { + &vmstate_spapr_tce_table_ex, + NULL + } }; 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) { sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev); - uint64_t window_size = (uint64_t)tcet->nb_table << tcet->page_shift; - - if (kvm_enabled() && !(window_size >> 32)) { - tcet->table = kvmppc_create_spapr_tce(tcet->liobn, - window_size, - &tcet->fd, - tcet->need_vfio); - } - - if (!tcet->table) { - size_t table_size = tcet->nb_table * sizeof(uint64_t); - tcet->table = g_malloc0(table_size); - } + Object *tcetobj = OBJECT(tcet); + char tmp[32]; - trace_spapr_iommu_new_table(tcet->liobn, tcet, tcet->table, tcet->fd); + tcet->fd = -1; + tcet->need_vfio = false; + snprintf(tmp, sizeof(tmp), "tce-root-%x", tcet->liobn); + memory_region_init(&tcet->root, tcetobj, tmp, UINT64_MAX); - memory_region_init_iommu(&tcet->iommu, OBJECT(dev), &spapr_iommu_ops, - "iommu-spapr", - (uint64_t)tcet->nb_table << tcet->page_shift); + snprintf(tmp, sizeof(tmp), "tce-iommu-%x", tcet->liobn); + memory_region_init_iommu(&tcet->iommu, tcetobj, &spapr_iommu_ops, tmp, 0); QLIST_INSERT_HEAD(&spapr_tce_tables, tcet, list); @@ -201,14 +304,10 @@ void spapr_tce_set_need_vfio(sPAPRTCETable *tcet, bool need_vfio) tcet->table = newtable; } -sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn, - uint64_t bus_offset, - uint32_t page_shift, - uint32_t nb_table, - bool need_vfio) +sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn) { sPAPRTCETable *tcet; - char tmp[64]; + char tmp[32]; if (spapr_tce_find_by_liobn(liobn)) { fprintf(stderr, "Attempted to create TCE table with duplicate" @@ -216,16 +315,8 @@ sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn, return NULL; } - if (!nb_table) { - return NULL; - } - tcet = SPAPR_TCE_TABLE(object_new(TYPE_SPAPR_TCE_TABLE)); tcet->liobn = liobn; - tcet->bus_offset = bus_offset; - tcet->page_shift = page_shift; - tcet->nb_table = nb_table; - tcet->need_vfio = need_vfio; snprintf(tmp, sizeof(tmp), "tce-table-%x", liobn); object_property_add_child(OBJECT(owner), tmp, OBJECT(tcet), NULL); @@ -235,22 +326,58 @@ sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn, return tcet; } +void spapr_tce_table_enable(sPAPRTCETable *tcet, + uint32_t page_shift, uint64_t bus_offset, + uint32_t nb_table) +{ + if (tcet->nb_table) { + error_report("Warning: trying to enable already enabled TCE table"); + return; + } + + tcet->bus_offset = bus_offset; + tcet->page_shift = page_shift; + tcet->nb_table = nb_table; + tcet->table = spapr_tce_alloc_table(tcet->liobn, + tcet->page_shift, + tcet->nb_table, + &tcet->fd, + tcet->need_vfio); + + memory_region_set_size(&tcet->iommu, + (uint64_t)tcet->nb_table << tcet->page_shift); + memory_region_add_subregion(&tcet->root, tcet->bus_offset, &tcet->iommu); +} + +void spapr_tce_table_disable(sPAPRTCETable *tcet) +{ + if (!tcet->nb_table) { + return; + } + + memory_region_del_subregion(&tcet->root, &tcet->iommu); + memory_region_set_size(&tcet->iommu, 0); + + spapr_tce_free_table(tcet->table, tcet->fd, tcet->nb_table); + tcet->fd = -1; + tcet->table = NULL; + tcet->bus_offset = 0; + tcet->page_shift = 0; + tcet->nb_table = 0; +} + static void spapr_tce_table_unrealize(DeviceState *dev, Error **errp) { sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev); QLIST_REMOVE(tcet, list); - if (!kvm_enabled() || - (kvmppc_remove_spapr_tce(tcet->table, tcet->fd, - tcet->nb_table) != 0)) { - g_free(tcet->table); - } + spapr_tce_table_disable(tcet); } MemoryRegion *spapr_tce_get_iommu(sPAPRTCETable *tcet) { - return &tcet->iommu; + return &tcet->root; } static void spapr_tce_reset(DeviceState *dev) @@ -258,7 +385,9 @@ static void spapr_tce_reset(DeviceState *dev) sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev); size_t table_size = tcet->nb_table * sizeof(uint64_t); - memset(tcet->table, 0, table_size); + if (tcet->nb_table) { + memset(tcet->table, 0, table_size); + } } static target_ulong put_tce_emu(sPAPRTCETable *tcet, target_ulong ioba, @@ -277,7 +406,7 @@ static target_ulong put_tce_emu(sPAPRTCETable *tcet, target_ulong ioba, tcet->table[index] = tce; entry.target_as = &address_space_memory, - entry.iova = ioba & page_mask; + entry.iova = (ioba - tcet->bus_offset) & page_mask; entry.translated_addr = tce & page_mask; entry.addr_mask = ~page_mask; entry.perm = spapr_tce_iommu_access_flags(tce); diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 573e635bf..949c44fec 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" @@ -44,6 +45,8 @@ #include "hw/pci/pci_bus.h" #include "hw/ppc/spapr_drc.h" #include "sysemu/device_tree.h" +#include "sysemu/kvm.h" +#include "sysemu/hostmem.h" #include "hw/vfio/vfio.h" @@ -321,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); } @@ -359,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: ", @@ -370,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); } @@ -732,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 = { @@ -1086,19 +1089,11 @@ 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); - } - - if (dev->hotplugged) { - fdt = create_device_tree(&fdt_size); - fdt_start_offset = spapr_create_pci_child_dt(phb, pdev, fdt, 0); - if (!fdt_start_offset) { - error_setg(errp, "Failed to create pci child device tree node"); - goto out; - } + fdt = create_device_tree(&fdt_size); + fdt_start_offset = spapr_create_pci_child_dt(phb, pdev, fdt, 0); + if (!fdt_start_offset) { + error_setg(errp, "Failed to create pci child device tree node"); + goto out; } drck->attach(drc, DEVICE(pdev), @@ -1311,12 +1306,14 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) PCIBus *bus; uint64_t msi_window_size = 4096; sPAPRTCETable *tcet; - uint32_t nb_table; + 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" @@ -1331,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; @@ -1344,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; } @@ -1444,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: "); @@ -1463,19 +1464,18 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) } } - nb_table = sphb->dma_win_size >> SPAPR_TCE_PAGE_SHIFT; - tcet = spapr_tce_new_table(DEVICE(sphb), sphb->dma_liobn, - 0, SPAPR_TCE_PAGE_SHIFT, nb_table, false); - 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); } - /* Register default 32bit DMA window */ - memory_region_add_subregion(&sphb->iommu_root, sphb->dma_win_addr, - spapr_tce_get_iommu(tcet)); - sphb->msi = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free); } @@ -1490,8 +1490,31 @@ static int spapr_phb_children_reset(Object *child, void *opaque) return 0; } +void spapr_phb_dma_reset(sPAPRPHBState *sphb) +{ + int i; + sPAPRTCETable *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); +} + static void spapr_phb_reset(DeviceState *qdev) { + sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(qdev); + + spapr_phb_dma_reset(sphb); + /* Reset the IOMMU state */ object_child_foreach(OBJECT(qdev), spapr_phb_children_reset, NULL); @@ -1503,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), @@ -1515,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(), }; @@ -1591,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), @@ -1625,7 +1654,6 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data) dc->reset = spapr_phb_reset; dc->vmsd = &vmstate_spapr_pci; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - dc->cannot_instantiate_with_device_add_yet = false; hp->plug = spapr_phb_hot_plug_child; hp->unplug = spapr_phb_hot_unplug_child; } @@ -1768,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; @@ -1790,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 @@ -1815,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(SPAPR_PCI_LIOBN(phb->index, 0)); + tcet = spapr_tce_find_by_liobn(phb->dma_liobn[0]); if (!tcet) { return -1; } diff --git a/hw/ppc/spapr_pci_vfio.c b/hw/ppc/spapr_pci_vfio.c index cbd3d23c9..8448e0b02 100644 --- a/hw/ppc/spapr_pci_vfio.c +++ b/hw/ppc/spapr_pci_vfio.c @@ -18,15 +18,16 @@ */ #include "qemu/osdep.h" +#include <linux/vfio.h> #include "qapi/error.h" #include "qemu-common.h" #include "cpu.h" #include "hw/ppc/spapr.h" #include "hw/pci-host/spapr.h" #include "hw/pci/msix.h" -#include "linux/vfio.h" #include "hw/vfio/vfio.h" #include "qemu/error-report.h" +#include "sysemu/qtest.h" #define TYPE_SPAPR_PCI_VFIO_HOST_BRIDGE "spapr-pci-vfio-host-bridge" @@ -48,7 +49,9 @@ static Property spapr_phb_vfio_properties[] = { static void spapr_phb_vfio_instance_init(Object *obj) { - error_report("spapr-pci-vfio-host-bridge is deprecated"); + if (!qtest_enabled()) { + error_report("spapr-pci-vfio-host-bridge is deprecated"); + } } bool spapr_phb_eeh_available(sPAPRPHBState *sphb) diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index f07325831..dc058e512 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -26,14 +26,17 @@ */ #include "qemu/osdep.h" #include "cpu.h" +#include "qemu/log.h" #include "sysemu/sysemu.h" #include "sysemu/char.h" #include "hw/qdev.h" #include "sysemu/device_tree.h" #include "sysemu/cpus.h" +#include "sysemu/kvm.h" #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_vio.h" +#include "hw/ppc/ppc.h" #include "qapi-event.h" #include "hw/boards.h" @@ -162,6 +165,27 @@ static void rtas_query_cpu_stopped_state(PowerPCCPU *cpu_, rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); } +/* + * Set the timebase offset of the CPU to that of first CPU. + * This helps hotplugged CPU to have the correct timebase offset. + */ +static void spapr_cpu_update_tb_offset(PowerPCCPU *cpu) +{ + PowerPCCPU *fcpu = POWERPC_CPU(first_cpu); + + cpu->env.tb_env->tb_offset = fcpu->env.tb_env->tb_offset; +} + +static void spapr_cpu_set_endianness(PowerPCCPU *cpu) +{ + PowerPCCPU *fcpu = POWERPC_CPU(first_cpu); + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(fcpu); + + if (!pcc->interrupts_big_endian(fcpu)) { + cpu->env.spr[SPR_LPCR] |= LPCR_ILE; + } +} + static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPRMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, @@ -198,6 +222,8 @@ static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPRMachineState *spapr, env->nip = start; env->gpr[3] = r3; cs->halted = 0; + spapr_cpu_set_endianness(cpu); + spapr_cpu_update_tb_offset(cpu); qemu_cpu_kick(cs); diff --git a/hw/ppc/spapr_rtas_ddw.c b/hw/ppc/spapr_rtas_ddw.c new file mode 100644 index 000000000..177dcffc9 --- /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 8aa021fde..f93244d7c 100644 --- a/hw/ppc/spapr_vio.c +++ b/hw/ppc/spapr_vio.c @@ -22,6 +22,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/hw.h" +#include "qemu/log.h" #include "sysemu/sysemu.h" #include "hw/boards.h" #include "hw/loader.h" @@ -56,12 +57,9 @@ static char *spapr_vio_get_dev_name(DeviceState *qdev) { VIOsPAPRDevice *dev = VIO_SPAPR_DEVICE(qdev); VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev); - char *name; /* Device tree style name device@reg */ - name = g_strdup_printf("%s@%x", pc->dt_name, dev->reg); - - return name; + return g_strdup_printf("%s@%x", pc->dt_name, dev->reg); } static void spapr_vio_bus_class_init(ObjectClass *klass, void *data) @@ -465,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; @@ -482,11 +480,9 @@ static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp) memory_region_add_subregion_overlap(&dev->mrroot, 0, &dev->mrbypass, 1); address_space_init(&dev->as, &dev->mrroot, qdev->id); - dev->tcet = spapr_tce_new_table(qdev, liobn, - 0, - SPAPR_TCE_PAGE_SHIFT, - pc->rtce_window_size >> - SPAPR_TCE_PAGE_SHIFT, false); + dev->tcet = spapr_tce_new_table(qdev, liobn); + spapr_tce_table_enable(dev->tcet, SPAPR_TCE_PAGE_SHIFT, 0, + pc->rtce_window_size >> SPAPR_TCE_PAGE_SHIFT); dev->tcet->vdev = dev; memory_region_add_subregion_overlap(&dev->mrroot, 0, spapr_tce_get_iommu(dev->tcet), 2); @@ -584,7 +580,7 @@ const VMStateDescription vmstate_spapr_vio = { VMSTATE_UINT32_EQUAL(irq, VIOsPAPRDevice), /* General VIO device state */ - VMSTATE_UINTTL(signal_state, VIOsPAPRDevice), + VMSTATE_UINT64(signal_state, VIOsPAPRDevice), VMSTATE_UINT64(crq.qladdr, VIOsPAPRDevice), VMSTATE_UINT32(crq.qsize, VIOsPAPRDevice), VMSTATE_UINT32(crq.qnext, VIOsPAPRDevice), diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events new file mode 100644 index 000000000..dfeab9308 --- /dev/null +++ b/hw/ppc/trace-events @@ -0,0 +1,43 @@ +# See docs/tracing.txt for syntax documentation. + +# hw/ppc/spapr_pci.c +spapr_pci_msi(const char *msg, uint32_t ca) "%s (cfg=%x)" +spapr_pci_msi_setup(const char *name, unsigned vector, uint64_t addr) "dev\"%s\" vector %u, addr=%"PRIx64 +spapr_pci_rtas_ibm_change_msi(unsigned cfg, unsigned func, unsigned req, unsigned first) "cfgaddr %x func %u, requested %u, first irq %u" +spapr_pci_rtas_ibm_query_interrupt_source_number(unsigned ioa, unsigned intr) "queries for #%u, IRQ%u" +spapr_pci_msi_write(uint64_t addr, uint64_t data, uint32_t dt_irq) "@%"PRIx64"<=%"PRIx64" IRQ %u" +spapr_pci_lsi_set(const char *busname, int pin, uint32_t irq) "%s PIN%d IRQ %u" +spapr_pci_msi_retry(unsigned config_addr, unsigned req_num, unsigned max_irqs) "Guest device at %x asked %u, have only %u" + +# hw/ppc/spapr.c +spapr_cas_failed(unsigned long n) "DT diff buffer is too small: %ld bytes" +spapr_cas_continue(unsigned long n) "Copy changes to the guest: %ld bytes" + +# hw/ppc/spapr_hcall.c +spapr_cas_pvr_try(uint32_t pvr) "%x" +spapr_cas_pvr(uint32_t cur_pvr, bool cpu_match, uint32_t new_pvr, uint64_t pcr) "current=%x, cpu_match=%u, new=%x, compat flags=%"PRIx64 + +# hw/ppc/spapr_iommu.c +spapr_iommu_put(uint64_t liobn, uint64_t ioba, uint64_t tce, uint64_t ret) "liobn=%"PRIx64" ioba=0x%"PRIx64" tce=0x%"PRIx64" ret=%"PRId64 +spapr_iommu_get(uint64_t liobn, uint64_t ioba, uint64_t ret, uint64_t tce) "liobn=%"PRIx64" ioba=0x%"PRIx64" ret=%"PRId64" tce=0x%"PRIx64 +spapr_iommu_indirect(uint64_t liobn, uint64_t ioba, uint64_t tce, uint64_t iobaN, uint64_t tceN, uint64_t ret) "liobn=%"PRIx64" ioba=0x%"PRIx64" tcelist=0x%"PRIx64" iobaN=0x%"PRIx64" tceN=0x%"PRIx64" ret=%"PRId64 +spapr_iommu_stuff(uint64_t liobn, uint64_t ioba, uint64_t tce_value, uint64_t npages, uint64_t ret) "liobn=%"PRIx64" ioba=0x%"PRIx64" tcevalue=0x%"PRIx64" npages=%"PRId64" ret=%"PRId64 +spapr_iommu_pci_put(uint64_t liobn, uint64_t ioba, uint64_t tce, uint64_t ret) "liobn=%"PRIx64" ioba=0x%"PRIx64" tce=0x%"PRIx64" ret=%"PRId64 +spapr_iommu_pci_get(uint64_t liobn, uint64_t ioba, uint64_t ret, uint64_t tce) "liobn=%"PRIx64" ioba=0x%"PRIx64" ret=%"PRId64" tce=0x%"PRIx64 +spapr_iommu_pci_indirect(uint64_t liobn, uint64_t ioba, uint64_t tce, uint64_t iobaN, uint64_t tceN, uint64_t ret) "liobn=%"PRIx64" ioba=0x%"PRIx64" tcelist=0x%"PRIx64" iobaN=0x%"PRIx64" tceN=0x%"PRIx64" ret=%"PRId64 +spapr_iommu_pci_stuff(uint64_t liobn, uint64_t ioba, uint64_t tce_value, uint64_t npages, uint64_t ret) "liobn=%"PRIx64" ioba=0x%"PRIx64" tcevalue=0x%"PRIx64" npages=%"PRId64" ret=%"PRId64 +spapr_iommu_xlate(uint64_t liobn, uint64_t ioba, uint64_t tce, unsigned perm, unsigned pgsize) "liobn=%"PRIx64" 0x%"PRIx64" -> 0x%"PRIx64" perm=%u mask=%x" +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)" + +# hw/ppc/prep.c +prep_io_800_writeb(uint32_t addr, uint32_t val) "0x%08" PRIx32 " => 0x%02" PRIx32 +prep_io_800_readb(uint32_t addr, uint32_t retval) "0x%08" PRIx32 " <= 0x%02" PRIx32 diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index b807a08c2..b97d96685 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "cpu.h" #include "hw/sysbus.h" #include "hw/hw.h" #include "hw/char/serial.h" diff --git a/hw/s390x/Makefile.objs b/hw/s390x/Makefile.objs index 220361782..41ac4ec32 100644 --- a/hw/s390x/Makefile.objs +++ b/hw/s390x/Makefile.objs @@ -8,6 +8,8 @@ obj-y += ipl.o obj-y += css.o obj-y += s390-virtio-ccw.o obj-y += virtio-ccw.o +obj-y += css-bridge.o +obj-y += ccw-device.o obj-y += s390-pci-bus.o s390-pci-inst.o obj-y += s390-skeys.o obj-$(CONFIG_KVM) += s390-skeys-kvm.o diff --git a/hw/s390x/ccw-device.c b/hw/s390x/ccw-device.c new file mode 100644 index 000000000..28ea20440 --- /dev/null +++ b/hw/s390x/ccw-device.c @@ -0,0 +1,27 @@ +/* + * Common device infrastructure for devices in the virtual css + * + * Copyright 2016 IBM Corp. + * Author(s): Jing Liu <liujbjl@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ +#include "qemu/osdep.h" +#include "ccw-device.h" + +static const TypeInfo ccw_device_info = { + .name = TYPE_CCW_DEVICE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(CcwDevice), + .class_size = sizeof(CCWDeviceClass), + .abstract = true, +}; + +static void ccw_device_register(void) +{ + type_register_static(&ccw_device_info); +} + +type_init(ccw_device_register) diff --git a/hw/s390x/ccw-device.h b/hw/s390x/ccw-device.h new file mode 100644 index 000000000..59ba01b6c --- /dev/null +++ b/hw/s390x/ccw-device.h @@ -0,0 +1,43 @@ +/* + * Common device infrastructure for devices in the virtual css + * + * Copyright 2016 IBM Corp. + * Author(s): Jing Liu <liujbjl@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#ifndef HW_S390X_CCW_DEVICE_H +#define HW_S390X_CCW_DEVICE_H +#include "qom/object.h" +#include "hw/qdev-core.h" +#include "hw/s390x/css.h" + +typedef struct CcwDevice { + DeviceState parent_obj; + SubchDev *sch; + /* <cssid>.<ssid>.<device number> */ + CssDevId bus_id; +} CcwDevice; + +typedef struct CCWDeviceClass { + DeviceClass parent_class; + void (*unplug)(HotplugHandler *, DeviceState *, Error **); +} CCWDeviceClass; + +static inline CcwDevice *to_ccw_dev_fast(DeviceState *d) +{ + return container_of(d, CcwDevice, parent_obj); +} + +#define TYPE_CCW_DEVICE "ccw-device" + +#define CCW_DEVICE(obj) OBJECT_CHECK(CcwDevice, (obj), TYPE_CCW_DEVICE) +#define CCW_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(CCWDeviceClass, (obj), TYPE_CCW_DEVICE) +#define CCW_DEVICE_CLASS(klass) \ + OBJECT_CLASS_CHECK(CCWDeviceClass, (klass), TYPE_CCW_DEVICE) + +#endif diff --git a/hw/s390x/css-bridge.c b/hw/s390x/css-bridge.c new file mode 100644 index 000000000..9a7f7ee60 --- /dev/null +++ b/hw/s390x/css-bridge.c @@ -0,0 +1,148 @@ +/* + * css bridge implementation + * + * Copyright 2012,2016 IBM Corp. + * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> + * Pierre Morel <pmorel@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/hotplug.h" +#include "hw/sysbus.h" +#include "qemu/bitops.h" +#include "hw/s390x/css.h" +#include "ccw-device.h" +#include "hw/s390x/css-bridge.h" + +/* + * Invoke device-specific unplug handler, disable the subchannel + * (including sending a channel report to the guest) and remove the + * device from the virtual css bus. + */ +static void ccw_device_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + CcwDevice *ccw_dev = CCW_DEVICE(dev); + CCWDeviceClass *k = CCW_DEVICE_GET_CLASS(ccw_dev); + SubchDev *sch = ccw_dev->sch; + Error *err = NULL; + + if (k->unplug) { + k->unplug(hotplug_dev, dev, &err); + if (err) { + error_propagate(errp, err); + return; + } + } + + /* + * We should arrive here only for device_del, since we don't support + * direct hot(un)plug of channels. + */ + assert(sch != NULL); + /* Subchannel is now disabled and no longer valid. */ + sch->curr_status.pmcw.flags &= ~(PMCW_FLAGS_MASK_ENA | + PMCW_FLAGS_MASK_DNV); + + css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, 1, 0); + + object_unparent(OBJECT(dev)); +} + +static void virtual_css_bus_reset(BusState *qbus) +{ + /* This should actually be modelled via the generic css */ + css_reset(); +} + +static char *virtual_css_bus_get_dev_path(DeviceState *dev) +{ + CcwDevice *ccw_dev = CCW_DEVICE(dev); + SubchDev *sch = ccw_dev->sch; + VirtualCssBridge *bridge = + VIRTUAL_CSS_BRIDGE(qdev_get_parent_bus(dev)->parent); + + /* + * We can't provide a dev path for backward compatibility on + * older machines, as it is visible in the migration stream. + */ + return bridge->css_dev_path ? + g_strdup_printf("/%02x.%1x.%04x", sch->cssid, sch->ssid, sch->devno) : + NULL; +} + +static void virtual_css_bus_class_init(ObjectClass *klass, void *data) +{ + BusClass *k = BUS_CLASS(klass); + + k->reset = virtual_css_bus_reset; + k->get_dev_path = virtual_css_bus_get_dev_path; +} + +static const TypeInfo virtual_css_bus_info = { + .name = TYPE_VIRTUAL_CSS_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(VirtualCssBus), + .class_init = virtual_css_bus_class_init, +}; + +VirtualCssBus *virtual_css_bus_init(void) +{ + VirtualCssBus *cbus; + BusState *bus; + DeviceState *dev; + + /* Create bridge device */ + dev = qdev_create(NULL, TYPE_VIRTUAL_CSS_BRIDGE); + qdev_init_nofail(dev); + + /* Create bus on bridge device */ + bus = qbus_create(TYPE_VIRTUAL_CSS_BUS, dev, "virtual-css"); + cbus = VIRTUAL_CSS_BUS(bus); + + /* Enable hotplugging */ + qbus_set_hotplug_handler(bus, dev, &error_abort); + + return cbus; + } + +/***************** Virtual-css Bus Bridge Device ********************/ + +static Property virtual_css_bridge_properties[] = { + DEFINE_PROP_BOOL("css_dev_path", VirtualCssBridge, css_dev_path, + true), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtual_css_bridge_class_init(ObjectClass *klass, void *data) +{ + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + hc->unplug = ccw_device_unplug; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); + dc->props = virtual_css_bridge_properties; +} + +static const TypeInfo virtual_css_bridge_info = { + .name = TYPE_VIRTUAL_CSS_BRIDGE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(VirtualCssBridge), + .class_init = virtual_css_bridge_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + } +}; + +static void virtual_css_register(void) +{ + type_register_static(&virtual_css_bridge_info); + type_register_static(&virtual_css_bus_info); +} + +type_init(virtual_css_register) diff --git a/hw/s390x/css.c b/hw/s390x/css.c index 3a1d91958..bb8e4be33 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -10,12 +10,14 @@ */ #include "qemu/osdep.h" -#include <hw/qdev.h> +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "hw/qdev.h" #include "qemu/bitops.h" #include "exec/address-spaces.h" #include "cpu.h" -#include "ioinst.h" -#include "css.h" +#include "hw/s390x/ioinst.h" +#include "hw/s390x/css.h" #include "trace.h" #include "hw/s390x/s390_flic.h" @@ -192,12 +194,46 @@ out: return ret; } -uint16_t css_build_subchannel_id(SubchDev *sch) +static void css_clear_io_interrupt(uint16_t subchannel_id, + uint16_t subchannel_nr) +{ + Error *err = NULL; + static bool no_clear_irq; + S390FLICState *fs = s390_get_flic(); + S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); + int r; + + if (unlikely(no_clear_irq)) { + return; + } + r = fsc->clear_io_irq(fs, subchannel_id, subchannel_nr); + switch (r) { + case 0: + break; + case -ENOSYS: + no_clear_irq = true; + /* + * Ignore unavailability, as the user can't do anything + * about it anyway. + */ + break; + default: + error_setg_errno(&err, -r, "unexpected error condition"); + error_propagate(&error_abort, err); + } +} + +static inline uint16_t css_do_build_subchannel_id(uint8_t cssid, uint8_t ssid) { if (channel_subsys.max_cssid > 0) { - return (sch->cssid << 8) | (1 << 3) | (sch->ssid << 1) | 1; + return (cssid << 8) | (1 << 3) | (ssid << 1) | 1; } - return (sch->ssid << 1) | 1; + return (ssid << 1) | 1; +} + +uint16_t css_build_subchannel_id(SubchDev *sch) +{ + return css_do_build_subchannel_id(sch->cssid, sch->ssid); } static void css_inject_io_interrupt(SubchDev *sch) @@ -475,6 +511,7 @@ static void sch_handle_start_func(SubchDev *sch, ORB *orb) path = 0x80; if (!(s->ctrl & SCSW_ACTL_SUSP)) { + /* Start Function triggered via ssch, i.e. we have an ORB */ s->cstat = 0; s->dstat = 0; /* Look at the orb and try to execute the channel program. */ @@ -488,9 +525,12 @@ static void sch_handle_start_func(SubchDev *sch, ORB *orb) return; } sch->ccw_fmt_1 = !!(orb->ctrl0 & ORB_CTRL0_MASK_FMT); + s->flags |= (sch->ccw_fmt_1) ? SCSW_FLAGS_MASK_FMT : 0; sch->ccw_no_data_cnt = 0; suspend_allowed = !!(orb->ctrl0 & ORB_CTRL0_MASK_SPND); } else { + /* Start Function resumed via rsch, i.e. we don't have an + * ORB */ s->ctrl &= ~(SCSW_ACTL_SUSP | SCSW_ACTL_RESUME_PEND); /* The channel program had been suspended before. */ suspend_allowed = true; @@ -573,6 +613,7 @@ static void do_subchannel_work(SubchDev *sch, ORB *orb) } else if (s->ctrl & SCSW_FCTL_HALT_FUNC) { sch_handle_halt_func(sch); } else if (s->ctrl & SCSW_FCTL_START_FUNC) { + /* Triggered by both ssch and rsch. */ sch_handle_start_func(sch, orb); } else { /* Cannot happen. */ @@ -1304,6 +1345,116 @@ SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid, uint16_t schid) return channel_subsys.css[real_cssid]->sch_set[ssid]->sch[schid]; } +/** + * Return free device number in subchannel set. + * + * Return index of the first free device number in the subchannel set + * identified by @p cssid and @p ssid, beginning the search at @p + * start and wrapping around at MAX_DEVNO. Return a value exceeding + * MAX_SCHID if there are no free device numbers in the subchannel + * set. + */ +static uint32_t css_find_free_devno(uint8_t cssid, uint8_t ssid, + uint16_t start) +{ + uint32_t round; + + for (round = 0; round <= MAX_DEVNO; round++) { + uint16_t devno = (start + round) % MAX_DEVNO; + + if (!css_devno_used(cssid, ssid, devno)) { + return devno; + } + } + return MAX_DEVNO + 1; +} + +/** + * Return first free subchannel (id) in subchannel set. + * + * Return index of the first free subchannel in the subchannel set + * identified by @p cssid and @p ssid, if there is any. Return a value + * exceeding MAX_SCHID if there are no free subchannels in the + * subchannel set. + */ +static uint32_t css_find_free_subch(uint8_t cssid, uint8_t ssid) +{ + uint32_t schid; + + for (schid = 0; schid <= MAX_SCHID; schid++) { + if (!css_find_subch(1, cssid, ssid, schid)) { + return schid; + } + } + return MAX_SCHID + 1; +} + +/** + * Return first free subchannel (id) in subchannel set for a device number + * + * Verify the device number @p devno is not used yet in the subchannel + * set identified by @p cssid and @p ssid. Set @p schid to the index + * of the first free subchannel in the subchannel set, if there is + * any. Return true if everything succeeded and false otherwise. + */ +static bool css_find_free_subch_for_devno(uint8_t cssid, uint8_t ssid, + uint16_t devno, uint16_t *schid, + Error **errp) +{ + uint32_t free_schid; + + assert(schid); + if (css_devno_used(cssid, ssid, devno)) { + error_setg(errp, "Device %x.%x.%04x already exists", + cssid, ssid, devno); + return false; + } + free_schid = css_find_free_subch(cssid, ssid); + if (free_schid > MAX_SCHID) { + error_setg(errp, "No free subchannel found for %x.%x.%04x", + cssid, ssid, devno); + return false; + } + *schid = free_schid; + return true; +} + +/** + * Return first free subchannel (id) and device number + * + * Locate the first free subchannel and first free device number in + * any of the subchannel sets of the channel subsystem identified by + * @p cssid. Return false if no free subchannel / device number could + * be found. Otherwise set @p ssid, @p devno and @p schid to identify + * the available subchannel and device number and return true. + * + * May modify @p ssid, @p devno and / or @p schid even if no free + * subchannel / device number could be found. + */ +static bool css_find_free_subch_and_devno(uint8_t cssid, uint8_t *ssid, + uint16_t *devno, uint16_t *schid, + Error **errp) +{ + uint32_t free_schid, free_devno; + + assert(ssid && devno && schid); + for (*ssid = 0; *ssid <= MAX_SSID; (*ssid)++) { + free_schid = css_find_free_subch(cssid, *ssid); + if (free_schid > MAX_SCHID) { + continue; + } + free_devno = css_find_free_devno(cssid, *ssid, free_schid); + if (free_devno > MAX_DEVNO) { + continue; + } + *schid = free_schid; + *devno = free_devno; + return true; + } + error_setg(errp, "Virtual channel subsystem is full!"); + return false; +} + bool css_subch_visible(SubchDev *sch) { if (sch->ssid > channel_subsys.max_ssid) { @@ -1429,6 +1580,8 @@ void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid, css_queue_crw(CRW_RSC_SUBCH, CRW_ERC_IPI, 0, (guest_cssid << 8) | (ssid << 4)); } + /* RW_ERC_IPI --> clear pending interrupts */ + css_clear_io_interrupt(css_do_build_subchannel_id(cssid, ssid), schid); } void css_generate_chp_crws(uint8_t cssid, uint8_t chpid) @@ -1644,3 +1797,116 @@ void css_reset(void) channel_subsys.max_cssid = 0; channel_subsys.max_ssid = 0; } + +static void get_css_devid(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + CssDevId *dev_id = qdev_get_prop_ptr(dev, prop); + char buffer[] = "xx.x.xxxx"; + char *p = buffer; + int r; + + if (dev_id->valid) { + + r = snprintf(buffer, sizeof(buffer), "%02x.%1x.%04x", dev_id->cssid, + dev_id->ssid, dev_id->devid); + assert(r == sizeof(buffer) - 1); + + /* drop leading zero */ + if (dev_id->cssid <= 0xf) { + p++; + } + } else { + snprintf(buffer, sizeof(buffer), "<unset>"); + } + + visit_type_str(v, name, &p, errp); +} + +/* + * parse <cssid>.<ssid>.<devid> and assert valid range for cssid/ssid + */ +static void set_css_devid(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + CssDevId *dev_id = qdev_get_prop_ptr(dev, prop); + Error *local_err = NULL; + char *str; + int num, n1, n2; + unsigned int cssid, ssid, devid; + + if (dev->realized) { + qdev_prop_set_after_realize(dev, name, errp); + return; + } + + visit_type_str(v, name, &str, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + num = sscanf(str, "%2x.%1x%n.%4x%n", &cssid, &ssid, &n1, &devid, &n2); + if (num != 3 || (n2 - n1) != 5 || strlen(str) != n2) { + error_set_from_qdev_prop_error(errp, EINVAL, dev, prop, str); + goto out; + } + if ((cssid > MAX_CSSID) || (ssid > MAX_SSID)) { + error_setg(errp, "Invalid cssid or ssid: cssid %x, ssid %x", + cssid, ssid); + goto out; + } + + dev_id->cssid = cssid; + dev_id->ssid = ssid; + dev_id->devid = devid; + dev_id->valid = true; + +out: + g_free(str); +} + +PropertyInfo css_devid_propinfo = { + .name = "str", + .description = "Identifier of an I/O device in the channel " + "subsystem, example: fe.1.23ab", + .get = get_css_devid, + .set = set_css_devid, +}; + +SubchDev *css_create_virtual_sch(CssDevId bus_id, Error **errp) +{ + uint16_t schid = 0; + SubchDev *sch; + + if (bus_id.valid) { + /* Enforce use of virtual cssid. */ + if (bus_id.cssid != VIRTUAL_CSSID) { + error_setg(errp, "cssid %hhx not valid for virtual devices", + bus_id.cssid); + return NULL; + } + if (!css_find_free_subch_for_devno(bus_id.cssid, bus_id.ssid, + bus_id.devid, &schid, errp)) { + return NULL; + } + } else { + bus_id.cssid = VIRTUAL_CSSID; + if (!css_find_free_subch_and_devno(bus_id.cssid, &bus_id.ssid, + &bus_id.devid, &schid, errp)) { + return NULL; + } + } + + sch = g_malloc0(sizeof(*sch)); + sch->cssid = bus_id.cssid; + sch->ssid = bus_id.ssid; + sch->devno = bus_id.devid; + sch->schid = schid; + css_subch_assign(sch->cssid, sch->ssid, schid, sch->devno, sch); + return sch; +} diff --git a/hw/s390x/css.h b/hw/s390x/css.h deleted file mode 100644 index a320eea59..000000000 --- a/hw/s390x/css.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Channel subsystem structures and definitions. - * - * Copyright 2012 IBM Corp. - * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> - * - * This work is licensed under the terms of the GNU GPL, version 2 or (at - * your option) any later version. See the COPYING file in the top-level - * directory. - */ - -#ifndef CSS_H -#define CSS_H - -#include "hw/s390x/adapter.h" -#include "hw/s390x/s390_flic.h" -#include "ioinst.h" - -/* Channel subsystem constants. */ -#define MAX_SCHID 65535 -#define MAX_SSID 3 -#define MAX_CSSID 254 /* 255 is reserved */ -#define MAX_CHPID 255 - -#define MAX_CIWS 62 - -typedef struct CIW { - uint8_t type; - uint8_t command; - uint16_t count; -} QEMU_PACKED CIW; - -typedef struct SenseId { - /* common part */ - uint8_t reserved; /* always 0x'FF' */ - uint16_t cu_type; /* control unit type */ - uint8_t cu_model; /* control unit model */ - uint16_t dev_type; /* device type */ - uint8_t dev_model; /* device model */ - uint8_t unused; /* padding byte */ - /* extended part */ - CIW ciw[MAX_CIWS]; /* variable # of CIWs */ -} QEMU_PACKED SenseId; - -/* Channel measurements, from linux/drivers/s390/cio/cmf.c. */ -typedef struct CMB { - uint16_t ssch_rsch_count; - uint16_t sample_count; - uint32_t device_connect_time; - uint32_t function_pending_time; - uint32_t device_disconnect_time; - uint32_t control_unit_queuing_time; - uint32_t device_active_only_time; - uint32_t reserved[2]; -} QEMU_PACKED CMB; - -typedef struct CMBE { - uint32_t ssch_rsch_count; - uint32_t sample_count; - uint32_t device_connect_time; - uint32_t function_pending_time; - uint32_t device_disconnect_time; - uint32_t control_unit_queuing_time; - uint32_t device_active_only_time; - uint32_t device_busy_time; - uint32_t initial_command_response_time; - uint32_t reserved[7]; -} QEMU_PACKED CMBE; - -struct SubchDev { - /* channel-subsystem related things: */ - uint8_t cssid; - uint8_t ssid; - uint16_t schid; - uint16_t devno; - SCHIB curr_status; - uint8_t sense_data[32]; - hwaddr channel_prog; - CCW1 last_cmd; - bool last_cmd_valid; - bool ccw_fmt_1; - bool thinint_active; - uint8_t ccw_no_data_cnt; - /* transport-provided data: */ - int (*ccw_cb) (SubchDev *, CCW1); - void (*disable_cb)(SubchDev *); - SenseId id; - void *driver_data; -}; - -typedef struct IndAddr { - hwaddr addr; - uint64_t map; - unsigned long refcnt; - int len; - QTAILQ_ENTRY(IndAddr) sibling; -} IndAddr; - -IndAddr *get_indicator(hwaddr ind_addr, int len); -void release_indicator(AdapterInfo *adapter, IndAddr *indicator); -int map_indicator(AdapterInfo *adapter, IndAddr *indicator); - -typedef SubchDev *(*css_subch_cb_func)(uint8_t m, uint8_t cssid, uint8_t ssid, - uint16_t schid); -void subch_device_save(SubchDev *s, QEMUFile *f); -int subch_device_load(SubchDev *s, QEMUFile *f); -int css_create_css_image(uint8_t cssid, bool default_image); -bool css_devno_used(uint8_t cssid, uint8_t ssid, uint16_t devno); -void css_subch_assign(uint8_t cssid, uint8_t ssid, uint16_t schid, - uint16_t devno, SubchDev *sch); -void css_sch_build_virtual_schib(SubchDev *sch, uint8_t chpid, uint8_t type); -uint16_t css_build_subchannel_id(SubchDev *sch); -void css_reset(void); -void css_reset_sch(SubchDev *sch); -void css_queue_crw(uint8_t rsc, uint8_t erc, int chain, uint16_t rsid); -void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid, - int hotplugged, int add); -void css_generate_chp_crws(uint8_t cssid, uint8_t chpid); -void css_generate_css_crws(uint8_t cssid); -void css_clear_sei_pending(void); -void css_adapter_interrupt(uint8_t isc); - -#define CSS_IO_ADAPTER_VIRTIO 1 -int css_register_io_adapter(uint8_t type, uint8_t isc, bool swap, - bool maskable, uint32_t *id); -#endif diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index f10420027..2e2664f22 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -30,6 +30,24 @@ #define ZIPL_IMAGE_START 0x009000UL #define IPL_PSW_MASK (PSW_MASK_32 | PSW_MASK_64) +static bool iplb_extended_needed(void *opaque) +{ + S390IPLState *ipl = S390_IPL(object_resolve_path(TYPE_S390_IPL, NULL)); + + return ipl->iplbext_migration; +} + +static const VMStateDescription vmstate_iplb_extended = { + .name = "ipl/iplb_extended", + .version_id = 0, + .minimum_version_id = 0, + .needed = iplb_extended_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT8_ARRAY(reserved_ext, IplParameterBlock, 4096 - 200), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_iplb = { .name = "ipl/iplb", .version_id = 0, @@ -39,6 +57,10 @@ static const VMStateDescription vmstate_iplb = { VMSTATE_UINT16(devno, IplParameterBlock), VMSTATE_UINT8_ARRAY(reserved2, IplParameterBlock, 88), VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription*[]) { + &vmstate_iplb_extended, + NULL } }; @@ -47,8 +69,8 @@ static const VMStateDescription vmstate_ipl = { .version_id = 0, .minimum_version_id = 0, .fields = (VMStateField[]) { - VMSTATE_UINT64(start_addr, S390IPLState), - VMSTATE_UINT64(bios_start_addr, S390IPLState), + VMSTATE_UINT64(compat_start_addr, S390IPLState), + VMSTATE_UINT64(compat_bios_start_addr, S390IPLState), VMSTATE_STRUCT(iplb, S390IPLState, 0, vmstate_iplb, IplParameterBlock), VMSTATE_BOOL(iplb_valid, S390IPLState), VMSTATE_UINT8(cssid, S390IPLState), @@ -107,7 +129,7 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) /* Adjust ELF start address to final location */ ipl->bios_start_addr += fwbase; } else { - /* Try to load non-ELF file (e.g. s390-ccw.img) */ + /* Try to load non-ELF file */ bios_size = load_image_targphys(bios_filename, ZIPL_IMAGE_START, 4096); ipl->bios_start_addr = ZIPL_IMAGE_START; @@ -170,6 +192,13 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) stq_p(rom_ptr(INITRD_PARM_SIZE), initrd_size); } } + /* + * Don't ever use the migrated values, they could come from a different + * BIOS and therefore don't work. But still migrate the values, so + * QEMUs relying on it don't break. + */ + ipl->compat_start_addr = ipl->start_addr; + ipl->compat_bios_start_addr = ipl->bios_start_addr; qemu_register_reset(qdev_reset_all_fn, dev); error: error_propagate(errp, err); @@ -181,46 +210,52 @@ static Property s390_ipl_properties[] = { DEFINE_PROP_STRING("cmdline", S390IPLState, cmdline), DEFINE_PROP_STRING("firmware", S390IPLState, firmware), DEFINE_PROP_BOOL("enforce_bios", S390IPLState, enforce_bios, false), + DEFINE_PROP_BOOL("iplbext_migration", S390IPLState, iplbext_migration, + true), DEFINE_PROP_END_OF_LIST(), }; -/* - * In addition to updating the iplstate, this function returns: - * - 0 if system was ipled with external kernel - * - -1 if no valid boot device was found - * - ccw id of the boot device otherwise - */ -static uint64_t s390_update_iplstate(S390IPLState *ipl) +static bool s390_gen_initial_iplb(S390IPLState *ipl) { DeviceState *dev_st; - if (ipl->iplb_valid) { - ipl->cssid = 0; - ipl->ssid = 0; - ipl->devno = ipl->iplb.devno; - goto out; - } - - if (ipl->kernel) { - return 0; - } - dev_st = get_boot_device(0); if (dev_st) { - VirtioCcwDevice *ccw_dev = (VirtioCcwDevice *) object_dynamic_cast( - OBJECT(qdev_get_parent_bus(dev_st)->parent), + VirtioCcwDevice *virtio_ccw_dev = (VirtioCcwDevice *) + object_dynamic_cast(OBJECT(qdev_get_parent_bus(dev_st)->parent), TYPE_VIRTIO_CCW_DEVICE); - if (ccw_dev) { - ipl->cssid = ccw_dev->sch->cssid; - ipl->ssid = ccw_dev->sch->ssid; - ipl->devno = ccw_dev->sch->devno; - goto out; + SCSIDevice *sd = (SCSIDevice *) object_dynamic_cast(OBJECT(dev_st), + TYPE_SCSI_DEVICE); + if (virtio_ccw_dev) { + CcwDevice *ccw_dev = CCW_DEVICE(virtio_ccw_dev); + + ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN); + ipl->iplb.blk0_len = + cpu_to_be32(S390_IPLB_MIN_CCW_LEN - S390_IPLB_HEADER_LEN); + ipl->iplb.pbt = S390_IPL_TYPE_CCW; + ipl->iplb.ccw.devno = cpu_to_be16(ccw_dev->sch->devno); + ipl->iplb.ccw.ssid = ccw_dev->sch->ssid & 3; + return true; + } else if (sd) { + SCSIBus *bus = scsi_bus_from_device(sd); + VirtIOSCSI *vdev = container_of(bus, VirtIOSCSI, bus); + VirtIOSCSICcw *scsi_ccw = container_of(vdev, VirtIOSCSICcw, vdev); + CcwDevice *ccw_dev = CCW_DEVICE(scsi_ccw); + + ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN); + ipl->iplb.blk0_len = + cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN - S390_IPLB_HEADER_LEN); + ipl->iplb.pbt = S390_IPL_TYPE_QEMU_SCSI; + ipl->iplb.scsi.lun = cpu_to_be32(sd->lun); + ipl->iplb.scsi.target = cpu_to_be16(sd->id); + ipl->iplb.scsi.channel = cpu_to_be16(sd->channel); + ipl->iplb.scsi.devno = cpu_to_be16(ccw_dev->sch->devno); + ipl->iplb.scsi.ssid = ccw_dev->sch->ssid & 3; + return true; } } - return -1; -out: - return (uint32_t) (ipl->cssid << 24 | ipl->ssid << 16 | ipl->devno); + return false; } void s390_ipl_update_diag308(IplParameterBlock *iplb) @@ -258,7 +293,9 @@ void s390_ipl_prepare_cpu(S390CPU *cpu) if (!ipl->kernel || ipl->iplb_valid) { cpu->env.psw.addr = ipl->bios_start_addr; - cpu->env.regs[7] = s390_update_iplstate(ipl); + if (!ipl->iplb_valid) { + ipl->iplb_valid = s390_gen_initial_iplb(ipl); + } } } @@ -268,6 +305,7 @@ static void s390_ipl_reset(DeviceState *dev) if (!ipl->reipl_requested) { ipl->iplb_valid = false; + memset(&ipl->iplb, 0, sizeof(IplParameterBlock)); } ipl->reipl_requested = false; } diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h index 6b48ed7b9..c89109585 100644 --- a/hw/s390x/ipl.h +++ b/hw/s390x/ipl.h @@ -15,11 +15,71 @@ #include "hw/qdev.h" #include "cpu.h" -typedef struct IplParameterBlock { - uint8_t reserved1[110]; - uint16_t devno; - uint8_t reserved2[88]; -} IplParameterBlock; +struct IplBlockCcw { + uint8_t reserved0[85]; + uint8_t ssid; + uint16_t devno; + uint8_t vm_flags; + uint8_t reserved3[3]; + uint32_t vm_parm_len; + uint8_t nss_name[8]; + uint8_t vm_parm[64]; + uint8_t reserved4[8]; +} QEMU_PACKED; +typedef struct IplBlockCcw IplBlockCcw; + +struct IplBlockFcp { + uint8_t reserved1[305 - 1]; + uint8_t opt; + uint8_t reserved2[3]; + uint16_t reserved3; + uint16_t devno; + uint8_t reserved4[4]; + uint64_t wwpn; + uint64_t lun; + uint32_t bootprog; + uint8_t reserved5[12]; + uint64_t br_lba; + uint32_t scp_data_len; + uint8_t reserved6[260]; + uint8_t scp_data[]; +} QEMU_PACKED; +typedef struct IplBlockFcp IplBlockFcp; + +struct IplBlockQemuScsi { + uint32_t lun; + uint16_t target; + uint16_t channel; + uint8_t reserved0[77]; + uint8_t ssid; + uint16_t devno; +} QEMU_PACKED; +typedef struct IplBlockQemuScsi IplBlockQemuScsi; + +union IplParameterBlock { + struct { + uint32_t len; + uint8_t reserved0[3]; + uint8_t version; + uint32_t blk0_len; + uint8_t pbt; + uint8_t flags; + uint16_t reserved01; + uint8_t loadparm[8]; + union { + IplBlockCcw ccw; + IplBlockFcp fcp; + IplBlockQemuScsi scsi; + }; + } QEMU_PACKED; + struct { + uint8_t reserved1[110]; + uint16_t devno; + uint8_t reserved2[88]; + uint8_t reserved_ext[4096 - 200]; + } QEMU_PACKED; +} QEMU_PACKED; +typedef union IplParameterBlock IplParameterBlock; void s390_ipl_update_diag308(IplParameterBlock *iplb); void s390_ipl_prepare_cpu(S390CPU *cpu); @@ -33,7 +93,9 @@ struct S390IPLState { /*< private >*/ DeviceState parent_obj; uint64_t start_addr; + uint64_t compat_start_addr; uint64_t bios_start_addr; + uint64_t compat_bios_start_addr; bool enforce_bios; IplParameterBlock iplb; bool iplb_valid; @@ -47,7 +109,34 @@ struct S390IPLState { uint8_t cssid; uint8_t ssid; uint16_t devno; + bool iplbext_migration; }; typedef struct S390IPLState S390IPLState; +#define S390_IPL_TYPE_FCP 0x00 +#define S390_IPL_TYPE_CCW 0x02 +#define S390_IPL_TYPE_QEMU_SCSI 0xff + +#define S390_IPLB_HEADER_LEN 8 +#define S390_IPLB_MIN_CCW_LEN 200 +#define S390_IPLB_MIN_FCP_LEN 384 +#define S390_IPLB_MIN_QEMU_SCSI_LEN 200 + +static inline bool iplb_valid_len(IplParameterBlock *iplb) +{ + return be32_to_cpu(iplb->len) <= sizeof(IplParameterBlock); +} + +static inline bool iplb_valid_ccw(IplParameterBlock *iplb) +{ + return be32_to_cpu(iplb->len) >= S390_IPLB_MIN_CCW_LEN && + iplb->pbt == S390_IPL_TYPE_CCW; +} + +static inline bool iplb_valid_fcp(IplParameterBlock *iplb) +{ + return be32_to_cpu(iplb->len) >= S390_IPLB_MIN_FCP_LEN && + iplb->pbt == S390_IPL_TYPE_FCP; +} + #endif diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 918b58543..9c1c04e59 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -12,12 +12,15 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/visitor.h" #include "qemu-common.h" #include "cpu.h" #include "s390-pci-bus.h" -#include <hw/pci/pci_bus.h> -#include <hw/pci/msi.h> -#include <qemu/error-report.h> +#include "s390-pci-inst.h" +#include "hw/pci/pci_bus.h" +#include "hw/pci/msi.h" +#include "qemu/error-report.h" /* #define DEBUG_S390PCI_BUS */ #ifdef DEBUG_S390PCI_BUS @@ -28,6 +31,19 @@ do { } while (0) #endif +static S390pciState *s390_get_phb(void) +{ + static S390pciState *phb; + + if (!phb) { + phb = S390_PCI_HOST_BRIDGE( + object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); + assert(phb != NULL); + } + + return phb; +} + int chsc_sei_nt2_get_event(void *res) { ChscSeiNt2Res *nt2_res = (ChscSeiNt2Res *)res; @@ -35,12 +51,7 @@ int chsc_sei_nt2_get_event(void *res) PciCcdfErr *eccdf; int rc = 1; SeiContainer *sei_cont; - S390pciState *s = S390_PCI_HOST_BRIDGE( - object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); - - if (!s) { - return rc; - } + S390pciState *s = s390_get_phb(); sei_cont = QTAILQ_FIRST(&s->pending_sei); if (sei_cont) { @@ -75,30 +86,40 @@ int chsc_sei_nt2_get_event(void *res) int chsc_sei_nt2_have_event(void) { - S390pciState *s = S390_PCI_HOST_BRIDGE( - object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); + S390pciState *s = s390_get_phb(); - if (!s) { - return 0; + return !QTAILQ_EMPTY(&s->pending_sei); +} + +S390PCIBusDevice *s390_pci_find_next_avail_dev(S390PCIBusDevice *pbdev) +{ + int idx = 0; + S390PCIBusDevice *dev = NULL; + S390pciState *s = s390_get_phb(); + + if (pbdev) { + idx = (pbdev->fh & FH_MASK_INDEX) + 1; } - return !QTAILQ_EMPTY(&s->pending_sei); + for (; idx < PCI_SLOT_MAX; idx++) { + dev = s->pbdev[idx]; + if (dev && dev->state != ZPCI_FS_RESERVED) { + return dev; + } + } + + return NULL; } S390PCIBusDevice *s390_pci_find_dev_by_fid(uint32_t fid) { S390PCIBusDevice *pbdev; int i; - S390pciState *s = S390_PCI_HOST_BRIDGE( - object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); - - if (!s) { - return NULL; - } + S390pciState *s = s390_get_phb(); for (i = 0; i < PCI_SLOT_MAX; i++) { - pbdev = &s->pbdev[i]; - if ((pbdev->fh != 0) && (pbdev->fid == fid)) { + pbdev = s->pbdev[i]; + if (pbdev && pbdev->fid == fid) { return pbdev; } } @@ -106,80 +127,117 @@ S390PCIBusDevice *s390_pci_find_dev_by_fid(uint32_t fid) return NULL; } -void s390_pci_sclp_configure(int configure, SCCB *sccb) +void s390_pci_sclp_configure(SCCB *sccb) { PciCfgSccb *psccb = (PciCfgSccb *)sccb; S390PCIBusDevice *pbdev = s390_pci_find_dev_by_fid(be32_to_cpu(psccb->aid)); uint16_t rc; - if (pbdev) { - if ((configure == 1 && pbdev->configured == true) || - (configure == 0 && pbdev->configured == false)) { - rc = SCLP_RC_NO_ACTION_REQUIRED; - } else { - pbdev->configured = !pbdev->configured; - rc = SCLP_RC_NORMAL_COMPLETION; - } - } else { - DPRINTF("sclp config %d no dev found\n", configure); + if (be16_to_cpu(sccb->h.length) < 16) { + rc = SCLP_RC_INSUFFICIENT_SCCB_LENGTH; + goto out; + } + + if (!pbdev) { + DPRINTF("sclp config no dev found\n"); rc = SCLP_RC_ADAPTER_ID_NOT_RECOGNIZED; + goto out; } + switch (pbdev->state) { + case ZPCI_FS_RESERVED: + rc = SCLP_RC_ADAPTER_IN_RESERVED_STATE; + break; + case ZPCI_FS_STANDBY: + pbdev->state = ZPCI_FS_DISABLED; + rc = SCLP_RC_NORMAL_COMPLETION; + break; + default: + rc = SCLP_RC_NO_ACTION_REQUIRED; + } +out: psccb->header.response_code = cpu_to_be16(rc); } -static uint32_t s390_pci_get_pfid(PCIDevice *pdev) +void s390_pci_sclp_deconfigure(SCCB *sccb) { - return PCI_SLOT(pdev->devfn); -} + PciCfgSccb *psccb = (PciCfgSccb *)sccb; + S390PCIBusDevice *pbdev = s390_pci_find_dev_by_fid(be32_to_cpu(psccb->aid)); + uint16_t rc; -static uint32_t s390_pci_get_pfh(PCIDevice *pdev) -{ - return PCI_SLOT(pdev->devfn) | FH_VIRT; + if (be16_to_cpu(sccb->h.length) < 16) { + rc = SCLP_RC_INSUFFICIENT_SCCB_LENGTH; + goto out; + } + + if (!pbdev) { + DPRINTF("sclp deconfig no dev found\n"); + rc = SCLP_RC_ADAPTER_ID_NOT_RECOGNIZED; + goto out; + } + + switch (pbdev->state) { + case ZPCI_FS_RESERVED: + rc = SCLP_RC_ADAPTER_IN_RESERVED_STATE; + break; + case ZPCI_FS_STANDBY: + rc = SCLP_RC_NO_ACTION_REQUIRED; + break; + default: + if (pbdev->summary_ind) { + pci_dereg_irqs(pbdev); + } + if (pbdev->iommu_enabled) { + pci_dereg_ioat(pbdev); + } + pbdev->state = ZPCI_FS_STANDBY; + rc = SCLP_RC_NORMAL_COMPLETION; + + if (pbdev->release_timer) { + qdev_unplug(DEVICE(pbdev->pdev), NULL); + } + } +out: + psccb->header.response_code = cpu_to_be16(rc); } -S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx) +static S390PCIBusDevice *s390_pci_find_dev_by_uid(uint16_t uid) { - S390PCIBusDevice *pbdev; int i; - int j = 0; - S390pciState *s = S390_PCI_HOST_BRIDGE( - object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); - - if (!s) { - return NULL; - } + S390PCIBusDevice *pbdev; + S390pciState *s = s390_get_phb(); for (i = 0; i < PCI_SLOT_MAX; i++) { - pbdev = &s->pbdev[i]; - - if (pbdev->fh == 0) { + pbdev = s->pbdev[i]; + if (!pbdev) { continue; } - if (j == idx) { + if (pbdev->uid == uid) { return pbdev; } - j++; } return NULL; } -S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh) +static S390PCIBusDevice *s390_pci_find_dev_by_target(const char *target) { - S390PCIBusDevice *pbdev; int i; - S390pciState *s = S390_PCI_HOST_BRIDGE( - object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); + S390PCIBusDevice *pbdev; + S390pciState *s = s390_get_phb(); - if (!s || !fh) { + if (!target) { return NULL; } for (i = 0; i < PCI_SLOT_MAX; i++) { - pbdev = &s->pbdev[i]; - if (pbdev->fh == fh) { + pbdev = s->pbdev[i]; + if (!pbdev) { + continue; + } + + if (!strcmp(pbdev->target, target)) { return pbdev; } } @@ -187,16 +245,31 @@ S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh) return NULL; } +S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx) +{ + S390pciState *s = s390_get_phb(); + + return s->pbdev[idx & FH_MASK_INDEX]; +} + +S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh) +{ + S390pciState *s = s390_get_phb(); + S390PCIBusDevice *pbdev; + + pbdev = s->pbdev[fh & FH_MASK_INDEX]; + if (pbdev && pbdev->fh == fh) { + return pbdev; + } + + return NULL; +} + static void s390_pci_generate_event(uint8_t cc, uint16_t pec, uint32_t fh, uint32_t fid, uint64_t faddr, uint32_t e) { SeiContainer *sei_cont; - S390pciState *s = S390_PCI_HOST_BRIDGE( - object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); - - if (!s) { - return; - } + S390pciState *s = s390_get_phb(); sei_cont = g_malloc0(sizeof(SeiContainer)); sei_cont->fh = fh; @@ -216,9 +289,8 @@ static void s390_pci_generate_plug_event(uint16_t pec, uint32_t fh, s390_pci_generate_event(2, pec, fh, fid, 0, 0); } -static void s390_pci_generate_error_event(uint16_t pec, uint32_t fh, - uint32_t fid, uint64_t faddr, - uint32_t e) +void s390_pci_generate_error_event(uint16_t pec, uint32_t fh, uint32_t fid, + uint64_t faddr, uint32_t e) { s390_pci_generate_event(1, pec, fh, fid, faddr, e); } @@ -320,7 +392,14 @@ static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *iommu, hwaddr addr, .perm = IOMMU_NONE, }; - if (!pbdev->configured || !pbdev->pdev || !(pbdev->fh & FH_ENABLED)) { + switch (pbdev->state) { + case ZPCI_FS_ENABLED: + case ZPCI_FS_BLOCKED: + if (!pbdev->iommu_enabled) { + return ret; + } + break; + default: return ret; } @@ -339,30 +418,13 @@ static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *iommu, hwaddr addr, return ret; } - if (!pbdev->g_iota) { - pbdev->error_state = true; - pbdev->lgstg_blocked = true; - s390_pci_generate_error_event(ERR_EVENT_INVALAS, pbdev->fh, pbdev->fid, - addr, 0); - return ret; - } - if (addr < pbdev->pba || addr > pbdev->pal) { - pbdev->error_state = true; - pbdev->lgstg_blocked = true; - s390_pci_generate_error_event(ERR_EVENT_OORANGE, pbdev->fh, pbdev->fid, - addr, 0); return ret; } pte = s390_guest_io_table_walk(s390_pci_get_table_origin(pbdev->g_iota), addr); - if (!pte) { - pbdev->error_state = true; - pbdev->lgstg_blocked = true; - s390_pci_generate_error_event(ERR_EVENT_SERR, pbdev->fh, pbdev->fid, - addr, ERR_EVENT_Q_BIT); return ret; } @@ -388,7 +450,7 @@ static AddressSpace *s390_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) { S390pciState *s = opaque; - return &s->pbdev[PCI_SLOT(devfn)].as; + return &s->iommu[PCI_SLOT(devfn)]->as; } static uint8_t set_ind_atomic(uint64_t ind_loc, uint8_t to_be_set) @@ -416,22 +478,22 @@ static void s390_msi_ctrl_write(void *opaque, hwaddr addr, uint64_t data, { S390PCIBusDevice *pbdev; uint32_t io_int_word; - uint32_t fid = data >> ZPCI_MSI_VEC_BITS; + uint32_t idx = data >> ZPCI_MSI_VEC_BITS; uint32_t vec = data & ZPCI_MSI_VEC_MASK; uint64_t ind_bit; uint32_t sum_bit; uint32_t e = 0; - DPRINTF("write_msix data 0x%" PRIx64 " fid %d vec 0x%x\n", data, fid, vec); + DPRINTF("write_msix data 0x%" PRIx64 " idx %d vec 0x%x\n", data, idx, vec); - pbdev = s390_pci_find_dev_by_fid(fid); + pbdev = s390_pci_find_dev_by_idx(idx); if (!pbdev) { e |= (vec << ERR_EVENT_MVN_OFFSET); - s390_pci_generate_error_event(ERR_EVENT_NOMSI, 0, fid, addr, e); + s390_pci_generate_error_event(ERR_EVENT_NOMSI, idx, 0, addr, e); return; } - if (!(pbdev->fh & FH_ENABLED)) { + if (pbdev->state != ZPCI_FS_ENABLED) { return; } @@ -458,32 +520,33 @@ static const MemoryRegionOps s390_msi_ctrl_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -void s390_pcihost_iommu_configure(S390PCIBusDevice *pbdev, bool enable) +void s390_pci_iommu_enable(S390PCIBusDevice *pbdev) { - pbdev->configured = false; - - if (enable) { - uint64_t size = pbdev->pal - pbdev->pba + 1; - memory_region_init_iommu(&pbdev->iommu_mr, OBJECT(&pbdev->mr), - &s390_iommu_ops, "iommu-s390", size); - memory_region_add_subregion(&pbdev->mr, pbdev->pba, &pbdev->iommu_mr); - } else { - memory_region_del_subregion(&pbdev->mr, &pbdev->iommu_mr); - } + memory_region_init_iommu(&pbdev->iommu_mr, OBJECT(&pbdev->iommu->mr), + &s390_iommu_ops, "iommu-s390", pbdev->pal + 1); + memory_region_add_subregion(&pbdev->iommu->mr, 0, &pbdev->iommu_mr); + pbdev->iommu_enabled = true; +} - pbdev->configured = true; +void s390_pci_iommu_disable(S390PCIBusDevice *pbdev) +{ + memory_region_del_subregion(&pbdev->iommu->mr, &pbdev->iommu_mr); + object_unparent(OBJECT(&pbdev->iommu_mr)); + pbdev->iommu_enabled = false; } static void s390_pcihost_init_as(S390pciState *s) { int i; - S390PCIBusDevice *pbdev; + S390PCIIOMMU *iommu; for (i = 0; i < PCI_SLOT_MAX; i++) { - pbdev = &s->pbdev[i]; - memory_region_init(&pbdev->mr, OBJECT(s), + iommu = g_malloc0(sizeof(S390PCIIOMMU)); + memory_region_init(&iommu->mr, OBJECT(s), "iommu-root-s390", UINT64_MAX); - address_space_init(&pbdev->as, &pbdev->mr, "iommu-pci"); + address_space_init(&iommu->as, &iommu->mr, "iommu-pci"); + + s->iommu[i] = iommu; } memory_region_init_io(&s->msix_notify_mr, OBJECT(s), @@ -510,6 +573,10 @@ static int s390_pcihost_init(SysBusDevice *dev) bus = BUS(b); qbus_set_hotplug_handler(bus, DEVICE(dev), NULL); phb->bus = b; + + s->bus = S390_PCI_BUS(qbus_create(TYPE_S390_PCI_BUS, DEVICE(s), NULL)); + qbus_set_hotplug_handler(BUS(s->bus), DEVICE(s), NULL); + QTAILQ_INIT(&s->pending_sei); return 0; } @@ -542,51 +609,155 @@ static int s390_pcihost_setup_msix(S390PCIBusDevice *pbdev) return 0; } +static S390PCIBusDevice *s390_pci_device_new(const char *target) +{ + DeviceState *dev = NULL; + S390pciState *s = s390_get_phb(); + + dev = qdev_try_create(BUS(s->bus), TYPE_S390_PCI_DEVICE); + if (!dev) { + return NULL; + } + + qdev_prop_set_string(dev, "target", target); + qdev_init_nofail(dev); + + return S390_PCI_DEVICE(dev); +} + static void s390_pcihost_hot_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - PCIDevice *pci_dev = PCI_DEVICE(dev); - S390PCIBusDevice *pbdev; - S390pciState *s = S390_PCI_HOST_BRIDGE(pci_device_root_bus(pci_dev) - ->qbus.parent); + PCIDevice *pdev = NULL; + S390PCIBusDevice *pbdev = NULL; + S390pciState *s = s390_get_phb(); - pbdev = &s->pbdev[PCI_SLOT(pci_dev->devfn)]; + if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { + pdev = PCI_DEVICE(dev); - pbdev->fid = s390_pci_get_pfid(pci_dev); - pbdev->pdev = pci_dev; - pbdev->configured = true; - pbdev->fh = s390_pci_get_pfh(pci_dev); + if (!dev->id) { + /* In the case the PCI device does not define an id */ + /* we generate one based on the PCI address */ + dev->id = g_strdup_printf("auto_%02x:%02x.%01x", + pci_bus_num(pdev->bus), + PCI_SLOT(pdev->devfn), + PCI_FUNC(pdev->devfn)); + } - s390_pcihost_setup_msix(pbdev); + pbdev = s390_pci_find_dev_by_target(dev->id); + if (!pbdev) { + pbdev = s390_pci_device_new(dev->id); + if (!pbdev) { + error_setg(errp, "create zpci device failed"); + } + } - if (dev->hotplugged) { - s390_pci_generate_plug_event(HP_EVENT_RESERVED_TO_STANDBY, - pbdev->fh, pbdev->fid); - s390_pci_generate_plug_event(HP_EVENT_TO_CONFIGURED, - pbdev->fh, pbdev->fid); + if (object_dynamic_cast(OBJECT(dev), "vfio-pci")) { + pbdev->fh |= FH_SHM_VFIO; + } else { + pbdev->fh |= FH_SHM_EMUL; + } + + pbdev->pdev = pdev; + pbdev->iommu = s->iommu[PCI_SLOT(pdev->devfn)]; + pbdev->state = ZPCI_FS_STANDBY; + s390_pcihost_setup_msix(pbdev); + + if (dev->hotplugged) { + s390_pci_generate_plug_event(HP_EVENT_RESERVED_TO_STANDBY, + pbdev->fh, pbdev->fid); + } + } else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) { + int idx; + + pbdev = S390_PCI_DEVICE(dev); + for (idx = 0; idx < PCI_SLOT_MAX; idx++) { + if (!s->pbdev[idx]) { + s->pbdev[idx] = pbdev; + pbdev->fh = idx; + return; + } + } + + error_setg(errp, "no slot for plugging zpci device"); } } +static void s390_pcihost_timer_cb(void *opaque) +{ + S390PCIBusDevice *pbdev = opaque; + + if (pbdev->summary_ind) { + pci_dereg_irqs(pbdev); + } + if (pbdev->iommu_enabled) { + pci_dereg_ioat(pbdev); + } + + pbdev->state = ZPCI_FS_STANDBY; + s390_pci_generate_plug_event(HP_EVENT_CONFIGURED_TO_STBRES, + pbdev->fh, pbdev->fid); + qdev_unplug(DEVICE(pbdev), NULL); +} + static void s390_pcihost_hot_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - PCIDevice *pci_dev = PCI_DEVICE(dev); - S390pciState *s = S390_PCI_HOST_BRIDGE(pci_device_root_bus(pci_dev) - ->qbus.parent); - S390PCIBusDevice *pbdev = &s->pbdev[PCI_SLOT(pci_dev->devfn)]; + int i; + PCIDevice *pci_dev = NULL; + S390PCIBusDevice *pbdev = NULL; + S390pciState *s = s390_get_phb(); + + if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { + pci_dev = PCI_DEVICE(dev); + + for (i = 0 ; i < PCI_SLOT_MAX; i++) { + if (s->pbdev[i] && s->pbdev[i]->pdev == pci_dev) { + pbdev = s->pbdev[i]; + break; + } + } - if (pbdev->configured) { - pbdev->configured = false; - s390_pci_generate_plug_event(HP_EVENT_CONFIGURED_TO_STBRES, + if (!pbdev) { + object_unparent(OBJECT(pci_dev)); + return; + } + } else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) { + pbdev = S390_PCI_DEVICE(dev); + pci_dev = pbdev->pdev; + } + + switch (pbdev->state) { + case ZPCI_FS_RESERVED: + goto out; + case ZPCI_FS_STANDBY: + break; + default: + s390_pci_generate_plug_event(HP_EVENT_DECONFIGURE_REQUEST, pbdev->fh, pbdev->fid); + pbdev->release_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + s390_pcihost_timer_cb, + pbdev); + timer_mod(pbdev->release_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + HOT_UNPLUG_TIMEOUT); + return; + } + + if (pbdev->release_timer && timer_pending(pbdev->release_timer)) { + timer_del(pbdev->release_timer); + timer_free(pbdev->release_timer); + pbdev->release_timer = NULL; } s390_pci_generate_plug_event(HP_EVENT_STANDBY_TO_RESERVED, pbdev->fh, pbdev->fid); - pbdev->fh = 0; - pbdev->fid = 0; - pbdev->pdev = NULL; object_unparent(OBJECT(pci_dev)); + pbdev->pdev = NULL; + pbdev->state = ZPCI_FS_RESERVED; +out: + pbdev->fid = 0; + s->pbdev[pbdev->fh & FH_MASK_INDEX] = NULL; + object_unparent(OBJECT(pbdev)); } static void s390_pcihost_class_init(ObjectClass *klass, void *data) @@ -613,9 +784,178 @@ static const TypeInfo s390_pcihost_info = { } }; +static const TypeInfo s390_pcibus_info = { + .name = TYPE_S390_PCI_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(S390PCIBus), +}; + +static uint16_t s390_pci_generate_uid(void) +{ + uint16_t uid = 0; + + do { + uid++; + if (!s390_pci_find_dev_by_uid(uid)) { + return uid; + } + } while (uid < ZPCI_MAX_UID); + + return UID_UNDEFINED; +} + +static uint32_t s390_pci_generate_fid(Error **errp) +{ + uint32_t fid = 0; + + while (fid <= ZPCI_MAX_FID) { + if (!s390_pci_find_dev_by_fid(fid)) { + return fid; + } + + if (fid == ZPCI_MAX_FID) { + break; + } + + fid++; + } + + error_setg(errp, "no free fid could be found"); + return 0; +} + +static void s390_pci_device_realize(DeviceState *dev, Error **errp) +{ + S390PCIBusDevice *zpci = S390_PCI_DEVICE(dev); + + if (!zpci->target) { + error_setg(errp, "target must be defined"); + return; + } + + if (s390_pci_find_dev_by_target(zpci->target)) { + error_setg(errp, "target %s already has an associated zpci device", + zpci->target); + return; + } + + if (zpci->uid == UID_UNDEFINED) { + zpci->uid = s390_pci_generate_uid(); + if (!zpci->uid) { + error_setg(errp, "no free uid could be found"); + return; + } + } else if (s390_pci_find_dev_by_uid(zpci->uid)) { + error_setg(errp, "uid %u already in use", zpci->uid); + return; + } + + if (!zpci->fid_defined) { + Error *local_error = NULL; + + zpci->fid = s390_pci_generate_fid(&local_error); + if (local_error) { + error_propagate(errp, local_error); + return; + } + } else if (s390_pci_find_dev_by_fid(zpci->fid)) { + error_setg(errp, "fid %u already in use", zpci->fid); + return; + } + + zpci->state = ZPCI_FS_RESERVED; +} + +static void s390_pci_device_reset(DeviceState *dev) +{ + S390PCIBusDevice *pbdev = S390_PCI_DEVICE(dev); + + switch (pbdev->state) { + case ZPCI_FS_RESERVED: + return; + case ZPCI_FS_STANDBY: + break; + default: + pbdev->fh &= ~FH_MASK_ENABLE; + pbdev->state = ZPCI_FS_DISABLED; + break; + } + + if (pbdev->summary_ind) { + pci_dereg_irqs(pbdev); + } + if (pbdev->iommu_enabled) { + pci_dereg_ioat(pbdev); + } + + pbdev->fmb_addr = 0; +} + +static void s390_pci_get_fid(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + Property *prop = opaque; + uint32_t *ptr = qdev_get_prop_ptr(DEVICE(obj), prop); + + visit_type_uint32(v, name, ptr, errp); +} + +static void s390_pci_set_fid(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + S390PCIBusDevice *zpci = S390_PCI_DEVICE(obj); + Property *prop = opaque; + uint32_t *ptr = qdev_get_prop_ptr(dev, prop); + + if (dev->realized) { + qdev_prop_set_after_realize(dev, name, errp); + return; + } + + visit_type_uint32(v, name, ptr, errp); + zpci->fid_defined = true; +} + +static PropertyInfo s390_pci_fid_propinfo = { + .name = "zpci_fid", + .get = s390_pci_get_fid, + .set = s390_pci_set_fid, +}; + +#define DEFINE_PROP_S390_PCI_FID(_n, _s, _f) \ + DEFINE_PROP(_n, _s, _f, s390_pci_fid_propinfo, uint32_t) + +static Property s390_pci_device_properties[] = { + DEFINE_PROP_UINT16("uid", S390PCIBusDevice, uid, UID_UNDEFINED), + DEFINE_PROP_S390_PCI_FID("fid", S390PCIBusDevice, fid), + DEFINE_PROP_STRING("target", S390PCIBusDevice, target), + DEFINE_PROP_END_OF_LIST(), +}; + +static void s390_pci_device_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "zpci device"; + dc->reset = s390_pci_device_reset; + dc->bus_type = TYPE_S390_PCI_BUS; + dc->realize = s390_pci_device_realize; + dc->props = s390_pci_device_properties; +} + +static const TypeInfo s390_pci_device_info = { + .name = TYPE_S390_PCI_DEVICE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(S390PCIBusDevice), + .class_init = s390_pci_device_class_init, +}; + static void s390_pci_register_types(void) { type_register_static(&s390_pcihost_info); + type_register_static(&s390_pcibus_info); + type_register_static(&s390_pci_device_info); } type_init(s390_pci_register_types) diff --git a/hw/s390x/s390-pci-bus.h b/hw/s390x/s390-pci-bus.h index 59fd5c958..4f564e02f 100644 --- a/hw/s390x/s390-pci-bus.h +++ b/hw/s390x/s390-pci-bus.h @@ -14,23 +14,38 @@ #ifndef HW_S390_PCI_BUS_H #define HW_S390_PCI_BUS_H -#include <hw/pci/pci.h> -#include <hw/pci/pci_host.h> +#include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" #include "hw/s390x/sclp.h" #include "hw/s390x/s390_flic.h" #include "hw/s390x/css.h" #define TYPE_S390_PCI_HOST_BRIDGE "s390-pcihost" -#define FH_VIRT 0x00ff0000 -#define ENABLE_BIT_OFFSET 31 -#define FH_ENABLED (1 << ENABLE_BIT_OFFSET) +#define TYPE_S390_PCI_BUS "s390-pcibus" +#define TYPE_S390_PCI_DEVICE "zpci" +#define FH_MASK_ENABLE 0x80000000 +#define FH_MASK_INSTANCE 0x7f000000 +#define FH_MASK_SHM 0x00ff0000 +#define FH_MASK_INDEX 0x0000001f +#define FH_SHM_VFIO 0x00010000 +#define FH_SHM_EMUL 0x00020000 #define S390_PCIPT_ADAPTER 2 +#define ZPCI_MAX_FID 0xffffffff +#define ZPCI_MAX_UID 0xffff +#define UID_UNDEFINED 0 +#define UID_CHECKING_ENABLED 0x01 +#define HOT_UNPLUG_TIMEOUT (NANOSECONDS_PER_SECOND * 60 * 5) #define S390_PCI_HOST_BRIDGE(obj) \ OBJECT_CHECK(S390pciState, (obj), TYPE_S390_PCI_HOST_BRIDGE) +#define S390_PCI_BUS(obj) \ + OBJECT_CHECK(S390PCIBus, (obj), TYPE_S390_PCI_BUS) +#define S390_PCI_DEVICE(obj) \ + OBJECT_CHECK(S390PCIBusDevice, (obj), TYPE_S390_PCI_DEVICE) #define HP_EVENT_TO_CONFIGURED 0x0301 #define HP_EVENT_RESERVED_TO_STANDBY 0x0302 +#define HP_EVENT_DECONFIGURE_REQUEST 0x0303 #define HP_EVENT_CONFIGURED_TO_STBRES 0x0304 #define HP_EVENT_STANDBY_TO_RESERVED 0x0308 @@ -150,6 +165,34 @@ enum ZpciIoatDtype { #define ZPCI_TABLE_VALID_MASK 0x20 #define ZPCI_TABLE_PROT_MASK 0x200 +/* PCI Function States + * + * reserved: default; device has just been plugged or is in progress of being + * unplugged + * standby: device is present but not configured; transition from any + * configured state/to this state via sclp configure/deconfigure + * + * The following states make up the "configured" meta-state: + * disabled: device is configured but not enabled; transition between this + * state and enabled via clp enable/disable + * enbaled: device is ready for use; transition to disabled via clp disable; + * may enter an error state + * blocked: ignore all DMA and interrupts; transition back to enabled or from + * error state via mpcifc + * error: an error occured; transition back to enabled via mpcifc + * permanent error: an unrecoverable error occured; transition to standby via + * sclp deconfigure + */ +typedef enum { + ZPCI_FS_RESERVED, + ZPCI_FS_STANDBY, + ZPCI_FS_DISABLED, + ZPCI_FS_ENABLED, + ZPCI_FS_BLOCKED, + ZPCI_FS_ERROR, + ZPCI_FS_PERMANENT_ERROR, +} ZpciState; + typedef struct SeiContainer { QTAILQ_ENTRY(SeiContainer) link; uint32_t fid; @@ -198,11 +241,11 @@ typedef struct ChscSeiNt2Res { } QEMU_PACKED ChscSeiNt2Res; typedef struct PciCfgSccb { - SCCBHeader header; - uint8_t atype; - uint8_t reserved1; - uint16_t reserved2; - uint32_t aid; + SCCBHeader header; + uint8_t atype; + uint8_t reserved1; + uint16_t reserved2; + uint32_t aid; } QEMU_PACKED PciCfgSccb; typedef struct S390MsixInfo { @@ -214,13 +257,21 @@ typedef struct S390MsixInfo { uint32_t pba_offset; } S390MsixInfo; +typedef struct S390PCIIOMMU { + AddressSpace as; + MemoryRegion mr; +} S390PCIIOMMU; + typedef struct S390PCIBusDevice { + DeviceState qdev; PCIDevice *pdev; - bool configured; - bool error_state; - bool lgstg_blocked; + ZpciState state; + bool iommu_enabled; + char *target; + uint16_t uid; uint32_t fh; uint32_t fid; + bool fid_defined; uint64_t g_iota; uint64_t pba; uint64_t pal; @@ -230,16 +281,22 @@ typedef struct S390PCIBusDevice { uint8_t sum; S390MsixInfo msix; AdapterRoutes routes; - AddressSpace as; - MemoryRegion mr; + S390PCIIOMMU *iommu; MemoryRegion iommu_mr; IndAddr *summary_ind; IndAddr *indicator; + QEMUTimer *release_timer; } S390PCIBusDevice; +typedef struct S390PCIBus { + BusState qbus; +} S390PCIBus; + typedef struct S390pciState { PCIHostState parent_obj; - S390PCIBusDevice pbdev[PCI_SLOT_MAX]; + S390PCIBus *bus; + S390PCIBusDevice *pbdev[PCI_SLOT_MAX]; + S390PCIIOMMU *iommu[PCI_SLOT_MAX]; AddressSpace msix_notify_as; MemoryRegion msix_notify_mr; QTAILQ_HEAD(, SeiContainer) pending_sei; @@ -247,10 +304,15 @@ typedef struct S390pciState { int chsc_sei_nt2_get_event(void *res); int chsc_sei_nt2_have_event(void); -void s390_pci_sclp_configure(int configure, SCCB *sccb); -void s390_pcihost_iommu_configure(S390PCIBusDevice *pbdev, bool enable); +void s390_pci_sclp_configure(SCCB *sccb); +void s390_pci_sclp_deconfigure(SCCB *sccb); +void s390_pci_iommu_enable(S390PCIBusDevice *pbdev); +void s390_pci_iommu_disable(S390PCIBusDevice *pbdev); +void s390_pci_generate_error_event(uint16_t pec, uint32_t fh, uint32_t fid, + uint64_t faddr, uint32_t e); S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx); S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh); S390PCIBusDevice *s390_pci_find_dev_by_fid(uint32_t fid); +S390PCIBusDevice *s390_pci_find_next_avail_dev(S390PCIBusDevice *pbdev); #endif diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index b28e7d14f..f069b110b 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -16,8 +16,8 @@ #include "cpu.h" #include "s390-pci-inst.h" #include "s390-pci-bus.h" -#include <exec/memory-internal.h> -#include <qemu/error-report.h> +#include "exec/memory-internal.h" +#include "qemu/error-report.h" /* #define DEBUG_S390PCI_INST */ #ifdef DEBUG_S390PCI_INST @@ -37,9 +37,9 @@ static void s390_set_status_code(CPUS390XState *env, static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc) { - S390PCIBusDevice *pbdev; - uint32_t res_code, initial_l2, g_l2, finish; - int rc, idx; + S390PCIBusDevice *pbdev = NULL; + uint32_t res_code, initial_l2, g_l2; + int rc, i; uint64_t resume_token; rc = 0; @@ -56,8 +56,7 @@ static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc) } if ((ldl_p(&rrb->request.fmt) & ~CLP_MASK_FMT) != 0 || - ldq_p(&rrb->request.reserved1) != 0 || - ldq_p(&rrb->request.reserved2) != 0) { + ldq_p(&rrb->request.reserved1) != 0) { res_code = CLP_RC_RESNOT0; rc = -EINVAL; goto out; @@ -72,6 +71,8 @@ static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc) rc = -EINVAL; goto out; } + } else { + pbdev = s390_pci_find_next_avail_dev(NULL); } if (lduw_p(&rrb->response.hdr.len) < 48) { @@ -91,43 +92,40 @@ static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc) stl_p(&rrb->response.fmt, 0); stq_p(&rrb->response.reserved1, 0); - stq_p(&rrb->response.reserved2, 0); - stl_p(&rrb->response.mdd, FH_VIRT); + stl_p(&rrb->response.mdd, FH_MASK_SHM); stw_p(&rrb->response.max_fn, PCI_MAX_FUNCTIONS); + rrb->response.flags = UID_CHECKING_ENABLED; rrb->response.entry_size = sizeof(ClpFhListEntry); - finish = 0; - idx = resume_token; + + i = 0; g_l2 = LIST_PCI_HDR_LEN; - do { - pbdev = s390_pci_find_dev_by_idx(idx); - if (!pbdev) { - finish = 1; - break; - } - stw_p(&rrb->response.fh_list[idx - resume_token].device_id, + while (g_l2 < initial_l2 && pbdev) { + stw_p(&rrb->response.fh_list[i].device_id, pci_get_word(pbdev->pdev->config + PCI_DEVICE_ID)); - stw_p(&rrb->response.fh_list[idx - resume_token].vendor_id, + stw_p(&rrb->response.fh_list[i].vendor_id, pci_get_word(pbdev->pdev->config + PCI_VENDOR_ID)); - stl_p(&rrb->response.fh_list[idx - resume_token].config, - pbdev->configured << 31); - stl_p(&rrb->response.fh_list[idx - resume_token].fid, pbdev->fid); - stl_p(&rrb->response.fh_list[idx - resume_token].fh, pbdev->fh); + /* Ignore RESERVED devices. */ + stl_p(&rrb->response.fh_list[i].config, + pbdev->state == ZPCI_FS_STANDBY ? 0 : 1 << 31); + stl_p(&rrb->response.fh_list[i].fid, pbdev->fid); + stl_p(&rrb->response.fh_list[i].fh, pbdev->fh); g_l2 += sizeof(ClpFhListEntry); /* Add endian check for DPRINTF? */ DPRINTF("g_l2 %d vendor id 0x%x device id 0x%x fid 0x%x fh 0x%x\n", - g_l2, - lduw_p(&rrb->response.fh_list[idx - resume_token].vendor_id), - lduw_p(&rrb->response.fh_list[idx - resume_token].device_id), - ldl_p(&rrb->response.fh_list[idx - resume_token].fid), - ldl_p(&rrb->response.fh_list[idx - resume_token].fh)); - idx++; - } while (g_l2 < initial_l2); - - if (finish == 1) { + g_l2, + lduw_p(&rrb->response.fh_list[i].vendor_id), + lduw_p(&rrb->response.fh_list[i].device_id), + ldl_p(&rrb->response.fh_list[i].fid), + ldl_p(&rrb->response.fh_list[i].fh)); + pbdev = s390_pci_find_next_avail_dev(pbdev); + i++; + } + + if (!pbdev) { resume_token = 0; } else { - resume_token = idx; + resume_token = pbdev->fh & FH_MASK_INDEX; } stq_p(&rrb->response.resume_token, resume_token); stw_p(&rrb->response.hdr.len, g_l2); @@ -212,14 +210,35 @@ int clp_service_call(S390CPU *cpu, uint8_t r2) switch (reqsetpci->oc) { case CLP_SET_ENABLE_PCI_FN: - pbdev->fh = pbdev->fh | FH_ENABLED; + switch (reqsetpci->ndas) { + case 0: + stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_DMAAS); + goto out; + case 1: + break; + default: + stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_RES); + goto out; + } + + if (pbdev->fh & FH_MASK_ENABLE) { + stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_FHOP); + goto out; + } + + pbdev->fh |= FH_MASK_ENABLE; + pbdev->state = ZPCI_FS_ENABLED; stl_p(&ressetpci->fh, pbdev->fh); stw_p(&ressetpci->hdr.rsp, CLP_RC_OK); break; case CLP_SET_DISABLE_PCI_FN: - pbdev->fh = pbdev->fh & ~FH_ENABLED; - pbdev->error_state = false; - pbdev->lgstg_blocked = false; + if (!(pbdev->fh & FH_MASK_ENABLE)) { + stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_FHOP); + goto out; + } + device_reset(DEVICE(pbdev)); + pbdev->fh &= ~FH_MASK_ENABLE; + pbdev->state = ZPCI_FS_DISABLED; stl_p(&ressetpci->fh, pbdev->fh); stw_p(&ressetpci->hdr.rsp, CLP_RC_OK); break; @@ -256,9 +275,10 @@ int clp_service_call(S390CPU *cpu, uint8_t r2) stq_p(&resquery->sdma, ZPCI_SDMA_ADDR); stq_p(&resquery->edma, ZPCI_EDMA_ADDR); + stl_p(&resquery->fid, pbdev->fid); stw_p(&resquery->pchid, 0); stw_p(&resquery->ug, 1); - stl_p(&resquery->uid, pbdev->fid); + stl_p(&resquery->uid, pbdev->uid); stw_p(&resquery->hdr.rsp, CLP_RC_OK); break; } @@ -317,16 +337,25 @@ int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) offset = env->regs[r2 + 1]; pbdev = s390_pci_find_dev_by_fh(fh); - if (!pbdev || !(pbdev->fh & FH_ENABLED)) { + if (!pbdev) { DPRINTF("pcilg no pci dev\n"); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); return 0; } - if (pbdev->lgstg_blocked) { + switch (pbdev->state) { + case ZPCI_FS_RESERVED: + case ZPCI_FS_STANDBY: + case ZPCI_FS_DISABLED: + case ZPCI_FS_PERMANENT_ERROR: + setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); + return 0; + case ZPCI_FS_ERROR: setcc(cpu, ZPCI_PCI_LS_ERR); s390_set_status_code(env, r2, ZPCI_PCI_ST_BLOCKED); return 0; + default: + break; } if (pcias < 6) { @@ -390,7 +419,8 @@ static void update_msix_table_msg_data(S390PCIBusDevice *pbdev, uint64_t offset, msg_data = (uint8_t *)data - offset % PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL; - val = pci_get_long(msg_data) | (pbdev->fid << ZPCI_MSI_VEC_BITS); + val = pci_get_long(msg_data) | + ((pbdev->fh & FH_MASK_INDEX) << ZPCI_MSI_VEC_BITS); pci_set_long(msg_data, val); DPRINTF("update msix msg_data to 0x%" PRIx64 "\n", *data); } @@ -434,16 +464,25 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) offset = env->regs[r2 + 1]; pbdev = s390_pci_find_dev_by_fh(fh); - if (!pbdev || !(pbdev->fh & FH_ENABLED)) { + if (!pbdev) { DPRINTF("pcistg no pci dev\n"); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); return 0; } - if (pbdev->lgstg_blocked) { + switch (pbdev->state) { + case ZPCI_FS_RESERVED: + case ZPCI_FS_STANDBY: + case ZPCI_FS_DISABLED: + case ZPCI_FS_PERMANENT_ERROR: + setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); + return 0; + case ZPCI_FS_ERROR: setcc(cpu, ZPCI_PCI_LS_ERR); s390_set_status_code(env, r2, ZPCI_PCI_ST_BLOCKED); return 0; + default: + break; } data = env->regs[r1]; @@ -525,18 +564,55 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) end = start + env->regs[r2 + 1]; pbdev = s390_pci_find_dev_by_fh(fh); - if (!pbdev || !(pbdev->fh & FH_ENABLED)) { + if (!pbdev) { DPRINTF("rpcit no pci dev\n"); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); goto out; } + switch (pbdev->state) { + case ZPCI_FS_RESERVED: + case ZPCI_FS_STANDBY: + case ZPCI_FS_DISABLED: + case ZPCI_FS_PERMANENT_ERROR: + setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); + return 0; + case ZPCI_FS_ERROR: + setcc(cpu, ZPCI_PCI_LS_ERR); + s390_set_status_code(env, r1, ZPCI_MOD_ST_ERROR_RECOVER); + return 0; + default: + break; + } + + if (!pbdev->g_iota) { + pbdev->state = ZPCI_FS_ERROR; + setcc(cpu, ZPCI_PCI_LS_ERR); + s390_set_status_code(env, r1, ZPCI_PCI_ST_INSUF_RES); + s390_pci_generate_error_event(ERR_EVENT_INVALAS, pbdev->fh, pbdev->fid, + start, 0); + goto out; + } + + if (end < pbdev->pba || start > pbdev->pal) { + pbdev->state = ZPCI_FS_ERROR; + setcc(cpu, ZPCI_PCI_LS_ERR); + s390_set_status_code(env, r1, ZPCI_PCI_ST_INSUF_RES); + s390_pci_generate_error_event(ERR_EVENT_OORANGE, pbdev->fh, pbdev->fid, + start, 0); + goto out; + } + mr = &pbdev->iommu_mr; while (start < end) { entry = mr->iommu_ops->translate(mr, start, 0); if (!entry.translated_addr) { + pbdev->state = ZPCI_FS_ERROR; setcc(cpu, ZPCI_PCI_LS_ERR); + s390_set_status_code(env, r1, ZPCI_PCI_ST_INSUF_RES); + s390_pci_generate_error_event(ERR_EVENT_SERR, pbdev->fh, pbdev->fid, + start, ERR_EVENT_Q_BIT); goto out; } @@ -589,16 +665,25 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr, } pbdev = s390_pci_find_dev_by_fh(fh); - if (!pbdev || !(pbdev->fh & FH_ENABLED)) { + if (!pbdev) { DPRINTF("pcistb no pci dev fh 0x%x\n", fh); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); return 0; } - if (pbdev->lgstg_blocked) { + switch (pbdev->state) { + case ZPCI_FS_RESERVED: + case ZPCI_FS_STANDBY: + case ZPCI_FS_DISABLED: + case ZPCI_FS_PERMANENT_ERROR: + setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); + return 0; + case ZPCI_FS_ERROR: setcc(cpu, ZPCI_PCI_LS_ERR); s390_set_status_code(env, r1, ZPCI_PCI_ST_BLOCKED); return 0; + default: + break; } mr = pbdev->pdev->io_regions[pcias].memory; @@ -634,8 +719,15 @@ static int reg_irqs(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib) len = BITS_TO_LONGS(FIB_DATA_NOI(ldl_p(&fib.data))) * sizeof(unsigned long); pbdev->indicator = get_indicator(ldq_p(&fib.aibv), len); - map_indicator(&pbdev->routes.adapter, pbdev->summary_ind); - map_indicator(&pbdev->routes.adapter, pbdev->indicator); + ret = map_indicator(&pbdev->routes.adapter, pbdev->summary_ind); + if (ret) { + goto out; + } + + ret = map_indicator(&pbdev->routes.adapter, pbdev->indicator); + if (ret) { + goto out; + } pbdev->routes.adapter.summary_addr = ldq_p(&fib.aisb); pbdev->routes.adapter.summary_offset = FIB_DATA_AISBO(ldl_p(&fib.data)); @@ -647,9 +739,15 @@ static int reg_irqs(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib) DPRINTF("reg_irqs adapter id %d\n", pbdev->routes.adapter.adapter_id); return 0; +out: + release_indicator(&pbdev->routes.adapter, pbdev->summary_ind); + release_indicator(&pbdev->routes.adapter, pbdev->indicator); + pbdev->summary_ind = NULL; + pbdev->indicator = NULL; + return ret; } -static int dereg_irqs(S390PCIBusDevice *pbdev) +int pci_dereg_irqs(S390PCIBusDevice *pbdev) { release_indicator(&pbdev->routes.adapter, pbdev->summary_ind); release_indicator(&pbdev->routes.adapter, pbdev->indicator); @@ -692,24 +790,23 @@ static int reg_ioat(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib) pbdev->pal = pal; pbdev->g_iota = g_iota; - s390_pcihost_iommu_configure(pbdev, true); + s390_pci_iommu_enable(pbdev); return 0; } -static void dereg_ioat(S390PCIBusDevice *pbdev) +void pci_dereg_ioat(S390PCIBusDevice *pbdev) { + s390_pci_iommu_disable(pbdev); pbdev->pba = 0; pbdev->pal = 0; pbdev->g_iota = 0; - - s390_pcihost_iommu_configure(pbdev, false); } int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) { CPUS390XState *env = &cpu->env; - uint8_t oc; + uint8_t oc, dmaas; uint32_t fh; ZpciFib fib; S390PCIBusDevice *pbdev; @@ -721,6 +818,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) } oc = env->regs[r1] & 0xff; + dmaas = (env->regs[r1] >> 16) & 0xff; fh = env->regs[r1] >> 32; if (fiba & 0x7) { @@ -729,45 +827,108 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) } pbdev = s390_pci_find_dev_by_fh(fh); - if (!pbdev || !(pbdev->fh & FH_ENABLED)) { + if (!pbdev) { DPRINTF("mpcifc no pci dev fh 0x%x\n", fh); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); return 0; } + switch (pbdev->state) { + case ZPCI_FS_RESERVED: + case ZPCI_FS_STANDBY: + case ZPCI_FS_DISABLED: + case ZPCI_FS_PERMANENT_ERROR: + setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); + return 0; + default: + break; + } + if (s390_cpu_virt_mem_read(cpu, fiba, ar, (uint8_t *)&fib, sizeof(fib))) { return 0; } + if (fib.fmt != 0) { + program_interrupt(env, PGM_OPERAND, 6); + return 0; + } + switch (oc) { case ZPCI_MOD_FC_REG_INT: - if (reg_irqs(env, pbdev, fib)) { + if (pbdev->summary_ind) { cc = ZPCI_PCI_LS_ERR; + s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE); + } else if (reg_irqs(env, pbdev, fib)) { + cc = ZPCI_PCI_LS_ERR; + s390_set_status_code(env, r1, ZPCI_MOD_ST_RES_NOT_AVAIL); } break; case ZPCI_MOD_FC_DEREG_INT: - dereg_irqs(pbdev); + if (!pbdev->summary_ind) { + cc = ZPCI_PCI_LS_ERR; + s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE); + } else { + pci_dereg_irqs(pbdev); + } break; case ZPCI_MOD_FC_REG_IOAT: - if (reg_ioat(env, pbdev, fib)) { + if (dmaas != 0) { cc = ZPCI_PCI_LS_ERR; + s390_set_status_code(env, r1, ZPCI_MOD_ST_DMAAS_INVAL); + } else if (pbdev->iommu_enabled) { + cc = ZPCI_PCI_LS_ERR; + s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE); + } else if (reg_ioat(env, pbdev, fib)) { + cc = ZPCI_PCI_LS_ERR; + s390_set_status_code(env, r1, ZPCI_MOD_ST_INSUF_RES); } break; case ZPCI_MOD_FC_DEREG_IOAT: - dereg_ioat(pbdev); + if (dmaas != 0) { + cc = ZPCI_PCI_LS_ERR; + s390_set_status_code(env, r1, ZPCI_MOD_ST_DMAAS_INVAL); + } else if (!pbdev->iommu_enabled) { + cc = ZPCI_PCI_LS_ERR; + s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE); + } else { + pci_dereg_ioat(pbdev); + } break; case ZPCI_MOD_FC_REREG_IOAT: - dereg_ioat(pbdev); - if (reg_ioat(env, pbdev, fib)) { + if (dmaas != 0) { cc = ZPCI_PCI_LS_ERR; + s390_set_status_code(env, r1, ZPCI_MOD_ST_DMAAS_INVAL); + } else if (!pbdev->iommu_enabled) { + cc = ZPCI_PCI_LS_ERR; + s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE); + } else { + pci_dereg_ioat(pbdev); + if (reg_ioat(env, pbdev, fib)) { + cc = ZPCI_PCI_LS_ERR; + s390_set_status_code(env, r1, ZPCI_MOD_ST_INSUF_RES); + } } break; case ZPCI_MOD_FC_RESET_ERROR: - pbdev->error_state = false; - pbdev->lgstg_blocked = false; + switch (pbdev->state) { + case ZPCI_FS_BLOCKED: + case ZPCI_FS_ERROR: + pbdev->state = ZPCI_FS_ENABLED; + break; + default: + cc = ZPCI_PCI_LS_ERR; + s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE); + } break; case ZPCI_MOD_FC_RESET_BLOCK: - pbdev->lgstg_blocked = false; + switch (pbdev->state) { + case ZPCI_FS_ERROR: + pbdev->state = ZPCI_FS_BLOCKED; + break; + default: + cc = ZPCI_PCI_LS_ERR; + s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE); + } break; case ZPCI_MOD_FC_SET_MEASURE: pbdev->fmb_addr = ldq_p(&fib.fmb_addr); @@ -784,6 +945,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) { CPUS390XState *env = &cpu->env; + uint8_t dmaas; uint32_t fh; ZpciFib fib; S390PCIBusDevice *pbdev; @@ -796,19 +958,59 @@ int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) } fh = env->regs[r1] >> 32; + dmaas = (env->regs[r1] >> 16) & 0xff; + + if (dmaas) { + setcc(cpu, ZPCI_PCI_LS_ERR); + s390_set_status_code(env, r1, ZPCI_STPCIFC_ST_INVAL_DMAAS); + return 0; + } if (fiba & 0x7) { program_interrupt(env, PGM_SPECIFICATION, 6); return 0; } - pbdev = s390_pci_find_dev_by_fh(fh); + pbdev = s390_pci_find_dev_by_idx(fh & FH_MASK_INDEX); if (!pbdev) { setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); return 0; } memset(&fib, 0, sizeof(fib)); + + switch (pbdev->state) { + case ZPCI_FS_RESERVED: + case ZPCI_FS_STANDBY: + setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); + return 0; + case ZPCI_FS_DISABLED: + if (fh & FH_MASK_ENABLE) { + setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); + return 0; + } + goto out; + /* BLOCKED bit is set to one coincident with the setting of ERROR bit. + * FH Enabled bit is set to one in states of ENABLED, BLOCKED or ERROR. */ + case ZPCI_FS_ERROR: + fib.fc |= 0x20; + case ZPCI_FS_BLOCKED: + fib.fc |= 0x40; + case ZPCI_FS_ENABLED: + fib.fc |= 0x80; + if (pbdev->iommu_enabled) { + fib.fc |= 0x10; + } + if (!(fh & FH_MASK_ENABLE)) { + env->regs[r1] |= 1ULL << 63; + } + break; + case ZPCI_FS_PERMANENT_ERROR: + setcc(cpu, ZPCI_PCI_LS_ERR); + s390_set_status_code(env, r1, ZPCI_STPCIFC_ST_PERM_ERROR); + return 0; + } + stq_p(&fib.pba, pbdev->pba); stq_p(&fib.pal, pbdev->pal); stq_p(&fib.iota, pbdev->g_iota); @@ -821,22 +1023,7 @@ int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) ((uint32_t)pbdev->sum << 7) | pbdev->routes.adapter.summary_offset; stl_p(&fib.data, data); - if (pbdev->fh & FH_ENABLED) { - fib.fc |= 0x80; - } - - if (pbdev->error_state) { - fib.fc |= 0x40; - } - - if (pbdev->lgstg_blocked) { - fib.fc |= 0x20; - } - - if (pbdev->g_iota) { - fib.fc |= 0x10; - } - +out: if (s390_cpu_virt_mem_write(cpu, fiba, ar, (uint8_t *)&fib, sizeof(fib))) { return 0; } diff --git a/hw/s390x/s390-pci-inst.h b/hw/s390x/s390-pci-inst.h index 70fa71395..23f4bfa0e 100644 --- a/hw/s390x/s390-pci-inst.h +++ b/hw/s390x/s390-pci-inst.h @@ -14,7 +14,8 @@ #ifndef HW_S390_PCI_INST_H #define HW_S390_PCI_INST_H -#include <sysemu/dma.h> +#include "s390-pci-bus.h" +#include "sysemu/dma.h" /* CLP common request & response block size */ #define CLP_BLK_SIZE 4096 @@ -103,7 +104,7 @@ typedef struct ClpRspListPci { uint64_t resume_token; uint32_t mdd; uint16_t max_fn; - uint8_t reserved2; + uint8_t flags; uint8_t entry_size; ClpFhListEntry fh_list[CLP_FH_LIST_NR_ENTRIES]; } QEMU_PACKED ClpRspListPci; @@ -230,6 +231,14 @@ typedef struct ClpReqRspQueryPciGrp { #define ZPCI_PCI_LS_BUSY 2 #define ZPCI_PCI_LS_INVAL_HANDLE 3 +/* Modify PCI status codes */ +#define ZPCI_MOD_ST_RES_NOT_AVAIL 4 +#define ZPCI_MOD_ST_INSUF_RES 16 +#define ZPCI_MOD_ST_SEQUENCE 24 +#define ZPCI_MOD_ST_DMAAS_INVAL 28 +#define ZPCI_MOD_ST_FRAME_INVAL 32 +#define ZPCI_MOD_ST_ERROR_RECOVER 40 + /* Modify PCI Function Controls */ #define ZPCI_MOD_FC_REG_INT 2 #define ZPCI_MOD_FC_DEREG_INT 3 @@ -240,6 +249,11 @@ typedef struct ClpReqRspQueryPciGrp { #define ZPCI_MOD_FC_RESET_BLOCK 9 #define ZPCI_MOD_FC_SET_MEASURE 10 +/* Store PCI Function Controls status codes */ +#define ZPCI_STPCIFC_ST_PERM_ERROR 8 +#define ZPCI_STPCIFC_ST_INVAL_DMAAS 28 +#define ZPCI_STPCIFC_ST_ERROR_RECOVER 40 + /* FIB function controls */ #define ZPCI_FIB_FC_ENABLED 0x80 #define ZPCI_FIB_FC_ERROR 0x40 @@ -277,6 +291,8 @@ typedef struct ZpciFib { uint32_t gd; } QEMU_PACKED ZpciFib; +int pci_dereg_irqs(S390PCIBusDevice *pbdev); +void pci_dereg_ioat(S390PCIBusDevice *pbdev); int clp_service_call(S390CPU *cpu, uint8_t r2); int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2); int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2); diff --git a/hw/s390x/s390-skeys.c b/hw/s390x/s390-skeys.c index 6528ffed1..e2d4e1af7 100644 --- a/hw/s390x/s390-skeys.c +++ b/hw/s390x/s390-skeys.c @@ -15,6 +15,7 @@ #include "migration/qemu-file.h" #include "hw/s390x/storage-keys.h" #include "qemu/error-report.h" +#include "sysemu/kvm.h" #define S390_SKEYS_BUFFER_SIZE 131072 /* Room for 128k storage keys */ #define S390_SKEYS_SAVE_FLAG_EOS 0x01 @@ -46,15 +47,11 @@ void s390_skeys_init(void) qdev_init_nofail(DEVICE(obj)); } -static void write_keys(QEMUFile *f, uint8_t *keys, uint64_t startgfn, +static void write_keys(FILE *f, uint8_t *keys, uint64_t startgfn, uint64_t count, Error **errp) { uint64_t curpage = startgfn; uint64_t maxpage = curpage + count - 1; - const char *fmt = "page=%03" PRIx64 ": key(%d) => ACC=%X, FP=%d, REF=%d," - " ch=%d, reserved=%d\n"; - char buf[128]; - int len; for (; curpage <= maxpage; curpage++) { uint8_t acc = (*keys & 0xF0) >> 4; @@ -63,10 +60,9 @@ static void write_keys(QEMUFile *f, uint8_t *keys, uint64_t startgfn, int ch = (*keys & 0x02); int res = (*keys & 0x01); - len = snprintf(buf, sizeof(buf), fmt, curpage, - *keys, acc, fp, ref, ch, res); - assert(len < sizeof(buf)); - qemu_put_buffer(f, (uint8_t *)buf, len); + fprintf(f, "page=%03" PRIx64 ": key(%d) => ACC=%X, FP=%d, REF=%d," + " ch=%d, reserved=%d\n", + curpage, *keys, acc, fp, ref, ch, res); keys++; } } @@ -115,7 +111,8 @@ void qmp_dump_skeys(const char *filename, Error **errp) vaddr cur_gfn = 0; uint8_t *buf; int ret; - QEMUFile *f; + int fd; + FILE *f; /* Quick check to see if guest is using storage keys*/ if (!skeyclass->skeys_enabled(ss)) { @@ -124,8 +121,14 @@ void qmp_dump_skeys(const char *filename, Error **errp) return; } - f = qemu_fopen(filename, "wb"); + fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd < 0) { + error_setg_file_open(errp, errno, filename); + return; + } + f = fdopen(fd, "wb"); if (!f) { + close(fd); error_setg_file_open(errp, errno, filename); return; } @@ -161,7 +164,7 @@ out_free: error_propagate(errp, lerr); g_free(buf); out: - qemu_fclose(f); + fclose(f); } static void qemu_s390_skeys_init(Object *obj) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index e3df9c78b..91d9cefbb 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -18,17 +18,19 @@ #include "s390-virtio.h" #include "hw/s390x/sclp.h" #include "hw/s390x/s390_flic.h" -#include "ioinst.h" -#include "css.h" +#include "hw/s390x/ioinst.h" +#include "hw/s390x/css.h" #include "virtio-ccw.h" #include "qemu/config-file.h" #include "s390-pci-bus.h" #include "hw/s390x/storage-keys.h" #include "hw/compat.h" +#include "ipl.h" #include "hw/s390x/s390-virtio-ccw.h" +#include "hw/s390x/css-bridge.h" static const char *const reset_dev_types[] = { - "virtual-css-bridge", + TYPE_VIRTUAL_CSS_BRIDGE, "s390-sclp-event-facility", "s390-flic", "diag288", @@ -179,10 +181,8 @@ static HotplugHandler *s390_get_hotplug_handler(MachineState *machine, static void s390_hot_add_cpu(const int64_t id, Error **errp) { MachineState *machine = MACHINE(qdev_get_machine()); - Error *err = NULL; - s390x_new_cpu(machine->cpu_model, id, &err); - error_propagate(errp, err); + s390x_new_cpu(machine->cpu_model, id, errp); } static void ccw_machine_class_init(ObjectClass *oc, void *data) @@ -190,7 +190,9 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data) MachineClass *mc = MACHINE_CLASS(oc); NMIClass *nc = NMI_CLASS(oc); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); + S390CcwMachineClass *s390mc = S390_MACHINE_CLASS(mc); + s390mc->ri_allowed = true; mc->init = ccw_init; mc->reset = s390_machine_reset; mc->hot_add_cpu = s390_hot_add_cpu; @@ -201,7 +203,7 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data) mc->no_parallel = 1; mc->no_sdcard = 1; mc->use_sclp = 1; - mc->max_cpus = 255; + mc->max_cpus = 248; mc->get_hotplug_handler = s390_get_hotplug_handler; hc->plug = s390_machine_device_plug; nc->nmi_monitor_handler = s390_nmi; @@ -237,6 +239,20 @@ static inline void machine_set_dea_key_wrap(Object *obj, bool value, ms->dea_key_wrap = value; } +bool ri_allowed(void) +{ + if (kvm_enabled()) { + MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); + if (object_class_dynamic_cast(OBJECT_CLASS(mc), + TYPE_S390_CCW_MACHINE)) { + S390CcwMachineClass *s390mc = S390_MACHINE_CLASS(mc); + + return s390mc->ri_allowed; + } + } + return 0; +} + static inline void s390_machine_initfn(Object *obj) { object_property_add_bool(obj, "aes-key-wrap", @@ -262,6 +278,7 @@ static const TypeInfo ccw_machine_info = { .abstract = true, .instance_size = sizeof(S390CcwMachineState), .instance_init = s390_machine_initfn, + .class_size = sizeof(S390CcwMachineClass), .class_init = ccw_machine_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_NMI }, @@ -299,11 +316,23 @@ static const TypeInfo ccw_machine_info = { } \ type_init(ccw_machine_register_##suffix) +#define CCW_COMPAT_2_6 \ + HW_COMPAT_2_6 \ + {\ + .driver = TYPE_S390_IPL,\ + .property = "iplbext_migration",\ + .value = "off",\ + }, {\ + .driver = TYPE_VIRTUAL_CSS_BRIDGE,\ + .property = "css_dev_path",\ + .value = "off",\ + }, + #define CCW_COMPAT_2_5 \ + CCW_COMPAT_2_6 \ HW_COMPAT_2_5 #define CCW_COMPAT_2_4 \ - CCW_COMPAT_2_5 \ HW_COMPAT_2_4 \ {\ .driver = TYPE_S390_SKEYS,\ @@ -343,21 +372,38 @@ static const TypeInfo ccw_machine_info = { .value = "0",\ }, +static void ccw_machine_2_7_instance_options(MachineState *machine) +{ +} + +static void ccw_machine_2_7_class_options(MachineClass *mc) +{ +} +DEFINE_CCW_MACHINE(2_7, "2.7", true); + static void ccw_machine_2_6_instance_options(MachineState *machine) { + ccw_machine_2_7_instance_options(machine); } static void ccw_machine_2_6_class_options(MachineClass *mc) { + S390CcwMachineClass *s390mc = S390_MACHINE_CLASS(mc); + + s390mc->ri_allowed = false; + ccw_machine_2_7_class_options(mc); + SET_MACHINE_COMPAT(mc, CCW_COMPAT_2_6); } -DEFINE_CCW_MACHINE(2_6, "2.6", true); +DEFINE_CCW_MACHINE(2_6, "2.6", false); static void ccw_machine_2_5_instance_options(MachineState *machine) { + ccw_machine_2_6_instance_options(machine); } static void ccw_machine_2_5_class_options(MachineClass *mc) { + ccw_machine_2_6_class_options(mc); SET_MACHINE_COMPAT(mc, CCW_COMPAT_2_5); } DEFINE_CCW_MACHINE(2_5, "2.5", false); @@ -369,6 +415,7 @@ static void ccw_machine_2_4_instance_options(MachineState *machine) static void ccw_machine_2_4_class_options(MachineClass *mc) { + ccw_machine_2_5_class_options(mc); SET_MACHINE_COMPAT(mc, CCW_COMPAT_2_4); } DEFINE_CCW_MACHINE(2_4, "2.4", false); diff --git a/hw/s390x/s390-virtio.h b/hw/s390x/s390-virtio.h index ffd014cb5..f588b80a6 100644 --- a/hw/s390x/s390-virtio.h +++ b/hw/s390x/s390-virtio.h @@ -10,7 +10,7 @@ */ #ifndef HW_S390_VIRTIO_H -#define HW_S390_VIRTIO_H 1 +#define HW_S390_VIRTIO_H #include "hw/nmi.h" #include "standard-headers/asm-s390/kvm_virtio.h" diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c index 85dbe1b60..fca37f511 100644 --- a/hw/s390x/sclp.c +++ b/hw/s390x/sclp.c @@ -357,10 +357,10 @@ static void sclp_execute(SCLPDevice *sclp, SCCB *sccb, uint32_t code) sclp_c->unassign_storage(sclp, sccb); break; case SCLP_CMDW_CONFIGURE_PCI: - s390_pci_sclp_configure(1, sccb); + s390_pci_sclp_configure(sccb); break; case SCLP_CMDW_DECONFIGURE_PCI: - s390_pci_sclp_configure(0, sccb); + s390_pci_sclp_deconfigure(sccb); break; default: efc->command_handler(ef, sccb, code); diff --git a/hw/s390x/sclpquiesce.c b/hw/s390x/sclpquiesce.c index c0ecab9c3..762cb184a 100644 --- a/hw/s390x/sclpquiesce.c +++ b/hw/s390x/sclpquiesce.c @@ -12,7 +12,7 @@ * */ #include "qemu/osdep.h" -#include <hw/qdev.h> +#include "hw/qdev.h" #include "sysemu/sysemu.h" #include "hw/s390x/sclp.h" #include "hw/s390x/event-facility.h" diff --git a/hw/s390x/trace-events b/hw/s390x/trace-events new file mode 100644 index 000000000..84ea96487 --- /dev/null +++ b/hw/s390x/trace-events @@ -0,0 +1,15 @@ +# See docs/tracing.txt for syntax documentation. + +# hw/s390x/css.c +css_enable_facility(const char *facility) "CSS: enable %s" +css_crw(uint8_t rsc, uint8_t erc, uint16_t rsid, const char *chained) "CSS: queueing crw: rsc=%x, erc=%x, rsid=%x %s" +css_chpid_add(uint8_t cssid, uint8_t chpid, uint8_t type) "CSS: add chpid %x.%02x (type %02x)" +css_new_image(uint8_t cssid, const char *default_cssid) "CSS: add css image %02x %s" +css_assign_subch(const char *do_assign, uint8_t cssid, uint8_t ssid, uint16_t schid, uint16_t devno) "CSS: %s %x.%x.%04x (devno %04x)" +css_io_interrupt(int cssid, int ssid, int schid, uint32_t intparm, uint8_t isc, const char *conditional) "CSS: I/O interrupt on sch %x.%x.%04x (intparm %08x, isc %x) %s" +css_adapter_interrupt(uint8_t isc) "CSS: adapter I/O interrupt (isc %x)" + +# hw/s390x/virtio-ccw.c +virtio_ccw_interpret_ccw(int cssid, int ssid, int schid, int cmd_code) "VIRTIO-CCW: %x.%x.%04x: interpret command %x" +virtio_ccw_new_device(int cssid, int ssid, int schid, int devno, const char *devno_mode) "VIRTIO-CCW: add subchannel %x.%x.%04x, devno %04x (%s)" +virtio_ccw_set_ind(uint64_t ind_loc, uint8_t ind_old, uint8_t ind_new) "VIRTIO-CCW: indicator at %" PRIu64 ": %x->%x" diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index d51642db0..a554a24d0 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -16,6 +16,7 @@ #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "sysemu/sysemu.h" +#include "sysemu/kvm.h" #include "net/net.h" #include "hw/virtio/virtio.h" #include "hw/virtio/virtio-serial.h" @@ -28,35 +29,15 @@ #include "hw/s390x/adapter.h" #include "hw/s390x/s390_flic.h" -#include "ioinst.h" -#include "css.h" +#include "hw/s390x/ioinst.h" +#include "hw/s390x/css.h" #include "virtio-ccw.h" #include "trace.h" +#include "hw/s390x/css-bridge.h" static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size, VirtioCcwDevice *dev); -static void virtual_css_bus_reset(BusState *qbus) -{ - /* This should actually be modelled via the generic css */ - css_reset(); -} - - -static void virtual_css_bus_class_init(ObjectClass *klass, void *data) -{ - BusClass *k = BUS_CLASS(klass); - - k->reset = virtual_css_bus_reset; -} - -static const TypeInfo virtual_css_bus_info = { - .name = TYPE_VIRTUAL_CSS_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(VirtualCssBus), - .class_init = virtual_css_bus_class_init, -}; - VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch) { VirtIODevice *vdev = NULL; @@ -68,112 +49,59 @@ 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; - - 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; + virtio_bus_start_ioeventfd(&dev->bus); } -static void virtio_ccw_start_ioeventfd(VirtioCcwDevice *dev) +static void virtio_ccw_stop_ioeventfd(VirtioCcwDevice *dev) { - VirtIODevice *vdev; - int n, r; + virtio_bus_stop_ioeventfd(&dev->bus); +} - 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; +static bool virtio_ccw_ioeventfd_started(DeviceState *d) +{ + VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); - 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); - } - 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__); + return dev->ioeventfd_started; } -static void virtio_ccw_stop_ioeventfd(VirtioCcwDevice *dev) +static void virtio_ccw_ioeventfd_set_started(DeviceState *d, bool started, + bool err) { - 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 = started; + if (err) { + /* Disable ioeventfd for this device. */ + dev->flags &= ~VIRTIO_CCW_FLAG_USE_IOEVENTFD; } - dev->ioeventfd_started = false; } -VirtualCssBus *virtual_css_bus_init(void) +static bool virtio_ccw_ioeventfd_disabled(DeviceState *d) { - VirtualCssBus *cbus; - BusState *bus; - DeviceState *dev; + VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); - /* Create bridge device */ - dev = qdev_create(NULL, "virtual-css-bridge"); - qdev_init_nofail(dev); + return dev->ioeventfd_disabled || + !(dev->flags & VIRTIO_CCW_FLAG_USE_IOEVENTFD); +} - /* Create bus on bridge device */ - bus = qbus_create(TYPE_VIRTUAL_CSS_BUS, dev, "virtual-css"); - cbus = VIRTUAL_CSS_BUS(bus); +static void virtio_ccw_ioeventfd_set_disabled(DeviceState *d, bool disabled) +{ + VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); - /* Enable hotplugging */ - qbus_set_hotplug_handler(bus, dev, &error_abort); + dev->ioeventfd_disabled = disabled; +} + +static int virtio_ccw_ioeventfd_assign(DeviceState *d, EventNotifier *notifier, + int n, bool assign) +{ + VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); + CcwDevice *ccw_dev = CCW_DEVICE(dev); + SubchDev *sch = ccw_dev->sch; + uint32_t sch_id = (css_build_subchannel_id(sch) << 16) | sch->schid; - return cbus; + return s390_assign_subch_ioeventfd(notifier, sch_id, n, assign); } /* Communication blocks used by several channel commands. */ @@ -267,6 +195,8 @@ static int virtio_ccw_set_vqs(SubchDev *sch, VqInfoBlock *info, static void virtio_ccw_reset_virtio(VirtioCcwDevice *dev, VirtIODevice *vdev) { + CcwDevice *ccw_dev = CCW_DEVICE(dev); + virtio_ccw_stop_ioeventfd(dev); virtio_reset(vdev); if (dev->indicators) { @@ -281,7 +211,7 @@ static void virtio_ccw_reset_virtio(VirtioCcwDevice *dev, VirtIODevice *vdev) release_indicator(&dev->routes.adapter, dev->summary_indicator); dev->summary_indicator = NULL; } - dev->sch->thinint_active = false; + ccw_dev->sch->thinint_active = false; } static int virtio_ccw_handle_set_vq(SubchDev *sch, CCW1 ccw, bool check_len, @@ -736,151 +666,44 @@ static void virtio_sch_disable_cb(SubchDev *sch) static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp) { - unsigned int cssid = 0; - unsigned int ssid = 0; - unsigned int schid; - unsigned int devno; - bool have_devno = false; - bool found = false; - SubchDev *sch; - int num; - Error *err = NULL; VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_GET_CLASS(dev); + CcwDevice *ccw_dev = CCW_DEVICE(dev); + SubchDev *sch = css_create_virtual_sch(ccw_dev->bus_id, errp); + Error *err = NULL; - sch = g_malloc0(sizeof(SubchDev)); - - sch->driver_data = dev; - dev->sch = sch; - - dev->indicators = NULL; - - /* Initialize subchannel structure. */ - sch->channel_prog = 0x0; - sch->last_cmd_valid = false; - sch->thinint_active = false; - /* - * Use a device number if provided. Otherwise, fall back to subchannel - * number. - */ - if (dev->bus_id) { - num = sscanf(dev->bus_id, "%x.%x.%04x", &cssid, &ssid, &devno); - if (num == 3) { - if ((cssid > MAX_CSSID) || (ssid > MAX_SSID)) { - error_setg(errp, "Invalid cssid or ssid: cssid %x, ssid %x", - cssid, ssid); - goto out_err; - } - /* Enforce use of virtual cssid. */ - if (cssid != VIRTUAL_CSSID) { - error_setg(errp, "cssid %x not valid for virtio devices", - cssid); - goto out_err; - } - if (css_devno_used(cssid, ssid, devno)) { - error_setg(errp, "Device %x.%x.%04x already exists", - cssid, ssid, devno); - goto out_err; - } - sch->cssid = cssid; - sch->ssid = ssid; - sch->devno = devno; - have_devno = true; - } else { - error_setg(errp, "Malformed devno parameter '%s'", dev->bus_id); - goto out_err; - } - } - - /* Find the next free id. */ - if (have_devno) { - for (schid = 0; schid <= MAX_SCHID; schid++) { - if (!css_find_subch(1, cssid, ssid, schid)) { - sch->schid = schid; - css_subch_assign(cssid, ssid, schid, devno, sch); - found = true; - break; - } - } - if (!found) { - error_setg(errp, "No free subchannel found for %x.%x.%04x", - cssid, ssid, devno); - goto out_err; - } - trace_virtio_ccw_new_device(cssid, ssid, schid, devno, - "user-configured"); - } else { - cssid = VIRTUAL_CSSID; - for (ssid = 0; ssid <= MAX_SSID; ssid++) { - for (schid = 0; schid <= MAX_SCHID; schid++) { - if (!css_find_subch(1, cssid, ssid, schid)) { - sch->cssid = cssid; - sch->ssid = ssid; - sch->schid = schid; - devno = schid; - /* - * If the devno is already taken, look further in this - * subchannel set. - */ - while (css_devno_used(cssid, ssid, devno)) { - if (devno == MAX_SCHID) { - devno = 0; - } else if (devno == schid - 1) { - error_setg(errp, "No free devno found"); - goto out_err; - } else { - devno++; - } - } - sch->devno = devno; - css_subch_assign(cssid, ssid, schid, devno, sch); - found = true; - break; - } - } - if (found) { - break; - } - } - if (!found) { - error_setg(errp, "Virtual channel subsystem is full!"); - goto out_err; - } - trace_virtio_ccw_new_device(cssid, ssid, schid, devno, - "auto-configured"); + if (!sch) { + return; } - /* Build initial schib. */ - css_sch_build_virtual_schib(sch, 0, VIRTIO_CCW_CHPID_TYPE); - + sch->driver_data = dev; sch->ccw_cb = virtio_ccw_cb; sch->disable_cb = virtio_sch_disable_cb; - - /* Build senseid data. */ - memset(&sch->id, 0, sizeof(SenseId)); sch->id.reserved = 0xff; sch->id.cu_type = VIRTIO_CCW_CU_TYPE; - + ccw_dev->sch = sch; + dev->indicators = NULL; dev->revision = -1; + css_sch_build_virtual_schib(sch, 0, VIRTIO_CCW_CHPID_TYPE); + + trace_virtio_ccw_new_device( + sch->cssid, sch->ssid, sch->schid, sch->devno, + ccw_dev->bus_id.valid ? "user-configured" : "auto-configured"); if (k->realize) { k->realize(dev, &err); } if (err) { error_propagate(errp, err); - css_subch_assign(cssid, ssid, schid, devno, NULL); - goto out_err; + css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); + ccw_dev->sch = NULL; + g_free(sch); } - - return; - -out_err: - dev->sch = NULL; - g_free(sch); } static int virtio_ccw_exit(VirtioCcwDevice *dev) { - SubchDev *sch = dev->sch; + CcwDevice *ccw_dev = CCW_DEVICE(dev); + SubchDev *sch = ccw_dev->sch; if (sch) { css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); @@ -898,15 +721,11 @@ static void virtio_ccw_net_realize(VirtioCcwDevice *ccw_dev, Error **errp) DeviceState *qdev = DEVICE(ccw_dev); VirtIONetCcw *dev = VIRTIO_NET_CCW(ccw_dev); DeviceState *vdev = DEVICE(&dev->vdev); - Error *err = NULL; virtio_net_set_netclient_name(&dev->vdev, qdev->id, object_get_typename(OBJECT(qdev))); qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", &err); - if (err) { - error_propagate(errp, err); - } + object_property_set_bool(OBJECT(vdev), true, "realized", errp); } static void virtio_ccw_net_instance_init(Object *obj) @@ -923,13 +742,9 @@ static void virtio_ccw_blk_realize(VirtioCcwDevice *ccw_dev, Error **errp) { VirtIOBlkCcw *dev = VIRTIO_BLK_CCW(ccw_dev); DeviceState *vdev = DEVICE(&dev->vdev); - Error *err = NULL; qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", &err); - if (err) { - error_propagate(errp, err); - } + object_property_set_bool(OBJECT(vdev), true, "realized", errp); } static void virtio_ccw_blk_instance_init(Object *obj) @@ -949,7 +764,6 @@ static void virtio_ccw_serial_realize(VirtioCcwDevice *ccw_dev, Error **errp) VirtioSerialCcw *dev = VIRTIO_SERIAL_CCW(ccw_dev); DeviceState *vdev = DEVICE(&dev->vdev); DeviceState *proxy = DEVICE(ccw_dev); - Error *err = NULL; char *bus_name; /* @@ -963,10 +777,7 @@ static void virtio_ccw_serial_realize(VirtioCcwDevice *ccw_dev, Error **errp) } qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", &err); - if (err) { - error_propagate(errp, err); - } + object_property_set_bool(OBJECT(vdev), true, "realized", errp); } @@ -982,13 +793,9 @@ static void virtio_ccw_balloon_realize(VirtioCcwDevice *ccw_dev, Error **errp) { VirtIOBalloonCcw *dev = VIRTIO_BALLOON_CCW(ccw_dev); DeviceState *vdev = DEVICE(&dev->vdev); - Error *err = NULL; qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", &err); - if (err) { - error_propagate(errp, err); - } + object_property_set_bool(OBJECT(vdev), true, "realized", errp); } static void virtio_ccw_balloon_instance_init(Object *obj) @@ -1009,7 +816,6 @@ static void virtio_ccw_scsi_realize(VirtioCcwDevice *ccw_dev, Error **errp) VirtIOSCSICcw *dev = VIRTIO_SCSI_CCW(ccw_dev); DeviceState *vdev = DEVICE(&dev->vdev); DeviceState *qdev = DEVICE(ccw_dev); - Error *err = NULL; char *bus_name; /* @@ -1023,10 +829,7 @@ static void virtio_ccw_scsi_realize(VirtioCcwDevice *ccw_dev, Error **errp) } qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", &err); - if (err) { - error_propagate(errp, err); - } + object_property_set_bool(OBJECT(vdev), true, "realized", errp); } static void virtio_ccw_scsi_instance_init(Object *obj) @@ -1044,13 +847,9 @@ static void vhost_ccw_scsi_realize(VirtioCcwDevice *ccw_dev, Error **errp) { VHostSCSICcw *dev = VHOST_SCSI_CCW(ccw_dev); DeviceState *vdev = DEVICE(&dev->vdev); - Error *err = NULL; qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", &err); - if (err) { - error_propagate(errp, err); - } + object_property_set_bool(OBJECT(vdev), true, "realized", errp); } static void vhost_ccw_scsi_instance_init(Object *obj) @@ -1085,7 +884,9 @@ static void virtio_ccw_rng_realize(VirtioCcwDevice *ccw_dev, Error **errp) */ static inline VirtioCcwDevice *to_virtio_ccw_dev_fast(DeviceState *d) { - return container_of(d, VirtioCcwDevice, parent_obj); + CcwDevice *ccw_dev = to_ccw_dev_fast(d); + + return container_of(ccw_dev, VirtioCcwDevice, parent_obj); } static uint8_t virtio_set_ind_atomic(SubchDev *sch, uint64_t ind_loc, @@ -1105,6 +906,7 @@ static uint8_t virtio_set_ind_atomic(SubchDev *sch, uint64_t ind_loc, ind_old = *ind_addr; ind_new = ind_old | to_be_set; } while (atomic_cmpxchg(ind_addr, ind_old, ind_new) != ind_old); + trace_virtio_ccw_set_ind(ind_loc, ind_old, ind_new); cpu_physical_memory_unmap(ind_addr, len, 1, len); return ind_old; @@ -1113,7 +915,8 @@ static uint8_t virtio_set_ind_atomic(SubchDev *sch, uint64_t ind_loc, static void virtio_ccw_notify(DeviceState *d, uint16_t vector) { VirtioCcwDevice *dev = to_virtio_ccw_dev_fast(d); - SubchDev *sch = dev->sch; + CcwDevice *ccw_dev = to_ccw_dev_fast(d); + SubchDev *sch = ccw_dev->sch; uint64_t indicators; /* queue indicators + secondary indicators */ @@ -1171,9 +974,10 @@ static void virtio_ccw_reset(DeviceState *d) { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); + CcwDevice *ccw_dev = CCW_DEVICE(d); virtio_ccw_reset_virtio(dev, vdev); - css_reset_sch(dev->sch); + css_reset_sch(ccw_dev->sch); } static void virtio_ccw_vmstate_change(DeviceState *d, bool running) @@ -1189,29 +993,17 @@ static void virtio_ccw_vmstate_change(DeviceState *d, bool running) static bool virtio_ccw_query_guest_notifiers(DeviceState *d) { - VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); + CcwDevice *dev = CCW_DEVICE(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; + CcwDevice *ccw_dev = CCW_DEVICE(dev); - if (!dev->sch->thinint_active) { + if (!ccw_dev->sch->thinint_active) { return -EINVAL; } @@ -1333,7 +1125,8 @@ static int virtio_ccw_set_guest_notifiers(DeviceState *d, int nvqs, { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); - bool with_irqfd = dev->sch->thinint_active && kvm_irqfds_enabled(); + CcwDevice *ccw_dev = CCW_DEVICE(d); + bool with_irqfd = ccw_dev->sch->thinint_active && kvm_irqfds_enabled(); int r, n; if (with_irqfd && assigned) { @@ -1392,7 +1185,8 @@ static int virtio_ccw_load_queue(DeviceState *d, int n, QEMUFile *f) static void virtio_ccw_save_config(DeviceState *d, QEMUFile *f) { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); - SubchDev *s = dev->sch; + CcwDevice *ccw_dev = CCW_DEVICE(d); + SubchDev *s = ccw_dev->sch; VirtIODevice *vdev = virtio_ccw_get_vdev(s); subch_device_save(s, f); @@ -1426,7 +1220,8 @@ static void virtio_ccw_save_config(DeviceState *d, QEMUFile *f) static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f) { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); - SubchDev *s = dev->sch; + CcwDevice *ccw_dev = CCW_DEVICE(d); + SubchDev *s = ccw_dev->sch; VirtIODevice *vdev = virtio_ccw_get_vdev(s); int len; @@ -1471,11 +1266,12 @@ static void virtio_ccw_device_plugged(DeviceState *d, Error **errp) { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); - SubchDev *sch = dev->sch; + CcwDevice *ccw_dev = CCW_DEVICE(d); + SubchDev *sch = ccw_dev->sch; int n = virtio_get_num_queues(vdev); if (virtio_get_num_queues(vdev) > VIRTIO_CCW_QUEUE_MAX) { - error_setg(errp, "The nubmer of virtqueues %d " + error_setg(errp, "The number of virtqueues %d " "exceeds ccw limit %d", n, VIRTIO_CCW_QUEUE_MAX); return; @@ -1515,7 +1311,7 @@ static void virtio_ccw_device_unplugged(DeviceState *d) /**************** Virtio-ccw Bus Device Descriptions *******************/ static Property virtio_ccw_net_properties[] = { - DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), + DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1544,7 +1340,7 @@ static const TypeInfo virtio_ccw_net = { }; static Property virtio_ccw_blk_properties[] = { - DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), + DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1573,7 +1369,7 @@ static const TypeInfo virtio_ccw_blk = { }; static Property virtio_ccw_serial_properties[] = { - DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), + DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1602,7 +1398,7 @@ static const TypeInfo virtio_ccw_serial = { }; static Property virtio_ccw_balloon_properties[] = { - DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), + DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1631,7 +1427,7 @@ static const TypeInfo virtio_ccw_balloon = { }; static Property virtio_ccw_scsi_properties[] = { - DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), + DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1661,7 +1457,7 @@ static const TypeInfo virtio_ccw_scsi = { #ifdef CONFIG_VHOST_SCSI static Property vhost_ccw_scsi_properties[] = { - DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), + DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), DEFINE_PROP_END_OF_LIST(), @@ -1699,7 +1495,7 @@ static void virtio_ccw_rng_instance_init(Object *obj) } static Property virtio_ccw_rng_properties[] = { - DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), + DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1746,29 +1542,17 @@ static int virtio_ccw_busdev_exit(DeviceState *dev) static void virtio_ccw_busdev_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev; - SubchDev *sch = _dev->sch; + VirtioCcwDevice *_dev = to_virtio_ccw_dev_fast(dev); virtio_ccw_stop_ioeventfd(_dev); - - /* - * We should arrive here only for device_del, since we don't support - * direct hot(un)plug of channels, but only through virtio. - */ - assert(sch != NULL); - /* Subchannel is now disabled and no longer valid. */ - sch->curr_status.pmcw.flags &= ~(PMCW_FLAGS_MASK_ENA | - PMCW_FLAGS_MASK_DNV); - - css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, 1, 0); - - object_unparent(OBJECT(dev)); } static void virtio_ccw_device_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + CCWDeviceClass *k = CCW_DEVICE_CLASS(dc); + k->unplug = virtio_ccw_busdev_unplug; dc->realize = virtio_ccw_busdev_realize; dc->exit = virtio_ccw_busdev_exit; dc->bus_type = TYPE_VIRTUAL_CSS_BUS; @@ -1776,44 +1560,13 @@ static void virtio_ccw_device_class_init(ObjectClass *klass, void *data) static const TypeInfo virtio_ccw_device_info = { .name = TYPE_VIRTIO_CCW_DEVICE, - .parent = TYPE_DEVICE, + .parent = TYPE_CCW_DEVICE, .instance_size = sizeof(VirtioCcwDevice), .class_init = virtio_ccw_device_class_init, .class_size = sizeof(VirtIOCCWDeviceClass), .abstract = true, }; -/***************** Virtual-css Bus Bridge Device ********************/ -/* Only required to have the virtio bus as child in the system bus */ - -static int virtual_css_bridge_init(SysBusDevice *dev) -{ - /* nothing */ - return 0; -} - -static void virtual_css_bridge_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->init = virtual_css_bridge_init; - hc->unplug = virtio_ccw_busdev_unplug; - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); -} - -static const TypeInfo virtual_css_bridge_info = { - .name = "virtual-css-bridge", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SysBusDevice), - .class_init = virtual_css_bridge_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_HOTPLUG_HANDLER }, - { } - } -}; - /* virtio-ccw-bus */ static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size, @@ -1835,7 +1588,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; @@ -1844,6 +1596,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 = { @@ -1855,7 +1612,7 @@ static const TypeInfo virtio_ccw_bus_info = { #ifdef CONFIG_VIRTFS static Property virtio_ccw_9p_properties[] = { - DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), + DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1867,13 +1624,9 @@ static void virtio_ccw_9p_realize(VirtioCcwDevice *ccw_dev, Error **errp) { V9fsCCWState *dev = VIRTIO_9P_CCW(ccw_dev); DeviceState *vdev = DEVICE(&dev->vdev); - Error *err = NULL; qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", &err); - if (err) { - error_propagate(errp, err); - } + object_property_set_bool(OBJECT(vdev), true, "realized", errp); } static void virtio_ccw_9p_class_init(ObjectClass *klass, void *data) @@ -1908,7 +1661,6 @@ static const TypeInfo virtio_ccw_9p_info = { static void virtio_ccw_register(void) { type_register_static(&virtio_ccw_bus_info); - type_register_static(&virtual_css_bus_info); type_register_static(&virtio_ccw_device_info); type_register_static(&virtio_ccw_serial); type_register_static(&virtio_ccw_blk); @@ -1919,7 +1671,6 @@ static void virtio_ccw_register(void) type_register_static(&vhost_ccw_scsi); #endif type_register_static(&virtio_ccw_rng); - type_register_static(&virtual_css_bridge_info); #ifdef CONFIG_VIRTFS type_register_static(&virtio_ccw_9p_info); #endif diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h index 66c831ba8..1c6bc8631 100644 --- a/hw/s390x/virtio-ccw.h +++ b/hw/s390x/virtio-ccw.h @@ -13,20 +13,21 @@ #ifndef HW_S390X_VIRTIO_CCW_H #define HW_S390X_VIRTIO_CCW_H -#include <hw/virtio/virtio-blk.h> -#include <hw/virtio/virtio-net.h> -#include <hw/virtio/virtio-serial.h> -#include <hw/virtio/virtio-scsi.h> +#include "hw/virtio/virtio-blk.h" +#include "hw/virtio/virtio-net.h" +#include "hw/virtio/virtio-serial.h" +#include "hw/virtio/virtio-scsi.h" #ifdef CONFIG_VHOST_SCSI -#include <hw/virtio/vhost-scsi.h> +#include "hw/virtio/vhost-scsi.h" #endif -#include <hw/virtio/virtio-balloon.h> -#include <hw/virtio/virtio-rng.h> -#include <hw/virtio/virtio-bus.h> +#include "hw/virtio/virtio-balloon.h" +#include "hw/virtio/virtio-rng.h" +#include "hw/virtio/virtio-bus.h" -#include "css.h" - -#define VIRTUAL_CSSID 0xfe +#include "hw/s390x/s390_flic.h" +#include "hw/s390x/css.h" +#include "ccw-device.h" +#include "hw/s390x/css-bridge.h" #define VIRTIO_CCW_CU_TYPE 0x3832 #define VIRTIO_CCW_CHPID_TYPE 0x32 @@ -66,7 +67,7 @@ typedef struct VirtioBusClass VirtioCcwBusClass; typedef struct VirtioCcwDevice VirtioCcwDevice; typedef struct VirtIOCCWDeviceClass { - DeviceClass parent_class; + CCWDeviceClass parent_class; void (*realize)(VirtioCcwDevice *dev, Error **errp); int (*exit)(VirtioCcwDevice *dev); } VirtIOCCWDeviceClass; @@ -77,9 +78,7 @@ typedef struct VirtIOCCWDeviceClass { #define VIRTIO_CCW_FLAG_USE_IOEVENTFD (1 << VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT) struct VirtioCcwDevice { - DeviceState parent_obj; - SubchDev *sch; - char *bus_id; + CcwDevice parent_obj; int revision; uint32_t max_rev; VirtioBusState bus; @@ -102,15 +101,6 @@ static inline int virtio_ccw_rev_max(VirtioCcwDevice *dev) return dev->max_rev; } -/* virtual css bus type */ -typedef struct VirtualCssBus { - BusState parent_obj; -} VirtualCssBus; - -#define TYPE_VIRTUAL_CSS_BUS "virtual-css-bus" -#define VIRTUAL_CSS_BUS(obj) \ - OBJECT_CHECK(VirtualCssBus, (obj), TYPE_VIRTUAL_CSS_BUS) - /* virtio-scsi-ccw */ #define TYPE_VIRTIO_SCSI_CCW "virtio-scsi-ccw" @@ -190,7 +180,6 @@ typedef struct VirtIORNGCcw { VirtIORNG vdev; } VirtIORNGCcw; -VirtualCssBus *virtual_css_bus_init(void); void virtio_ccw_device_update_status(SubchDev *sch); VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch); diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index 8961be2f3..1f2f2d33d 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -82,7 +82,7 @@ void esp_request_cancelled(SCSIRequest *req) } } -static uint32_t get_cmd(ESPState *s, uint8_t *buf) +static uint32_t get_cmd(ESPState *s, uint8_t *buf, uint8_t buflen) { uint32_t dmalen; int target; @@ -92,9 +92,15 @@ static uint32_t get_cmd(ESPState *s, uint8_t *buf) dmalen = s->rregs[ESP_TCLO]; dmalen |= s->rregs[ESP_TCMID] << 8; dmalen |= s->rregs[ESP_TCHI] << 16; + if (dmalen > buflen) { + return 0; + } s->dma_memory_read(s->dma_opaque, buf, dmalen); } else { dmalen = s->ti_size; + if (dmalen > TI_BUFSZ) { + return 0; + } memcpy(buf, s->ti_buf, dmalen); buf[0] = buf[2] >> 5; } @@ -166,7 +172,7 @@ static void handle_satn(ESPState *s) s->dma_cb = handle_satn; return; } - len = get_cmd(s, buf); + len = get_cmd(s, buf, sizeof(buf)); if (len) do_cmd(s, buf); } @@ -180,7 +186,7 @@ static void handle_s_without_atn(ESPState *s) s->dma_cb = handle_s_without_atn; return; } - len = get_cmd(s, buf); + len = get_cmd(s, buf, sizeof(buf)); if (len) { do_busid_cmd(s, buf, 0); } @@ -192,7 +198,7 @@ static void handle_satn_stop(ESPState *s) s->dma_cb = handle_satn_stop; return; } - s->cmdlen = get_cmd(s, s->cmdbuf); + s->cmdlen = get_cmd(s, s->cmdbuf, sizeof(s->cmdbuf)); if (s->cmdlen) { trace_esp_handle_satn_stop(s->cmdlen); s->do_cmd = 1; @@ -216,7 +222,7 @@ static void write_response(ESPState *s) } else { s->ti_size = 2; s->ti_rptr = 0; - s->ti_wptr = 0; + s->ti_wptr = 2; s->rregs[ESP_RFLAGS] = 2; } esp_raise_irq(s); @@ -239,15 +245,12 @@ static void esp_do_dma(ESPState *s) uint32_t len; int to_device; - to_device = (s->ti_size < 0); len = s->dma_left; if (s->do_cmd) { trace_esp_do_dma(s->cmdlen, len); + assert (s->cmdlen <= sizeof(s->cmdbuf) && + len <= sizeof(s->cmdbuf) - s->cmdlen); s->dma_memory_read(s->dma_opaque, &s->cmdbuf[s->cmdlen], len); - s->ti_size = 0; - s->cmdlen = 0; - s->do_cmd = 0; - do_cmd(s, s->cmdbuf); return; } if (s->async_len == 0) { @@ -257,6 +260,7 @@ static void esp_do_dma(ESPState *s) if (len > s->async_len) { len = s->async_len; } + to_device = (s->ti_size < 0); if (to_device) { s->dma_memory_read(s->dma_opaque, s->async_buf, len); } else { @@ -312,6 +316,7 @@ void esp_transfer_data(SCSIRequest *req, uint32_t len) { ESPState *s = req->hba_private; + assert(!s->do_cmd); trace_esp_transfer_data(s->dma_left, s->ti_size); s->async_len = len; s->async_buf = scsi_req_get_buf(req); @@ -342,7 +347,7 @@ static void handle_ti(ESPState *s) s->dma_counter = dmalen; if (s->do_cmd) - minlen = (dmalen < 32) ? dmalen : 32; + minlen = (dmalen < ESP_CMDBUF_SZ) ? dmalen : ESP_CMDBUF_SZ; else if (s->ti_size < 0) minlen = (dmalen < -s->ti_size) ? dmalen : -s->ti_size; else @@ -352,13 +357,13 @@ static void handle_ti(ESPState *s) s->dma_left = minlen; s->rregs[ESP_RSTAT] &= ~STAT_TC; esp_do_dma(s); - } else if (s->do_cmd) { + } + if (s->do_cmd) { trace_esp_handle_ti_cmd(s->cmdlen); s->ti_size = 0; s->cmdlen = 0; s->do_cmd = 0; do_cmd(s, s->cmdbuf); - return; } } @@ -397,19 +402,17 @@ uint64_t esp_reg_read(ESPState *s, uint32_t saddr) trace_esp_mem_readb(saddr, s->rregs[saddr]); switch (saddr) { case ESP_FIFO: - if (s->ti_size > 0) { + if ((s->rregs[ESP_RSTAT] & STAT_PIO_MASK) == 0) { + /* Data out. */ + qemu_log_mask(LOG_UNIMP, "esp: PIO data read not implemented\n"); + s->rregs[ESP_FIFO] = 0; + esp_raise_irq(s); + } else if (s->ti_rptr < s->ti_wptr) { s->ti_size--; - if ((s->rregs[ESP_RSTAT] & STAT_PIO_MASK) == 0) { - /* Data out. */ - qemu_log_mask(LOG_UNIMP, - "esp: PIO data read not implemented\n"); - s->rregs[ESP_FIFO] = 0; - } else { - s->rregs[ESP_FIFO] = s->ti_buf[s->ti_rptr++]; - } + s->rregs[ESP_FIFO] = s->ti_buf[s->ti_rptr++]; esp_raise_irq(s); } - if (s->ti_size == 0) { + if (s->ti_rptr == s->ti_wptr) { s->ti_rptr = 0; s->ti_wptr = 0; } @@ -448,8 +451,12 @@ void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val) break; case ESP_FIFO: if (s->do_cmd) { - s->cmdbuf[s->cmdlen++] = val & 0xff; - } else if (s->ti_size == TI_BUFSZ - 1) { + if (s->cmdlen < ESP_CMDBUF_SZ) { + s->cmdbuf[s->cmdlen++] = val & 0xff; + } else { + trace_esp_error_fifo_overrun(); + } + } else if (s->ti_wptr == TI_BUFSZ - 1) { trace_esp_error_fifo_overrun(); } else { s->ti_size++; @@ -567,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), @@ -578,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/megasas.c b/hw/scsi/megasas.c index a63a58155..e968302fd 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -29,7 +29,7 @@ #include "hw/scsi/scsi.h" #include "block/scsi.h" #include "trace.h" - +#include "qapi/error.h" #include "mfi.h" #define MEGASAS_VERSION_GEN1 "1.70" @@ -48,11 +48,7 @@ #define MEGASAS_FLAG_USE_JBOD 0 #define MEGASAS_MASK_USE_JBOD (1 << MEGASAS_FLAG_USE_JBOD) -#define MEGASAS_FLAG_USE_MSI 1 -#define MEGASAS_MASK_USE_MSI (1 << MEGASAS_FLAG_USE_MSI) -#define MEGASAS_FLAG_USE_MSIX 2 -#define MEGASAS_MASK_USE_MSIX (1 << MEGASAS_FLAG_USE_MSIX) -#define MEGASAS_FLAG_USE_QUEUE64 3 +#define MEGASAS_FLAG_USE_QUEUE64 1 #define MEGASAS_MASK_USE_QUEUE64 (1 << MEGASAS_FLAG_USE_QUEUE64) static const char *mfi_frame_desc[] = { @@ -96,6 +92,8 @@ typedef struct MegasasState { int busy; int diag; int adp_reset; + OnOffAuto msi; + OnOffAuto msix; MegasasCmd *event_cmd; int event_locale; @@ -157,14 +155,9 @@ static bool megasas_use_queue64(MegasasState *s) return s->flags & MEGASAS_MASK_USE_QUEUE64; } -static bool megasas_use_msi(MegasasState *s) -{ - return s->flags & MEGASAS_MASK_USE_MSI; -} - static bool megasas_use_msix(MegasasState *s) { - return s->flags & MEGASAS_MASK_USE_MSIX; + return s->msix != ON_OFF_AUTO_OFF; } static bool megasas_is_jbod(MegasasState *s) @@ -410,17 +403,14 @@ static void megasas_encode_lba(uint8_t *cdb, uint64_t lba, static uint64_t megasas_fw_time(void) { struct tm curtime; - uint64_t bcd_time; qemu_get_timedate(&curtime, 0); - bcd_time = ((uint64_t)curtime.tm_sec & 0xff) << 48 | + return ((uint64_t)curtime.tm_sec & 0xff) << 48 | ((uint64_t)curtime.tm_min & 0xff) << 40 | ((uint64_t)curtime.tm_hour & 0xff) << 32 | ((uint64_t)curtime.tm_mday & 0xff) << 24 | ((uint64_t)curtime.tm_mon & 0xff) << 16 | ((uint64_t)(curtime.tm_year + 1900) & 0xffff); - - return bcd_time; } /* @@ -650,7 +640,9 @@ static int megasas_init_firmware(MegasasState *s, MegasasCmd *cmd) pa_hi = le32_to_cpu(initq->pi_addr_hi); s->producer_pa = ((uint64_t) pa_hi << 32) | pa_lo; s->reply_queue_head = ldl_le_pci_dma(pcid, s->producer_pa); + s->reply_queue_head %= MEGASAS_MAX_FRAMES; s->reply_queue_tail = ldl_le_pci_dma(pcid, s->consumer_pa); + s->reply_queue_tail %= MEGASAS_MAX_FRAMES; flags = le32_to_cpu(initq->flags); if (flags & MFI_QUEUE_FLAG_CONTEXT64) { s->flags |= MEGASAS_MASK_USE_QUEUE64; @@ -771,6 +763,7 @@ static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd) ptr = memory_region_get_ram_ptr(&pci_dev->rom); memcpy(biosver, ptr + 0x41, 31); + biosver[31] = 0; memcpy(info.image_component[1].name, "BIOS", 4); memcpy(info.image_component[1].version, biosver, strlen((const char *)biosver)); @@ -1293,7 +1286,7 @@ static int megasas_dcmd_ld_get_info(MegasasState *s, MegasasCmd *cmd) static int megasas_dcmd_cfg_read(MegasasState *s, MegasasCmd *cmd) { - uint8_t data[4096]; + uint8_t data[4096] = { 0 }; struct mfi_config_data *info; int num_pd_disks = 0, array_offset, ld_offset; BusChild *kid; @@ -1446,7 +1439,7 @@ static int megasas_dcmd_set_properties(MegasasState *s, MegasasCmd *cmd) dcmd_size); return MFI_STAT_INVALID_PARAMETER; } - dma_buf_write((uint8_t *)&info, cmd->iov_size, &cmd->qsg); + dma_buf_write((uint8_t *)&info, dcmd_size, &cmd->qsg); trace_megasas_dcmd_unsupported(cmd->index, cmd->iov_size); return MFI_STAT_OK; } @@ -1988,11 +1981,7 @@ static void megasas_handle_frame(MegasasState *s, uint64_t frame_addr, break; } if (frame_status != MFI_STAT_INVALID_STATUS) { - if (cmd->frame) { - cmd->frame->header.cmd_status = frame_status; - } else { - megasas_frame_set_cmd_status(s, frame_addr, frame_status); - } + cmd->frame->header.cmd_status = frame_status; megasas_unmap_frame(s, cmd); megasas_complete_frame(s, cmd->context); } @@ -2309,9 +2298,7 @@ static void megasas_scsi_uninit(PCIDevice *d) if (megasas_use_msix(s)) { msix_uninit(d, &s->mmio_io, &s->mmio_io); } - if (megasas_use_msi(s)) { - msi_uninit(d); - } + msi_uninit(d); } static const struct SCSIBusInfo megasas_scsi_info = { @@ -2332,6 +2319,8 @@ static void megasas_scsi_realize(PCIDevice *dev, Error **errp) MegasasBaseClass *b = MEGASAS_DEVICE_GET_CLASS(s); uint8_t *pci_conf; int i, bar_type; + Error *err = NULL; + int ret; pci_conf = dev->config; @@ -2340,6 +2329,24 @@ static void megasas_scsi_realize(PCIDevice *dev, Error **errp) /* Interrupt pin 1 */ pci_conf[PCI_INTERRUPT_PIN] = 0x01; + if (s->msi != ON_OFF_AUTO_OFF) { + ret = msi_init(dev, 0x50, 1, true, false, &err); + /* Any error other than -ENOTSUP(board's MSI support is broken) + * is a programming error */ + assert(!ret || ret == -ENOTSUP); + if (ret && s->msi == ON_OFF_AUTO_ON) { + /* Can't satisfy user's explicit msi=on request, fail */ + error_append_hint(&err, "You have to use msi=auto (default) or " + "msi=off with this machine type.\n"); + error_propagate(errp, err); + return; + } else if (ret) { + /* With msi=auto, we fall back to MSI off silently */ + s->msi = ON_OFF_AUTO_OFF; + error_free(err); + } + } + memory_region_init_io(&s->mmio_io, OBJECT(s), &megasas_mmio_ops, s, "megasas-mmio", 0x4000); memory_region_init_io(&s->port_io, OBJECT(s), &megasas_port_ops, s, @@ -2347,14 +2354,10 @@ static void megasas_scsi_realize(PCIDevice *dev, Error **errp) memory_region_init_io(&s->queue_io, OBJECT(s), &megasas_queue_ops, s, "megasas-queue", 0x40000); - if (megasas_use_msi(s) && - msi_init(dev, 0x50, 1, true, false)) { - s->flags &= ~MEGASAS_MASK_USE_MSI; - } if (megasas_use_msix(s) && msix_init(dev, 15, &s->mmio_io, b->mmio_bar, 0x2000, &s->mmio_io, b->mmio_bar, 0x3800, 0x68)) { - s->flags &= ~MEGASAS_MASK_USE_MSIX; + s->msix = ON_OFF_AUTO_OFF; } if (pci_is_express(dev)) { pcie_endpoint_cap_init(dev, 0xa0); @@ -2422,10 +2425,8 @@ static Property megasas_properties_gen1[] = { MEGASAS_DEFAULT_FRAMES), DEFINE_PROP_STRING("hba_serial", MegasasState, hba_serial), DEFINE_PROP_UINT64("sas_address", MegasasState, sas_addr, 0), - DEFINE_PROP_BIT("use_msi", MegasasState, flags, - MEGASAS_FLAG_USE_MSI, false), - DEFINE_PROP_BIT("use_msix", MegasasState, flags, - MEGASAS_FLAG_USE_MSIX, false), + DEFINE_PROP_ON_OFF_AUTO("msi", MegasasState, msi, ON_OFF_AUTO_AUTO), + DEFINE_PROP_ON_OFF_AUTO("msix", MegasasState, msix, ON_OFF_AUTO_AUTO), DEFINE_PROP_BIT("use_jbod", MegasasState, flags, MEGASAS_FLAG_USE_JBOD, false), DEFINE_PROP_END_OF_LIST(), @@ -2438,10 +2439,8 @@ static Property megasas_properties_gen2[] = { MEGASAS_GEN2_DEFAULT_FRAMES), DEFINE_PROP_STRING("hba_serial", MegasasState, hba_serial), DEFINE_PROP_UINT64("sas_address", MegasasState, sas_addr, 0), - DEFINE_PROP_BIT("use_msi", MegasasState, flags, - MEGASAS_FLAG_USE_MSI, true), - DEFINE_PROP_BIT("use_msix", MegasasState, flags, - MEGASAS_FLAG_USE_MSIX, true), + DEFINE_PROP_ON_OFF_AUTO("msi", MegasasState, msi, ON_OFF_AUTO_AUTO), + DEFINE_PROP_ON_OFF_AUTO("msix", MegasasState, msix, ON_OFF_AUTO_AUTO), DEFINE_PROP_BIT("use_jbod", MegasasState, flags, MEGASAS_FLAG_USE_JBOD, false), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/scsi/mfi.h b/hw/scsi/mfi.h index 29d41775d..e67a5c0b4 100644 --- a/hw/scsi/mfi.h +++ b/hw/scsi/mfi.h @@ -30,8 +30,8 @@ * SUCH DAMAGE. */ -#ifndef MFI_REG_H -#define MFI_REG_H +#ifndef SCSI_MFI_H +#define SCSI_MFI_H /* * MegaRAID SAS MFI firmware definitions @@ -1269,4 +1269,4 @@ struct mfi_config_data { #define MFI_SCSI_MAX_CMDS 8 #define MFI_SCSI_MAX_CDB_LEN 16 -#endif /* MFI_REG_H */ +#endif /* SCSI_MFI_H */ diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c index 499c1465a..0e0a22f69 100644 --- a/hw/scsi/mptsas.c +++ b/hw/scsi/mptsas.c @@ -32,7 +32,7 @@ #include "hw/scsi/scsi.h" #include "block/scsi.h" #include "trace.h" - +#include "qapi/error.h" #include "mptsas.h" #include "mpi.h" @@ -63,7 +63,7 @@ static void mptsas_update_interrupt(MPTSASState *s) PCIDevice *pci = (PCIDevice *) s; uint32_t state = s->intr_status & ~(s->intr_mask | MPI_HIS_IOP_DOORBELL_STATUS); - if (s->msi_in_use && msi_enabled(pci)) { + if (msi_enabled(pci)) { if (state) { trace_mptsas_irq_msi(s); msi_notify(pci, 0); @@ -754,11 +754,6 @@ static void mptsas_fetch_request(MPTSASState *s) hwaddr addr; int size; - if (s->state != MPI_IOC_STATE_OPERATIONAL) { - mptsas_set_fault(s, MPI_IOCSTATUS_INVALID_STATE); - return; - } - /* Read the message header from the guest first. */ addr = s->host_mfa_high_addr | MPTSAS_FIFO_GET(s, request_post); pci_dma_read(pci, addr, req, sizeof(hdr)); @@ -789,6 +784,10 @@ static void mptsas_fetch_requests(void *opaque) { MPTSASState *s = opaque; + if (s->state != MPI_IOC_STATE_OPERATIONAL) { + mptsas_set_fault(s, MPI_IOCSTATUS_INVALID_STATE); + return; + } while (!MPTSAS_FIFO_EMPTY(s, request_post)) { mptsas_fetch_request(s); } @@ -1274,10 +1273,32 @@ static void mptsas_scsi_init(PCIDevice *dev, Error **errp) { DeviceState *d = DEVICE(dev); MPTSASState *s = MPT_SAS(dev); + Error *err = NULL; + int ret; dev->config[PCI_LATENCY_TIMER] = 0; dev->config[PCI_INTERRUPT_PIN] = 0x01; + if (s->msi != ON_OFF_AUTO_OFF) { + ret = msi_init(dev, 0, 1, true, false, &err); + /* Any error other than -ENOTSUP(board's MSI support is broken) + * is a programming error */ + assert(!ret || ret == -ENOTSUP); + if (ret && s->msi == ON_OFF_AUTO_ON) { + /* Can't satisfy user's explicit msi=on request, fail */ + error_append_hint(&err, "You have to use msi=auto (default) or " + "msi=off with this machine type.\n"); + error_propagate(errp, err); + return; + } + assert(!err || s->msi == ON_OFF_AUTO_AUTO); + /* With msi=auto, we fall back to MSI off silently */ + error_free(err); + + /* Only used for migration. */ + s->msi_in_use = (ret == 0); + } + memory_region_init_io(&s->mmio_io, OBJECT(s), &mptsas_mmio_ops, s, "mptsas-mmio", 0x4000); memory_region_init_io(&s->port_io, OBJECT(s), &mptsas_port_ops, s, @@ -1285,11 +1306,6 @@ static void mptsas_scsi_init(PCIDevice *dev, Error **errp) memory_region_init_io(&s->diag_io, OBJECT(s), &mptsas_diag_ops, s, "mptsas-diag", 0x10000); - if (s->msi_available && - msi_init(dev, 0, 1, true, false) >= 0) { - s->msi_in_use = true; - } - pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->port_io); pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_32, &s->mmio_io); @@ -1320,9 +1336,7 @@ static void mptsas_scsi_uninit(PCIDevice *dev) MPTSASState *s = MPT_SAS(dev); qemu_bh_delete(s->request_bh); - if (s->msi_in_use) { - msi_uninit(dev); - } + msi_uninit(dev); } static void mptsas_reset(DeviceState *dev) @@ -1359,7 +1373,6 @@ static const VMStateDescription vmstate_mptsas = { .fields = (VMStateField[]) { VMSTATE_PCI_DEVICE(dev, MPTSASState), VMSTATE_BOOL(msi_in_use, MPTSASState), - VMSTATE_UINT32(state, MPTSASState), VMSTATE_UINT8(who_init, MPTSASState), VMSTATE_UINT8(doorbell_state, MPTSASState), @@ -1404,7 +1417,7 @@ static const VMStateDescription vmstate_mptsas = { static Property mptsas_properties[] = { DEFINE_PROP_UINT64("sas_address", MPTSASState, sas_addr, 0), /* TODO: test MSI support under Windows */ - DEFINE_PROP_BIT("msi", MPTSASState, msi_available, 0, true), + DEFINE_PROP_ON_OFF_AUTO("msi", MPTSASState, msi, ON_OFF_AUTO_AUTO), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/scsi/mptsas.h b/hw/scsi/mptsas.h index 595f81fb5..0436a3391 100644 --- a/hw/scsi/mptsas.h +++ b/hw/scsi/mptsas.h @@ -27,7 +27,8 @@ struct MPTSASState { MemoryRegion diag_io; QEMUBH *request_bh; - uint32_t msi_available; + /* properties */ + OnOffAuto msi; uint64_t sas_addr; bool msi_in_use; diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index ad6f398c3..297216dfc 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -461,6 +461,14 @@ static bool scsi_target_emulate_inquiry(SCSITargetReq *r) return true; } +static size_t scsi_sense_len(SCSIRequest *req) +{ + if (req->dev->type == TYPE_SCANNER) + return SCSI_SENSE_LEN_SCANNER; + else + return SCSI_SENSE_LEN; +} + static int32_t scsi_target_send_command(SCSIRequest *req, uint8_t *buf) { SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req); @@ -477,7 +485,7 @@ static int32_t scsi_target_send_command(SCSIRequest *req, uint8_t *buf) } break; case REQUEST_SENSE: - scsi_target_alloc_buf(&r->req, SCSI_SENSE_LEN); + scsi_target_alloc_buf(&r->req, scsi_sense_len(req)); r->len = scsi_device_get_sense(r->req.dev, r->buf, MIN(req->cmd.xfer, r->buf_len), (req->cmd.buf[1] & 1) == 0); @@ -1132,6 +1140,29 @@ static int scsi_req_medium_changer_xfer(SCSICommand *cmd, SCSIDevice *dev, uint8 return 0; } +static int scsi_req_scanner_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) +{ + switch (buf[0]) { + /* Scanner commands */ + case OBJECT_POSITION: + cmd->xfer = 0; + break; + case SCAN: + cmd->xfer = buf[4]; + break; + case READ_10: + case SEND: + case GET_WINDOW: + case SET_WINDOW: + cmd->xfer = buf[8] | (buf[7] << 8) | (buf[6] << 16); + break; + default: + /* GET_DATA_BUFFER_STATUS xfer handled by scsi_req_xfer */ + return scsi_req_xfer(cmd, dev, buf); + } + + return 0; +} static void scsi_cmd_xfer_mode(SCSICommand *cmd) { @@ -1178,6 +1209,11 @@ static void scsi_cmd_xfer_mode(SCSICommand *cmd) case SEND_DVD_STRUCTURE: case PERSISTENT_RESERVE_OUT: case MAINTENANCE_OUT: + case SET_WINDOW: + case SCAN: + /* SCAN conflicts with START_STOP. START_STOP has cmd->xfer set to 0 for + * non-scanner devices, so we only get here for SCAN and not for START_STOP. + */ cmd->mode = SCSI_XFER_TO_DEV; break; case ATA_PASSTHROUGH_12: @@ -1258,6 +1294,9 @@ int scsi_req_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf) case TYPE_MEDIUM_CHANGER: rc = scsi_req_medium_changer_xfer(cmd, dev, buf); break; + case TYPE_SCANNER: + rc = scsi_req_scanner_length(cmd, dev, buf); + break; default: rc = scsi_req_xfer(cmd, dev, buf); break; diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index c3ce54a20..836a1553e 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -53,7 +53,21 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0) #define DEFAULT_MAX_UNMAP_SIZE (1 << 30) /* 1 GB */ #define DEFAULT_MAX_IO_SIZE INT_MAX /* 2 GB - 1 block */ -typedef struct SCSIDiskState SCSIDiskState; +#define TYPE_SCSI_DISK_BASE "scsi-disk-base" + +#define SCSI_DISK_BASE(obj) \ + OBJECT_CHECK(SCSIDiskState, (obj), TYPE_SCSI_DISK_BASE) +#define SCSI_DISK_BASE_CLASS(klass) \ + OBJECT_CLASS_CHECK(SCSIDiskClass, (klass), TYPE_SCSI_DISK_BASE) +#define SCSI_DISK_BASE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(SCSIDiskClass, (obj), TYPE_SCSI_DISK_BASE) + +typedef struct SCSIDiskClass { + SCSIDeviceClass parent_class; + DMAIOFunc *dma_readv; + DMAIOFunc *dma_writev; + bool (*need_fua_emulation)(SCSICommand *cmd); +} SCSIDiskClass; typedef struct SCSIDiskReq { SCSIRequest req; @@ -62,16 +76,18 @@ typedef struct SCSIDiskReq { uint32_t sector_count; uint32_t buflen; bool started; + bool need_fua_emulation; struct iovec iov; QEMUIOVector qiov; BlockAcctCookie acct; + unsigned char *status; } SCSIDiskReq; #define SCSI_DISK_F_REMOVABLE 0 #define SCSI_DISK_F_DPOFUA 1 #define SCSI_DISK_F_NO_REMOVABLE_DEVOPS 2 -struct SCSIDiskState +typedef struct SCSIDiskState { SCSIDevice qdev; uint32_t features; @@ -88,7 +104,7 @@ struct SCSIDiskState char *product; bool tray_open; bool tray_locked; -}; +} SCSIDiskState; static int scsi_handle_rw_error(SCSIDiskReq *r, int error, bool acct_failed); @@ -108,7 +124,7 @@ static void scsi_check_condition(SCSIDiskReq *r, SCSISense sense) scsi_req_complete(&r->req, CHECK_CONDITION); } -static uint32_t scsi_init_iovec(SCSIDiskReq *r, size_t size) +static void scsi_init_iovec(SCSIDiskReq *r, size_t size) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); @@ -118,7 +134,6 @@ static uint32_t scsi_init_iovec(SCSIDiskReq *r, size_t size) } r->iov.iov_len = MIN(r->sector_count * 512, r->buflen); qemu_iovec_init_external(&r->qiov, &r->iov, 1); - return r->qiov.size / 512; } static void scsi_disk_save_request(QEMUFile *f, SCSIRequest *req) @@ -162,6 +177,29 @@ static void scsi_disk_load_request(QEMUFile *f, SCSIRequest *req) qemu_iovec_init_external(&r->qiov, &r->iov, 1); } +static bool scsi_disk_req_check_error(SCSIDiskReq *r, int ret, bool acct_failed) +{ + if (r->req.io_canceled) { + scsi_req_cancel_complete(&r->req); + return true; + } + + if (ret < 0) { + return scsi_handle_rw_error(r, -ret, acct_failed); + } + + if (r->status && *r->status) { + if (acct_failed) { + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); + } + scsi_req_complete(&r->req, *r->status); + return true; + } + + return false; +} + static void scsi_aio_complete(void *opaque, int ret) { SCSIDiskReq *r = (SCSIDiskReq *)opaque; @@ -169,17 +207,10 @@ static void scsi_aio_complete(void *opaque, int ret) assert(r->req.aiocb != NULL); r->req.aiocb = NULL; - if (r->req.io_canceled) { - scsi_req_cancel_complete(&r->req); + if (scsi_disk_req_check_error(r, ret, true)) { goto done; } - if (ret < 0) { - if (scsi_handle_rw_error(r, -ret, true)) { - goto done; - } - } - block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); scsi_req_complete(&r->req, GOOD); @@ -218,13 +249,9 @@ static void scsi_write_do_fua(SCSIDiskReq *r) SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); assert(r->req.aiocb == NULL); + assert(!r->req.io_canceled); - if (r->req.io_canceled) { - scsi_req_cancel_complete(&r->req); - goto done; - } - - if (scsi_is_cmd_fua(&r->req.cmd)) { + if (r->need_fua_emulation) { block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, 0, BLOCK_ACCT_FLUSH); r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, scsi_aio_complete, r); @@ -232,26 +259,16 @@ static void scsi_write_do_fua(SCSIDiskReq *r) } scsi_req_complete(&r->req, GOOD); - -done: scsi_req_unref(&r->req); } static void scsi_dma_complete_noio(SCSIDiskReq *r, int ret) { assert(r->req.aiocb == NULL); - - if (r->req.io_canceled) { - scsi_req_cancel_complete(&r->req); + if (scsi_disk_req_check_error(r, ret, false)) { goto done; } - if (ret < 0) { - if (scsi_handle_rw_error(r, -ret, false)) { - goto done; - } - } - r->sector += r->sector_count; r->sector_count = 0; if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { @@ -289,17 +306,10 @@ static void scsi_read_complete(void * opaque, int ret) assert(r->req.aiocb != NULL); r->req.aiocb = NULL; - if (r->req.io_canceled) { - scsi_req_cancel_complete(&r->req); + if (scsi_disk_req_check_error(r, ret, true)) { goto done; } - if (ret < 0) { - if (scsi_handle_rw_error(r, -ret, true)) { - goto done; - } - } - block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); DPRINTF("Data ready tag=0x%x len=%zd\n", r->req.tag, r->qiov.size); @@ -316,35 +326,29 @@ done: static void scsi_do_read(SCSIDiskReq *r, int ret) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - uint32_t n; + SCSIDiskClass *sdc = (SCSIDiskClass *) object_get_class(OBJECT(s)); assert (r->req.aiocb == NULL); - - if (r->req.io_canceled) { - scsi_req_cancel_complete(&r->req); + if (scsi_disk_req_check_error(r, ret, false)) { goto done; } - if (ret < 0) { - if (scsi_handle_rw_error(r, -ret, false)) { - goto done; - } - } - /* The request is used as the AIO opaque value, so add a ref. */ scsi_req_ref(&r->req); if (r->req.sg) { dma_acct_start(s->qdev.conf.blk, &r->acct, r->req.sg, BLOCK_ACCT_READ); r->req.resid -= r->req.sg->size; - r->req.aiocb = dma_blk_read(s->qdev.conf.blk, r->req.sg, r->sector, - scsi_dma_complete, r); + r->req.aiocb = dma_blk_io(blk_get_aio_context(s->qdev.conf.blk), + r->req.sg, r->sector << BDRV_SECTOR_BITS, + sdc->dma_readv, r, scsi_dma_complete, r, + DMA_DIRECTION_FROM_DEVICE); } else { - n = scsi_init_iovec(r, SCSI_DMA_BUF_SIZE); + scsi_init_iovec(r, SCSI_DMA_BUF_SIZE); block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, - n * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ); - r->req.aiocb = blk_aio_readv(s->qdev.conf.blk, r->sector, &r->qiov, n, - scsi_read_complete, r); + r->qiov.size, BLOCK_ACCT_READ); + r->req.aiocb = sdc->dma_readv(r->sector << BDRV_SECTOR_BITS, &r->qiov, + scsi_read_complete, r, r); } done: @@ -399,7 +403,7 @@ static void scsi_read_data(SCSIRequest *req) first = !r->started; r->started = true; - if (first && scsi_is_cmd_fua(&r->req.cmd)) { + if (first && r->need_fua_emulation) { block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, 0, BLOCK_ACCT_FLUSH); r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, scsi_do_read_cb, r); @@ -456,18 +460,10 @@ static void scsi_write_complete_noio(SCSIDiskReq *r, int ret) uint32_t n; assert (r->req.aiocb == NULL); - - if (r->req.io_canceled) { - scsi_req_cancel_complete(&r->req); + if (scsi_disk_req_check_error(r, ret, false)) { goto done; } - if (ret < 0) { - if (scsi_handle_rw_error(r, -ret, false)) { - goto done; - } - } - n = r->qiov.size / 512; r->sector += n; r->sector_count -= n; @@ -504,7 +500,7 @@ static void scsi_write_data(SCSIRequest *req) { SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - uint32_t n; + SCSIDiskClass *sdc = (SCSIDiskClass *) object_get_class(OBJECT(s)); /* No data transfer may already be in progress */ assert(r->req.aiocb == NULL); @@ -541,14 +537,15 @@ static void scsi_write_data(SCSIRequest *req) if (r->req.sg) { dma_acct_start(s->qdev.conf.blk, &r->acct, r->req.sg, BLOCK_ACCT_WRITE); r->req.resid -= r->req.sg->size; - r->req.aiocb = dma_blk_write(s->qdev.conf.blk, r->req.sg, r->sector, - scsi_dma_complete, r); + r->req.aiocb = dma_blk_io(blk_get_aio_context(s->qdev.conf.blk), + r->req.sg, r->sector << BDRV_SECTOR_BITS, + sdc->dma_writev, r, scsi_dma_complete, r, + DMA_DIRECTION_TO_DEVICE); } else { - n = r->qiov.size / 512; block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, - n * BDRV_SECTOR_SIZE, BLOCK_ACCT_WRITE); - r->req.aiocb = blk_aio_writev(s->qdev.conf.blk, r->sector, &r->qiov, n, - scsi_write_complete, r); + r->qiov.size, BLOCK_ACCT_WRITE); + r->req.aiocb = sdc->dma_writev(r->sector << BDRV_SECTOR_BITS, &r->qiov, + scsi_write_complete, r, r); } } @@ -1600,18 +1597,10 @@ static void scsi_unmap_complete_noio(UnmapCBData *data, int ret) uint32_t nb_sectors; assert(r->req.aiocb == NULL); - - if (r->req.io_canceled) { - scsi_req_cancel_complete(&r->req); + if (scsi_disk_req_check_error(r, ret, false)) { goto done; } - if (ret < 0) { - if (scsi_handle_rw_error(r, -ret, false)) { - goto done; - } - } - if (data->count > 0) { sector_num = ldq_be_p(&data->inbuf[0]); nb_sectors = ldl_be_p(&data->inbuf[8]) & 0xffffffffULL; @@ -1620,10 +1609,10 @@ static void scsi_unmap_complete_noio(UnmapCBData *data, int ret) goto done; } - r->req.aiocb = blk_aio_discard(s->qdev.conf.blk, - sector_num * (s->qdev.blocksize / 512), - nb_sectors * (s->qdev.blocksize / 512), - scsi_unmap_complete, data); + r->req.aiocb = blk_aio_pdiscard(s->qdev.conf.blk, + sector_num * s->qdev.blocksize, + nb_sectors * s->qdev.blocksize, + scsi_unmap_complete, data); data->count--; data->inbuf += 16; return; @@ -1711,17 +1700,10 @@ static void scsi_write_same_complete(void *opaque, int ret) assert(r->req.aiocb != NULL); r->req.aiocb = NULL; - if (r->req.io_canceled) { - scsi_req_cancel_complete(&r->req); + if (scsi_disk_req_check_error(r, ret, true)) { goto done; } - if (ret < 0) { - if (scsi_handle_rw_error(r, -ret, true)) { - goto done; - } - } - block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); data->nb_sectors -= data->iov.iov_len / 512; @@ -1730,13 +1712,13 @@ static void scsi_write_same_complete(void *opaque, int ret) if (data->iov.iov_len) { block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, data->iov.iov_len, BLOCK_ACCT_WRITE); - /* blk_aio_write doesn't like the qiov size being different from - * nb_sectors, make sure they match. - */ + /* Reinitialize qiov, to handle unaligned WRITE SAME request + * where final qiov may need smaller size */ qemu_iovec_init_external(&data->qiov, &data->iov, 1); - r->req.aiocb = blk_aio_writev(s->qdev.conf.blk, data->sector, - &data->qiov, data->iov.iov_len / 512, - scsi_write_same_complete, data); + r->req.aiocb = blk_aio_pwritev(s->qdev.conf.blk, + data->sector << BDRV_SECTOR_BITS, + &data->qiov, 0, + scsi_write_same_complete, data); return; } @@ -1780,9 +1762,9 @@ static void scsi_disk_emulate_write_same(SCSIDiskReq *r, uint8_t *inbuf) block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, nb_sectors * s->qdev.blocksize, BLOCK_ACCT_WRITE); - r->req.aiocb = blk_aio_write_zeroes(s->qdev.conf.blk, - r->req.cmd.lba * (s->qdev.blocksize / 512), - nb_sectors * (s->qdev.blocksize / 512), + r->req.aiocb = blk_aio_pwrite_zeroes(s->qdev.conf.blk, + r->req.cmd.lba * s->qdev.blocksize, + nb_sectors * s->qdev.blocksize, flags, scsi_aio_complete, r); return; } @@ -1803,9 +1785,10 @@ static void scsi_disk_emulate_write_same(SCSIDiskReq *r, uint8_t *inbuf) scsi_req_ref(&r->req); block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, data->iov.iov_len, BLOCK_ACCT_WRITE); - r->req.aiocb = blk_aio_writev(s->qdev.conf.blk, data->sector, - &data->qiov, data->iov.iov_len / 512, - scsi_write_same_complete, data); + r->req.aiocb = blk_aio_pwritev(s->qdev.conf.blk, + data->sector << BDRV_SECTOR_BITS, + &data->qiov, 0, + scsi_write_same_complete, data); } static void scsi_disk_emulate_write_data(SCSIRequest *req) @@ -2077,13 +2060,13 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf) } break; case MODE_SELECT: - DPRINTF("Mode Select(6) (len %lu)\n", (long)r->req.cmd.xfer); + DPRINTF("Mode Select(6) (len %lu)\n", (unsigned long)r->req.cmd.xfer); break; case MODE_SELECT_10: - DPRINTF("Mode Select(10) (len %lu)\n", (long)r->req.cmd.xfer); + DPRINTF("Mode Select(10) (len %lu)\n", (unsigned long)r->req.cmd.xfer); break; case UNMAP: - DPRINTF("Unmap (len %lu)\n", (long)r->req.cmd.xfer); + DPRINTF("Unmap (len %lu)\n", (unsigned long)r->req.cmd.xfer); break; case VERIFY_10: case VERIFY_12: @@ -2097,7 +2080,7 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf) case WRITE_SAME_16: DPRINTF("WRITE SAME %d (len %lu)\n", req->cmd.buf[0] == WRITE_SAME_10 ? 10 : 16, - (long)r->req.cmd.xfer); + (unsigned long)r->req.cmd.xfer); break; default: DPRINTF("Unknown SCSI command (%2.2x=%s)\n", buf[0], @@ -2137,6 +2120,7 @@ static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf) { SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); + SCSIDiskClass *sdc = (SCSIDiskClass *) object_get_class(OBJECT(s)); uint32_t len; uint8_t command; @@ -2195,6 +2179,7 @@ static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf) scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE)); return 0; } + r->need_fua_emulation = sdc->need_fua_emulation(&r->req.cmd); if (r->sector_count == 0) { scsi_req_complete(&r->req, GOOD); } @@ -2324,6 +2309,7 @@ static void scsi_realize(SCSIDevice *dev, Error **errp) return; } } + blkconf_apply_backend_options(&dev->conf); if (s->qdev.conf.discard_granularity == -1) { s->qdev.conf.discard_granularity = @@ -2577,16 +2563,145 @@ static void scsi_block_realize(SCSIDevice *dev, Error **errp) scsi_generic_read_device_identification(&s->qdev); } +typedef struct SCSIBlockReq { + SCSIDiskReq req; + sg_io_hdr_t io_header; + + /* Selected bytes of the original CDB, copied into our own CDB. */ + uint8_t cmd, cdb1, group_number; + + /* CDB passed to SG_IO. */ + uint8_t cdb[16]; +} SCSIBlockReq; + +static BlockAIOCB *scsi_block_do_sgio(SCSIBlockReq *req, + int64_t offset, QEMUIOVector *iov, + int direction, + BlockCompletionFunc *cb, void *opaque) +{ + sg_io_hdr_t *io_header = &req->io_header; + SCSIDiskReq *r = &req->req; + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + int nb_logical_blocks; + uint64_t lba; + BlockAIOCB *aiocb; + + /* This is not supported yet. It can only happen if the guest does + * reads and writes that are not aligned to one logical sectors + * _and_ cover multiple MemoryRegions. + */ + assert(offset % s->qdev.blocksize == 0); + assert(iov->size % s->qdev.blocksize == 0); + + io_header->interface_id = 'S'; + + /* The data transfer comes from the QEMUIOVector. */ + io_header->dxfer_direction = direction; + io_header->dxfer_len = iov->size; + io_header->dxferp = (void *)iov->iov; + io_header->iovec_count = iov->niov; + assert(io_header->iovec_count == iov->niov); /* no overflow! */ + + /* Build a new CDB with the LBA and length patched in, in case + * DMA helpers split the transfer in multiple segments. Do not + * build a CDB smaller than what the guest wanted, and only build + * a larger one if strictly necessary. + */ + io_header->cmdp = req->cdb; + lba = offset / s->qdev.blocksize; + nb_logical_blocks = io_header->dxfer_len / s->qdev.blocksize; + + if ((req->cmd >> 5) == 0 && lba <= 0x1ffff) { + /* 6-byte CDB */ + stl_be_p(&req->cdb[0], lba | (req->cmd << 24)); + req->cdb[4] = nb_logical_blocks; + req->cdb[5] = 0; + io_header->cmd_len = 6; + } else if ((req->cmd >> 5) <= 1 && lba <= 0xffffffffULL) { + /* 10-byte CDB */ + req->cdb[0] = (req->cmd & 0x1f) | 0x20; + req->cdb[1] = req->cdb1; + stl_be_p(&req->cdb[2], lba); + req->cdb[6] = req->group_number; + stw_be_p(&req->cdb[7], nb_logical_blocks); + req->cdb[9] = 0; + io_header->cmd_len = 10; + } else if ((req->cmd >> 5) != 4 && lba <= 0xffffffffULL) { + /* 12-byte CDB */ + req->cdb[0] = (req->cmd & 0x1f) | 0xA0; + req->cdb[1] = req->cdb1; + stl_be_p(&req->cdb[2], lba); + stl_be_p(&req->cdb[6], nb_logical_blocks); + req->cdb[10] = req->group_number; + req->cdb[11] = 0; + io_header->cmd_len = 12; + } else { + /* 16-byte CDB */ + req->cdb[0] = (req->cmd & 0x1f) | 0x80; + req->cdb[1] = req->cdb1; + stq_be_p(&req->cdb[2], lba); + stl_be_p(&req->cdb[10], nb_logical_blocks); + req->cdb[14] = req->group_number; + req->cdb[15] = 0; + io_header->cmd_len = 16; + } + + /* The rest is as in scsi-generic.c. */ + io_header->mx_sb_len = sizeof(r->req.sense); + io_header->sbp = r->req.sense; + io_header->timeout = UINT_MAX; + io_header->usr_ptr = r; + io_header->flags |= SG_FLAG_DIRECT_IO; + + aiocb = blk_aio_ioctl(s->qdev.conf.blk, SG_IO, io_header, cb, opaque); + assert(aiocb != NULL); + return aiocb; +} + +static bool scsi_block_no_fua(SCSICommand *cmd) +{ + return false; +} + +static BlockAIOCB *scsi_block_dma_readv(int64_t offset, + QEMUIOVector *iov, + BlockCompletionFunc *cb, void *cb_opaque, + void *opaque) +{ + SCSIBlockReq *r = opaque; + return scsi_block_do_sgio(r, offset, iov, + SG_DXFER_FROM_DEV, cb, cb_opaque); +} + +static BlockAIOCB *scsi_block_dma_writev(int64_t offset, + QEMUIOVector *iov, + BlockCompletionFunc *cb, void *cb_opaque, + void *opaque) +{ + SCSIBlockReq *r = opaque; + return scsi_block_do_sgio(r, offset, iov, + SG_DXFER_TO_DEV, cb, cb_opaque); +} + static bool scsi_block_is_passthrough(SCSIDiskState *s, uint8_t *buf) { switch (buf[0]) { + case VERIFY_10: + case VERIFY_12: + case VERIFY_16: + /* Check if BYTCHK == 0x01 (data-out buffer contains data + * for the number of logical blocks specified in the length + * field). For other modes, do not use scatter/gather operation. + */ + if ((buf[1] & 6) != 2) { + return false; + } + break; + case READ_6: case READ_10: case READ_12: case READ_16: - case VERIFY_10: - case VERIFY_12: - case VERIFY_16: case WRITE_6: case WRITE_10: case WRITE_12: @@ -2594,21 +2709,8 @@ static bool scsi_block_is_passthrough(SCSIDiskState *s, uint8_t *buf) case WRITE_VERIFY_10: case WRITE_VERIFY_12: case WRITE_VERIFY_16: - /* If we are not using O_DIRECT, we might read stale data from the - * host cache if writes were made using other commands than these - * ones (such as WRITE SAME or EXTENDED COPY, etc.). So, without - * O_DIRECT everything must go through SG_IO. - */ - if (!(blk_get_flags(s->qdev.conf.blk) & BDRV_O_NOCACHE)) { - break; - } - - /* MMC writing cannot be done via pread/pwrite, because it sometimes + /* MMC writing cannot be done via DMA helpers, because it sometimes * involves writing beyond the maximum LBA or to negative LBA (lead-in). - * And once you do these writes, reading from the block device is - * unreliable, too. It is even possible that reads deliver random data - * from the host page cache (this is probably a Linux bug). - * * We might use scsi_disk_dma_reqops as long as no writing commands are * seen, but performance usually isn't paramount on optical media. So, * just make scsi-block operate the same as scsi-generic for them. @@ -2626,6 +2728,55 @@ static bool scsi_block_is_passthrough(SCSIDiskState *s, uint8_t *buf) } +static int32_t scsi_block_dma_command(SCSIRequest *req, uint8_t *buf) +{ + SCSIBlockReq *r = (SCSIBlockReq *)req; + r->cmd = req->cmd.buf[0]; + switch (r->cmd >> 5) { + case 0: + /* 6-byte CDB. */ + r->cdb1 = r->group_number = 0; + break; + case 1: + /* 10-byte CDB. */ + r->cdb1 = req->cmd.buf[1]; + r->group_number = req->cmd.buf[6]; + break; + case 4: + /* 12-byte CDB. */ + r->cdb1 = req->cmd.buf[1]; + r->group_number = req->cmd.buf[10]; + break; + case 5: + /* 16-byte CDB. */ + r->cdb1 = req->cmd.buf[1]; + r->group_number = req->cmd.buf[14]; + break; + default: + abort(); + } + + if (r->cdb1 & 0xe0) { + /* Protection information is not supported. */ + scsi_check_condition(&r->req, SENSE_CODE(INVALID_FIELD)); + return 0; + } + + r->req.status = &r->io_header.status; + return scsi_disk_dma_command(req, buf); +} + +static const SCSIReqOps scsi_block_dma_reqops = { + .size = sizeof(SCSIBlockReq), + .free_req = scsi_free_request, + .send_command = scsi_block_dma_command, + .read_data = scsi_read_data, + .write_data = scsi_write_data, + .get_buf = scsi_get_buf, + .load_request = scsi_disk_load_request, + .save_request = scsi_disk_save_request, +}; + static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun, uint8_t *buf, void *hba_private) @@ -2636,7 +2787,7 @@ static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag, return scsi_req_alloc(&scsi_generic_req_ops, &s->qdev, tag, lun, hba_private); } else { - return scsi_req_alloc(&scsi_disk_dma_reqops, &s->qdev, tag, lun, + return scsi_req_alloc(&scsi_block_dma_reqops, &s->qdev, tag, lun, hba_private); } } @@ -2655,8 +2806,50 @@ static int scsi_block_parse_cdb(SCSIDevice *d, SCSICommand *cmd, #endif +static +BlockAIOCB *scsi_dma_readv(int64_t offset, QEMUIOVector *iov, + BlockCompletionFunc *cb, void *cb_opaque, + void *opaque) +{ + SCSIDiskReq *r = opaque; + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + return blk_aio_preadv(s->qdev.conf.blk, offset, iov, 0, cb, cb_opaque); +} + +static +BlockAIOCB *scsi_dma_writev(int64_t offset, QEMUIOVector *iov, + BlockCompletionFunc *cb, void *cb_opaque, + void *opaque) +{ + SCSIDiskReq *r = opaque; + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + return blk_aio_pwritev(s->qdev.conf.blk, offset, iov, 0, cb, cb_opaque); +} + +static void scsi_disk_base_class_initfn(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SCSIDiskClass *sdc = SCSI_DISK_BASE_CLASS(klass); + + dc->fw_name = "disk"; + dc->reset = scsi_disk_reset; + sdc->dma_readv = scsi_dma_readv; + sdc->dma_writev = scsi_dma_writev; + sdc->need_fua_emulation = scsi_is_cmd_fua; +} + +static const TypeInfo scsi_disk_base_info = { + .name = TYPE_SCSI_DISK_BASE, + .parent = TYPE_SCSI_DEVICE, + .class_init = scsi_disk_base_class_initfn, + .instance_size = sizeof(SCSIDiskState), + .class_size = sizeof(SCSIDiskClass), + .abstract = true, +}; + #define DEFINE_SCSI_DISK_PROPERTIES() \ DEFINE_BLOCK_PROPERTIES(SCSIDiskState, qdev.conf), \ + DEFINE_BLOCK_ERROR_PROPERTIES(SCSIDiskState, qdev.conf), \ DEFINE_PROP_STRING("ver", SCSIDiskState, version), \ DEFINE_PROP_STRING("serial", SCSIDiskState, serial), \ DEFINE_PROP_STRING("vendor", SCSIDiskState, vendor), \ @@ -2702,17 +2895,14 @@ static void scsi_hd_class_initfn(ObjectClass *klass, void *data) sc->realize = scsi_hd_realize; sc->alloc_req = scsi_new_request; sc->unit_attention_reported = scsi_disk_unit_attention_reported; - dc->fw_name = "disk"; dc->desc = "virtual SCSI disk"; - dc->reset = scsi_disk_reset; dc->props = scsi_hd_properties; dc->vmsd = &vmstate_scsi_disk_state; } static const TypeInfo scsi_hd_info = { .name = "scsi-hd", - .parent = TYPE_SCSI_DEVICE, - .instance_size = sizeof(SCSIDiskState), + .parent = TYPE_SCSI_DISK_BASE, .class_init = scsi_hd_class_initfn, }; @@ -2734,17 +2924,14 @@ static void scsi_cd_class_initfn(ObjectClass *klass, void *data) sc->realize = scsi_cd_realize; sc->alloc_req = scsi_new_request; sc->unit_attention_reported = scsi_disk_unit_attention_reported; - dc->fw_name = "disk"; dc->desc = "virtual SCSI CD-ROM"; - dc->reset = scsi_disk_reset; dc->props = scsi_cd_properties; dc->vmsd = &vmstate_scsi_disk_state; } static const TypeInfo scsi_cd_info = { .name = "scsi-cd", - .parent = TYPE_SCSI_DEVICE, - .instance_size = sizeof(SCSIDiskState), + .parent = TYPE_SCSI_DISK_BASE, .class_init = scsi_cd_class_initfn, }; @@ -2758,21 +2945,22 @@ static void scsi_block_class_initfn(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass); + SCSIDiskClass *sdc = SCSI_DISK_BASE_CLASS(klass); sc->realize = scsi_block_realize; sc->alloc_req = scsi_block_new_request; sc->parse_cdb = scsi_block_parse_cdb; - dc->fw_name = "disk"; + sdc->dma_readv = scsi_block_dma_readv; + sdc->dma_writev = scsi_block_dma_writev; + sdc->need_fua_emulation = scsi_block_no_fua; dc->desc = "SCSI block device passthrough"; - dc->reset = scsi_disk_reset; dc->props = scsi_block_properties; dc->vmsd = &vmstate_scsi_disk_state; } static const TypeInfo scsi_block_info = { .name = "scsi-block", - .parent = TYPE_SCSI_DEVICE, - .instance_size = sizeof(SCSIDiskState), + .parent = TYPE_SCSI_DISK_BASE, .class_init = scsi_block_class_initfn, }; #endif @@ -2810,13 +2998,13 @@ static void scsi_disk_class_initfn(ObjectClass *klass, void *data) static const TypeInfo scsi_disk_info = { .name = "scsi-disk", - .parent = TYPE_SCSI_DEVICE, - .instance_size = sizeof(SCSIDiskState), + .parent = TYPE_SCSI_DISK_BASE, .class_init = scsi_disk_class_initfn, }; static void scsi_disk_register_types(void) { + type_register_static(&scsi_disk_base_info); type_register_static(&scsi_hd_info); type_register_static(&scsi_cd_info); #ifdef __linux__ diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index 7459465f6..7a588a7ad 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -222,6 +222,19 @@ static void scsi_read_complete(void * opaque, int ret) r->buf[3] |= 0x80; } } + if (s->type == TYPE_DISK && + r->req.cmd.buf[0] == INQUIRY && + r->req.cmd.buf[2] == 0xb0) { + uint32_t max_transfer = + blk_get_max_transfer(s->conf.blk) / s->blocksize; + + assert(max_transfer); + stl_be_p(&r->buf[8], max_transfer); + /* Also take care of the opt xfer len. */ + if (ldl_be_p(&r->buf[12]) > max_transfer) { + stl_be_p(&r->buf[12], max_transfer); + } + } scsi_req_data(&r->req, len); scsi_req_unref(&r->req); } @@ -567,10 +580,7 @@ const SCSIReqOps scsi_generic_req_ops = { static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun, uint8_t *buf, void *hba_private) { - SCSIRequest *req; - - req = scsi_req_alloc(&scsi_generic_req_ops, d, tag, lun, hba_private); - return req; + return scsi_req_alloc(&scsi_generic_req_ops, d, tag, lun, hba_private); } static Property scsi_generic_properties[] = { diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c index b00edf7fd..8fbd50f66 100644 --- a/hw/scsi/spapr_vscsi.c +++ b/hw/scsi/spapr_vscsi.c @@ -698,7 +698,7 @@ static void vscsi_inquiry_no_target(VSCSIState *s, vscsi_req *req) uint8_t resp_data[36]; int rc, len, alen; - /* We dont do EVPD. Also check that page_code is 0 */ + /* We don't do EVPD. Also check that page_code is 0 */ if ((cdb[1] & 0x01) || cdb[2] != 0) { /* Send INVALID FIELD IN CDB */ vscsi_makeup_sense(s, req, ILLEGAL_REQUEST, 0x24, 0); diff --git a/hw/scsi/trace-events b/hw/scsi/trace-events new file mode 100644 index 000000000..ed64858fe --- /dev/null +++ b/hw/scsi/trace-events @@ -0,0 +1,204 @@ +# See docs/tracing.txt for syntax documentation. + +# hw/scsi/scsi-bus.c +scsi_req_alloc(int target, int lun, int tag) "target %d lun %d tag %d" +scsi_req_cancel(int target, int lun, int tag) "target %d lun %d tag %d" +scsi_req_data(int target, int lun, int tag, int len) "target %d lun %d tag %d len %d" +scsi_req_data_canceled(int target, int lun, int tag, int len) "target %d lun %d tag %d len %d" +scsi_req_dequeue(int target, int lun, int tag) "target %d lun %d tag %d" +scsi_req_continue(int target, int lun, int tag) "target %d lun %d tag %d" +scsi_req_continue_canceled(int target, int lun, int tag) "target %d lun %d tag %d" +scsi_req_parsed(int target, int lun, int tag, int cmd, int mode, int xfer) "target %d lun %d tag %d command %d dir %d length %d" +scsi_req_parsed_lba(int target, int lun, int tag, int cmd, uint64_t lba) "target %d lun %d tag %d command %d lba %"PRIu64 +scsi_req_parse_bad(int target, int lun, int tag, int cmd) "target %d lun %d tag %d command %d" +scsi_req_build_sense(int target, int lun, int tag, int key, int asc, int ascq) "target %d lun %d tag %d key %#02x asc %#02x ascq %#02x" +scsi_device_set_ua(int target, int lun, int key, int asc, int ascq) "target %d lun %d key %#02x asc %#02x ascq %#02x" +scsi_report_luns(int target, int lun, int tag) "target %d lun %d tag %d" +scsi_inquiry(int target, int lun, int tag, int cdb1, int cdb2) "target %d lun %d tag %d page %#02x/%#02x" +scsi_test_unit_ready(int target, int lun, int tag) "target %d lun %d tag %d" +scsi_request_sense(int target, int lun, int tag) "target %d lun %d tag %d" + +# hw/scsi/mptsas.c +mptsas_command_complete(void *dev, uint32_t ctx, uint32_t status, uint32_t resid) "dev %p context 0x%08x status %x resid %d" +mptsas_diag_read(void *dev, uint32_t addr, uint32_t val) "dev %p addr 0x%08x value 0x%08x" +mptsas_diag_write(void *dev, uint32_t addr, uint32_t val) "dev %p addr 0x%08x value 0x%08x" +mptsas_irq_intx(void *dev, int level) "dev %p level %d" +mptsas_irq_msi(void *dev) "dev %p " +mptsas_mmio_read(void *dev, uint32_t addr, uint32_t val) "dev %p addr 0x%08x value 0x%x" +mptsas_mmio_unhandled_read(void *dev, uint32_t addr) "dev %p addr 0x%08x" +mptsas_mmio_unhandled_write(void *dev, uint32_t addr, uint32_t val) "dev %p addr 0x%08x value 0x%x" +mptsas_mmio_write(void *dev, uint32_t addr, uint32_t val) "dev %p addr 0x%08x value 0x%x" +mptsas_process_message(void *dev, int msg, uint32_t ctx) "dev %p cmd %d context 0x%08x\n" +mptsas_process_scsi_io_request(void *dev, int bus, int target, int lun, uint64_t len) "dev %p dev %d:%d:%d length %"PRIu64"" +mptsas_reset(void *dev) "dev %p " +mptsas_scsi_overflow(void *dev, uint32_t ctx, uint64_t req, uint64_t found) "dev %p context 0x%08x: %"PRIu64"/%"PRIu64"" +mptsas_sgl_overflow(void *dev, uint32_t ctx, uint64_t req, uint64_t found) "dev %p context 0x%08x: %"PRIu64"/%"PRIu64"" +mptsas_unhandled_cmd(void *dev, uint32_t ctx, uint8_t msg_cmd) "dev %p context 0x%08x: Unhandled cmd %x" +mptsas_unhandled_doorbell_cmd(void *dev, int cmd) "dev %p value 0x%08x" + +# hw/scsi/mptconfig.c +mptsas_config_sas_device(void *dev, int address, int port, int phy_handle, int dev_handle, int page) "dev %p address %d (port %d, handles: phy %d dev %d) page %d" +mptsas_config_sas_phy(void *dev, int address, int port, int phy_handle, int dev_handle, int page) "dev %p address %d (port %d, handles: phy %d dev %d) page %d" + +# hw/scsi/megasas.c +megasas_init_firmware(uint64_t pa) "pa %" PRIx64 " " +megasas_init_queue(uint64_t queue_pa, int queue_len, uint64_t head, uint64_t tail, uint32_t flags) "queue at %" PRIx64 " len %d head %" PRIx64 " tail %" PRIx64 " flags %x" +megasas_initq_map_failed(int frame) "scmd %d: failed to map queue" +megasas_initq_mapped(uint64_t pa) "queue already mapped at %" PRIx64 +megasas_initq_mismatch(int queue_len, int fw_cmds) "queue size %d max fw cmds %d" +megasas_qf_mapped(unsigned int index) "skip mapped frame %x" +megasas_qf_new(unsigned int index, uint64_t frame) "frame %x addr %" PRIx64 +megasas_qf_busy(unsigned long pa) "all frames busy for frame %lx" +megasas_qf_enqueue(unsigned int index, unsigned int count, uint64_t context, unsigned int head, unsigned int tail, int busy) "frame %x count %d context %" PRIx64 " head %x tail %x busy %d" +megasas_qf_update(unsigned int head, unsigned int tail, unsigned int busy) "head %x tail %x busy %d" +megasas_qf_map_failed(int cmd, unsigned long frame) "scmd %d: frame %lu" +megasas_qf_complete_noirq(uint64_t context) "context %" PRIx64 " " +megasas_qf_complete(uint64_t context, unsigned int head, unsigned int tail, int busy) "context %" PRIx64 " head %x tail %x busy %d" +megasas_frame_busy(uint64_t addr) "frame %" PRIx64 " busy" +megasas_unhandled_frame_cmd(int cmd, uint8_t frame_cmd) "scmd %d: MFI cmd %x" +megasas_handle_scsi(const char *frame, int bus, int dev, int lun, void *sdev, unsigned long size) "%s dev %x/%x/%x sdev %p xfer %lu" +megasas_scsi_target_not_present(const char *frame, int bus, int dev, int lun) "%s dev %x/%x/%x" +megasas_scsi_invalid_cdb_len(const char *frame, int bus, int dev, int lun, int len) "%s dev %x/%x/%x invalid cdb len %d" +megasas_iov_read_overflow(int cmd, int bytes, int len) "scmd %d: %d/%d bytes" +megasas_iov_write_overflow(int cmd, int bytes, int len) "scmd %d: %d/%d bytes" +megasas_iov_read_underflow(int cmd, int bytes, int len) "scmd %d: %d/%d bytes" +megasas_iov_write_underflow(int cmd, int bytes, int len) "scmd %d: %d/%d bytes" +megasas_scsi_req_alloc_failed(const char *frame, int dev, int lun) "%s dev %x/%x" +megasas_scsi_read_start(int cmd, int len) "scmd %d: transfer %d bytes of data" +megasas_scsi_write_start(int cmd, int len) "scmd %d: transfer %d bytes of data" +megasas_scsi_nodata(int cmd) "scmd %d: no data to be transferred" +megasas_scsi_complete(int cmd, uint32_t status, int len, int xfer) "scmd %d: status %x, len %u/%u" +megasas_command_complete(int cmd, uint32_t status, uint32_t resid) "scmd %d: status %x, residual %d" +megasas_handle_io(int cmd, const char *frame, int dev, int lun, unsigned long lba, unsigned long count) "scmd %d: %s dev %x/%x lba %lx count %lu" +megasas_io_target_not_present(int cmd, const char *frame, int dev, int lun) "scmd %d: %s dev 1/%x/%x LUN not present" +megasas_io_read_start(int cmd, unsigned long lba, unsigned long count, unsigned long len) "scmd %d: start LBA %lx %lu blocks (%lu bytes)" +megasas_io_write_start(int cmd, unsigned long lba, unsigned long count, unsigned long len) "scmd %d: start LBA %lx %lu blocks (%lu bytes)" +megasas_io_complete(int cmd, uint32_t len) "scmd %d: %d bytes" +megasas_iovec_sgl_overflow(int cmd, int index, int limit) "scmd %d: iovec count %d limit %d" +megasas_iovec_sgl_underflow(int cmd, int index) "scmd %d: iovec count %d" +megasas_iovec_sgl_invalid(int cmd, int index, uint64_t pa, uint32_t len) "scmd %d: element %d pa %" PRIx64 " len %u" +megasas_iovec_overflow(int cmd, int len, int limit) "scmd %d: len %d limit %d" +megasas_iovec_underflow(int cmd, int len, int limit) "scmd %d: len %d limit %d" +megasas_handle_dcmd(int cmd, int opcode) "scmd %d: MFI DCMD opcode %x" +megasas_finish_dcmd(int cmd, int size) "scmd %d: MFI DCMD wrote %d bytes" +megasas_dcmd_req_alloc_failed(int cmd, const char *desc) "scmd %d: %s" +megasas_dcmd_internal_submit(int cmd, const char *desc, int dev) "scmd %d: %s to dev %d" +megasas_dcmd_internal_finish(int cmd, int opcode, int lun) "scmd %d: cmd %x lun %d" +megasas_dcmd_internal_invalid(int cmd, int opcode) "scmd %d: DCMD %x" +megasas_dcmd_unhandled(int cmd, int opcode, int len) "scmd %d: opcode %x, len %d" +megasas_dcmd_zero_sge(int cmd) "scmd %d: zero DCMD sge count" +megasas_dcmd_invalid_sge(int cmd, int count) "scmd %d: DCMD sge count %d" +megasas_dcmd_invalid_xfer_len(int cmd, unsigned long size, unsigned long max) "scmd %d: xfer len %ld, max %ld" +megasas_dcmd_enter(int cmd, const char *dcmd, int len) "scmd %d: DCMD %s len %d" +megasas_dcmd_dummy(int cmd, unsigned long size) "scmd %d: xfer len %ld" +megasas_dcmd_set_fw_time(int cmd, unsigned long time) "scmd %d: Set FW time %lx" +megasas_dcmd_pd_get_list(int cmd, int num, int max, int offset) "scmd %d: DCMD PD get list: %d / %d PDs, size %d" +megasas_dcmd_ld_get_list(int cmd, int num, int max) "scmd %d: DCMD LD get list: found %d / %d LDs" +megasas_dcmd_ld_get_info(int cmd, int ld_id) "scmd %d: dev %d" +megasas_dcmd_ld_list_query(int cmd, int flags) "scmd %d: query flags %x" +megasas_dcmd_pd_get_info(int cmd, int pd_id) "scmd %d: dev %d" +megasas_dcmd_pd_list_query(int cmd, int flags) "scmd %d: query flags %x" +megasas_dcmd_reset_ld(int cmd, int target_id) "scmd %d: dev %d" +megasas_dcmd_unsupported(int cmd, unsigned long size) "scmd %d: set properties len %ld" +megasas_abort_frame(int cmd, int abort_cmd) "scmd %d: frame %x" +megasas_abort_no_cmd(int cmd, uint64_t context) "scmd %d: no active command for frame context %" PRIx64 +megasas_abort_invalid_context(int cmd, uint64_t context, int abort_cmd) "scmd %d: invalid frame context %" PRIx64 " for abort frame %x" +megasas_reset(int fw_state) "firmware state %x" +megasas_init(int sges, int cmds, const char *mode) "Using %d sges, %d cmds, %s mode" +megasas_msix_raise(int vector) "vector %d" +megasas_msi_raise(int vector) "vector %d" +megasas_irq_lower(void) "INTx" +megasas_irq_raise(void) "INTx" +megasas_intr_enabled(void) "Interrupts enabled" +megasas_intr_disabled(void) "Interrupts disabled" +megasas_msix_enabled(int vector) "vector %d" +megasas_msi_enabled(int vector) "vector %d" +megasas_mmio_readl(const char *reg, uint32_t val) "reg %s: 0x%x" +megasas_mmio_invalid_readl(unsigned long addr) "addr 0x%lx" +megasas_mmio_writel(const char *reg, uint32_t val) "reg %s: 0x%x" +megasas_mmio_invalid_writel(uint32_t addr, uint32_t val) "addr 0x%x: 0x%x" + +# hw/scsi/vmw_pvscsi.c +pvscsi_ring_init_data(uint32_t txr_len_log2, uint32_t rxr_len_log2) "TX/RX rings logarithms set to %d/%d" +pvscsi_ring_init_msg(uint32_t len_log2) "MSG ring logarithm set to %d" +pvscsi_ring_flush_cmp(uint64_t filled_cmp_ptr) "new production counter of completion ring is 0x%"PRIx64 +pvscsi_ring_flush_msg(uint64_t filled_cmp_ptr) "new production counter of message ring is 0x%"PRIx64 +pvscsi_update_irq_level(bool raise, uint64_t mask, uint64_t status) "interrupt level set to %d (MASK: 0x%"PRIx64", STATUS: 0x%"PRIx64")" +pvscsi_update_irq_msi(void) "sending MSI notification" +pvscsi_cmp_ring_put(unsigned long addr) "got completion descriptor 0x%lx" +pvscsi_msg_ring_put(unsigned long addr) "got message descriptor 0x%lx" +pvscsi_complete_request(uint64_t context, uint64_t len, uint8_t sense_key) "completion: ctx: 0x%"PRIx64", len: 0x%"PRIx64", sense key: %u" +pvscsi_get_sg_list(int nsg, size_t size) "get SG list: depth: %u, size: %zu" +pvscsi_get_next_sg_elem(uint32_t flags) "unknown flags in SG element (val: 0x%x)" +pvscsi_command_complete_not_found(uint32_t tag) "can't find request for tag 0x%x" +pvscsi_command_complete_data_run(void) "not all data required for command transferred" +pvscsi_command_complete_sense_len(int len) "sense information length is %d bytes" +pvscsi_convert_sglist(uint64_t context, unsigned long addr, uint32_t resid) "element: ctx: 0x%"PRIx64" addr: 0x%lx, len: %ul" +pvscsi_process_req_descr(uint8_t cmd, uint64_t ctx) "SCSI cmd 0x%x, ctx: 0x%"PRIx64 +pvscsi_process_req_descr_unknown_device(void) "command directed to unknown device rejected" +pvscsi_process_req_descr_invalid_dir(void) "command with invalid transfer direction rejected" +pvscsi_process_io(unsigned long addr) "got descriptor 0x%lx" +pvscsi_on_cmd_noimpl(const char* cmd) "unimplemented command %s ignored" +pvscsi_on_cmd_reset_dev(uint32_t tgt, int lun, void* dev) "PVSCSI_CMD_RESET_DEVICE[target %u lun %d (dev 0x%p)]" +pvscsi_on_cmd_arrived(const char* cmd) "command %s arrived" +pvscsi_on_cmd_abort(uint64_t ctx, uint32_t tgt) "command PVSCSI_CMD_ABORT_CMD for ctx 0x%"PRIx64", target %u" +pvscsi_on_cmd_unknown(uint64_t cmd_id) "unknown command %"PRIx64 +pvscsi_on_cmd_unknown_data(uint32_t data) "data for unknown command 0x:%x" +pvscsi_io_write(const char* cmd, uint64_t val) "%s write: %"PRIx64 +pvscsi_io_write_unknown(unsigned long addr, unsigned sz, uint64_t val) "unknown write address: 0x%lx size: %u bytes value: 0x%"PRIx64 +pvscsi_io_read(const char* cmd, uint64_t status) "%s read: 0x%"PRIx64 +pvscsi_io_read_unknown(unsigned long addr, unsigned sz) "unknown read address: 0x%lx size: %u bytes" +pvscsi_init_msi_fail(int res) "failed to initialize MSI, error %d" +pvscsi_state(const char* state) "starting %s ..." +pvscsi_tx_rings_ppn(const char* label, uint64_t ppn) "%s page: %"PRIx64 +pvscsi_tx_rings_num_pages(const char* label, uint32_t num) "Number of %s pages: %u" + +# hw/scsi/esp.c +esp_error_fifo_overrun(void) "FIFO overrun" +esp_error_unhandled_command(uint32_t val) "unhandled command (%2.2x)" +esp_error_invalid_write(uint32_t val, uint32_t addr) "invalid write of 0x%02x at [0x%x]" +esp_raise_irq(void) "Raise IRQ" +esp_lower_irq(void) "Lower IRQ" +esp_dma_enable(void) "Raise enable" +esp_dma_disable(void) "Lower enable" +esp_get_cmd(uint32_t dmalen, int target) "len %d target %d" +esp_do_busid_cmd(uint8_t busid) "busid 0x%x" +esp_handle_satn_stop(uint32_t cmdlen) "cmdlen %d" +esp_write_response(uint32_t status) "Transfer status (status=%d)" +esp_do_dma(uint32_t cmdlen, uint32_t len) "command len %d + %d" +esp_command_complete(void) "SCSI Command complete" +esp_command_complete_unexpected(void) "SCSI command completed unexpectedly" +esp_command_complete_fail(void) "Command failed" +esp_transfer_data(uint32_t dma_left, int32_t ti_size) "transfer %d/%d" +esp_handle_ti(uint32_t minlen) "Transfer Information len %d" +esp_handle_ti_cmd(uint32_t cmdlen) "command len %d" +esp_mem_readb(uint32_t saddr, uint8_t reg) "reg[%d]: 0x%2.2x" +esp_mem_writeb(uint32_t saddr, uint8_t reg, uint32_t val) "reg[%d]: 0x%2.2x -> 0x%2.2x" +esp_mem_writeb_cmd_nop(uint32_t val) "NOP (%2.2x)" +esp_mem_writeb_cmd_flush(uint32_t val) "Flush FIFO (%2.2x)" +esp_mem_writeb_cmd_reset(uint32_t val) "Chip reset (%2.2x)" +esp_mem_writeb_cmd_bus_reset(uint32_t val) "Bus reset (%2.2x)" +esp_mem_writeb_cmd_iccs(uint32_t val) "Initiator Command Complete Sequence (%2.2x)" +esp_mem_writeb_cmd_msgacc(uint32_t val) "Message Accepted (%2.2x)" +esp_mem_writeb_cmd_pad(uint32_t val) "Transfer padding (%2.2x)" +esp_mem_writeb_cmd_satn(uint32_t val) "Set ATN (%2.2x)" +esp_mem_writeb_cmd_rstatn(uint32_t val) "Reset ATN (%2.2x)" +esp_mem_writeb_cmd_sel(uint32_t val) "Select without ATN (%2.2x)" +esp_mem_writeb_cmd_selatn(uint32_t val) "Select with ATN (%2.2x)" +esp_mem_writeb_cmd_selatns(uint32_t val) "Select with ATN & stop (%2.2x)" +esp_mem_writeb_cmd_ensel(uint32_t val) "Enable selection (%2.2x)" +esp_mem_writeb_cmd_dissel(uint32_t val) "Disable selection (%2.2x)" + +# hw/scsi/esp-pci.c +esp_pci_error_invalid_dma_direction(void) "invalid DMA transfer direction" +esp_pci_error_invalid_read(uint32_t reg) "read access outside bounds (reg 0x%x)" +esp_pci_error_invalid_write(uint32_t reg) "write access outside bounds (reg 0x%x)" +esp_pci_error_invalid_write_dma(uint32_t val, uint32_t addr) "invalid write of 0x%02x at [0x%x]" +esp_pci_dma_read(uint32_t saddr, uint32_t reg) "reg[%d]: 0x%8.8x" +esp_pci_dma_write(uint32_t saddr, uint32_t reg, uint32_t val) "reg[%d]: 0x%8.8x -> 0x%8.8x" +esp_pci_dma_idle(uint32_t val) "IDLE (%.8x)" +esp_pci_dma_blast(uint32_t val) "BLAST (%.8x)" +esp_pci_dma_abort(uint32_t val) "ABORT (%.8x)" +esp_pci_dma_start(uint32_t val) "START (%.8x)" +esp_pci_sbac_read(uint32_t reg) "sbac: 0x%8.8x" +esp_pci_sbac_write(uint32_t reg, uint32_t val) "sbac: 0x%8.8x -> 0x%8.8x" diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c index 9261d51da..5b2694615 100644 --- a/hw/scsi/vhost-scsi.c +++ b/hw/scsi/vhost-scsi.c @@ -15,8 +15,9 @@ */ #include "qemu/osdep.h" -#include "qapi/error.h" +#include <linux/vhost.h> #include <sys/ioctl.h> +#include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/queue.h" #include "monitor/monitor.h" @@ -27,7 +28,6 @@ #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio-access.h" #include "hw/fw-path-provider.h" -#include "linux/vhost.h" #include "qemu/cutils.h" /* Features supported by host kernel. */ @@ -248,7 +248,7 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp) s->dev.backend_features = 0; ret = vhost_dev_init(&s->dev, (void *)(uintptr_t)vhostfd, - VHOST_BACKEND_TYPE_KERNEL); + VHOST_BACKEND_TYPE_KERNEL, 0); if (ret < 0) { error_setg(errp, "vhost-scsi: vhost initialization failed: %s", strerror(-ret)); diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c index 1a49f1e4b..b173b9494 100644 --- a/hw/scsi/virtio-scsi-dataplane.c +++ b/hw/scsi/virtio-scsi-dataplane.c @@ -15,9 +15,9 @@ #include "hw/virtio/virtio-scsi.h" #include "qemu/error-report.h" #include "sysemu/block-backend.h" -#include <hw/scsi/scsi.h> -#include <block/scsi.h> -#include <hw/virtio/virtio-bus.h> +#include "hw/scsi/scsi.h" +#include "block/scsi.h" +#include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio-access.h" /* Context: QEMU global mutex held */ @@ -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 30415c6a9..ce57ef624 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -20,9 +20,9 @@ #include "qemu/error-report.h" #include "qemu/iov.h" #include "sysemu/block-backend.h" -#include <hw/scsi/scsi.h> -#include <block/scsi.h> -#include <hw/virtio/virtio-bus.h> +#include "hw/scsi/scsi.h" +#include "block/scsi.h" +#include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio-access.h" static inline int virtio_scsi_get_lun(uint8_t *lun) @@ -185,7 +185,7 @@ static void virtio_scsi_save_request(QEMUFile *f, SCSIRequest *sreq) { VirtIOSCSIReq *req = sreq->hba_private; VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(req->dev); - uint32_t n = virtio_queue_get_id(req->vq) - 2; + uint32_t n = virtio_get_queue_index(req->vq) - 2; assert(n < vs->conf.num_queues); qemu_put_be32s(f, &n); @@ -663,27 +663,17 @@ static void virtio_scsi_reset(VirtIODevice *vdev) /* The device does not have anything to save beyond the virtio data. * Request data is saved with callbacks from SCSI devices. */ -static void virtio_scsi_save(QEMUFile *f, void *opaque) +static void virtio_scsi_save(QEMUFile *f, void *opaque, size_t size) { VirtIODevice *vdev = VIRTIO_DEVICE(opaque); - VirtIOSCSI *s = VIRTIO_SCSI(vdev); - - if (s->dataplane_started) { - virtio_scsi_dataplane_stop(s); - } virtio_save(vdev, f); } -static int virtio_scsi_load(QEMUFile *f, void *opaque, int version_id) +static int virtio_scsi_load(QEMUFile *f, void *opaque, size_t size) { VirtIODevice *vdev = VIRTIO_DEVICE(opaque); - int ret; - ret = virtio_load(vdev, f, version_id); - if (ret) { - return ret; - } - return 0; + return virtio_load(vdev, f, 1); } void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev, @@ -773,22 +763,6 @@ static void virtio_scsi_change(SCSIBus *bus, SCSIDevice *dev, SCSISense sense) } } -static void virtio_scsi_blk_insert_notifier(Notifier *n, void *data) -{ - VirtIOSCSIBlkChangeNotifier *cn = DO_UPCAST(VirtIOSCSIBlkChangeNotifier, - n, n); - assert(cn->sd->conf.blk == data); - blk_op_block_all(cn->sd->conf.blk, cn->s->blocker); -} - -static void virtio_scsi_blk_remove_notifier(Notifier *n, void *data) -{ - VirtIOSCSIBlkChangeNotifier *cn = DO_UPCAST(VirtIOSCSIBlkChangeNotifier, - n, n); - assert(cn->sd->conf.blk == data); - blk_op_unblock_all(cn->sd->conf.blk, cn->s->blocker); -} - static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { @@ -797,29 +771,13 @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, SCSIDevice *sd = SCSI_DEVICE(dev); if (s->ctx && !s->dataplane_fenced) { - VirtIOSCSIBlkChangeNotifier *insert_notifier, *remove_notifier; - if (blk_op_is_blocked(sd->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) { return; } - blk_op_block_all(sd->conf.blk, s->blocker); aio_context_acquire(s->ctx); blk_set_aio_context(sd->conf.blk, s->ctx); aio_context_release(s->ctx); - insert_notifier = g_new0(VirtIOSCSIBlkChangeNotifier, 1); - insert_notifier->n.notify = virtio_scsi_blk_insert_notifier; - insert_notifier->s = s; - insert_notifier->sd = sd; - blk_add_insert_bs_notifier(sd->conf.blk, &insert_notifier->n); - QTAILQ_INSERT_TAIL(&s->insert_notifiers, insert_notifier, next); - - remove_notifier = g_new0(VirtIOSCSIBlkChangeNotifier, 1); - remove_notifier->n.notify = virtio_scsi_blk_remove_notifier; - remove_notifier->s = s; - remove_notifier->sd = sd; - blk_add_remove_bs_notifier(sd->conf.blk, &remove_notifier->n); - QTAILQ_INSERT_TAIL(&s->remove_notifiers, remove_notifier, next); } if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) { @@ -835,7 +793,6 @@ static void virtio_scsi_hotunplug(HotplugHandler *hotplug_dev, DeviceState *dev, VirtIODevice *vdev = VIRTIO_DEVICE(hotplug_dev); VirtIOSCSI *s = VIRTIO_SCSI(vdev); SCSIDevice *sd = SCSI_DEVICE(dev); - VirtIOSCSIBlkChangeNotifier *insert_notifier, *remove_notifier; if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) { virtio_scsi_push_event(s, sd, @@ -843,28 +800,6 @@ static void virtio_scsi_hotunplug(HotplugHandler *hotplug_dev, DeviceState *dev, VIRTIO_SCSI_EVT_RESET_REMOVED); } - if (s->ctx) { - blk_op_unblock_all(sd->conf.blk, s->blocker); - } - - QTAILQ_FOREACH(insert_notifier, &s->insert_notifiers, next) { - if (insert_notifier->sd == sd) { - notifier_remove(&insert_notifier->n); - QTAILQ_REMOVE(&s->insert_notifiers, insert_notifier, next); - g_free(insert_notifier); - break; - } - } - - QTAILQ_FOREACH(remove_notifier, &s->remove_notifiers, next) { - if (remove_notifier->sd == sd) { - notifier_remove(&remove_notifier->n); - QTAILQ_REMOVE(&s->remove_notifiers, remove_notifier, next); - g_free(remove_notifier); - break; - } - } - qdev_simple_device_unplug_cb(hotplug_dev, dev, errp); } @@ -884,8 +819,9 @@ static struct SCSIBusInfo virtio_scsi_scsi_info = { }; void virtio_scsi_common_realize(DeviceState *dev, Error **errp, - HandleOutput ctrl, HandleOutput evt, - HandleOutput cmd) + VirtIOHandleOutput ctrl, + VirtIOHandleOutput evt, + VirtIOHandleOutput cmd) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIOSCSICommon *s = VIRTIO_SCSI_COMMON(dev); @@ -906,13 +842,10 @@ void virtio_scsi_common_realize(DeviceState *dev, Error **errp, s->sense_size = VIRTIO_SCSI_SENSE_DEFAULT_SIZE; s->cdb_size = VIRTIO_SCSI_CDB_DEFAULT_SIZE; - s->ctrl_vq = virtio_add_queue(vdev, VIRTIO_SCSI_VQ_SIZE, - ctrl); - s->event_vq = virtio_add_queue(vdev, VIRTIO_SCSI_VQ_SIZE, - evt); + s->ctrl_vq = virtio_add_queue_aio(vdev, VIRTIO_SCSI_VQ_SIZE, ctrl); + s->event_vq = virtio_add_queue_aio(vdev, VIRTIO_SCSI_VQ_SIZE, evt); for (i = 0; i < s->conf.num_queues; i++) { - s->cmd_vqs[i] = virtio_add_queue(vdev, VIRTIO_SCSI_VQ_SIZE, - cmd); + s->cmd_vqs[i] = virtio_add_queue_aio(vdev, VIRTIO_SCSI_VQ_SIZE, cmd); } if (s->conf.iothread) { @@ -924,7 +857,6 @@ static void virtio_scsi_device_realize(DeviceState *dev, Error **errp) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIOSCSI *s = VIRTIO_SCSI(dev); - static int virtio_scsi_id; Error *err = NULL; virtio_scsi_common_realize(dev, &err, virtio_scsi_handle_ctrl, @@ -947,14 +879,6 @@ static void virtio_scsi_device_realize(DeviceState *dev, Error **errp) return; } } - - register_savevm(dev, "virtio-scsi", virtio_scsi_id++, 1, - virtio_scsi_save, virtio_scsi_load, s); - - error_setg(&s->blocker, "block device is in use by data plane"); - - QTAILQ_INIT(&s->insert_notifiers); - QTAILQ_INIT(&s->remove_notifiers); } static void virtio_scsi_instance_init(Object *obj) @@ -978,11 +902,6 @@ void virtio_scsi_common_unrealize(DeviceState *dev, Error **errp) static void virtio_scsi_device_unrealize(DeviceState *dev, Error **errp) { - VirtIOSCSI *s = VIRTIO_SCSI(dev); - - error_free(s->blocker); - - unregister_savevm(dev, "virtio-scsi", s); virtio_scsi_common_unrealize(dev, errp); } @@ -999,6 +918,8 @@ static Property virtio_scsi_properties[] = { DEFINE_PROP_END_OF_LIST(), }; +VMSTATE_VIRTIO_DEVICE(scsi, 1, virtio_scsi_load, virtio_scsi_save); + static void virtio_scsi_common_class_init(ObjectClass *klass, void *data) { VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); @@ -1015,6 +936,7 @@ static void virtio_scsi_class_init(ObjectClass *klass, void *data) HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); dc->props = virtio_scsi_properties; + dc->vmsd = &vmstate_virtio_scsi; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); vdc->realize = virtio_scsi_device_realize; vdc->unrealize = virtio_scsi_device_unrealize; diff --git a/hw/scsi/vmw_pvscsi.c b/hw/scsi/vmw_pvscsi.c index e690b4ec0..5116f4ad6 100644 --- a/hw/scsi/vmw_pvscsi.c +++ b/hw/scsi/vmw_pvscsi.c @@ -28,7 +28,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/scsi/scsi.h" -#include <block/scsi.h> +#include "block/scsi.h" #include "hw/pci/msi.h" #include "vmw_pvscsi.h" #include "trace.h" @@ -63,7 +63,7 @@ typedef struct PVSCSIClass { #define PVSCSI_DEVICE_GET_CLASS(obj) \ OBJECT_GET_CLASS(PVSCSIClass, (obj), TYPE_PVSCSI) -/* Compatability flags for migration */ +/* Compatibility flags for migration */ #define PVSCSI_COMPAT_OLD_PCI_CONFIGURATION_BIT 0 #define PVSCSI_COMPAT_OLD_PCI_CONFIGURATION \ (1 << PVSCSI_COMPAT_OLD_PCI_CONFIGURATION_BIT) @@ -121,8 +121,7 @@ typedef struct { uint8_t msg_ring_info_valid; /* Whether message ring initialized */ uint8_t use_msg; /* Whether to use message ring */ - uint8_t msi_used; /* Whether MSI support was installed successfully */ - + uint8_t msi_used; /* For migration compatibility */ PVSCSIRingInfo rings; /* Data transfer rings manager */ uint32_t resetting; /* Reset in progress */ @@ -153,7 +152,7 @@ pvscsi_log2(uint32_t input) return log; } -static void +static int pvscsi_ring_init_data(PVSCSIRingInfo *m, PVSCSICmdDescSetupRings *ri) { int i; @@ -161,6 +160,10 @@ pvscsi_ring_init_data(PVSCSIRingInfo *m, PVSCSICmdDescSetupRings *ri) uint32_t req_ring_size, cmp_ring_size; m->rs_pa = ri->ringsStatePPN << VMW_PAGE_SHIFT; + if ((ri->reqRingNumPages > PVSCSI_SETUP_RINGS_MAX_NUM_PAGES) + || (ri->cmpRingNumPages > PVSCSI_SETUP_RINGS_MAX_NUM_PAGES)) { + return -1; + } req_ring_size = ri->reqRingNumPages * PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE; cmp_ring_size = ri->cmpRingNumPages * PVSCSI_MAX_NUM_CMP_ENTRIES_PER_PAGE; txr_len_log2 = pvscsi_log2(req_ring_size - 1); @@ -192,15 +195,20 @@ pvscsi_ring_init_data(PVSCSIRingInfo *m, PVSCSICmdDescSetupRings *ri) /* Flush ring state page changes */ smp_wmb(); + + return 0; } -static void +static int pvscsi_ring_init_msg(PVSCSIRingInfo *m, PVSCSICmdDescSetupMsgRing *ri) { int i; uint32_t len_log2; uint32_t ring_size; + if (ri->numPages > PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES) { + return -1; + } ring_size = ri->numPages * PVSCSI_MAX_NUM_MSG_ENTRIES_PER_PAGE; len_log2 = pvscsi_log2(ring_size - 1); @@ -220,6 +228,8 @@ pvscsi_ring_init_msg(PVSCSIRingInfo *m, PVSCSICmdDescSetupMsgRing *ri) /* Flush ring state page changes */ smp_wmb(); + + return 0; } static void @@ -351,7 +361,7 @@ pvscsi_update_irq_status(PVSCSIState *s) trace_pvscsi_update_irq_level(should_raise, s->reg_interrupt_enabled, s->reg_interrupt_status); - if (s->msi_used && msi_enabled(d)) { + if (msi_enabled(d)) { if (should_raise) { trace_pvscsi_update_irq_msi(); msi_notify(d, PVSCSI_VECTOR_COMPLETION); @@ -770,7 +780,10 @@ pvscsi_on_cmd_setup_rings(PVSCSIState *s) trace_pvscsi_on_cmd_arrived("PVSCSI_CMD_SETUP_RINGS"); pvscsi_dbg_dump_tx_rings_config(rc); - pvscsi_ring_init_data(&s->rings, rc); + if (pvscsi_ring_init_data(&s->rings, rc) < 0) { + return PVSCSI_COMMAND_PROCESSING_FAILED; + } + s->rings_info_valid = TRUE; return PVSCSI_COMMAND_PROCESSING_SUCCEEDED; } @@ -850,7 +863,9 @@ pvscsi_on_cmd_setup_msg_ring(PVSCSIState *s) } if (s->rings_info_valid) { - pvscsi_ring_init_msg(&s->rings, rc); + if (pvscsi_ring_init_msg(&s->rings, rc) < 0) { + return PVSCSI_COMMAND_PROCESSING_FAILED; + } s->msg_ring_info_valid = TRUE; } return sizeof(PVSCSICmdDescSetupMsgRing) / sizeof(uint32_t); @@ -1040,22 +1055,20 @@ pvscsi_io_read(void *opaque, hwaddr addr, unsigned size) } -static bool +static void pvscsi_init_msi(PVSCSIState *s) { int res; PCIDevice *d = PCI_DEVICE(s); res = msi_init(d, PVSCSI_MSI_OFFSET(s), PVSCSI_MSIX_NUM_VECTORS, - PVSCSI_USE_64BIT, PVSCSI_PER_VECTOR_MASK); + PVSCSI_USE_64BIT, PVSCSI_PER_VECTOR_MASK, NULL); if (res < 0) { trace_pvscsi_init_msi_fail(res); s->msi_used = false; } else { s->msi_used = true; } - - return s->msi_used; } static void @@ -1063,9 +1076,7 @@ pvscsi_cleanup_msi(PVSCSIState *s) { PCIDevice *d = PCI_DEVICE(s); - if (s->msi_used) { - msi_uninit(d); - } + msi_uninit(d); } static const MemoryRegionOps pvscsi_ops = { diff --git a/hw/sd/milkymist-memcard.c b/hw/sd/milkymist-memcard.c index c04ff02fa..1f2f0ed44 100644 --- a/hw/sd/milkymist-memcard.c +++ b/hw/sd/milkymist-memcard.c @@ -18,7 +18,7 @@ * * * Specification available at: - * http://www.milkymist.org/socdoc/memcard.pdf + * http://milkymist.walle.cc/socdoc/memcard.pdf */ #include "qemu/osdep.h" diff --git a/hw/sd/pl181.c b/hw/sd/pl181.c index e87abb205..82c63a4fb 100644 --- a/hw/sd/pl181.c +++ b/hw/sd/pl181.c @@ -12,6 +12,8 @@ #include "sysemu/blockdev.h" #include "hw/sysbus.h" #include "hw/sd/sd.h" +#include "qemu/log.h" +#include "qapi/error.h" //#define DEBUG_PL181 1 @@ -480,43 +482,48 @@ static void pl181_reset(DeviceState *d) sd_set_cb(s->card, s->cardstatus[0], s->cardstatus[1]); } -static int pl181_init(SysBusDevice *sbd) +static void pl181_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - PL181State *s = PL181(dev); - DriveInfo *dinfo; + DeviceState *dev = DEVICE(obj); + PL181State *s = PL181(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - memory_region_init_io(&s->iomem, OBJECT(s), &pl181_ops, s, "pl181", 0x1000); + memory_region_init_io(&s->iomem, obj, &pl181_ops, s, "pl181", 0x1000); sysbus_init_mmio(sbd, &s->iomem); sysbus_init_irq(sbd, &s->irq[0]); sysbus_init_irq(sbd, &s->irq[1]); qdev_init_gpio_out(dev, s->cardstatus, 2); +} + +static void pl181_realize(DeviceState *dev, Error **errp) +{ + PL181State *s = PL181(dev); + DriveInfo *dinfo; + /* FIXME use a qdev drive property instead of drive_get_next() */ dinfo = drive_get_next(IF_SD); s->card = sd_init(dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, false); if (s->card == NULL) { - return -1; + error_setg(errp, "sd_init failed"); } - - return 0; } static void pl181_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); DeviceClass *k = DEVICE_CLASS(klass); - sdc->init = pl181_init; k->vmsd = &vmstate_pl181; k->reset = pl181_reset; /* Reason: init() method uses drive_get_next() */ k->cannot_instantiate_with_device_add_yet = true; + k->realize = pl181_realize; } static const TypeInfo pl181_info = { .name = TYPE_PL181, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PL181State), + .instance_init = pl181_init, .class_init = pl181_class_init, }; diff --git a/hw/sd/sd.c b/hw/sd/sd.c index b66e5d2db..87c6dc108 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -39,6 +39,7 @@ #include "hw/qdev-properties.h" #include "qemu/error-report.h" #include "qemu/timer.h" +#include "qemu/log.h" //#define DEBUG_SD 1 @@ -123,7 +124,6 @@ struct SDState { qemu_irq readonly_cb; qemu_irq inserted_cb; BlockBackend *blk; - uint8_t *buf; bool enable; }; @@ -551,7 +551,7 @@ static const VMStateDescription sd_vmstate = { VMSTATE_UINT64(data_start, SDState), VMSTATE_UINT32(data_offset, SDState), VMSTATE_UINT8_ARRAY(data, SDState, 512), - VMSTATE_BUFFER_POINTER_UNSAFE(buf, SDState, 1, 512), + VMSTATE_UNUSED_V(1, 512), VMSTATE_BOOL(enable, SDState), VMSTATE_END_OF_LIST() }, @@ -1577,57 +1577,17 @@ send_response: static void sd_blk_read(SDState *sd, uint64_t addr, uint32_t len) { - uint64_t end = addr + len; - DPRINTF("sd_blk_read: addr = 0x%08llx, len = %d\n", (unsigned long long) addr, len); - if (!sd->blk || blk_read(sd->blk, addr >> 9, sd->buf, 1) < 0) { + if (!sd->blk || blk_pread(sd->blk, addr, sd->data, len) < 0) { fprintf(stderr, "sd_blk_read: read error on host side\n"); - return; } - - if (end > (addr & ~511) + 512) { - memcpy(sd->data, sd->buf + (addr & 511), 512 - (addr & 511)); - - if (blk_read(sd->blk, end >> 9, sd->buf, 1) < 0) { - fprintf(stderr, "sd_blk_read: read error on host side\n"); - return; - } - memcpy(sd->data + 512 - (addr & 511), sd->buf, end & 511); - } else - memcpy(sd->data, sd->buf + (addr & 511), len); } static void sd_blk_write(SDState *sd, uint64_t addr, uint32_t len) { - uint64_t end = addr + len; - - if ((addr & 511) || len < 512) - if (!sd->blk || blk_read(sd->blk, addr >> 9, sd->buf, 1) < 0) { - fprintf(stderr, "sd_blk_write: read error on host side\n"); - return; - } - - if (end > (addr & ~511) + 512) { - memcpy(sd->buf + (addr & 511), sd->data, 512 - (addr & 511)); - if (blk_write(sd->blk, addr >> 9, sd->buf, 1) < 0) { - fprintf(stderr, "sd_blk_write: write error on host side\n"); - return; - } - - if (blk_read(sd->blk, end >> 9, sd->buf, 1) < 0) { - fprintf(stderr, "sd_blk_write: read error on host side\n"); - return; - } - memcpy(sd->buf, sd->data + 512 - (addr & 511), end & 511); - if (blk_write(sd->blk, end >> 9, sd->buf, 1) < 0) { - fprintf(stderr, "sd_blk_write: write error on host side\n"); - } - } else { - memcpy(sd->buf + (addr & 511), sd->data, len); - if (!sd->blk || blk_write(sd->blk, addr >> 9, sd->buf, 1) < 0) { - fprintf(stderr, "sd_blk_write: write error on host side\n"); - } + if (!sd->blk || blk_pwrite(sd->blk, addr, sd->data, len, 0) < 0) { + fprintf(stderr, "sd_blk_write: write error on host side\n"); } } @@ -1925,8 +1885,6 @@ static void sd_realize(DeviceState *dev, Error **errp) return; } - sd->buf = blk_blockalign(sd->blk, 512); - if (sd->blk) { blk_set_dev_ops(sd->blk, &sd_block_ops, sd); } diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index d28b5871f..01fbf228b 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -30,6 +30,7 @@ #include "qemu/timer.h" #include "qemu/bitops.h" #include "sdhci-internal.h" +#include "qemu/log.h" /* host controller debug messages */ #ifndef SDHC_DEBUG diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c index 075e4ed5d..3ff0886dd 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/sd/trace-events b/hw/sd/trace-events new file mode 100644 index 000000000..b17e7ba44 --- /dev/null +++ b/hw/sd/trace-events @@ -0,0 +1,5 @@ +# See docs/tracing.txt for syntax documentation. + +# hw/sd/milkymist-memcard.c +milkymist_memcard_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x" +milkymist_memcard_memory_write(uint32_t addr, uint32_t value) "addr %08x value %08x" diff --git a/hw/sh4/sh7750.c b/hw/sh4/sh7750.c index a1ea760f6..3132d559d 100644 --- a/hw/sh4/sh7750.c +++ b/hw/sh4/sh7750.c @@ -30,6 +30,7 @@ #include "sh7750_regnames.h" #include "hw/sh4/sh_intc.h" #include "cpu.h" +#include "exec/exec-all.h" #include "exec/address-spaces.h" #define NB_DEVICES 4 diff --git a/hw/sh4/sh7750_regnames.h b/hw/sh4/sh7750_regnames.h index 7463709b4..e3ba88636 100644 --- a/hw/sh4/sh7750_regnames.h +++ b/hw/sh4/sh7750_regnames.h @@ -1,6 +1,6 @@ -#ifndef _SH7750_REGNAMES_H -#define _SH7750_REGNAMES_H +#ifndef SH7750_REGNAMES_H +#define SH7750_REGNAMES_H const char *regname(uint32_t addr); -#endif /* _SH7750_REGNAMES_H */ +#endif /* SH7750_REGNAMES_H */ diff --git a/hw/sh4/sh7750_regs.h b/hw/sh4/sh7750_regs.h index 534aa4840..3e4554af3 100644 --- a/hw/sh4/sh7750_regs.h +++ b/hw/sh4/sh7750_regs.h @@ -16,8 +16,8 @@ * @(#) sh7750_regs.h,v 1.2.4.1 2003/09/04 18:46:00 joel Exp */ -#ifndef __SH7750_REGS_H__ -#define __SH7750_REGS_H__ +#ifndef SH7750_REGS_H +#define SH7750_REGS_H /* * All register has 2 addresses: in 0xff000000 - 0xffffffff (P4 address) and diff --git a/hw/sh4/sh_pci.c b/hw/sh4/sh_pci.c index e820a3230..1747628f3 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 f69a92f96..c3d375360 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 cb8a11110..74c710292 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 000000000..68b8b72e0 --- /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 000000000..56e8609c0 --- /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/sparc/leon3.c b/hw/sparc/leon3.c index dbae41f3a..6e1647841 100644 --- a/hw/sparc/leon3.c +++ b/hw/sparc/leon3.c @@ -171,7 +171,11 @@ static void leon3_generic_hw_init(MachineState *machine) } filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - bios_size = get_image_size(filename); + if (filename) { + bios_size = get_image_size(filename); + } else { + bios_size = -1; + } if (bios_size > prom_size) { fprintf(stderr, "qemu: could not load prom '%s': file too big\n", diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index 7bfc00abc..478fda820 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -1000,7 +1000,7 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, slavio_timer_init_all(hwdef->counter_base, slavio_irq[19], slavio_cpu_irq, smp_cpus); slavio_serial_ms_kbd_init(hwdef->ms_kb_base, slavio_irq[14], - display_type == DT_NOGRAPHIC, ESCC_CLOCK, 1); + !machine->enable_graphics, ESCC_CLOCK, 1); /* Slavio TTYA (base+4, Linux ttyS0) is the first QEMU serial device Slavio TTYB (base+0, Linux ttyS1) is the second QEMU serial device */ escc_init(hwdef->serial_base, slavio_irq[15], slavio_irq[15], diff --git a/hw/sparc/trace-events b/hw/sparc/trace-events new file mode 100644 index 000000000..30fb0373e --- /dev/null +++ b/hw/sparc/trace-events @@ -0,0 +1,11 @@ +# See docs/tracing.txt for syntax documentation. + +# hw/sparc/sun4m.c +sun4m_cpu_interrupt(unsigned int level) "Set CPU IRQ %d" +sun4m_cpu_reset_interrupt(unsigned int level) "Reset CPU IRQ %d" +sun4m_cpu_set_irq_raise(int level) "Raise CPU IRQ %d" +sun4m_cpu_set_irq_lower(int level) "Lower CPU IRQ %d" + +# hw/sparc/leon3.c +leon3_set_irq(int intno) "Set CPU IRQ %d" +leon3_reset_irq(int intno) "Reset CPU IRQ %d" diff --git a/hw/ssi/Makefile.objs b/hw/ssi/Makefile.objs index 9555825ac..c79a8dcd8 100644 --- a/hw/ssi/Makefile.objs +++ b/hw/ssi/Makefile.objs @@ -2,5 +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 000000000..d319e04a2 --- /dev/null +++ b/hw/ssi/aspeed_smc.c @@ -0,0 +1,469 @@ +/* + * 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); + + /* Pretend DMA is done (u-boot initialization) */ + s->regs[R_INTR_CTRL] = INTR_CTRL_DMA_STATUS; + + /* 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 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 (addr == s->r_conf || + addr == s->r_timings || + addr == s->r_ce_ctrl || + addr == R_INTR_CTRL || + (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + s->num_cs)) { + return s->regs[addr]; + } else { + qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n", + __func__, addr); + return 0; + } +} + +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 (addr == s->r_conf || + addr == s->r_timings || + addr == s->r_ce_ctrl) { + s->regs[addr] = value; + } else if (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + s->num_cs) { + s->regs[addr] = value; + aspeed_smc_update_cs(s); + } else { + qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n", + __func__, addr); + return; + } +} + +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/imx_spi.c b/hw/ssi/imx_spi.c new file mode 100644 index 000000000..422619981 --- /dev/null +++ b/hw/ssi/imx_spi.c @@ -0,0 +1,455 @@ +/* + * IMX SPI Controller + * + * Copyright (c) 2016 Jean-Christophe Dubois <jcd@tribudubois.net> + * + * 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/ssi/imx_spi.h" +#include "sysemu/sysemu.h" +#include "qemu/log.h" + +#ifndef DEBUG_IMX_SPI +#define DEBUG_IMX_SPI 0 +#endif + +#define DPRINTF(fmt, args...) \ + do { \ + if (DEBUG_IMX_SPI) { \ + fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_SPI, \ + __func__, ##args); \ + } \ + } while (0) + +static char const *imx_spi_reg_name(uint32_t reg) +{ + static char unknown[20]; + + switch (reg) { + case ECSPI_RXDATA: + return "ECSPI_RXDATA"; + case ECSPI_TXDATA: + return "ECSPI_TXDATA"; + case ECSPI_CONREG: + return "ECSPI_CONREG"; + case ECSPI_CONFIGREG: + return "ECSPI_CONFIGREG"; + case ECSPI_INTREG: + return "ECSPI_INTREG"; + case ECSPI_DMAREG: + return "ECSPI_DMAREG"; + case ECSPI_STATREG: + return "ECSPI_STATREG"; + case ECSPI_PERIODREG: + return "ECSPI_PERIODREG"; + case ECSPI_TESTREG: + return "ECSPI_TESTREG"; + case ECSPI_MSGDATA: + return "ECSPI_MSGDATA"; + default: + sprintf(unknown, "%d ?", reg); + return unknown; + } +} + +static const VMStateDescription vmstate_imx_spi = { + .name = TYPE_IMX_SPI, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_FIFO32(tx_fifo, IMXSPIState), + VMSTATE_FIFO32(rx_fifo, IMXSPIState), + VMSTATE_INT16(burst_length, IMXSPIState), + VMSTATE_UINT32_ARRAY(regs, IMXSPIState, ECSPI_MAX), + VMSTATE_END_OF_LIST() + }, +}; + +static void imx_spi_txfifo_reset(IMXSPIState *s) +{ + fifo32_reset(&s->tx_fifo); + s->regs[ECSPI_STATREG] |= ECSPI_STATREG_TE; + s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_TF; +} + +static void imx_spi_rxfifo_reset(IMXSPIState *s) +{ + fifo32_reset(&s->rx_fifo); + s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_RR; + s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_RF; + s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_RO; +} + +static void imx_spi_update_irq(IMXSPIState *s) +{ + int level; + + if (fifo32_is_empty(&s->rx_fifo)) { + s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_RR; + } else { + s->regs[ECSPI_STATREG] |= ECSPI_STATREG_RR; + } + + if (fifo32_is_full(&s->rx_fifo)) { + s->regs[ECSPI_STATREG] |= ECSPI_STATREG_RF; + } else { + s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_RF; + } + + if (fifo32_is_empty(&s->tx_fifo)) { + s->regs[ECSPI_STATREG] |= ECSPI_STATREG_TE; + } else { + s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_TE; + } + + if (fifo32_is_full(&s->tx_fifo)) { + s->regs[ECSPI_STATREG] |= ECSPI_STATREG_TF; + } else { + s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_TF; + } + + level = s->regs[ECSPI_STATREG] & s->regs[ECSPI_INTREG] ? 1 : 0; + + qemu_set_irq(s->irq, level); + + DPRINTF("IRQ level is %d\n", level); +} + +static uint8_t imx_spi_selected_channel(IMXSPIState *s) +{ + return EXTRACT(s->regs[ECSPI_CONREG], ECSPI_CONREG_CHANNEL_SELECT); +} + +static uint32_t imx_spi_burst_length(IMXSPIState *s) +{ + return EXTRACT(s->regs[ECSPI_CONREG], ECSPI_CONREG_BURST_LENGTH) + 1; +} + +static bool imx_spi_is_enabled(IMXSPIState *s) +{ + return s->regs[ECSPI_CONREG] & ECSPI_CONREG_EN; +} + +static bool imx_spi_channel_is_master(IMXSPIState *s) +{ + uint8_t mode = EXTRACT(s->regs[ECSPI_CONREG], ECSPI_CONREG_CHANNEL_MODE); + + return (mode & (1 << imx_spi_selected_channel(s))) ? true : false; +} + +static bool imx_spi_is_multiple_master_burst(IMXSPIState *s) +{ + uint8_t wave = EXTRACT(s->regs[ECSPI_CONFIGREG], ECSPI_CONFIGREG_SS_CTL); + + return imx_spi_channel_is_master(s) && + !(s->regs[ECSPI_CONREG] & ECSPI_CONREG_SMC) && + ((wave & (1 << imx_spi_selected_channel(s))) ? true : false); +} + +static void imx_spi_flush_txfifo(IMXSPIState *s) +{ + uint32_t tx; + uint32_t rx; + + DPRINTF("Begin: TX Fifo Size = %d, RX Fifo Size = %d\n", + fifo32_num_used(&s->tx_fifo), fifo32_num_used(&s->rx_fifo)); + + while (!fifo32_is_empty(&s->tx_fifo)) { + int tx_burst = 0; + int index = 0; + + if (s->burst_length <= 0) { + s->burst_length = imx_spi_burst_length(s); + + DPRINTF("Burst length = %d\n", s->burst_length); + + if (imx_spi_is_multiple_master_burst(s)) { + s->regs[ECSPI_CONREG] |= ECSPI_CONREG_XCH; + } + } + + tx = fifo32_pop(&s->tx_fifo); + + DPRINTF("data tx:0x%08x\n", tx); + + tx_burst = MIN(s->burst_length, 32); + + rx = 0; + + while (tx_burst) { + uint8_t byte = tx & 0xff; + + DPRINTF("writing 0x%02x\n", (uint32_t)byte); + + /* We need to write one byte at a time */ + byte = ssi_transfer(s->bus, byte); + + DPRINTF("0x%02x read\n", (uint32_t)byte); + + tx = tx >> 8; + rx |= (byte << (index * 8)); + + /* Remove 8 bits from the actual burst */ + tx_burst -= 8; + s->burst_length -= 8; + index++; + } + + DPRINTF("data rx:0x%08x\n", rx); + + if (fifo32_is_full(&s->rx_fifo)) { + s->regs[ECSPI_STATREG] |= ECSPI_STATREG_RO; + } else { + fifo32_push(&s->rx_fifo, (uint8_t)rx); + } + + if (s->burst_length <= 0) { + s->regs[ECSPI_CONREG] &= ~ECSPI_CONREG_XCH; + + if (!imx_spi_is_multiple_master_burst(s)) { + s->regs[ECSPI_STATREG] |= ECSPI_STATREG_TC; + break; + } + } + } + + if (fifo32_is_empty(&s->tx_fifo)) { + s->regs[ECSPI_STATREG] |= ECSPI_STATREG_TC; + } + + /* TODO: We should also use TDR and RDR bits */ + + DPRINTF("End: TX Fifo Size = %d, RX Fifo Size = %d\n", + fifo32_num_used(&s->tx_fifo), fifo32_num_used(&s->rx_fifo)); +} + +static void imx_spi_reset(DeviceState *dev) +{ + IMXSPIState *s = IMX_SPI(dev); + + DPRINTF("\n"); + + memset(s->regs, 0, sizeof(s->regs)); + + s->regs[ECSPI_STATREG] = 0x00000003; + + imx_spi_rxfifo_reset(s); + imx_spi_txfifo_reset(s); + + imx_spi_update_irq(s); + + s->burst_length = 0; +} + +static uint64_t imx_spi_read(void *opaque, hwaddr offset, unsigned size) +{ + uint32_t value = 0; + IMXSPIState *s = opaque; + uint32_t index = offset >> 2; + + if (index >= ECSPI_MAX) { + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_SPI, __func__, offset); + return 0; + } + + switch (index) { + case ECSPI_RXDATA: + if (!imx_spi_is_enabled(s)) { + value = 0; + } else if (fifo32_is_empty(&s->rx_fifo)) { + /* value is undefined */ + value = 0xdeadbeef; + } else { + /* read from the RX FIFO */ + value = fifo32_pop(&s->rx_fifo); + } + + break; + case ECSPI_TXDATA: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Trying to read from TX FIFO\n", + TYPE_IMX_SPI, __func__); + + /* Reading from TXDATA gives 0 */ + + break; + case ECSPI_MSGDATA: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Trying to read from MSG FIFO\n", + TYPE_IMX_SPI, __func__); + + /* Reading from MSGDATA gives 0 */ + + break; + default: + value = s->regs[index]; + break; + } + + DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx_spi_reg_name(index), value); + + imx_spi_update_irq(s); + + return (uint64_t)value; +} + +static void imx_spi_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + IMXSPIState *s = opaque; + uint32_t index = offset >> 2; + uint32_t change_mask; + + if (index >= ECSPI_MAX) { + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_IMX_SPI, __func__, offset); + return; + } + + DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx_spi_reg_name(index), + (uint32_t)value); + + change_mask = s->regs[index] ^ value; + + switch (index) { + case ECSPI_RXDATA: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Trying to write to RX FIFO\n", + TYPE_IMX_SPI, __func__); + break; + case ECSPI_TXDATA: + case ECSPI_MSGDATA: + /* Is there any difference between TXDATA and MSGDATA ? */ + /* I'll have to look in the linux driver */ + if (!imx_spi_is_enabled(s)) { + /* Ignore writes if device is disabled */ + break; + } else if (fifo32_is_full(&s->tx_fifo)) { + /* Ignore writes if queue is full */ + break; + } + + fifo32_push(&s->tx_fifo, (uint32_t)value); + + if (imx_spi_channel_is_master(s) && + (s->regs[ECSPI_CONREG] & ECSPI_CONREG_SMC)) { + /* + * Start emitting if current channel is master and SMC bit is + * set. + */ + imx_spi_flush_txfifo(s); + } + + break; + case ECSPI_STATREG: + /* the RO and TC bits are write-one-to-clear */ + value &= ECSPI_STATREG_RO | ECSPI_STATREG_TC; + s->regs[ECSPI_STATREG] &= ~value; + + break; + case ECSPI_CONREG: + s->regs[ECSPI_CONREG] = value; + + if (!imx_spi_is_enabled(s)) { + /* device is disabled, so this is a reset */ + imx_spi_reset(DEVICE(s)); + return; + } + + if (imx_spi_channel_is_master(s)) { + int i; + + /* We are in master mode */ + + for (i = 0; i < 4; i++) { + qemu_set_irq(s->cs_lines[i], + i == imx_spi_selected_channel(s) ? 0 : 1); + } + + if ((value & change_mask & ECSPI_CONREG_SMC) && + !fifo32_is_empty(&s->tx_fifo)) { + /* SMC bit is set and TX FIFO has some slots filled in */ + imx_spi_flush_txfifo(s); + } else if ((value & change_mask & ECSPI_CONREG_XCH) && + !(value & ECSPI_CONREG_SMC)) { + /* This is a request to start emitting */ + imx_spi_flush_txfifo(s); + } + } + + break; + default: + s->regs[index] = value; + + break; + } + + imx_spi_update_irq(s); +} + +static const struct MemoryRegionOps imx_spi_ops = { + .read = imx_spi_read, + .write = imx_spi_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void imx_spi_realize(DeviceState *dev, Error **errp) +{ + IMXSPIState *s = IMX_SPI(dev); + int i; + + s->bus = ssi_create_bus(dev, "spi"); + + memory_region_init_io(&s->iomem, OBJECT(dev), &imx_spi_ops, s, + TYPE_IMX_SPI, 0x1000); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); + + ssi_auto_connect_slaves(dev, s->cs_lines, s->bus); + + for (i = 0; i < 4; ++i) { + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->cs_lines[i]); + } + + s->burst_length = 0; + + fifo32_create(&s->tx_fifo, ECSPI_FIFO_SIZE); + fifo32_create(&s->rx_fifo, ECSPI_FIFO_SIZE); +} + +static void imx_spi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = imx_spi_realize; + dc->vmsd = &vmstate_imx_spi; + dc->reset = imx_spi_reset; + dc->desc = "i.MX SPI Controller"; +} + +static const TypeInfo imx_spi_info = { + .name = TYPE_IMX_SPI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IMXSPIState), + .class_init = imx_spi_class_init, +}; + +static void imx_spi_register_types(void) +{ + type_register_static(&imx_spi_info); +} + +type_init(imx_spi_register_types) diff --git a/hw/ssi/pl022.c b/hw/ssi/pl022.c index 564a0d36e..c1368018e 100644 --- a/hw/ssi/pl022.c +++ b/hw/ssi/pl022.c @@ -10,6 +10,7 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" #include "hw/ssi/ssi.h" +#include "qemu/log.h" //#define DEBUG_PL022 1 diff --git a/hw/ssi/ssi.c b/hw/ssi/ssi.c index 9791c0d94..7eaaf565f 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/Makefile.objs b/hw/timer/Makefile.objs index 003c14fa2..7ba8c23c7 100644 --- a/hw/timer/Makefile.objs +++ b/hw/timer/Makefile.objs @@ -26,6 +26,7 @@ obj-$(CONFIG_OMAP) += omap_synctimer.o obj-$(CONFIG_PXA2XX) += pxa2xx_timer.o obj-$(CONFIG_SH4) += sh_timer.o obj-$(CONFIG_DIGIC) += digic-timer.o +obj-$(CONFIG_MIPS_CPS) += mips_gictimer.o obj-$(CONFIG_MC146818RTC) += mc146818rtc.o diff --git a/hw/timer/a9gtimer.c b/hw/timer/a9gtimer.c index afe577c76..772f85f5f 100644 --- a/hw/timer/a9gtimer.c +++ b/hw/timer/a9gtimer.c @@ -184,7 +184,7 @@ static void a9_gtimer_write(void *opaque, hwaddr addr, uint64_t value, case R_COUNTER_LO: /* * Keep it simple - ARM docco explicitly says to disable timer before - * modding it, so dont bother trying to do all the difficult on the fly + * modding it, so don't bother trying to do all the difficult on the fly * timer modifications - (if they even work in real hardware??). */ if (s->control & R_CONTROL_TIMER_ENABLE) { diff --git a/hw/timer/allwinner-a10-pit.c b/hw/timer/allwinner-a10-pit.c index 51cdc98f3..3385e5dc3 100644 --- a/hw/timer/allwinner-a10-pit.c +++ b/hw/timer/allwinner-a10-pit.c @@ -19,6 +19,7 @@ #include "hw/sysbus.h" #include "sysemu/sysemu.h" #include "hw/timer/allwinner-a10-pit.h" +#include "qemu/log.h" static void a10_pit_update_irq(AwA10PITState *s) { diff --git a/hw/timer/arm_timer.c b/hw/timer/arm_timer.c index f1ede5f53..111a16db3 100644 --- a/hw/timer/arm_timer.c +++ b/hw/timer/arm_timer.c @@ -14,6 +14,7 @@ #include "hw/qdev.h" #include "hw/ptimer.h" #include "qemu/main-loop.h" +#include "qemu/log.h" /* Common timer implementation. */ diff --git a/hw/timer/aspeed_timer.c b/hw/timer/aspeed_timer.c index 51e8303cd..9b70ee09b 100644 --- a/hw/timer/aspeed_timer.c +++ b/hw/timer/aspeed_timer.c @@ -10,13 +10,12 @@ */ #include "qemu/osdep.h" -#include "hw/ptimer.h" #include "hw/sysbus.h" #include "hw/timer/aspeed_timer.h" #include "qemu-common.h" #include "qemu/bitops.h" -#include "qemu/main-loop.h" #include "qemu/timer.h" +#include "qemu/log.h" #include "trace.h" #define TIMER_NR_REGS 4 @@ -76,21 +75,96 @@ static inline bool timer_can_pulse(AspeedTimer *t) return t->id >= TIMER_FIRST_CAP_PULSE; } +static inline bool timer_external_clock(AspeedTimer *t) +{ + return timer_ctrl_status(t, op_external_clock); +} + +static uint32_t clock_rates[] = { TIMER_CLOCK_APB_HZ, TIMER_CLOCK_EXT_HZ }; + +static inline uint32_t calculate_rate(struct AspeedTimer *t) +{ + return clock_rates[timer_external_clock(t)]; +} + +static inline uint32_t calculate_ticks(struct AspeedTimer *t, uint64_t now_ns) +{ + uint64_t delta_ns = now_ns - MIN(now_ns, t->start); + uint32_t rate = calculate_rate(t); + uint64_t ticks = muldiv64(delta_ns, rate, NANOSECONDS_PER_SECOND); + + return t->reload - MIN(t->reload, ticks); +} + +static inline uint64_t calculate_time(struct AspeedTimer *t, uint32_t ticks) +{ + uint64_t delta_ns; + uint64_t delta_ticks; + + delta_ticks = t->reload - MIN(t->reload, ticks); + delta_ns = muldiv64(delta_ticks, NANOSECONDS_PER_SECOND, calculate_rate(t)); + + return t->start + delta_ns; +} + +static uint64_t calculate_next(struct AspeedTimer *t) +{ + uint64_t next = 0; + uint32_t rate = calculate_rate(t); + + while (!next) { + /* We don't know the relationship between the values in the match + * registers, so sort using MAX/MIN/zero. We sort in that order as the + * timer counts down to zero. */ + uint64_t seq[] = { + calculate_time(t, MAX(t->match[0], t->match[1])), + calculate_time(t, MIN(t->match[0], t->match[1])), + calculate_time(t, 0), + }; + uint64_t reload_ns; + uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + + if (now < seq[0]) { + next = seq[0]; + } else if (now < seq[1]) { + next = seq[1]; + } else if (now < seq[2]) { + next = seq[2]; + } else { + reload_ns = muldiv64(t->reload, NANOSECONDS_PER_SECOND, rate); + t->start = now - ((now - t->start) % reload_ns); + } + } + + return next; +} + static void aspeed_timer_expire(void *opaque) { AspeedTimer *t = opaque; + bool interrupt = false; + uint32_t ticks; - /* Only support interrupts on match values of zero for the moment - this is - * sufficient to boot an aspeed_defconfig Linux kernel. - * - * TODO: matching on arbitrary values (see e.g. hw/timer/a9gtimer.c) - */ - bool match = !(t->match[0] && t->match[1]); - bool interrupt = timer_overflow_interrupt(t) || match; - if (timer_enabled(t) && interrupt) { + if (!timer_enabled(t)) { + return; + } + + ticks = calculate_ticks(t, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); + + if (!ticks) { + interrupt = timer_overflow_interrupt(t) || !t->match[0] || !t->match[1]; + } else if (ticks <= MIN(t->match[0], t->match[1])) { + interrupt = true; + } else if (ticks <= MAX(t->match[0], t->match[1])) { + interrupt = true; + } + + if (interrupt) { t->level = !t->level; qemu_set_irq(t->irq, t->level); } + + timer_mod(&t->timer, calculate_next(t)); } static uint64_t aspeed_timer_get_value(AspeedTimer *t, int reg) @@ -99,7 +173,7 @@ static uint64_t aspeed_timer_get_value(AspeedTimer *t, int reg) switch (reg) { case TIMER_REG_STATUS: - value = ptimer_get_count(t->timer); + value = calculate_ticks(t, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); break; case TIMER_REG_RELOAD: value = t->reload; @@ -159,24 +233,22 @@ static void aspeed_timer_set_value(AspeedTimerCtrlState *s, int timer, int reg, switch (reg) { case TIMER_REG_STATUS: if (timer_enabled(t)) { - ptimer_set_count(t->timer, value); + uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + int64_t delta = (int64_t) value - (int64_t) calculate_ticks(t, now); + uint32_t rate = calculate_rate(t); + + t->start += muldiv64(delta, NANOSECONDS_PER_SECOND, rate); + timer_mod(&t->timer, calculate_next(t)); } break; case TIMER_REG_RELOAD: t->reload = value; - ptimer_set_limit(t->timer, value, 1); break; case TIMER_REG_MATCH_FIRST: case TIMER_REG_MATCH_SECOND: - if (value) { - /* Non-zero match values are unsupported. As such an interrupt will - * always be triggered when the timer reaches zero even if the - * overflow interrupt control bit is clear. - */ - qemu_log_mask(LOG_UNIMP, "%s: Match value unsupported by device: " - "0x%" PRIx32 "\n", __func__, value); - } else { - t->match[reg - 2] = value; + t->match[reg - 2] = value; + if (timer_enabled(t)) { + timer_mod(&t->timer, calculate_next(t)); } break; default: @@ -187,7 +259,7 @@ static void aspeed_timer_set_value(AspeedTimerCtrlState *s, int timer, int reg, } /* Control register operations are broken out into helpers that can be - * explictly called on aspeed_timer_reset(), but also from + * explicitly called on aspeed_timer_reset(), but also from * aspeed_timer_ctrl_op(). */ @@ -195,21 +267,16 @@ static void aspeed_timer_ctrl_enable(AspeedTimer *t, bool enable) { trace_aspeed_timer_ctrl_enable(t->id, enable); if (enable) { - ptimer_run(t->timer, 0); + t->start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + timer_mod(&t->timer, calculate_next(t)); } else { - ptimer_stop(t->timer); - ptimer_set_limit(t->timer, t->reload, 1); + timer_del(&t->timer); } } static void aspeed_timer_ctrl_external_clock(AspeedTimer *t, bool enable) { trace_aspeed_timer_ctrl_external_clock(t->id, enable); - if (enable) { - ptimer_set_freq(t->timer, TIMER_CLOCK_EXT_HZ); - } else { - ptimer_set_freq(t->timer, TIMER_CLOCK_APB_HZ); - } } static void aspeed_timer_ctrl_overflow_interrupt(AspeedTimer *t, bool enable) @@ -350,12 +417,10 @@ static const MemoryRegionOps aspeed_timer_ops = { static void aspeed_init_one_timer(AspeedTimerCtrlState *s, uint8_t id) { - QEMUBH *bh; AspeedTimer *t = &s->timers[id]; t->id = id; - bh = qemu_bh_new(aspeed_timer_expire, t); - t->timer = ptimer_init(bh); + timer_init_ns(&t->timer, QEMU_CLOCK_VIRTUAL, aspeed_timer_expire, t); } static void aspeed_timer_realize(DeviceState *dev, Error **errp) @@ -380,7 +445,7 @@ static void aspeed_timer_reset(DeviceState *dev) for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) { AspeedTimer *t = &s->timers[i]; - /* Explictly call helpers to avoid any conditional behaviour through + /* Explicitly call helpers to avoid any conditional behaviour through * aspeed_timer_set_ctrl(). */ aspeed_timer_ctrl_enable(t, false); @@ -398,12 +463,12 @@ static void aspeed_timer_reset(DeviceState *dev) static const VMStateDescription vmstate_aspeed_timer = { .name = "aspeed.timer", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (VMStateField[]) { VMSTATE_UINT8(id, AspeedTimer), VMSTATE_INT32(level, AspeedTimer), - VMSTATE_PTIMER(timer, AspeedTimer), + VMSTATE_TIMER(timer, AspeedTimer), VMSTATE_UINT32(reload, AspeedTimer), VMSTATE_UINT32_ARRAY(match, AspeedTimer, 2), VMSTATE_END_OF_LIST() @@ -418,7 +483,7 @@ static const VMStateDescription vmstate_aspeed_timer_state = { VMSTATE_UINT32(ctrl, AspeedTimerCtrlState), VMSTATE_UINT32(ctrl2, AspeedTimerCtrlState), VMSTATE_STRUCT_ARRAY(timers, AspeedTimerCtrlState, - ASPEED_TIMER_NR_TIMERS, 1, vmstate_aspeed_timer, + ASPEED_TIMER_NR_TIMERS, 2, vmstate_aspeed_timer, AspeedTimer), VMSTATE_END_OF_LIST() } diff --git a/hw/timer/digic-timer.c b/hw/timer/digic-timer.c index 5b97e1e1a..0f21faf87 100644 --- a/hw/timer/digic-timer.c +++ b/hw/timer/digic-timer.c @@ -30,6 +30,7 @@ #include "hw/sysbus.h" #include "hw/ptimer.h" #include "qemu/main-loop.h" +#include "qemu/log.h" #include "hw/timer/digic-timer.h" diff --git a/hw/timer/imx_epit.c b/hw/timer/imx_epit.c index f5836e21f..eddf3481e 100644 --- a/hw/timer/imx_epit.c +++ b/hw/timer/imx_epit.c @@ -16,6 +16,7 @@ #include "hw/timer/imx_epit.h" #include "hw/misc/imx_ccm.h" #include "qemu/main-loop.h" +#include "qemu/log.h" #ifndef DEBUG_IMX_EPIT #define DEBUG_IMX_EPIT 0 diff --git a/hw/timer/imx_gpt.c b/hw/timer/imx_gpt.c index ab2e213a1..82bc73cb8 100644 --- a/hw/timer/imx_gpt.c +++ b/hw/timer/imx_gpt.c @@ -14,8 +14,8 @@ #include "qemu/osdep.h" #include "hw/timer/imx_gpt.h" -#include "hw/misc/imx_ccm.h" #include "qemu/main-loop.h" +#include "qemu/log.h" #ifndef DEBUG_IMX_GPT #define DEBUG_IMX_GPT 0 @@ -80,7 +80,18 @@ static const VMStateDescription vmstate_imx_timer_gpt = { } }; -static const IMXClk imx_gpt_clocks[] = { +static const IMXClk imx25_gpt_clocks[] = { + CLK_NONE, /* 000 No clock source */ + CLK_IPG, /* 001 ipg_clk, 532MHz*/ + CLK_IPG_HIGH, /* 010 ipg_clk_highfreq */ + CLK_NONE, /* 011 not defined */ + CLK_32k, /* 100 ipg_clk_32k */ + CLK_32k, /* 101 ipg_clk_32k */ + CLK_32k, /* 110 ipg_clk_32k */ + CLK_32k, /* 111 ipg_clk_32k */ +}; + +static const IMXClk imx31_gpt_clocks[] = { CLK_NONE, /* 000 No clock source */ CLK_IPG, /* 001 ipg_clk, 532MHz*/ CLK_IPG_HIGH, /* 010 ipg_clk_highfreq */ @@ -91,12 +102,23 @@ static const IMXClk imx_gpt_clocks[] = { CLK_NONE, /* 111 not defined */ }; +static const IMXClk imx6_gpt_clocks[] = { + CLK_NONE, /* 000 No clock source */ + CLK_IPG, /* 001 ipg_clk, 532MHz*/ + CLK_IPG_HIGH, /* 010 ipg_clk_highfreq */ + CLK_EXT, /* 011 External clock */ + CLK_32k, /* 100 ipg_clk_32k */ + CLK_HIGH_DIV, /* 101 reference clock / 8 */ + CLK_NONE, /* 110 not defined */ + CLK_HIGH, /* 111 reference clock */ +}; + static void imx_gpt_set_freq(IMXGPTState *s) { uint32_t clksrc = extract32(s->cr, GPT_CR_CLKSRC_SHIFT, 3); s->freq = imx_ccm_get_clock_frequency(s->ccm, - imx_gpt_clocks[clksrc]) / (1 + s->pr); + s->clocks[clksrc]) / (1 + s->pr); DPRINTF("Setting clksrc %d to frequency %d\n", clksrc, s->freq); @@ -452,16 +474,52 @@ static void imx_gpt_class_init(ObjectClass *klass, void *data) dc->desc = "i.MX general timer"; } -static const TypeInfo imx_gpt_info = { - .name = TYPE_IMX_GPT, +static void imx25_gpt_init(Object *obj) +{ + IMXGPTState *s = IMX_GPT(obj); + + s->clocks = imx25_gpt_clocks; +} + +static void imx31_gpt_init(Object *obj) +{ + IMXGPTState *s = IMX_GPT(obj); + + s->clocks = imx31_gpt_clocks; +} + +static void imx6_gpt_init(Object *obj) +{ + IMXGPTState *s = IMX_GPT(obj); + + s->clocks = imx6_gpt_clocks; +} + +static const TypeInfo imx25_gpt_info = { + .name = TYPE_IMX25_GPT, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(IMXGPTState), + .instance_init = imx25_gpt_init, .class_init = imx_gpt_class_init, }; +static const TypeInfo imx31_gpt_info = { + .name = TYPE_IMX31_GPT, + .parent = TYPE_IMX25_GPT, + .instance_init = imx31_gpt_init, +}; + +static const TypeInfo imx6_gpt_info = { + .name = TYPE_IMX6_GPT, + .parent = TYPE_IMX25_GPT, + .instance_init = imx6_gpt_init, +}; + static void imx_gpt_register_types(void) { - type_register_static(&imx_gpt_info); + type_register_static(&imx25_gpt_info); + type_register_static(&imx31_gpt_info); + type_register_static(&imx6_gpt_info); } type_init(imx_gpt_register_types) diff --git a/hw/timer/lm32_timer.c b/hw/timer/lm32_timer.c index 3198355aa..e45a65bb9 100644 --- a/hw/timer/lm32_timer.c +++ b/hw/timer/lm32_timer.c @@ -176,21 +176,26 @@ static void timer_reset(DeviceState *d) ptimer_stop(s->ptimer); } -static int lm32_timer_init(SysBusDevice *dev) +static void lm32_timer_init(Object *obj) { - LM32TimerState *s = LM32_TIMER(dev); + LM32TimerState *s = LM32_TIMER(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); sysbus_init_irq(dev, &s->irq); s->bh = qemu_bh_new(timer_hit, s); s->ptimer = ptimer_init(s->bh); - ptimer_set_freq(s->ptimer, s->freq_hz); - memory_region_init_io(&s->iomem, OBJECT(s), &timer_ops, s, + memory_region_init_io(&s->iomem, obj, &timer_ops, s, "timer", R_MAX * 4); sysbus_init_mmio(dev, &s->iomem); +} - return 0; +static void lm32_timer_realize(DeviceState *dev, Error **errp) +{ + LM32TimerState *s = LM32_TIMER(dev); + + ptimer_set_freq(s->ptimer, s->freq_hz); } static const VMStateDescription vmstate_lm32_timer = { @@ -213,9 +218,8 @@ static Property lm32_timer_properties[] = { static void lm32_timer_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = lm32_timer_init; + dc->realize = lm32_timer_realize; dc->reset = timer_reset; dc->vmsd = &vmstate_lm32_timer; dc->props = lm32_timer_properties; @@ -225,6 +229,7 @@ static const TypeInfo lm32_timer_info = { .name = TYPE_LM32_TIMER, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(LM32TimerState), + .instance_init = lm32_timer_init, .class_init = lm32_timer_class_init, }; diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c index 2ac0fd3e4..ea625f25c 100644 --- a/hw/timer/mc146818rtc.c +++ b/hw/timer/mc146818rtc.c @@ -22,7 +22,6 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" -#include "config-target.h" #include "qemu/cutils.h" #include "qemu/bcd.h" #include "hw/hw.h" @@ -106,12 +105,10 @@ static inline bool rtc_running(RTCState *s) static uint64_t get_guest_rtc_ns(RTCState *s) { - uint64_t guest_rtc; uint64_t guest_clock = qemu_clock_get_ns(rtc_clock); - guest_rtc = s->base_rtc * NANOSECONDS_PER_SECOND + + return s->base_rtc * NANOSECONDS_PER_SECOND + guest_clock - s->last_update + s->offset; - return guest_rtc; } #ifdef TARGET_I386 @@ -909,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) @@ -923,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/timer/milkymist-sysctl.c b/hw/timer/milkymist-sysctl.c index 5f2948037..21948328c 100644 --- a/hw/timer/milkymist-sysctl.c +++ b/hw/timer/milkymist-sysctl.c @@ -18,7 +18,7 @@ * * * Specification available at: - * http://www.milkymist.org/socdoc/sysctl.pdf + * http://milkymist.walle.cc/socdoc/sysctl.pdf */ #include "qemu/osdep.h" @@ -270,9 +270,10 @@ static void milkymist_sysctl_reset(DeviceState *d) s->regs[R_GPIO_IN] = s->strappings; } -static int milkymist_sysctl_init(SysBusDevice *dev) +static void milkymist_sysctl_init(Object *obj) { - MilkymistSysctlState *s = MILKYMIST_SYSCTL(dev); + MilkymistSysctlState *s = MILKYMIST_SYSCTL(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); sysbus_init_irq(dev, &s->gpio_irq); sysbus_init_irq(dev, &s->timer0_irq); @@ -282,14 +283,18 @@ static int milkymist_sysctl_init(SysBusDevice *dev) s->bh1 = qemu_bh_new(timer1_hit, s); s->ptimer0 = ptimer_init(s->bh0); s->ptimer1 = ptimer_init(s->bh1); - ptimer_set_freq(s->ptimer0, s->freq_hz); - ptimer_set_freq(s->ptimer1, s->freq_hz); - memory_region_init_io(&s->regs_region, OBJECT(s), &sysctl_mmio_ops, s, + memory_region_init_io(&s->regs_region, obj, &sysctl_mmio_ops, s, "milkymist-sysctl", R_MAX * 4); sysbus_init_mmio(dev, &s->regs_region); +} - return 0; +static void milkymist_sysctl_realize(DeviceState *dev, Error **errp) +{ + MilkymistSysctlState *s = MILKYMIST_SYSCTL(dev); + + ptimer_set_freq(s->ptimer0, s->freq_hz); + ptimer_set_freq(s->ptimer1, s->freq_hz); } static const VMStateDescription vmstate_milkymist_sysctl = { @@ -319,9 +324,8 @@ static Property milkymist_sysctl_properties[] = { static void milkymist_sysctl_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = milkymist_sysctl_init; + dc->realize = milkymist_sysctl_realize; dc->reset = milkymist_sysctl_reset; dc->vmsd = &vmstate_milkymist_sysctl; dc->props = milkymist_sysctl_properties; @@ -331,6 +335,7 @@ static const TypeInfo milkymist_sysctl_info = { .name = TYPE_MILKYMIST_SYSCTL, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(MilkymistSysctlState), + .instance_init = milkymist_sysctl_init, .class_init = milkymist_sysctl_class_init, }; diff --git a/hw/timer/mips_gictimer.c b/hw/timer/mips_gictimer.c new file mode 100644 index 000000000..369888947 --- /dev/null +++ b/hw/timer/mips_gictimer.c @@ -0,0 +1,142 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2016 Imagination Technologies + */ + +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "qemu/timer.h" +#include "hw/timer/mips_gictimer.h" + +#define TIMER_PERIOD 10 /* 10 ns period for 100 Mhz frequency */ + +static void gic_vptimer_update(MIPSGICTimerState *gictimer, + uint32_t vp_index, uint64_t now) +{ + uint64_t next; + uint32_t wait; + + wait = gictimer->vptimers[vp_index].comparelo - gictimer->sh_counterlo - + (uint32_t)(now / TIMER_PERIOD); + next = now + (uint64_t)wait * TIMER_PERIOD; + + timer_mod(gictimer->vptimers[vp_index].qtimer, next); +} + +static void gic_vptimer_expire(MIPSGICTimerState *gictimer, uint32_t vp_index, + uint64_t now) +{ + if (gictimer->countstop) { + /* timer stopped */ + return; + } + gictimer->cb(gictimer->opaque, vp_index); + gic_vptimer_update(gictimer, vp_index, now); +} + +static void gic_vptimer_cb(void *opaque) +{ + MIPSGICTimerVPState *vptimer = opaque; + MIPSGICTimerState *gictimer = vptimer->gictimer; + gic_vptimer_expire(gictimer, vptimer->vp_index, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); +} + +uint32_t mips_gictimer_get_sh_count(MIPSGICTimerState *gictimer) +{ + int i; + if (gictimer->countstop) { + return gictimer->sh_counterlo; + } else { + uint64_t now; + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + for (i = 0; i < gictimer->num_vps; i++) { + if (timer_pending(gictimer->vptimers[i].qtimer) + && timer_expired(gictimer->vptimers[i].qtimer, now)) { + /* The timer has already expired. */ + gic_vptimer_expire(gictimer, i, now); + } + } + return gictimer->sh_counterlo + (uint32_t)(now / TIMER_PERIOD); + } +} + +void mips_gictimer_store_sh_count(MIPSGICTimerState *gictimer, uint64_t count) +{ + int i; + uint64_t now; + + if (gictimer->countstop || !gictimer->vptimers[0].qtimer) { + gictimer->sh_counterlo = count; + } else { + /* Store new count register */ + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + gictimer->sh_counterlo = count - (uint32_t)(now / TIMER_PERIOD); + /* Update timer timer */ + for (i = 0; i < gictimer->num_vps; i++) { + gic_vptimer_update(gictimer, i, now); + } + } +} + +uint32_t mips_gictimer_get_vp_compare(MIPSGICTimerState *gictimer, + uint32_t vp_index) +{ + return gictimer->vptimers[vp_index].comparelo; +} + +void mips_gictimer_store_vp_compare(MIPSGICTimerState *gictimer, + uint32_t vp_index, uint64_t compare) +{ + gictimer->vptimers[vp_index].comparelo = (uint32_t) compare; + gic_vptimer_update(gictimer, vp_index, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); +} + +uint8_t mips_gictimer_get_countstop(MIPSGICTimerState *gictimer) +{ + return gictimer->countstop; +} + +void mips_gictimer_start_count(MIPSGICTimerState *gictimer) +{ + gictimer->countstop = 0; + mips_gictimer_store_sh_count(gictimer, gictimer->sh_counterlo); +} + +void mips_gictimer_stop_count(MIPSGICTimerState *gictimer) +{ + int i; + + gictimer->countstop = 1; + /* Store the current value */ + gictimer->sh_counterlo += + (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / TIMER_PERIOD); + for (i = 0; i < gictimer->num_vps; i++) { + timer_del(gictimer->vptimers[i].qtimer); + } +} + +MIPSGICTimerState *mips_gictimer_init(void *opaque, uint32_t nvps, + MIPSGICTimerCB *cb) +{ + int i; + MIPSGICTimerState *gictimer = g_new(MIPSGICTimerState, 1); + gictimer->vptimers = g_new(MIPSGICTimerVPState, nvps); + gictimer->countstop = 1; + gictimer->num_vps = nvps; + gictimer->opaque = opaque; + gictimer->cb = cb; + for (i = 0; i < nvps; i++) { + gictimer->vptimers[i].gictimer = gictimer; + gictimer->vptimers[i].vp_index = i; + gictimer->vptimers[i].qtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + &gic_vptimer_cb, + &gictimer->vptimers[i]); + } + return gictimer; +} diff --git a/hw/timer/omap_gptimer.c b/hw/timer/omap_gptimer.c index 3a4386304..5e3e8a6d7 100644 --- a/hw/timer/omap_gptimer.c +++ b/hw/timer/omap_gptimer.c @@ -133,8 +133,8 @@ static inline void omap_gp_timer_update(struct omap_gp_timer_s *timer) timer_mod(timer->timer, timer->time + expires); if (timer->ce && timer->match_val >= timer->val) { - matches = muldiv64(timer->match_val - timer->val, - timer->ticks_per_sec, timer->rate); + matches = muldiv64(timer->ticks_per_sec, + timer->match_val - timer->val, timer->rate); timer_mod(timer->match, timer->time + matches); } else timer_del(timer->match); diff --git a/hw/timer/pl031.c b/hw/timer/pl031.c index 38e0cb5ad..dbbeb9b16 100644 --- a/hw/timer/pl031.c +++ b/hw/timer/pl031.c @@ -16,6 +16,7 @@ #include "qemu/timer.h" #include "sysemu/sysemu.h" #include "qemu/cutils.h" +#include "qemu/log.h" //#define DEBUG_PL031 diff --git a/hw/timer/stm32f2xx_timer.c b/hw/timer/stm32f2xx_timer.c index 55dacbbe3..bf0fb288c 100644 --- a/hw/timer/stm32f2xx_timer.c +++ b/hw/timer/stm32f2xx_timer.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" #include "hw/timer/stm32f2xx_timer.h" +#include "qemu/log.h" #ifndef STM_TIMER_ERR_DEBUG #define STM_TIMER_ERR_DEBUG 0 diff --git a/hw/timer/trace-events b/hw/timer/trace-events new file mode 100644 index 000000000..3495c41c1 --- /dev/null +++ b/hw/timer/trace-events @@ -0,0 +1,51 @@ +# See docs/tracing.txt for syntax documentation. + +# hw/timer/slavio_timer.c +slavio_timer_get_out(uint64_t limit, uint32_t counthigh, uint32_t count) "limit %"PRIx64" count %x%08x" +slavio_timer_irq(uint32_t counthigh, uint32_t count) "callback: count %x%08x" +slavio_timer_mem_readl_invalid(uint64_t addr) "invalid read address %"PRIx64 +slavio_timer_mem_readl(uint64_t addr, uint32_t ret) "read %"PRIx64" = %08x" +slavio_timer_mem_writel(uint64_t addr, uint32_t val) "write %"PRIx64" = %08x" +slavio_timer_mem_writel_limit(unsigned int timer_index, uint64_t count) "processor %d user timer set to %016"PRIx64 +slavio_timer_mem_writel_counter_invalid(void) "not user timer" +slavio_timer_mem_writel_status_start(unsigned int timer_index) "processor %d user timer started" +slavio_timer_mem_writel_status_stop(unsigned int timer_index) "processor %d user timer stopped" +slavio_timer_mem_writel_mode_user(unsigned int timer_index) "processor %d changed from counter to user timer" +slavio_timer_mem_writel_mode_counter(unsigned int timer_index) "processor %d changed from user timer to counter" +slavio_timer_mem_writel_mode_invalid(void) "not system timer" +slavio_timer_mem_writel_invalid(uint64_t addr) "invalid write address %"PRIx64 + +# hw/timer/grlib_gptimer.c +grlib_gptimer_enable(int id, uint32_t count) "timer:%d set count 0x%x and run" +grlib_gptimer_disabled(int id, uint32_t config) "timer:%d Timer disable config 0x%x" +grlib_gptimer_restart(int id, uint32_t reload) "timer:%d reload val: 0x%x" +grlib_gptimer_set_scaler(uint32_t scaler, uint32_t freq) "scaler:0x%x freq: 0x%x" +grlib_gptimer_hit(int id) "timer:%d HIT" +grlib_gptimer_readl(int id, uint64_t addr, uint32_t val) "timer:%d addr 0x%"PRIx64" 0x%x" +grlib_gptimer_writel(int id, uint64_t addr, uint32_t val) "timer:%d addr 0x%"PRIx64" 0x%x" + +# hw/timer/lm32_timer.c +lm32_timer_memory_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" +lm32_timer_memory_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" +lm32_timer_hit(void) "timer hit" +lm32_timer_irq_state(int level) "irq state %d" + +# hw/timer/milkymist-sysctl.c +milkymist_sysctl_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x" +milkymist_sysctl_memory_write(uint32_t addr, uint32_t value) "addr %08x value %08x" +milkymist_sysctl_icap_write(uint32_t value) "value %08x" +milkymist_sysctl_start_timer0(void) "Start timer0" +milkymist_sysctl_stop_timer0(void) "Stop timer0" +milkymist_sysctl_start_timer1(void) "Start timer1" +milkymist_sysctl_stop_timer1(void) "Stop timer1" +milkymist_sysctl_pulse_irq_timer0(void) "Pulse IRQ Timer0" +milkymist_sysctl_pulse_irq_timer1(void) "Pulse IRQ Timer1" + +# hw/timer/aspeed_timer.c +aspeed_timer_ctrl_enable(uint8_t i, bool enable) "Timer %" PRIu8 ": %d" +aspeed_timer_ctrl_external_clock(uint8_t i, bool enable) "Timer %" PRIu8 ": %d" +aspeed_timer_ctrl_overflow_interrupt(uint8_t i, bool enable) "Timer %" PRIu8 ": %d" +aspeed_timer_ctrl_pulse_enable(uint8_t i, bool enable) "Timer %" PRIu8 ": %d" +aspeed_timer_set_ctrl2(uint32_t value) "Value: 0x%" PRIx32 +aspeed_timer_set_value(int timer, int reg, uint32_t value) "Timer %d register %d: 0x%" PRIx32 +aspeed_timer_read(uint64_t offset, unsigned size, uint64_t value) "From 0x%" PRIx64 ": of size %u: 0x%" PRIx64 diff --git a/hw/tpm/tpm_util.h b/hw/tpm/tpm_util.h index e7f354a52..df76245e6 100644 --- a/hw/tpm/tpm_util.h +++ b/hw/tpm/tpm_util.h @@ -18,11 +18,12 @@ * 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 TPM_TPM_UTILS_H -#define TPM_TPM_UTILS_H + +#ifndef TPM_TPM_UTIL_H +#define TPM_TPM_UTIL_H #include "sysemu/tpm_backend.h" int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version); -#endif /* TPM_TPM_UTILS_H */ +#endif /* TPM_TPM_UTIL_H */ diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs index 2717027d3..98b5c9d27 100644 --- a/hw/usb/Makefile.objs +++ b/hw/usb/Makefile.objs @@ -38,3 +38,7 @@ common-obj-$(CONFIG_USB_REDIR) += redirect.o quirks.o # usb pass-through common-obj-y += $(patsubst %,host-%.o,$(HOST_USB)) + +ifeq ($(CONFIG_USB_LIBUSB),y) +common-obj-$(CONFIG_XEN_BACKEND) += xen-usb.o +endif diff --git a/hw/usb/bus.c b/hw/usb/bus.c index 16c3461d9..25913ad48 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -55,9 +55,9 @@ static int usb_device_post_load(void *opaque, int version_id) USBDevice *dev = opaque; if (dev->state == USB_STATE_NOTATTACHED) { - dev->attached = 0; + dev->attached = false; } else { - dev->attached = 1; + dev->attached = true; } if (dev->setup_index < 0 || dev->setup_len < 0 || @@ -279,6 +279,13 @@ static void usb_qdev_realize(DeviceState *qdev, Error **errp) static void usb_qdev_unrealize(DeviceState *qdev, Error **errp) { USBDevice *dev = USB_DEVICE(qdev); + USBDescString *s, *next; + + QLIST_FOREACH_SAFE(s, &dev->strings, next, next) { + QLIST_REMOVE(s, next); + g_free(s->str); + g_free(s); + } if (dev->attached) { usb_device_detach(dev); @@ -533,7 +540,7 @@ void usb_device_attach(USBDevice *dev, Error **errp) return; } - dev->attached++; + dev->attached = true; usb_attach(port); } @@ -547,7 +554,7 @@ int usb_device_detach(USBDevice *dev) trace_usb_port_detach(bus->busnr, port->path); usb_detach(port); - dev->attached--; + dev->attached = false; return 0; } @@ -736,6 +743,48 @@ USBDevice *usbdevice_create(const char *cmdline) return dev; } +static bool usb_get_attached(Object *obj, Error **errp) +{ + USBDevice *dev = USB_DEVICE(obj); + + return dev->attached; +} + +static void usb_set_attached(Object *obj, bool value, Error **errp) +{ + USBDevice *dev = USB_DEVICE(obj); + Error *err = NULL; + + if (dev->attached == value) { + return; + } + + if (value) { + usb_device_attach(dev, &err); + if (err) { + error_propagate(errp, err); + } + } else { + usb_device_detach(dev); + } +} + +static void usb_device_instance_init(Object *obj) +{ + USBDevice *dev = USB_DEVICE(obj); + USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); + + if (klass->attached_settable) { + object_property_add_bool(obj, "attached", + usb_get_attached, usb_set_attached, + NULL); + } else { + object_property_add_bool(obj, "attached", + usb_get_attached, NULL, + NULL); + } +} + static void usb_device_class_init(ObjectClass *klass, void *data) { DeviceClass *k = DEVICE_CLASS(klass); @@ -749,6 +798,7 @@ static const TypeInfo usb_device_type_info = { .name = TYPE_USB_DEVICE, .parent = TYPE_DEVICE, .instance_size = sizeof(USBDevice), + .instance_init = usb_device_instance_init, .abstract = true, .class_size = sizeof(USBDeviceClass), .class_init = usb_device_class_init, diff --git a/hw/usb/desc.c b/hw/usb/desc.c index adb026e43..5e0e1d157 100644 --- a/hw/usb/desc.c +++ b/hw/usb/desc.c @@ -574,6 +574,7 @@ void usb_desc_create_serial(USBDevice *dev) } dst += snprintf(serial+dst, sizeof(serial)-dst, "-%s", dev->port->path); usb_desc_set_string(dev, index, serial); + g_free(path); } const char *usb_desc_get_string(USBDevice *dev, uint8_t index) diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index bda84a64b..1be85ae75 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -788,8 +788,8 @@ static MTPData *usb_mtp_get_device_info(MTPState *s, MTPControl *c) trace_usb_mtp_op_get_device_info(s->dev.addr); usb_mtp_add_u16(d, 100); - usb_mtp_add_u32(d, 0xffffffff); - usb_mtp_add_u16(d, 0x0101); + usb_mtp_add_u32(d, 0x00000006); + usb_mtp_add_u16(d, 0x0064); usb_mtp_add_wstr(d, L""); usb_mtp_add_u16(d, 0x0000); diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c index 74306b58e..c0f1193ba 100644 --- a/hw/usb/dev-network.c +++ b/hw/usb/dev-network.c @@ -670,48 +670,49 @@ static int ndis_query(USBNetState *s, uint32_t oid, /* general oids (table 4-1) */ /* mandatory */ case OID_GEN_SUPPORTED_LIST: - for (i = 0; i < ARRAY_SIZE(oid_supported_list); i++) - ((le32 *) outbuf)[i] = cpu_to_le32(oid_supported_list[i]); + for (i = 0; i < ARRAY_SIZE(oid_supported_list); i++) { + stl_le_p(outbuf + (i * sizeof(le32)), oid_supported_list[i]); + } return sizeof(oid_supported_list); /* mandatory */ case OID_GEN_HARDWARE_STATUS: - *((le32 *) outbuf) = cpu_to_le32(0); + stl_le_p(outbuf, 0); return sizeof(le32); /* mandatory */ case OID_GEN_MEDIA_SUPPORTED: - *((le32 *) outbuf) = cpu_to_le32(s->medium); + stl_le_p(outbuf, s->medium); return sizeof(le32); /* mandatory */ case OID_GEN_MEDIA_IN_USE: - *((le32 *) outbuf) = cpu_to_le32(s->medium); + stl_le_p(outbuf, s->medium); return sizeof(le32); /* mandatory */ case OID_GEN_MAXIMUM_FRAME_SIZE: - *((le32 *) outbuf) = cpu_to_le32(ETH_FRAME_LEN); + stl_le_p(outbuf, ETH_FRAME_LEN); return sizeof(le32); /* mandatory */ case OID_GEN_LINK_SPEED: - *((le32 *) outbuf) = cpu_to_le32(s->speed); + stl_le_p(outbuf, s->speed); return sizeof(le32); /* mandatory */ case OID_GEN_TRANSMIT_BLOCK_SIZE: - *((le32 *) outbuf) = cpu_to_le32(ETH_FRAME_LEN); + stl_le_p(outbuf, ETH_FRAME_LEN); return sizeof(le32); /* mandatory */ case OID_GEN_RECEIVE_BLOCK_SIZE: - *((le32 *) outbuf) = cpu_to_le32(ETH_FRAME_LEN); + stl_le_p(outbuf, ETH_FRAME_LEN); return sizeof(le32); /* mandatory */ case OID_GEN_VENDOR_ID: - *((le32 *) outbuf) = cpu_to_le32(s->vendorid); + stl_le_p(outbuf, s->vendorid); return sizeof(le32); /* mandatory */ @@ -720,58 +721,57 @@ static int ndis_query(USBNetState *s, uint32_t oid, return strlen((char *)outbuf) + 1; case OID_GEN_VENDOR_DRIVER_VERSION: - *((le32 *) outbuf) = cpu_to_le32(1); + stl_le_p(outbuf, 1); return sizeof(le32); /* mandatory */ case OID_GEN_CURRENT_PACKET_FILTER: - *((le32 *) outbuf) = cpu_to_le32(s->filter); + stl_le_p(outbuf, s->filter); return sizeof(le32); /* mandatory */ case OID_GEN_MAXIMUM_TOTAL_SIZE: - *((le32 *) outbuf) = cpu_to_le32(RNDIS_MAX_TOTAL_SIZE); + stl_le_p(outbuf, RNDIS_MAX_TOTAL_SIZE); return sizeof(le32); /* mandatory */ case OID_GEN_MEDIA_CONNECT_STATUS: - *((le32 *) outbuf) = cpu_to_le32(s->media_state); + stl_le_p(outbuf, s->media_state); return sizeof(le32); case OID_GEN_PHYSICAL_MEDIUM: - *((le32 *) outbuf) = cpu_to_le32(0); + stl_le_p(outbuf, 0); return sizeof(le32); case OID_GEN_MAC_OPTIONS: - *((le32 *) outbuf) = cpu_to_le32( - NDIS_MAC_OPTION_RECEIVE_SERIALIZED | - NDIS_MAC_OPTION_FULL_DUPLEX); + stl_le_p(outbuf, NDIS_MAC_OPTION_RECEIVE_SERIALIZED | + NDIS_MAC_OPTION_FULL_DUPLEX); return sizeof(le32); /* statistics OIDs (table 4-2) */ /* mandatory */ case OID_GEN_XMIT_OK: - *((le32 *) outbuf) = cpu_to_le32(0); + stl_le_p(outbuf, 0); return sizeof(le32); /* mandatory */ case OID_GEN_RCV_OK: - *((le32 *) outbuf) = cpu_to_le32(0); + stl_le_p(outbuf, 0); return sizeof(le32); /* mandatory */ case OID_GEN_XMIT_ERROR: - *((le32 *) outbuf) = cpu_to_le32(0); + stl_le_p(outbuf, 0); return sizeof(le32); /* mandatory */ case OID_GEN_RCV_ERROR: - *((le32 *) outbuf) = cpu_to_le32(0); + stl_le_p(outbuf, 0); return sizeof(le32); /* mandatory */ case OID_GEN_RCV_NO_BUFFER: - *((le32 *) outbuf) = cpu_to_le32(0); + stl_le_p(outbuf, 0); return sizeof(le32); /* ieee802.3 OIDs (table 4-3) */ @@ -787,12 +787,12 @@ static int ndis_query(USBNetState *s, uint32_t oid, /* mandatory */ case OID_802_3_MULTICAST_LIST: - *((le32 *) outbuf) = cpu_to_le32(0xe0000000); + stl_le_p(outbuf, 0xe0000000); return sizeof(le32); /* mandatory */ case OID_802_3_MAXIMUM_LIST_SIZE: - *((le32 *) outbuf) = cpu_to_le32(1); + stl_le_p(outbuf, 1); return sizeof(le32); case OID_802_3_MAC_OPTIONS: @@ -801,17 +801,17 @@ static int ndis_query(USBNetState *s, uint32_t oid, /* ieee802.3 statistics OIDs (table 4-4) */ /* mandatory */ case OID_802_3_RCV_ERROR_ALIGNMENT: - *((le32 *) outbuf) = cpu_to_le32(0); + stl_le_p(outbuf, 0); return sizeof(le32); /* mandatory */ case OID_802_3_XMIT_ONE_COLLISION: - *((le32 *) outbuf) = cpu_to_le32(0); + stl_le_p(outbuf, 0); return sizeof(le32); /* mandatory */ case OID_802_3_XMIT_MORE_COLLISIONS: - *((le32 *) outbuf) = cpu_to_le32(0); + stl_le_p(outbuf, 0); return sizeof(le32); default: @@ -826,7 +826,7 @@ static int ndis_set(USBNetState *s, uint32_t oid, { switch (oid) { case OID_GEN_CURRENT_PACKET_FILTER: - s->filter = le32_to_cpup((le32 *) inbuf); + s->filter = ldl_le_p(inbuf); if (s->filter) { s->rndis_state = RNDIS_DATA_INITIALIZED; } else { @@ -1026,10 +1026,7 @@ static void usb_net_reset_in_buf(USBNetState *s) static int rndis_parse(USBNetState *s, uint8_t *data, int length) { - uint32_t msg_type; - le32 *tmp = (le32 *) data; - - msg_type = le32_to_cpup(tmp); + uint32_t msg_type = ldl_le_p(data); switch (msg_type) { case RNDIS_INITIALIZE_MSG: @@ -1337,7 +1334,7 @@ static void usb_net_handle_destroy(USBDevice *dev) } static NetClientInfo net_usbnet_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, + .type = NET_CLIENT_DRIVER_NIC, .size = sizeof(NICState), .receive = usbnet_receive, .cleanup = usbnet_cleanup, @@ -1399,7 +1396,7 @@ static USBDevice *usb_net_init(USBBus *bus, const char *cmdline) qemu_opt_set(opts, "type", "nic", &error_abort); qemu_opt_set(opts, "model", "usb", &error_abort); - idx = net_client_init(opts, 0, &local_err); + idx = net_client_init(opts, false, &local_err); if (local_err) { error_report_err(local_err); return NULL; diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index 248a58045..c607f7606 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -556,21 +556,6 @@ static void usb_msd_handle_data(USBDevice *dev, USBPacket *p) } } -static void usb_msd_password_cb(void *opaque, int err) -{ - MSDState *s = opaque; - Error *local_err = NULL; - - if (!err) { - usb_device_attach(&s->dev, &local_err); - } - - if (local_err) { - error_report_err(local_err); - qdev_unplug(&s->dev.qdev, NULL); - } -} - static void *usb_msd_load_request(QEMUFile *f, SCSIRequest *req) { MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); @@ -616,37 +601,21 @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp) return; } - if (blk_bs(blk)) { - bdrv_add_key(blk_bs(blk), NULL, &err); - if (err) { - if (monitor_cur_is_qmp()) { - error_propagate(errp, err); - return; - } - error_free(err); - err = NULL; - if (cur_mon) { - monitor_read_bdrv_key_start(cur_mon, blk_bs(blk), - usb_msd_password_cb, s); - s->dev.auto_attach = 0; - } else { - autostart = 0; - } - } - } - blkconf_serial(&s->conf, &dev->serial); blkconf_blocksizes(&s->conf); + blkconf_apply_backend_options(&s->conf); /* * Hack alert: this pretends to be a block device, but it's really * a SCSI bus that can serve only a single device, which it * creates automatically. But first it needs to detach from its * blockdev, or else scsi_bus_legacy_add_drive() dies when it - * attaches again. + * attaches again. We also need to take another reference so that + * blk_detach_dev() doesn't free blk while we still need it. * * The hack is probably a bad idea. */ + blk_ref(blk); blk_detach_dev(blk, &s->dev.qdev); s->conf.blk = NULL; @@ -657,6 +626,7 @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp) scsi_dev = scsi_bus_legacy_add_drive(&s->bus, blk, 0, !!s->removable, s->conf.bootindex, dev->serial, &err); + blk_unref(blk); if (!scsi_dev) { error_propagate(errp, err); return; @@ -668,9 +638,14 @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp) static void usb_msd_realize_bot(USBDevice *dev, Error **errp) { MSDState *s = USB_STORAGE_DEV(dev); + DeviceState *d = DEVICE(dev); usb_desc_create_serial(dev); usb_desc_init(dev); + if (d->hotplugged) { + s->dev.auto_attach = 0; + } + scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev), &usb_msd_scsi_info_bot, NULL); usb_msd_handle_reset(dev); @@ -818,9 +793,7 @@ static void usb_msd_set_bootindex(Object *obj, Visitor *v, const char *name, } out: - if (local_err) { - error_propagate(errp, local_err); - } + error_propagate(errp, local_err); } static const TypeInfo usb_storage_dev_type_info = { @@ -842,10 +815,9 @@ static void usb_msd_instance_init(Object *obj) static void usb_msd_class_initfn_bot(ObjectClass *klass, void *data) { USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); uc->realize = usb_msd_realize_bot; - dc->hotpluggable = false; + uc->attached_settable = true; } static const TypeInfo msd_info = { diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c index 0678b1b05..3a8ff18b1 100644 --- a/hw/usb/dev-uas.c +++ b/hw/usb/dev-uas.c @@ -900,9 +900,13 @@ static void usb_uas_handle_destroy(USBDevice *dev) static void usb_uas_realize(USBDevice *dev, Error **errp) { UASDevice *uas = USB_UAS(dev); + DeviceState *d = DEVICE(dev); usb_desc_create_serial(dev); usb_desc_init(dev); + if (d->hotplugged) { + uas->dev.auto_attach = 0; + } QTAILQ_INIT(&uas->results); QTAILQ_INIT(&uas->requests); @@ -940,6 +944,7 @@ static void usb_uas_class_initfn(ObjectClass *klass, void *data) uc->handle_control = usb_uas_handle_control; uc->handle_data = usb_uas_handle_data; uc->handle_destroy = usb_uas_handle_destroy; + uc->attached_settable = true; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); dc->fw_name = "storage"; dc->vmsd = &vmstate_usb_uas; diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 43a8f7abc..b093db729 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -2206,29 +2206,28 @@ static void ehci_advance_periodic_state(EHCIState *ehci) static void ehci_update_frindex(EHCIState *ehci, int uframes) { - int i; - if (!ehci_enabled(ehci) && ehci->pstate == EST_INACTIVE) { return; } - for (i = 0; i < uframes; i++) { - ehci->frindex++; - - if (ehci->frindex == 0x00002000) { - ehci_raise_irq(ehci, USBSTS_FLR); - } + /* Generate FLR interrupt if frame index rolls over 0x2000 */ + if ((ehci->frindex % 0x2000) + uframes >= 0x2000) { + ehci_raise_irq(ehci, USBSTS_FLR); + } - if (ehci->frindex == 0x00004000) { - ehci_raise_irq(ehci, USBSTS_FLR); - ehci->frindex = 0; - if (ehci->usbsts_frindex >= 0x00004000) { - ehci->usbsts_frindex -= 0x00004000; - } else { - ehci->usbsts_frindex = 0; - } + /* How many times will frindex roll over 0x4000 with this frame count? + * usbsts_frindex is decremented by 0x4000 on rollover until it reaches 0 + */ + int rollovers = (ehci->frindex + uframes) / 0x4000; + if (rollovers > 0) { + if (ehci->usbsts_frindex >= (rollovers * 0x4000)) { + ehci->usbsts_frindex -= 0x4000 * rollovers; + } else { + ehci->usbsts_frindex = 0; } } + + ehci->frindex = (ehci->frindex + uframes) % 0x4000; } static void ehci_frame_timer(void *opaque) diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h index 30218423c..3fd703865 100644 --- a/hw/usb/hcd-ehci.h +++ b/hw/usb/hcd-ehci.h @@ -14,8 +14,9 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, see <http://www.gnu.org/licenses/>. */ -#ifndef HW_USB_EHCI_H -#define HW_USB_EHCI_H 1 + +#ifndef HW_USB_HCD_EHCI_H +#define HW_USB_HCD_EHCI_H #include "hw/hw.h" #include "qemu/timer.h" diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index ffab561cf..fa5703832 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -1474,7 +1474,7 @@ static uint32_t ohci_get_frame_remaining(OHCIState *ohci) if (tks >= usb_frame_time) return (ohci->frt << 31); - tks = muldiv64(1, tks, usb_bit_time); + tks = tks / usb_bit_time; fr = (uint16_t)(ohci->fi - tks); return (ohci->frt << 31) | fr; @@ -1848,6 +1848,12 @@ static void usb_ohci_init(OHCIState *ohci, DeviceState *dev, ohci->as = as; + if (num_ports > OHCI_MAX_PORTS) { + error_setg(errp, "OHCI num-ports=%d is too big (limit is %d ports)", + num_ports, OHCI_MAX_PORTS); + return; + } + if (usb_frame_time == 0) { #ifdef OHCI_TIME_WARP usb_frame_time = NANOSECONDS_PER_SECOND; diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index bcde8a2f4..188f95416 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -26,6 +26,7 @@ #include "hw/pci/msi.h" #include "hw/pci/msix.h" #include "trace.h" +#include "qapi/error.h" //#define DEBUG_XHCI //#define DEBUG_DATA @@ -461,6 +462,8 @@ struct XHCIState { uint32_t numslots; uint32_t flags; uint32_t max_pstreams_mask; + OnOffAuto msi; + OnOffAuto msix; /* Operational Registers */ uint32_t usbcmd; @@ -498,9 +501,7 @@ typedef struct XHCIEvRingSeg { } XHCIEvRingSeg; enum xhci_flags { - XHCI_FLAG_USE_MSI = 1, - XHCI_FLAG_USE_MSI_X, - XHCI_FLAG_SS_FIRST, + XHCI_FLAG_SS_FIRST = 1, XHCI_FLAG_FORCE_PCIE_ENDCAP, XHCI_FLAG_ENABLE_STREAMS, }; @@ -1531,7 +1532,10 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid, usb_packet_cleanup(&epctx->transfers[i].packet); } - xhci_set_ep_state(xhci, epctx, NULL, EP_DISABLED); + /* only touch guest RAM if we're not resetting the HC */ + if (xhci->dcbaap_low || xhci->dcbaap_high) { + xhci_set_ep_state(xhci, epctx, NULL, EP_DISABLED); + } timer_free(epctx->kick_timer); g_free(epctx); @@ -2197,7 +2201,9 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, xfer->trb_count = length; for (i = 0; i < length; i++) { - assert(xhci_ring_fetch(xhci, ring, &xfer->trbs[i], NULL)); + TRBType type; + type = xhci_ring_fetch(xhci, ring, &xfer->trbs[i], NULL); + assert(type); } xfer->streamid = streamid; @@ -2360,6 +2366,8 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid, slot->uport = uport; slot->ctx = octx; + /* Make sure device is in USB_STATE_DEFAULT state */ + usb_device_reset(dev); if (bsr) { slot_ctx[3] = SLOT_DEFAULT << SLOT_STATE_SHIFT; } else { @@ -2367,7 +2375,6 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid, uint8_t buf[1]; slot_ctx[3] = (SLOT_ADDRESSED << SLOT_STATE_SHIFT) | slotid; - usb_device_reset(dev); memset(&p, 0, sizeof(p)); usb_packet_addbuf(&p, buf, sizeof(buf)); usb_packet_setup(&p, USB_TOKEN_OUT, @@ -3578,6 +3585,7 @@ static void usb_xhci_init(XHCIState *xhci) static void usb_xhci_realize(struct PCIDevice *dev, Error **errp) { int i, ret; + Error *err = NULL; XHCIState *xhci = XHCI(dev); @@ -3588,6 +3596,23 @@ static void usb_xhci_realize(struct PCIDevice *dev, Error **errp) usb_xhci_init(xhci); + if (xhci->msi != ON_OFF_AUTO_OFF) { + ret = msi_init(dev, 0x70, xhci->numintrs, true, false, &err); + /* Any error other than -ENOTSUP(board's MSI support is broken) + * is a programming error */ + assert(!ret || ret == -ENOTSUP); + if (ret && xhci->msi == ON_OFF_AUTO_ON) { + /* Can't satisfy user's explicit msi=on request, fail */ + error_append_hint(&err, "You have to use msi=auto (default) or " + "msi=off with this machine type.\n"); + error_propagate(errp, err); + return; + } + assert(!err || xhci->msi == ON_OFF_AUTO_AUTO); + /* With msi=auto, we fall back to MSI off silently */ + error_free(err); + } + if (xhci->numintrs > MAXINTRS) { xhci->numintrs = MAXINTRS; } @@ -3645,10 +3670,8 @@ static void usb_xhci_realize(struct PCIDevice *dev, Error **errp) assert(ret >= 0); } - if (xhci_get_flag(xhci, XHCI_FLAG_USE_MSI)) { - msi_init(dev, 0x70, xhci->numintrs, true, false); - } - if (xhci_get_flag(xhci, XHCI_FLAG_USE_MSI_X)) { + if (xhci->msix != ON_OFF_AUTO_OFF) { + /* TODO check for errors */ msix_init(dev, xhci->numintrs, &xhci->mem, 0, OFF_MSIX_TABLE, &xhci->mem, 0, OFF_MSIX_PBA, @@ -3869,8 +3892,8 @@ static const VMStateDescription vmstate_xhci = { }; static Property xhci_properties[] = { - DEFINE_PROP_BIT("msi", XHCIState, flags, XHCI_FLAG_USE_MSI, true), - DEFINE_PROP_BIT("msix", XHCIState, flags, XHCI_FLAG_USE_MSI_X, true), + DEFINE_PROP_ON_OFF_AUTO("msi", XHCIState, msi, ON_OFF_AUTO_AUTO), + DEFINE_PROP_ON_OFF_AUTO("msix", XHCIState, msix, ON_OFF_AUTO_AUTO), DEFINE_PROP_BIT("superspeed-ports-first", XHCIState, flags, XHCI_FLAG_SS_FIRST, true), DEFINE_PROP_BIT("force-pcie-endcap", XHCIState, flags, diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index 6458a9448..e94672c15 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -34,7 +34,9 @@ */ #include "qemu/osdep.h" +#ifndef CONFIG_WIN32 #include <poll.h> +#endif #include <libusb.h> #include "qapi/error.h" @@ -79,6 +81,7 @@ struct USBHostDevice { uint32_t iso_urb_frames; uint32_t options; uint32_t loglevel; + bool needs_autoscan; /* state */ QTAILQ_ENTRY(USBHostDevice) next; @@ -204,6 +207,8 @@ static const char *err_names[] = { static libusb_context *ctx; static uint32_t loglevel; +#ifndef CONFIG_WIN32 + static void usb_host_handle_fd(void *opaque) { struct timeval tv = { 0, 0 }; @@ -223,10 +228,14 @@ static void usb_host_del_fd(int fd, void *user_data) qemu_set_fd_handler(fd, NULL, NULL, NULL); } +#endif /* !CONFIG_WIN32 */ + static int usb_host_init(void) { +#ifndef CONFIG_WIN32 const struct libusb_pollfd **poll; - int i, rc; +#endif + int rc; if (ctx) { return 0; @@ -236,17 +245,21 @@ static int usb_host_init(void) return -1; } libusb_set_debug(ctx, loglevel); - +#ifdef CONFIG_WIN32 + /* FIXME: add support for Windows. */ +#else libusb_set_pollfd_notifiers(ctx, usb_host_add_fd, usb_host_del_fd, ctx); poll = libusb_get_pollfds(ctx); if (poll) { + int i; for (i = 0; poll[i] != NULL; i++) { usb_host_add_fd(poll[i]->fd, poll[i]->events, ctx); } } free(poll); +#endif return 0; } @@ -346,7 +359,7 @@ static USBHostRequest *usb_host_req_find(USBHostDevice *s, USBPacket *p) return NULL; } -static void usb_host_req_complete_ctrl(struct libusb_transfer *xfer) +static void LIBUSB_CALL usb_host_req_complete_ctrl(struct libusb_transfer *xfer) { USBHostRequest *r = xfer->user_data; USBHostDevice *s = r->host; @@ -379,7 +392,7 @@ out: } } -static void usb_host_req_complete_data(struct libusb_transfer *xfer) +static void LIBUSB_CALL usb_host_req_complete_data(struct libusb_transfer *xfer) { USBHostRequest *r = xfer->user_data; USBHostDevice *s = r->host; @@ -435,7 +448,8 @@ static void usb_host_req_abort(USBHostRequest *r) /* ------------------------------------------------------------------------ */ -static void usb_host_req_complete_iso(struct libusb_transfer *transfer) +static void LIBUSB_CALL +usb_host_req_complete_iso(struct libusb_transfer *transfer) { USBHostIsoXfer *xfer = transfer->user_data; @@ -963,9 +977,32 @@ static void usb_host_exit_notifier(struct Notifier *n, void *data) } } +static libusb_device *usb_host_find_ref(int bus, int addr) +{ + libusb_device **devs = NULL; + libusb_device *ret = NULL; + int i, n; + + if (usb_host_init() != 0) { + return NULL; + } + n = libusb_get_device_list(ctx, &devs); + for (i = 0; i < n; i++) { + if (libusb_get_bus_number(devs[i]) == bus && + libusb_get_device_address(devs[i]) == addr) { + ret = libusb_ref_device(devs[i]); + break; + } + } + libusb_free_device_list(devs, 1); + return ret; +} + static void usb_host_realize(USBDevice *udev, Error **errp) { USBHostDevice *s = USB_HOST_DEVICE(udev); + libusb_device *ldev; + int rc; if (s->match.vendor_id > 0xffff) { error_setg(errp, "vendorid out of range"); @@ -986,11 +1023,33 @@ static void usb_host_realize(USBDevice *udev, Error **errp) QTAILQ_INIT(&s->requests); QTAILQ_INIT(&s->isorings); + if (s->match.addr && s->match.bus_num && + !s->match.vendor_id && + !s->match.product_id && + !s->match.port) { + s->needs_autoscan = false; + ldev = usb_host_find_ref(s->match.bus_num, + s->match.addr); + if (!ldev) { + error_setg(errp, "failed to find host usb device %d:%d", + s->match.bus_num, s->match.addr); + return; + } + rc = usb_host_open(s, ldev); + libusb_unref_device(ldev); + if (rc < 0) { + error_setg(errp, "failed to open host usb device %d:%d", + s->match.bus_num, s->match.addr); + return; + } + } else { + s->needs_autoscan = true; + QTAILQ_INSERT_TAIL(&hostdevs, s, next); + usb_host_auto_check(NULL); + } + s->exit.notify = usb_host_exit_notifier; qemu_add_exit_notifier(&s->exit); - - QTAILQ_INSERT_TAIL(&hostdevs, s, next); - usb_host_auto_check(NULL); } static void usb_host_instance_init(Object *obj) @@ -1008,7 +1067,9 @@ static void usb_host_handle_destroy(USBDevice *udev) USBHostDevice *s = USB_HOST_DEVICE(udev); qemu_remove_exit_notifier(&s->exit); - QTAILQ_REMOVE(&hostdevs, s, next); + if (s->needs_autoscan) { + QTAILQ_REMOVE(&hostdevs, s, next); + } usb_host_close(s); } diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 8d8054037..444672a00 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -109,6 +109,7 @@ struct USBRedirDevice { uint8_t debug; char *filter_str; int32_t bootindex; + bool enable_streams; /* Data passed from chardev the fd_read cb to the usbredirparser read cb */ const uint8_t *read_buf; int read_buf_size; @@ -542,9 +543,9 @@ static void usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p, start_iso.pkts_per_urb = 32; } - start_iso.no_urbs = (dev->endpoint[EP2I(ep)].bufpq_target_size + - start_iso.pkts_per_urb - 1) / - start_iso.pkts_per_urb; + start_iso.no_urbs = DIV_ROUND_UP( + dev->endpoint[EP2I(ep)].bufpq_target_size, + start_iso.pkts_per_urb); /* Output endpoints pre-fill only 1/2 of the packets, keeping the rest as overflow buffer. Also see the usbredir protocol documentation */ if (!(ep & USB_DIR_IN)) { @@ -1229,7 +1230,9 @@ static void usbredir_create_parser(USBRedirDevice *dev) usbredirparser_caps_set_cap(caps, usb_redir_cap_32bits_bulk_length); usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_receiving); #if USBREDIR_VERSION >= 0x000700 - usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_streams); + if (dev->enable_streams) { + usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_streams); + } #endif if (runstate_check(RUN_STATE_INMIGRATE)) { @@ -2476,6 +2479,7 @@ static Property usbredir_properties[] = { DEFINE_PROP_CHR("chardev", USBRedirDevice, cs), DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, usbredirparser_warning), DEFINE_PROP_STRING("filter", USBRedirDevice, filter_str), + DEFINE_PROP_BOOL("streams", USBRedirDevice, enable_streams, true), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/usb/trace-events b/hw/usb/trace-events new file mode 100644 index 000000000..2d42fd45d --- /dev/null +++ b/hw/usb/trace-events @@ -0,0 +1,268 @@ +# See docs/tracing.txt for syntax documentation. + +# hw/usb/core.c +usb_packet_state_change(int bus, const char *port, int ep, void *p, const char *o, const char *n) "bus %d, port %s, ep %d, packet %p, state %s -> %s" +usb_packet_state_fault(int bus, const char *port, int ep, void *p, const char *o, const char *n) "bus %d, port %s, ep %d, packet %p, state %s, expected %s" + +# hw/usb/bus.c +usb_port_claim(int bus, const char *port) "bus %d, port %s" +usb_port_attach(int bus, const char *port, const char *devspeed, const char *portspeed) "bus %d, port %s, devspeed %s, portspeed %s" +usb_port_detach(int bus, const char *port) "bus %d, port %s" +usb_port_release(int bus, const char *port) "bus %d, port %s" + +# hw/usb/hcd-ohci.c +usb_ohci_iso_td_read_failed(uint32_t addr) "ISO_TD read error at %x" +usb_ohci_iso_td_head(uint32_t head, uint32_t tail, uint32_t flags, uint32_t bp, uint32_t next, uint32_t be, uint32_t framenum, uint32_t startframe, uint32_t framecount, int rel_frame_num) "ISO_TD ED head 0x%.8x tailp 0x%.8x\n0x%.8x 0x%.8x 0x%.8x 0x%.8x\nframe_number 0x%.8x starting_frame 0x%.8x\nframe_count 0x%.8x relative %d" +usb_ohci_iso_td_head_offset(uint32_t o0, uint32_t o1, uint32_t o2, uint32_t o3, uint32_t o4, uint32_t o5, uint32_t o6, uint32_t o7) "0x%.8x 0x%.8x 0x%.8x 0x%.8x 0x%.8x 0x%.8x 0x%.8x 0x%.8x" +usb_ohci_iso_td_relative_frame_number_neg(int rel) "ISO_TD R=%d < 0" +usb_ohci_iso_td_relative_frame_number_big(int rel, int count) "ISO_TD R=%d > FC=%d" +usb_ohci_iso_td_bad_direction(int dir) "Bad direction %d" +usb_ohci_iso_td_bad_bp_be(uint32_t bp, uint32_t be) "ISO_TD bp 0x%.8x be 0x%.8x" +usb_ohci_iso_td_bad_cc_not_accessed(uint32_t start, uint32_t next) "ISO_TD cc != not accessed 0x%.8x 0x%.8x" +usb_ohci_iso_td_bad_cc_overrun(uint32_t start, uint32_t next) "ISO_TD start_offset=0x%.8x > next_offset=0x%.8x" +usb_ohci_iso_td_so(uint32_t so, uint32_t eo, uint32_t s, uint32_t e, const char *str, ssize_t len, int ret) "0x%.8x eo 0x%.8x\nsa 0x%.8x ea 0x%.8x\ndir %s len %zu ret %d" +usb_ohci_iso_td_data_overrun(int ret, ssize_t len) "DataOverrun %d > %zu" +usb_ohci_iso_td_data_underrun(int ret) "DataUnderrun %d" +usb_ohci_iso_td_nak(int ret) "got NAK/STALL %d" +usb_ohci_iso_td_bad_response(int ret) "Bad device response %d" +usb_ohci_port_attach(int index) "port #%d" +usb_ohci_port_detach(int index) "port #%d" +usb_ohci_port_wakeup(int index) "port #%d" +usb_ohci_port_suspend(int index) "port #%d" +usb_ohci_port_reset(int index) "port #%d" +usb_ohci_remote_wakeup(const char *s) "%s: SUSPEND->RESUME" +usb_ohci_reset(const char *s) "%s" +usb_ohci_start(const char *s) "%s: USB Operational" +usb_ohci_resume(const char *s) "%s: USB Resume" +usb_ohci_stop(const char *s) "%s: USB Suspended" +usb_ohci_exit(const char *s) "%s" +usb_ohci_set_ctl(const char *s, uint32_t new_state) "%s: new state 0x%x" +usb_ohci_td_underrun(void) "" +usb_ohci_td_dev_error(void) "" +usb_ohci_td_nak(void) "" +usb_ohci_td_stall(void) "" +usb_ohci_td_babble(void) "" +usb_ohci_td_bad_device_response(int rc) "%d" +usb_ohci_td_read_error(uint32_t addr) "TD read error at %x" +usb_ohci_td_bad_direction(int dir) "Bad direction %d" +usb_ohci_td_skip_async(void) "" +usb_ohci_td_pkt_hdr(uint32_t addr, int64_t pktlen, int64_t len, const char *s, int flag_r, uint32_t cbp, uint32_t be) " TD @ 0x%.8x %" PRId64 " of %" PRId64 " bytes %s r=%d cbp=0x%.8x be=0x%.8x" +usb_ohci_td_pkt_short(const char *dir, const char *buf) "%s data: %s" +usb_ohci_td_pkt_full(const char *dir, const char *buf) "%s data: %s" +usb_ohci_td_too_many_pending(void) "" +usb_ohci_td_packet_status(int status) "status=%d" +usb_ohci_ed_read_error(uint32_t addr) "ED read error at %x" +usb_ohci_ed_pkt(uint32_t cur, int h, int c, uint32_t head, uint32_t tail, uint32_t next) "ED @ 0x%.8x h=%u c=%u\n head=0x%.8x tailp=0x%.8x next=0x%.8x" +usb_ohci_ed_pkt_flags(uint32_t fa, uint32_t en, uint32_t d, int s, int k, int f, uint32_t mps) "fa=%u en=%u d=%u s=%u k=%u f=%u mps=%u" +usb_ohci_hcca_read_error(uint32_t addr) "HCCA read error at %x" +usb_ohci_mem_read_unaligned(uint32_t addr) "at %x" +usb_ohci_mem_read_bad_offset(uint32_t addr) "%x" +usb_ohci_mem_write_unaligned(uint32_t addr) "at %x" +usb_ohci_mem_write_bad_offset(uint32_t addr) "%x" +usb_ohci_process_lists(uint32_t head, uint32_t cur) "head %x, cur %x" +usb_ohci_bus_eof_timer_failed(const char *name) "%s: timer_new_ns failed" +usb_ohci_set_frame_interval(const char *name, uint16_t fi_x, uint16_t fi_u) "%s: FrameInterval = 0x%x (%u)" +usb_ohci_hub_power_up(void) "powered up all ports" +usb_ohci_hub_power_down(void) "powered down all ports" +usb_ohci_init_time(int64_t frametime, int64_t bittime) "usb_bit_time=%" PRId64 " usb_frame_time=%" PRId64 +usb_ohci_die(void) "" +usb_ohci_async_complete(void) "" + +# hw/usb/hcd-ehci.c +usb_ehci_reset(void) "=== RESET ===" +usb_ehci_unrealize(void) "=== UNREALIZE ===" +usb_ehci_opreg_read(uint32_t addr, const char *str, uint32_t val) "rd mmio %04x [%s] = %x" +usb_ehci_opreg_write(uint32_t addr, const char *str, uint32_t val) "wr mmio %04x [%s] = %x" +usb_ehci_opreg_change(uint32_t addr, const char *str, uint32_t new, uint32_t old) "ch mmio %04x [%s] = %x (old: %x)" +usb_ehci_portsc_read(uint32_t addr, uint32_t port, uint32_t val) "rd mmio %04x [port %d] = %x" +usb_ehci_portsc_write(uint32_t addr, uint32_t port, uint32_t val) "wr mmio %04x [port %d] = %x" +usb_ehci_portsc_change(uint32_t addr, uint32_t port, uint32_t new, uint32_t old) "ch mmio %04x [port %d] = %x (old: %x)" +usb_ehci_usbsts(const char *sts, int state) "usbsts %s %d" +usb_ehci_state(const char *schedule, const char *state) "%s schedule %s" +usb_ehci_qh_ptrs(void *q, uint32_t addr, uint32_t nxt, uint32_t c_qtd, uint32_t n_qtd, uint32_t a_qtd) "q %p - QH @ %08x: next %08x qtds %08x,%08x,%08x" +usb_ehci_qh_fields(uint32_t addr, int rl, int mplen, int eps, int ep, int devaddr) "QH @ %08x - rl %d, mplen %d, eps %d, ep %d, dev %d" +usb_ehci_qh_bits(uint32_t addr, int c, int h, int dtc, int i) "QH @ %08x - c %d, h %d, dtc %d, i %d" +usb_ehci_qtd_ptrs(void *q, uint32_t addr, uint32_t nxt, uint32_t altnext) "q %p - QTD @ %08x: next %08x altnext %08x" +usb_ehci_qtd_fields(uint32_t addr, int tbytes, int cpage, int cerr, int pid) "QTD @ %08x - tbytes %d, cpage %d, cerr %d, pid %d" +usb_ehci_qtd_bits(uint32_t addr, int ioc, int active, int halt, int babble, int xacterr) "QTD @ %08x - ioc %d, active %d, halt %d, babble %d, xacterr %d" +usb_ehci_itd(uint32_t addr, uint32_t nxt, uint32_t mplen, uint32_t mult, uint32_t ep, uint32_t devaddr) "ITD @ %08x: next %08x - mplen %d, mult %d, ep %d, dev %d" +usb_ehci_sitd(uint32_t addr, uint32_t nxt, uint32_t active) "ITD @ %08x: next %08x - active %d" +usb_ehci_port_attach(uint32_t port, const char *owner, const char *device) "attach port #%d, owner %s, device %s" +usb_ehci_port_detach(uint32_t port, const char *owner) "detach port #%d, owner %s" +usb_ehci_port_reset(uint32_t port, int enable) "reset port #%d - %d" +usb_ehci_port_suspend(uint32_t port) "port #%d" +usb_ehci_port_wakeup(uint32_t port) "port #%d" +usb_ehci_port_resume(uint32_t port) "port #%d" +usb_ehci_queue_action(void *q, const char *action) "q %p: %s" +usb_ehci_packet_action(void *q, void *p, const char *action) "q %p p %p: %s" +usb_ehci_irq(uint32_t level, uint32_t frindex, uint32_t sts, uint32_t mask) "level %d, frindex 0x%04x, sts 0x%x, mask 0x%x" +usb_ehci_guest_bug(const char *reason) "%s" +usb_ehci_doorbell_ring(void) "" +usb_ehci_doorbell_ack(void) "" +usb_ehci_dma_error(void) "" + +# hw/usb/hcd-uhci.c +usb_uhci_reset(void) "=== RESET ===" +usb_uhci_exit(void) "=== EXIT ===" +usb_uhci_schedule_start(void) "" +usb_uhci_schedule_stop(void) "" +usb_uhci_frame_start(uint32_t num) "nr %d" +usb_uhci_frame_stop_bandwidth(void) "" +usb_uhci_frame_loop_stop_idle(void) "" +usb_uhci_frame_loop_continue(void) "" +usb_uhci_mmio_readw(uint32_t addr, uint32_t val) "addr 0x%04x, ret 0x%04x" +usb_uhci_mmio_writew(uint32_t addr, uint32_t val) "addr 0x%04x, val 0x%04x" +usb_uhci_queue_add(uint32_t token) "token 0x%x" +usb_uhci_queue_del(uint32_t token, const char *reason) "token 0x%x: %s" +usb_uhci_packet_add(uint32_t token, uint32_t addr) "token 0x%x, td 0x%x" +usb_uhci_packet_link_async(uint32_t token, uint32_t addr) "token 0x%x, td 0x%x" +usb_uhci_packet_unlink_async(uint32_t token, uint32_t addr) "token 0x%x, td 0x%x" +usb_uhci_packet_cancel(uint32_t token, uint32_t addr, int done) "token 0x%x, td 0x%x, done %d" +usb_uhci_packet_complete_success(uint32_t token, uint32_t addr) "token 0x%x, td 0x%x" +usb_uhci_packet_complete_shortxfer(uint32_t token, uint32_t addr) "token 0x%x, td 0x%x" +usb_uhci_packet_complete_stall(uint32_t token, uint32_t addr) "token 0x%x, td 0x%x" +usb_uhci_packet_complete_babble(uint32_t token, uint32_t addr) "token 0x%x, td 0x%x" +usb_uhci_packet_complete_error(uint32_t token, uint32_t addr) "token 0x%x, td 0x%x" +usb_uhci_packet_del(uint32_t token, uint32_t addr) "token 0x%x, td 0x%x" +usb_uhci_qh_load(uint32_t qh) "qh 0x%x" +usb_uhci_td_load(uint32_t qh, uint32_t td, uint32_t ctrl, uint32_t token) "qh 0x%x, td 0x%x, ctrl 0x%x, token 0x%x" +usb_uhci_td_queue(uint32_t td, uint32_t ctrl, uint32_t token) "td 0x%x, ctrl 0x%x, token 0x%x" +usb_uhci_td_nextqh(uint32_t qh, uint32_t td) "qh 0x%x, td 0x%x" +usb_uhci_td_async(uint32_t qh, uint32_t td) "qh 0x%x, td 0x%x" +usb_uhci_td_complete(uint32_t qh, uint32_t td) "qh 0x%x, td 0x%x" + +# hw/usb/hcd-xhci.c +usb_xhci_reset(void) "=== RESET ===" +usb_xhci_exit(void) "=== EXIT ===" +usb_xhci_run(void) "" +usb_xhci_stop(void) "" +usb_xhci_cap_read(uint32_t off, uint32_t val) "off 0x%04x, ret 0x%08x" +usb_xhci_oper_read(uint32_t off, uint32_t val) "off 0x%04x, ret 0x%08x" +usb_xhci_port_read(uint32_t port, uint32_t off, uint32_t val) "port %d, off 0x%04x, ret 0x%08x" +usb_xhci_runtime_read(uint32_t off, uint32_t val) "off 0x%04x, ret 0x%08x" +usb_xhci_doorbell_read(uint32_t off, uint32_t val) "off 0x%04x, ret 0x%08x" +usb_xhci_oper_write(uint32_t off, uint32_t val) "off 0x%04x, val 0x%08x" +usb_xhci_port_write(uint32_t port, uint32_t off, uint32_t val) "port %d, off 0x%04x, val 0x%08x" +usb_xhci_runtime_write(uint32_t off, uint32_t val) "off 0x%04x, val 0x%08x" +usb_xhci_doorbell_write(uint32_t off, uint32_t val) "off 0x%04x, val 0x%08x" +usb_xhci_irq_intx(uint32_t level) "level %d" +usb_xhci_irq_msi(uint32_t nr) "nr %d" +usb_xhci_irq_msix(uint32_t nr) "nr %d" +usb_xhci_irq_msix_use(uint32_t nr) "nr %d" +usb_xhci_irq_msix_unuse(uint32_t nr) "nr %d" +usb_xhci_queue_event(uint32_t vector, uint32_t idx, const char *trb, const char *evt, uint64_t param, uint32_t status, uint32_t control) "v %d, idx %d, %s, %s, p %016" PRIx64 ", s %08x, c 0x%08x" +usb_xhci_fetch_trb(uint64_t addr, const char *name, uint64_t param, uint32_t status, uint32_t control) "addr %016" PRIx64 ", %s, p %016" PRIx64 ", s %08x, c 0x%08x" +usb_xhci_port_reset(uint32_t port, bool warm) "port %d, warm %d" +usb_xhci_port_link(uint32_t port, uint32_t pls) "port %d, pls %d" +usb_xhci_port_notify(uint32_t port, uint32_t pls) "port %d, bits %x" +usb_xhci_slot_enable(uint32_t slotid) "slotid %d" +usb_xhci_slot_disable(uint32_t slotid) "slotid %d" +usb_xhci_slot_address(uint32_t slotid, const char *port) "slotid %d, port %s" +usb_xhci_slot_configure(uint32_t slotid) "slotid %d" +usb_xhci_slot_evaluate(uint32_t slotid) "slotid %d" +usb_xhci_slot_reset(uint32_t slotid) "slotid %d" +usb_xhci_ep_enable(uint32_t slotid, uint32_t epid) "slotid %d, epid %d" +usb_xhci_ep_disable(uint32_t slotid, uint32_t epid) "slotid %d, epid %d" +usb_xhci_ep_set_dequeue(uint32_t slotid, uint32_t epid, uint32_t streamid, uint64_t param) "slotid %d, epid %d, streamid %d, ptr %016" PRIx64 +usb_xhci_ep_kick(uint32_t slotid, uint32_t epid, uint32_t streamid) "slotid %d, epid %d, streamid %d" +usb_xhci_ep_stop(uint32_t slotid, uint32_t epid) "slotid %d, epid %d" +usb_xhci_ep_reset(uint32_t slotid, uint32_t epid) "slotid %d, epid %d" +usb_xhci_ep_state(uint32_t slotid, uint32_t epid, const char *os, const char *ns) "slotid %d, epid %d, %s -> %s" +usb_xhci_xfer_start(void *xfer, uint32_t slotid, uint32_t epid, uint32_t streamid) "%p: slotid %d, epid %d, streamid %d" +usb_xhci_xfer_async(void *xfer) "%p" +usb_xhci_xfer_nak(void *xfer) "%p" +usb_xhci_xfer_retry(void *xfer) "%p" +usb_xhci_xfer_success(void *xfer, uint32_t bytes) "%p: len %d" +usb_xhci_xfer_error(void *xfer, uint32_t ret) "%p: ret %d" +usb_xhci_unimplemented(const char *item, int nr) "%s (0x%x)" + +# hw/usb/desc.c +usb_desc_device(int addr, int len, int ret) "dev %d query device, len %d, ret %d" +usb_desc_device_qualifier(int addr, int len, int ret) "dev %d query device qualifier, len %d, ret %d" +usb_desc_config(int addr, int index, int len, int ret) "dev %d query config %d, len %d, ret %d" +usb_desc_other_speed_config(int addr, int index, int len, int ret) "dev %d query config %d, len %d, ret %d" +usb_desc_string(int addr, int index, int len, int ret) "dev %d query string %d, len %d, ret %d" +usb_desc_bos(int addr, int len, int ret) "dev %d bos, len %d, ret %d" +usb_desc_msos(int addr, int index, int len, int ret) "dev %d msos, index 0x%x, len %d, ret %d" +usb_set_addr(int addr) "dev %d" +usb_set_config(int addr, int config, int ret) "dev %d, config %d, ret %d" +usb_set_interface(int addr, int iface, int alt, int ret) "dev %d, interface %d, altsetting %d, ret %d" +usb_clear_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d" +usb_set_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d" + +# hw/usb/dev-hub.c +usb_hub_reset(int addr) "dev %d" +usb_hub_control(int addr, int request, int value, int index, int length) "dev %d, req 0x%x, value %d, index %d, langth %d" +usb_hub_get_port_status(int addr, int nr, int status, int changed) "dev %d, port %d, status 0x%x, changed 0x%x" +usb_hub_set_port_feature(int addr, int nr, const char *f) "dev %d, port %d, feature %s" +usb_hub_clear_port_feature(int addr, int nr, const char *f) "dev %d, port %d, feature %s" +usb_hub_attach(int addr, int nr) "dev %d, port %d" +usb_hub_detach(int addr, int nr) "dev %d, port %d" +usb_hub_status_report(int addr, int status) "dev %d, status 0x%x" + +# hw/usb/dev-uas.c +usb_uas_reset(int addr) "dev %d" +usb_uas_command(int addr, uint16_t tag, int lun, uint32_t lun64_1, uint32_t lun64_2) "dev %d, tag 0x%x, lun %d, lun64 %08x-%08x" +usb_uas_response(int addr, uint16_t tag, uint8_t code) "dev %d, tag 0x%x, code 0x%x" +usb_uas_sense(int addr, uint16_t tag, uint8_t status) "dev %d, tag 0x%x, status 0x%x" +usb_uas_read_ready(int addr, uint16_t tag) "dev %d, tag 0x%x" +usb_uas_write_ready(int addr, uint16_t tag) "dev %d, tag 0x%x" +usb_uas_xfer_data(int addr, uint16_t tag, uint32_t copy, uint32_t uoff, uint32_t usize, uint32_t soff, uint32_t ssize) "dev %d, tag 0x%x, copy %d, usb-pkt %d/%d, scsi-buf %d/%d" +usb_uas_scsi_data(int addr, uint16_t tag, uint32_t bytes) "dev %d, tag 0x%x, bytes %d" +usb_uas_scsi_complete(int addr, uint16_t tag, uint32_t status, uint32_t resid) "dev %d, tag 0x%x, status 0x%x, residue %d" +usb_uas_tmf_abort_task(int addr, uint16_t tag, uint16_t task_tag) "dev %d, tag 0x%x, task-tag 0x%x" +usb_uas_tmf_logical_unit_reset(int addr, uint16_t tag, int lun) "dev %d, tag 0x%x, lun %d" +usb_uas_tmf_unsupported(int addr, uint16_t tag, uint32_t function) "dev %d, tag 0x%x, function 0x%x" + +# hw/usb/dev-mtp.c +usb_mtp_reset(int addr) "dev %d" +usb_mtp_command(int dev, uint16_t code, uint32_t trans, uint32_t arg0, uint32_t arg1, uint32_t arg2, uint32_t arg3, uint32_t arg4) "dev %d, code 0x%x, trans 0x%x, args 0x%x, 0x%x, 0x%x, 0x%x, 0x%x" +usb_mtp_success(int dev, uint32_t trans, uint32_t arg0, uint32_t arg1) "dev %d, trans 0x%x, args 0x%x, 0x%x" +usb_mtp_error(int dev, uint16_t code, uint32_t trans, uint32_t arg0, uint32_t arg1) "dev %d, code 0x%x, trans 0x%x, args 0x%x, 0x%x" +usb_mtp_data_in(int dev, uint32_t trans, uint32_t len) "dev %d, trans 0x%x, len %d" +usb_mtp_xfer(int dev, uint32_t ep, uint32_t dlen, uint32_t plen) "dev %d, ep %d, %d/%d" +usb_mtp_nak(int dev, uint32_t ep) "dev %d, ep %d" +usb_mtp_stall(int dev, const char *reason) "dev %d, reason: %s" +usb_mtp_op_get_device_info(int dev) "dev %d" +usb_mtp_op_open_session(int dev) "dev %d" +usb_mtp_op_close_session(int dev) "dev %d" +usb_mtp_op_get_storage_ids(int dev) "dev %d" +usb_mtp_op_get_storage_info(int dev) "dev %d" +usb_mtp_op_get_num_objects(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s" +usb_mtp_op_get_object_handles(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s" +usb_mtp_op_get_object_info(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s" +usb_mtp_op_get_object(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s" +usb_mtp_op_get_partial_object(int dev, uint32_t handle, const char *path, uint32_t offset, uint32_t length) "dev %d, handle 0x%x, path %s, off %d, len %d" +usb_mtp_op_unknown(int dev, uint32_t code) "dev %d, command code 0x%x" +usb_mtp_object_alloc(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s" +usb_mtp_object_free(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s" +usb_mtp_add_child(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s" +usb_mtp_inotify_event(int dev, const char *path, uint32_t mask, const char *s) "dev %d, path %s mask 0x%x event %s" + +# hw/usb/host-libusb.c +usb_host_open_started(int bus, int addr) "dev %d:%d" +usb_host_open_success(int bus, int addr) "dev %d:%d" +usb_host_open_failure(int bus, int addr) "dev %d:%d" +usb_host_close(int bus, int addr) "dev %d:%d" +usb_host_attach_kernel(int bus, int addr, int interface) "dev %d:%d, if %d" +usb_host_detach_kernel(int bus, int addr, int interface) "dev %d:%d, if %d" +usb_host_set_address(int bus, int addr, int config) "dev %d:%d, address %d" +usb_host_set_config(int bus, int addr, int config) "dev %d:%d, config %d" +usb_host_set_interface(int bus, int addr, int interface, int alt) "dev %d:%d, interface %d, alt %d" +usb_host_claim_interface(int bus, int addr, int config, int interface) "dev %d:%d, config %d, if %d" +usb_host_release_interface(int bus, int addr, int interface) "dev %d:%d, if %d" +usb_host_req_control(int bus, int addr, void *p, int req, int value, int index) "dev %d:%d, packet %p, req 0x%x, value %d, index %d" +usb_host_req_data(int bus, int addr, void *p, int in, int ep, int size) "dev %d:%d, packet %p, in %d, ep %d, size %d" +usb_host_req_complete(int bus, int addr, void *p, int status, int length) "dev %d:%d, packet %p, status %d, length %d" +usb_host_req_emulated(int bus, int addr, void *p, int status) "dev %d:%d, packet %p, status %d" +usb_host_req_canceled(int bus, int addr, void *p) "dev %d:%d, packet %p" +usb_host_iso_start(int bus, int addr, int ep) "dev %d:%d, ep %d" +usb_host_iso_stop(int bus, int addr, int ep) "dev %d:%d, ep %d" +usb_host_iso_out_of_bufs(int bus, int addr, int ep) "dev %d:%d, ep %d" +usb_host_reset(int bus, int addr) "dev %d:%d" +usb_host_auto_scan_enabled(void) +usb_host_auto_scan_disabled(void) +usb_host_parse_config(int bus, int addr, int value, int active) "dev %d:%d, value %d, active %d" +usb_host_parse_interface(int bus, int addr, int num, int alt, int active) "dev %d:%d, num %d, alt %d, active %d" +usb_host_parse_endpoint(int bus, int addr, int ep, const char *dir, const char *type, int active) "dev %d:%d, ep %d, %s, %s, active %d" +usb_host_parse_error(int bus, int addr, const char *errmsg) "dev %d:%d, msg %s" diff --git a/hw/usb/xen-usb.c b/hw/usb/xen-usb.c new file mode 100644 index 000000000..174d715e3 --- /dev/null +++ b/hw/usb/xen-usb.c @@ -0,0 +1,1107 @@ +/* + * xen paravirt usb device backend + * + * (c) Juergen Gross <jgross@suse.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; under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include <libusb.h> +#include <sys/user.h> + +#include "qemu-common.h" +#include "qemu/config-file.h" +#include "hw/sysbus.h" +#include "hw/usb.h" +#include "hw/xen/xen_backend.h" +#include "monitor/qdev.h" +#include "qapi/qmp/qbool.h" +#include "qapi/qmp/qint.h" +#include "qapi/qmp/qstring.h" + +#include <xen/io/ring.h> +#include <xen/io/usbif.h> + +/* + * Check for required support of usbif.h: USBIF_SHORT_NOT_OK was the last + * macro added we rely on. + */ +#ifdef USBIF_SHORT_NOT_OK + +#define TR(xendev, lvl, fmt, args...) \ + { \ + struct timeval tv; \ + \ + gettimeofday(&tv, NULL); \ + xen_be_printf(xendev, lvl, "%8ld.%06ld xen-usb(%s):" fmt, \ + tv.tv_sec, tv.tv_usec, __func__, ##args); \ + } +#define TR_BUS(xendev, fmt, args...) TR(xendev, 2, fmt, ##args) +#define TR_REQ(xendev, fmt, args...) TR(xendev, 3, fmt, ##args) + +#define USBBACK_MAXPORTS USBIF_PIPE_PORT_MASK +#define USB_DEV_ADDR_SIZE (USBIF_PIPE_DEV_MASK + 1) + +/* USB wire protocol: structure describing control request parameter. */ +struct usbif_ctrlrequest { + uint8_t bRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +}; + +struct usbback_info; +struct usbback_req; + +struct usbback_stub { + USBDevice *dev; + USBPort port; + unsigned int speed; + bool attached; + QTAILQ_HEAD(submit_q_head, usbback_req) submit_q; +}; + +struct usbback_req { + struct usbback_info *usbif; + struct usbback_stub *stub; + struct usbif_urb_request req; + USBPacket packet; + + unsigned int nr_buffer_segs; /* # of transfer_buffer segments */ + unsigned int nr_extra_segs; /* # of iso_frame_desc segments */ + + QTAILQ_ENTRY(usbback_req) q; + + void *buffer; + void *isoc_buffer; + struct libusb_transfer *xfer; + + bool cancelled; +}; + +struct usbback_hotplug { + QSIMPLEQ_ENTRY(usbback_hotplug) q; + unsigned port; +}; + +struct usbback_info { + struct XenDevice xendev; /* must be first */ + USBBus bus; + void *urb_sring; + void *conn_sring; + struct usbif_urb_back_ring urb_ring; + struct usbif_conn_back_ring conn_ring; + int num_ports; + int usb_ver; + bool ring_error; + QTAILQ_HEAD(req_free_q_head, usbback_req) req_free_q; + QSIMPLEQ_HEAD(hotplug_q_head, usbback_hotplug) hotplug_q; + struct usbback_stub ports[USBBACK_MAXPORTS]; + struct usbback_stub *addr_table[USB_DEV_ADDR_SIZE]; + QEMUBH *bh; +}; + +static struct usbback_req *usbback_get_req(struct usbback_info *usbif) +{ + struct usbback_req *usbback_req; + + if (QTAILQ_EMPTY(&usbif->req_free_q)) { + usbback_req = g_new0(struct usbback_req, 1); + } else { + usbback_req = QTAILQ_FIRST(&usbif->req_free_q); + QTAILQ_REMOVE(&usbif->req_free_q, usbback_req, q); + } + return usbback_req; +} + +static void usbback_put_req(struct usbback_req *usbback_req) +{ + struct usbback_info *usbif; + + usbif = usbback_req->usbif; + memset(usbback_req, 0, sizeof(*usbback_req)); + QTAILQ_INSERT_HEAD(&usbif->req_free_q, usbback_req, q); +} + +static int usbback_gnttab_map(struct usbback_req *usbback_req) +{ + unsigned int nr_segs, i, prot; + uint32_t ref[USBIF_MAX_SEGMENTS_PER_REQUEST]; + struct usbback_info *usbif = usbback_req->usbif; + struct XenDevice *xendev = &usbif->xendev; + struct usbif_request_segment *seg; + void *addr; + + nr_segs = usbback_req->nr_buffer_segs + usbback_req->nr_extra_segs; + if (!nr_segs) { + return 0; + } + + if (nr_segs > USBIF_MAX_SEGMENTS_PER_REQUEST) { + xen_be_printf(xendev, 0, "bad number of segments in request (%d)\n", + nr_segs); + return -EINVAL; + } + + for (i = 0; i < nr_segs; i++) { + if ((unsigned)usbback_req->req.seg[i].offset + + (unsigned)usbback_req->req.seg[i].length > PAGE_SIZE) { + xen_be_printf(xendev, 0, "segment crosses page boundary\n"); + return -EINVAL; + } + } + + if (usbback_req->nr_buffer_segs) { + prot = PROT_READ; + if (usbif_pipein(usbback_req->req.pipe)) { + prot |= PROT_WRITE; + } + for (i = 0; i < usbback_req->nr_buffer_segs; i++) { + ref[i] = usbback_req->req.seg[i].gref; + } + usbback_req->buffer = xengnttab_map_domain_grant_refs(xendev->gnttabdev, + usbback_req->nr_buffer_segs, xendev->dom, ref, prot); + + if (!usbback_req->buffer) { + return -ENOMEM; + } + + for (i = 0; i < usbback_req->nr_buffer_segs; i++) { + seg = usbback_req->req.seg + i; + addr = usbback_req->buffer + i * PAGE_SIZE + seg->offset; + qemu_iovec_add(&usbback_req->packet.iov, addr, seg->length); + } + } + + if (!usbif_pipeisoc(usbback_req->req.pipe)) { + return 0; + } + + /* + * Right now isoc requests are not supported. + * Prepare supporting those by doing the work needed on the guest + * interface side. + */ + + if (!usbback_req->nr_extra_segs) { + xen_be_printf(xendev, 0, "iso request without descriptor segments\n"); + return -EINVAL; + } + + prot = PROT_READ | PROT_WRITE; + for (i = 0; i < usbback_req->nr_extra_segs; i++) { + ref[i] = usbback_req->req.seg[i + usbback_req->req.nr_buffer_segs].gref; + } + usbback_req->isoc_buffer = xengnttab_map_domain_grant_refs( + xendev->gnttabdev, usbback_req->nr_extra_segs, xendev->dom, ref, prot); + + if (!usbback_req->isoc_buffer) { + return -ENOMEM; + } + + return 0; +} + +static int usbback_init_packet(struct usbback_req *usbback_req) +{ + struct XenDevice *xendev = &usbback_req->usbif->xendev; + USBPacket *packet = &usbback_req->packet; + USBDevice *dev = usbback_req->stub->dev; + USBEndpoint *ep; + unsigned int pid, ep_nr; + bool sok; + int ret = 0; + + qemu_iovec_init(&packet->iov, USBIF_MAX_SEGMENTS_PER_REQUEST); + pid = usbif_pipein(usbback_req->req.pipe) ? USB_TOKEN_IN : USB_TOKEN_OUT; + ep_nr = usbif_pipeendpoint(usbback_req->req.pipe); + sok = !!(usbback_req->req.transfer_flags & USBIF_SHORT_NOT_OK); + if (usbif_pipectrl(usbback_req->req.pipe)) { + ep_nr = 0; + sok = false; + } + ep = usb_ep_get(dev, pid, ep_nr); + usb_packet_setup(packet, pid, ep, 0, 1, sok, true); + + switch (usbif_pipetype(usbback_req->req.pipe)) { + case USBIF_PIPE_TYPE_ISOC: + TR_REQ(xendev, "iso transfer %s: buflen: %x, %d frames\n", + (pid == USB_TOKEN_IN) ? "in" : "out", + usbback_req->req.buffer_length, + usbback_req->req.u.isoc.nr_frame_desc_segs); + ret = -EINVAL; /* isoc not implemented yet */ + break; + + case USBIF_PIPE_TYPE_INT: + TR_REQ(xendev, "int transfer %s: buflen: %x\n", + (pid == USB_TOKEN_IN) ? "in" : "out", + usbback_req->req.buffer_length); + break; + + case USBIF_PIPE_TYPE_CTRL: + packet->parameter = *(uint64_t *)usbback_req->req.u.ctrl; + TR_REQ(xendev, "ctrl parameter: %"PRIx64", buflen: %x\n", + packet->parameter, + usbback_req->req.buffer_length); + break; + + case USBIF_PIPE_TYPE_BULK: + TR_REQ(xendev, "bulk transfer %s: buflen: %x\n", + (pid == USB_TOKEN_IN) ? "in" : "out", + usbback_req->req.buffer_length); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static void usbback_do_response(struct usbback_req *usbback_req, int32_t status, + int32_t actual_length, int32_t error_count) +{ + struct usbback_info *usbif; + struct usbif_urb_response *res; + struct XenDevice *xendev; + unsigned int notify; + + usbif = usbback_req->usbif; + xendev = &usbif->xendev; + + TR_REQ(xendev, "id %d, status %d, length %d, errcnt %d\n", + usbback_req->req.id, status, actual_length, error_count); + + if (usbback_req->packet.iov.iov) { + qemu_iovec_destroy(&usbback_req->packet.iov); + } + + if (usbback_req->buffer) { + xengnttab_unmap(xendev->gnttabdev, usbback_req->buffer, + usbback_req->nr_buffer_segs); + usbback_req->buffer = NULL; + } + + if (usbback_req->isoc_buffer) { + xengnttab_unmap(xendev->gnttabdev, usbback_req->isoc_buffer, + usbback_req->nr_extra_segs); + usbback_req->isoc_buffer = NULL; + } + + if (usbif->urb_sring) { + res = RING_GET_RESPONSE(&usbif->urb_ring, usbif->urb_ring.rsp_prod_pvt); + res->id = usbback_req->req.id; + res->status = status; + res->actual_length = actual_length; + res->error_count = error_count; + res->start_frame = 0; + usbif->urb_ring.rsp_prod_pvt++; + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&usbif->urb_ring, notify); + + if (notify) { + xen_be_send_notify(xendev); + } + } + + if (!usbback_req->cancelled) + usbback_put_req(usbback_req); +} + +static void usbback_do_response_ret(struct usbback_req *usbback_req, + int32_t status) +{ + usbback_do_response(usbback_req, status, 0, 0); +} + +static int32_t usbback_xlat_status(int status) +{ + switch (status) { + case USB_RET_SUCCESS: + return 0; + case USB_RET_NODEV: + return -ENODEV; + case USB_RET_STALL: + return -EPIPE; + case USB_RET_BABBLE: + return -EOVERFLOW; + case USB_RET_IOERROR: + return -EPROTO; + } + + return -ESHUTDOWN; +} + +static void usbback_packet_complete(USBPacket *packet) +{ + struct usbback_req *usbback_req; + int32_t status; + + usbback_req = container_of(packet, struct usbback_req, packet); + + QTAILQ_REMOVE(&usbback_req->stub->submit_q, usbback_req, q); + + status = usbback_xlat_status(packet->status); + usbback_do_response(usbback_req, status, packet->actual_length, 0); +} + +static void usbback_set_address(struct usbback_info *usbif, + struct usbback_stub *stub, + unsigned int cur_addr, unsigned int new_addr) +{ + if (cur_addr) { + usbif->addr_table[cur_addr] = NULL; + } + if (new_addr) { + usbif->addr_table[new_addr] = stub; + } +} + +static void usbback_cancel_req(struct usbback_req *usbback_req) +{ + if (usb_packet_is_inflight(&usbback_req->packet)) { + usb_cancel_packet(&usbback_req->packet); + QTAILQ_REMOVE(&usbback_req->stub->submit_q, usbback_req, q); + usbback_req->cancelled = true; + usbback_do_response_ret(usbback_req, -EPROTO); + } +} + +static void usbback_process_unlink_req(struct usbback_req *usbback_req) +{ + struct usbback_info *usbif; + struct usbback_req *unlink_req; + unsigned int id, devnum; + int ret; + + usbif = usbback_req->usbif; + ret = 0; + id = usbback_req->req.u.unlink.unlink_id; + TR_REQ(&usbif->xendev, "unlink id %d\n", id); + devnum = usbif_pipedevice(usbback_req->req.pipe); + if (unlikely(devnum == 0)) { + usbback_req->stub = usbif->ports + + usbif_pipeportnum(usbback_req->req.pipe) - 1; + if (unlikely(!usbback_req->stub)) { + ret = -ENODEV; + goto fail_response; + } + } else { + if (unlikely(!usbif->addr_table[devnum])) { + ret = -ENODEV; + goto fail_response; + } + usbback_req->stub = usbif->addr_table[devnum]; + } + + QTAILQ_FOREACH(unlink_req, &usbback_req->stub->submit_q, q) { + if (unlink_req->req.id == id) { + usbback_cancel_req(unlink_req); + break; + } + } + +fail_response: + usbback_do_response_ret(usbback_req, ret); +} + +/* + * Checks whether a request can be handled at once or should be forwarded + * to the usb framework. + * Return value is: + * 0 in case of usb framework is needed + * 1 in case of local handling (no error) + * The request response has been queued already if return value not 0. + */ +static int usbback_check_and_submit(struct usbback_req *usbback_req) +{ + struct usbback_info *usbif; + unsigned int devnum; + struct usbback_stub *stub; + struct usbif_ctrlrequest *ctrl; + int ret; + uint16_t wValue; + + usbif = usbback_req->usbif; + stub = NULL; + devnum = usbif_pipedevice(usbback_req->req.pipe); + ctrl = (struct usbif_ctrlrequest *)usbback_req->req.u.ctrl; + wValue = le16_to_cpu(ctrl->wValue); + + /* + * When the device is first connected or resetted, USB device has no + * address. In this initial state, following requests are sent to device + * address (#0), + * + * 1. GET_DESCRIPTOR (with Descriptor Type is "DEVICE") is sent, + * and OS knows what device is connected to. + * + * 2. SET_ADDRESS is sent, and then device has its address. + * + * In the next step, SET_CONFIGURATION is sent to addressed device, and + * then the device is finally ready to use. + */ + if (unlikely(devnum == 0)) { + stub = usbif->ports + usbif_pipeportnum(usbback_req->req.pipe) - 1; + if (!stub->dev || !stub->attached) { + ret = -ENODEV; + goto do_response; + } + + switch (ctrl->bRequest) { + case USB_REQ_GET_DESCRIPTOR: + /* + * GET_DESCRIPTOR request to device #0. + * through normal transfer. + */ + TR_REQ(&usbif->xendev, "devnum 0 GET_DESCRIPTOR\n"); + usbback_req->stub = stub; + return 0; + case USB_REQ_SET_ADDRESS: + /* + * SET_ADDRESS request to device #0. + * add attached device to addr_table. + */ + TR_REQ(&usbif->xendev, "devnum 0 SET_ADDRESS\n"); + usbback_set_address(usbif, stub, 0, wValue); + ret = 0; + break; + default: + ret = -EINVAL; + break; + } + goto do_response; + } + + if (unlikely(!usbif->addr_table[devnum])) { + ret = -ENODEV; + goto do_response; + } + usbback_req->stub = usbif->addr_table[devnum]; + + /* + * Check special request + */ + if (ctrl->bRequest != USB_REQ_SET_ADDRESS) { + return 0; + } + + /* + * SET_ADDRESS request to addressed device. + * change addr or remove from addr_table. + */ + usbback_set_address(usbif, usbback_req->stub, devnum, wValue); + ret = 0; + +do_response: + usbback_do_response_ret(usbback_req, ret); + return 1; +} + +static void usbback_dispatch(struct usbback_req *usbback_req) +{ + int ret; + unsigned int devnum; + struct usbback_info *usbif; + + usbif = usbback_req->usbif; + + TR_REQ(&usbif->xendev, "start req_id %d pipe %08x\n", usbback_req->req.id, + usbback_req->req.pipe); + + /* unlink request */ + if (unlikely(usbif_pipeunlink(usbback_req->req.pipe))) { + usbback_process_unlink_req(usbback_req); + return; + } + + if (usbif_pipectrl(usbback_req->req.pipe)) { + if (usbback_check_and_submit(usbback_req)) { + return; + } + } else { + devnum = usbif_pipedevice(usbback_req->req.pipe); + usbback_req->stub = usbif->addr_table[devnum]; + + if (!usbback_req->stub || !usbback_req->stub->attached) { + ret = -ENODEV; + goto fail_response; + } + } + + QTAILQ_INSERT_TAIL(&usbback_req->stub->submit_q, usbback_req, q); + + usbback_req->nr_buffer_segs = usbback_req->req.nr_buffer_segs; + usbback_req->nr_extra_segs = usbif_pipeisoc(usbback_req->req.pipe) ? + usbback_req->req.u.isoc.nr_frame_desc_segs : 0; + + ret = usbback_init_packet(usbback_req); + if (ret) { + xen_be_printf(&usbif->xendev, 0, "invalid request\n"); + ret = -ESHUTDOWN; + goto fail_free_urb; + } + + ret = usbback_gnttab_map(usbback_req); + if (ret) { + xen_be_printf(&usbif->xendev, 0, "invalid buffer, ret=%d\n", ret); + ret = -ESHUTDOWN; + goto fail_free_urb; + } + + usb_handle_packet(usbback_req->stub->dev, &usbback_req->packet); + if (usbback_req->packet.status != USB_RET_ASYNC) { + usbback_packet_complete(&usbback_req->packet); + } + return; + +fail_free_urb: + QTAILQ_REMOVE(&usbback_req->stub->submit_q, usbback_req, q); + +fail_response: + usbback_do_response_ret(usbback_req, ret); +} + +static void usbback_hotplug_notify(struct usbback_info *usbif) +{ + struct usbif_conn_back_ring *ring = &usbif->conn_ring; + struct usbif_conn_request req; + struct usbif_conn_response *res; + struct usbback_hotplug *usb_hp; + unsigned int notify; + + if (!usbif->conn_sring) { + return; + } + + /* Check for full ring. */ + if ((RING_SIZE(ring) - ring->rsp_prod_pvt - ring->req_cons) == 0) { + xen_be_send_notify(&usbif->xendev); + return; + } + + usb_hp = QSIMPLEQ_FIRST(&usbif->hotplug_q); + QSIMPLEQ_REMOVE_HEAD(&usbif->hotplug_q, q); + + RING_COPY_REQUEST(ring, ring->req_cons, &req); + ring->req_cons++; + ring->sring->req_event = ring->req_cons + 1; + + res = RING_GET_RESPONSE(ring, ring->rsp_prod_pvt); + res->id = req.id; + res->portnum = usb_hp->port; + res->speed = usbif->ports[usb_hp->port - 1].speed; + ring->rsp_prod_pvt++; + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(ring, notify); + + if (notify) { + xen_be_send_notify(&usbif->xendev); + } + + TR_BUS(&usbif->xendev, "hotplug port %d speed %d\n", usb_hp->port, + res->speed); + + g_free(usb_hp); + + if (!QSIMPLEQ_EMPTY(&usbif->hotplug_q)) { + qemu_bh_schedule(usbif->bh); + } +} + +static void usbback_bh(void *opaque) +{ + struct usbback_info *usbif; + struct usbif_urb_back_ring *urb_ring; + struct usbback_req *usbback_req; + RING_IDX rc, rp; + unsigned int more_to_do; + + usbif = opaque; + if (usbif->ring_error) { + return; + } + + if (!QSIMPLEQ_EMPTY(&usbif->hotplug_q)) { + usbback_hotplug_notify(usbif); + } + + urb_ring = &usbif->urb_ring; + rc = urb_ring->req_cons; + rp = urb_ring->sring->req_prod; + xen_rmb(); /* Ensure we see queued requests up to 'rp'. */ + + if (RING_REQUEST_PROD_OVERFLOW(urb_ring, rp)) { + rc = urb_ring->rsp_prod_pvt; + xen_be_printf(&usbif->xendev, 0, "domU provided bogus ring requests " + "(%#x - %#x = %u). Halting ring processing.\n", + rp, rc, rp - rc); + usbif->ring_error = true; + return; + } + + while (rc != rp) { + if (RING_REQUEST_CONS_OVERFLOW(urb_ring, rc)) { + break; + } + usbback_req = usbback_get_req(usbif); + + RING_COPY_REQUEST(urb_ring, rc, &usbback_req->req); + usbback_req->usbif = usbif; + + usbback_dispatch(usbback_req); + + urb_ring->req_cons = ++rc; + } + + RING_FINAL_CHECK_FOR_REQUESTS(urb_ring, more_to_do); + if (more_to_do) { + qemu_bh_schedule(usbif->bh); + } +} + +static void usbback_hotplug_enq(struct usbback_info *usbif, unsigned port) +{ + struct usbback_hotplug *usb_hp; + + usb_hp = g_new0(struct usbback_hotplug, 1); + usb_hp->port = port; + QSIMPLEQ_INSERT_TAIL(&usbif->hotplug_q, usb_hp, q); + usbback_hotplug_notify(usbif); +} + +static void usbback_portid_drain(struct usbback_info *usbif, unsigned port) +{ + struct usbback_req *req, *tmp; + bool sched = false; + + QTAILQ_FOREACH_SAFE(req, &usbif->ports[port - 1].submit_q, q, tmp) { + usbback_cancel_req(req); + sched = true; + } + + if (sched) { + qemu_bh_schedule(usbif->bh); + } +} + +static void usbback_portid_detach(struct usbback_info *usbif, unsigned port) +{ + if (!usbif->ports[port - 1].attached) { + return; + } + + usbif->ports[port - 1].speed = USBIF_SPEED_NONE; + usbif->ports[port - 1].attached = false; + usbback_portid_drain(usbif, port); + usbback_hotplug_enq(usbif, port); +} + +static void usbback_portid_remove(struct usbback_info *usbif, unsigned port) +{ + USBPort *p; + + if (!usbif->ports[port - 1].dev) { + return; + } + + p = &(usbif->ports[port - 1].port); + snprintf(p->path, sizeof(p->path), "%d", 99); + + object_unparent(OBJECT(usbif->ports[port - 1].dev)); + usbif->ports[port - 1].dev = NULL; + usbback_portid_detach(usbif, port); + + TR_BUS(&usbif->xendev, "port %d removed\n", port); +} + +static void usbback_portid_add(struct usbback_info *usbif, unsigned port, + char *busid) +{ + unsigned speed; + char *portname; + USBPort *p; + Error *local_err = NULL; + QDict *qdict; + QemuOpts *opts; + + if (usbif->ports[port - 1].dev) { + return; + } + + portname = strchr(busid, '-'); + if (!portname) { + xen_be_printf(&usbif->xendev, 0, "device %s illegal specification\n", + busid); + return; + } + portname++; + p = &(usbif->ports[port - 1].port); + snprintf(p->path, sizeof(p->path), "%s", portname); + + qdict = qdict_new(); + qdict_put(qdict, "driver", qstring_from_str("usb-host")); + qdict_put(qdict, "hostbus", qint_from_int(atoi(busid))); + qdict_put(qdict, "hostport", qstring_from_str(portname)); + opts = qemu_opts_from_qdict(qemu_find_opts("device"), qdict, &local_err); + if (local_err) { + goto err; + } + usbif->ports[port - 1].dev = USB_DEVICE(qdev_device_add(opts, &local_err)); + if (!usbif->ports[port - 1].dev) { + goto err; + } + QDECREF(qdict); + snprintf(p->path, sizeof(p->path), "%d", port); + speed = usbif->ports[port - 1].dev->speed; + switch (speed) { + case USB_SPEED_LOW: + speed = USBIF_SPEED_LOW; + break; + case USB_SPEED_FULL: + speed = USBIF_SPEED_FULL; + break; + case USB_SPEED_HIGH: + speed = (usbif->usb_ver < USB_VER_USB20) ? + USBIF_SPEED_NONE : USBIF_SPEED_HIGH; + break; + default: + speed = USBIF_SPEED_NONE; + break; + } + if (speed == USBIF_SPEED_NONE) { + xen_be_printf(&usbif->xendev, 0, "device %s wrong speed\n", busid); + object_unparent(OBJECT(usbif->ports[port - 1].dev)); + usbif->ports[port - 1].dev = NULL; + return; + } + usb_device_reset(usbif->ports[port - 1].dev); + usbif->ports[port - 1].speed = speed; + usbif->ports[port - 1].attached = true; + QTAILQ_INIT(&usbif->ports[port - 1].submit_q); + usbback_hotplug_enq(usbif, port); + + TR_BUS(&usbif->xendev, "port %d attached\n", port); + return; + +err: + QDECREF(qdict); + snprintf(p->path, sizeof(p->path), "%d", 99); + xen_be_printf(&usbif->xendev, 0, "device %s could not be opened\n", busid); +} + +static void usbback_process_port(struct usbback_info *usbif, unsigned port) +{ + char node[8]; + char *busid; + + snprintf(node, sizeof(node), "port/%d", port); + busid = xenstore_read_be_str(&usbif->xendev, node); + if (busid == NULL) { + xen_be_printf(&usbif->xendev, 0, "xenstore_read %s failed\n", node); + return; + } + + /* Remove portid, if the port is not connected. */ + if (strlen(busid) == 0) { + usbback_portid_remove(usbif, port); + } else { + usbback_portid_add(usbif, port, busid); + } + + g_free(busid); +} + +static void usbback_disconnect(struct XenDevice *xendev) +{ + struct usbback_info *usbif; + unsigned int i; + + TR_BUS(xendev, "start\n"); + + usbif = container_of(xendev, struct usbback_info, xendev); + + xen_be_unbind_evtchn(xendev); + + if (usbif->urb_sring) { + xengnttab_unmap(xendev->gnttabdev, usbif->urb_sring, 1); + usbif->urb_sring = NULL; + } + if (usbif->conn_sring) { + xengnttab_unmap(xendev->gnttabdev, usbif->conn_sring, 1); + usbif->conn_sring = NULL; + } + + for (i = 0; i < usbif->num_ports; i++) { + if (usbif->ports[i].dev) { + usbback_portid_drain(usbif, i + 1); + } + } + + TR_BUS(xendev, "finished\n"); +} + +static int usbback_connect(struct XenDevice *xendev) +{ + struct usbback_info *usbif; + struct usbif_urb_sring *urb_sring; + struct usbif_conn_sring *conn_sring; + int urb_ring_ref; + int conn_ring_ref; + unsigned int i; + + TR_BUS(xendev, "start\n"); + + usbif = container_of(xendev, struct usbback_info, xendev); + + if (xenstore_read_fe_int(xendev, "urb-ring-ref", &urb_ring_ref)) { + xen_be_printf(xendev, 0, "error reading urb-ring-ref\n"); + return -1; + } + if (xenstore_read_fe_int(xendev, "conn-ring-ref", &conn_ring_ref)) { + xen_be_printf(xendev, 0, "error reading conn-ring-ref\n"); + return -1; + } + if (xenstore_read_fe_int(xendev, "event-channel", &xendev->remote_port)) { + xen_be_printf(xendev, 0, "error reading event-channel\n"); + return -1; + } + + usbif->urb_sring = xengnttab_map_grant_ref(xendev->gnttabdev, xendev->dom, + urb_ring_ref, + PROT_READ | PROT_WRITE); + usbif->conn_sring = xengnttab_map_grant_ref(xendev->gnttabdev, xendev->dom, + conn_ring_ref, + PROT_READ | PROT_WRITE); + if (!usbif->urb_sring || !usbif->conn_sring) { + xen_be_printf(xendev, 0, "error mapping rings\n"); + usbback_disconnect(xendev); + return -1; + } + + urb_sring = usbif->urb_sring; + conn_sring = usbif->conn_sring; + BACK_RING_INIT(&usbif->urb_ring, urb_sring, XC_PAGE_SIZE); + BACK_RING_INIT(&usbif->conn_ring, conn_sring, XC_PAGE_SIZE); + + xen_be_bind_evtchn(xendev); + + xen_be_printf(xendev, 1, "urb-ring-ref %d, conn-ring-ref %d, " + "remote port %d, local port %d\n", urb_ring_ref, + conn_ring_ref, xendev->remote_port, xendev->local_port); + + for (i = 1; i <= usbif->num_ports; i++) { + if (usbif->ports[i - 1].dev) { + usbback_hotplug_enq(usbif, i); + } + } + + return 0; +} + +static void usbback_backend_changed(struct XenDevice *xendev, const char *node) +{ + struct usbback_info *usbif; + unsigned int i; + + TR_BUS(xendev, "path %s\n", node); + + usbif = container_of(xendev, struct usbback_info, xendev); + for (i = 1; i <= usbif->num_ports; i++) { + usbback_process_port(usbif, i); + } +} + +static int usbback_init(struct XenDevice *xendev) +{ + struct usbback_info *usbif; + + TR_BUS(xendev, "start\n"); + + usbif = container_of(xendev, struct usbback_info, xendev); + + if (xenstore_read_be_int(xendev, "num-ports", &usbif->num_ports) || + usbif->num_ports < 1 || usbif->num_ports > USBBACK_MAXPORTS) { + xen_be_printf(xendev, 0, "num-ports not readable or out of bounds\n"); + return -1; + } + if (xenstore_read_be_int(xendev, "usb-ver", &usbif->usb_ver) || + (usbif->usb_ver != USB_VER_USB11 && usbif->usb_ver != USB_VER_USB20)) { + xen_be_printf(xendev, 0, "usb-ver not readable or out of bounds\n"); + return -1; + } + + usbback_backend_changed(xendev, "port"); + + TR_BUS(xendev, "finished\n"); + + return 0; +} + +static void xen_bus_attach(USBPort *port) +{ + struct usbback_info *usbif; + + usbif = port->opaque; + TR_BUS(&usbif->xendev, "\n"); + usbif->ports[port->index].attached = true; + usbback_hotplug_enq(usbif, port->index + 1); +} + +static void xen_bus_detach(USBPort *port) +{ + struct usbback_info *usbif; + + usbif = port->opaque; + TR_BUS(&usbif->xendev, "\n"); + usbback_portid_detach(usbif, port->index + 1); +} + +static void xen_bus_child_detach(USBPort *port, USBDevice *child) +{ + struct usbback_info *usbif; + + usbif = port->opaque; + TR_BUS(&usbif->xendev, "\n"); +} + +static void xen_bus_complete(USBPort *port, USBPacket *packet) +{ + struct usbback_req *usbback_req; + struct usbback_info *usbif; + + usbback_req = container_of(packet, struct usbback_req, packet); + if (usbback_req->cancelled) { + g_free(usbback_req); + return; + } + + usbif = usbback_req->usbif; + TR_REQ(&usbif->xendev, "\n"); + usbback_packet_complete(packet); +} + +static USBPortOps xen_usb_port_ops = { + .attach = xen_bus_attach, + .detach = xen_bus_detach, + .child_detach = xen_bus_child_detach, + .complete = xen_bus_complete, +}; + +static USBBusOps xen_usb_bus_ops = { +}; + +static void usbback_alloc(struct XenDevice *xendev) +{ + struct usbback_info *usbif; + USBPort *p; + unsigned int i, max_grants; + + usbif = container_of(xendev, struct usbback_info, xendev); + + usb_bus_new(&usbif->bus, sizeof(usbif->bus), &xen_usb_bus_ops, xen_sysdev); + for (i = 0; i < USBBACK_MAXPORTS; i++) { + p = &(usbif->ports[i].port); + usb_register_port(&usbif->bus, p, usbif, i, &xen_usb_port_ops, + USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL | + USB_SPEED_MASK_HIGH); + snprintf(p->path, sizeof(p->path), "%d", 99); + } + + QTAILQ_INIT(&usbif->req_free_q); + QSIMPLEQ_INIT(&usbif->hotplug_q); + usbif->bh = qemu_bh_new(usbback_bh, usbif); + + /* max_grants: for each request and for the rings (request and connect). */ + max_grants = USBIF_MAX_SEGMENTS_PER_REQUEST * USB_URB_RING_SIZE + 2; + if (xengnttab_set_max_grants(xendev->gnttabdev, max_grants) < 0) { + xen_be_printf(xendev, 0, "xengnttab_set_max_grants failed: %s\n", + strerror(errno)); + } +} + +static int usbback_free(struct XenDevice *xendev) +{ + struct usbback_info *usbif; + struct usbback_req *usbback_req; + struct usbback_hotplug *usb_hp; + unsigned int i; + + TR_BUS(xendev, "start\n"); + + usbback_disconnect(xendev); + usbif = container_of(xendev, struct usbback_info, xendev); + for (i = 1; i <= usbif->num_ports; i++) { + usbback_portid_remove(usbif, i); + } + + while (!QTAILQ_EMPTY(&usbif->req_free_q)) { + usbback_req = QTAILQ_FIRST(&usbif->req_free_q); + QTAILQ_REMOVE(&usbif->req_free_q, usbback_req, q); + g_free(usbback_req); + } + while (!QSIMPLEQ_EMPTY(&usbif->hotplug_q)) { + usb_hp = QSIMPLEQ_FIRST(&usbif->hotplug_q); + QSIMPLEQ_REMOVE_HEAD(&usbif->hotplug_q, q); + g_free(usb_hp); + } + + qemu_bh_delete(usbif->bh); + + for (i = 0; i < USBBACK_MAXPORTS; i++) { + usb_unregister_port(&usbif->bus, &(usbif->ports[i].port)); + } + + usb_bus_release(&usbif->bus); + object_unparent(OBJECT(&usbif->bus)); + + TR_BUS(xendev, "finished\n"); + + return 0; +} + +static void usbback_event(struct XenDevice *xendev) +{ + struct usbback_info *usbif; + + usbif = container_of(xendev, struct usbback_info, xendev); + qemu_bh_schedule(usbif->bh); +} + +struct XenDevOps xen_usb_ops = { + .size = sizeof(struct usbback_info), + .flags = DEVOPS_FLAG_NEED_GNTDEV, + .init = usbback_init, + .alloc = usbback_alloc, + .free = usbback_free, + .backend_changed = usbback_backend_changed, + .initialise = usbback_connect, + .disconnect = usbback_disconnect, + .event = usbback_event, +}; + +#else /* USBIF_SHORT_NOT_OK */ + +static int usbback_not_supported(void) +{ + return -EINVAL; +} + +struct XenDevOps xen_usb_ops = { + .backend_register = usbback_not_supported, +}; + +#endif diff --git a/hw/vfio/Makefile.objs b/hw/vfio/Makefile.objs index ceddbb8f9..c25e32b02 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 f27db36fb..b313e7c2c 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -20,7 +20,9 @@ #include "qemu/osdep.h" #include <sys/ioctl.h> -#include <sys/mman.h> +#ifdef CONFIG_KVM +#include <linux/kvm.h> +#endif #include <linux/vfio.h> #include "hw/vfio/vfio-common.h" @@ -29,6 +31,7 @@ #include "exec/memory.h" #include "hw/hw.h" #include "qemu/error-report.h" +#include "qemu/range.h" #include "sysemu/kvm.h" #include "trace.h" @@ -239,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) && @@ -257,14 +298,20 @@ static void vfio_iommu_map_notify(Notifier *n, void *data) VFIOGuestIOMMU *giommu = container_of(n, VFIOGuestIOMMU, n); VFIOContainer *container = giommu->container; IOMMUTLBEntry *iotlb = data; + hwaddr iova = iotlb->iova + giommu->iommu_offset; MemoryRegion *mr; hwaddr xlat; hwaddr len = iotlb->addr_mask + 1; void *vaddr; int ret; - trace_vfio_iommu_map_notify(iotlb->iova, - iotlb->iova + iotlb->addr_mask); + trace_vfio_iommu_map_notify(iova, iova + iotlb->addr_mask); + + if (iotlb->target_as != &address_space_memory) { + error_report("Wrong target AS \"%s\", only system memory is allowed", + iotlb->target_as->name ? iotlb->target_as->name : "none"); + return; + } /* * The IOMMU TLB entry we have just covers translation through @@ -291,21 +338,21 @@ static void vfio_iommu_map_notify(Notifier *n, void *data) if ((iotlb->perm & IOMMU_RW) != IOMMU_NONE) { vaddr = memory_region_get_ram_ptr(mr) + xlat; - ret = vfio_dma_map(container, iotlb->iova, + ret = vfio_dma_map(container, iova, iotlb->addr_mask + 1, vaddr, !(iotlb->perm & IOMMU_WO) || mr->readonly); if (ret) { error_report("vfio_dma_map(%p, 0x%"HWADDR_PRIx", " "0x%"HWADDR_PRIx", %p) = %d (%m)", - container, iotlb->iova, + container, iova, iotlb->addr_mask + 1, vaddr, ret); } } else { - ret = vfio_dma_unmap(container, iotlb->iova, iotlb->addr_mask + 1); + ret = vfio_dma_unmap(container, iova, iotlb->addr_mask + 1); if (ret) { error_report("vfio_dma_unmap(%p, 0x%"HWADDR_PRIx", " "0x%"HWADDR_PRIx") = %d (%m)", - container, iotlb->iova, + container, iova, iotlb->addr_mask + 1, ret); } } @@ -313,11 +360,6 @@ out: rcu_read_unlock(); } -static hwaddr vfio_container_granularity(VFIOContainer *container) -{ - return (hwaddr)1 << ctz64(container->iova_pgsizes); -} - static void vfio_listener_region_add(MemoryListener *listener, MemoryRegionSection *section) { @@ -326,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( @@ -351,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); @@ -366,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 @@ -377,14 +450,14 @@ static void vfio_listener_region_add(MemoryListener *listener, */ giommu = g_malloc0(sizeof(*giommu)); giommu->iommu = section->mr; + giommu->iommu_offset = section->offset_within_address_space - + section->offset_within_region; giommu->container = container; giommu->n.notify = vfio_iommu_map_notify; QLIST_INSERT_HEAD(&container->giommu_list, giommu, giommu_next); memory_region_register_iommu_notifier(giommu->iommu, &giommu->n); - memory_region_iommu_replay(giommu->iommu, &giommu->n, - vfio_container_granularity(container), - false); + memory_region_iommu_replay(giommu->iommu, &giommu->n, false); return; } @@ -430,6 +503,7 @@ static void vfio_listener_region_del(MemoryListener *listener, { VFIOContainer *container = container_of(listener, VFIOContainer, listener); hwaddr iova, end; + Int128 llend, llsize; int ret; if (vfio_listener_skipped_section(section)) { @@ -451,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; @@ -468,21 +543,37 @@ static void vfio_listener_region_del(MemoryListener *listener, } iova = TARGET_PAGE_ALIGN(section->offset_within_address_space); - end = (section->offset_within_address_space + int128_get64(section->size)) & - TARGET_PAGE_MASK; + llend = int128_make64(section->offset_within_address_space); + llend = int128_add(llend, section->size); + llend = int128_and(llend, int128_exts64(TARGET_PAGE_MASK)); - if (iova >= end) { + if (int128_ge(int128_make64(iova), llend)) { return; } + end = int128_get64(int128_sub(llend, int128_one())); + + llsize = int128_sub(llend, int128_make64(iova)); - trace_vfio_listener_region_del(iova, end - 1); + trace_vfio_listener_region_del(iova, end); - ret = vfio_dma_unmap(container, iova, end - iova); + ret = vfio_dma_unmap(container, iova, int128_get64(llsize)); memory_region_unref(section->mr); if (ret) { error_report("vfio_dma_unmap(%p, 0x%"HWADDR_PRIx", " "0x%"HWADDR_PRIx") = %d (%m)", - container, iova, end - iova, ret); + 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); + } } } @@ -494,6 +585,57 @@ 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 * +vfio_get_region_info_cap(struct vfio_region_info *info, uint16_t id) +{ + struct vfio_info_cap_header *hdr; + void *ptr = info; + + if (!(info->flags & VFIO_REGION_INFO_FLAG_CAPS)) { + return NULL; + } + + for (hdr = ptr + info->cap_offset; hdr != ptr; hdr = ptr + hdr->next) { + if (hdr->id == id) { + return hdr; + } + } + + return NULL; +} + +static void vfio_setup_region_sparse_mmaps(VFIORegion *region, + struct vfio_region_info *info) +{ + struct vfio_info_cap_header *hdr; + struct vfio_region_info_cap_sparse_mmap *sparse; + int i; + + hdr = vfio_get_region_info_cap(info, VFIO_REGION_INFO_CAP_SPARSE_MMAP); + if (!hdr) { + return; + } + + sparse = container_of(hdr, struct vfio_region_info_cap_sparse_mmap, header); + + trace_vfio_region_sparse_mmap_header(region->vbasedev->name, + region->nr, sparse->nr_areas); + + region->nr_mmaps = sparse->nr_areas; + region->mmaps = g_new0(VFIOMmap, region->nr_mmaps); + + for (i = 0; i < region->nr_mmaps; i++) { + region->mmaps[i].offset = sparse->areas[i].offset; + region->mmaps[i].size = sparse->areas[i].size; + trace_vfio_region_sparse_mmap_entry(i, region->mmaps[i].offset, + region->mmaps[i].offset + + region->mmaps[i].size); + } } int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region, @@ -522,11 +664,14 @@ int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region, region->flags & VFIO_REGION_INFO_FLAG_MMAP && !(region->size & ~qemu_real_host_page_mask)) { - region->nr_mmaps = 1; - region->mmaps = g_new0(VFIOMmap, region->nr_mmaps); + vfio_setup_region_sparse_mmaps(region, info); - region->mmaps[0].offset = 0; - region->mmaps[0].size = region->size; + if (!region->nr_mmaps) { + region->nr_mmaps = 1; + region->mmaps = g_new0(VFIOMmap, region->nr_mmaps); + region->mmaps[0].offset = 0; + region->mmaps[0].size = region->size; + } } } @@ -801,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; @@ -816,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) { @@ -836,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; @@ -848,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; @@ -932,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); } @@ -1086,16 +1256,60 @@ int vfio_get_region_info(VFIODevice *vbasedev, int index, *info = g_malloc0(argsz); (*info)->index = index; +retry: (*info)->argsz = argsz; if (ioctl(vbasedev->fd, VFIO_DEVICE_GET_REGION_INFO, *info)) { g_free(*info); + *info = NULL; return -errno; } + if ((*info)->argsz > argsz) { + argsz = (*info)->argsz; + *info = g_realloc(*info, argsz); + + goto retry; + } + return 0; } +int vfio_get_dev_region_info(VFIODevice *vbasedev, uint32_t type, + uint32_t subtype, struct vfio_region_info **info) +{ + int i; + + for (i = 0; i < vbasedev->num_regions; i++) { + struct vfio_info_cap_header *hdr; + struct vfio_region_info_cap_type *cap_type; + + if (vfio_get_region_info(vbasedev, i, info)) { + continue; + } + + hdr = vfio_get_region_info_cap(*info, VFIO_REGION_INFO_CAP_TYPE); + if (!hdr) { + g_free(*info); + continue; + } + + cap_type = container_of(hdr, struct vfio_region_info_cap_type, header); + + trace_vfio_get_dev_region(vbasedev->name, i, + cap_type->type, cap_type->subtype); + + if (cap_type->type == type && cap_type->subtype == subtype) { + return 0; + } + + g_free(*info); + } + + *info = NULL; + return -ENODEV; +} + /* * Interfaces for IBM EEH (Enhanced Error Handling) */ @@ -1147,7 +1361,7 @@ static int vfio_eeh_container_op(VFIOContainer *container, uint32_t op) return -errno; } - return 0; + return ret; } static VFIOContainer *vfio_eeh_as_container(AddressSpace *as) diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index 49ecf1172..bec694c8d 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -11,9 +11,12 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/range.h" +#include "qapi/error.h" +#include "hw/nvram/fw_cfg.h" #include "pci.h" #include "trace.h" -#include "qemu/range.h" /* Use uin32_t for vendor & device so PCI_ANY_ID expands and cannot match hw */ static bool vfio_pci_is(VFIOPCIDevice *vdev, uint32_t vendor, uint32_t device) @@ -315,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; } @@ -363,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; } @@ -657,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; } @@ -773,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); @@ -962,6 +965,643 @@ static void vfio_probe_rtl8168_bar2_quirk(VFIOPCIDevice *vdev, int nr) } /* + * Intel IGD support + * + * Obviously IGD is not a discrete device, this is evidenced not only by it + * being integrated into the CPU, but by the various chipset and BIOS + * dependencies that it brings along with it. Intel is trying to move away + * from this and Broadwell and newer devices can run in what Intel calls + * "Universal Pass-Through" mode, or UPT. Theoretically in UPT mode, nothing + * more is required beyond assigning the IGD device to a VM. There are + * however support limitations to this mode. It only supports IGD as a + * secondary graphics device in the VM and it doesn't officially support any + * physical outputs. + * + * The code here attempts to enable what we'll call legacy mode assignment, + * IGD retains most of the capabilities we expect for it to have on bare + * metal. To enable this mode, the IGD device must be assigned to the VM + * at PCI address 00:02.0, it must have a ROM, it very likely needs VGA + * support, we must have VM BIOS support for reserving and populating some + * of the required tables, and we need to tweak the chipset with revisions + * and IDs and an LPC/ISA bridge device. The intention is to make all of + * this happen automatically by installing the device at the correct VM PCI + * bus address. If any of the conditions are not met, we cross our fingers + * and hope the user knows better. + * + * NB - It is possible to enable physical outputs in UPT mode by supplying + * an OpRegion table. We don't do this by default because the guest driver + * behaves differently if an OpRegion is provided and no monitor is attached + * vs no OpRegion and a monitor being attached or not. Effectively, if a + * headless setup is desired, the OpRegion gets in the way of that. + */ + +/* + * This presumes the device is already known to be an Intel VGA device, so we + * take liberties in which device ID bits match which generation. This should + * not be taken as an indication that all the devices are supported, or even + * supportable, some of them don't even support VT-d. + * See linux:include/drm/i915_pciids.h for IDs. + */ +static int igd_gen(VFIOPCIDevice *vdev) +{ + if ((vdev->device_id & 0xfff) == 0xa84) { + return 8; /* Broxton */ + } + + switch (vdev->device_id & 0xff00) { + /* Old, untested, unavailable, unknown */ + case 0x0000: + case 0x2500: + case 0x2700: + case 0x2900: + case 0x2a00: + case 0x2e00: + case 0x3500: + case 0xa000: + return -1; + /* SandyBridge, IvyBridge, ValleyView, Haswell */ + case 0x0100: + case 0x0400: + case 0x0a00: + case 0x0c00: + case 0x0d00: + case 0x0f00: + return 6; + /* BroadWell, CherryView, SkyLake, KabyLake */ + case 0x1600: + case 0x1900: + case 0x2200: + case 0x5900: + return 8; + } + + return 8; /* Assume newer is compatible */ +} + +typedef struct VFIOIGDQuirk { + struct VFIOPCIDevice *vdev; + uint32_t index; +} VFIOIGDQuirk; + +#define IGD_GMCH 0x50 /* Graphics Control Register */ +#define IGD_BDSM 0x5c /* Base Data of Stolen Memory */ +#define IGD_ASLS 0xfc /* ASL Storage Register */ + +/* + * The OpRegion includes the Video BIOS Table, which seems important for + * telling the driver what sort of outputs it has. Without this, the device + * may work in the guest, but we may not get output. This also requires BIOS + * support to reserve and populate a section of guest memory sufficient for + * the table and to write the base address of that memory to the ASLS register + * of the IGD device. + */ +int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev, + struct vfio_region_info *info) +{ + int ret; + + vdev->igd_opregion = g_malloc0(info->size); + ret = pread(vdev->vbasedev.fd, vdev->igd_opregion, + info->size, info->offset); + if (ret != info->size) { + error_report("vfio: Error reading IGD OpRegion"); + g_free(vdev->igd_opregion); + vdev->igd_opregion = NULL; + return -EINVAL; + } + + /* + * Provide fw_cfg with a copy of the OpRegion which the VM firmware is to + * allocate 32bit reserved memory for, copy these contents into, and write + * the reserved memory base address to the device ASLS register at 0xFC. + * Alignment of this reserved region seems flexible, but using a 4k page + * alignment seems to work well. This interface assumes a single IGD + * device, which may be at VM address 00:02.0 in legacy mode or another + * address in UPT mode. + * + * NB, there may be future use cases discovered where the VM should have + * direct interaction with the host OpRegion, in which case the write to + * the ASLS register would trigger MemoryRegion setup to enable that. + */ + fw_cfg_add_file(fw_cfg_find(), "etc/igd-opregion", + vdev->igd_opregion, info->size); + + trace_vfio_pci_igd_opregion_enabled(vdev->vbasedev.name); + + pci_set_long(vdev->pdev.config + IGD_ASLS, 0); + pci_set_long(vdev->pdev.wmask + IGD_ASLS, ~0); + pci_set_long(vdev->emulated_config_bits + IGD_ASLS, ~0); + + return 0; +} + +/* + * The rather short list of registers that we copy from the host devices. + * The LPC/ISA bridge values are definitely needed to support the vBIOS, the + * host bridge values may or may not be needed depending on the guest OS. + * Since we're only munging revision and subsystem values on the host bridge, + * we don't require our own device. The LPC/ISA bridge needs to be our very + * own though. + */ +typedef struct { + uint8_t offset; + uint8_t len; +} IGDHostInfo; + +static const IGDHostInfo igd_host_bridge_infos[] = { + {PCI_REVISION_ID, 2}, + {PCI_SUBSYSTEM_VENDOR_ID, 2}, + {PCI_SUBSYSTEM_ID, 2}, +}; + +static const IGDHostInfo igd_lpc_bridge_infos[] = { + {PCI_VENDOR_ID, 2}, + {PCI_DEVICE_ID, 2}, + {PCI_REVISION_ID, 2}, + {PCI_SUBSYSTEM_VENDOR_ID, 2}, + {PCI_SUBSYSTEM_ID, 2}, +}; + +static int vfio_pci_igd_copy(VFIOPCIDevice *vdev, PCIDevice *pdev, + struct vfio_region_info *info, + const IGDHostInfo *list, int len) +{ + int i, ret; + + for (i = 0; i < len; i++) { + ret = pread(vdev->vbasedev.fd, pdev->config + list[i].offset, + list[i].len, info->offset + list[i].offset); + if (ret != list[i].len) { + error_report("IGD copy failed: %m"); + return -errno; + } + } + + return 0; +} + +/* + * Stuff a few values into the host bridge. + */ +static int vfio_pci_igd_host_init(VFIOPCIDevice *vdev, + struct vfio_region_info *info) +{ + PCIBus *bus; + PCIDevice *host_bridge; + int ret; + + bus = pci_device_root_bus(&vdev->pdev); + host_bridge = pci_find_device(bus, 0, PCI_DEVFN(0, 0)); + + if (!host_bridge) { + error_report("Can't find host bridge"); + return -ENODEV; + } + + ret = vfio_pci_igd_copy(vdev, host_bridge, info, igd_host_bridge_infos, + ARRAY_SIZE(igd_host_bridge_infos)); + if (!ret) { + trace_vfio_pci_igd_host_bridge_enabled(vdev->vbasedev.name); + } + + return ret; +} + +/* + * IGD LPC/ISA bridge support code. The vBIOS needs this, but we can't write + * arbitrary values into just any bridge, so we must create our own. We try + * to handle if the user has created it for us, which they might want to do + * to enable multifuction so we don't occupy the whole PCI slot. + */ +static void vfio_pci_igd_lpc_bridge_realize(PCIDevice *pdev, Error **errp) +{ + if (pdev->devfn != PCI_DEVFN(0x1f, 0)) { + error_setg(errp, "VFIO dummy ISA/LPC bridge must have address 1f.0"); + } +} + +static void vfio_pci_igd_lpc_bridge_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + dc->desc = "VFIO dummy ISA/LPC bridge for IGD assignment"; + dc->hotpluggable = false; + k->realize = vfio_pci_igd_lpc_bridge_realize; + k->class_id = PCI_CLASS_BRIDGE_ISA; +} + +static TypeInfo vfio_pci_igd_lpc_bridge_info = { + .name = "vfio-pci-igd-lpc-bridge", + .parent = TYPE_PCI_DEVICE, + .class_init = vfio_pci_igd_lpc_bridge_class_init, +}; + +static void vfio_pci_igd_register_types(void) +{ + type_register_static(&vfio_pci_igd_lpc_bridge_info); +} + +type_init(vfio_pci_igd_register_types) + +static int vfio_pci_igd_lpc_init(VFIOPCIDevice *vdev, + struct vfio_region_info *info) +{ + PCIDevice *lpc_bridge; + int ret; + + lpc_bridge = pci_find_device(pci_device_root_bus(&vdev->pdev), + 0, PCI_DEVFN(0x1f, 0)); + if (!lpc_bridge) { + lpc_bridge = pci_create_simple(pci_device_root_bus(&vdev->pdev), + PCI_DEVFN(0x1f, 0), "vfio-pci-igd-lpc-bridge"); + } + + ret = vfio_pci_igd_copy(vdev, lpc_bridge, info, igd_lpc_bridge_infos, + ARRAY_SIZE(igd_lpc_bridge_infos)); + if (!ret) { + trace_vfio_pci_igd_lpc_bridge_enabled(vdev->vbasedev.name); + } + + return ret; +} + +/* + * IGD Gen8 and newer support up to 8MB for the GTT and use a 64bit PTE + * entry, older IGDs use 2MB and 32bit. Each PTE maps a 4k page. Therefore + * we either have 2M/4k * 4 = 2k or 8M/4k * 8 = 16k as the maximum iobar index + * for programming the GTT. + * + * See linux:include/drm/i915_drm.h for shift and mask values. + */ +static int vfio_igd_gtt_max(VFIOPCIDevice *vdev) +{ + uint32_t gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, sizeof(gmch)); + int ggms, gen = igd_gen(vdev); + + gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, sizeof(gmch)); + ggms = (gmch >> (gen < 8 ? 8 : 6)) & 0x3; + if (gen > 6) { + ggms = 1 << ggms; + } + + ggms *= 1024 * 1024; + + return (ggms / (4 * 1024)) * (gen < 8 ? 4 : 8); +} + +/* + * The IGD ROM will make use of stolen memory (GGMS) for support of VESA modes. + * Somehow the host stolen memory range is used for this, but how the ROM gets + * it is a mystery, perhaps it's hardcoded into the ROM. Thankfully though, it + * reprograms the GTT through the IOBAR where we can trap it and transpose the + * programming to the VM allocated buffer. That buffer gets reserved by the VM + * firmware via the fw_cfg entry added below. Here we're just monitoring the + * IOBAR address and data registers to detect a write sequence targeting the + * GTTADR. This code is developed by observed behavior and doesn't have a + * direct spec reference, unfortunately. + */ +static uint64_t vfio_igd_quirk_data_read(void *opaque, + hwaddr addr, unsigned size) +{ + VFIOIGDQuirk *igd = opaque; + VFIOPCIDevice *vdev = igd->vdev; + + igd->index = ~0; + + return vfio_region_read(&vdev->bars[4].region, addr + 4, size); +} + +static void vfio_igd_quirk_data_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + VFIOIGDQuirk *igd = opaque; + VFIOPCIDevice *vdev = igd->vdev; + uint64_t val = data; + int gen = igd_gen(vdev); + + /* + * Programming the GGMS starts at index 0x1 and uses every 4th index (ie. + * 0x1, 0x5, 0x9, 0xd,...). For pre-Gen8 each 4-byte write is a whole PTE + * entry, with 0th bit enable set. For Gen8 and up, PTEs are 64bit, so + * entries 0x5 & 0xd are the high dword, in our case zero. Each PTE points + * to a 4k page, which we translate to a page from the VM allocated region, + * pointed to by the BDSM register. If this is not set, we fail. + * + * We trap writes to the full configured GTT size, but we typically only + * see the vBIOS writing up to (nearly) the 1MB barrier. In fact it often + * seems to miss the last entry for an even 1MB GTT. Doing a gratuitous + * write of that last entry does work, but is hopefully unnecessary since + * we clear the previous GTT on initialization. + */ + if ((igd->index % 4 == 1) && igd->index < vfio_igd_gtt_max(vdev)) { + if (gen < 8 || (igd->index % 8 == 1)) { + uint32_t base; + + base = pci_get_long(vdev->pdev.config + IGD_BDSM); + if (!base) { + hw_error("vfio-igd: Guest attempted to program IGD GTT before " + "BIOS reserved stolen memory. Unsupported BIOS?"); + } + + val = base | (data & ((1 << 20) - 1)); + } else { + val = 0; /* upper 32bits of pte, we only enable below 4G PTEs */ + } + + trace_vfio_pci_igd_bar4_write(vdev->vbasedev.name, + igd->index, data, val); + } + + vfio_region_write(&vdev->bars[4].region, addr + 4, val, size); + + igd->index = ~0; +} + +static const MemoryRegionOps vfio_igd_data_quirk = { + .read = vfio_igd_quirk_data_read, + .write = vfio_igd_quirk_data_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static uint64_t vfio_igd_quirk_index_read(void *opaque, + hwaddr addr, unsigned size) +{ + VFIOIGDQuirk *igd = opaque; + VFIOPCIDevice *vdev = igd->vdev; + + igd->index = ~0; + + return vfio_region_read(&vdev->bars[4].region, addr, size); +} + +static void vfio_igd_quirk_index_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + VFIOIGDQuirk *igd = opaque; + VFIOPCIDevice *vdev = igd->vdev; + + igd->index = data; + + vfio_region_write(&vdev->bars[4].region, addr, data, size); +} + +static const MemoryRegionOps vfio_igd_index_quirk = { + .read = vfio_igd_quirk_index_read, + .write = vfio_igd_quirk_index_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) +{ + struct vfio_region_info *rom = NULL, *opregion = NULL, + *host = NULL, *lpc = NULL; + VFIOQuirk *quirk; + VFIOIGDQuirk *igd; + PCIDevice *lpc_bridge; + int i, ret, ggms_mb, gms_mb = 0, gen; + uint64_t *bdsm_size; + uint32_t gmch; + uint16_t cmd_orig, cmd; + + /* + * This must be an Intel VGA device at address 00:02.0 for us to even + * consider enabling legacy mode. The vBIOS has dependencies on the + * PCI bus address. + */ + if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) || + !vfio_is_vga(vdev) || nr != 4 || + &vdev->pdev != pci_find_device(pci_device_root_bus(&vdev->pdev), + 0, PCI_DEVFN(0x2, 0))) { + return; + } + + /* + * We need to create an LPC/ISA bridge at PCI bus address 00:1f.0 that we + * can stuff host values into, so if there's already one there and it's not + * one we can hack on, legacy mode is no-go. Sorry Q35. + */ + lpc_bridge = pci_find_device(pci_device_root_bus(&vdev->pdev), + 0, PCI_DEVFN(0x1f, 0)); + if (lpc_bridge && !object_dynamic_cast(OBJECT(lpc_bridge), + "vfio-pci-igd-lpc-bridge")) { + error_report("IGD device %s cannot support legacy mode due to existing " + "devices at address 1f.0", vdev->vbasedev.name); + return; + } + + /* + * IGD is not a standard, they like to change their specs often. We + * only attempt to support back to SandBridge and we hope that newer + * devices maintain compatibility with generation 8. + */ + gen = igd_gen(vdev); + if (gen != 6 && gen != 8) { + error_report("IGD device %s is unsupported in legacy mode, " + "try SandyBridge or newer", vdev->vbasedev.name); + return; + } + + /* + * Most of what we're doing here is to enable the ROM to run, so if + * there's no ROM, there's no point in setting up this quirk. + * NB. We only seem to get BIOS ROMs, so a UEFI VM would need CSM support. + */ + ret = vfio_get_region_info(&vdev->vbasedev, + VFIO_PCI_ROM_REGION_INDEX, &rom); + if ((ret || !rom->size) && !vdev->pdev.romfile) { + error_report("IGD device %s has no ROM, legacy mode disabled", + vdev->vbasedev.name); + goto out; + } + + /* + * Ignore the hotplug corner case, mark the ROM failed, we can't + * create the devices we need for legacy mode in the hotplug scenario. + */ + if (vdev->pdev.qdev.hotplugged) { + error_report("IGD device %s hotplugged, ROM disabled, " + "legacy mode disabled", vdev->vbasedev.name); + vdev->rom_read_failed = true; + goto out; + } + + /* + * Check whether we have all the vfio device specific regions to + * support legacy mode (added in Linux v4.6). If not, bail. + */ + ret = vfio_get_dev_region_info(&vdev->vbasedev, + VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL, + VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION, &opregion); + if (ret) { + error_report("IGD device %s does not support OpRegion access," + "legacy mode disabled", vdev->vbasedev.name); + goto out; + } + + ret = vfio_get_dev_region_info(&vdev->vbasedev, + VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL, + VFIO_REGION_SUBTYPE_INTEL_IGD_HOST_CFG, &host); + if (ret) { + error_report("IGD device %s does not support host bridge access," + "legacy mode disabled", vdev->vbasedev.name); + goto out; + } + + ret = vfio_get_dev_region_info(&vdev->vbasedev, + VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL, + VFIO_REGION_SUBTYPE_INTEL_IGD_LPC_CFG, &lpc); + if (ret) { + error_report("IGD device %s does not support LPC bridge access," + "legacy mode disabled", vdev->vbasedev.name); + goto out; + } + + gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, 4); + + /* + * If IGD VGA Disable is clear (expected) and VGA is not already enabled, + * try to enable it. Probably shouldn't be using legacy mode without VGA, + * but also no point in us enabling VGA if disabled in hardware. + */ + if (!(gmch & 0x2) && !vdev->vga && vfio_populate_vga(vdev)) { + error_report("IGD device %s failed to enable VGA access, " + "legacy mode disabled", vdev->vbasedev.name); + goto out; + } + + /* Create our LPC/ISA bridge */ + ret = vfio_pci_igd_lpc_init(vdev, lpc); + if (ret) { + error_report("IGD device %s failed to create LPC bridge, " + "legacy mode disabled", vdev->vbasedev.name); + goto out; + } + + /* Stuff some host values into the VM PCI host bridge */ + ret = vfio_pci_igd_host_init(vdev, host); + if (ret) { + error_report("IGD device %s failed to modify host bridge, " + "legacy mode disabled", vdev->vbasedev.name); + goto out; + } + + /* Setup OpRegion access */ + ret = vfio_pci_igd_opregion_init(vdev, opregion); + if (ret) { + error_report("IGD device %s failed to setup OpRegion, " + "legacy mode disabled", vdev->vbasedev.name); + goto out; + } + + /* Setup our quirk to munge GTT addresses to the VM allocated buffer */ + quirk = g_malloc0(sizeof(*quirk)); + quirk->mem = g_new0(MemoryRegion, 2); + quirk->nr_mem = 2; + igd = quirk->data = g_malloc0(sizeof(*igd)); + igd->vdev = vdev; + igd->index = ~0; + + memory_region_init_io(&quirk->mem[0], OBJECT(vdev), &vfio_igd_index_quirk, + igd, "vfio-igd-index-quirk", 4); + memory_region_add_subregion_overlap(vdev->bars[nr].region.mem, + 0, &quirk->mem[0], 1); + + memory_region_init_io(&quirk->mem[1], OBJECT(vdev), &vfio_igd_data_quirk, + igd, "vfio-igd-data-quirk", 4); + memory_region_add_subregion_overlap(vdev->bars[nr].region.mem, + 4, &quirk->mem[1], 1); + + QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); + + /* Determine the size of stolen memory needed for GTT */ + ggms_mb = (gmch >> (gen < 8 ? 8 : 6)) & 0x3; + if (gen > 6) { + ggms_mb = 1 << ggms_mb; + } + + /* + * Assume we have no GMS memory, but allow it to be overrided by device + * option (experimental). The spec doesn't actually allow zero GMS when + * when IVD (IGD VGA Disable) is clear, but the claim is that it's unused, + * so let's not waste VM memory for it. + */ + gmch &= ~((gen < 8 ? 0x1f : 0xff) << (gen < 8 ? 3 : 8)); + + if (vdev->igd_gms) { + if (vdev->igd_gms <= 0x10) { + gms_mb = vdev->igd_gms * 32; + gmch |= vdev->igd_gms << (gen < 8 ? 3 : 8); + } else { + error_report("Unsupported IGD GMS value 0x%x", vdev->igd_gms); + vdev->igd_gms = 0; + } + } + + /* + * Request reserved memory for stolen memory via fw_cfg. VM firmware + * must allocate a 1MB aligned reserved memory region below 4GB with + * the requested size (in bytes) for use by the Intel PCI class VGA + * device at VM address 00:02.0. The base address of this reserved + * memory region must be written to the device BDSM regsiter at PCI + * config offset 0x5C. + */ + bdsm_size = g_malloc(sizeof(*bdsm_size)); + *bdsm_size = cpu_to_le64((ggms_mb + gms_mb) * 1024 * 1024); + fw_cfg_add_file(fw_cfg_find(), "etc/igd-bdsm-size", + bdsm_size, sizeof(*bdsm_size)); + + /* GMCH is read-only, emulated */ + pci_set_long(vdev->pdev.config + IGD_GMCH, gmch); + pci_set_long(vdev->pdev.wmask + IGD_GMCH, 0); + pci_set_long(vdev->emulated_config_bits + IGD_GMCH, ~0); + + /* BDSM is read-write, emulated. The BIOS needs to be able to write it */ + pci_set_long(vdev->pdev.config + IGD_BDSM, 0); + pci_set_long(vdev->pdev.wmask + IGD_BDSM, ~0); + pci_set_long(vdev->emulated_config_bits + IGD_BDSM, ~0); + + /* + * This IOBAR gives us access to GTTADR, which allows us to write to + * the GTT itself. So let's go ahead and write zero to all the GTT + * entries to avoid spurious DMA faults. Be sure I/O access is enabled + * before talking to the device. + */ + if (pread(vdev->vbasedev.fd, &cmd_orig, sizeof(cmd_orig), + vdev->config_offset + PCI_COMMAND) != sizeof(cmd_orig)) { + error_report("IGD device %s - failed to read PCI command register", + vdev->vbasedev.name); + } + + cmd = cmd_orig | PCI_COMMAND_IO; + + if (pwrite(vdev->vbasedev.fd, &cmd, sizeof(cmd), + vdev->config_offset + PCI_COMMAND) != sizeof(cmd)) { + error_report("IGD device %s - failed to write PCI command register", + vdev->vbasedev.name); + } + + for (i = 1; i < vfio_igd_gtt_max(vdev); i += 4) { + vfio_region_write(&vdev->bars[4].region, 0, i, 4); + vfio_region_write(&vdev->bars[4].region, 4, 0, 4); + } + + if (pwrite(vdev->vbasedev.fd, &cmd_orig, sizeof(cmd_orig), + vdev->config_offset + PCI_COMMAND) != sizeof(cmd_orig)) { + error_report("IGD device %s - failed to restore PCI command register", + vdev->vbasedev.name); + } + + trace_vfio_pci_igd_bdsm_enabled(vdev->vbasedev.name, ggms_mb + gms_mb); + +out: + g_free(rom); + g_free(opregion); + g_free(host); + g_free(lpc); +} + +/* * Common quirk probe entry points. */ void vfio_vga_quirk_setup(VFIOPCIDevice *vdev) @@ -1010,6 +1650,7 @@ void vfio_bar_quirk_setup(VFIOPCIDevice *vdev, int nr) vfio_probe_nvidia_bar5_quirk(vdev, nr); vfio_probe_nvidia_bar0_quirk(vdev, nr); vfio_probe_rtl8168_bar2_quirk(vdev, nr); + vfio_probe_igd_bar4_quirk(vdev, nr); } void vfio_bar_quirk_exit(VFIOPCIDevice *vdev, int nr) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index d091d8cf0..7bfa17ce3 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -21,7 +21,6 @@ #include "qemu/osdep.h" #include <linux/vfio.h> #include <sys/ioctl.h> -#include <sys/mman.h> #include "hw/pci/msi.h" #include "hw/pci/msix.h" @@ -32,6 +31,7 @@ #include "sysemu/sysemu.h" #include "pci.h" #include "trace.h" +#include "qapi/error.h" #define MSIX_CAP_LENGTH 12 @@ -417,11 +417,11 @@ static int vfio_enable_vectors(VFIOPCIDevice *vdev, bool msix) } static void vfio_add_kvm_msi_virq(VFIOPCIDevice *vdev, VFIOMSIVector *vector, - MSIMessage *msg, bool msix) + int vector_n, bool msix) { int virq; - if ((msix && vdev->no_kvm_msix) || (!msix && vdev->no_kvm_msi) || !msg) { + if ((msix && vdev->no_kvm_msix) || (!msix && vdev->no_kvm_msi)) { return; } @@ -429,7 +429,7 @@ static void vfio_add_kvm_msi_virq(VFIOPCIDevice *vdev, VFIOMSIVector *vector, return; } - virq = kvm_irqchip_add_msi_route(kvm_state, *msg, &vdev->pdev); + virq = kvm_irqchip_add_msi_route(kvm_state, vector_n, &vdev->pdev); if (virq < 0) { event_notifier_cleanup(&vector->kvm_interrupt); return; @@ -458,6 +458,7 @@ static void vfio_update_kvm_msi_virq(VFIOMSIVector *vector, MSIMessage msg, PCIDevice *pdev) { kvm_irqchip_update_msi_route(kvm_state, vector->virq, msg, pdev); + kvm_irqchip_commit_routes(kvm_state); } static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, @@ -495,7 +496,7 @@ static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, vfio_update_kvm_msi_virq(vector, *msg, pdev); } } else { - vfio_add_kvm_msi_virq(vdev, vector, msg, true); + vfio_add_kvm_msi_virq(vdev, vector, nr, true); } /* @@ -639,7 +640,6 @@ retry: for (i = 0; i < vdev->nr_vectors; i++) { VFIOMSIVector *vector = &vdev->msi_vectors[i]; - MSIMessage msg = msi_get_message(&vdev->pdev, i); vector->vdev = vdev; vector->virq = -1; @@ -656,7 +656,7 @@ retry: * Attempt to enable route through KVM irqchip, * default to userspace handling if unavailable. */ - vfio_add_kvm_msi_virq(vdev, vector, &msg, false); + vfio_add_kvm_msi_virq(vdev, vector, i, false); } /* Set interrupt type prior to possible interrupts */ @@ -1171,6 +1171,7 @@ static int vfio_msi_setup(VFIOPCIDevice *vdev, int pos) uint16_t ctrl; bool msi_64bit, msi_maskbit; int ret, entries; + Error *err = NULL; if (pread(vdev->vbasedev.fd, &ctrl, sizeof(ctrl), vdev->config_offset + pos + PCI_CAP_FLAGS) != sizeof(ctrl)) { @@ -1184,12 +1185,13 @@ static int vfio_msi_setup(VFIOPCIDevice *vdev, int pos) trace_vfio_msi_setup(vdev->vbasedev.name, pos); - ret = msi_init(&vdev->pdev, pos, entries, msi_64bit, msi_maskbit); + ret = msi_init(&vdev->pdev, pos, entries, msi_64bit, msi_maskbit, &err); if (ret < 0) { if (ret == -ENOTSUP) { return 0; } - error_report("vfio: msi_init failed"); + error_prepend(&err, "vfio: msi_init failed: "); + error_report_err(err); return ret; } vdev->msi_cap_size = 0xa + (msi_maskbit ? 0xa : 0) + (msi_64bit ? 0x4 : 0); @@ -1440,8 +1442,6 @@ static void vfio_bar_setup(VFIOPCIDevice *vdev, int nr) vdev->vbasedev.name, nr); } - vfio_bar_quirk_setup(vdev, nr); - pci_register_bar(&vdev->pdev, nr, type, bar->region.mem); } @@ -1452,29 +1452,6 @@ static void vfio_bars_setup(VFIOPCIDevice *vdev) for (i = 0; i < PCI_ROM_SLOT; i++) { vfio_bar_setup(vdev, i); } - - if (vdev->vga) { - memory_region_init_io(&vdev->vga->region[QEMU_PCI_VGA_MEM].mem, - OBJECT(vdev), &vfio_vga_ops, - &vdev->vga->region[QEMU_PCI_VGA_MEM], - "vfio-vga-mmio@0xa0000", - QEMU_PCI_VGA_MEM_SIZE); - memory_region_init_io(&vdev->vga->region[QEMU_PCI_VGA_IO_LO].mem, - OBJECT(vdev), &vfio_vga_ops, - &vdev->vga->region[QEMU_PCI_VGA_IO_LO], - "vfio-vga-io@0x3b0", - QEMU_PCI_VGA_IO_LO_SIZE); - memory_region_init_io(&vdev->vga->region[QEMU_PCI_VGA_IO_HI].mem, - OBJECT(vdev), &vfio_vga_ops, - &vdev->vga->region[QEMU_PCI_VGA_IO_HI], - "vfio-vga-io@0x3c0", - QEMU_PCI_VGA_IO_HI_SIZE); - - pci_register_vga(&vdev->pdev, &vdev->vga->region[QEMU_PCI_VGA_MEM].mem, - &vdev->vga->region[QEMU_PCI_VGA_IO_LO].mem, - &vdev->vga->region[QEMU_PCI_VGA_IO_HI].mem); - vfio_vga_quirk_setup(vdev); - } } static void vfio_bars_exit(VFIOPCIDevice *vdev) @@ -1528,6 +1505,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); @@ -1775,16 +1767,101 @@ 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 */ + case PCI_EXT_CAP_ID_ARI: /* XXX Needs next function virtualization */ + 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) @@ -2061,42 +2138,61 @@ int vfio_populate_vga(VFIOPCIDevice *vdev) struct vfio_region_info *reg_info; int ret; - if (vbasedev->num_regions > VFIO_PCI_VGA_REGION_INDEX) { - ret = vfio_get_region_info(vbasedev, - VFIO_PCI_VGA_REGION_INDEX, ®_info); - if (ret) { - return ret; - } + ret = vfio_get_region_info(vbasedev, VFIO_PCI_VGA_REGION_INDEX, ®_info); + if (ret) { + return ret; + } - if (!(reg_info->flags & VFIO_REGION_INFO_FLAG_READ) || - !(reg_info->flags & VFIO_REGION_INFO_FLAG_WRITE) || - reg_info->size < 0xbffff + 1) { - error_report("vfio: Unexpected VGA info, flags 0x%lx, size 0x%lx", - (unsigned long)reg_info->flags, - (unsigned long)reg_info->size); - g_free(reg_info); - return -EINVAL; - } + if (!(reg_info->flags & VFIO_REGION_INFO_FLAG_READ) || + !(reg_info->flags & VFIO_REGION_INFO_FLAG_WRITE) || + reg_info->size < 0xbffff + 1) { + error_report("vfio: Unexpected VGA info, flags 0x%lx, size 0x%lx", + (unsigned long)reg_info->flags, + (unsigned long)reg_info->size); + g_free(reg_info); + return -EINVAL; + } - vdev->vga = g_new0(VFIOVGA, 1); + vdev->vga = g_new0(VFIOVGA, 1); - vdev->vga->fd_offset = reg_info->offset; - vdev->vga->fd = vdev->vbasedev.fd; + vdev->vga->fd_offset = reg_info->offset; + vdev->vga->fd = vdev->vbasedev.fd; - g_free(reg_info); + g_free(reg_info); - vdev->vga->region[QEMU_PCI_VGA_MEM].offset = QEMU_PCI_VGA_MEM_BASE; - vdev->vga->region[QEMU_PCI_VGA_MEM].nr = QEMU_PCI_VGA_MEM; - QLIST_INIT(&vdev->vga->region[QEMU_PCI_VGA_MEM].quirks); + vdev->vga->region[QEMU_PCI_VGA_MEM].offset = QEMU_PCI_VGA_MEM_BASE; + vdev->vga->region[QEMU_PCI_VGA_MEM].nr = QEMU_PCI_VGA_MEM; + QLIST_INIT(&vdev->vga->region[QEMU_PCI_VGA_MEM].quirks); - vdev->vga->region[QEMU_PCI_VGA_IO_LO].offset = QEMU_PCI_VGA_IO_LO_BASE; - vdev->vga->region[QEMU_PCI_VGA_IO_LO].nr = QEMU_PCI_VGA_IO_LO; - QLIST_INIT(&vdev->vga->region[QEMU_PCI_VGA_IO_LO].quirks); + memory_region_init_io(&vdev->vga->region[QEMU_PCI_VGA_MEM].mem, + OBJECT(vdev), &vfio_vga_ops, + &vdev->vga->region[QEMU_PCI_VGA_MEM], + "vfio-vga-mmio@0xa0000", + QEMU_PCI_VGA_MEM_SIZE); - vdev->vga->region[QEMU_PCI_VGA_IO_HI].offset = QEMU_PCI_VGA_IO_HI_BASE; - vdev->vga->region[QEMU_PCI_VGA_IO_HI].nr = QEMU_PCI_VGA_IO_HI; - QLIST_INIT(&vdev->vga->region[QEMU_PCI_VGA_IO_HI].quirks); - } + vdev->vga->region[QEMU_PCI_VGA_IO_LO].offset = QEMU_PCI_VGA_IO_LO_BASE; + vdev->vga->region[QEMU_PCI_VGA_IO_LO].nr = QEMU_PCI_VGA_IO_LO; + QLIST_INIT(&vdev->vga->region[QEMU_PCI_VGA_IO_LO].quirks); + + memory_region_init_io(&vdev->vga->region[QEMU_PCI_VGA_IO_LO].mem, + OBJECT(vdev), &vfio_vga_ops, + &vdev->vga->region[QEMU_PCI_VGA_IO_LO], + "vfio-vga-io@0x3b0", + QEMU_PCI_VGA_IO_LO_SIZE); + + vdev->vga->region[QEMU_PCI_VGA_IO_HI].offset = QEMU_PCI_VGA_IO_HI_BASE; + vdev->vga->region[QEMU_PCI_VGA_IO_HI].nr = QEMU_PCI_VGA_IO_HI; + QLIST_INIT(&vdev->vga->region[QEMU_PCI_VGA_IO_HI].quirks); + + memory_region_init_io(&vdev->vga->region[QEMU_PCI_VGA_IO_HI].mem, + OBJECT(vdev), &vfio_vga_ops, + &vdev->vga->region[QEMU_PCI_VGA_IO_HI], + "vfio-vga-io@0x3c0", + QEMU_PCI_VGA_IO_HI_SIZE); + + pci_register_vga(&vdev->pdev, &vdev->vga->region[QEMU_PCI_VGA_MEM].mem, + &vdev->vga->region[QEMU_PCI_VGA_IO_LO].mem, + &vdev->vga->region[QEMU_PCI_VGA_IO_HI].mem); return 0; } @@ -2398,7 +2494,7 @@ static int vfio_initfn(PCIDevice *pdev) ssize_t len; struct stat st; int groupid; - int ret; + int i, ret; if (!vdev->vbasedev.sysfsdev) { vdev->vbasedev.sysfsdev = @@ -2560,6 +2656,43 @@ static int vfio_initfn(PCIDevice *pdev) goto out_teardown; } + if (vdev->vga) { + vfio_vga_quirk_setup(vdev); + } + + for (i = 0; i < PCI_ROM_SLOT; i++) { + vfio_bar_quirk_setup(vdev, i); + } + + if (!vdev->igd_opregion && + vdev->features & VFIO_FEATURE_ENABLE_IGD_OPREGION) { + struct vfio_region_info *opregion; + + if (vdev->pdev.qdev.hotplugged) { + error_report("Cannot support IGD OpRegion feature on hotplugged " + "device %s", vdev->vbasedev.name); + ret = -EINVAL; + goto out_teardown; + } + + ret = vfio_get_dev_region_info(&vdev->vbasedev, + VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL, + VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION, &opregion); + if (ret) { + error_report("Device %s does not support requested IGD OpRegion " + "feature", vdev->vbasedev.name); + goto out_teardown; + } + + ret = vfio_pci_igd_opregion_init(vdev, opregion); + g_free(opregion); + if (ret) { + error_report("Device %s IGD OpRegion initialization failed", + vdev->vbasedev.name); + goto out_teardown; + } + } + /* QEMU emulates all of MSI & MSIX */ if (pdev->cap_present & QEMU_PCI_CAP_MSIX) { memset(vdev->emulated_config_bits + pdev->msix_cap, 0xff, @@ -2603,6 +2736,13 @@ static void vfio_instance_finalize(Object *obj) vfio_bars_finalize(vdev); g_free(vdev->emulated_config_bits); g_free(vdev->rom); + /* + * XXX Leaking igd_opregion is not an oversight, we can't remove the + * fw_cfg entry therefore leaking this allocation seems like the safest + * option. + * + * g_free(vdev->igd_opregion); + */ vfio_put_device(vdev); vfio_put_group(group); } @@ -2677,6 +2817,8 @@ static Property vfio_pci_dev_properties[] = { VFIO_FEATURE_ENABLE_VGA_BIT, false), DEFINE_PROP_BIT("x-req", VFIOPCIDevice, features, VFIO_FEATURE_ENABLE_REQ_BIT, true), + DEFINE_PROP_BIT("x-igd-opregion", VFIOPCIDevice, features, + VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT, false), DEFINE_PROP_BOOL("x-no-mmap", VFIOPCIDevice, vbasedev.no_mmap, false), DEFINE_PROP_BOOL("x-no-kvm-intx", VFIOPCIDevice, no_kvm_intx, false), DEFINE_PROP_BOOL("x-no-kvm-msi", VFIOPCIDevice, no_kvm_msi, false), @@ -2687,6 +2829,7 @@ static Property vfio_pci_dev_properties[] = { sub_vendor_id, PCI_ANY_ID), DEFINE_PROP_UINT32("x-pci-sub-device-id", VFIOPCIDevice, sub_device_id, PCI_ANY_ID), + DEFINE_PROP_UINT32("x-igd-gms", VFIOPCIDevice, igd_gms, 0), /* * TODO - support passed fds... is this necessary? * DEFINE_PROP_STRING("vfiofd", VFIOPCIDevice, vfiofd_name), diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 3976f6854..7d482d9d2 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -115,6 +115,7 @@ typedef struct VFIOPCIDevice { int interrupt; /* Current interrupt type */ VFIOBAR bars[PCI_NUM_REGIONS - 1]; /* No ROM */ VFIOVGA *vga; /* 0xa0000, 0x3b0, 0x3c0 */ + void *igd_opregion; PCIHostDeviceAddress host; EventNotifier err_notifier; EventNotifier req_notifier; @@ -128,9 +129,12 @@ typedef struct VFIOPCIDevice { #define VFIO_FEATURE_ENABLE_VGA (1 << VFIO_FEATURE_ENABLE_VGA_BIT) #define VFIO_FEATURE_ENABLE_REQ_BIT 1 #define VFIO_FEATURE_ENABLE_REQ (1 << VFIO_FEATURE_ENABLE_REQ_BIT) +#define VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT 2 +#define VFIO_FEATURE_ENABLE_IGD_OPREGION \ + (1 << VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT) int32_t bootindex; + uint32_t igd_gms; uint8_t pm_cap; - bool has_vga; bool pci_aer; bool req_enabled; bool has_flr; @@ -159,4 +163,7 @@ void vfio_setup_resetfn_quirk(VFIOPCIDevice *vdev); int vfio_populate_vga(VFIOPCIDevice *vdev); +int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev, + struct vfio_region_info *info); + #endif /* HW_VFIO_VFIO_PCI_H */ diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c index 1798a00a3..a559e7b65 100644 --- a/hw/vfio/platform.c +++ b/hw/vfio/platform.c @@ -496,7 +496,7 @@ static int vfio_populate_device(VFIODevice *vbasedev) irq.index = i; ret = ioctl(vbasedev->fd, VFIO_DEVICE_GET_IRQ_INFO, &irq); if (ret) { - error_printf("vfio: error getting device %s irq info", + error_report("vfio: error getting device %s irq info", vbasedev->name); goto irq_err; } else { diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c new file mode 100644 index 000000000..7443d348d --- /dev/null +++ b/hw/vfio/spapr.c @@ -0,0 +1,209 @@ +/* + * 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, ®); + 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, ®); + 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); + 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 new file mode 100644 index 000000000..da133221d --- /dev/null +++ b/hw/vfio/trace-events @@ -0,0 +1,125 @@ +# See docs/tracing.txt for syntax documentation. + +# hw/vfio/pci.c +vfio_intx_interrupt(const char *name, char line) " (%s) Pin %c" +vfio_intx_eoi(const char *name) " (%s) EOI" +vfio_intx_enable_kvm(const char *name) " (%s) KVM INTx accel enabled" +vfio_intx_disable_kvm(const char *name) " (%s) KVM INTx accel disabled" +vfio_intx_update(const char *name, int new_irq, int target_irq) " (%s) IRQ moved %d -> %d" +vfio_intx_enable(const char *name) " (%s)" +vfio_intx_disable(const char *name) " (%s)" +vfio_msi_interrupt(const char *name, int index, uint64_t addr, int data) " (%s) vector %d 0x%"PRIx64"/0x%x" +vfio_msix_vector_do_use(const char *name, int index) " (%s) vector %d used" +vfio_msix_vector_release(const char *name, int index) " (%s) vector %d released" +vfio_msix_enable(const char *name) " (%s)" +vfio_msix_pba_disable(const char *name) " (%s)" +vfio_msix_pba_enable(const char *name) " (%s)" +vfio_msix_disable(const char *name) " (%s)" +vfio_msix_fixup(const char *name, int bar, uint64_t start, uint64_t end) " (%s) MSI-X region %d mmap fixup [0x%"PRIx64" - 0x%"PRIx64"]" +vfio_msi_enable(const char *name, int nr_vectors) " (%s) Enabled %d MSI vectors" +vfio_msi_disable(const char *name) " (%s)" +vfio_pci_load_rom(const char *name, unsigned long size, unsigned long offset, unsigned long flags) "Device %s ROM:\n size: 0x%lx, offset: 0x%lx, flags: 0x%lx" +vfio_rom_read(const char *name, uint64_t addr, int size, uint64_t data) " (%s, 0x%"PRIx64", 0x%x) = 0x%"PRIx64 +vfio_pci_size_rom(const char *name, int size) "%s ROM size 0x%x" +vfio_vga_write(uint64_t addr, uint64_t data, int size) " (0x%"PRIx64", 0x%"PRIx64", %d)" +vfio_vga_read(uint64_t addr, int size, uint64_t data) " (0x%"PRIx64", %d) = 0x%"PRIx64 +vfio_pci_read_config(const char *name, int addr, int len, int val) " (%s, @0x%x, len=0x%x) %x" +vfio_pci_write_config(const char *name, int addr, int val, int len) " (%s, @0x%x, 0x%x, len=0x%x)" +vfio_msi_setup(const char *name, int pos) "%s PCI MSI CAP @0x%x" +vfio_msix_early_setup(const char *name, int pos, int table_bar, int offset, int entries) "%s PCI MSI-X CAP @0x%x, BAR %d, offset 0x%x, entries %d" +vfio_check_pcie_flr(const char *name) "%s Supports FLR via PCIe cap" +vfio_check_pm_reset(const char *name) "%s Supports PM reset" +vfio_check_af_flr(const char *name) "%s Supports FLR via AF cap" +vfio_pci_hot_reset(const char *name, const char *type) " (%s) %s" +vfio_pci_hot_reset_has_dep_devices(const char *name) "%s: hot reset dependent devices:" +vfio_pci_hot_reset_dep_devices(int domain, int bus, int slot, int function, int group_id) "\t%04x:%02x:%02x.%x group %d" +vfio_pci_hot_reset_result(const char *name, const char *result) "%s hot reset: %s" +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" +vfio_pci_emulated_vendor_id(const char *name, uint16_t val) "%s %04x" +vfio_pci_emulated_device_id(const char *name, uint16_t val) "%s %04x" +vfio_pci_emulated_sub_vendor_id(const char *name, uint16_t val) "%s %04x" +vfio_pci_emulated_sub_device_id(const char *name, uint16_t val) "%s %04x" + +# hw/vfio/pci-quirks. +vfio_quirk_rom_blacklisted(const char *name, uint16_t vid, uint16_t did) "%s %04x:%04x" +vfio_quirk_generic_window_address_write(const char *name, const char * region_name, uint64_t data) "%s %s 0x%"PRIx64 +vfio_quirk_generic_window_data_read(const char *name, const char * region_name, uint64_t data) "%s %s 0x%"PRIx64 +vfio_quirk_generic_window_data_write(const char *name, const char * region_name, uint64_t data) "%s %s 0x%"PRIx64 +vfio_quirk_generic_mirror_read(const char *name, const char * region_name, uint64_t addr, uint64_t data) "%s %s 0x%"PRIx64": 0x%"PRIx64 +vfio_quirk_generic_mirror_write(const char *name, const char * region_name, uint64_t addr, uint64_t data) "%s %s 0x%"PRIx64": 0x%"PRIx64 +vfio_quirk_ati_3c3_read(const char *name, uint64_t data) "%s 0x%"PRIx64 +vfio_quirk_ati_3c3_probe(const char *name) "%s" +vfio_quirk_ati_bar4_probe(const char *name) "%s" +vfio_quirk_ati_bar2_probe(const char *name) "%s" +vfio_quirk_nvidia_3d0_state(const char *name, const char *state) "%s %s" +vfio_quirk_nvidia_3d0_read(const char *name, uint8_t offset, unsigned size, uint64_t val) " (%s, @0x%x, len=0x%x) %"PRIx64 +vfio_quirk_nvidia_3d0_write(const char *name, uint8_t offset, uint64_t data, unsigned size) "(%s, @0x%x, 0x%"PRIx64", len=0x%x)" +vfio_quirk_nvidia_3d0_probe(const char *name) "%s" +vfio_quirk_nvidia_bar5_state(const char *name, const char *state) "%s %s" +vfio_quirk_nvidia_bar5_probe(const char *name) "%s" +vfio_quirk_nvidia_bar0_msi_ack(const char *name) "%s" +vfio_quirk_nvidia_bar0_probe(const char *name) "%s" +vfio_quirk_rtl8168_fake_latch(const char *name, uint64_t val) "%s 0x%"PRIx64 +vfio_quirk_rtl8168_msix_write(const char *name, uint16_t offset, uint64_t val) "%s MSI-X table write[0x%x]: 0x%"PRIx64 +vfio_quirk_rtl8168_msix_read(const char *name, uint16_t offset, uint64_t val) "%s MSI-X table read[0x%x]: 0x%"PRIx64 +vfio_quirk_rtl8168_probe(const char *name) "%s" + +vfio_quirk_ati_bonaire_reset_skipped(const char *name) "%s" +vfio_quirk_ati_bonaire_reset_no_smc(const char *name) "%s" +vfio_quirk_ati_bonaire_reset_timeout(const char *name) "%s" +vfio_quirk_ati_bonaire_reset_done(const char *name) "%s" +vfio_quirk_ati_bonaire_reset(const char *name) "%s" +vfio_pci_igd_bar4_write(const char *name, uint32_t index, uint32_t data, uint32_t base) "%s [%03x] %08x -> %08x" +vfio_pci_igd_bdsm_enabled(const char *name, int size) "%s %dMB" +vfio_pci_igd_opregion_enabled(const char *name) "%s" +vfio_pci_igd_host_bridge_enabled(const char *name) "%s" +vfio_pci_igd_lpc_bridge_enabled(const char *name) "%s" + +# hw/vfio/common.c +vfio_region_write(const char *name, int index, uint64_t addr, uint64_t data, unsigned size) " (%s:region%d+0x%"PRIx64", 0x%"PRIx64 ", %d)" +vfio_region_read(char *name, int index, uint64_t addr, unsigned size, uint64_t data) " (%s:region%d+0x%"PRIx64", %d) = 0x%"PRIx64 +vfio_iommu_map_notify(uint64_t iova_start, uint64_t iova_end) "iommu map @ %"PRIx64" - %"PRIx64 +vfio_listener_region_add_skip(uint64_t start, uint64_t end) "SKIPPING region_add %"PRIx64" - %"PRIx64 +vfio_listener_region_add_iommu(uint64_t start, uint64_t end) "region_add [iommu] %"PRIx64" - %"PRIx64 +vfio_listener_region_add_ram(uint64_t iova_start, uint64_t iova_end, void *vaddr) "region_add [ram] %"PRIx64" - %"PRIx64" [%p]" +vfio_listener_region_del_skip(uint64_t start, uint64_t end) "SKIPPING region_del %"PRIx64" - %"PRIx64 +vfio_listener_region_del(uint64_t start, uint64_t end) "region_del %"PRIx64" - %"PRIx64 +vfio_disconnect_container(int fd) "close container->fd=%d" +vfio_put_group(int fd) "close group->fd=%d" +vfio_get_device(const char * name, unsigned int flags, unsigned int num_regions, unsigned int num_irqs) "Device %s flags: %u, regions: %u, irqs: %u" +vfio_put_base_device(int fd) "close vdev->fd=%d" +vfio_region_setup(const char *dev, int index, const char *name, unsigned long flags, unsigned long offset, unsigned long size) "Device %s, region %d \"%s\", flags: %lx, offset: %lx, size: %lx" +vfio_region_mmap_fault(const char *name, int index, unsigned long offset, unsigned long size, int fault) "Region %s mmaps[%d], [%lx - %lx], fault: %d" +vfio_region_mmap(const char *name, unsigned long offset, unsigned long end) "Region %s [%lx - %lx]" +vfio_region_exit(const char *name, int index) "Device %s, region %d" +vfio_region_finalize(const char *name, int index) "Device %s, region %d" +vfio_region_mmaps_set_enabled(const char *name, bool enabled) "Region %s mmaps enabled: %d" +vfio_region_sparse_mmap_header(const char *name, int index, int nr_areas) "Device %s region %d: %d sparse mmap entries" +vfio_region_sparse_mmap_entry(int i, unsigned long start, unsigned long end) "sparse entry %d [0x%lx - 0x%lx]" +vfio_get_dev_region(const char *name, int index, uint32_t type, uint32_t subtype) "%s index %d, %08x/%0x8" + +# hw/vfio/platform.c +vfio_platform_base_device_init(char *name, int groupid) "%s belongs to group #%d" +vfio_platform_realize(char *name, char *compat) "vfio device %s, compat = %s" +vfio_platform_eoi(int pin, int fd) "EOI IRQ pin %d (fd=%d)" +vfio_platform_intp_mmap_enable(int pin) "IRQ #%d still active, stay in slow path" +vfio_platform_intp_interrupt(int pin, int fd) "Inject IRQ #%d (fd = %d)" +vfio_platform_intp_inject_pending_lockheld(int pin, int fd) "Inject pending IRQ #%d (fd = %d)" +vfio_platform_populate_interrupts(int pin, int count, int flags) "- IRQ index %d: count %d, flags=0x%x" +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/trace-events b/hw/virtio/trace-events new file mode 100644 index 000000000..55184d33b --- /dev/null +++ b/hw/virtio/trace-events @@ -0,0 +1,16 @@ +# See docs/tracing.txt for syntax documentation. + +# hw/virtio/virtio.c +virtqueue_fill(void *vq, const void *elem, unsigned int len, unsigned int idx) "vq %p elem %p len %u idx %u" +virtqueue_flush(void *vq, unsigned int count) "vq %p count %u" +virtqueue_pop(void *vq, void *elem, unsigned int in_num, unsigned int out_num) "vq %p elem %p in_num %u out_num %u" +virtio_queue_notify(void *vdev, int n, void *vq) "vdev %p n %d vq %p" +virtio_irq(void *vq) "vq %p" +virtio_notify(void *vdev, void *vq) "vdev %p vq %p" +virtio_set_status(void *vdev, uint8_t val) "vdev %p val %u" + +# hw/virtio/virtio-rng.c +virtio_rng_guest_not_ready(void *rng) "rng %p: guest not ready" +virtio_rng_pushed(void *rng, size_t len) "rng %p: %zd bytes pushed" +virtio_rng_request(void *rng, size_t size, unsigned quota) "rng %p: %zd bytes requested, %u bytes quota left" + diff --git a/hw/virtio/vhost-backend.c b/hw/virtio/vhost-backend.c index b35890289..7681f152f 100644 --- a/hw/virtio/vhost-backend.c +++ b/hw/virtio/vhost-backend.c @@ -9,12 +9,11 @@ */ #include "qemu/osdep.h" +#include <linux/vhost.h> +#include <sys/ioctl.h> #include "hw/virtio/vhost.h" #include "hw/virtio/vhost-backend.h" #include "qemu/error-report.h" -#include "linux/vhost.h" - -#include <sys/ioctl.h> static int vhost_kernel_call(struct vhost_dev *dev, unsigned long int request, void *arg) @@ -138,6 +137,12 @@ static int vhost_kernel_set_vring_call(struct vhost_dev *dev, return vhost_kernel_call(dev, VHOST_SET_VRING_CALL, file); } +static int vhost_kernel_set_vring_busyloop_timeout(struct vhost_dev *dev, + struct vhost_vring_state *s) +{ + return vhost_kernel_call(dev, VHOST_SET_VRING_BUSYLOOP_TIMEOUT, s); +} + static int vhost_kernel_set_features(struct vhost_dev *dev, uint64_t features) { @@ -185,6 +190,8 @@ static const VhostOps kernel_ops = { .vhost_get_vring_base = vhost_kernel_get_vring_base, .vhost_set_vring_kick = vhost_kernel_set_vring_kick, .vhost_set_vring_call = vhost_kernel_set_vring_call, + .vhost_set_vring_busyloop_timeout = + vhost_kernel_set_vring_busyloop_timeout, .vhost_set_features = vhost_kernel_set_features, .vhost_get_features = vhost_kernel_get_features, .vhost_set_owner = vhost_kernel_set_owner, diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 5914e8510..b57454a4b 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -17,7 +17,6 @@ #include "sysemu/kvm.h" #include "qemu/error-report.h" #include "qemu/sockets.h" -#include "exec/ram_addr.h" #include "migration/migration.h" #include <sys/ioctl.h> @@ -32,6 +31,7 @@ enum VhostUserProtocolFeature { VHOST_USER_PROTOCOL_F_MQ = 0, VHOST_USER_PROTOCOL_F_LOG_SHMFD = 1, VHOST_USER_PROTOCOL_F_RARP = 2, + VHOST_USER_PROTOCOL_F_REPLY_ACK = 3, VHOST_USER_PROTOCOL_F_MAX }; @@ -85,6 +85,7 @@ typedef struct VhostUserMsg { #define VHOST_USER_VERSION_MASK (0x3) #define VHOST_USER_REPLY_MASK (0x1<<2) +#define VHOST_USER_NEED_REPLY_MASK (0x1 << 3) uint32_t flags; uint32_t size; /* the following payload size */ union { @@ -159,6 +160,25 @@ fail: return -1; } +static int process_message_reply(struct vhost_dev *dev, + VhostUserRequest request) +{ + VhostUserMsg msg; + + if (vhost_user_read(dev, &msg) < 0) { + return -1; + } + + if (msg.request != request) { + error_report("Received unexpected msg type." + "Expected %d received %d", + request, msg.request); + return -1; + } + + return msg.payload.u64 ? -1 : 0; +} + static bool vhost_user_one_time_request(VhostUserRequest request) { switch (request) { @@ -177,7 +197,7 @@ static int vhost_user_write(struct vhost_dev *dev, VhostUserMsg *msg, int *fds, int fd_num) { CharDriverState *chr = dev->opaque; - int size = VHOST_USER_HDR_SIZE + msg->size; + int ret, size = VHOST_USER_HDR_SIZE + msg->size; /* * For non-vring specific requests, like VHOST_USER_SET_MEM_TABLE, @@ -188,12 +208,19 @@ static int vhost_user_write(struct vhost_dev *dev, VhostUserMsg *msg, return 0; } - if (fd_num) { - qemu_chr_fe_set_msgfds(chr, fds, fd_num); + if (qemu_chr_fe_set_msgfds(chr, fds, fd_num) < 0) { + error_report("Failed to set msg fds."); + return -1; + } + + ret = qemu_chr_fe_write_all(chr, (const uint8_t *) msg, size); + if (ret != size) { + error_report("Failed to write msg." + " Wrote %d instead of %d.", ret, size); + return -1; } - return qemu_chr_fe_write_all(chr, (const uint8_t *) msg, size) == size ? - 0 : -1; + return 0; } static int vhost_user_set_log_base(struct vhost_dev *dev, uint64_t base, @@ -215,12 +242,14 @@ static int vhost_user_set_log_base(struct vhost_dev *dev, uint64_t base, fds[fd_num++] = log->fd; } - vhost_user_write(dev, &msg, fds, fd_num); + if (vhost_user_write(dev, &msg, fds, fd_num) < 0) { + return -1; + } if (shmfd) { msg.size = 0; if (vhost_user_read(dev, &msg) < 0) { - return 0; + return -1; } if (msg.request != VHOST_USER_SET_LOG_BASE) { @@ -240,25 +269,32 @@ static int vhost_user_set_mem_table(struct vhost_dev *dev, int fds[VHOST_MEMORY_MAX_NREGIONS]; int i, fd; size_t fd_num = 0; + bool reply_supported = virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_REPLY_ACK); + VhostUserMsg msg = { .request = VHOST_USER_SET_MEM_TABLE, .flags = VHOST_USER_VERSION, }; + if (reply_supported) { + msg.flags |= VHOST_USER_NEED_REPLY_MASK; + } + for (i = 0; i < dev->mem->nregions; ++i) { struct vhost_memory_region *reg = dev->mem->regions + i; - ram_addr_t ram_addr; + ram_addr_t offset; + MemoryRegion *mr; assert((uintptr_t)reg->userspace_addr == reg->userspace_addr); - qemu_ram_addr_from_host((void *)(uintptr_t)reg->userspace_addr, - &ram_addr); - fd = qemu_get_ram_fd(ram_addr); + mr = memory_region_from_host((void *)(uintptr_t)reg->userspace_addr, + &offset); + fd = memory_region_get_fd(mr); if (fd > 0) { msg.payload.memory.regions[fd_num].userspace_addr = reg->userspace_addr; msg.payload.memory.regions[fd_num].memory_size = reg->memory_size; msg.payload.memory.regions[fd_num].guest_phys_addr = reg->guest_phys_addr; - msg.payload.memory.regions[fd_num].mmap_offset = reg->userspace_addr - - (uintptr_t) qemu_get_ram_block_host_ptr(ram_addr); + msg.payload.memory.regions[fd_num].mmap_offset = offset; assert(fd_num < VHOST_MEMORY_MAX_NREGIONS); fds[fd_num++] = fd; } @@ -276,7 +312,13 @@ static int vhost_user_set_mem_table(struct vhost_dev *dev, msg.size += sizeof(msg.payload.memory.padding); msg.size += fd_num * sizeof(VhostUserMemoryRegion); - vhost_user_write(dev, &msg, fds, fd_num); + if (vhost_user_write(dev, &msg, fds, fd_num) < 0) { + return -1; + } + + if (reply_supported) { + return process_message_reply(dev, msg.request); + } return 0; } @@ -291,7 +333,9 @@ static int vhost_user_set_vring_addr(struct vhost_dev *dev, .size = sizeof(msg.payload.addr), }; - vhost_user_write(dev, &msg, NULL, 0); + if (vhost_user_write(dev, &msg, NULL, 0) < 0) { + return -1; + } return 0; } @@ -314,7 +358,9 @@ static int vhost_set_vring(struct vhost_dev *dev, .size = sizeof(msg.payload.state), }; - vhost_user_write(dev, &msg, NULL, 0); + if (vhost_user_write(dev, &msg, NULL, 0) < 0) { + return -1; + } return 0; } @@ -361,10 +407,12 @@ static int vhost_user_get_vring_base(struct vhost_dev *dev, .size = sizeof(msg.payload.state), }; - vhost_user_write(dev, &msg, NULL, 0); + if (vhost_user_write(dev, &msg, NULL, 0) < 0) { + return -1; + } if (vhost_user_read(dev, &msg) < 0) { - return 0; + return -1; } if (msg.request != VHOST_USER_GET_VRING_BASE) { @@ -402,7 +450,9 @@ static int vhost_set_vring_file(struct vhost_dev *dev, msg.payload.u64 |= VHOST_USER_VRING_NOFD_MASK; } - vhost_user_write(dev, &msg, fds, fd_num); + if (vhost_user_write(dev, &msg, fds, fd_num) < 0) { + return -1; + } return 0; } @@ -428,7 +478,9 @@ static int vhost_user_set_u64(struct vhost_dev *dev, int request, uint64_t u64) .size = sizeof(msg.payload.u64), }; - vhost_user_write(dev, &msg, NULL, 0); + if (vhost_user_write(dev, &msg, NULL, 0) < 0) { + return -1; + } return 0; } @@ -456,10 +508,12 @@ static int vhost_user_get_u64(struct vhost_dev *dev, int request, uint64_t *u64) return 0; } - vhost_user_write(dev, &msg, NULL, 0); + if (vhost_user_write(dev, &msg, NULL, 0) < 0) { + return -1; + } if (vhost_user_read(dev, &msg) < 0) { - return 0; + return -1; } if (msg.request != request) { @@ -490,7 +544,9 @@ static int vhost_user_set_owner(struct vhost_dev *dev) .flags = VHOST_USER_VERSION, }; - vhost_user_write(dev, &msg, NULL, 0); + if (vhost_user_write(dev, &msg, NULL, 0) < 0) { + return -1; + } return 0; } @@ -502,7 +558,9 @@ static int vhost_user_reset_device(struct vhost_dev *dev) .flags = VHOST_USER_VERSION, }; - vhost_user_write(dev, &msg, NULL, 0); + if (vhost_user_write(dev, &msg, NULL, 0) < 0) { + return -1; + } return 0; } @@ -589,7 +647,6 @@ static bool vhost_user_requires_shm_log(struct vhost_dev *dev) static int vhost_user_migration_done(struct vhost_dev *dev, char* mac_addr) { VhostUserMsg msg = { 0 }; - int err; assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); @@ -606,8 +663,7 @@ static int vhost_user_migration_done(struct vhost_dev *dev, char* mac_addr) memcpy((char *)&msg.payload.u64, mac_addr, 6); msg.size = sizeof(msg.payload.u64); - err = vhost_user_write(dev, &msg, NULL, 0); - return err; + return vhost_user_write(dev, &msg, NULL, 0); } return -1; } @@ -616,17 +672,15 @@ static bool vhost_user_can_merge(struct vhost_dev *dev, uint64_t start1, uint64_t size1, uint64_t start2, uint64_t size2) { - ram_addr_t ram_addr; + ram_addr_t offset; int mfd, rfd; MemoryRegion *mr; - mr = qemu_ram_addr_from_host((void *)(uintptr_t)start1, &ram_addr); - assert(mr); - mfd = qemu_get_ram_fd(ram_addr); + mr = memory_region_from_host((void *)(uintptr_t)start1, &offset); + mfd = memory_region_get_fd(mr); - mr = qemu_ram_addr_from_host((void *)(uintptr_t)start2, &ram_addr); - assert(mr); - rfd = qemu_get_ram_fd(ram_addr); + mr = memory_region_from_host((void *)(uintptr_t)start2, &offset); + rfd = memory_region_get_fd(mr); return mfd == rfd; } diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 440071815..3d0c807d0 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -27,6 +27,18 @@ #include "hw/virtio/virtio-access.h" #include "migration/migration.h" +/* enabled until disconnected backend stabilizes */ +#define _VHOST_DEBUG 1 + +#ifdef _VHOST_DEBUG +#define VHOST_OPS_DEBUG(fmt, ...) \ + do { error_report(fmt ": %s (%d)", ## __VA_ARGS__, \ + strerror(errno), errno); } while (0) +#else +#define VHOST_OPS_DEBUG(fmt, ...) \ + do { } while (0) +#endif + static struct vhost_log *vhost_log; static struct vhost_log *vhost_log_shm; @@ -362,6 +374,8 @@ static void vhost_log_put(struct vhost_dev *dev, bool sync) if (!log) { return; } + dev->log = NULL; + dev->log_size = 0; --log->refcnt; if (log->refcnt == 0) { @@ -398,7 +412,10 @@ static inline void vhost_dev_log_resize(struct vhost_dev *dev, uint64_t size) /* inform backend of log switching, this must be done before releasing the current log, to ensure no logging is lost */ r = dev->vhost_ops->vhost_set_log_base(dev, log_base, log); - assert(r >= 0); + if (r < 0) { + VHOST_OPS_DEBUG("vhost_set_log_base failed"); + } + vhost_log_put(dev, true); dev->log = log; dev->log_size = size; @@ -422,11 +439,11 @@ static int vhost_verify_ring_mappings(struct vhost_dev *dev, l = vq->ring_size; p = cpu_physical_memory_map(vq->ring_phys, &l, 1); if (!p || l != vq->ring_size) { - fprintf(stderr, "Unable to map ring buffer for ring %d\n", i); + error_report("Unable to map ring buffer for ring %d", i); r = -ENOMEM; } if (p != vq->ring) { - fprintf(stderr, "Ring buffer relocated for ring %d\n", i); + error_report("Ring buffer relocated for ring %d", i); r = -EBUSY; } cpu_physical_memory_unmap(p, l, 0, 0); @@ -565,7 +582,9 @@ static void vhost_commit(MemoryListener *listener) if (!dev->log_enabled) { r = dev->vhost_ops->vhost_set_mem_table(dev, dev->mem); - assert(r >= 0); + if (r < 0) { + VHOST_OPS_DEBUG("vhost_set_mem_table failed"); + } dev->memory_changed = false; return; } @@ -578,7 +597,9 @@ static void vhost_commit(MemoryListener *listener) vhost_dev_log_resize(dev, log_size + VHOST_LOG_BUFFER); } r = dev->vhost_ops->vhost_set_mem_table(dev, dev->mem); - assert(r >= 0); + if (r < 0) { + VHOST_OPS_DEBUG("vhost_set_mem_table failed"); + } /* To log less, can only decrease log size after table update. */ if (dev->log_size > log_size + VHOST_LOG_BUFFER) { vhost_dev_log_resize(dev, log_size); @@ -647,6 +668,7 @@ static int vhost_virtqueue_set_addr(struct vhost_dev *dev, }; int r = dev->vhost_ops->vhost_set_vring_addr(dev, &addr); if (r < 0) { + VHOST_OPS_DEBUG("vhost_set_vring_addr failed"); return -errno; } return 0; @@ -660,12 +682,15 @@ static int vhost_dev_set_features(struct vhost_dev *dev, bool enable_log) features |= 0x1ULL << VHOST_F_LOG_ALL; } r = dev->vhost_ops->vhost_set_features(dev, features); + if (r < 0) { + VHOST_OPS_DEBUG("vhost_set_features failed"); + } return r < 0 ? -errno : 0; } static int vhost_dev_set_log(struct vhost_dev *dev, bool enable_log) { - int r, t, i, idx; + int r, i, idx; r = vhost_dev_set_features(dev, enable_log); if (r < 0) { goto err_features; @@ -682,12 +707,10 @@ static int vhost_dev_set_log(struct vhost_dev *dev, bool enable_log) err_vq: for (; i >= 0; --i) { idx = dev->vhost_ops->vhost_get_vq_index(dev, dev->vq_index + i); - t = vhost_virtqueue_set_addr(dev, dev->vqs + i, idx, - dev->log_enabled); - assert(t >= 0); + vhost_virtqueue_set_addr(dev, dev->vqs + i, idx, + dev->log_enabled); } - t = vhost_dev_set_features(dev, dev->log_enabled); - assert(t >= 0); + vhost_dev_set_features(dev, dev->log_enabled); err_features: return r; } @@ -710,8 +733,6 @@ static int vhost_migration_log(MemoryListener *listener, int enable) return r; } vhost_log_put(dev, false); - dev->log = NULL; - dev->log_size = 0; } else { vhost_dev_log_resize(dev, vhost_get_log_size(dev)); r = vhost_dev_set_log(dev, true); @@ -767,15 +788,11 @@ static inline bool vhost_needs_vring_endian(VirtIODevice *vdev) if (virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)) { return false; } -#ifdef TARGET_IS_BIENDIAN #ifdef HOST_WORDS_BIGENDIAN return vdev->device_endian == VIRTIO_DEVICE_ENDIAN_LITTLE; #else return vdev->device_endian == VIRTIO_DEVICE_ENDIAN_BIG; #endif -#else - return false; -#endif } static int vhost_virtqueue_set_vring_endian_legacy(struct vhost_dev *dev, @@ -791,6 +808,7 @@ static int vhost_virtqueue_set_vring_endian_legacy(struct vhost_dev *dev, return 0; } + VHOST_OPS_DEBUG("vhost_set_vring_endian failed"); if (errno == ENOTTY) { error_report("vhost does not support cross-endian"); return -ENOSYS; @@ -819,12 +837,14 @@ static int vhost_virtqueue_start(struct vhost_dev *dev, vq->num = state.num = virtio_queue_get_num(vdev, idx); r = dev->vhost_ops->vhost_set_vring_num(dev, &state); if (r) { + VHOST_OPS_DEBUG("vhost_set_vring_num failed"); return -errno; } state.num = virtio_queue_get_last_avail_idx(vdev, idx); r = dev->vhost_ops->vhost_set_vring_base(dev, &state); if (r) { + VHOST_OPS_DEBUG("vhost_set_vring_base failed"); return -errno; } @@ -876,6 +896,7 @@ static int vhost_virtqueue_start(struct vhost_dev *dev, file.fd = event_notifier_get_fd(virtio_queue_get_host_notifier(vvq)); r = dev->vhost_ops->vhost_set_vring_kick(dev, &file); if (r) { + VHOST_OPS_DEBUG("vhost_set_vring_kick failed"); r = -errno; goto fail_kick; } @@ -923,25 +944,21 @@ static void vhost_virtqueue_stop(struct vhost_dev *dev, r = dev->vhost_ops->vhost_get_vring_base(dev, &state); if (r < 0) { - fprintf(stderr, "vhost VQ %d ring restore failed: %d\n", idx, r); - fflush(stderr); + VHOST_OPS_DEBUG("vhost VQ %d ring restore failed: %d", idx, r); + } else { + virtio_queue_set_last_avail_idx(vdev, idx, state.num); } - virtio_queue_set_last_avail_idx(vdev, idx, state.num); virtio_queue_invalidate_signalled_used(vdev, idx); /* In the cross-endian case, we need to reset the vring endianness to * native as legacy devices expect so by default. */ if (vhost_needs_vring_endian(vdev)) { - r = vhost_virtqueue_set_vring_endian_legacy(dev, - !virtio_is_big_endian(vdev), - vhost_vq_index); - if (r < 0) { - error_report("failed to reset vring endianness"); - } + vhost_virtqueue_set_vring_endian_legacy(dev, + !virtio_is_big_endian(vdev), + vhost_vq_index); } - assert (r >= 0); cpu_physical_memory_unmap(vq->ring, virtio_queue_get_ring_size(vdev, idx), 0, virtio_queue_get_ring_size(vdev, idx)); cpu_physical_memory_unmap(vq->used, virtio_queue_get_used_size(vdev, idx), @@ -964,6 +981,29 @@ static void vhost_eventfd_del(MemoryListener *listener, { } +static int vhost_virtqueue_set_busyloop_timeout(struct vhost_dev *dev, + int n, uint32_t timeout) +{ + int vhost_vq_index = dev->vhost_ops->vhost_get_vq_index(dev, n); + struct vhost_vring_state state = { + .index = vhost_vq_index, + .num = timeout, + }; + int r; + + if (!dev->vhost_ops->vhost_set_vring_busyloop_timeout) { + return -EINVAL; + } + + r = dev->vhost_ops->vhost_set_vring_busyloop_timeout(dev, &state); + if (r) { + VHOST_OPS_DEBUG("vhost_set_vring_busyloop_timeout failed"); + return r; + } + + return 0; +} + static int vhost_virtqueue_init(struct vhost_dev *dev, struct vhost_virtqueue *vq, int n) { @@ -979,6 +1019,7 @@ static int vhost_virtqueue_init(struct vhost_dev *dev, file.fd = event_notifier_get_fd(&vq->masked_notifier); r = dev->vhost_ops->vhost_set_vring_call(dev, &file); if (r) { + VHOST_OPS_DEBUG("vhost_set_vring_call failed"); r = -errno; goto fail_call; } @@ -994,47 +1035,57 @@ static void vhost_virtqueue_cleanup(struct vhost_virtqueue *vq) } int vhost_dev_init(struct vhost_dev *hdev, void *opaque, - VhostBackendType backend_type) + VhostBackendType backend_type, uint32_t busyloop_timeout) { uint64_t features; - int i, r; + int i, r, n_initialized_vqs = 0; hdev->migration_blocker = NULL; - if (vhost_set_backend_type(hdev, backend_type) < 0) { - close((uintptr_t)opaque); - return -1; - } + r = vhost_set_backend_type(hdev, backend_type); + assert(r >= 0); - if (hdev->vhost_ops->vhost_backend_init(hdev, opaque) < 0) { - close((uintptr_t)opaque); - return -errno; + r = hdev->vhost_ops->vhost_backend_init(hdev, opaque); + if (r < 0) { + goto fail; } if (used_memslots > hdev->vhost_ops->vhost_backend_memslots_limit(hdev)) { - fprintf(stderr, "vhost backend memory slots limit is less" - " than current number of present memory slots\n"); - close((uintptr_t)opaque); - return -1; + error_report("vhost backend memory slots limit is less" + " than current number of present memory slots"); + r = -1; + goto fail; } - QLIST_INSERT_HEAD(&vhost_devices, hdev, entry); r = hdev->vhost_ops->vhost_set_owner(hdev); if (r < 0) { + VHOST_OPS_DEBUG("vhost_set_owner failed"); goto fail; } r = hdev->vhost_ops->vhost_get_features(hdev, &features); if (r < 0) { + VHOST_OPS_DEBUG("vhost_get_features failed"); goto fail; } - for (i = 0; i < hdev->nvqs; ++i) { + for (i = 0; i < hdev->nvqs; ++i, ++n_initialized_vqs) { r = vhost_virtqueue_init(hdev, hdev->vqs + i, hdev->vq_index + i); if (r < 0) { - goto fail_vq; + goto fail; } } + + if (busyloop_timeout) { + for (i = 0; i < hdev->nvqs; ++i) { + r = vhost_virtqueue_set_busyloop_timeout(hdev, hdev->vq_index + i, + busyloop_timeout); + if (r < 0) { + goto fail_busyloop; + } + } + } + hdev->features = features; hdev->memory_listener = (MemoryListener) { @@ -1076,33 +1127,43 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, hdev->started = false; hdev->memory_changed = false; memory_listener_register(&hdev->memory_listener, &address_space_memory); + QLIST_INSERT_HEAD(&vhost_devices, hdev, entry); return 0; -fail_vq: + +fail_busyloop: while (--i >= 0) { - vhost_virtqueue_cleanup(hdev->vqs + i); + vhost_virtqueue_set_busyloop_timeout(hdev, hdev->vq_index + i, 0); } fail: - r = -errno; - hdev->vhost_ops->vhost_backend_cleanup(hdev); - QLIST_REMOVE(hdev, entry); + hdev->nvqs = n_initialized_vqs; + vhost_dev_cleanup(hdev); return r; } void vhost_dev_cleanup(struct vhost_dev *hdev) { int i; + for (i = 0; i < hdev->nvqs; ++i) { vhost_virtqueue_cleanup(hdev->vqs + i); } - memory_listener_unregister(&hdev->memory_listener); + if (hdev->mem) { + /* those are only safe after successful init */ + memory_listener_unregister(&hdev->memory_listener); + QLIST_REMOVE(hdev, entry); + } if (hdev->migration_blocker) { migrate_del_blocker(hdev->migration_blocker); error_free(hdev->migration_blocker); } g_free(hdev->mem); g_free(hdev->mem_sections); - hdev->vhost_ops->vhost_backend_cleanup(hdev); - QLIST_REMOVE(hdev, entry); + if (hdev->vhost_ops) { + hdev->vhost_ops->vhost_backend_cleanup(hdev); + } + assert(!hdev->log); + + memset(hdev, 0, sizeof(struct vhost_dev)); } /* Stop processing guest IO notifications in qemu. @@ -1114,16 +1175,18 @@ 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) { - fprintf(stderr, "binding does not support host notifiers\n"); + + if (!k->ioeventfd_started) { + error_report("binding does not support host notifiers"); 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); + error_report("vhost VQ %d notifier binding failed: %d", i, -r); goto fail_vq; } } @@ -1131,10 +1194,10 @@ 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); + error_report("vhost VQ %d notifier cleanup error: %d", i, -r); } assert (e >= 0); } @@ -1150,15 +1213,13 @@ 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); + error_report("vhost VQ %d notifier cleanup failed: %d", i, -r); } assert (r >= 0); } @@ -1182,6 +1243,9 @@ void vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, int n, int r, index = n - hdev->vq_index; struct vhost_vring_file file; + /* should only be called after backend is connected */ + assert(hdev->vhost_ops); + if (mask) { assert(vdev->use_guest_notifier_mask); file.fd = event_notifier_get_fd(&hdev->vqs[index].masked_notifier); @@ -1191,7 +1255,9 @@ void vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, int n, file.index = hdev->vhost_ops->vhost_get_vq_index(hdev, n); r = hdev->vhost_ops->vhost_set_vring_call(hdev, &file); - assert(r >= 0); + if (r < 0) { + VHOST_OPS_DEBUG("vhost_set_vring_call failed"); + } } uint64_t vhost_get_features(struct vhost_dev *hdev, const int *feature_bits, @@ -1226,6 +1292,9 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) { int i, r; + /* should only be called after backend is connected */ + assert(hdev->vhost_ops); + hdev->started = true; r = vhost_dev_set_features(hdev, hdev->log_enabled); @@ -1234,6 +1303,7 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) } r = hdev->vhost_ops->vhost_set_mem_table(hdev, hdev->mem); if (r < 0) { + VHOST_OPS_DEBUG("vhost_set_mem_table failed"); r = -errno; goto fail_mem; } @@ -1258,6 +1328,7 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) hdev->log_size ? log_base : 0, hdev->log); if (r < 0) { + VHOST_OPS_DEBUG("vhost_set_log_base failed"); r = -errno; goto fail_log; } @@ -1286,6 +1357,9 @@ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev) { int i; + /* should only be called after backend is connected */ + assert(hdev->vhost_ops); + for (i = 0; i < hdev->nvqs; ++i) { vhost_virtqueue_stop(hdev, vdev, @@ -1295,7 +1369,14 @@ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev) vhost_log_put(hdev, true); hdev->started = false; - hdev->log = NULL; - hdev->log_size = 0; } +int vhost_net_set_backend(struct vhost_dev *hdev, + struct vhost_vring_file *file) +{ + if (hdev->vhost_ops->vhost_net_set_backend) { + return hdev->vhost_ops->vhost_net_set_backend(hdev, file); + } + + return -1; +} diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index 9dbe68179..5af429a58 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -27,10 +27,6 @@ #include "qapi-event.h" #include "trace.h" -#if defined(__linux__) -#include <sys/mman.h> -#endif - #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio-access.h" @@ -138,17 +134,18 @@ static void balloon_stats_get_all(Object *obj, Visitor *v, const char *name, for (i = 0; i < VIRTIO_BALLOON_S_NR; i++) { visit_type_uint64(v, balloon_stat_names[i], &s->stats[i], &err); if (err) { - break; + goto out_nested; } } - error_propagate(errp, err); - err = NULL; - visit_end_struct(v, &err); + visit_check_struct(v, &err); +out_nested: + visit_end_struct(v, NULL); + if (!err) { + visit_check_struct(v, &err); + } out_end: - error_propagate(errp, err); - err = NULL; - visit_end_struct(v, &err); + visit_end_struct(v, NULL); out: error_propagate(errp, err); } @@ -399,11 +396,6 @@ static void virtio_balloon_to_target(void *opaque, ram_addr_t target) trace_virtio_balloon_to_target(target, dev->num_pages); } -static void virtio_balloon_save(QEMUFile *f, void *opaque) -{ - virtio_save(VIRTIO_DEVICE(opaque), f); -} - static void virtio_balloon_save_device(VirtIODevice *vdev, QEMUFile *f) { VirtIOBalloon *s = VIRTIO_BALLOON(vdev); @@ -412,12 +404,9 @@ static void virtio_balloon_save_device(VirtIODevice *vdev, QEMUFile *f) qemu_put_be32(f, s->actual); } -static int virtio_balloon_load(QEMUFile *f, void *opaque, int version_id) +static int virtio_balloon_load(QEMUFile *f, void *opaque, size_t size) { - if (version_id != 1) - return -EINVAL; - - return virtio_load(VIRTIO_DEVICE(opaque), f, version_id); + return virtio_load(VIRTIO_DEVICE(opaque), f, 1); } static int virtio_balloon_load_device(VirtIODevice *vdev, QEMUFile *f, @@ -457,9 +446,6 @@ static void virtio_balloon_device_realize(DeviceState *dev, Error **errp) s->svq = virtio_add_queue(vdev, 128, virtio_balloon_receive_stats); reset_stats(s); - - register_savevm(dev, "virtio-balloon", -1, 1, - virtio_balloon_save, virtio_balloon_load, s); } static void virtio_balloon_device_unrealize(DeviceState *dev, Error **errp) @@ -469,7 +455,6 @@ static void virtio_balloon_device_unrealize(DeviceState *dev, Error **errp) balloon_stats_destroy_timer(s); qemu_remove_balloon_handler(s); - unregister_savevm(dev, "virtio-balloon", s); virtio_cleanup(vdev); } @@ -496,6 +481,8 @@ static void virtio_balloon_instance_init(Object *obj) NULL, s, NULL); } +VMSTATE_VIRTIO_DEVICE(balloon, 1, virtio_balloon_load, virtio_vmstate_save); + static Property virtio_balloon_properties[] = { DEFINE_PROP_BIT("deflate-on-oom", VirtIOBalloon, host_features, VIRTIO_BALLOON_F_DEFLATE_ON_OOM, false), @@ -508,6 +495,7 @@ static void virtio_balloon_class_init(ObjectClass *klass, void *data) VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); dc->props = virtio_balloon_properties; + dc->vmsd = &vmstate_virtio_balloon; set_bit(DEVICE_CATEGORY_MISC, dc->categories); vdc->realize = virtio_balloon_device_realize; vdc->unrealize = virtio_balloon_device_unrealize; diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c index 574f0e23f..a85b7c8ab 100644 --- a/hw/virtio/virtio-bus.c +++ b/hw/virtio/virtio-bus.c @@ -146,6 +146,132 @@ 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 { + k->ioeventfd_assign(proxy, notifier, n, assign); + virtio_queue_set_host_notifier_fd_handler(vq, false, false); + 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); + + if (!k->ioeventfd_started) { + return -ENOSYS; + } + k->ioeventfd_set_disabled(proxy, assign); + if (assign) { + /* + * Stop using the generic ioeventfd, we are doing eventfd handling + * ourselves below + * + * FIXME: We should just switch the handler and not 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_bus_stop_ioeventfd(bus); + } + return set_host_notifier_internal(proxy, bus, n, assign, false); +} + 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 d4cd91f8c..13798b3cb 100644 --- a/hw/virtio/virtio-mmio.c +++ b/hw/virtio/virtio-mmio.c @@ -91,92 +91,62 @@ typedef struct { VirtioBusState bus; bool ioeventfd_disabled; bool ioeventfd_started; + bool format_transport_address; } 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,27 +468,14 @@ 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 Property virtio_mmio_properties[] = { + DEFINE_PROP_BOOL("format_transport_address", VirtIOMMIOProxy, + format_transport_address, true), + DEFINE_PROP_END_OF_LIST(), +}; + static void virtio_mmio_realizefn(DeviceState *d, Error **errp) { VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d); @@ -539,6 +496,7 @@ static void virtio_mmio_class_init(ObjectClass *klass, void *data) dc->realize = virtio_mmio_realizefn; dc->reset = virtio_mmio_reset; set_bit(DEVICE_CATEGORY_MISC, dc->categories); + dc->props = virtio_mmio_properties; } static const TypeInfo virtio_mmio_info = { @@ -550,6 +508,46 @@ static const TypeInfo virtio_mmio_info = { /* virtio-mmio-bus. */ +static char *virtio_mmio_bus_get_dev_path(DeviceState *dev) +{ + BusState *virtio_mmio_bus; + VirtIOMMIOProxy *virtio_mmio_proxy; + char *proxy_path; + SysBusDevice *proxy_sbd; + char *path; + + virtio_mmio_bus = qdev_get_parent_bus(dev); + virtio_mmio_proxy = VIRTIO_MMIO(virtio_mmio_bus->parent); + proxy_path = qdev_get_dev_path(DEVICE(virtio_mmio_proxy)); + + /* + * If @format_transport_address is false, then we just perform the same as + * virtio_bus_get_dev_path(): we delegate the address formatting for the + * device on the virtio-mmio bus to the bus that the virtio-mmio proxy + * (i.e., the device that implements the virtio-mmio bus) resides on. In + * this case the base address of the virtio-mmio transport will be + * invisible. + */ + if (!virtio_mmio_proxy->format_transport_address) { + return proxy_path; + } + + /* Otherwise, we append the base address of the transport. */ + proxy_sbd = SYS_BUS_DEVICE(virtio_mmio_proxy); + assert(proxy_sbd->num_mmio == 1); + assert(proxy_sbd->mmio[0].memory == &virtio_mmio_proxy->iomem); + + if (proxy_path) { + path = g_strdup_printf("%s/virtio-mmio@" TARGET_FMT_plx, proxy_path, + proxy_sbd->mmio[0].addr); + } else { + path = g_strdup_printf("virtio-mmio@" TARGET_FMT_plx, + proxy_sbd->mmio[0].addr); + } + g_free(proxy_path); + return path; +} + static void virtio_mmio_bus_class_init(ObjectClass *klass, void *data) { BusClass *bus_class = BUS_CLASS(klass); @@ -558,10 +556,15 @@ 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; + bus_class->get_dev_path = virtio_mmio_bus_get_dev_path; } static const TypeInfo virtio_mmio_bus_info = { diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index bfedbbf17..755f9218b 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -161,7 +161,7 @@ static bool virtio_pci_modern_state_needed(void *opaque) { VirtIOPCIProxy *proxy = opaque; - return !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_MODERN); + return virtio_pci_modern(proxy); } static const VMStateDescription vmstate_virtio_pci_modern_state = { @@ -262,16 +262,46 @@ 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 legacy = virtio_pci_legacy(proxy); + bool modern = virtio_pci_modern(proxy); bool fast_mmio = kvm_ioeventfd_any_length_enabled(); bool modern_pio = proxy->flags & VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY; MemoryRegion *modern_mr = &proxy->notify.mr; @@ -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) @@ -727,14 +699,13 @@ static uint32_t virtio_read_config(PCIDevice *pci_dev, static int kvm_virtio_pci_vq_vector_use(VirtIOPCIProxy *proxy, unsigned int queue_no, - unsigned int vector, - MSIMessage msg) + unsigned int vector) { VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector]; int ret; if (irqfd->users == 0) { - ret = kvm_irqchip_add_msi_route(kvm_state, msg, &proxy->pci_dev); + ret = kvm_irqchip_add_msi_route(kvm_state, vector, &proxy->pci_dev); if (ret < 0) { return ret; } @@ -761,9 +732,7 @@ static int kvm_virtio_pci_irqfd_use(VirtIOPCIProxy *proxy, VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); VirtQueue *vq = virtio_get_queue(vdev, queue_no); EventNotifier *n = virtio_queue_get_guest_notifier(vq); - int ret; - ret = kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL, irqfd->virq); - return ret; + return kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL, irqfd->virq); } static void kvm_virtio_pci_irqfd_release(VirtIOPCIProxy *proxy, @@ -787,7 +756,6 @@ static int kvm_virtio_pci_vector_use(VirtIOPCIProxy *proxy, int nvqs) VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); unsigned int vector; int ret, queue_no; - MSIMessage msg; for (queue_no = 0; queue_no < nvqs; queue_no++) { if (!virtio_queue_get_num(vdev, queue_no)) { @@ -797,8 +765,7 @@ static int kvm_virtio_pci_vector_use(VirtIOPCIProxy *proxy, int nvqs) if (vector >= msix_nr_vectors_allocated(dev)) { continue; } - msg = msix_get_message(dev, vector); - ret = kvm_virtio_pci_vq_vector_use(proxy, queue_no, vector, msg); + ret = kvm_virtio_pci_vq_vector_use(proxy, queue_no, vector); if (ret < 0) { goto undo; } @@ -875,6 +842,7 @@ static int virtio_pci_vq_vector_unmask(VirtIOPCIProxy *proxy, if (ret < 0) { return ret; } + kvm_irqchip_commit_routes(kvm_state); } } @@ -1112,24 +1080,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); @@ -1624,8 +1574,8 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp) { VirtIOPCIProxy *proxy = VIRTIO_PCI(d); VirtioBusState *bus = &proxy->bus; - bool legacy = !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_LEGACY); - bool modern = !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_MODERN); + bool legacy = virtio_pci_legacy(proxy); + bool modern = virtio_pci_modern(proxy); bool modern_pio = proxy->flags & VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY; uint8_t *config; uint32_t size; @@ -1744,7 +1694,7 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp) static void virtio_pci_device_unplugged(DeviceState *d) { VirtIOPCIProxy *proxy = VIRTIO_PCI(d); - bool modern = !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_MODERN); + bool modern = virtio_pci_modern(proxy); bool modern_pio = proxy->flags & VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY; virtio_pci_stop_ioeventfd(proxy); @@ -1764,6 +1714,8 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) { VirtIOPCIProxy *proxy = VIRTIO_PCI(pci_dev); VirtioPCIClass *k = VIRTIO_PCI_GET_CLASS(pci_dev); + bool pcie_port = pci_bus_is_express(pci_dev->bus) && + !pci_bus_is_root(pci_dev->bus); /* * virtio pci bar layout used by default. @@ -1814,8 +1766,11 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) address_space_init(&proxy->modern_as, &proxy->modern_cfg, "virtio-pci-cfg-as"); - if (pci_is_express(pci_dev) && pci_bus_is_express(pci_dev->bus) && - !pci_bus_is_root(pci_dev->bus)) { + if (proxy->disable_legacy == ON_OFF_AUTO_AUTO) { + proxy->disable_legacy = pcie_port ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; + } + + if (pcie_port && pci_is_express(pci_dev)) { int pos; pos = pcie_endpoint_cap_init(pci_dev, 0); @@ -1869,10 +1824,9 @@ static void virtio_pci_reset(DeviceState *qdev) static Property virtio_pci_properties[] = { DEFINE_PROP_BIT("virtio-pci-bus-master-bug-migration", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION_BIT, false), - DEFINE_PROP_BIT("disable-legacy", VirtIOPCIProxy, flags, - VIRTIO_PCI_FLAG_DISABLE_LEGACY_BIT, false), - DEFINE_PROP_BIT("disable-modern", VirtIOPCIProxy, flags, - VIRTIO_PCI_FLAG_DISABLE_MODERN_BIT, true), + DEFINE_PROP_ON_OFF_AUTO("disable-legacy", VirtIOPCIProxy, disable_legacy, + ON_OFF_AUTO_AUTO), + DEFINE_PROP_BOOL("disable-modern", VirtIOPCIProxy, disable_modern, false), DEFINE_PROP_BIT("migrate-extra", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_MIGRATE_EXTRA_BIT, true), DEFINE_PROP_BIT("modern-pio-notify", VirtIOPCIProxy, flags, @@ -1889,7 +1843,7 @@ static void virtio_pci_dc_realize(DeviceState *qdev, Error **errp) PCIDevice *pci_dev = &proxy->pci_dev; if (!(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_PCIE) && - !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_MODERN)) { + virtio_pci_modern(proxy)) { pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS; } @@ -2351,9 +2305,7 @@ static void virtio_input_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) DeviceState *vdev = DEVICE(&vinput->vdev); qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); - /* force virtio-1.0 */ - vpci_dev->flags &= ~VIRTIO_PCI_FLAG_DISABLE_MODERN; - vpci_dev->flags |= VIRTIO_PCI_FLAG_DISABLE_LEGACY; + virtio_pci_force_virtio_1(vpci_dev); object_property_set_bool(OBJECT(vdev), true, "realized", errp); } @@ -2490,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 = { diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h index e4548c2f9..25fbf8a37 100644 --- a/hw/virtio/virtio-pci.h +++ b/hw/virtio/virtio-pci.h @@ -61,8 +61,6 @@ typedef struct VirtioBusClass VirtioPCIBusClass; enum { VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION_BIT, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, - VIRTIO_PCI_FLAG_DISABLE_LEGACY_BIT, - VIRTIO_PCI_FLAG_DISABLE_MODERN_BIT, VIRTIO_PCI_FLAG_MIGRATE_EXTRA_BIT, VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY_BIT, VIRTIO_PCI_FLAG_DISABLE_PCIE_BIT, @@ -77,8 +75,6 @@ enum { #define VIRTIO_PCI_FLAG_USE_IOEVENTFD (1 << VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT) /* virtio version flags */ -#define VIRTIO_PCI_FLAG_DISABLE_LEGACY (1 << VIRTIO_PCI_FLAG_DISABLE_LEGACY_BIT) -#define VIRTIO_PCI_FLAG_DISABLE_MODERN (1 << VIRTIO_PCI_FLAG_DISABLE_MODERN_BIT) #define VIRTIO_PCI_FLAG_DISABLE_PCIE (1 << VIRTIO_PCI_FLAG_DISABLE_PCIE_BIT) /* migrate extra state */ @@ -144,6 +140,8 @@ struct VirtIOPCIProxy { uint32_t modern_mem_bar; int config_cap; uint32_t flags; + bool disable_modern; + OnOffAuto disable_legacy; uint32_t class_code; uint32_t nvectors; uint32_t dfselect; @@ -158,6 +156,21 @@ struct VirtIOPCIProxy { VirtioBusState bus; }; +static inline bool virtio_pci_modern(VirtIOPCIProxy *proxy) +{ + return !proxy->disable_modern; +} + +static inline bool virtio_pci_legacy(VirtIOPCIProxy *proxy) +{ + return proxy->disable_legacy == ON_OFF_AUTO_OFF; +} + +static inline void virtio_pci_force_virtio_1(VirtIOPCIProxy *proxy) +{ + proxy->disable_modern = false; + proxy->disable_legacy = ON_OFF_AUTO_ON; +} /* * virtio-scsi-pci: This extends VirtioPCIProxy. diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c index 6b991a764..cd8ca1017 100644 --- a/hw/virtio/virtio-rng.c +++ b/hw/virtio/virtio-rng.c @@ -120,22 +120,12 @@ static uint64_t get_features(VirtIODevice *vdev, uint64_t f, Error **errp) return f; } -static void virtio_rng_save(QEMUFile *f, void *opaque) -{ - VirtIODevice *vdev = opaque; - - virtio_save(vdev, f); -} - -static int virtio_rng_load(QEMUFile *f, void *opaque, int version_id) +static int virtio_rng_load(QEMUFile *f, void *opaque, size_t size) { VirtIORNG *vrng = opaque; int ret; - if (version_id != 1) { - return -EINVAL; - } - ret = virtio_load(VIRTIO_DEVICE(vrng), f, version_id); + ret = virtio_load(VIRTIO_DEVICE(vrng), f, 1); if (ret != 0) { return ret; } @@ -214,8 +204,6 @@ static void virtio_rng_device_realize(DeviceState *dev, Error **errp) vrng->rate_limit_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, check_rate_limit, vrng); vrng->activate_timer = true; - register_savevm(dev, "virtio-rng", -1, 1, virtio_rng_save, - virtio_rng_load, vrng); } static void virtio_rng_device_unrealize(DeviceState *dev, Error **errp) @@ -225,10 +213,11 @@ static void virtio_rng_device_unrealize(DeviceState *dev, Error **errp) timer_del(vrng->rate_limit_timer); timer_free(vrng->rate_limit_timer); - unregister_savevm(dev, "virtio-rng", vrng); virtio_cleanup(vdev); } +VMSTATE_VIRTIO_DEVICE(rng, 1, virtio_rng_load, virtio_vmstate_save); + static Property virtio_rng_properties[] = { /* Set a default rate limit of 2^47 bytes per minute or roughly 2TB/s. If * you have an entropy source capable of generating more entropy than this @@ -246,6 +235,7 @@ static void virtio_rng_class_init(ObjectClass *klass, void *data) VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); dc->props = virtio_rng_properties; + dc->vmsd = &vmstate_virtio_rng; set_bit(DEVICE_CATEGORY_MISC, dc->categories); vdc->realize = virtio_rng_device_realize; vdc->unrealize = virtio_rng_device_unrealize; diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 30ede3d1c..74c085c74 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -95,8 +95,9 @@ struct VirtQueue int inuse; uint16_t vector; - void (*handle_output)(VirtIODevice *vdev, VirtQueue *vq); - void (*handle_aio_output)(VirtIODevice *vdev, VirtQueue *vq); + VirtIOHandleOutput handle_output; + VirtIOHandleOutput handle_aio_output; + bool use_aio; VirtIODevice *vdev; EventNotifier guest_notifier; EventNotifier host_notifier; @@ -267,6 +268,7 @@ void virtqueue_discard(VirtQueue *vq, const VirtQueueElement *elem, unsigned int len) { vq->last_avail_idx--; + vq->inuse--; virtqueue_unmap_sg(vq, elem, len); } @@ -457,6 +459,11 @@ static void virtqueue_map_desc(unsigned int *p_num_sg, hwaddr *addr, struct iove unsigned num_sg = *p_num_sg; assert(num_sg <= max_num_sg); + if (!sz) { + error_report("virtio: zero sized buffers are not allowed"); + exit(1); + } + while (sz) { hwaddr len = sz; @@ -561,6 +568,11 @@ void *virtqueue_pop(VirtQueue *vq, size_t sz) max = vq->vring.num; + if (vq->inuse >= vq->vring.num) { + error_report("Virtqueue size exceeded"); + exit(1); + } + i = head = virtqueue_get_head(vq, vq->last_avail_idx++); if (virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { vring_set_avail_event(vq, vq->last_avail_idx); @@ -1062,13 +1074,6 @@ int virtio_get_num_queues(VirtIODevice *vdev) return i; } -int virtio_queue_get_id(VirtQueue *vq) -{ - VirtIODevice *vdev = vq->vdev; - assert(vq >= &vdev->vq[0] && vq < &vdev->vq[VIRTIO_QUEUE_MAX]); - return vq - &vdev->vq[0]; -} - void virtio_queue_set_align(VirtIODevice *vdev, int n, int align) { BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); @@ -1137,8 +1142,9 @@ void virtio_queue_set_vector(VirtIODevice *vdev, int n, uint16_t vector) } } -VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size, - void (*handle_output)(VirtIODevice *, VirtQueue *)) +static VirtQueue *virtio_add_queue_internal(VirtIODevice *vdev, int queue_size, + VirtIOHandleOutput handle_output, + bool use_aio) { int i; @@ -1155,10 +1161,28 @@ VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size, vdev->vq[i].vring.align = VIRTIO_PCI_VRING_ALIGN; vdev->vq[i].handle_output = handle_output; vdev->vq[i].handle_aio_output = NULL; + vdev->vq[i].use_aio = use_aio; return &vdev->vq[i]; } +/* Add a virt queue and mark AIO. + * An AIO queue will use the AioContext based event interface instead of the + * default IOHandler and EventNotifier interface. + */ +VirtQueue *virtio_add_queue_aio(VirtIODevice *vdev, int queue_size, + VirtIOHandleOutput handle_output) +{ + return virtio_add_queue_internal(vdev, queue_size, handle_output, true); +} + +/* Add a normal virt queue (on the contrary to the AIO version above. */ +VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size, + VirtIOHandleOutput handle_output) +{ + return virtio_add_queue_internal(vdev, queue_size, handle_output, false); +} + void virtio_del_queue(VirtIODevice *vdev, int n) { if (n < 0 || n >= VIRTIO_QUEUE_MAX) { @@ -1451,6 +1475,12 @@ void virtio_save(VirtIODevice *vdev, QEMUFile *f) vmstate_save_state(f, &vmstate_virtio, vdev, NULL); } +/* A wrapper for use as a VMState .put function */ +void virtio_vmstate_save(QEMUFile *f, void *opaque, size_t size) +{ + virtio_save(VIRTIO_DEVICE(opaque), f); +} + static int virtio_set_features_nocheck(VirtIODevice *vdev, uint64_t val) { VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); @@ -1506,6 +1536,16 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) } qemu_get_be32s(f, &features); + /* + * Temporarily set guest_features low bits - needed by + * virtio net load code testing for VIRTIO_NET_F_CTRL_GUEST_OFFLOADS + * VIRTIO_NET_F_GUEST_ANNOUNCE and VIRTIO_NET_F_CTRL_VQ. + * + * Note: devices should always test host features in future - don't create + * new dependencies like this. + */ + vdev->guest_features = features; + config_len = qemu_get_be32(f); /* @@ -1609,6 +1649,21 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) } vdev->vq[i].used_idx = vring_used_idx(&vdev->vq[i]); vdev->vq[i].shadow_avail_idx = vring_avail_idx(&vdev->vq[i]); + + /* + * Some devices migrate VirtQueueElements that have been popped + * from the avail ring but not yet returned to the used ring. + */ + vdev->vq[i].inuse = vdev->vq[i].last_avail_idx - + vdev->vq[i].used_idx; + if (vdev->vq[i].inuse > vdev->vq[i].vring.num) { + error_report("VQ %d size 0x%x < last_avail_idx 0x%x - " + "used_idx 0x%x", + i, vdev->vq[i].vring.num, + vdev->vq[i].last_avail_idx, + vdev->vq[i].used_idx); + return -1; + } } } @@ -1801,8 +1856,7 @@ static void virtio_queue_host_notifier_aio_read(EventNotifier *n) } void virtio_queue_aio_set_host_notifier_handler(VirtQueue *vq, AioContext *ctx, - void (*handle_output)(VirtIODevice *, - VirtQueue *)) + VirtIOHandleOutput handle_output) { if (handle_output) { vq->handle_aio_output = handle_output; @@ -1828,11 +1882,21 @@ static void virtio_queue_host_notifier_read(EventNotifier *n) void virtio_queue_set_host_notifier_fd_handler(VirtQueue *vq, bool assign, bool set_handler) { + AioContext *ctx = qemu_get_aio_context(); if (assign && set_handler) { - event_notifier_set_handler(&vq->host_notifier, true, + if (vq->use_aio) { + aio_set_event_notifier(ctx, &vq->host_notifier, true, virtio_queue_host_notifier_read); + } else { + event_notifier_set_handler(&vq->host_notifier, true, + virtio_queue_host_notifier_read); + } } else { - event_notifier_set_handler(&vq->host_notifier, true, NULL); + if (vq->use_aio) { + aio_set_event_notifier(ctx, &vq->host_notifier, true, NULL); + } else { + event_notifier_set_handler(&vq->host_notifier, true, NULL); + } } if (!assign) { /* Test and clear notifier before after disabling event, diff --git a/hw/watchdog/watchdog.c b/hw/watchdog/watchdog.c index bbf3646ba..2aeaf1fbc 100644 --- a/hw/watchdog/watchdog.c +++ b/hw/watchdog/watchdog.c @@ -143,7 +143,7 @@ void watchdog_perform_action(void) case WDT_NMI: qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_INJECT_NMI, &error_abort); - inject_nmi(); + nmi_monitor_handle(0, NULL); break; } } diff --git a/hw/watchdog/wdt_diag288.c b/hw/watchdog/wdt_diag288.c index f54a35a0e..a7b64e2c4 100644 --- a/hw/watchdog/wdt_diag288.c +++ b/hw/watchdog/wdt_diag288.c @@ -16,6 +16,7 @@ #include "hw/sysbus.h" #include "qemu/timer.h" #include "hw/watchdog/wdt_diag288.h" +#include "qemu/log.h" static WatchdogTimerModel model = { .wdt_name = TYPE_WDT_DIAG288, diff --git a/hw/xen/xen-host-pci-device.h b/hw/xen/xen-host-pci-device.h index 6acf36e13..4d8d34ecb 100644 --- a/hw/xen/xen-host-pci-device.h +++ b/hw/xen/xen-host-pci-device.h @@ -55,4 +55,4 @@ int xen_host_pci_set_block(XenHostPCIDevice *d, int pos, uint8_t *buf, int xen_host_pci_find_ext_cap_offset(XenHostPCIDevice *s, uint32_t cap); -#endif /* !XEN_HOST_PCI_DEVICE_H_ */ +#endif /* XEN_HOST_PCI_DEVICE_H */ diff --git a/hw/xen/xen_backend.c b/hw/xen/xen_backend.c index 60575ad38..69a238817 100644 --- a/hw/xen/xen_backend.c +++ b/hw/xen/xen_backend.c @@ -23,16 +23,20 @@ */ #include "qemu/osdep.h" -#include <sys/mman.h> #include <sys/signal.h> #include "hw/hw.h" +#include "hw/sysbus.h" #include "sysemu/char.h" #include "qemu/log.h" #include "hw/xen/xen_backend.h" #include <xen/grant_table.h> +#define TYPE_XENSYSDEV "xensysdev" + +DeviceState *xen_sysdev; + /* ------------------------------------------------------------- */ /* public */ @@ -42,11 +46,36 @@ struct xs_handle *xenstore = NULL; const char *xen_protocol; /* private */ +struct xs_dirs { + char *xs_dir; + QTAILQ_ENTRY(xs_dirs) list; +}; +static QTAILQ_HEAD(xs_dirs_head, xs_dirs) xs_cleanup = + QTAILQ_HEAD_INITIALIZER(xs_cleanup); + static QTAILQ_HEAD(XenDeviceHead, XenDevice) xendevs = QTAILQ_HEAD_INITIALIZER(xendevs); static int debug = 0; /* ------------------------------------------------------------- */ +static void xenstore_cleanup_dir(char *dir) +{ + struct xs_dirs *d; + + d = g_malloc(sizeof(*d)); + d->xs_dir = dir; + QTAILQ_INSERT_TAIL(&xs_cleanup, d, list); +} + +void xen_config_cleanup(void) +{ + struct xs_dirs *d; + + QTAILQ_FOREACH(d, &xs_cleanup, list) { + xs_rm(xenstore, 0, d->xs_dir); + } +} + int xenstore_write_str(const char *base, const char *node, const char *val) { char abspath[XEN_BUFSIZE]; @@ -75,6 +104,30 @@ char *xenstore_read_str(const char *base, const char *node) return ret; } +int xenstore_mkdir(char *path, int p) +{ + struct xs_permissions perms[2] = { + { + .id = 0, /* set owner: dom0 */ + }, { + .id = xen_domid, + .perms = p, + } + }; + + if (!xs_mkdir(xenstore, 0, path)) { + xen_be_printf(NULL, 0, "xs_mkdir %s: failed\n", path); + return -1; + } + xenstore_cleanup_dir(g_strdup(path)); + + if (!xs_set_permissions(xenstore, 0, path, perms, 2)) { + xen_be_printf(NULL, 0, "xs_set_permissions %s: failed\n", path); + return -1; + } + return 0; +} + int xenstore_write_int(const char *base, const char *node, int ival) { char val[12]; @@ -268,48 +321,28 @@ static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev, /* * release xen backend device. */ -static struct XenDevice *xen_be_del_xendev(int dom, int dev) +static void xen_be_del_xendev(struct XenDevice *xendev) { - struct XenDevice *xendev, *xnext; - - /* - * This is pretty much like QTAILQ_FOREACH(xendev, &xendevs, next) but - * we save the next pointer in xnext because we might free xendev. - */ - xnext = xendevs.tqh_first; - while (xnext) { - xendev = xnext; - xnext = xendev->next.tqe_next; - - if (xendev->dom != dom) { - continue; - } - if (xendev->dev != dev && dev != -1) { - continue; - } - - if (xendev->ops->free) { - xendev->ops->free(xendev); - } - - if (xendev->fe) { - char token[XEN_BUFSIZE]; - snprintf(token, sizeof(token), "fe:%p", xendev); - xs_unwatch(xenstore, xendev->fe, token); - g_free(xendev->fe); - } + if (xendev->ops->free) { + xendev->ops->free(xendev); + } - if (xendev->evtchndev != NULL) { - xenevtchn_close(xendev->evtchndev); - } - if (xendev->gnttabdev != NULL) { - xengnttab_close(xendev->gnttabdev); - } + if (xendev->fe) { + char token[XEN_BUFSIZE]; + snprintf(token, sizeof(token), "fe:%p", xendev); + xs_unwatch(xenstore, xendev->fe, token); + g_free(xendev->fe); + } - QTAILQ_REMOVE(&xendevs, xendev, next); - g_free(xendev); + if (xendev->evtchndev != NULL) { + xenevtchn_close(xendev->evtchndev); } - return NULL; + if (xendev->gnttabdev != NULL) { + xengnttab_close(xendev->gnttabdev); + } + + QTAILQ_REMOVE(&xendevs, xendev, next); + g_free(xendev); } /* @@ -629,7 +662,7 @@ static void xenstore_update_be(char *watch, char *type, int dom, if (xendev != NULL) { bepath = xs_read(xenstore, 0, xendev->be, &len); if (bepath == NULL) { - xen_be_del_xendev(dom, dev); + xen_be_del_xendev(xendev); } else { free(bepath); xen_be_backend_changed(xendev, path); @@ -714,6 +747,10 @@ int xen_be_init(void) /* Check if xen_init() have been called */ goto err; } + + xen_sysdev = qdev_create(NULL, TYPE_XENSYSDEV); + qdev_init_nofail(xen_sysdev); + return 0; err: @@ -726,9 +763,33 @@ err: int xen_be_register(const char *type, struct XenDevOps *ops) { + char path[50]; + int rc; + + if (ops->backend_register) { + rc = ops->backend_register(); + if (rc) { + return rc; + } + } + + snprintf(path, sizeof(path), "device-model/%u/backends/%s", xen_domid, + type); + xenstore_mkdir(path, XS_PERM_NONE); + return xenstore_scan(type, xen_domid, ops); } +void xen_be_register_common(void) +{ + xen_be_register("console", &xen_console_ops); + xen_be_register("vkbd", &xen_kbdmouse_ops); + xen_be_register("qdisk", &xen_blkdev_ops); +#ifdef CONFIG_USB_LIBUSB + xen_be_register("qusb", &xen_usb_ops); +#endif +} + int xen_be_bind_evtchn(struct XenDevice *xendev) { if (xendev->local_port != -1) { @@ -800,3 +861,35 @@ void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ... } qemu_log_flush(); } + +static int xen_sysdev_init(SysBusDevice *dev) +{ + return 0; +} + +static Property xen_sysdev_properties[] = { + {/* end of property list */}, +}; + +static void xen_sysdev_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = xen_sysdev_init; + dc->props = xen_sysdev_properties; +} + +static const TypeInfo xensysdev_info = { + .name = TYPE_XENSYSDEV, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SysBusDevice), + .class_init = xen_sysdev_class_init, +}; + +static void xenbe_register_types(void) +{ + type_register_static(&xensysdev_info); +} + +type_init(xenbe_register_types); diff --git a/hw/xen/xen_devconfig.c b/hw/xen/xen_devconfig.c index 1f30fe4f5..b7d290df6 100644 --- a/hw/xen/xen_devconfig.c +++ b/hw/xen/xen_devconfig.c @@ -5,54 +5,6 @@ /* ------------------------------------------------------------- */ -struct xs_dirs { - char *xs_dir; - QTAILQ_ENTRY(xs_dirs) list; -}; -static QTAILQ_HEAD(xs_dirs_head, xs_dirs) xs_cleanup = QTAILQ_HEAD_INITIALIZER(xs_cleanup); - -static void xen_config_cleanup_dir(char *dir) -{ - struct xs_dirs *d; - - d = g_malloc(sizeof(*d)); - d->xs_dir = dir; - QTAILQ_INSERT_TAIL(&xs_cleanup, d, list); -} - -void xen_config_cleanup(void) -{ - struct xs_dirs *d; - - QTAILQ_FOREACH(d, &xs_cleanup, list) { - xs_rm(xenstore, 0, d->xs_dir); - } -} - -/* ------------------------------------------------------------- */ - -static int xen_config_dev_mkdir(char *dev, int p) -{ - struct xs_permissions perms[2] = {{ - .id = 0, /* set owner: dom0 */ - },{ - .id = xen_domid, - .perms = p, - }}; - - if (!xs_mkdir(xenstore, 0, dev)) { - xen_be_printf(NULL, 0, "xs_mkdir %s: failed\n", dev); - return -1; - } - xen_config_cleanup_dir(g_strdup(dev)); - - if (!xs_set_permissions(xenstore, 0, dev, perms, 2)) { - xen_be_printf(NULL, 0, "xs_set_permissions %s: failed\n", dev); - return -1; - } - return 0; -} - static int xen_config_dev_dirs(const char *ftype, const char *btype, int vdev, char *fe, char *be, int len) { @@ -66,8 +18,8 @@ static int xen_config_dev_dirs(const char *ftype, const char *btype, int vdev, snprintf(be, len, "%s/backend/%s/%d/%d", dom, btype, xen_domid, vdev); free(dom); - xen_config_dev_mkdir(fe, XS_PERM_READ | XS_PERM_WRITE); - xen_config_dev_mkdir(be, XS_PERM_READ); + xenstore_mkdir(fe, XS_PERM_READ | XS_PERM_WRITE); + xenstore_mkdir(be, XS_PERM_READ); return 0; } diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c index f593b046e..b6d71bb52 100644 --- a/hw/xen/xen_pt.c +++ b/hw/xen/xen_pt.c @@ -842,7 +842,7 @@ static void xen_pt_realize(PCIDevice *d, Error **errp) goto err_out; } if (!scratch) { - error_setg(errp, "no pin interrupt"); + XEN_PT_LOG(d, "no pin interrupt\n"); goto out; } diff --git a/hw/xen/xen_pt.h b/hw/xen/xen_pt.h index c2f8e1fc2..191d9caea 100644 --- a/hw/xen/xen_pt.h +++ b/hw/xen/xen_pt.h @@ -332,4 +332,4 @@ int xen_pt_register_vga_regions(XenHostPCIDevice *dev); int xen_pt_unregister_vga_regions(XenHostPCIDevice *dev); void xen_pt_setup_vga(XenPCIPassthroughState *s, XenHostPCIDevice *dev, Error **errp); -#endif /* !XEN_PT_H */ +#endif /* XEN_PT_H */ diff --git a/hw/xen/xen_pt_config_init.c b/hw/xen/xen_pt_config_init.c index 9869ffda0..6f18366f6 100644 --- a/hw/xen/xen_pt_config_init.c +++ b/hw/xen/xen_pt_config_init.c @@ -2049,9 +2049,8 @@ void xen_pt_config_init(XenPCIPassthroughState *s, Error **errp) for (j = 0; regs->size != 0; j++, regs++) { xen_pt_config_reg_init(s, reg_grp_entry, regs, &err); if (err) { - error_append_hint(&err, "Failed to initialize %d/%zu" - " reg 0x%x in grp_type = 0x%x (%d/%zu)", - j, ARRAY_SIZE(xen_pt_emu_reg_grps[i].emu_regs), + error_append_hint(&err, "Failed to init register %d" + " offsets 0x%x in grp_type = 0x%x (%d/%zu)", j, regs->offset, xen_pt_emu_reg_grps[i].grp_type, i, ARRAY_SIZE(xen_pt_emu_reg_grps)); error_propagate(errp, err); diff --git a/hw/xen/xen_pt_msi.c b/hw/xen/xen_pt_msi.c index 9a16f2bff..62add0639 100644 --- a/hw/xen/xen_pt_msi.c +++ b/hw/xen/xen_pt_msi.c @@ -10,7 +10,6 @@ */ #include "qemu/osdep.h" -#include <sys/mman.h> #include "hw/xen/xen_backend.h" #include "xen_pt.h" diff --git a/hw/xenpv/xen_domainbuild.h b/hw/xenpv/xen_domainbuild.h index 29a91ea7b..652d9b410 100644 --- a/hw/xenpv/xen_domainbuild.h +++ b/hw/xenpv/xen_domainbuild.h @@ -1,5 +1,5 @@ #ifndef QEMU_HW_XEN_DOMAINBUILD_H -#define QEMU_HW_XEN_DOMAINBUILD_H 1 +#define QEMU_HW_XEN_DOMAINBUILD_H #include "hw/xen/xen_common.h" diff --git a/hw/xenpv/xen_machine_pv.c b/hw/xenpv/xen_machine_pv.c index fc1353599..79aef4ecc 100644 --- a/hw/xenpv/xen_machine_pv.c +++ b/hw/xenpv/xen_machine_pv.c @@ -67,10 +67,8 @@ static void xen_init_pv(MachineState *machine) break; } - xen_be_register("console", &xen_console_ops); - xen_be_register("vkbd", &xen_kbdmouse_ops); + xen_be_register_common(); xen_be_register("vfb", &xen_framebuffer_ops); - xen_be_register("qdisk", &xen_blkdev_ops); xen_be_register("qnic", &xen_netdev_ops); /* configure framebuffer */ diff --git a/hw/xtensa/bootparam.h b/hw/xtensa/bootparam.h index 955f4e86e..ade7891ec 100644 --- a/hw/xtensa/bootparam.h +++ b/hw/xtensa/bootparam.h @@ -1,5 +1,5 @@ -#ifndef HW_XTENSA_BOOTPARAM -#define HW_XTENSA_BOOTPARAM +#ifndef HW_XTENSA_BOOTPARAM_H +#define HW_XTENSA_BOOTPARAM_H #define BP_TAG_COMMAND_LINE 0x1001 /* command line (0-terminated string)*/ #define BP_TAG_INITRD 0x1002 /* ramdisk addr and size (bp_meminfo) */ diff --git a/hw/xtensa/pic_cpu.c b/hw/xtensa/pic_cpu.c index c835bd009..2bed64f15 100644 --- a/hw/xtensa/pic_cpu.c +++ b/hw/xtensa/pic_cpu.c @@ -26,6 +26,7 @@ */ #include "qemu/osdep.h" +#include "cpu.h" #include "hw/hw.h" #include "qemu/log.h" #include "qemu/timer.h" @@ -121,8 +122,8 @@ void xtensa_rearm_ccompare_timer(CPUXtensaState *env) } env->wake_ccount = wake_ccount; timer_mod(env->ccompare_timer, env->halt_clock + - muldiv64(wake_ccount - env->sregs[CCOUNT], - 1000000, env->config->clock_freq_khz)); + (uint64_t)(wake_ccount - env->sregs[CCOUNT]) * + 1000000 / env->config->clock_freq_khz); } static void xtensa_ccompare_cb(void *opaque) diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c index 2d117369a..ac7594948 100644 --- a/hw/xtensa/xtfpga.c +++ b/hw/xtensa/xtfpga.c @@ -165,7 +165,7 @@ static pflash_t *xtfpga_flash_init(MemoryRegion *address_space, qdev_prop_set_uint32(dev, "num-blocks", board->flash_size / board->flash_sector_size); qdev_prop_set_uint64(dev, "sector-length", board->flash_sector_size); - qdev_prop_set_uint8(dev, "width", 4); + qdev_prop_set_uint8(dev, "width", 2); qdev_prop_set_bit(dev, "big-endian", be); qdev_prop_set_string(dev, "name", "lx60.io.flash"); qdev_init_nofail(dev); |