diff options
Diffstat (limited to 'hw')
133 files changed, 7080 insertions, 3419 deletions
diff --git a/hw/9pfs/codir.c b/hw/9pfs/codir.c index 72732e7c53..9b6d47d91d 100644 --- a/hw/9pfs/codir.c +++ b/hw/9pfs/codir.c @@ -29,7 +29,7 @@ int v9fs_co_readdir_r(V9fsPDU *pdu, V9fsFidState *fidp, struct dirent *dent, v9fs_co_run_in_worker( { errno = 0; - err = s->ops->readdir_r(&s->ctx, fidp->fs.dir, dent, result); + err = s->ops->readdir_r(&s->ctx, &fidp->fs, dent, result); if (!*result && errno) { err = -errno; } else { @@ -49,7 +49,7 @@ off_t v9fs_co_telldir(V9fsPDU *pdu, V9fsFidState *fidp) } v9fs_co_run_in_worker( { - err = s->ops->telldir(&s->ctx, fidp->fs.dir); + err = s->ops->telldir(&s->ctx, &fidp->fs); if (err < 0) { err = -errno; } @@ -65,7 +65,7 @@ void v9fs_co_seekdir(V9fsPDU *pdu, V9fsFidState *fidp, off_t offset) } v9fs_co_run_in_worker( { - s->ops->seekdir(&s->ctx, fidp->fs.dir, offset); + s->ops->seekdir(&s->ctx, &fidp->fs, offset); }); } @@ -77,7 +77,7 @@ void v9fs_co_rewinddir(V9fsPDU *pdu, V9fsFidState *fidp) } v9fs_co_run_in_worker( { - s->ops->rewinddir(&s->ctx, fidp->fs.dir); + s->ops->rewinddir(&s->ctx, &fidp->fs); }); } @@ -129,8 +129,8 @@ int v9fs_co_opendir(V9fsPDU *pdu, V9fsFidState *fidp) v9fs_path_read_lock(s); v9fs_co_run_in_worker( { - fidp->fs.dir = s->ops->opendir(&s->ctx, &fidp->path); - if (!fidp->fs.dir) { + err = s->ops->opendir(&s->ctx, &fidp->path, &fidp->fs); + if (err < 0) { err = -errno; } else { err = 0; @@ -146,7 +146,7 @@ int v9fs_co_opendir(V9fsPDU *pdu, V9fsFidState *fidp) return err; } -int v9fs_co_closedir(V9fsPDU *pdu, DIR *dir) +int v9fs_co_closedir(V9fsPDU *pdu, V9fsFidOpenState *fs) { int err; V9fsState *s = pdu->s; @@ -156,7 +156,7 @@ int v9fs_co_closedir(V9fsPDU *pdu, DIR *dir) } v9fs_co_run_in_worker( { - err = s->ops->closedir(&s->ctx, dir); + err = s->ops->closedir(&s->ctx, fs); if (err < 0) { err = -errno; } diff --git a/hw/9pfs/cofile.c b/hw/9pfs/cofile.c index 7ad4bec005..586b0382f6 100644 --- a/hw/9pfs/cofile.c +++ b/hw/9pfs/cofile.c @@ -17,6 +17,30 @@ #include "qemu-coroutine.h" #include "virtio-9p-coth.h" +int v9fs_co_st_gen(V9fsPDU *pdu, V9fsPath *path, mode_t st_mode, + V9fsStatDotl *v9stat) +{ + int err = 0; + V9fsState *s = pdu->s; + + if (v9fs_request_cancelled(pdu)) { + return -EINTR; + } + if (s->ctx.exops.get_st_gen) { + v9fs_path_read_lock(s); + v9fs_co_run_in_worker( + { + err = s->ctx.exops.get_st_gen(&s->ctx, path, st_mode, + &v9stat->st_gen); + if (err < 0) { + err = -errno; + } + }); + v9fs_path_unlock(s); + } + return err; +} + int v9fs_co_lstat(V9fsPDU *pdu, V9fsPath *path, struct stat *stbuf) { int err; @@ -37,7 +61,7 @@ int v9fs_co_lstat(V9fsPDU *pdu, V9fsPath *path, struct stat *stbuf) return err; } -int v9fs_co_fstat(V9fsPDU *pdu, int fd, struct stat *stbuf) +int v9fs_co_fstat(V9fsPDU *pdu, V9fsFidState *fidp, struct stat *stbuf) { int err; V9fsState *s = pdu->s; @@ -47,7 +71,7 @@ int v9fs_co_fstat(V9fsPDU *pdu, int fd, struct stat *stbuf) } v9fs_co_run_in_worker( { - err = s->ops->fstat(&s->ctx, fd, stbuf); + err = s->ops->fstat(&s->ctx, &fidp->fs, stbuf); if (err < 0) { err = -errno; } @@ -66,8 +90,8 @@ int v9fs_co_open(V9fsPDU *pdu, V9fsFidState *fidp, int flags) v9fs_path_read_lock(s); v9fs_co_run_in_worker( { - fidp->fs.fd = s->ops->open(&s->ctx, &fidp->path, flags); - if (fidp->fs.fd == -1) { + err = s->ops->open(&s->ctx, &fidp->path, flags, &fidp->fs); + if (err == -1) { err = -errno; } else { err = 0; @@ -106,9 +130,9 @@ int v9fs_co_open2(V9fsPDU *pdu, V9fsFidState *fidp, V9fsString *name, gid_t gid, v9fs_path_read_lock(s); v9fs_co_run_in_worker( { - fidp->fs.fd = s->ops->open2(&s->ctx, &fidp->path, - name->data, flags, &cred); - if (fidp->fs.fd == -1) { + err = s->ops->open2(&s->ctx, &fidp->path, + name->data, flags, &cred, &fidp->fs); + if (err < 0) { err = -errno; } else { v9fs_path_init(&path); @@ -117,12 +141,12 @@ int v9fs_co_open2(V9fsPDU *pdu, V9fsFidState *fidp, V9fsString *name, gid_t gid, err = s->ops->lstat(&s->ctx, &path, stbuf); if (err < 0) { err = -errno; - s->ops->close(&s->ctx, fidp->fs.fd); + s->ops->close(&s->ctx, &fidp->fs); } else { v9fs_path_copy(&fidp->path, &path); } } else { - s->ops->close(&s->ctx, fidp->fs.fd); + s->ops->close(&s->ctx, &fidp->fs); } v9fs_path_free(&path); } @@ -137,7 +161,7 @@ int v9fs_co_open2(V9fsPDU *pdu, V9fsFidState *fidp, V9fsString *name, gid_t gid, return err; } -int v9fs_co_close(V9fsPDU *pdu, int fd) +int v9fs_co_close(V9fsPDU *pdu, V9fsFidOpenState *fs) { int err; V9fsState *s = pdu->s; @@ -147,7 +171,7 @@ int v9fs_co_close(V9fsPDU *pdu, int fd) } v9fs_co_run_in_worker( { - err = s->ops->close(&s->ctx, fd); + err = s->ops->close(&s->ctx, fs); if (err < 0) { err = -errno; } @@ -160,16 +184,15 @@ int v9fs_co_close(V9fsPDU *pdu, int fd) int v9fs_co_fsync(V9fsPDU *pdu, V9fsFidState *fidp, int datasync) { - int fd, err; + int err; V9fsState *s = pdu->s; if (v9fs_request_cancelled(pdu)) { return -EINTR; } - fd = fidp->fs.fd; v9fs_co_run_in_worker( { - err = s->ops->fsync(&s->ctx, fd, datasync); + err = s->ops->fsync(&s->ctx, &fidp->fs, datasync); if (err < 0) { err = -errno; } @@ -202,16 +225,15 @@ int v9fs_co_link(V9fsPDU *pdu, V9fsFidState *oldfid, int v9fs_co_pwritev(V9fsPDU *pdu, V9fsFidState *fidp, struct iovec *iov, int iovcnt, int64_t offset) { - int fd, err; + int err; V9fsState *s = pdu->s; if (v9fs_request_cancelled(pdu)) { return -EINTR; } - fd = fidp->fs.fd; v9fs_co_run_in_worker( { - err = s->ops->pwritev(&s->ctx, fd, iov, iovcnt, offset); + err = s->ops->pwritev(&s->ctx, &fidp->fs, iov, iovcnt, offset); if (err < 0) { err = -errno; } @@ -222,16 +244,15 @@ int v9fs_co_pwritev(V9fsPDU *pdu, V9fsFidState *fidp, int v9fs_co_preadv(V9fsPDU *pdu, V9fsFidState *fidp, struct iovec *iov, int iovcnt, int64_t offset) { - int fd, err; + int err; V9fsState *s = pdu->s; if (v9fs_request_cancelled(pdu)) { return -EINTR; } - fd = fidp->fs.fd; v9fs_co_run_in_worker( { - err = s->ops->preadv(&s->ctx, fd, iov, iovcnt, offset); + err = s->ops->preadv(&s->ctx, &fidp->fs, iov, iovcnt, offset); if (err < 0) { err = -errno; } diff --git a/hw/9pfs/cofs.c b/hw/9pfs/cofs.c index 68745add1e..83f125bd47 100644 --- a/hw/9pfs/cofs.c +++ b/hw/9pfs/cofs.c @@ -323,7 +323,7 @@ int v9fs_co_name_to_path(V9fsPDU *pdu, V9fsPath *dirpath, int err; V9fsState *s = pdu->s; - if (s->ctx.flags & PATHNAME_FSCONTEXT) { + if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) { err = s->ops->name_to_path(&s->ctx, dirpath, name, path); if (err < 0) { err = -errno; diff --git a/hw/9pfs/virtio-9p-coth.h b/hw/9pfs/virtio-9p-coth.h index 4630080e53..c4b74b0221 100644 --- a/hw/9pfs/virtio-9p-coth.h +++ b/hw/9pfs/virtio-9p-coth.h @@ -80,7 +80,7 @@ extern int v9fs_co_rename(V9fsPDU *, V9fsPath *, V9fsPath *); extern int v9fs_co_unlinkat(V9fsPDU *, V9fsPath *, V9fsString *, int flags); extern int v9fs_co_renameat(V9fsPDU *, V9fsPath *, V9fsString *, V9fsPath *, V9fsString *); -extern int v9fs_co_fstat(V9fsPDU *, int, struct stat *); +extern int v9fs_co_fstat(V9fsPDU *, V9fsFidState *, struct stat *); extern int v9fs_co_opendir(V9fsPDU *, V9fsFidState *); extern int v9fs_co_open(V9fsPDU *, V9fsFidState *, int); extern int v9fs_co_open2(V9fsPDU *, V9fsFidState *, V9fsString *, @@ -88,8 +88,8 @@ extern int v9fs_co_open2(V9fsPDU *, V9fsFidState *, V9fsString *, extern int v9fs_co_lsetxattr(V9fsPDU *, V9fsPath *, V9fsString *, void *, size_t, int); extern int v9fs_co_lremovexattr(V9fsPDU *, V9fsPath *, V9fsString *); -extern int v9fs_co_closedir(V9fsPDU *, DIR *); -extern int v9fs_co_close(V9fsPDU *, int); +extern int v9fs_co_closedir(V9fsPDU *, V9fsFidOpenState *); +extern int v9fs_co_close(V9fsPDU *, V9fsFidOpenState *); extern int v9fs_co_fsync(V9fsPDU *, V9fsFidState *, int); extern int v9fs_co_symlink(V9fsPDU *, V9fsFidState *, V9fsString *, const char *, gid_t, struct stat *); @@ -101,4 +101,7 @@ extern int v9fs_co_preadv(V9fsPDU *, V9fsFidState *, struct iovec *, int, int64_t); extern int v9fs_co_name_to_path(V9fsPDU *, V9fsPath *, const char *, V9fsPath *); +extern int v9fs_co_st_gen(V9fsPDU *pdu, V9fsPath *path, mode_t, + V9fsStatDotl *v9stat); + #endif diff --git a/hw/9pfs/virtio-9p-debug.c b/hw/9pfs/virtio-9p-debug.c deleted file mode 100644 index 96925f04a4..0000000000 --- a/hw/9pfs/virtio-9p-debug.c +++ /dev/null @@ -1,646 +0,0 @@ -/* - * Virtio 9p PDU debug - * - * Copyright IBM, Corp. 2010 - * - * Authors: - * Anthony Liguori <aliguori@us.ibm.com> - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#include "hw/virtio.h" -#include "hw/pc.h" -#include "virtio-9p.h" -#include "virtio-9p-debug.h" - -#define BUG_ON(cond) assert(!(cond)) - -static FILE *llogfile; - -static struct iovec *get_sg(V9fsPDU *pdu, int rx) -{ - if (rx) { - return pdu->elem.in_sg; - } - return pdu->elem.out_sg; -} - -static int get_sg_count(V9fsPDU *pdu, int rx) -{ - if (rx) { - return pdu->elem.in_num; - } - return pdu->elem.out_num; - -} - -static void pprint_int8(V9fsPDU *pdu, int rx, size_t *offsetp, - const char *name) -{ - size_t copied; - int count = get_sg_count(pdu, rx); - size_t offset = *offsetp; - struct iovec *sg = get_sg(pdu, rx); - int8_t value; - - copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value)); - - BUG_ON(copied != sizeof(value)); - offset += sizeof(value); - fprintf(llogfile, "%s=0x%x", name, value); - *offsetp = offset; -} - -static void pprint_int16(V9fsPDU *pdu, int rx, size_t *offsetp, - const char *name) -{ - size_t copied; - int count = get_sg_count(pdu, rx); - struct iovec *sg = get_sg(pdu, rx); - size_t offset = *offsetp; - int16_t value; - - - copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value)); - - BUG_ON(copied != sizeof(value)); - offset += sizeof(value); - fprintf(llogfile, "%s=0x%x", name, value); - *offsetp = offset; -} - -static void pprint_int32(V9fsPDU *pdu, int rx, size_t *offsetp, - const char *name) -{ - size_t copied; - int count = get_sg_count(pdu, rx); - struct iovec *sg = get_sg(pdu, rx); - size_t offset = *offsetp; - int32_t value; - - - copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value)); - - BUG_ON(copied != sizeof(value)); - offset += sizeof(value); - fprintf(llogfile, "%s=0x%x", name, value); - *offsetp = offset; -} - -static void pprint_int64(V9fsPDU *pdu, int rx, size_t *offsetp, - const char *name) -{ - size_t copied; - int count = get_sg_count(pdu, rx); - struct iovec *sg = get_sg(pdu, rx); - size_t offset = *offsetp; - int64_t value; - - - copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value)); - - BUG_ON(copied != sizeof(value)); - offset += sizeof(value); - fprintf(llogfile, "%s=0x%" PRIx64, name, value); - *offsetp = offset; -} - -static void pprint_str(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name) -{ - int sg_count = get_sg_count(pdu, rx); - struct iovec *sg = get_sg(pdu, rx); - size_t offset = *offsetp; - uint16_t tmp_size, size; - size_t result; - size_t copied = 0; - int i = 0; - - /* get the size */ - copied = do_pdu_unpack(&tmp_size, sg, sg_count, offset, sizeof(tmp_size)); - BUG_ON(copied != sizeof(tmp_size)); - size = le16_to_cpupu(&tmp_size); - offset += copied; - - fprintf(llogfile, "%s=", name); - for (i = 0; size && i < sg_count; i++) { - size_t len; - if (offset >= sg[i].iov_len) { - /* skip this sg */ - offset -= sg[i].iov_len; - continue; - } else { - len = MIN(sg[i].iov_len - offset, size); - result = fwrite(sg[i].iov_base + offset, 1, len, llogfile); - BUG_ON(result != len); - size -= len; - copied += len; - if (size) { - offset = 0; - continue; - } - } - } - *offsetp += copied; -} - -static void pprint_qid(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name) -{ - fprintf(llogfile, "%s={", name); - pprint_int8(pdu, rx, offsetp, "type"); - pprint_int32(pdu, rx, offsetp, ", version"); - pprint_int64(pdu, rx, offsetp, ", path"); - fprintf(llogfile, "}"); -} - -static void pprint_stat(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name) -{ - fprintf(llogfile, "%s={", name); - pprint_int16(pdu, rx, offsetp, "size"); - pprint_int16(pdu, rx, offsetp, ", type"); - pprint_int32(pdu, rx, offsetp, ", dev"); - pprint_qid(pdu, rx, offsetp, ", qid"); - pprint_int32(pdu, rx, offsetp, ", mode"); - pprint_int32(pdu, rx, offsetp, ", atime"); - pprint_int32(pdu, rx, offsetp, ", mtime"); - pprint_int64(pdu, rx, offsetp, ", length"); - pprint_str(pdu, rx, offsetp, ", name"); - pprint_str(pdu, rx, offsetp, ", uid"); - pprint_str(pdu, rx, offsetp, ", gid"); - pprint_str(pdu, rx, offsetp, ", muid"); - pprint_str(pdu, rx, offsetp, ", extension"); - pprint_int32(pdu, rx, offsetp, ", uid"); - pprint_int32(pdu, rx, offsetp, ", gid"); - pprint_int32(pdu, rx, offsetp, ", muid"); - fprintf(llogfile, "}"); -} - -static void pprint_stat_dotl(V9fsPDU *pdu, int rx, size_t *offsetp, - const char *name) -{ - fprintf(llogfile, "%s={", name); - pprint_qid(pdu, rx, offsetp, "qid"); - pprint_int32(pdu, rx, offsetp, ", st_mode"); - pprint_int64(pdu, rx, offsetp, ", st_nlink"); - pprint_int32(pdu, rx, offsetp, ", st_uid"); - pprint_int32(pdu, rx, offsetp, ", st_gid"); - pprint_int64(pdu, rx, offsetp, ", st_rdev"); - pprint_int64(pdu, rx, offsetp, ", st_size"); - pprint_int64(pdu, rx, offsetp, ", st_blksize"); - pprint_int64(pdu, rx, offsetp, ", st_blocks"); - pprint_int64(pdu, rx, offsetp, ", atime"); - pprint_int64(pdu, rx, offsetp, ", atime_nsec"); - pprint_int64(pdu, rx, offsetp, ", mtime"); - pprint_int64(pdu, rx, offsetp, ", mtime_nsec"); - pprint_int64(pdu, rx, offsetp, ", ctime"); - pprint_int64(pdu, rx, offsetp, ", ctime_nsec"); - fprintf(llogfile, "}"); -} - - - -static void pprint_strs(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name) -{ - int sg_count = get_sg_count(pdu, rx); - struct iovec *sg = get_sg(pdu, rx); - size_t offset = *offsetp; - uint16_t tmp_count, count, i; - size_t copied = 0; - - fprintf(llogfile, "%s={", name); - - /* Get the count */ - copied = do_pdu_unpack(&tmp_count, sg, sg_count, offset, sizeof(tmp_count)); - BUG_ON(copied != sizeof(tmp_count)); - count = le16_to_cpupu(&tmp_count); - offset += copied; - - for (i = 0; i < count; i++) { - char str[512]; - if (i) { - fprintf(llogfile, ", "); - } - snprintf(str, sizeof(str), "[%d]", i); - pprint_str(pdu, rx, &offset, str); - } - - fprintf(llogfile, "}"); - - *offsetp = offset; -} - -static void pprint_qids(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name) -{ - int sg_count = get_sg_count(pdu, rx); - struct iovec *sg = get_sg(pdu, rx); - size_t offset = *offsetp; - uint16_t tmp_count, count, i; - size_t copied = 0; - - fprintf(llogfile, "%s={", name); - - copied = do_pdu_unpack(&tmp_count, sg, sg_count, offset, sizeof(tmp_count)); - BUG_ON(copied != sizeof(tmp_count)); - count = le16_to_cpupu(&tmp_count); - offset += copied; - - for (i = 0; i < count; i++) { - char str[512]; - if (i) { - fprintf(llogfile, ", "); - } - snprintf(str, sizeof(str), "[%d]", i); - pprint_qid(pdu, rx, &offset, str); - } - - fprintf(llogfile, "}"); - - *offsetp = offset; -} - -static void pprint_sg(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name) -{ - struct iovec *sg = get_sg(pdu, rx); - unsigned int count; - int i; - - if (rx) { - count = pdu->elem.in_num; - } else { - count = pdu->elem.out_num; - } - - fprintf(llogfile, "%s={", name); - for (i = 0; i < count; i++) { - if (i) { - fprintf(llogfile, ", "); - } - fprintf(llogfile, "(%p, 0x%zx)", sg[i].iov_base, sg[i].iov_len); - } - fprintf(llogfile, "}"); -} - -/* FIXME: read from a directory fid returns serialized stat_t's */ -#ifdef DEBUG_DATA -static void pprint_data(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name) -{ - struct iovec *sg = get_sg(pdu, rx); - size_t offset = *offsetp; - unsigned int count; - int32_t size; - int total, i, j; - ssize_t len; - - if (rx) { - count = pdu->elem.in_num; - } else { - count = pdu->elem.out_num; - } - - BUG_ON((offset + sizeof(size)) > sg[0].iov_len); - - memcpy(&size, sg[0].iov_base + offset, sizeof(size)); - offset += sizeof(size); - - fprintf(llogfile, "size: %x\n", size); - - sg[0].iov_base += 11; /* skip header */ - sg[0].iov_len -= 11; - - total = 0; - for (i = 0; i < count; i++) { - total += sg[i].iov_len; - if (total >= size) { - /* trim sg list so writev does the right thing */ - sg[i].iov_len -= (total - size); - i++; - break; - } - } - - fprintf(llogfile, "%s={\"", name); - fflush(llogfile); - for (j = 0; j < i; j++) { - if (j) { - fprintf(llogfile, "\", \""); - fflush(llogfile); - } - - do { - len = writev(fileno(llogfile), &sg[j], 1); - } while (len == -1 && errno == EINTR); - fprintf(llogfile, "len == %ld: %m\n", len); - BUG_ON(len != sg[j].iov_len); - } - fprintf(llogfile, "\"}"); - - sg[0].iov_base -= 11; - sg[0].iov_len += 11; - -} -#endif - -void pprint_pdu(V9fsPDU *pdu) -{ - size_t offset = 7; - - if (llogfile == NULL) { - llogfile = fopen("/tmp/pdu.log", "w"); - } - - BUG_ON(!llogfile); - - switch (pdu->id) { - case P9_TREADDIR: - fprintf(llogfile, "TREADDIR: ("); - pprint_int32(pdu, 0, &offset, "fid"); - pprint_int64(pdu, 0, &offset, ", initial offset"); - pprint_int32(pdu, 0, &offset, ", max count"); - break; - case P9_RREADDIR: - fprintf(llogfile, "RREADDIR: ("); - pprint_int32(pdu, 1, &offset, "count"); -#ifdef DEBUG_DATA - pprint_data(pdu, 1, &offset, ", data"); -#endif - break; - case P9_TMKDIR: - fprintf(llogfile, "TMKDIR: ("); - pprint_int32(pdu, 0, &offset, "fid"); - pprint_str(pdu, 0, &offset, "name"); - pprint_int32(pdu, 0, &offset, "mode"); - pprint_int32(pdu, 0, &offset, "gid"); - break; - case P9_RMKDIR: - fprintf(llogfile, "RMKDIR: ("); - pprint_qid(pdu, 0, &offset, "qid"); - break; - case P9_TVERSION: - fprintf(llogfile, "TVERSION: ("); - pprint_int32(pdu, 0, &offset, "msize"); - pprint_str(pdu, 0, &offset, ", version"); - break; - case P9_RVERSION: - fprintf(llogfile, "RVERSION: ("); - pprint_int32(pdu, 1, &offset, "msize"); - pprint_str(pdu, 1, &offset, ", version"); - break; - case P9_TGETATTR: - fprintf(llogfile, "TGETATTR: ("); - pprint_int32(pdu, 0, &offset, "fid"); - break; - case P9_RGETATTR: - fprintf(llogfile, "RGETATTR: ("); - pprint_stat_dotl(pdu, 1, &offset, "getattr"); - break; - case P9_TAUTH: - fprintf(llogfile, "TAUTH: ("); - pprint_int32(pdu, 0, &offset, "afid"); - pprint_str(pdu, 0, &offset, ", uname"); - pprint_str(pdu, 0, &offset, ", aname"); - pprint_int32(pdu, 0, &offset, ", n_uname"); - break; - case P9_RAUTH: - fprintf(llogfile, "RAUTH: ("); - pprint_qid(pdu, 1, &offset, "qid"); - break; - case P9_TATTACH: - fprintf(llogfile, "TATTACH: ("); - pprint_int32(pdu, 0, &offset, "fid"); - pprint_int32(pdu, 0, &offset, ", afid"); - pprint_str(pdu, 0, &offset, ", uname"); - pprint_str(pdu, 0, &offset, ", aname"); - pprint_int32(pdu, 0, &offset, ", n_uname"); - break; - case P9_RATTACH: - fprintf(llogfile, "RATTACH: ("); - pprint_qid(pdu, 1, &offset, "qid"); - break; - case P9_TERROR: - fprintf(llogfile, "TERROR: ("); - break; - case P9_RERROR: - fprintf(llogfile, "RERROR: ("); - pprint_str(pdu, 1, &offset, "ename"); - pprint_int32(pdu, 1, &offset, ", ecode"); - break; - case P9_TFLUSH: - fprintf(llogfile, "TFLUSH: ("); - pprint_int16(pdu, 0, &offset, "oldtag"); - break; - case P9_RFLUSH: - fprintf(llogfile, "RFLUSH: ("); - break; - case P9_TWALK: - fprintf(llogfile, "TWALK: ("); - pprint_int32(pdu, 0, &offset, "fid"); - pprint_int32(pdu, 0, &offset, ", newfid"); - pprint_strs(pdu, 0, &offset, ", wnames"); - break; - case P9_RWALK: - fprintf(llogfile, "RWALK: ("); - pprint_qids(pdu, 1, &offset, "wqids"); - break; - case P9_TOPEN: - fprintf(llogfile, "TOPEN: ("); - pprint_int32(pdu, 0, &offset, "fid"); - pprint_int8(pdu, 0, &offset, ", mode"); - break; - case P9_ROPEN: - fprintf(llogfile, "ROPEN: ("); - pprint_qid(pdu, 1, &offset, "qid"); - pprint_int32(pdu, 1, &offset, ", iounit"); - break; - case P9_TCREATE: - fprintf(llogfile, "TCREATE: ("); - pprint_int32(pdu, 0, &offset, "fid"); - pprint_str(pdu, 0, &offset, ", name"); - pprint_int32(pdu, 0, &offset, ", perm"); - pprint_int8(pdu, 0, &offset, ", mode"); - pprint_str(pdu, 0, &offset, ", extension"); - break; - case P9_RCREATE: - fprintf(llogfile, "RCREATE: ("); - pprint_qid(pdu, 1, &offset, "qid"); - pprint_int32(pdu, 1, &offset, ", iounit"); - break; - case P9_TSYMLINK: - fprintf(llogfile, "TSYMLINK: ("); - pprint_int32(pdu, 0, &offset, "fid"); - pprint_str(pdu, 0, &offset, ", name"); - pprint_str(pdu, 0, &offset, ", symname"); - pprint_int32(pdu, 0, &offset, ", gid"); - break; - case P9_RSYMLINK: - fprintf(llogfile, "RSYMLINK: ("); - pprint_qid(pdu, 1, &offset, "qid"); - break; - case P9_TLCREATE: - fprintf(llogfile, "TLCREATE: ("); - pprint_int32(pdu, 0, &offset, "dfid"); - pprint_str(pdu, 0, &offset, ", name"); - pprint_int32(pdu, 0, &offset, ", flags"); - pprint_int32(pdu, 0, &offset, ", mode"); - pprint_int32(pdu, 0, &offset, ", gid"); - break; - case P9_RLCREATE: - fprintf(llogfile, "RLCREATE: ("); - pprint_qid(pdu, 1, &offset, "qid"); - pprint_int32(pdu, 1, &offset, ", iounit"); - break; - case P9_TMKNOD: - fprintf(llogfile, "TMKNOD: ("); - pprint_int32(pdu, 0, &offset, "fid"); - pprint_str(pdu, 0, &offset, "name"); - pprint_int32(pdu, 0, &offset, "mode"); - pprint_int32(pdu, 0, &offset, "major"); - pprint_int32(pdu, 0, &offset, "minor"); - pprint_int32(pdu, 0, &offset, "gid"); - break; - case P9_RMKNOD: - fprintf(llogfile, "RMKNOD: )"); - pprint_qid(pdu, 0, &offset, "qid"); - break; - case P9_TREADLINK: - fprintf(llogfile, "TREADLINK: ("); - pprint_int32(pdu, 0, &offset, "fid"); - break; - case P9_RREADLINK: - fprintf(llogfile, "RREADLINK: ("); - pprint_str(pdu, 0, &offset, "target"); - break; - case P9_TREAD: - fprintf(llogfile, "TREAD: ("); - pprint_int32(pdu, 0, &offset, "fid"); - pprint_int64(pdu, 0, &offset, ", offset"); - pprint_int32(pdu, 0, &offset, ", count"); - pprint_sg(pdu, 0, &offset, ", sg"); - break; - case P9_RREAD: - fprintf(llogfile, "RREAD: ("); - pprint_int32(pdu, 1, &offset, "count"); - pprint_sg(pdu, 1, &offset, ", sg"); - offset = 7; -#ifdef DEBUG_DATA - pprint_data(pdu, 1, &offset, ", data"); -#endif - break; - case P9_TWRITE: - fprintf(llogfile, "TWRITE: ("); - pprint_int32(pdu, 0, &offset, "fid"); - pprint_int64(pdu, 0, &offset, ", offset"); - pprint_int32(pdu, 0, &offset, ", count"); - break; - case P9_RWRITE: - fprintf(llogfile, "RWRITE: ("); - pprint_int32(pdu, 1, &offset, "count"); - break; - case P9_TCLUNK: - fprintf(llogfile, "TCLUNK: ("); - pprint_int32(pdu, 0, &offset, "fid"); - break; - case P9_RCLUNK: - fprintf(llogfile, "RCLUNK: ("); - break; - case P9_TFSYNC: - fprintf(llogfile, "TFSYNC: ("); - pprint_int32(pdu, 0, &offset, "fid"); - break; - case P9_RFSYNC: - fprintf(llogfile, "RFSYNC: ("); - break; - case P9_TLINK: - fprintf(llogfile, "TLINK: ("); - pprint_int32(pdu, 0, &offset, "dfid"); - pprint_int32(pdu, 0, &offset, ", fid"); - pprint_str(pdu, 0, &offset, ", newpath"); - break; - case P9_RLINK: - fprintf(llogfile, "RLINK: ("); - break; - case P9_TREMOVE: - fprintf(llogfile, "TREMOVE: ("); - pprint_int32(pdu, 0, &offset, "fid"); - break; - case P9_RREMOVE: - fprintf(llogfile, "RREMOVE: ("); - break; - case P9_TSTAT: - fprintf(llogfile, "TSTAT: ("); - pprint_int32(pdu, 0, &offset, "fid"); - break; - case P9_RSTAT: - fprintf(llogfile, "RSTAT: ("); - offset += 2; /* ignored */ - pprint_stat(pdu, 1, &offset, "stat"); - break; - case P9_TWSTAT: - fprintf(llogfile, "TWSTAT: ("); - pprint_int32(pdu, 0, &offset, "fid"); - offset += 2; /* ignored */ - pprint_stat(pdu, 0, &offset, ", stat"); - break; - case P9_RWSTAT: - fprintf(llogfile, "RWSTAT: ("); - break; - case P9_TXATTRWALK: - fprintf(llogfile, "TXATTRWALK: ("); - pprint_int32(pdu, 0, &offset, "fid"); - pprint_int32(pdu, 0, &offset, ", newfid"); - pprint_str(pdu, 0, &offset, ", xattr name"); - break; - case P9_RXATTRWALK: - fprintf(llogfile, "RXATTRWALK: ("); - pprint_int64(pdu, 1, &offset, "xattrsize"); - case P9_TXATTRCREATE: - fprintf(llogfile, "TXATTRCREATE: ("); - pprint_int32(pdu, 0, &offset, "fid"); - pprint_str(pdu, 0, &offset, ", name"); - pprint_int64(pdu, 0, &offset, ", xattrsize"); - pprint_int32(pdu, 0, &offset, ", flags"); - break; - case P9_RXATTRCREATE: - fprintf(llogfile, "RXATTRCREATE: ("); - break; - case P9_TLOCK: - fprintf(llogfile, "TLOCK: ("); - pprint_int32(pdu, 0, &offset, "fid"); - pprint_int8(pdu, 0, &offset, ", type"); - pprint_int32(pdu, 0, &offset, ", flags"); - pprint_int64(pdu, 0, &offset, ", start"); - pprint_int64(pdu, 0, &offset, ", length"); - pprint_int32(pdu, 0, &offset, ", proc_id"); - pprint_str(pdu, 0, &offset, ", client_id"); - break; - case P9_RLOCK: - fprintf(llogfile, "RLOCK: ("); - pprint_int8(pdu, 0, &offset, "status"); - break; - case P9_TGETLOCK: - fprintf(llogfile, "TGETLOCK: ("); - pprint_int32(pdu, 0, &offset, "fid"); - pprint_int8(pdu, 0, &offset, ", type"); - pprint_int64(pdu, 0, &offset, ", start"); - pprint_int64(pdu, 0, &offset, ", length"); - pprint_int32(pdu, 0, &offset, ", proc_id"); - pprint_str(pdu, 0, &offset, ", client_id"); - break; - case P9_RGETLOCK: - fprintf(llogfile, "RGETLOCK: ("); - pprint_int8(pdu, 0, &offset, "type"); - pprint_int64(pdu, 0, &offset, ", start"); - pprint_int64(pdu, 0, &offset, ", length"); - pprint_int32(pdu, 0, &offset, ", proc_id"); - pprint_str(pdu, 0, &offset, ", client_id"); - break; - default: - fprintf(llogfile, "unknown(%d): (", pdu->id); - break; - } - - fprintf(llogfile, ")\n"); - /* Flush the log message out */ - fflush(llogfile); -} diff --git a/hw/9pfs/virtio-9p-debug.h b/hw/9pfs/virtio-9p-debug.h deleted file mode 100644 index d9a249118d..0000000000 --- a/hw/9pfs/virtio-9p-debug.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _QEMU_VIRTIO_9P_DEBUG_H -#define _QEMU_VIRTIO_9P_DEBUG_H - -void pprint_pdu(V9fsPDU *pdu); - -#endif diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c index 513e181c82..bba4c54762 100644 --- a/hw/9pfs/virtio-9p-device.c +++ b/hw/9pfs/virtio-9p-device.c @@ -49,7 +49,8 @@ VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf) V9fsState *s; int i, len; struct stat stat; - FsTypeEntry *fse; + FsDriverEntry *fse; + V9fsPath path; s = (V9fsState *)virtio_common_init("virtio-9p", VIRTIO_ID_9P, @@ -82,55 +83,33 @@ VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf) exit(1); } - if (!strcmp(fse->security_model, "passthrough")) { - /* Files on the Fileserver set to client user credentials */ - s->ctx.fs_sm = SM_PASSTHROUGH; + s->ctx.export_flags = fse->export_flags; + s->ctx.fs_root = g_strdup(fse->path); + s->ctx.exops.get_st_gen = NULL; + + if (fse->export_flags & V9FS_SM_PASSTHROUGH) { s->ctx.xops = passthrough_xattr_ops; - } else if (!strcmp(fse->security_model, "mapped")) { - /* Files on the fileserver are set to QEMU credentials. - * Client user credentials are saved in extended attributes. - */ - s->ctx.fs_sm = SM_MAPPED; + } else if (fse->export_flags & V9FS_SM_MAPPED) { s->ctx.xops = mapped_xattr_ops; - } else if (!strcmp(fse->security_model, "none")) { - /* - * Files on the fileserver are set to QEMU credentials. - */ - s->ctx.fs_sm = SM_NONE; - s->ctx.xops = none_xattr_ops; - } else { - fprintf(stderr, "Default to security_model=none. You may want" - " enable advanced security model using " - "security option:\n\t security_model=passthrough\n\t " - "security_model=mapped\n"); - s->ctx.fs_sm = SM_NONE; + } else if (fse->export_flags & V9FS_SM_NONE) { s->ctx.xops = none_xattr_ops; } - if (lstat(fse->path, &stat)) { - fprintf(stderr, "share path %s does not exist\n", fse->path); - exit(1); - } else if (!S_ISDIR(stat.st_mode)) { - fprintf(stderr, "share path %s is not a directory\n", fse->path); - exit(1); - } - - s->ctx.fs_root = g_strdup(fse->path); len = strlen(conf->tag); if (len > MAX_TAG_LEN) { - len = MAX_TAG_LEN; + fprintf(stderr, "mount tag '%s' (%d bytes) is longer than " + "maximum (%d bytes)", conf->tag, len, MAX_TAG_LEN); + exit(1); } /* s->tag is non-NULL terminated string */ s->tag = g_malloc(len); memcpy(s->tag, conf->tag, len); s->tag_len = len; s->ctx.uid = -1; - s->ctx.flags = 0; s->ops = fse->ops; s->vdev.get_features = virtio_9p_get_features; - s->config_size = sizeof(struct virtio_9p_config) + - s->tag_len; + s->config_size = sizeof(struct virtio_9p_config) + s->tag_len; s->vdev.get_config = virtio_9p_get_config; s->fid_list = NULL; qemu_co_rwlock_init(&s->rename_lock); @@ -144,6 +123,27 @@ VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf) fprintf(stderr, "worker thread initialization failed\n"); exit(1); } + + /* + * Check details of export path, We need to use fs driver + * call back to do that. Since we are in the init path, we don't + * use co-routines here. + */ + v9fs_path_init(&path); + if (s->ops->name_to_path(&s->ctx, NULL, "/", &path) < 0) { + fprintf(stderr, + "error in converting name to path %s", strerror(errno)); + exit(1); + } + if (s->ops->lstat(&s->ctx, &path, &stat)) { + fprintf(stderr, "share path %s does not exist\n", fse->path); + exit(1); + } else if (!S_ISDIR(stat.st_mode)) { + fprintf(stderr, "share path %s is not a directory\n", fse->path); + exit(1); + } + v9fs_path_free(&path); + return &s->vdev; } @@ -169,6 +169,8 @@ static PCIDeviceInfo virtio_9p_info = { .revision = VIRTIO_PCI_ABI_VERSION, .class_id = 0x2, .qdev.props = (Property[]) { + DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features), DEFINE_PROP_STRING("mount_tag", VirtIOPCIProxy, fsconf.tag), diff --git a/hw/9pfs/virtio-9p-handle.c b/hw/9pfs/virtio-9p-handle.c index 5c8b5ed471..c38e0e7863 100644 --- a/hw/9pfs/virtio-9p-handle.c +++ b/hw/9pfs/virtio-9p-handle.c @@ -21,49 +21,56 @@ #include <sys/un.h> #include <attr/xattr.h> #include <unistd.h> - -struct handle_data { - int mountfd; - int handle_bytes; -}; - -#if __GLIBC__ <= 2 && __GLIBC_MINOR__ < 14 -struct file_handle { - unsigned int handle_bytes; - int handle_type; - unsigned char handle[0]; -}; +#include <linux/fs.h> +#ifdef CONFIG_LINUX_MAGIC_H +#include <linux/magic.h> #endif +#include <sys/ioctl.h> -#ifndef AT_EMPTY_PATH -#define AT_EMPTY_PATH 0x1000 /* Allow empty relative pathname */ +#ifndef XFS_SUPER_MAGIC +#define XFS_SUPER_MAGIC 0x58465342 #endif -#ifndef O_PATH -#define O_PATH 010000000 +#ifndef EXT2_SUPER_MAGIC +#define EXT2_SUPER_MAGIC 0xEF53 #endif - -#ifndef __NR_name_to_handle_at -#if defined(__i386__) -#define __NR_name_to_handle_at 341 -#define __NR_open_by_handle_at 342 -#elif defined(__x86_64__) -#define __NR_name_to_handle_at 303 -#define __NR_open_by_handle_at 304 +#ifndef REISERFS_SUPER_MAGIC +#define REISERFS_SUPER_MAGIC 0x52654973 #endif +#ifndef BTRFS_SUPER_MAGIC +#define BTRFS_SUPER_MAGIC 0x9123683E #endif -#ifdef __NR_name_to_handle_at +struct handle_data { + int mountfd; + int handle_bytes; +}; + +#ifdef CONFIG_OPEN_BY_HANDLE static inline int name_to_handle(int dirfd, const char *name, struct file_handle *fh, int *mnt_id, int flags) { - return syscall(__NR_name_to_handle_at, dirfd, name, fh, mnt_id, flags); + return name_to_handle_at(dirfd, name, fh, mnt_id, flags); } static inline int open_by_handle(int mountfd, const char *fh, int flags) { - return syscall(__NR_open_by_handle_at, mountfd, fh, flags); + return open_by_handle_at(mountfd, (struct file_handle *)fh, flags); } #else + +struct file_handle { + unsigned int handle_bytes; + int handle_type; + unsigned char handle[0]; +}; + +#ifndef AT_EMPTY_PATH +#define AT_EMPTY_PATH 0x1000 /* Allow empty relative pathname */ +#endif +#ifndef O_PATH +#define O_PATH 010000000 +#endif + static inline int name_to_handle(int dirfd, const char *name, struct file_handle *fh, int *mnt_id, int flags) { @@ -126,82 +133,105 @@ static ssize_t handle_readlink(FsContext *fs_ctx, V9fsPath *fs_path, return ret; } -static int handle_close(FsContext *ctx, int fd) +static int handle_close(FsContext *ctx, V9fsFidOpenState *fs) { - return close(fd); + return close(fs->fd); } -static int handle_closedir(FsContext *ctx, DIR *dir) +static int handle_closedir(FsContext *ctx, V9fsFidOpenState *fs) { - return closedir(dir); + return closedir(fs->dir); } -static int handle_open(FsContext *ctx, V9fsPath *fs_path, int flags) +static int handle_open(FsContext *ctx, V9fsPath *fs_path, + int flags, V9fsFidOpenState *fs) { struct handle_data *data = (struct handle_data *)ctx->private; - return open_by_handle(data->mountfd, fs_path->data, flags); + fs->fd = open_by_handle(data->mountfd, fs_path->data, flags); + return fs->fd; } -static DIR *handle_opendir(FsContext *ctx, V9fsPath *fs_path) +static int handle_opendir(FsContext *ctx, + V9fsPath *fs_path, V9fsFidOpenState *fs) { - int fd; - fd = handle_open(ctx, fs_path, O_DIRECTORY); - if (fd < 0) { - return NULL; + int ret; + ret = handle_open(ctx, fs_path, O_DIRECTORY, fs); + if (ret < 0) { + return -1; + } + fs->dir = fdopendir(ret); + if (!fs->dir) { + return -1; } - return fdopendir(fd); + return 0; } -static void handle_rewinddir(FsContext *ctx, DIR *dir) +static void handle_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) { - return rewinddir(dir); + return rewinddir(fs->dir); } -static off_t handle_telldir(FsContext *ctx, DIR *dir) +static off_t handle_telldir(FsContext *ctx, V9fsFidOpenState *fs) { - return telldir(dir); + return telldir(fs->dir); } -static int handle_readdir_r(FsContext *ctx, DIR *dir, struct dirent *entry, +static int handle_readdir_r(FsContext *ctx, V9fsFidOpenState *fs, + struct dirent *entry, struct dirent **result) { - return readdir_r(dir, entry, result); + return readdir_r(fs->dir, entry, result); } -static void handle_seekdir(FsContext *ctx, DIR *dir, off_t off) +static void handle_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) { - return seekdir(dir, off); + return seekdir(fs->dir, off); } -static ssize_t handle_preadv(FsContext *ctx, int fd, const struct iovec *iov, +static ssize_t handle_preadv(FsContext *ctx, V9fsFidOpenState *fs, + const struct iovec *iov, int iovcnt, off_t offset) { #ifdef CONFIG_PREADV - return preadv(fd, iov, iovcnt, offset); + return preadv(fs->fd, iov, iovcnt, offset); #else - int err = lseek(fd, offset, SEEK_SET); + int err = lseek(fs->fd, offset, SEEK_SET); if (err == -1) { return err; } else { - return readv(fd, iov, iovcnt); + return readv(fs->fd, iov, iovcnt); } #endif } -static ssize_t handle_pwritev(FsContext *ctx, int fd, const struct iovec *iov, +static ssize_t handle_pwritev(FsContext *ctx, V9fsFidOpenState *fs, + const struct iovec *iov, int iovcnt, off_t offset) { + ssize_t ret; #ifdef CONFIG_PREADV - return pwritev(fd, iov, iovcnt, offset); + ret = pwritev(fs->fd, iov, iovcnt, offset); #else - int err = lseek(fd, offset, SEEK_SET); + int err = lseek(fs->fd, offset, SEEK_SET); if (err == -1) { return err; } else { - return writev(fd, iov, iovcnt); + ret = writev(fs->fd, iov, iovcnt); } #endif +#ifdef CONFIG_SYNC_FILE_RANGE + if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) { + /* + * Initiate a writeback. This is not a data integrity sync. + * We want to ensure that we don't leave dirty pages in the cache + * after write when writeout=immediate is sepcified. + */ + sync_file_range(fs->fd, offset, ret, + SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE); + } +#endif + return ret; } static int handle_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) @@ -254,13 +284,14 @@ static int handle_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, return ret; } -static int handle_fstat(FsContext *fs_ctx, int fd, struct stat *stbuf) +static int handle_fstat(FsContext *fs_ctx, V9fsFidOpenState *fs, + struct stat *stbuf) { - return fstat(fd, stbuf); + return fstat(fs->fd, stbuf); } static int handle_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, - int flags, FsCred *credp) + int flags, FsCred *credp, V9fsFidOpenState *fs) { int ret; int dirfd, fd; @@ -276,6 +307,8 @@ static int handle_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, if (ret < 0) { close(fd); fd = ret; + } else { + fs->fd = fd; } } close(dirfd); @@ -367,7 +400,9 @@ static int handle_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) static int handle_utimensat(FsContext *ctx, V9fsPath *fs_path, const struct timespec *buf) { - int fd, ret; + int ret; +#ifdef CONFIG_UTIMENSAT + int fd; struct handle_data *data = (struct handle_data *)ctx->private; fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); @@ -376,6 +411,10 @@ static int handle_utimensat(FsContext *ctx, V9fsPath *fs_path, } ret = futimens(fd, buf); close(fd); +#else + ret = -1; + errno = ENOSYS; +#endif return ret; } @@ -385,12 +424,12 @@ static int handle_remove(FsContext *ctx, const char *path) return -1; } -static int handle_fsync(FsContext *ctx, int fd, int datasync) +static int handle_fsync(FsContext *ctx, V9fsFidOpenState *fs, int datasync) { if (datasync) { - return qemu_fdatasync(fd); + return qemu_fdatasync(fs->fd); } else { - return fsync(fd); + return fsync(fs->fd); } } @@ -546,16 +585,51 @@ static int handle_unlinkat(FsContext *ctx, V9fsPath *dir, return ret; } +static int handle_ioc_getversion(FsContext *ctx, V9fsPath *path, + mode_t st_mode, uint64_t *st_gen) +{ + int err; + V9fsFidOpenState fid_open; + + /* + * Do not try to open special files like device nodes, fifos etc + * We can get fd for regular files and directories only + */ + if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) { + return 0; + } + err = handle_open(ctx, path, O_RDONLY, &fid_open); + if (err < 0) { + return err; + } + err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen); + handle_close(ctx, &fid_open); + return err; +} + static int handle_init(FsContext *ctx) { int ret, mnt_id; + struct statfs stbuf; struct file_handle fh; struct handle_data *data = g_malloc(sizeof(struct handle_data)); + data->mountfd = open(ctx->fs_root, O_DIRECTORY); if (data->mountfd < 0) { ret = data->mountfd; goto err_out; } + ret = statfs(ctx->fs_root, &stbuf); + if (!ret) { + switch (stbuf.f_type) { + case EXT2_SUPER_MAGIC: + case BTRFS_SUPER_MAGIC: + case REISERFS_SUPER_MAGIC: + case XFS_SUPER_MAGIC: + ctx->exops.get_st_gen = handle_ioc_getversion; + break; + } + } memset(&fh, 0, sizeof(struct file_handle)); ret = name_to_handle(data->mountfd, ".", &fh, &mnt_id, 0); if (ret && errno == EOVERFLOW) { diff --git a/hw/9pfs/virtio-9p-local.c b/hw/9pfs/virtio-9p-local.c index 9559ff6550..782dc0ab21 100644 --- a/hw/9pfs/virtio-9p-local.c +++ b/hw/9pfs/virtio-9p-local.c @@ -20,6 +20,24 @@ #include <sys/socket.h> #include <sys/un.h> #include <attr/xattr.h> +#include <linux/fs.h> +#ifdef CONFIG_LINUX_MAGIC_H +#include <linux/magic.h> +#endif +#include <sys/ioctl.h> + +#ifndef XFS_SUPER_MAGIC +#define XFS_SUPER_MAGIC 0x58465342 +#endif +#ifndef EXT2_SUPER_MAGIC +#define EXT2_SUPER_MAGIC 0xEF53 +#endif +#ifndef REISERFS_SUPER_MAGIC +#define REISERFS_SUPER_MAGIC 0x52654973 +#endif +#ifndef BTRFS_SUPER_MAGIC +#define BTRFS_SUPER_MAGIC 0x9123683E +#endif static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf) { @@ -31,7 +49,7 @@ static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf) if (err) { return err; } - if (fs_ctx->fs_sm == SM_MAPPED) { + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { /* Actual credentials are part of extended attrs */ uid_t tmp_uid; gid_t tmp_gid; @@ -106,7 +124,7 @@ static int local_post_create_passthrough(FsContext *fs_ctx, const char *path, * If we fail to change ownership and if we are * using security model none. Ignore the error */ - if (fs_ctx->fs_sm != SM_NONE) { + if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) { return -1; } } @@ -120,7 +138,7 @@ static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path, char buffer[PATH_MAX]; char *path = fs_path->data; - if (fs_ctx->fs_sm == SM_MAPPED) { + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { int fd; fd = open(rpath(fs_ctx, path, buffer), O_RDONLY); if (fd == -1) { @@ -131,88 +149,112 @@ static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path, } while (tsize == -1 && errno == EINTR); close(fd); return tsize; - } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) || - (fs_ctx->fs_sm == SM_NONE)) { + } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || + (fs_ctx->export_flags & V9FS_SM_NONE)) { tsize = readlink(rpath(fs_ctx, path, buffer), buf, bufsz); } return tsize; } -static int local_close(FsContext *ctx, int fd) +static int local_close(FsContext *ctx, V9fsFidOpenState *fs) { - return close(fd); + return close(fs->fd); } -static int local_closedir(FsContext *ctx, DIR *dir) +static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs) { - return closedir(dir); + return closedir(fs->dir); } -static int local_open(FsContext *ctx, V9fsPath *fs_path, int flags) +static int local_open(FsContext *ctx, V9fsPath *fs_path, + int flags, V9fsFidOpenState *fs) { char buffer[PATH_MAX]; char *path = fs_path->data; - return open(rpath(ctx, path, buffer), flags); + fs->fd = open(rpath(ctx, path, buffer), flags); + return fs->fd; } -static DIR *local_opendir(FsContext *ctx, V9fsPath *fs_path) +static int local_opendir(FsContext *ctx, + V9fsPath *fs_path, V9fsFidOpenState *fs) { char buffer[PATH_MAX]; char *path = fs_path->data; - return opendir(rpath(ctx, path, buffer)); + fs->dir = opendir(rpath(ctx, path, buffer)); + if (!fs->dir) { + return -1; + } + return 0; } -static void local_rewinddir(FsContext *ctx, DIR *dir) +static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) { - return rewinddir(dir); + return rewinddir(fs->dir); } -static off_t local_telldir(FsContext *ctx, DIR *dir) +static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs) { - return telldir(dir); + return telldir(fs->dir); } -static int local_readdir_r(FsContext *ctx, DIR *dir, struct dirent *entry, +static int local_readdir_r(FsContext *ctx, V9fsFidOpenState *fs, + struct dirent *entry, struct dirent **result) { - return readdir_r(dir, entry, result); + return readdir_r(fs->dir, entry, result); } -static void local_seekdir(FsContext *ctx, DIR *dir, off_t off) +static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) { - return seekdir(dir, off); + return seekdir(fs->dir, off); } -static ssize_t local_preadv(FsContext *ctx, int fd, const struct iovec *iov, +static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs, + const struct iovec *iov, int iovcnt, off_t offset) { #ifdef CONFIG_PREADV - return preadv(fd, iov, iovcnt, offset); + return preadv(fs->fd, iov, iovcnt, offset); #else - int err = lseek(fd, offset, SEEK_SET); + int err = lseek(fs->fd, offset, SEEK_SET); if (err == -1) { return err; } else { - return readv(fd, iov, iovcnt); + return readv(fs->fd, iov, iovcnt); } #endif } -static ssize_t local_pwritev(FsContext *ctx, int fd, const struct iovec *iov, +static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs, + const struct iovec *iov, int iovcnt, off_t offset) { + ssize_t ret +; #ifdef CONFIG_PREADV - return pwritev(fd, iov, iovcnt, offset); + ret = pwritev(fs->fd, iov, iovcnt, offset); #else - int err = lseek(fd, offset, SEEK_SET); + int err = lseek(fs->fd, offset, SEEK_SET); if (err == -1) { return err; } else { - return writev(fd, iov, iovcnt); + ret = writev(fs->fd, iov, iovcnt); } #endif +#ifdef CONFIG_SYNC_FILE_RANGE + if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) { + /* + * Initiate a writeback. This is not a data integrity sync. + * We want to ensure that we don't leave dirty pages in the cache + * after write when writeout=immediate is sepcified. + */ + sync_file_range(fs->fd, offset, ret, + SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE); + } +#endif + return ret; } static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) @@ -220,10 +262,10 @@ static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) char buffer[PATH_MAX]; char *path = fs_path->data; - if (fs_ctx->fs_sm == SM_MAPPED) { + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { return local_set_xattr(rpath(fs_ctx, path, buffer), credp); - } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) || - (fs_ctx->fs_sm == SM_NONE)) { + } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || + (fs_ctx->export_flags & V9FS_SM_NONE)) { return chmod(rpath(fs_ctx, path, buffer), credp->fc_mode); } return -1; @@ -243,19 +285,19 @@ static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path, path = fullname.data; /* Determine the security model */ - if (fs_ctx->fs_sm == SM_MAPPED) { + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { err = mknod(rpath(fs_ctx, path, buffer), SM_LOCAL_MODE_BITS|S_IFREG, 0); if (err == -1) { goto out; } - local_set_xattr(rpath(fs_ctx, path, buffer), credp); + err = local_set_xattr(rpath(fs_ctx, path, buffer), credp); if (err == -1) { serrno = errno; goto err_end; } - } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) || - (fs_ctx->fs_sm == SM_NONE)) { + } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || + (fs_ctx->export_flags & V9FS_SM_NONE)) { err = mknod(rpath(fs_ctx, path, buffer), credp->fc_mode, credp->fc_rdev); if (err == -1) { @@ -291,7 +333,7 @@ static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, path = fullname.data; /* Determine the security model */ - if (fs_ctx->fs_sm == SM_MAPPED) { + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { err = mkdir(rpath(fs_ctx, path, buffer), SM_LOCAL_DIR_MODE_BITS); if (err == -1) { goto out; @@ -302,8 +344,8 @@ static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, serrno = errno; goto err_end; } - } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) || - (fs_ctx->fs_sm == SM_NONE)) { + } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || + (fs_ctx->export_flags & V9FS_SM_NONE)) { err = mkdir(rpath(fs_ctx, path, buffer), credp->fc_mode); if (err == -1) { goto out; @@ -324,30 +366,35 @@ out: return err; } -static int local_fstat(FsContext *fs_ctx, int fd, struct stat *stbuf) +static int local_fstat(FsContext *fs_ctx, + V9fsFidOpenState *fs, struct stat *stbuf) { int err; - err = fstat(fd, stbuf); + err = fstat(fs->fd, stbuf); if (err) { return err; } - if (fs_ctx->fs_sm == SM_MAPPED) { + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { /* Actual credentials are part of extended attrs */ uid_t tmp_uid; gid_t tmp_gid; mode_t tmp_mode; dev_t tmp_dev; - if (fgetxattr(fd, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) { + if (fgetxattr(fs->fd, "user.virtfs.uid", + &tmp_uid, sizeof(uid_t)) > 0) { stbuf->st_uid = tmp_uid; } - if (fgetxattr(fd, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) { + if (fgetxattr(fs->fd, "user.virtfs.gid", + &tmp_gid, sizeof(gid_t)) > 0) { stbuf->st_gid = tmp_gid; } - if (fgetxattr(fd, "user.virtfs.mode", &tmp_mode, sizeof(mode_t)) > 0) { + if (fgetxattr(fs->fd, "user.virtfs.mode", + &tmp_mode, sizeof(mode_t)) > 0) { stbuf->st_mode = tmp_mode; } - if (fgetxattr(fd, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) { + if (fgetxattr(fs->fd, "user.virtfs.rdev", + &tmp_dev, sizeof(dev_t)) > 0) { stbuf->st_rdev = tmp_dev; } } @@ -355,7 +402,7 @@ static int local_fstat(FsContext *fs_ctx, int fd, struct stat *stbuf) } static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, - int flags, FsCred *credp) + int flags, FsCred *credp, V9fsFidOpenState *fs) { char *path; int fd = -1; @@ -369,7 +416,7 @@ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, path = fullname.data; /* Determine the security model */ - if (fs_ctx->fs_sm == SM_MAPPED) { + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { fd = open(rpath(fs_ctx, path, buffer), flags, SM_LOCAL_MODE_BITS); if (fd == -1) { err = fd; @@ -382,8 +429,8 @@ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, serrno = errno; goto err_end; } - } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) || - (fs_ctx->fs_sm == SM_NONE)) { + } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || + (fs_ctx->export_flags & V9FS_SM_NONE)) { fd = open(rpath(fs_ctx, path, buffer), flags, credp->fc_mode); if (fd == -1) { err = fd; @@ -396,6 +443,7 @@ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, } } err = fd; + fs->fd = fd; goto out; err_end: @@ -422,7 +470,7 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath, newpath = fullname.data; /* Determine the security model */ - if (fs_ctx->fs_sm == SM_MAPPED) { + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { int fd; ssize_t oldpath_size, write_size; fd = open(rpath(fs_ctx, newpath, buffer), O_CREAT|O_EXCL|O_RDWR, @@ -451,8 +499,8 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath, serrno = errno; goto err_end; } - } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) || - (fs_ctx->fs_sm == SM_NONE)) { + } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || + (fs_ctx->export_flags & V9FS_SM_NONE)) { err = symlink(oldpath, rpath(fs_ctx, newpath, buffer)); if (err) { goto out; @@ -464,7 +512,7 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath, * If we fail to change ownership and if we are * using security model none. Ignore the error */ - if (fs_ctx->fs_sm != SM_NONE) { + if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) { serrno = errno; goto err_end; } else @@ -519,15 +567,12 @@ static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) char *path = fs_path->data; if ((credp->fc_uid == -1 && credp->fc_gid == -1) || - (fs_ctx->fs_sm == SM_PASSTHROUGH)) { - return lchown(rpath(fs_ctx, path, buffer), credp->fc_uid, - credp->fc_gid); - } else if (fs_ctx->fs_sm == SM_MAPPED) { + (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || + (fs_ctx->export_flags & V9FS_SM_NONE)) { + return lchown(rpath(fs_ctx, path, buffer), + credp->fc_uid, credp->fc_gid); + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) { return local_set_xattr(rpath(fs_ctx, path, buffer), credp); - } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) || - (fs_ctx->fs_sm == SM_NONE)) { - return lchown(rpath(fs_ctx, path, buffer), credp->fc_uid, - credp->fc_gid); } return -1; } @@ -548,12 +593,12 @@ static int local_remove(FsContext *ctx, const char *path) return remove(rpath(ctx, path, buffer)); } -static int local_fsync(FsContext *ctx, int fd, int datasync) +static int local_fsync(FsContext *ctx, V9fsFidOpenState *fs, int datasync) { if (datasync) { - return qemu_fdatasync(fd); + return qemu_fdatasync(fs->fd); } else { - return fsync(fd); + return fsync(fs->fd); } } @@ -645,10 +690,46 @@ static int local_unlinkat(FsContext *ctx, V9fsPath *dir, return ret; } +static int local_ioc_getversion(FsContext *ctx, V9fsPath *path, + mode_t st_mode, uint64_t *st_gen) +{ + int err; + V9fsFidOpenState fid_open; + + /* + * Do not try to open special files like device nodes, fifos etc + * We can get fd for regular files and directories only + */ + if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) { + return 0; + } + err = local_open(ctx, path, O_RDONLY, &fid_open); + if (err < 0) { + return err; + } + err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen); + local_close(ctx, &fid_open); + return err; +} + static int local_init(FsContext *ctx) { - ctx->flags |= PATHNAME_FSCONTEXT; - return 0; + int err; + struct statfs stbuf; + + ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT; + err = statfs(ctx->fs_root, &stbuf); + if (!err) { + switch (stbuf.f_type) { + case EXT2_SUPER_MAGIC: + case BTRFS_SUPER_MAGIC: + case REISERFS_SUPER_MAGIC: + case XFS_SUPER_MAGIC: + ctx->exops.get_st_gen = local_ioc_getversion; + break; + } + } + return err; } FileOperations local_ops = { diff --git a/hw/9pfs/virtio-9p-synth.c b/hw/9pfs/virtio-9p-synth.c new file mode 100644 index 0000000000..f573616363 --- /dev/null +++ b/hw/9pfs/virtio-9p-synth.c @@ -0,0 +1,571 @@ +/* + * Virtio 9p synthetic file system support + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Malahal Naineni <malahal@us.ibm.com> + * Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#include "hw/virtio.h" +#include "virtio-9p.h" +#include "virtio-9p-xattr.h" +#include "fsdev/qemu-fsdev.h" +#include "virtio-9p-synth.h" + +#include <sys/stat.h> + +/* Root node for synth file system */ +V9fsSynthNode v9fs_synth_root = { + .name = "/", + .actual_attr = { + .mode = 0555 | S_IFDIR, + .nlink = 1, + }, + .attr = &v9fs_synth_root.actual_attr, +}; + +static QemuMutex v9fs_synth_mutex; +static int v9fs_synth_node_count; +/* set to 1 when the synth fs is ready */ +static int v9fs_synth_fs; + +static V9fsSynthNode *v9fs_add_dir_node(V9fsSynthNode *parent, int mode, + const char *name, + V9fsSynthNodeAttr *attr, int inode) +{ + V9fsSynthNode *node; + + /* Add directory type and remove write bits */ + mode = ((mode & 0777) | S_IFDIR) & ~(S_IWUSR | S_IWGRP | S_IWOTH); + node = g_malloc0(sizeof(V9fsSynthNode)); + if (attr) { + /* We are adding .. or . entries */ + node->attr = attr; + node->attr->nlink++; + } else { + node->attr = &node->actual_attr; + node->attr->inode = inode; + node->attr->nlink = 1; + /* We don't allow write to directories */ + node->attr->mode = mode; + node->attr->write = NULL; + node->attr->read = NULL; + } + node->private = node; + strncpy(node->name, name, sizeof(node->name)); + QLIST_INSERT_HEAD_RCU(&parent->child, node, sibling); + return node; +} + +int qemu_v9fs_synth_mkdir(V9fsSynthNode *parent, int mode, + const char *name, V9fsSynthNode **result) +{ + int ret; + V9fsSynthNode *node, *tmp; + + if (!v9fs_synth_fs) { + return EAGAIN; + } + if (!name || (strlen(name) >= NAME_MAX)) { + return EINVAL; + } + if (!parent) { + parent = &v9fs_synth_root; + } + qemu_mutex_lock(&v9fs_synth_mutex); + QLIST_FOREACH(tmp, &parent->child, sibling) { + if (!strcmp(tmp->name, name)) { + ret = EEXIST; + goto err_out; + } + } + /* Add the name */ + node = v9fs_add_dir_node(parent, mode, name, NULL, v9fs_synth_node_count++); + v9fs_add_dir_node(node, parent->attr->mode, "..", + parent->attr, parent->attr->inode); + v9fs_add_dir_node(node, node->attr->mode, ".", + node->attr, node->attr->inode); + *result = node; + ret = 0; +err_out: + qemu_mutex_unlock(&v9fs_synth_mutex); + return ret; +} + +int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode, + const char *name, v9fs_synth_read read, + v9fs_synth_write write, void *arg) +{ + int ret; + V9fsSynthNode *node, *tmp; + + if (!v9fs_synth_fs) { + return EAGAIN; + } + if (!name || (strlen(name) >= NAME_MAX)) { + return EINVAL; + } + if (!parent) { + parent = &v9fs_synth_root; + } + + qemu_mutex_lock(&v9fs_synth_mutex); + QLIST_FOREACH(tmp, &parent->child, sibling) { + if (!strcmp(tmp->name, name)) { + ret = EEXIST; + goto err_out; + } + } + /* Add file type and remove write bits */ + mode = ((mode & 0777) | S_IFREG); + node = g_malloc0(sizeof(V9fsSynthNode)); + node->attr = &node->actual_attr; + node->attr->inode = v9fs_synth_node_count++; + node->attr->nlink = 1; + node->attr->read = read; + node->attr->write = write; + node->attr->mode = mode; + node->private = arg; + strncpy(node->name, name, sizeof(node->name)); + QLIST_INSERT_HEAD_RCU(&parent->child, node, sibling); + ret = 0; +err_out: + qemu_mutex_unlock(&v9fs_synth_mutex); + return ret; +} + +static void v9fs_synth_fill_statbuf(V9fsSynthNode *node, struct stat *stbuf) +{ + stbuf->st_dev = 0; + stbuf->st_ino = node->attr->inode; + stbuf->st_mode = node->attr->mode; + stbuf->st_nlink = node->attr->nlink; + stbuf->st_uid = 0; + stbuf->st_gid = 0; + stbuf->st_rdev = 0; + stbuf->st_size = 0; + stbuf->st_blksize = 0; + stbuf->st_blocks = 0; + stbuf->st_atime = 0; + stbuf->st_mtime = 0; + stbuf->st_ctime = 0; +} + +static int v9fs_synth_lstat(FsContext *fs_ctx, + V9fsPath *fs_path, struct stat *stbuf) +{ + V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data; + + v9fs_synth_fill_statbuf(node, stbuf); + return 0; +} + +static int v9fs_synth_fstat(FsContext *fs_ctx, + V9fsFidOpenState *fs, struct stat *stbuf) +{ + V9fsSynthOpenState *synth_open = fs->private; + v9fs_synth_fill_statbuf(synth_open->node, stbuf); + return 0; +} + +static int v9fs_synth_opendir(FsContext *ctx, + V9fsPath *fs_path, V9fsFidOpenState *fs) +{ + V9fsSynthOpenState *synth_open; + V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data; + + synth_open = g_malloc(sizeof(*synth_open)); + synth_open->node = node; + node->open_count++; + fs->private = synth_open; + return 0; +} + +static int v9fs_synth_closedir(FsContext *ctx, V9fsFidOpenState *fs) +{ + V9fsSynthOpenState *synth_open = fs->private; + V9fsSynthNode *node = synth_open->node; + + node->open_count--; + g_free(synth_open); + fs->private = NULL; + return 0; +} + +static off_t v9fs_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) +{ + V9fsSynthOpenState *synth_open = fs->private; + synth_open->offset = off; +} + +static void v9fs_synth_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) +{ + v9fs_synth_seekdir(ctx, fs, 0); +} + +static void v9fs_synth_direntry(V9fsSynthNode *node, + struct dirent *entry, off_t off) +{ + strcpy(entry->d_name, node->name); + entry->d_ino = node->attr->inode; + entry->d_off = off + 1; +} + +static int v9fs_synth_get_dentry(V9fsSynthNode *dir, struct dirent *entry, + struct dirent **result, off_t off) +{ + int i = 0; + V9fsSynthNode *node; + + rcu_read_lock(); + QLIST_FOREACH(node, &dir->child, sibling) { + /* This is the off child of the directory */ + if (i == off) { + break; + } + i++; + } + rcu_read_unlock(); + if (!node) { + /* end of directory */ + *result = NULL; + return 0; + } + v9fs_synth_direntry(node, entry, off); + *result = entry; + return 0; +} + +static int v9fs_synth_readdir_r(FsContext *ctx, V9fsFidOpenState *fs, + struct dirent *entry, struct dirent **result) +{ + int ret; + 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) { + synth_open->offset++; + } + return ret; +} + +static int v9fs_synth_open(FsContext *ctx, V9fsPath *fs_path, + int flags, V9fsFidOpenState *fs) +{ + V9fsSynthOpenState *synth_open; + V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data; + + synth_open = g_malloc(sizeof(*synth_open)); + synth_open->node = node; + node->open_count++; + fs->private = synth_open; + return 0; +} + +static int v9fs_synth_open2(FsContext *fs_ctx, V9fsPath *dir_path, + const char *name, int flags, + FsCred *credp, V9fsFidOpenState *fs) +{ + errno = ENOSYS; + return -1; +} + +static int v9fs_synth_close(FsContext *ctx, V9fsFidOpenState *fs) +{ + V9fsSynthOpenState *synth_open = fs->private; + V9fsSynthNode *node = synth_open->node; + + node->open_count--; + g_free(synth_open); + fs->private = NULL; + return 0; +} + +static ssize_t v9fs_synth_pwritev(FsContext *ctx, V9fsFidOpenState *fs, + const struct iovec *iov, + int iovcnt, off_t offset) +{ + int i, count = 0, wcount; + V9fsSynthOpenState *synth_open = fs->private; + V9fsSynthNode *node = synth_open->node; + if (!node->attr->write) { + errno = EPERM; + return -1; + } + for (i = 0; i < iovcnt; i++) { + wcount = node->attr->write(iov[i].iov_base, iov[i].iov_len, + offset, node->private); + offset += wcount; + count += wcount; + /* If we wrote less than requested. we are done */ + if (wcount < iov[i].iov_len) { + break; + } + } + return count; +} + +static ssize_t v9fs_synth_preadv(FsContext *ctx, V9fsFidOpenState *fs, + const struct iovec *iov, + int iovcnt, off_t offset) +{ + int i, count = 0, rcount; + V9fsSynthOpenState *synth_open = fs->private; + V9fsSynthNode *node = synth_open->node; + if (!node->attr->read) { + errno = EPERM; + return -1; + } + for (i = 0; i < iovcnt; i++) { + rcount = node->attr->read(iov[i].iov_base, iov[i].iov_len, + offset, node->private); + offset += rcount; + count += rcount; + /* If we read less than requested. we are done */ + if (rcount < iov[i].iov_len) { + break; + } + } + return count; +} + +static int v9fs_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) +{ + errno = EPERM; + return -1; +} + +static int v9fs_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, + const char *buf, FsCred *credp) +{ + errno = EPERM; + return -1; +} + +static ssize_t v9fs_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, + V9fsPath *newpath, const char *buf, FsCred *credp) +{ + errno = EPERM; + return -1; +} + +static int v9fs_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, + const char *newpath) +{ + errno = EPERM; + return -1; +} + +static int v9fs_synth_chown(FsContext *fs_ctx, V9fsPath *path, FsCred *credp) +{ + errno = EPERM; + return -1; +} + +static int v9fs_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) +{ + errno = EPERM; + return -1; +} + +static int v9fs_synth_fsync(FsContext *ctx, V9fsFidOpenState *fs, int datasync) +{ + errno = ENOSYS; + return 0; +} + +static int v9fs_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_namelen = NAME_MAX; + return 0; +} + +static ssize_t v9fs_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, + void *value, size_t size) +{ + errno = ENOTSUP; + return -1; +} + +static int v9fs_synth_lsetxattr(FsContext *ctx, V9fsPath *path, + const char *name, void *value, + size_t size, int flags) +{ + errno = ENOTSUP; + return -1; +} + +static int v9fs_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, + const char *name, V9fsPath *target) +{ + V9fsSynthNode *node; + V9fsSynthNode *dir_node; + + /* "." and ".." are not allowed */ + if (!strcmp(name, ".") || !strcmp(name, "..")) { + errno = EINVAL; + return -1; + + } + if (!dir_path) { + dir_node = &v9fs_synth_root; + } else { + dir_node = *(V9fsSynthNode **)dir_path->data; + } + if (!strcmp(name, "/")) { + node = dir_node; + goto out; + } + /* search for the name in the childern */ + rcu_read_lock(); + QLIST_FOREACH(node, &dir_node->child, sibling) { + if (!strcmp(node->name, name)) { + break; + } + } + rcu_read_unlock(); + + if (!node) { + errno = ENOENT; + return -1; + } +out: + /* Copy the node pointer to fid */ + target->data = g_malloc(sizeof(void *)); + memcpy(target->data, &node, sizeof(void *)); + target->size = sizeof(void *); + return 0; +} + +static int v9fs_synth_renameat(FsContext *ctx, V9fsPath *olddir, + const char *old_name, V9fsPath *newdir, + const char *new_name) +{ + errno = EPERM; + return -1; +} + +static int v9fs_synth_unlinkat(FsContext *ctx, V9fsPath *dir, + const char *name, int flags) +{ + errno = EPERM; + return -1; +} + +static int v9fs_synth_init(FsContext *ctx) +{ + QLIST_INIT(&v9fs_synth_root.child); + qemu_mutex_init(&v9fs_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); + + /* Mark the subsystem is ready for use */ + v9fs_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, +}; diff --git a/hw/9pfs/virtio-9p-synth.h b/hw/9pfs/virtio-9p-synth.h new file mode 100644 index 0000000000..e03f434633 --- /dev/null +++ b/hw/9pfs/virtio-9p-synth.h @@ -0,0 +1,50 @@ +/* + * Virtio 9p + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#include <unistd.h> +#include <sys/types.h> +#include <limits.h> + +typedef struct V9fsSynthNode V9fsSynthNode; +typedef ssize_t (*v9fs_synth_read)(void *buf, int len, off_t offset, + void *arg); +typedef ssize_t (*v9fs_synth_write)(void *buf, int len, off_t offset, + void *arg); +typedef struct V9fsSynthNodeAttr { + int mode; + int inode; + int nlink; + v9fs_synth_read read; + v9fs_synth_write write; +} V9fsSynthNodeAttr; + +struct V9fsSynthNode { + QLIST_HEAD(, V9fsSynthNode) child; + QLIST_ENTRY(V9fsSynthNode) sibling; + char name[NAME_MAX]; + V9fsSynthNodeAttr *attr; + V9fsSynthNodeAttr actual_attr; + void *private; + int open_count; +}; + +typedef struct V9fsSynthOpenState { + off_t offset; + V9fsSynthNode *node; +} V9fsSynthOpenState; + +extern int qemu_v9fs_synth_mkdir(V9fsSynthNode *parent, int mode, + const char *name, V9fsSynthNode **result); +extern int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode, + const char *name, v9fs_synth_read read, + v9fs_synth_write write, void *arg); diff --git a/hw/9pfs/virtio-9p.c b/hw/9pfs/virtio-9p.c index c01c31aa25..0777ece816 100644 --- a/hw/9pfs/virtio-9p.c +++ b/hw/9pfs/virtio-9p.c @@ -17,11 +17,10 @@ #include "hw/virtio-pci.h" #include "virtio-9p.h" #include "fsdev/qemu-fsdev.h" -#include "virtio-9p-debug.h" #include "virtio-9p-xattr.h" #include "virtio-9p-coth.h" +#include "trace.h" -int debug_9p_pdu; int open_fd_hw; int total_open_fd; static int open_fd_rc; @@ -72,6 +71,55 @@ static int omode_to_uflags(int8_t mode) return ret; } +static int dotl_to_at_flags(int flags) +{ + int rflags = 0; + if (flags & P9_DOTL_AT_REMOVEDIR) { + rflags |= AT_REMOVEDIR; + } + return rflags; +} + +struct dotl_openflag_map { + int dotl_flag; + int open_flag; +}; + +static int dotl_to_open_flags(int flags) +{ + int i; + /* + * We have same bits for P9_DOTL_READONLY, P9_DOTL_WRONLY + * and P9_DOTL_NOACCESS + */ + int oflags = flags & O_ACCMODE; + + struct dotl_openflag_map dotl_oflag_map[] = { + { P9_DOTL_CREATE, O_CREAT }, + { P9_DOTL_EXCL, O_EXCL }, + { P9_DOTL_NOCTTY , O_NOCTTY }, + { P9_DOTL_TRUNC, O_TRUNC }, + { P9_DOTL_APPEND, O_APPEND }, + { P9_DOTL_NONBLOCK, O_NONBLOCK } , + { P9_DOTL_DSYNC, O_DSYNC }, + { P9_DOTL_FASYNC, FASYNC }, + { P9_DOTL_DIRECT, O_DIRECT }, + { P9_DOTL_LARGEFILE, O_LARGEFILE }, + { P9_DOTL_DIRECTORY, O_DIRECTORY }, + { P9_DOTL_NOFOLLOW, O_NOFOLLOW }, + { P9_DOTL_NOATIME, O_NOATIME }, + { P9_DOTL_SYNC, O_SYNC }, + }; + + for (i = 0; i < ARRAY_SIZE(dotl_oflag_map); i++) { + if (flags & dotl_oflag_map[i].dotl_flag) { + oflags |= dotl_oflag_map[i].open_flag; + } + } + + return oflags; +} + void cred_init(FsCred *credp) { credp->fc_uid = -1; @@ -80,6 +128,21 @@ void cred_init(FsCred *credp) credp->fc_rdev = -1; } +static int get_dotl_openflags(V9fsState *s, int oflags) +{ + int flags; + /* + * Filter the client open flags + */ + flags = dotl_to_open_flags(oflags); + flags &= ~(O_NOCTTY | O_ASYNC | O_CREAT); + /* + * Ignore direct disk access hint until the server supports it. + */ + flags &= ~O_DIRECT; + return flags; +} + void v9fs_string_init(V9fsString *str) { str->data = NULL; @@ -392,11 +455,11 @@ static int free_fid(V9fsPDU *pdu, V9fsFidState *fidp) if (fidp->fid_type == P9_FID_FILE) { /* If we reclaimed the fd no need to close */ if (fidp->fs.fd != -1) { - retval = v9fs_co_close(pdu, fidp->fs.fd); + retval = v9fs_co_close(pdu, &fidp->fs); } } else if (fidp->fid_type == P9_FID_DIR) { if (fidp->fs.dir != NULL) { - retval = v9fs_co_closedir(pdu, fidp->fs.dir); + retval = v9fs_co_closedir(pdu, &fidp->fs); } } else if (fidp->fid_type == P9_FID_XATTR) { retval = v9fs_xattr_fid_clunk(pdu, fidp); @@ -504,9 +567,9 @@ void v9fs_reclaim_fd(V9fsPDU *pdu) f = reclaim_list; reclaim_list = f->rclm_lst; if (f->fid_type == P9_FID_FILE) { - v9fs_co_close(pdu, f->fs_reclaim.fd); + v9fs_co_close(pdu, &f->fs_reclaim); } else if (f->fid_type == P9_FID_DIR) { - v9fs_co_closedir(pdu, f->fs_reclaim.dir); + v9fs_co_closedir(pdu, &f->fs_reclaim); } f->rclm_lst = NULL; /* @@ -621,9 +684,6 @@ static V9fsPDU *alloc_pdu(V9fsState *s) static void free_pdu(V9fsState *s, V9fsPDU *pdu) { if (pdu) { - if (debug_9p_pdu) { - pprint_pdu(pdu); - } /* * Cancelled pdu are added back to the freelist * by flush request . @@ -909,6 +969,7 @@ static void complete_pdu(V9fsState *s, V9fsPDU *pdu, ssize_t len) if (s->proto_version == V9FS_PROTO_2000L) { id = P9_RLERROR; } + trace_v9fs_rerror(pdu->tag, pdu->id, err); /* Trace ERROR */ } /* fill out the header */ @@ -1210,6 +1271,11 @@ static void v9fs_fix_path(V9fsPath *dst, V9fsPath *src, int len) dst->size++; } +static inline bool is_ro_export(FsContext *ctx) +{ + return ctx->export_flags & V9FS_RDONLY; +} + static void v9fs_version(void *opaque) { V9fsPDU *pdu = opaque; @@ -1218,6 +1284,7 @@ static void v9fs_version(void *opaque) size_t offset = 7; pdu_unmarshal(pdu, offset, "ds", &s->msize, &version); + trace_v9fs_version(pdu->tag, pdu->id, s->msize, version.data); if (!strcmp(version.data, "9P2000.u")) { s->proto_version = V9FS_PROTO_2000U; @@ -1228,6 +1295,8 @@ static void v9fs_version(void *opaque) } offset += pdu_marshal(pdu, offset, "ds", s->msize, &version); + trace_v9fs_version_return(pdu->tag, pdu->id, s->msize, version.data); + complete_pdu(s, pdu, offset); v9fs_string_free(&version); @@ -1246,6 +1315,7 @@ static void v9fs_attach(void *opaque) ssize_t err; pdu_unmarshal(pdu, offset, "ddssd", &fid, &afid, &uname, &aname, &n_uname); + trace_v9fs_attach(pdu->tag, pdu->id, fid, afid, uname.data, aname.data); fidp = alloc_fid(s, fid); if (fidp == NULL) { @@ -1267,6 +1337,8 @@ static void v9fs_attach(void *opaque) } offset += pdu_marshal(pdu, offset, "Q", &qid); err = offset; + trace_v9fs_attach_return(pdu->tag, pdu->id, + qid.type, qid.version, qid.path); out: put_fid(pdu, fidp); out_nofid: @@ -1287,6 +1359,7 @@ static void v9fs_stat(void *opaque) V9fsState *s = pdu->s; pdu_unmarshal(pdu, offset, "d", &fid); + trace_v9fs_stat(pdu->tag, pdu->id, fid); fidp = get_fid(pdu, fid); if (fidp == NULL) { @@ -1303,6 +1376,8 @@ static void v9fs_stat(void *opaque) } offset += pdu_marshal(pdu, offset, "wS", 0, &v9stat); err = offset; + trace_v9fs_stat_return(pdu->tag, pdu->id, v9stat.mode, + v9stat.atime, v9stat.mtime, v9stat.length); v9fs_stat_free(&v9stat); out: put_fid(pdu, fidp); @@ -1323,6 +1398,7 @@ static void v9fs_getattr(void *opaque) V9fsState *s = pdu->s; pdu_unmarshal(pdu, offset, "dq", &fid, &request_mask); + trace_v9fs_getattr(pdu->tag, pdu->id, fid, request_mask); fidp = get_fid(pdu, fid); if (fidp == NULL) { @@ -1338,8 +1414,20 @@ static void v9fs_getattr(void *opaque) goto out; } stat_to_v9stat_dotl(s, &stbuf, &v9stat_dotl); + + /* fill st_gen if requested and supported by underlying fs */ + if (request_mask & P9_STATS_GEN) { + retval = v9fs_co_st_gen(pdu, &fidp->path, stbuf.st_mode, &v9stat_dotl); + if (retval < 0) { + goto out; + } + v9stat_dotl.st_result_mask |= P9_STATS_GEN; + } retval = offset; retval += pdu_marshal(pdu, offset, "A", &v9stat_dotl); + trace_v9fs_getattr_return(pdu->tag, pdu->id, v9stat_dotl.st_result_mask, + v9stat_dotl.st_mode, v9stat_dotl.st_uid, + v9stat_dotl.st_gid); out: put_fid(pdu, fidp); out_nofid: @@ -1470,6 +1558,8 @@ static void v9fs_walk(void *opaque) offset += pdu_unmarshal(pdu, offset, "ddw", &fid, &newfid, &nwnames); + trace_v9fs_walk(pdu->tag, pdu->id, fid, newfid, nwnames); + if (nwnames && nwnames <= P9_MAXWELEM) { wnames = g_malloc0(sizeof(wnames[0]) * nwnames); qids = g_malloc0(sizeof(qids[0]) * nwnames); @@ -1518,6 +1608,7 @@ static void v9fs_walk(void *opaque) v9fs_path_copy(&newfidp->path, &path); } err = v9fs_walk_marshal(pdu, nwnames, qids); + trace_v9fs_walk_return(pdu->tag, pdu->id, nwnames, qids); out: put_fid(pdu, fidp); if (newfidp) { @@ -1560,10 +1651,10 @@ static int32_t get_iounit(V9fsPDU *pdu, V9fsPath *path) static void v9fs_open(void *opaque) { int flags; - int iounit; int32_t fid; int32_t mode; V9fsQID qid; + int iounit = 0; ssize_t err = 0; size_t offset = 7; struct stat stbuf; @@ -1576,6 +1667,8 @@ static void v9fs_open(void *opaque) } else { pdu_unmarshal(pdu, offset, "db", &fid, &mode); } + trace_v9fs_open(pdu->tag, pdu->id, fid, mode); + fidp = get_fid(pdu, fid); if (fidp == NULL) { err = -ENOENT; @@ -1598,13 +1691,18 @@ static void v9fs_open(void *opaque) err = offset; } else { if (s->proto_version == V9FS_PROTO_2000L) { - flags = mode; - flags &= ~(O_NOCTTY | O_ASYNC | O_CREAT); - /* Ignore direct disk access hint until the server supports it. */ - flags &= ~O_DIRECT; + flags = get_dotl_openflags(s, mode); } else { flags = omode_to_uflags(mode); } + if (is_ro_export(&s->ctx)) { + if (mode & O_WRONLY || mode & O_RDWR || + mode & O_APPEND || mode & O_TRUNC) { + err = -EROFS; + goto out; + } + flags |= O_NOATIME; + } err = v9fs_co_open(pdu, fidp, flags); if (err < 0) { goto out; @@ -1622,6 +1720,8 @@ static void v9fs_open(void *opaque) offset += pdu_marshal(pdu, offset, "Qd", &qid, iounit); err = offset; } + trace_v9fs_open_return(pdu->tag, pdu->id, + qid.type, qid.version, qid.path, iounit); out: put_fid(pdu, fidp); out_nofid: @@ -1643,6 +1743,7 @@ static void v9fs_lcreate(void *opaque) pdu_unmarshal(pdu, offset, "dsddd", &dfid, &name, &flags, &mode, &gid); + trace_v9fs_lcreate(pdu->tag, pdu->id, dfid, flags, mode, gid); fidp = get_fid(pdu, dfid); if (fidp == NULL) { @@ -1650,8 +1751,7 @@ static void v9fs_lcreate(void *opaque) goto out_nofid; } - /* Ignore direct disk access hint until the server supports it. */ - flags &= ~O_DIRECT; + flags = get_dotl_openflags(pdu->s, flags); err = v9fs_co_open2(pdu, fidp, &name, gid, flags | O_CREAT, mode, &stbuf); if (err < 0) { @@ -1670,6 +1770,8 @@ static void v9fs_lcreate(void *opaque) stat_to_qid(&stbuf, &qid); offset += pdu_marshal(pdu, offset, "Qd", &qid, iounit); err = offset; + trace_v9fs_lcreate_return(pdu->tag, pdu->id, + qid.type, qid.version, qid.path, iounit); out: put_fid(pdu, fidp); out_nofid: @@ -1688,6 +1790,8 @@ static void v9fs_fsync(void *opaque) V9fsState *s = pdu->s; pdu_unmarshal(pdu, offset, "dd", &fid, &datasync); + trace_v9fs_fsync(pdu->tag, pdu->id, fid, datasync); + fidp = get_fid(pdu, fid); if (fidp == NULL) { err = -ENOENT; @@ -1712,6 +1816,7 @@ static void v9fs_clunk(void *opaque) V9fsState *s = pdu->s; pdu_unmarshal(pdu, offset, "d", &fid); + trace_v9fs_clunk(pdu->tag, pdu->id, fid); fidp = clunk_fid(s, fid); if (fidp == NULL) { @@ -1828,6 +1933,7 @@ static void v9fs_read(void *opaque) V9fsState *s = pdu->s; pdu_unmarshal(pdu, offset, "dqd", &fid, &off, &max_count); + trace_v9fs_read(pdu->tag, pdu->id, fid, off, max_count); fidp = get_fid(pdu, fid); if (fidp == NULL) { @@ -1883,6 +1989,7 @@ static void v9fs_read(void *opaque) } else { err = -EINVAL; } + trace_v9fs_read_return(pdu->tag, pdu->id, count, err); out: put_fid(pdu, fidp); out_nofid: @@ -1970,6 +2077,8 @@ static void v9fs_readdir(void *opaque) pdu_unmarshal(pdu, offset, "dqd", &fid, &initial_offset, &max_count); + trace_v9fs_readdir(pdu->tag, pdu->id, fid, initial_offset, max_count); + fidp = get_fid(pdu, fid); if (fidp == NULL) { retval = -EINVAL; @@ -1992,6 +2101,7 @@ static void v9fs_readdir(void *opaque) retval = offset; retval += pdu_marshal(pdu, offset, "d", count); retval += count; + trace_v9fs_readdir_return(pdu->tag, pdu->id, count, retval); out: put_fid(pdu, fidp); out_nofid: @@ -2059,6 +2169,7 @@ static void v9fs_write(void *opaque) V9fsState *s = pdu->s; pdu_unmarshal(pdu, offset, "dqdv", &fid, &off, &count, sg, &cnt); + trace_v9fs_write(pdu->tag, pdu->id, fid, off, count, cnt); fidp = get_fid(pdu, fid); if (fidp == NULL) { @@ -2102,6 +2213,7 @@ static void v9fs_write(void *opaque) } while (total < count && len > 0); offset += pdu_marshal(pdu, offset, "d", total); err = offset; + trace_v9fs_write_return(pdu->tag, pdu->id, total, err); out: put_fid(pdu, fidp); out_nofid: @@ -2129,6 +2241,8 @@ static void v9fs_create(void *opaque) pdu_unmarshal(pdu, offset, "dsdbs", &fid, &name, &perm, &mode, &extension); + trace_v9fs_create(pdu->tag, pdu->id, fid, name.data, perm, mode); + fidp = get_fid(pdu, fid); if (fidp == NULL) { err = -EINVAL; @@ -2259,6 +2373,8 @@ static void v9fs_create(void *opaque) stat_to_qid(&stbuf, &qid); offset += pdu_marshal(pdu, offset, "Qd", &qid, iounit); err = offset; + trace_v9fs_create_return(pdu->tag, pdu->id, + qid.type, qid.version, qid.path, iounit); out: put_fid(pdu, fidp); out_nofid: @@ -2282,6 +2398,7 @@ static void v9fs_symlink(void *opaque) size_t offset = 7; pdu_unmarshal(pdu, offset, "dssd", &dfid, &name, &symname, &gid); + trace_v9fs_symlink(pdu->tag, pdu->id, dfid, name.data, symname.data, gid); dfidp = get_fid(pdu, dfid); if (dfidp == NULL) { @@ -2295,6 +2412,8 @@ static void v9fs_symlink(void *opaque) stat_to_qid(&stbuf, &qid); offset += pdu_marshal(pdu, offset, "Q", &qid); err = offset; + trace_v9fs_symlink_return(pdu->tag, pdu->id, + qid.type, qid.version, qid.path); out: put_fid(pdu, dfidp); out_nofid: @@ -2312,6 +2431,7 @@ static void v9fs_flush(void *opaque) V9fsState *s = pdu->s; pdu_unmarshal(pdu, offset, "w", &tag); + trace_v9fs_flush(pdu->tag, pdu->id, tag); QLIST_FOREACH(cancel_pdu, &s->active_list, next) { if (cancel_pdu->tag == tag) { @@ -2342,6 +2462,7 @@ static void v9fs_link(void *opaque) int err = 0; pdu_unmarshal(pdu, offset, "dds", &dfid, &oldfid, &name); + trace_v9fs_link(pdu->tag, pdu->id, dfid, oldfid, name.data); dfidp = get_fid(pdu, dfid); if (dfidp == NULL) { @@ -2375,6 +2496,7 @@ static void v9fs_remove(void *opaque) V9fsPDU *pdu = opaque; pdu_unmarshal(pdu, offset, "d", &fid); + trace_v9fs_remove(pdu->tag, pdu->id, fid); fidp = get_fid(pdu, fid); if (fidp == NULL) { @@ -2382,7 +2504,7 @@ static void v9fs_remove(void *opaque) goto out_nofid; } /* if fs driver is not path based, return EOPNOTSUPP */ - if (!pdu->s->ctx.flags & PATHNAME_FSCONTEXT) { + if (!(pdu->s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT)) { err = -EOPNOTSUPP; goto out_err; } @@ -2417,6 +2539,7 @@ static void v9fs_unlinkat(void *opaque) V9fsPDU *pdu = opaque; pdu_unmarshal(pdu, offset, "dsd", &dfid, &name, &flags); + flags = dotl_to_at_flags(flags); dfidp = get_fid(pdu, dfid); if (dfidp == NULL) { @@ -2528,7 +2651,7 @@ static void v9fs_rename(void *opaque) } BUG_ON(fidp->fid_type != P9_FID_NONE); /* if fs driver is not path based, return EOPNOTSUPP */ - if (!pdu->s->ctx.flags & PATHNAME_FSCONTEXT) { + if (!(pdu->s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT)) { err = -EOPNOTSUPP; goto out; } @@ -2601,7 +2724,7 @@ static int v9fs_complete_renameat(V9fsPDU *pdu, int32_t olddirfid, if (err < 0) { goto out; } - if (s->ctx.flags & PATHNAME_FSCONTEXT) { + if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) { /* Only for path based fid we need to do the below fixup */ v9fs_fix_fid_paths(pdu, &olddirfidp->path, old_name, &newdirfidp->path, new_name); @@ -2653,6 +2776,8 @@ static void v9fs_wstat(void *opaque) V9fsState *s = pdu->s; pdu_unmarshal(pdu, offset, "dwS", &fid, &unused, &v9stat); + trace_v9fs_wstat(pdu->tag, pdu->id, fid, + v9stat.mode, v9stat.atime, v9stat.mtime); fidp = get_fid(pdu, fid); if (fidp == NULL) { @@ -2821,6 +2946,7 @@ static void v9fs_mknod(void *opaque) pdu_unmarshal(pdu, offset, "dsdddd", &fid, &name, &mode, &major, &minor, &gid); + trace_v9fs_mknod(pdu->tag, pdu->id, fid, mode, major, minor); fidp = get_fid(pdu, fid); if (fidp == NULL) { @@ -2835,6 +2961,8 @@ static void v9fs_mknod(void *opaque) stat_to_qid(&stbuf, &qid); err = offset; err += pdu_marshal(pdu, offset, "Q", &qid); + trace_v9fs_mknod_return(pdu->tag, pdu->id, + qid.type, qid.version, qid.path); out: put_fid(pdu, fidp); out_nofid: @@ -2865,6 +2993,10 @@ static void v9fs_lock(void *opaque) pdu_unmarshal(pdu, offset, "dbdqqds", &fid, &flock->type, &flock->flags, &flock->start, &flock->length, &flock->proc_id, &flock->client_id); + + trace_v9fs_lock(pdu->tag, pdu->id, fid, + flock->type, flock->start, flock->length); + status = P9_LOCK_ERROR; /* We support only block flag now (that too ignored currently) */ @@ -2877,7 +3009,7 @@ static void v9fs_lock(void *opaque) err = -ENOENT; goto out_nofid; } - err = v9fs_co_fstat(pdu, fidp->fs.fd, &stbuf); + err = v9fs_co_fstat(pdu, fidp, &stbuf); if (err < 0) { goto out; } @@ -2887,6 +3019,7 @@ out: out_nofid: err = offset; err += pdu_marshal(pdu, offset, "b", status); + trace_v9fs_lock_return(pdu->tag, pdu->id, status); complete_pdu(s, pdu, err); v9fs_string_free(&flock->client_id); g_free(flock); @@ -2911,20 +3044,25 @@ static void v9fs_getlock(void *opaque) &glock->start, &glock->length, &glock->proc_id, &glock->client_id); + trace_v9fs_getlock(pdu->tag, pdu->id, fid, + glock->type, glock->start, glock->length); + fidp = get_fid(pdu, fid); if (fidp == NULL) { err = -ENOENT; goto out_nofid; } - err = v9fs_co_fstat(pdu, fidp->fs.fd, &stbuf); + err = v9fs_co_fstat(pdu, fidp, &stbuf); if (err < 0) { goto out; } - glock->type = F_UNLCK; + glock->type = P9_LOCK_TYPE_UNLCK; offset += pdu_marshal(pdu, offset, "bqqds", glock->type, glock->start, glock->length, glock->proc_id, &glock->client_id); err = offset; + trace_v9fs_getlock_return(pdu->tag, pdu->id, glock->type, glock->start, + glock->length, glock->proc_id); out: put_fid(pdu, fidp); out_nofid: @@ -2948,6 +3086,8 @@ static void v9fs_mkdir(void *opaque) pdu_unmarshal(pdu, offset, "dsdd", &fid, &name, &mode, &gid); + trace_v9fs_mkdir(pdu->tag, pdu->id, fid, name.data, mode, gid); + fidp = get_fid(pdu, fid); if (fidp == NULL) { err = -ENOENT; @@ -2960,6 +3100,8 @@ static void v9fs_mkdir(void *opaque) stat_to_qid(&stbuf, &qid); offset += pdu_marshal(pdu, offset, "Q", &qid); err = offset; + trace_v9fs_mkdir_return(pdu->tag, pdu->id, + qid.type, qid.version, qid.path, err); out: put_fid(pdu, fidp); out_nofid: @@ -2980,6 +3122,8 @@ static void v9fs_xattrwalk(void *opaque) V9fsState *s = pdu->s; pdu_unmarshal(pdu, offset, "dds", &fid, &newfid, &name); + trace_v9fs_xattrwalk(pdu->tag, pdu->id, fid, newfid, name.data); + file_fidp = get_fid(pdu, fid); if (file_fidp == NULL) { err = -ENOENT; @@ -3050,6 +3194,7 @@ static void v9fs_xattrwalk(void *opaque) offset += pdu_marshal(pdu, offset, "q", size); err = offset; } + trace_v9fs_xattrwalk_return(pdu->tag, pdu->id, size); out: put_fid(pdu, file_fidp); if (xattr_fidp) { @@ -3075,6 +3220,7 @@ static void v9fs_xattrcreate(void *opaque) pdu_unmarshal(pdu, offset, "dsqd", &fid, &name, &size, &flags); + trace_v9fs_xattrcreate(pdu->tag, pdu->id, fid, name.data, size, flags); file_fidp = get_fid(pdu, fid); if (file_fidp == NULL) { @@ -3111,6 +3257,7 @@ static void v9fs_readlink(void *opaque) V9fsFidState *fidp; pdu_unmarshal(pdu, offset, "d", &fid); + trace_v9fs_readlink(pdu->tag, pdu->id, fid); fidp = get_fid(pdu, fid); if (fidp == NULL) { err = -ENOENT; @@ -3124,6 +3271,7 @@ static void v9fs_readlink(void *opaque) } offset += pdu_marshal(pdu, offset, "s", &target); err = offset; + trace_v9fs_readlink_return(pdu->tag, pdu->id, target.data); v9fs_string_free(&target); out: put_fid(pdu, fidp); @@ -3174,20 +3322,54 @@ static void v9fs_op_not_supp(void *opaque) complete_pdu(pdu->s, pdu, -EOPNOTSUPP); } +static void v9fs_fs_ro(void *opaque) +{ + V9fsPDU *pdu = opaque; + complete_pdu(pdu->s, pdu, -EROFS); +} + +static inline bool is_read_only_op(V9fsPDU *pdu) +{ + switch (pdu->id) { + case P9_TREADDIR: + case P9_TSTATFS: + case P9_TGETATTR: + case P9_TXATTRWALK: + case P9_TLOCK: + case P9_TGETLOCK: + case P9_TREADLINK: + case P9_TVERSION: + case P9_TLOPEN: + case P9_TATTACH: + case P9_TSTAT: + case P9_TWALK: + case P9_TCLUNK: + case P9_TFSYNC: + case P9_TOPEN: + case P9_TREAD: + case P9_TAUTH: + case P9_TFLUSH: + return 1; + default: + return 0; + } +} + static void submit_pdu(V9fsState *s, V9fsPDU *pdu) { Coroutine *co; CoroutineEntry *handler; - if (debug_9p_pdu) { - pprint_pdu(pdu); - } if (pdu->id >= ARRAY_SIZE(pdu_co_handlers) || (pdu_co_handlers[pdu->id] == NULL)) { handler = v9fs_op_not_supp; } else { handler = pdu_co_handlers[pdu->id]; } + + 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); } diff --git a/hw/9pfs/virtio-9p.h b/hw/9pfs/virtio-9p.h index 60b8a56e57..7f883563d6 100644 --- a/hw/9pfs/virtio-9p.h +++ b/hw/9pfs/virtio-9p.h @@ -204,20 +204,29 @@ typedef struct V9fsXattr int flags; } V9fsXattr; +/* + * Filled by fs driver on open and other + * calls. + */ +union V9fsFidOpenState { + int fd; + DIR *dir; + V9fsXattr xattr; + /* + * private pointer for fs drivers, that + * have its own internal representation of + * open files. + */ + void *private; +}; + struct V9fsFidState { int fid_type; int32_t fid; V9fsPath path; - union { - int fd; - DIR *dir; - V9fsXattr xattr; - } fs; - union { - int fd; - DIR *dir; - } fs_reclaim; + V9fsFidOpenState fs; + V9fsFidOpenState fs_reclaim; int flags; int open_flags; uid_t uid; @@ -352,6 +361,35 @@ typedef struct V9fsMkState { V9fsString fullname; } V9fsMkState; +/* 9p2000.L open flags */ +#define P9_DOTL_RDONLY 00000000 +#define P9_DOTL_WRONLY 00000001 +#define P9_DOTL_RDWR 00000002 +#define P9_DOTL_NOACCESS 00000003 +#define P9_DOTL_CREATE 00000100 +#define P9_DOTL_EXCL 00000200 +#define P9_DOTL_NOCTTY 00000400 +#define P9_DOTL_TRUNC 00001000 +#define P9_DOTL_APPEND 00002000 +#define P9_DOTL_NONBLOCK 00004000 +#define P9_DOTL_DSYNC 00010000 +#define P9_DOTL_FASYNC 00020000 +#define P9_DOTL_DIRECT 00040000 +#define P9_DOTL_LARGEFILE 00100000 +#define P9_DOTL_DIRECTORY 00200000 +#define P9_DOTL_NOFOLLOW 00400000 +#define P9_DOTL_NOATIME 01000000 +#define P9_DOTL_CLOEXEC 02000000 +#define P9_DOTL_SYNC 04000000 + +/* 9p2000.L at flags */ +#define P9_DOTL_AT_REMOVEDIR 0x200 + +/* 9P2000.L lock type */ +#define P9_LOCK_TYPE_RDLCK 0 +#define P9_LOCK_TYPE_WRLCK 1 +#define P9_LOCK_TYPE_UNLCK 2 + #define P9_LOCK_SUCCESS 0 #define P9_LOCK_BLOCKED 1 #define P9_LOCK_ERROR 2 @@ -393,21 +431,21 @@ static inline size_t do_pdu_unpack(void *dst, struct iovec *sg, int sg_count, static inline void v9fs_path_write_lock(V9fsState *s) { - if (s->ctx.flags & PATHNAME_FSCONTEXT) { + if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) { qemu_co_rwlock_wrlock(&s->rename_lock); } } static inline void v9fs_path_read_lock(V9fsState *s) { - if (s->ctx.flags & PATHNAME_FSCONTEXT) { + if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) { qemu_co_rwlock_rdlock(&s->rename_lock); } } static inline void v9fs_path_unlock(V9fsState *s) { - if (s->ctx.flags & PATHNAME_FSCONTEXT) { + if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) { qemu_co_rwlock_unlock(&s->rename_lock); } } diff --git a/hw/ac97.c b/hw/ac97.c index bc69d4e345..6800af43a4 100644 --- a/hw/ac97.c +++ b/hw/ac97.c @@ -18,6 +18,7 @@ #include "audiodev.h" #include "audio/audio.h" #include "pci.h" +#include "dma.h" enum { AC97_Reset = 0x00, @@ -224,7 +225,7 @@ static void fetch_bd (AC97LinkState *s, AC97BusMasterRegs *r) { uint8_t b[8]; - cpu_physical_memory_read (r->bdbar + r->civ * 8, b, 8); + pci_dma_read (&s->dev, r->bdbar + r->civ * 8, b, 8); r->bd_valid = 1; r->bd.addr = le32_to_cpu (*(uint32_t *) &b[0]) & ~3; r->bd.ctl_len = le32_to_cpu (*(uint32_t *) &b[4]); @@ -973,7 +974,7 @@ static int write_audio (AC97LinkState *s, AC97BusMasterRegs *r, while (temp) { int copied; to_copy = audio_MIN (temp, sizeof (tmpbuf)); - cpu_physical_memory_read (addr, tmpbuf, to_copy); + pci_dma_read (&s->dev, addr, tmpbuf, to_copy); copied = AUD_write (s->voice_po, tmpbuf, to_copy); dolog ("write_audio max=%x to_copy=%x copied=%x\n", max, to_copy, copied); @@ -1054,7 +1055,7 @@ static int read_audio (AC97LinkState *s, AC97BusMasterRegs *r, *stop = 1; break; } - cpu_physical_memory_write (addr, tmpbuf, acquired); + pci_dma_write (&s->dev, addr, tmpbuf, acquired); temp -= acquired; addr += acquired; nread += acquired; diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c index 29f0f76c35..d9075e6611 100644 --- a/hw/acpi_piix4.c +++ b/hw/acpi_piix4.c @@ -276,7 +276,7 @@ static void piix4_update_hotplug(PIIX4PMState *s) s->pci0_hotplug_enable = ~0; - QLIST_FOREACH_SAFE(qdev, &bus->children, sibling, next) { + QTAILQ_FOREACH_SAFE(qdev, &bus->children, sibling, next) { PCIDeviceInfo *info = container_of(qdev->info, PCIDeviceInfo, qdev); PCIDevice *pdev = DO_UPCAST(PCIDevice, qdev, qdev); int slot = PCI_SLOT(pdev->devfn); @@ -486,7 +486,7 @@ static void pciej_write(void *opaque, uint32_t addr, uint32_t val) PCIDeviceInfo *info; int slot = ffs(val) - 1; - QLIST_FOREACH_SAFE(qdev, &bus->children, sibling, next) { + QTAILQ_FOREACH_SAFE(qdev, &bus->children, sibling, next) { dev = DO_UPCAST(PCIDevice, qdev, qdev); info = container_of(qdev->info, PCIDeviceInfo, qdev); if (PCI_SLOT(dev->devfn) == slot && !info->no_hotplug) { diff --git a/hw/an5206.c b/hw/an5206.c index 481ae60449..3fe1f00d9b 100644 --- a/hw/an5206.c +++ b/hw/an5206.c @@ -7,7 +7,6 @@ */ #include "hw.h" -#include "pc.h" #include "mcf.h" #include "boards.h" #include "loader.h" @@ -18,15 +17,6 @@ #define AN5206_MBAR_ADDR 0x10000000 #define AN5206_RAMBAR_ADDR 0x20000000 -/* Stub functions for hardware that doesn't exist. */ -void pic_info(Monitor *mon) -{ -} - -void irq_info(Monitor *mon) -{ -} - /* Board init. */ static void an5206_init(ram_addr_t ram_size, diff --git a/hw/apic.c b/hw/apic.c index d8f56c8b76..8289eef5b8 100644 --- a/hw/apic.c +++ b/hw/apic.c @@ -23,6 +23,7 @@ #include "host-utils.h" #include "sysbus.h" #include "trace.h" +#include "pc.h" /* APIC Local Vector Table */ #define APIC_LVT_TIMER 0 @@ -399,6 +400,9 @@ static void apic_update_irq(APICState *s) } if (apic_irq_pending(s) > 0) { cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD); + } else if (apic_accept_pic_intr(&s->busdev.qdev) && + pic_get_output(isa_pic)) { + apic_deliver_pic_intr(&s->busdev.qdev, 1); } } diff --git a/hw/arm_gic.c b/hw/arm_gic.c index 83213dd406..8dd8742f70 100644 --- a/hw/arm_gic.c +++ b/hw/arm_gic.c @@ -658,9 +658,6 @@ static void gic_save(QEMUFile *f, void *opaque) qemu_put_be32(f, s->enabled); for (i = 0; i < NUM_CPU(s); i++) { qemu_put_be32(f, s->cpu_enabled[i]); -#ifndef NVIC - qemu_put_be32(f, s->irq_target[i]); -#endif for (j = 0; j < 32; j++) qemu_put_be32(f, s->priority1[j][i]); for (j = 0; j < GIC_NIRQ; j++) @@ -674,6 +671,9 @@ static void gic_save(QEMUFile *f, void *opaque) qemu_put_be32(f, s->priority2[i]); } for (i = 0; i < GIC_NIRQ; i++) { +#ifndef NVIC + qemu_put_be32(f, s->irq_target[i]); +#endif qemu_put_byte(f, s->irq_state[i].enabled); qemu_put_byte(f, s->irq_state[i].pending); qemu_put_byte(f, s->irq_state[i].active); @@ -689,15 +689,12 @@ static int gic_load(QEMUFile *f, void *opaque, int version_id) int i; int j; - if (version_id != 1) + if (version_id != 2) return -EINVAL; s->enabled = qemu_get_be32(f); for (i = 0; i < NUM_CPU(s); i++) { s->cpu_enabled[i] = qemu_get_be32(f); -#ifndef NVIC - s->irq_target[i] = qemu_get_be32(f); -#endif for (j = 0; j < 32; j++) s->priority1[j][i] = qemu_get_be32(f); for (j = 0; j < GIC_NIRQ; j++) @@ -711,6 +708,9 @@ static int gic_load(QEMUFile *f, void *opaque, int version_id) s->priority2[i] = qemu_get_be32(f); } for (i = 0; i < GIC_NIRQ; i++) { +#ifndef NVIC + s->irq_target[i] = qemu_get_be32(f); +#endif s->irq_state[i].enabled = qemu_get_byte(f); s->irq_state[i].pending = qemu_get_byte(f); s->irq_state[i].active = qemu_get_byte(f); @@ -739,5 +739,5 @@ static void gic_init(gic_state *s) } memory_region_init_io(&s->iomem, &gic_dist_ops, s, "gic_dist", 0x1000); gic_reset(s); - register_savevm(NULL, "arm_gic", -1, 1, gic_save, gic_load, s); + register_savevm(NULL, "arm_gic", -1, 2, gic_save, gic_load, s); } diff --git a/hw/arm_pic.c b/hw/arm_pic.c index 41f8d3e112..a2e8a73301 100644 --- a/hw/arm_pic.c +++ b/hw/arm_pic.c @@ -8,19 +8,8 @@ */ #include "hw.h" -#include "pc.h" #include "arm-misc.h" -/* Stub functions for hardware that doesn't exist. */ -void pic_info(Monitor *mon) -{ -} - -void irq_info(Monitor *mon) -{ -} - - /* Input 0 is IRQ and input 1 is FIQ. */ static void arm_pic_cpu_handler(void *opaque, int irq, int level) { diff --git a/hw/audiodev.h b/hw/audiodev.h index 8e930b21ae..d60c3498ee 100644 --- a/hw/audiodev.h +++ b/hw/audiodev.h @@ -11,7 +11,7 @@ int Adlib_init(qemu_irq *pic); int GUS_init(qemu_irq *pic); /* ac97.c */ -int ac97_init(PCIBus *buf); +int ac97_init(PCIBus *bus); /* cs4231a.c */ int cs4231a_init(qemu_irq *pic); diff --git a/hw/collie.c b/hw/collie.c index a10cc1b90c..8dd6e4ec7e 100644 --- a/hw/collie.c +++ b/hw/collie.c @@ -13,6 +13,7 @@ #include "arm-misc.h" #include "flash.h" #include "blockdev.h" +#include "exec-memory.h" static struct arm_boot_info collie_binfo = { .loader_start = SA_SDCS0, @@ -26,12 +27,13 @@ static void collie_init(ram_addr_t ram_size, { StrongARMState *s; DriveInfo *dinfo; + MemoryRegion *sysmem = get_system_memory(); if (!cpu_model) { cpu_model = "sa1110"; } - s = sa1110_init(collie_binfo.ram_size, cpu_model); + s = sa1110_init(sysmem, collie_binfo.ram_size, cpu_model); dinfo = drive_get(IF_PFLASH, 0, 0); pflash_cfi01_register(SA_CS0, NULL, "collie.fl1", 0x02000000, diff --git a/hw/cris_pic_cpu.c b/hw/cris_pic_cpu.c index 7f1e4ab855..06ae484950 100644 --- a/hw/cris_pic_cpu.c +++ b/hw/cris_pic_cpu.c @@ -24,16 +24,10 @@ #include "sysbus.h" #include "hw.h" -#include "pc.h" #include "etraxfs.h" #define D(x) -void pic_info(Monitor *mon) -{} -void irq_info(Monitor *mon) -{} - static void cris_pic_cpu_handler(void *opaque, int irq, int level) { CPUState *env = (CPUState *)opaque; diff --git a/hw/devices.h b/hw/devices.h index 8ac384ff20..1a55c1e905 100644 --- a/hw/devices.h +++ b/hw/devices.h @@ -53,7 +53,8 @@ void retu_key_event(void *retu, int state); /* tc6393xb.c */ typedef struct TC6393xbState TC6393xbState; #define TC6393XB_RAM 0x110000 /* amount of ram for Video and USB */ -TC6393xbState *tc6393xb_init(uint32_t base, qemu_irq irq); +TC6393xbState *tc6393xb_init(struct MemoryRegion *sysmem, + uint32_t base, qemu_irq irq); void tc6393xb_gpio_out_set(TC6393xbState *s, int line, qemu_irq handler); qemu_irq *tc6393xb_gpio_in_get(TC6393xbState *s); diff --git a/hw/ds1225y.c b/hw/ds1225y.c index 9875c445f8..6852a61d08 100644 --- a/hw/ds1225y.c +++ b/hw/ds1225y.c @@ -29,7 +29,7 @@ typedef struct { DeviceState qdev; uint32_t chip_size; char *filename; - QEMUFile *file; + FILE *file; uint8_t *contents; } NvRamState; @@ -70,9 +70,9 @@ static void nvram_writeb (void *opaque, target_phys_addr_t addr, uint32_t val) s->contents[addr] = val; if (s->file) { - qemu_fseek(s->file, addr, SEEK_SET); - qemu_put_byte(s->file, (int)val); - qemu_fflush(s->file); + fseek(s->file, addr, SEEK_SET); + fputc(val, s->file); + fflush(s->file); } } @@ -108,15 +108,17 @@ static int nvram_post_load(void *opaque, int version_id) /* Close file, as filename may has changed in load/store process */ if (s->file) { - qemu_fclose(s->file); + fclose(s->file); } /* Write back nvram contents */ - s->file = qemu_fopen(s->filename, "wb"); + s->file = fopen(s->filename, "wb"); if (s->file) { /* Write back contents, as 'wb' mode cleaned the file */ - qemu_put_buffer(s->file, s->contents, s->chip_size); - qemu_fflush(s->file); + if (fwrite(s->contents, s->chip_size, 1, s->file) != 1) { + printf("nvram_post_load: short write\n"); + } + fflush(s->file); } return 0; @@ -143,7 +145,7 @@ typedef struct { static int nvram_sysbus_initfn(SysBusDevice *dev) { NvRamState *s = &FROM_SYSBUS(SysBusNvRamState, dev)->nvram; - QEMUFile *file; + FILE *file; int s_io; s->contents = g_malloc0(s->chip_size); @@ -153,11 +155,13 @@ static int nvram_sysbus_initfn(SysBusDevice *dev) sysbus_init_mmio(dev, s->chip_size, s_io); /* Read current file */ - file = qemu_fopen(s->filename, "rb"); + file = fopen(s->filename, "rb"); if (file) { /* Read nvram contents */ - qemu_get_buffer(file, s->contents, s->chip_size); - qemu_fclose(file); + if (fread(s->contents, s->chip_size, 1, file) != 1) { + printf("nvram_sysbus_initfn: short read\n"); + } + fclose(file); } nvram_post_load(s, 0); diff --git a/hw/e1000.c b/hw/e1000.c index ce8fc8b510..986ed9cf79 100644 --- a/hw/e1000.c +++ b/hw/e1000.c @@ -31,6 +31,7 @@ #include "net/checksum.h" #include "loader.h" #include "sysemu.h" +#include "dma.h" #include "e1000_hw.h" @@ -465,7 +466,7 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp) bytes = split_size; if (tp->size + bytes > msh) bytes = msh - tp->size; - cpu_physical_memory_read(addr, tp->data + tp->size, bytes); + pci_dma_read(&s->dev, addr, tp->data + tp->size, bytes); if ((sz = tp->size + bytes) >= hdr && tp->size < hdr) memmove(tp->header, tp->data, hdr); tp->size = sz; @@ -480,7 +481,7 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp) // context descriptor TSE is not set, while data descriptor TSE is set DBGOUT(TXERR, "TCP segmentaion Error\n"); } else { - cpu_physical_memory_read(addr, tp->data + tp->size, split_size); + pci_dma_read(&s->dev, addr, tp->data + tp->size, split_size); tp->size += split_size; } @@ -496,7 +497,7 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp) } static uint32_t -txdesc_writeback(target_phys_addr_t base, struct e1000_tx_desc *dp) +txdesc_writeback(E1000State *s, dma_addr_t base, struct e1000_tx_desc *dp) { uint32_t txd_upper, txd_lower = le32_to_cpu(dp->lower.data); @@ -505,8 +506,8 @@ txdesc_writeback(target_phys_addr_t base, struct e1000_tx_desc *dp) txd_upper = (le32_to_cpu(dp->upper.data) | E1000_TXD_STAT_DD) & ~(E1000_TXD_STAT_EC | E1000_TXD_STAT_LC | E1000_TXD_STAT_TU); dp->upper.data = cpu_to_le32(txd_upper); - cpu_physical_memory_write(base + ((char *)&dp->upper - (char *)dp), - (void *)&dp->upper, sizeof(dp->upper)); + pci_dma_write(&s->dev, base + ((char *)&dp->upper - (char *)dp), + (void *)&dp->upper, sizeof(dp->upper)); return E1000_ICR_TXDW; } @@ -521,7 +522,7 @@ static uint64_t tx_desc_base(E1000State *s) static void start_xmit(E1000State *s) { - target_phys_addr_t base; + dma_addr_t base; struct e1000_tx_desc desc; uint32_t tdh_start = s->mac_reg[TDH], cause = E1000_ICS_TXQE; @@ -533,14 +534,14 @@ start_xmit(E1000State *s) while (s->mac_reg[TDH] != s->mac_reg[TDT]) { base = tx_desc_base(s) + sizeof(struct e1000_tx_desc) * s->mac_reg[TDH]; - cpu_physical_memory_read(base, (void *)&desc, sizeof(desc)); + pci_dma_read(&s->dev, base, (void *)&desc, sizeof(desc)); DBGOUT(TX, "index %d: %p : %x %x\n", s->mac_reg[TDH], (void *)(intptr_t)desc.buffer_addr, desc.lower.data, desc.upper.data); process_tx_desc(s, &desc); - cause |= txdesc_writeback(base, &desc); + cause |= txdesc_writeback(s, base, &desc); if (++s->mac_reg[TDH] * sizeof(desc) >= s->mac_reg[TDLEN]) s->mac_reg[TDH] = 0; @@ -668,7 +669,7 @@ e1000_receive(VLANClientState *nc, const uint8_t *buf, size_t size) { E1000State *s = DO_UPCAST(NICState, nc, nc)->opaque; struct e1000_rx_desc desc; - target_phys_addr_t base; + dma_addr_t base; unsigned int n, rdt; uint32_t rdh_start; uint16_t vlan_special = 0; @@ -713,7 +714,7 @@ e1000_receive(VLANClientState *nc, const uint8_t *buf, size_t size) desc_size = s->rxbuf_size; } base = rx_desc_base(s) + sizeof(desc) * s->mac_reg[RDH]; - cpu_physical_memory_read(base, (void *)&desc, sizeof(desc)); + pci_dma_read(&s->dev, base, (void *)&desc, sizeof(desc)); desc.special = vlan_special; desc.status |= (vlan_status | E1000_RXD_STAT_DD); if (desc.buffer_addr) { @@ -722,9 +723,9 @@ e1000_receive(VLANClientState *nc, const uint8_t *buf, size_t size) if (copy_size > s->rxbuf_size) { copy_size = s->rxbuf_size; } - cpu_physical_memory_write(le64_to_cpu(desc.buffer_addr), - (void *)(buf + desc_offset + vlan_offset), - copy_size); + pci_dma_write(&s->dev, le64_to_cpu(desc.buffer_addr), + (void *)(buf + desc_offset + vlan_offset), + copy_size); } desc_offset += desc_size; desc.length = cpu_to_le16(desc_size); @@ -738,7 +739,7 @@ e1000_receive(VLANClientState *nc, const uint8_t *buf, size_t size) } else { // as per intel docs; skip descriptors with null buf addr DBGOUT(RX, "Null RX descriptor!!\n"); } - cpu_physical_memory_write(base, (void *)&desc, sizeof(desc)); + pci_dma_write(&s->dev, base, (void *)&desc, sizeof(desc)); if (++s->mac_reg[RDH] * sizeof(desc) >= s->mac_reg[RDLEN]) s->mac_reg[RDH] = 0; diff --git a/hw/eepro100.c b/hw/eepro100.c index 4e3c52f50a..7d59e7136d 100644 --- a/hw/eepro100.c +++ b/hw/eepro100.c @@ -46,6 +46,7 @@ #include "net.h" #include "eeprom93xx.h" #include "sysemu.h" +#include "dma.h" /* QEMU sends frames smaller than 60 bytes to ethernet nics. * Such frames are rejected by real nics and their emulations. @@ -315,38 +316,6 @@ static const uint16_t eepro100_mdi_mask[] = { 0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; -/* Read a 16 bit little endian value from physical memory. */ -static uint16_t e100_ldw_le_phys(target_phys_addr_t addr) -{ - /* Load 16 bit (little endian) word from emulated hardware. */ - uint16_t val; - cpu_physical_memory_read(addr, &val, sizeof(val)); - return le16_to_cpu(val); -} - -/* Read a 32 bit little endian value from physical memory. */ -static uint32_t e100_ldl_le_phys(target_phys_addr_t addr) -{ - /* Load 32 bit (little endian) word from emulated hardware. */ - uint32_t val; - cpu_physical_memory_read(addr, &val, sizeof(val)); - return le32_to_cpu(val); -} - -/* Write a 16 bit little endian value to physical memory. */ -static void e100_stw_le_phys(target_phys_addr_t addr, uint16_t val) -{ - val = cpu_to_le16(val); - cpu_physical_memory_write(addr, &val, sizeof(val)); -} - -/* Write a 32 bit little endian value to physical memory. */ -static void e100_stl_le_phys(target_phys_addr_t addr, uint32_t val) -{ - val = cpu_to_le32(val); - cpu_physical_memory_write(addr, &val, sizeof(val)); -} - #define POLYNOMIAL 0x04c11db6 /* From FreeBSD */ @@ -744,21 +713,26 @@ static void dump_statistics(EEPRO100State * s) * values which really matter. * Number of data should check configuration!!! */ - cpu_physical_memory_write(s->statsaddr, &s->statistics, s->stats_size); - e100_stl_le_phys(s->statsaddr + 0, s->statistics.tx_good_frames); - e100_stl_le_phys(s->statsaddr + 36, s->statistics.rx_good_frames); - e100_stl_le_phys(s->statsaddr + 48, s->statistics.rx_resource_errors); - e100_stl_le_phys(s->statsaddr + 60, s->statistics.rx_short_frame_errors); + pci_dma_write(&s->dev, s->statsaddr, + (uint8_t *) &s->statistics, s->stats_size); + stl_le_pci_dma(&s->dev, s->statsaddr + 0, + s->statistics.tx_good_frames); + stl_le_pci_dma(&s->dev, s->statsaddr + 36, + s->statistics.rx_good_frames); + stl_le_pci_dma(&s->dev, s->statsaddr + 48, + s->statistics.rx_resource_errors); + stl_le_pci_dma(&s->dev, s->statsaddr + 60, + s->statistics.rx_short_frame_errors); #if 0 - e100_stw_le_phys(s->statsaddr + 76, s->statistics.xmt_tco_frames); - e100_stw_le_phys(s->statsaddr + 78, s->statistics.rcv_tco_frames); + stw_le_pci_dma(&s->dev, s->statsaddr + 76, s->statistics.xmt_tco_frames); + stw_le_pci_dma(&s->dev, s->statsaddr + 78, s->statistics.rcv_tco_frames); missing("CU dump statistical counters"); #endif } static void read_cb(EEPRO100State *s) { - cpu_physical_memory_read(s->cb_address, &s->tx, sizeof(s->tx)); + pci_dma_read(&s->dev, s->cb_address, (uint8_t *) &s->tx, sizeof(s->tx)); s->tx.status = le16_to_cpu(s->tx.status); s->tx.command = le16_to_cpu(s->tx.command); s->tx.link = le32_to_cpu(s->tx.link); @@ -788,18 +762,17 @@ static void tx_command(EEPRO100State *s) } assert(tcb_bytes <= sizeof(buf)); while (size < tcb_bytes) { - uint32_t tx_buffer_address = e100_ldl_le_phys(tbd_address); - uint16_t tx_buffer_size = e100_ldw_le_phys(tbd_address + 4); + uint32_t tx_buffer_address = ldl_le_pci_dma(&s->dev, tbd_address); + uint16_t tx_buffer_size = lduw_le_pci_dma(&s->dev, tbd_address + 4); #if 0 - uint16_t tx_buffer_el = e100_ldw_le_phys(tbd_address + 6); + uint16_t tx_buffer_el = lduw_le_pci_dma(&s->dev, tbd_address + 6); #endif tbd_address += 8; TRACE(RXTX, logout ("TBD (simplified mode): buffer address 0x%08x, size 0x%04x\n", tx_buffer_address, tx_buffer_size)); tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size); - cpu_physical_memory_read(tx_buffer_address, &buf[size], - tx_buffer_size); + pci_dma_read(&s->dev, tx_buffer_address, &buf[size], tx_buffer_size); size += tx_buffer_size; } if (tbd_array == 0xffffffff) { @@ -810,16 +783,19 @@ static void tx_command(EEPRO100State *s) if (s->has_extended_tcb_support && !(s->configuration[6] & BIT(4))) { /* Extended Flexible TCB. */ for (; tbd_count < 2; tbd_count++) { - uint32_t tx_buffer_address = e100_ldl_le_phys(tbd_address); - uint16_t tx_buffer_size = e100_ldw_le_phys(tbd_address + 4); - uint16_t tx_buffer_el = e100_ldw_le_phys(tbd_address + 6); + uint32_t tx_buffer_address = ldl_le_pci_dma(&s->dev, + tbd_address); + uint16_t tx_buffer_size = lduw_le_pci_dma(&s->dev, + tbd_address + 4); + uint16_t tx_buffer_el = lduw_le_pci_dma(&s->dev, + tbd_address + 6); tbd_address += 8; TRACE(RXTX, logout ("TBD (extended flexible mode): buffer address 0x%08x, size 0x%04x\n", tx_buffer_address, tx_buffer_size)); tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size); - cpu_physical_memory_read(tx_buffer_address, &buf[size], - tx_buffer_size); + pci_dma_read(&s->dev, tx_buffer_address, + &buf[size], tx_buffer_size); size += tx_buffer_size; if (tx_buffer_el & 1) { break; @@ -828,16 +804,16 @@ static void tx_command(EEPRO100State *s) } tbd_address = tbd_array; for (; tbd_count < s->tx.tbd_count; tbd_count++) { - uint32_t tx_buffer_address = e100_ldl_le_phys(tbd_address); - uint16_t tx_buffer_size = e100_ldw_le_phys(tbd_address + 4); - uint16_t tx_buffer_el = e100_ldw_le_phys(tbd_address + 6); + uint32_t tx_buffer_address = ldl_le_pci_dma(&s->dev, tbd_address); + uint16_t tx_buffer_size = lduw_le_pci_dma(&s->dev, tbd_address + 4); + uint16_t tx_buffer_el = lduw_le_pci_dma(&s->dev, tbd_address + 6); tbd_address += 8; TRACE(RXTX, logout ("TBD (flexible mode): buffer address 0x%08x, size 0x%04x\n", tx_buffer_address, tx_buffer_size)); tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size); - cpu_physical_memory_read(tx_buffer_address, &buf[size], - tx_buffer_size); + pci_dma_read(&s->dev, tx_buffer_address, + &buf[size], tx_buffer_size); size += tx_buffer_size; if (tx_buffer_el & 1) { break; @@ -862,7 +838,7 @@ static void set_multicast_list(EEPRO100State *s) TRACE(OTHER, logout("multicast list, multicast count = %u\n", multicast_count)); for (i = 0; i < multicast_count; i += 6) { uint8_t multicast_addr[6]; - cpu_physical_memory_read(s->cb_address + 10 + i, multicast_addr, 6); + pci_dma_read(&s->dev, s->cb_address + 10 + i, multicast_addr, 6); TRACE(OTHER, logout("multicast entry %s\n", nic_dump(multicast_addr, 6))); unsigned mcast_idx = compute_mcast_idx(multicast_addr); assert(mcast_idx < 64); @@ -896,12 +872,12 @@ static void action_command(EEPRO100State *s) /* Do nothing. */ break; case CmdIASetup: - cpu_physical_memory_read(s->cb_address + 8, &s->conf.macaddr.a[0], 6); + pci_dma_read(&s->dev, s->cb_address + 8, &s->conf.macaddr.a[0], 6); TRACE(OTHER, logout("macaddr: %s\n", nic_dump(&s->conf.macaddr.a[0], 6))); break; case CmdConfigure: - cpu_physical_memory_read(s->cb_address + 8, &s->configuration[0], - sizeof(s->configuration)); + pci_dma_read(&s->dev, s->cb_address + 8, + &s->configuration[0], sizeof(s->configuration)); TRACE(OTHER, logout("configuration: %s\n", nic_dump(&s->configuration[0], 16))); TRACE(OTHER, logout("configuration: %s\n", @@ -938,7 +914,8 @@ static void action_command(EEPRO100State *s) break; } /* Write new status. */ - e100_stw_le_phys(s->cb_address, s->tx.status | ok_status | STATUS_C); + stw_le_pci_dma(&s->dev, s->cb_address, + s->tx.status | ok_status | STATUS_C); if (bit_i) { /* CU completed action. */ eepro100_cx_interrupt(s); @@ -1005,7 +982,7 @@ static void eepro100_cu_command(EEPRO100State * s, uint8_t val) /* Dump statistical counters. */ TRACE(OTHER, logout("val=0x%02x (dump stats)\n", val)); dump_statistics(s); - e100_stl_le_phys(s->statsaddr + s->stats_size, 0xa005); + stl_le_pci_dma(&s->dev, s->statsaddr + s->stats_size, 0xa005); break; case CU_CMD_BASE: /* Load CU base. */ @@ -1016,7 +993,7 @@ static void eepro100_cu_command(EEPRO100State * s, uint8_t val) /* Dump and reset statistical counters. */ TRACE(OTHER, logout("val=0x%02x (dump stats and reset)\n", val)); dump_statistics(s); - e100_stl_le_phys(s->statsaddr + s->stats_size, 0xa007); + stl_le_pci_dma(&s->dev, s->statsaddr + s->stats_size, 0xa007); memset(&s->statistics, 0, sizeof(s->statistics)); break; case CU_SRESUME: @@ -1310,10 +1287,10 @@ static void eepro100_write_port(EEPRO100State *s) case PORT_SELFTEST: TRACE(OTHER, logout("selftest address=0x%08x\n", address)); eepro100_selftest_t data; - cpu_physical_memory_read(address, &data, sizeof(data)); + pci_dma_read(&s->dev, address, (uint8_t *) &data, sizeof(data)); data.st_sign = 0xffffffff; data.st_result = 0; - cpu_physical_memory_write(address, &data, sizeof(data)); + pci_dma_write(&s->dev, address, (uint8_t *) &data, sizeof(data)); break; case PORT_SELECTIVE_RESET: TRACE(OTHER, logout("selective reset, selftest address=0x%08x\n", address)); @@ -1729,8 +1706,8 @@ static ssize_t nic_receive(VLANClientState *nc, const uint8_t * buf, size_t size } /* !!! */ eepro100_rx_t rx; - cpu_physical_memory_read(s->ru_base + s->ru_offset, &rx, - sizeof(eepro100_rx_t)); + pci_dma_read(&s->dev, s->ru_base + s->ru_offset, + (uint8_t *) &rx, sizeof(eepro100_rx_t)); uint16_t rfd_command = le16_to_cpu(rx.command); uint16_t rfd_size = le16_to_cpu(rx.size); @@ -1746,10 +1723,10 @@ static ssize_t nic_receive(VLANClientState *nc, const uint8_t * buf, size_t size #endif TRACE(OTHER, logout("command 0x%04x, link 0x%08x, addr 0x%08x, size %u\n", rfd_command, rx.link, rx.rx_buf_addr, rfd_size)); - e100_stw_le_phys(s->ru_base + s->ru_offset + - offsetof(eepro100_rx_t, status), rfd_status); - e100_stw_le_phys(s->ru_base + s->ru_offset + - offsetof(eepro100_rx_t, count), size); + stw_le_pci_dma(&s->dev, s->ru_base + s->ru_offset + + offsetof(eepro100_rx_t, status), rfd_status); + stw_le_pci_dma(&s->dev, s->ru_base + s->ru_offset + + offsetof(eepro100_rx_t, count), size); /* Early receive interrupt not supported. */ #if 0 eepro100_er_interrupt(s); @@ -1763,8 +1740,8 @@ static ssize_t nic_receive(VLANClientState *nc, const uint8_t * buf, size_t size #if 0 assert(!(s->configuration[17] & BIT(0))); #endif - cpu_physical_memory_write(s->ru_base + s->ru_offset + - sizeof(eepro100_rx_t), buf, size); + pci_dma_write(&s->dev, s->ru_base + s->ru_offset + + sizeof(eepro100_rx_t), buf, size); s->statistics.rx_good_frames++; eepro100_fr_interrupt(s); s->ru_offset = le32_to_cpu(rx.link); diff --git a/hw/es1370.c b/hw/es1370.c index 2daadde0e6..c5c16b0484 100644 --- a/hw/es1370.c +++ b/hw/es1370.c @@ -30,6 +30,7 @@ #include "audiodev.h" #include "audio/audio.h" #include "pci.h" +#include "dma.h" /* Missing stuff: SCTRL_P[12](END|ST)INC @@ -802,7 +803,7 @@ static void es1370_transfer_audio (ES1370State *s, struct chan *d, int loop_sel, if (!acquired) break; - cpu_physical_memory_write (addr, tmpbuf, acquired); + pci_dma_write (&s->dev, addr, tmpbuf, acquired); temp -= acquired; addr += acquired; @@ -816,7 +817,7 @@ static void es1370_transfer_audio (ES1370State *s, struct chan *d, int loop_sel, int copied, to_copy; to_copy = audio_MIN ((size_t) temp, sizeof (tmpbuf)); - cpu_physical_memory_read (addr, tmpbuf, to_copy); + pci_dma_read (&s->dev, addr, tmpbuf, to_copy); copied = AUD_write (voice, tmpbuf, to_copy); if (!copied) break; diff --git a/hw/esp.c b/hw/esp.c index 697c2c5b80..b698a43fe6 100644 --- a/hw/esp.c +++ b/hw/esp.c @@ -217,7 +217,8 @@ static uint32_t get_cmd(ESPState *s, uint8_t *buf) s->async_len = 0; } - if (target >= ESP_MAX_DEVS || !s->bus.devs[target]) { + s->current_dev = scsi_device_find(&s->bus, 0, target, 0); + if (!s->current_dev) { // No such drive s->rregs[ESP_RSTAT] = 0; s->rregs[ESP_RINTR] = INTR_DC; @@ -225,7 +226,6 @@ static uint32_t get_cmd(ESPState *s, uint8_t *buf) esp_raise_irq(s); return 0; } - s->current_dev = s->bus.devs[target]; return dmalen; } @@ -233,10 +233,12 @@ static void do_busid_cmd(ESPState *s, uint8_t *buf, uint8_t busid) { int32_t datalen; int lun; + SCSIDevice *current_lun; trace_esp_do_busid_cmd(busid); lun = busid & 7; - s->current_req = scsi_req_new(s->current_dev, 0, lun, buf, NULL); + current_lun = scsi_device_find(&s->bus, 0, s->current_dev->id, lun); + s->current_req = scsi_req_new(current_lun, 0, lun, buf, NULL); datalen = scsi_req_enqueue(s->current_req); s->ti_size = datalen; if (datalen != 0) { @@ -720,7 +722,11 @@ void esp_init(target_phys_addr_t espaddr, int it_shift, *dma_enable = qdev_get_gpio_in(dev, 1); } -static const struct SCSIBusOps esp_scsi_ops = { +static const struct SCSIBusInfo esp_scsi_info = { + .tcq = false, + .max_target = ESP_MAX_DEVS, + .max_lun = 7, + .transfer_data = esp_transfer_data, .complete = esp_command_complete, .cancel = esp_request_cancelled @@ -740,7 +746,7 @@ static int esp_init1(SysBusDevice *dev) qdev_init_gpio_in(&dev->qdev, esp_gpio_demux, 2); - scsi_bus_new(&s->bus, &dev->qdev, 0, ESP_MAX_DEVS, &esp_scsi_ops); + scsi_bus_new(&s->bus, &dev->qdev, &esp_scsi_info); return scsi_bus_legacy_handle_cmdline(&s->bus); } diff --git a/hw/etraxfs.h b/hw/etraxfs.h index 1554b0b8e8..24e8fd880b 100644 --- a/hw/etraxfs.h +++ b/hw/etraxfs.h @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "net.h" #include "etraxfs_dma.h" qemu_irq *cris_pic_init_cpu(CPUState *env); diff --git a/hw/event_notifier.c b/hw/event_notifier.c deleted file mode 100644 index 13f3656460..0000000000 --- a/hw/event_notifier.c +++ /dev/null @@ -1,62 +0,0 @@ -/* - * event notifier support - * - * Copyright Red Hat, Inc. 2010 - * - * Authors: - * Michael S. Tsirkin <mst@redhat.com> - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - */ - -#include "hw.h" -#include "event_notifier.h" -#ifdef CONFIG_EVENTFD -#include <sys/eventfd.h> -#endif - -int event_notifier_init(EventNotifier *e, int active) -{ -#ifdef CONFIG_EVENTFD - int fd = eventfd(!!active, EFD_NONBLOCK | EFD_CLOEXEC); - if (fd < 0) - return -errno; - e->fd = fd; - return 0; -#else - return -ENOSYS; -#endif -} - -void event_notifier_cleanup(EventNotifier *e) -{ - close(e->fd); -} - -int event_notifier_get_fd(EventNotifier *e) -{ - return e->fd; -} - -int event_notifier_test_and_clear(EventNotifier *e) -{ - uint64_t value; - int r = read(e->fd, &value, sizeof(value)); - return r == sizeof(value); -} - -int event_notifier_test(EventNotifier *e) -{ - uint64_t value; - int r = read(e->fd, &value, sizeof(value)); - if (r == sizeof(value)) { - /* restore previous value. */ - int s = write(e->fd, &value, sizeof(value)); - /* never blocks because we use EFD_SEMAPHORE. - * If we didn't we'd get EAGAIN on overflow - * and we'd have to write code to ignore it. */ - assert(s == sizeof(value)); - } - return r == sizeof(value); -} diff --git a/hw/event_notifier.h b/hw/event_notifier.h deleted file mode 100644 index 24117ea97b..0000000000 --- a/hw/event_notifier.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef QEMU_EVENT_NOTIFIER_H -#define QEMU_EVENT_NOTIFIER_H - -#include "qemu-common.h" - -struct EventNotifier { - int fd; -}; - -int event_notifier_init(EventNotifier *, int active); -void event_notifier_cleanup(EventNotifier *); -int event_notifier_get_fd(EventNotifier *); -int event_notifier_test_and_clear(EventNotifier *); -int event_notifier_test(EventNotifier *); - -#endif diff --git a/hw/fdc.c b/hw/fdc.c index 4b06e049e8..ecaad0965c 100644 --- a/hw/fdc.c +++ b/hw/fdc.c @@ -434,6 +434,7 @@ static uint32_t fdctrl_read (void *opaque, uint32_t reg) FDCtrl *fdctrl = opaque; uint32_t retval; + reg &= 7; switch (reg) { case FD_REG_SRA: retval = fdctrl_read_statusA(fdctrl); @@ -471,6 +472,7 @@ static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value) FLOPPY_DPRINTF("write reg%d: 0x%02x\n", reg & 7, value); + reg &= 7; switch (reg) { case FD_REG_DOR: fdctrl_write_dor(fdctrl, value); @@ -1945,6 +1947,18 @@ static int sun4m_fdc_init1(SysBusDevice *dev) return fdctrl_init_common(fdctrl); } +void fdc_get_bs(BlockDriverState *bs[], ISADevice *dev) +{ + FDCtrlISABus *isa = DO_UPCAST(FDCtrlISABus, busdev, dev); + FDCtrl *fdctrl = &isa->state; + int i; + + for (i = 0; i < MAX_FD; i++) { + bs[i] = fdctrl->drives[i].bs; + } +} + + static const VMStateDescription vmstate_isa_fdc ={ .name = "fdc", .version_id = 2, diff --git a/hw/fdc.h b/hw/fdc.h index 09f73c6a9f..506feb6557 100644 --- a/hw/fdc.h +++ b/hw/fdc.h @@ -7,14 +7,15 @@ /* fdc.c */ #define MAX_FD 2 -static inline void fdctrl_init_isa(DriveInfo **fds) +static inline ISADevice *fdctrl_init_isa(DriveInfo **fds) { ISADevice *dev; dev = isa_try_create("isa-fdc"); if (!dev) { - return; + return NULL; } + if (fds[0]) { qdev_prop_set_drive_nofail(&dev->qdev, "driveA", fds[0]->bdrv); } @@ -22,10 +23,14 @@ static inline void fdctrl_init_isa(DriveInfo **fds) qdev_prop_set_drive_nofail(&dev->qdev, "driveB", fds[1]->bdrv); } qdev_init_nofail(&dev->qdev); + + return dev; } void fdctrl_init_sysbus(qemu_irq irq, int dma_chann, target_phys_addr_t mmio_base, DriveInfo **fds); void sun4m_fdctrl_init(qemu_irq irq, target_phys_addr_t io_base, DriveInfo **fds, qemu_irq *fdc_tc); +void fdc_get_bs(BlockDriverState *bs[], ISADevice *dev); + #endif diff --git a/hw/fw_cfg.c b/hw/fw_cfg.c index 8df265c61d..dbcb888bbd 100644 --- a/hw/fw_cfg.c +++ b/hw/fw_cfg.c @@ -60,71 +60,55 @@ struct FWCfgState { #define JPG_FILE 0 #define BMP_FILE 1 -static FILE *probe_splashfile(char *filename, int *file_sizep, int *file_typep) +static char *read_splashfile(char *filename, int *file_sizep, int *file_typep) { - FILE *fp = NULL; - int fop_ret; - int file_size; + GError *err = NULL; + gboolean res; + gchar *content; int file_type = -1; - unsigned char buf[2] = {0, 0}; - unsigned int filehead_value = 0; + unsigned int filehead = 0; int bmp_bpp; - fp = fopen(filename, "rb"); - if (fp == NULL) { - error_report("failed to open file '%s'.", filename); - return fp; + res = g_file_get_contents(filename, &content, (gsize *)file_sizep, &err); + if (res == FALSE) { + error_report("failed to read splash file '%s'", filename); + g_error_free(err); + return NULL; } + /* check file size */ - fseek(fp, 0L, SEEK_END); - file_size = ftell(fp); - if (file_size < 2) { - error_report("file size is less than 2 bytes '%s'.", filename); - fclose(fp); - fp = NULL; - return fp; + if (*file_sizep < 30) { + goto error; } + /* check magic ID */ - fseek(fp, 0L, SEEK_SET); - fop_ret = fread(buf, 1, 2, fp); - if (fop_ret != 2) { - error_report("Could not read header from '%s': %s", - filename, strerror(errno)); - fclose(fp); - fp = NULL; - return fp; - } - filehead_value = (buf[0] + (buf[1] << 8)) & 0xffff; - if (filehead_value == 0xd8ff) { + filehead = ((content[0] & 0xff) + (content[1] << 8)) & 0xffff; + if (filehead == 0xd8ff) { file_type = JPG_FILE; + } else if (filehead == 0x4d42) { + file_type = BMP_FILE; } else { - if (filehead_value == 0x4d42) { - file_type = BMP_FILE; - } - } - if (file_type < 0) { - error_report("'%s' not jpg/bmp file,head:0x%x.", - filename, filehead_value); - fclose(fp); - fp = NULL; - return fp; + goto error; } + /* check BMP bpp */ if (file_type == BMP_FILE) { - fseek(fp, 28, SEEK_SET); - fop_ret = fread(buf, 1, 2, fp); - bmp_bpp = (buf[0] + (buf[1] << 8)) & 0xffff; + bmp_bpp = (content[28] + (content[29] << 8)) & 0xffff; if (bmp_bpp != 24) { - error_report("only 24bpp bmp file is supported."); - fclose(fp); - fp = NULL; - return fp; + goto error; } } + /* return values */ - *file_sizep = file_size; *file_typep = file_type; - return fp; + + return content; + +error: + error_report("splash file '%s' format not recognized; must be JPEG " + "or 24 bit BMP", filename); + g_free(content); + return NULL; } static void fw_cfg_bootsplash(FWCfgState *s) @@ -132,9 +116,7 @@ static void fw_cfg_bootsplash(FWCfgState *s) int boot_splash_time = -1; const char *boot_splash_filename = NULL; char *p; - char *filename; - FILE *fp; - int fop_ret; + char *filename, *file_data; int file_size; int file_type = -1; const char *temp; @@ -174,27 +156,19 @@ static void fw_cfg_bootsplash(FWCfgState *s) error_report("failed to find file '%s'.", boot_splash_filename); return; } - /* probing the file */ - fp = probe_splashfile(filename, &file_size, &file_type); - if (fp == NULL) { + + /* loading file data */ + file_data = read_splashfile(filename, &file_size, &file_type); + if (file_data == NULL) { g_free(filename); return; } - /* loading file data */ if (boot_splash_filedata != NULL) { g_free(boot_splash_filedata); } - boot_splash_filedata = g_malloc(file_size); + boot_splash_filedata = (uint8_t *)file_data; boot_splash_filedata_size = file_size; - fseek(fp, 0L, SEEK_SET); - fop_ret = fread(boot_splash_filedata, 1, file_size, fp); - if (fop_ret != file_size) { - error_report("failed to read data from '%s'.", - boot_splash_filename); - fclose(fp); - return; - } - fclose(fp); + /* insert data */ if (file_type == JPG_FILE) { fw_cfg_add_file(s, "bootsplash.jpg", diff --git a/hw/hda-audio.c b/hw/hda-audio.c index c699d6fd8b..9b089e65b4 100644 --- a/hw/hda-audio.c +++ b/hw/hda-audio.c @@ -466,7 +466,8 @@ struct HDAAudioState { QEMUSoundCard card; const desc_codec *desc; HDAAudioStream st[4]; - bool running[16]; + bool running_compat[16]; + bool running_real[2 * 16]; /* properties */ uint32_t debug; @@ -663,7 +664,7 @@ static void hda_audio_command(HDACodecDevice *hda, uint32_t nid, uint32_t data) st->channel = payload & 0x0f; dprint(a, 2, "%s: stream %d, channel %d\n", st->node->name, st->stream, st->channel); - hda_audio_set_running(st, a->running[st->stream]); + hda_audio_set_running(st, a->running_real[st->output * 16 + st->stream]); hda_codec_response(hda, true, 0); break; case AC_VERB_GET_CONV: @@ -746,16 +747,20 @@ fail: hda_codec_response(hda, true, 0); } -static void hda_audio_stream(HDACodecDevice *hda, uint32_t stnr, bool running) +static void hda_audio_stream(HDACodecDevice *hda, uint32_t stnr, bool running, bool output) { HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda); int s; - a->running[stnr] = running; + a->running_compat[stnr] = running; + a->running_real[output * 16 + stnr] = running; for (s = 0; s < ARRAY_SIZE(a->st); s++) { if (a->st[s].node == NULL) { continue; } + if (a->st[s].output != output) { + continue; + } if (a->st[s].stream != stnr) { continue; } @@ -837,6 +842,12 @@ static int hda_audio_post_load(void *opaque, int version) int i; dprint(a, 1, "%s\n", __FUNCTION__); + if (version == 1) { + /* assume running_compat[] is for output streams */ + for (i = 0; i < ARRAY_SIZE(a->running_compat); i++) + a->running_real[16 + i] = a->running_compat[i]; + } + for (i = 0; i < ARRAY_SIZE(a->st); i++) { st = a->st + i; if (st->node == NULL) @@ -844,7 +855,7 @@ static int hda_audio_post_load(void *opaque, int version) hda_codec_parse_fmt(st->format, &st->as); hda_audio_setup(st); hda_audio_set_amp(st); - hda_audio_set_running(st, a->running[st->stream]); + hda_audio_set_running(st, a->running_real[st->output * 16 + st->stream]); } return 0; } @@ -868,13 +879,14 @@ static const VMStateDescription vmstate_hda_audio_stream = { static const VMStateDescription vmstate_hda_audio = { .name = "hda-audio", - .version_id = 1, + .version_id = 2, .post_load = hda_audio_post_load, .fields = (VMStateField []) { VMSTATE_STRUCT_ARRAY(st, HDAAudioState, 4, 0, vmstate_hda_audio_stream, HDAAudioStream), - VMSTATE_BOOL_ARRAY(running, HDAAudioState, 16), + VMSTATE_BOOL_ARRAY(running_compat, HDAAudioState, 16), + VMSTATE_BOOL_ARRAY_V(running_real, HDAAudioState, 2 * 16, 2), VMSTATE_END_OF_LIST() } }; diff --git a/hw/hw.h b/hw/hw.h index a124da9c26..ed20f5a27a 100644 --- a/hw/hw.h +++ b/hw/hw.h @@ -85,8 +85,8 @@ uint64_t qemu_get_be64(QEMUFile *f); int qemu_file_rate_limit(QEMUFile *f); int64_t qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate); int64_t qemu_file_get_rate_limit(QEMUFile *f); -int qemu_file_has_error(QEMUFile *f); -void qemu_file_set_error(QEMUFile *f); +int qemu_file_get_error(QEMUFile *f); +void qemu_file_set_error(QEMUFile *f, int error); /* Try to send any outstanding data. This function is useful when output is * halted due to rate limiting or EAGAIN errors occur as it can be used to diff --git a/hw/i2c.c b/hw/i2c.c index 49b9ecb8b6..9bcf3e1d31 100644 --- a/hw/i2c.c +++ b/hw/i2c.c @@ -84,7 +84,7 @@ int i2c_start_transfer(i2c_bus *bus, uint8_t address, int recv) DeviceState *qdev; i2c_slave *slave = NULL; - QLIST_FOREACH(qdev, &bus->qbus.children, sibling) { + QTAILQ_FOREACH(qdev, &bus->qbus.children, sibling) { i2c_slave *candidate = I2C_SLAVE_FROM_QDEV(qdev); if (candidate->address == address) { slave = candidate; diff --git a/hw/i8259.c b/hw/i8259.c index e5323ffa4d..ab519de5d8 100644 --- a/hw/i8259.c +++ b/hw/i8259.c @@ -40,7 +40,8 @@ //#define DEBUG_IRQ_LATENCY //#define DEBUG_IRQ_COUNT -typedef struct PicState { +struct PicState { + ISADevice dev; uint8_t last_irr; /* edge detection */ uint8_t irr; /* interrupt request register */ uint8_t imr; /* interrupt mask register */ @@ -58,63 +59,39 @@ typedef struct PicState { uint8_t single_mode; /* true if slave pic is not initialized */ uint8_t elcr; /* PIIX edge/trigger selection*/ uint8_t elcr_mask; - PicState2 *pics_state; + qemu_irq int_out[1]; + uint32_t master; /* reflects /SP input pin */ + uint32_t iobase; + uint32_t elcr_addr; MemoryRegion base_io; MemoryRegion elcr_io; -} PicState; - -struct PicState2 { - /* 0 is master pic, 1 is slave pic */ - /* XXX: better separation between the two pics */ - PicState pics[2]; - qemu_irq parent_irq; - void *irq_request_opaque; }; -#if defined(DEBUG_PIC) || defined (DEBUG_IRQ_COUNT) +#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT) static int irq_level[16]; #endif #ifdef DEBUG_IRQ_COUNT static uint64_t irq_count[16]; #endif -PicState2 *isa_pic; - -/* set irq level. If an edge is detected, then the IRR is set to 1 */ -static inline void pic_set_irq1(PicState *s, int irq, int level) -{ - int mask; - mask = 1 << irq; - if (s->elcr & mask) { - /* level triggered */ - if (level) { - s->irr |= mask; - s->last_irr |= mask; - } else { - s->irr &= ~mask; - s->last_irr &= ~mask; - } - } else { - /* edge triggered */ - if (level) { - if ((s->last_irr & mask) == 0) - s->irr |= mask; - s->last_irr |= mask; - } else { - s->last_irr &= ~mask; - } - } -} +#ifdef DEBUG_IRQ_LATENCY +static int64_t irq_time[16]; +#endif +PicState *isa_pic; +static PicState *slave_pic; /* return the highest priority found in mask (highest = smallest number). Return 8 if no irq */ -static inline int get_priority(PicState *s, int mask) +static int get_priority(PicState *s, int mask) { int priority; - if (mask == 0) + + if (mask == 0) { return 8; + } priority = 0; - while ((mask & (1 << ((priority + s->priority_add) & 7))) == 0) + while ((mask & (1 << ((priority + s->priority_add) & 7))) == 0) { priority++; + } return priority; } @@ -125,16 +102,19 @@ static int pic_get_irq(PicState *s) mask = s->irr & ~s->imr; priority = get_priority(s, mask); - if (priority == 8) + if (priority == 8) { return -1; + } /* compute current priority. If special fully nested mode on the master, the IRQ coming from the slave is not taken into account for the priority computation. */ mask = s->isr; - if (s->special_mask) + if (s->special_mask) { mask &= ~s->imr; - if (s->special_fully_nested_mode && s == &s->pics_state->pics[0]) + } + if (s->special_fully_nested_mode && s->master) { mask &= ~(1 << 2); + } cur_priority = get_priority(s, mask); if (priority < cur_priority) { /* higher priority found: an irq should be generated */ @@ -144,116 +124,118 @@ static int pic_get_irq(PicState *s) } } -/* raise irq to CPU if necessary. must be called every time the active - irq may change */ -/* XXX: should not export it, but it is needed for an APIC kludge */ -void pic_update_irq(PicState2 *s) +/* Update INT output. Must be called every time the output may have changed. */ +static void pic_update_irq(PicState *s) { - int irq2, irq; - - /* first look at slave pic */ - irq2 = pic_get_irq(&s->pics[1]); - if (irq2 >= 0) { - /* if irq request by slave pic, signal master PIC */ - pic_set_irq1(&s->pics[0], 2, 1); - pic_set_irq1(&s->pics[0], 2, 0); - } - /* look at requested irq */ - irq = pic_get_irq(&s->pics[0]); - if (irq >= 0) { -#if defined(DEBUG_PIC) - { - int i; - for(i = 0; i < 2; i++) { - printf("pic%d: imr=%x irr=%x padd=%d\n", - i, s->pics[i].imr, s->pics[i].irr, - s->pics[i].priority_add); + int irq; - } - } - printf("pic: cpu_interrupt\n"); -#endif - qemu_irq_raise(s->parent_irq); - } - -/* all targets should do this rather than acking the IRQ in the cpu */ -#if defined(TARGET_MIPS) || defined(TARGET_PPC) || defined(TARGET_ALPHA) - else { - qemu_irq_lower(s->parent_irq); + irq = pic_get_irq(s); + if (irq >= 0) { + DPRINTF("pic%d: imr=%x irr=%x padd=%d\n", + s->master ? 0 : 1, s->imr, s->irr, s->priority_add); + qemu_irq_raise(s->int_out[0]); + } else { + qemu_irq_lower(s->int_out[0]); } -#endif } -#ifdef DEBUG_IRQ_LATENCY -int64_t irq_time[16]; -#endif - -static void i8259_set_irq(void *opaque, int irq, int level) +/* set irq level. If an edge is detected, then the IRR is set to 1 */ +static void pic_set_irq(void *opaque, int irq, int level) { - PicState2 *s = opaque; + PicState *s = opaque; + int mask = 1 << irq; +#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT) || \ + defined(DEBUG_IRQ_LATENCY) + int irq_index = s->master ? irq : irq + 8; +#endif #if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT) - if (level != irq_level[irq]) { - DPRINTF("i8259_set_irq: irq=%d level=%d\n", irq, level); - irq_level[irq] = level; + if (level != irq_level[irq_index]) { + DPRINTF("pic_set_irq: irq=%d level=%d\n", irq_index, level); + irq_level[irq_index] = level; #ifdef DEBUG_IRQ_COUNT - if (level == 1) - irq_count[irq]++; + if (level == 1) { + irq_count[irq_index]++; + } #endif } #endif #ifdef DEBUG_IRQ_LATENCY if (level) { - irq_time[irq] = qemu_get_clock_ns(vm_clock); + irq_time[irq_index] = qemu_get_clock_ns(vm_clock); } #endif - pic_set_irq1(&s->pics[irq >> 3], irq & 7, level); + + if (s->elcr & mask) { + /* level triggered */ + if (level) { + s->irr |= mask; + s->last_irr |= mask; + } else { + s->irr &= ~mask; + s->last_irr &= ~mask; + } + } else { + /* edge triggered */ + if (level) { + if ((s->last_irr & mask) == 0) { + s->irr |= mask; + } + s->last_irr |= mask; + } else { + s->last_irr &= ~mask; + } + } pic_update_irq(s); } /* acknowledge interrupt 'irq' */ -static inline void pic_intack(PicState *s, int irq) +static void pic_intack(PicState *s, int irq) { if (s->auto_eoi) { - if (s->rotate_on_auto_eoi) + if (s->rotate_on_auto_eoi) { s->priority_add = (irq + 1) & 7; + } } else { s->isr |= (1 << irq); } /* We don't clear a level sensitive interrupt here */ - if (!(s->elcr & (1 << irq))) + if (!(s->elcr & (1 << irq))) { s->irr &= ~(1 << irq); + } + pic_update_irq(s); } -int pic_read_irq(PicState2 *s) +int pic_read_irq(PicState *s) { int irq, irq2, intno; - irq = pic_get_irq(&s->pics[0]); + irq = pic_get_irq(s); if (irq >= 0) { - pic_intack(&s->pics[0], irq); if (irq == 2) { - irq2 = pic_get_irq(&s->pics[1]); + irq2 = pic_get_irq(slave_pic); if (irq2 >= 0) { - pic_intack(&s->pics[1], irq2); + pic_intack(slave_pic, irq2); } else { /* spurious IRQ on slave controller */ irq2 = 7; } - intno = s->pics[1].irq_base + irq2; -#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_LATENCY) - irq = irq2 + 8; -#endif + intno = slave_pic->irq_base + irq2; } else { - intno = s->pics[0].irq_base + irq; + intno = s->irq_base + irq; } + pic_intack(s, irq); } else { /* spurious IRQ on host controller */ irq = 7; - intno = s->pics[0].irq_base + irq; + intno = s->irq_base + irq; } - pic_update_irq(s); +#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_LATENCY) + if (irq == 2) { + irq = irq2 + 8; + } +#endif #ifdef DEBUG_IRQ_LATENCY printf("IRQ%d latency=%0.3fus\n", irq, @@ -264,10 +246,8 @@ int pic_read_irq(PicState2 *s) return intno; } -static void pic_reset(void *opaque) +static void pic_init_reset(PicState *s) { - PicState *s = opaque; - s->last_irr = 0; s->irr = 0; s->imr = 0; @@ -284,6 +264,15 @@ static void pic_reset(void *opaque) s->init4 = 0; s->single_mode = 0; /* Note: ELCR is not reset */ + pic_update_irq(s); +} + +static void pic_reset(DeviceState *dev) +{ + PicState *s = container_of(dev, PicState, dev.qdev); + + pic_init_reset(s); + s->elcr = 0; } static void pic_ioport_write(void *opaque, target_phys_addr_t addr64, @@ -297,25 +286,26 @@ static void pic_ioport_write(void *opaque, target_phys_addr_t addr64, DPRINTF("write: addr=0x%02x val=0x%02x\n", addr, val); if (addr == 0) { if (val & 0x10) { - /* init */ - pic_reset(s); - /* deassert a pending interrupt */ - qemu_irq_lower(s->pics_state->parent_irq); + pic_init_reset(s); s->init_state = 1; s->init4 = val & 1; s->single_mode = val & 2; - if (val & 0x08) + if (val & 0x08) { hw_error("level sensitive irq not supported"); + } } else if (val & 0x08) { - if (val & 0x04) + if (val & 0x04) { s->poll = 1; - if (val & 0x02) + } + if (val & 0x02) { s->read_reg_select = val & 1; - if (val & 0x40) + } + if (val & 0x40) { s->special_mask = (val >> 5) & 1; + } } else { cmd = val >> 5; - switch(cmd) { + switch (cmd) { case 0: case 4: s->rotate_on_auto_eoi = cmd >> 2; @@ -326,25 +316,26 @@ static void pic_ioport_write(void *opaque, target_phys_addr_t addr64, if (priority != 8) { irq = (priority + s->priority_add) & 7; s->isr &= ~(1 << irq); - if (cmd == 5) + if (cmd == 5) { s->priority_add = (irq + 1) & 7; - pic_update_irq(s->pics_state); + } + pic_update_irq(s); } break; case 3: irq = val & 7; s->isr &= ~(1 << irq); - pic_update_irq(s->pics_state); + pic_update_irq(s); break; case 6: s->priority_add = (val + 1) & 7; - pic_update_irq(s->pics_state); + pic_update_irq(s); break; case 7: irq = val & 7; s->isr &= ~(1 << irq); s->priority_add = (irq + 1) & 7; - pic_update_irq(s->pics_state); + pic_update_irq(s); break; default: /* no operation */ @@ -352,11 +343,11 @@ static void pic_ioport_write(void *opaque, target_phys_addr_t addr64, } } } else { - switch(s->init_state) { + switch (s->init_state) { case 0: /* normal mode */ s->imr = val; - pic_update_irq(s->pics_state); + pic_update_irq(s); break; case 1: s->irq_base = val & 0xf8; @@ -378,46 +369,28 @@ static void pic_ioport_write(void *opaque, target_phys_addr_t addr64, } } -static uint32_t pic_poll_read(PicState *s) -{ - int ret; - - ret = pic_get_irq(s); - if (ret >= 0) { - bool slave = (s == &isa_pic->pics[1]); - - if (slave) { - s->pics_state->pics[0].isr &= ~(1 << 2); - s->pics_state->pics[0].irr &= ~(1 << 2); - } - s->irr &= ~(1 << ret); - s->isr &= ~(1 << ret); - if (slave || ret != 2) - pic_update_irq(s->pics_state); - } else { - ret = 0x07; - pic_update_irq(s->pics_state); - } - - return ret; -} - -static uint64_t pic_ioport_read(void *opaque, target_phys_addr_t addr1, +static uint64_t pic_ioport_read(void *opaque, target_phys_addr_t addr, unsigned size) { PicState *s = opaque; - unsigned int addr = addr1; int ret; if (s->poll) { - ret = pic_poll_read(s); + ret = pic_get_irq(s); + if (ret >= 0) { + pic_intack(s, ret); + ret |= 0x80; + } else { + ret = 0; + } s->poll = 0; } else { if (addr == 0) { - if (s->read_reg_select) + if (s->read_reg_select) { ret = s->isr; - else + } else { ret = s->irr; + } } else { ret = s->imr; } @@ -426,19 +399,9 @@ static uint64_t pic_ioport_read(void *opaque, target_phys_addr_t addr1, return ret; } -/* memory mapped interrupt status */ -/* XXX: may be the same than pic_read_irq() */ -uint32_t pic_intack_read(PicState2 *s) +int pic_get_output(PicState *s) { - int ret; - - ret = pic_poll_read(&s->pics[0]); - if (ret == 2) - ret = pic_poll_read(&s->pics[1]) + 8; - /* Prepare for ISR read */ - s->pics[0].read_reg_select = 1; - - return ret; + return (pic_get_irq(s) >= 0); } static void elcr_ioport_write(void *opaque, target_phys_addr_t addr, @@ -460,7 +423,7 @@ static const VMStateDescription vmstate_pic = { .version_id = 1, .minimum_version_id = 1, .minimum_version_id_old = 1, - .fields = (VMStateField []) { + .fields = (VMStateField[]) { VMSTATE_UINT8(last_irr, PicState), VMSTATE_UINT8(irr, PicState), VMSTATE_UINT8(imr, PicState), @@ -499,19 +462,24 @@ static const MemoryRegionOps pic_elcr_ioport_ops = { }, }; -/* XXX: add generic master/slave system */ -static void pic_init1(int io_addr, int elcr_addr, PicState *s) +static int pic_initfn(ISADevice *dev) { + PicState *s = DO_UPCAST(PicState, dev, dev); + memory_region_init_io(&s->base_io, &pic_base_ioport_ops, s, "pic", 2); memory_region_init_io(&s->elcr_io, &pic_elcr_ioport_ops, s, "elcr", 1); - isa_register_ioport(NULL, &s->base_io, io_addr); - if (elcr_addr >= 0) { - isa_register_ioport(NULL, &s->elcr_io, elcr_addr); + isa_register_ioport(NULL, &s->base_io, s->iobase); + if (s->elcr_addr != -1) { + isa_register_ioport(NULL, &s->elcr_io, s->elcr_addr); } - vmstate_register(NULL, io_addr, &vmstate_pic, s); - qemu_register_reset(pic_reset, s); + qdev_init_gpio_out(&dev->qdev, s->int_out, ARRAY_SIZE(s->int_out)); + qdev_init_gpio_in(&dev->qdev, pic_set_irq, 8); + + qdev_set_legacy_instance_id(&dev->qdev, s->iobase, 1); + + return 0; } void pic_info(Monitor *mon) @@ -519,11 +487,11 @@ void pic_info(Monitor *mon) int i; PicState *s; - if (!isa_pic) + if (!isa_pic) { return; - - for(i=0;i<2;i++) { - s = &isa_pic->pics[i]; + } + for (i = 0; i < 2; i++) { + s = i == 0 ? isa_pic : slave_pic; monitor_printf(mon, "pic%d: irr=%02x imr=%02x isr=%02x hprio=%d " "irq_base=%02x rr_sel=%d elcr=%02x fnm=%d\n", i, s->irr, s->imr, s->isr, s->priority_add, @@ -543,24 +511,69 @@ void irq_info(Monitor *mon) monitor_printf(mon, "IRQ statistics:\n"); for (i = 0; i < 16; i++) { count = irq_count[i]; - if (count > 0) + if (count > 0) { monitor_printf(mon, "%2d: %" PRId64 "\n", i, count); + } } #endif } qemu_irq *i8259_init(qemu_irq parent_irq) { - PicState2 *s; - - s = g_malloc0(sizeof(PicState2)); - pic_init1(0x20, 0x4d0, &s->pics[0]); - pic_init1(0xa0, 0x4d1, &s->pics[1]); - s->pics[0].elcr_mask = 0xf8; - s->pics[1].elcr_mask = 0xde; - s->parent_irq = parent_irq; - s->pics[0].pics_state = s; - s->pics[1].pics_state = s; - isa_pic = s; - return qemu_allocate_irqs(i8259_set_irq, s, 16); + qemu_irq *irq_set; + ISADevice *dev; + int i; + + irq_set = g_malloc(ISA_NUM_IRQS * sizeof(qemu_irq)); + + dev = isa_create("isa-i8259"); + qdev_prop_set_uint32(&dev->qdev, "iobase", 0x20); + qdev_prop_set_uint32(&dev->qdev, "elcr_addr", 0x4d0); + qdev_prop_set_uint8(&dev->qdev, "elcr_mask", 0xf8); + qdev_prop_set_bit(&dev->qdev, "master", true); + qdev_init_nofail(&dev->qdev); + + qdev_connect_gpio_out(&dev->qdev, 0, parent_irq); + for (i = 0 ; i < 8; i++) { + irq_set[i] = qdev_get_gpio_in(&dev->qdev, i); + } + + isa_pic = DO_UPCAST(PicState, dev, dev); + + dev = isa_create("isa-i8259"); + qdev_prop_set_uint32(&dev->qdev, "iobase", 0xa0); + qdev_prop_set_uint32(&dev->qdev, "elcr_addr", 0x4d1); + qdev_prop_set_uint8(&dev->qdev, "elcr_mask", 0xde); + qdev_init_nofail(&dev->qdev); + + qdev_connect_gpio_out(&dev->qdev, 0, irq_set[2]); + for (i = 0 ; i < 8; i++) { + irq_set[i + 8] = qdev_get_gpio_in(&dev->qdev, i); + } + + slave_pic = DO_UPCAST(PicState, dev, dev); + + return irq_set; +} + +static ISADeviceInfo i8259_info = { + .qdev.name = "isa-i8259", + .qdev.size = sizeof(PicState), + .qdev.vmsd = &vmstate_pic, + .qdev.reset = pic_reset, + .qdev.no_user = 1, + .init = pic_initfn, + .qdev.props = (Property[]) { + DEFINE_PROP_HEX32("iobase", PicState, iobase, -1), + DEFINE_PROP_HEX32("elcr_addr", PicState, elcr_addr, -1), + DEFINE_PROP_HEX8("elcr_mask", PicState, elcr_mask, -1), + DEFINE_PROP_BIT("master", PicState, master, 0, false), + DEFINE_PROP_END_OF_LIST(), + }, +}; + +static void pic_register(void) +{ + isa_qdev_register(&i8259_info); } +device_init(pic_register) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 1c7e3a00b5..0af201de2f 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -327,7 +327,7 @@ static void ahci_mem_write(void *opaque, target_phys_addr_t addr, } if (addr < AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR) { - DPRINTF(-1, "(addr 0x%08X), val 0x%08X\n", (unsigned) addr, val); + DPRINTF(-1, "(addr 0x%08X), val 0x%08"PRIX64"\n", (unsigned) addr, val); switch (addr) { case HOST_CAP: /* R/WO, RO */ @@ -777,7 +777,8 @@ static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis, ncq_tfs->sector_count = ((uint16_t)ncq_fis->sector_count_high << 8) | ncq_fis->sector_count_low; - DPRINTF(port, "NCQ transfer LBA from %ld to %ld, drive max %ld\n", + DPRINTF(port, "NCQ transfer LBA from %"PRId64" to %"PRId64", " + "drive max %"PRId64"\n", ncq_tfs->lba, ncq_tfs->lba + ncq_tfs->sector_count - 2, s->dev[port].port.ifs[0].nb_sectors - 1); @@ -786,10 +787,12 @@ static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis, switch(ncq_fis->command) { case READ_FPDMA_QUEUED: - DPRINTF(port, "NCQ reading %d sectors from LBA %ld, tag %d\n", + DPRINTF(port, "NCQ reading %d sectors from LBA %"PRId64", " + "tag %d\n", ncq_tfs->sector_count-1, ncq_tfs->lba, ncq_tfs->tag); - DPRINTF(port, "tag %d aio read %ld\n", ncq_tfs->tag, ncq_tfs->lba); + DPRINTF(port, "tag %d aio read %"PRId64"\n", + ncq_tfs->tag, ncq_tfs->lba); bdrv_acct_start(ncq_tfs->drive->port.ifs[0].bs, &ncq_tfs->acct, (ncq_tfs->sector_count-1) * BDRV_SECTOR_SIZE, @@ -799,10 +802,11 @@ static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis, ncq_cb, ncq_tfs); break; case WRITE_FPDMA_QUEUED: - DPRINTF(port, "NCQ writing %d sectors to LBA %ld, tag %d\n", + DPRINTF(port, "NCQ writing %d sectors to LBA %"PRId64", tag %d\n", ncq_tfs->sector_count-1, ncq_tfs->lba, ncq_tfs->tag); - DPRINTF(port, "tag %d aio write %ld\n", ncq_tfs->tag, ncq_tfs->lba); + DPRINTF(port, "tag %d aio write %"PRId64"\n", + ncq_tfs->tag, ncq_tfs->lba); bdrv_acct_start(ncq_tfs->drive->port.ifs[0].bs, &ncq_tfs->acct, (ncq_tfs->sector_count-1) * BDRV_SECTOR_SIZE, diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c index 3f909c3a99..90b6729692 100644 --- a/hw/ide/atapi.c +++ b/hw/ide/atapi.c @@ -154,10 +154,10 @@ void ide_atapi_io_error(IDEState *s, int ret) { /* XXX: handle more errors */ if (ret == -ENOMEDIUM) { - ide_atapi_cmd_error(s, SENSE_NOT_READY, + ide_atapi_cmd_error(s, NOT_READY, ASC_MEDIUM_NOT_PRESENT); } else { - ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, + ide_atapi_cmd_error(s, ILLEGAL_REQUEST, ASC_LOGICAL_BLOCK_OOR); } } @@ -282,7 +282,7 @@ static void ide_atapi_cmd_check_status(IDEState *s) #ifdef DEBUG_IDE_ATAPI printf("atapi_cmd_check_status\n"); #endif - s->error = MC_ERR | (SENSE_UNIT_ATTENTION << 4); + s->error = MC_ERR | (UNIT_ATTENTION << 4); s->status = ERR_STAT; s->nsector = 0; ide_set_irq(s->bus); @@ -354,7 +354,7 @@ static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret) ide_atapi_cmd_read_dma_cb, s); if (!s->bus->dma->aiocb) { /* Note: media not present is the most likely case */ - ide_atapi_cmd_error(s, SENSE_NOT_READY, + ide_atapi_cmd_error(s, NOT_READY, ASC_MEDIUM_NOT_PRESENT); goto eot; } @@ -505,19 +505,6 @@ static int ide_dvd_read_structure(IDEState *s, int format, static unsigned int event_status_media(IDEState *s, uint8_t *buf) { - enum media_event_code { - MEC_NO_CHANGE = 0, /* Status unchanged */ - MEC_EJECT_REQUESTED, /* received a request from user to eject */ - MEC_NEW_MEDIA, /* new media inserted and ready for access */ - MEC_MEDIA_REMOVAL, /* only for media changers */ - MEC_MEDIA_CHANGED, /* only for media changers */ - MEC_BG_FORMAT_COMPLETED, /* MRW or DVD+RW b/g format completed */ - MEC_BG_FORMAT_RESTARTED, /* MRW or DVD+RW b/g format restarted */ - }; - enum media_status { - MS_TRAY_OPEN = 1, - MS_MEDIA_PRESENT = 2, - }; uint8_t event_code, media_status; media_status = 0; @@ -564,27 +551,6 @@ static void cmd_get_event_status_notification(IDEState *s, uint8_t notification_class; uint8_t supported_events; } QEMU_PACKED *gesn_event_header; - - enum notification_class_request_type { - NCR_RESERVED1 = 1 << 0, - NCR_OPERATIONAL_CHANGE = 1 << 1, - NCR_POWER_MANAGEMENT = 1 << 2, - NCR_EXTERNAL_REQUEST = 1 << 3, - NCR_MEDIA = 1 << 4, - NCR_MULTI_HOST = 1 << 5, - NCR_DEVICE_BUSY = 1 << 6, - NCR_RESERVED2 = 1 << 7, - }; - enum event_notification_class_field { - ENC_NO_EVENTS = 0, - ENC_OPERATIONAL_CHANGE, - ENC_POWER_MANAGEMENT, - ENC_EXTERNAL_REQUEST, - ENC_MEDIA, - ENC_MULTIPLE_HOSTS, - ENC_DEVICE_BUSY, - ENC_RESERVED, - }; unsigned int max_len, used_len; gesn_cdb = (void *)packet; @@ -595,7 +561,7 @@ static void cmd_get_event_status_notification(IDEState *s, /* It is fine by the MMC spec to not support async mode operations */ if (!(gesn_cdb->polled & 0x01)) { /* asynchronous mode */ /* Only polling is supported, asynchronous mode is not. */ - ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, + ide_atapi_cmd_error(s, ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET); return; } @@ -606,8 +572,11 @@ static void cmd_get_event_status_notification(IDEState *s, * These are the supported events. * * We currently only support requests of the 'media' type. + * Notification class requests and supported event classes are bitmasks, + * but they are build from the same values as the "notification class" + * field. */ - gesn_event_header->supported_events = NCR_MEDIA; + gesn_event_header->supported_events = 1 << GESN_MEDIA; /* * We use |= below to set the class field; other bits in this byte @@ -621,8 +590,8 @@ static void cmd_get_event_status_notification(IDEState *s, * notification_class_request_type enum above specifies the * priority: upper elements are higher prio than lower ones. */ - if (gesn_cdb->class & NCR_MEDIA) { - gesn_event_header->notification_class |= ENC_MEDIA; + if (gesn_cdb->class & (1 << GESN_MEDIA)) { + gesn_event_header->notification_class |= GESN_MEDIA; used_len = event_status_media(s, buf); } else { gesn_event_header->notification_class = 0x80; /* No event available */ @@ -643,8 +612,8 @@ static void cmd_request_sense(IDEState *s, uint8_t *buf) buf[7] = 10; buf[12] = s->asc; - if (s->sense_key == SENSE_UNIT_ATTENTION) { - s->sense_key = SENSE_NONE; + if (s->sense_key == UNIT_ATTENTION) { + s->sense_key = NO_SENSE; } ide_atapi_cmd_reply(s, 18, max_len); @@ -676,7 +645,7 @@ static void cmd_get_configuration(IDEState *s, uint8_t *buf) /* only feature 0 is supported */ if (buf[2] != 0 || buf[3] != 0) { - ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, + ide_atapi_cmd_error(s, ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET); return; } @@ -733,7 +702,7 @@ static void cmd_mode_sense(IDEState *s, uint8_t *buf) switch(action) { case 0: /* current values */ switch(code) { - case GPMODE_R_W_ERROR_PAGE: /* error recovery */ + case MODE_PAGE_R_W_ERROR: /* error recovery */ cpu_to_ube16(&buf[0], 16 + 6); buf[2] = 0x70; buf[3] = 0; @@ -742,8 +711,8 @@ static void cmd_mode_sense(IDEState *s, uint8_t *buf) buf[6] = 0; buf[7] = 0; - buf[8] = 0x01; - buf[9] = 0x06; + buf[8] = MODE_PAGE_R_W_ERROR; + buf[9] = 16 - 10; buf[10] = 0x00; buf[11] = 0x05; buf[12] = 0x00; @@ -752,7 +721,7 @@ static void cmd_mode_sense(IDEState *s, uint8_t *buf) buf[15] = 0x00; ide_atapi_cmd_reply(s, 16, max_len); break; - case GPMODE_AUDIO_CTL_PAGE: + case MODE_PAGE_AUDIO_CTL: cpu_to_ube16(&buf[0], 24 + 6); buf[2] = 0x70; buf[3] = 0; @@ -761,6 +730,8 @@ static void cmd_mode_sense(IDEState *s, uint8_t *buf) buf[6] = 0; buf[7] = 0; + buf[8] = MODE_PAGE_AUDIO_CTL; + buf[9] = 24 - 10; /* Fill with CDROM audio volume */ buf[17] = 0; buf[19] = 0; @@ -769,7 +740,7 @@ static void cmd_mode_sense(IDEState *s, uint8_t *buf) ide_atapi_cmd_reply(s, 24, max_len); break; - case GPMODE_CAPABILITIES_PAGE: + case MODE_PAGE_CAPABILITIES: cpu_to_ube16(&buf[0], 28 + 6); buf[2] = 0x70; buf[3] = 0; @@ -778,9 +749,9 @@ static void cmd_mode_sense(IDEState *s, uint8_t *buf) buf[6] = 0; buf[7] = 0; - buf[8] = 0x2a; - buf[9] = 0x12; - buf[10] = 0x00; + buf[8] = MODE_PAGE_CAPABILITIES; + buf[9] = 28 - 10; + buf[10] = 0x3b; /* read CDR/CDRW/DVDROM/DVDR/DVDRAM */ buf[11] = 0x00; /* Claim PLAY_AUDIO capability (0x01) since some Linux @@ -789,14 +760,14 @@ static void cmd_mode_sense(IDEState *s, uint8_t *buf) buf[13] = 3 << 5; buf[14] = (1 << 0) | (1 << 3) | (1 << 5); if (s->tray_locked) { - buf[6] |= 1 << 1; + buf[14] |= 1 << 1; } - buf[15] = 0x00; - cpu_to_ube16(&buf[16], 706); - buf[18] = 0; + buf[15] = 0x00; /* No volume & mute control, no changer */ + cpu_to_ube16(&buf[16], 704); /* 4x read speed */ + buf[18] = 0; /* Two volume levels */ buf[19] = 2; - cpu_to_ube16(&buf[20], 512); - cpu_to_ube16(&buf[22], 706); + cpu_to_ube16(&buf[20], 512); /* 512k buffer */ + cpu_to_ube16(&buf[22], 704); /* 4x read speed current */ buf[24] = 0; buf[25] = 0; buf[26] = 0; @@ -813,14 +784,14 @@ static void cmd_mode_sense(IDEState *s, uint8_t *buf) goto error_cmd; default: case 3: /* saved values */ - ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, + ide_atapi_cmd_error(s, ILLEGAL_REQUEST, ASC_SAVING_PARAMETERS_NOT_SUPPORTED); break; } return; error_cmd: - ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET); + ide_atapi_cmd_error(s, ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET); } static void cmd_test_unit_ready(IDEState *s, uint8_t *buf) @@ -883,7 +854,7 @@ static void cmd_read_cd(IDEState *s, uint8_t* buf) ide_atapi_cmd_read(s, lba, nb_sectors, 2352); break; default: - ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, + ide_atapi_cmd_error(s, ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET); break; } @@ -896,7 +867,7 @@ static void cmd_seek(IDEState *s, uint8_t* buf) lba = ube32_to_cpu(buf + 2); if (lba >= total_sectors) { - ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, ASC_LOGICAL_BLOCK_OOR); + ide_atapi_cmd_error(s, ILLEGAL_REQUEST, ASC_LOGICAL_BLOCK_OOR); return; } @@ -912,7 +883,7 @@ static void cmd_start_stop_unit(IDEState *s, uint8_t* buf) if (loej) { if (!start && !s->tray_open && s->tray_locked) { sense = bdrv_is_inserted(s->bs) - ? SENSE_NOT_READY : SENSE_ILLEGAL_REQUEST; + ? NOT_READY : ILLEGAL_REQUEST; ide_atapi_cmd_error(s, sense, ASC_MEDIA_REMOVAL_PREVENTED); return; } @@ -971,7 +942,7 @@ static void cmd_read_toc_pma_atip(IDEState *s, uint8_t* buf) break; default: error_cmd: - ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, + ide_atapi_cmd_error(s, ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET); } } @@ -997,11 +968,11 @@ static void cmd_read_dvd_structure(IDEState *s, uint8_t* buf) if (format < 0xff) { if (media_is_cd(s)) { - ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, + ide_atapi_cmd_error(s, ILLEGAL_REQUEST, ASC_INCOMPATIBLE_FORMAT); return; } else if (!media_present(s)) { - ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, + ide_atapi_cmd_error(s, ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET); return; } @@ -1017,7 +988,7 @@ static void cmd_read_dvd_structure(IDEState *s, uint8_t* buf) ret = ide_dvd_read_structure(s, format, buf, buf); if (ret < 0) { - ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, -ret); + ide_atapi_cmd_error(s, ILLEGAL_REQUEST, -ret); } else { ide_atapi_cmd_reply(s, ret, max_len); } @@ -1034,7 +1005,7 @@ static void cmd_read_dvd_structure(IDEState *s, uint8_t* buf) case 0x90: /* TODO: List of recognized format layers */ case 0xc0: /* TODO: Write protection status */ default: - ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, + ide_atapi_cmd_error(s, ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET); break; } @@ -1106,7 +1077,7 @@ void ide_atapi_cmd(IDEState *s) * condition response unless a higher priority status, defined by the drive * here, is pending. */ - if (s->sense_key == SENSE_UNIT_ATTENTION && + if (s->sense_key == UNIT_ATTENTION && !(atapi_cmd_table[s->io_buffer[0]].flags & ALLOW_UA)) { ide_atapi_cmd_check_status(s); return; @@ -1119,10 +1090,10 @@ void ide_atapi_cmd(IDEState *s) * states rely on this behavior. */ if (!s->tray_open && bdrv_is_inserted(s->bs) && s->cdrom_changed) { - ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT); + ide_atapi_cmd_error(s, NOT_READY, ASC_MEDIUM_NOT_PRESENT); s->cdrom_changed = 0; - s->sense_key = SENSE_UNIT_ATTENTION; + s->sense_key = UNIT_ATTENTION; s->asc = ASC_MEDIUM_MAY_HAVE_CHANGED; return; } @@ -1131,7 +1102,7 @@ void ide_atapi_cmd(IDEState *s) if ((atapi_cmd_table[s->io_buffer[0]].flags & CHECK_READY) && (!media_present(s) || !bdrv_is_inserted(s->bs))) { - ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT); + ide_atapi_cmd_error(s, NOT_READY, ASC_MEDIUM_NOT_PRESENT); return; } @@ -1141,5 +1112,5 @@ void ide_atapi_cmd(IDEState *s) return; } - ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, ASC_ILLEGAL_OPCODE); + ide_atapi_cmd_error(s, ILLEGAL_REQUEST, ASC_ILLEGAL_OPCODE); } diff --git a/hw/ide/core.c b/hw/ide/core.c index 280a117fe2..9a2fd30607 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -799,7 +799,7 @@ static void ide_cd_change_cb(void *opaque, bool load) * First indicate to the guest that a CD has been removed. That's * done on the next command the guest sends us. * - * Then we set SENSE_UNIT_ATTENTION, by which the guest will + * Then we set UNIT_ATTENTION, by which the guest will * detect a new CD in the drive. See ide_atapi_cmd() for details. */ s->cdrom_changed = 1; @@ -2027,7 +2027,7 @@ static int ide_drive_post_load(void *opaque, int version_id) IDEState *s = opaque; if (version_id < 3) { - if (s->sense_key == SENSE_UNIT_ATTENTION && + if (s->sense_key == UNIT_ATTENTION && s->asc == ASC_MEDIUM_MAY_HAVE_CHANGED) { s->cdrom_changed = 1; } @@ -2039,7 +2039,7 @@ static int ide_drive_pio_post_load(void *opaque, int version_id) { IDEState *s = opaque; - if (s->end_transfer_fn_idx > ARRAY_SIZE(transfer_end_table)) { + if (s->end_transfer_fn_idx >= ARRAY_SIZE(transfer_end_table)) { return -EINVAL; } s->end_transfer_func = transfer_end_table[s->end_transfer_fn_idx]; diff --git a/hw/ide/internal.h b/hw/ide/internal.h index c39dc058f4..00b28dfdbc 100644 --- a/hw/ide/internal.h +++ b/hw/ide/internal.h @@ -11,6 +11,7 @@ #include "iorange.h" #include "dma.h" #include "sysemu.h" +#include "hw/scsi-defs.h" /* debug IDE devices */ //#define DEBUG_IDE @@ -280,71 +281,6 @@ typedef struct IDEDMAOps IDEDMAOps; #define GPCMD_GET_MEDIA_STATUS 0xda #define GPCMD_MODE_SENSE_6 0x1a -/* Mode page codes for mode sense/set */ -#define GPMODE_R_W_ERROR_PAGE 0x01 -#define GPMODE_WRITE_PARMS_PAGE 0x05 -#define GPMODE_AUDIO_CTL_PAGE 0x0e -#define GPMODE_POWER_PAGE 0x1a -#define GPMODE_FAULT_FAIL_PAGE 0x1c -#define GPMODE_TO_PROTECT_PAGE 0x1d -#define GPMODE_CAPABILITIES_PAGE 0x2a -#define GPMODE_ALL_PAGES 0x3f -/* Not in Mt. Fuji, but in ATAPI 2.6 -- depricated now in favor - * of MODE_SENSE_POWER_PAGE */ -#define GPMODE_CDROM_PAGE 0x0d - -/* - * Based on values from <linux/cdrom.h> but extending CD_MINS - * to the maximum common size allowed by the Orange's Book ATIP - * - * 90 and 99 min CDs are also available but using them as the - * upper limit reduces the effectiveness of the heuristic to - * detect DVDs burned to less than 25% of their maximum capacity - */ - -/* Some generally useful CD-ROM information */ -#define CD_MINS 80 /* max. minutes per CD */ -#define CD_SECS 60 /* seconds per minute */ -#define CD_FRAMES 75 /* frames per second */ -#define CD_FRAMESIZE 2048 /* bytes per frame, "cooked" mode */ -#define CD_MAX_BYTES (CD_MINS * CD_SECS * CD_FRAMES * CD_FRAMESIZE) -#define CD_MAX_SECTORS (CD_MAX_BYTES / 512) - -/* - * The MMC values are not IDE specific and might need to be moved - * to a common header if they are also needed for the SCSI emulation - */ - -/* Profile list from MMC-6 revision 1 table 91 */ -#define MMC_PROFILE_NONE 0x0000 -#define MMC_PROFILE_CD_ROM 0x0008 -#define MMC_PROFILE_CD_R 0x0009 -#define MMC_PROFILE_CD_RW 0x000A -#define MMC_PROFILE_DVD_ROM 0x0010 -#define MMC_PROFILE_DVD_R_SR 0x0011 -#define MMC_PROFILE_DVD_RAM 0x0012 -#define MMC_PROFILE_DVD_RW_RO 0x0013 -#define MMC_PROFILE_DVD_RW_SR 0x0014 -#define MMC_PROFILE_DVD_R_DL_SR 0x0015 -#define MMC_PROFILE_DVD_R_DL_JR 0x0016 -#define MMC_PROFILE_DVD_RW_DL 0x0017 -#define MMC_PROFILE_DVD_DDR 0x0018 -#define MMC_PROFILE_DVD_PLUS_RW 0x001A -#define MMC_PROFILE_DVD_PLUS_R 0x001B -#define MMC_PROFILE_DVD_PLUS_RW_DL 0x002A -#define MMC_PROFILE_DVD_PLUS_R_DL 0x002B -#define MMC_PROFILE_BD_ROM 0x0040 -#define MMC_PROFILE_BD_R_SRM 0x0041 -#define MMC_PROFILE_BD_R_RRM 0x0042 -#define MMC_PROFILE_BD_RE 0x0043 -#define MMC_PROFILE_HDDVD_ROM 0x0050 -#define MMC_PROFILE_HDDVD_R 0x0051 -#define MMC_PROFILE_HDDVD_RAM 0x0052 -#define MMC_PROFILE_HDDVD_RW 0x0053 -#define MMC_PROFILE_HDDVD_R_DL 0x0058 -#define MMC_PROFILE_HDDVD_RW_DL 0x005A -#define MMC_PROFILE_INVALID 0xFFFF - #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 @@ -366,11 +302,6 @@ typedef struct IDEDMAOps IDEDMAOps; #define CFA_INVALID_ADDRESS 0x21 #define CFA_ADDRESS_OVERFLOW 0x2f -#define SENSE_NONE 0 -#define SENSE_NOT_READY 2 -#define SENSE_ILLEGAL_REQUEST 5 -#define SENSE_UNIT_ATTENTION 6 - #define SMART_READ_DATA 0xd0 #define SMART_READ_THRESH 0xd1 #define SMART_ATTR_AUTOSAVE 0xd2 diff --git a/hw/ide/macio.c b/hw/ide/macio.c index 37b8239b4d..70b33422d2 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -87,7 +87,7 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) if (!m->aiocb) { qemu_sglist_destroy(&s->sg); /* Note: media not present is the most likely case */ - ide_atapi_cmd_error(s, SENSE_NOT_READY, + ide_atapi_cmd_error(s, NOT_READY, ASC_MEDIUM_NOT_PRESENT); goto done; } diff --git a/hw/ide/pci.c b/hw/ide/pci.c index f133c422b6..49b823df79 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -62,7 +62,8 @@ static int bmdma_prepare_buf(IDEDMA *dma, int is_write) } prd; int l, len; - qemu_sglist_init(&s->sg, s->nsector / (BMDMA_PAGE_SIZE / 512) + 1); + pci_dma_sglist_init(&s->sg, &bm->pci_dev->dev, + s->nsector / (BMDMA_PAGE_SIZE / 512) + 1); s->io_buffer_size = 0; for(;;) { if (bm->cur_prd_len == 0) { @@ -70,7 +71,7 @@ static int bmdma_prepare_buf(IDEDMA *dma, int is_write) if (bm->cur_prd_last || (bm->cur_addr - bm->addr) >= BMDMA_PAGE_SIZE) return s->io_buffer_size != 0; - cpu_physical_memory_read(bm->cur_addr, (uint8_t *)&prd, 8); + pci_dma_read(&bm->pci_dev->dev, bm->cur_addr, (uint8_t *)&prd, 8); bm->cur_addr += 8; prd.addr = le32_to_cpu(prd.addr); prd.size = le32_to_cpu(prd.size); @@ -112,7 +113,7 @@ static int bmdma_rw_buf(IDEDMA *dma, int is_write) if (bm->cur_prd_last || (bm->cur_addr - bm->addr) >= BMDMA_PAGE_SIZE) return 0; - cpu_physical_memory_read(bm->cur_addr, (uint8_t *)&prd, 8); + pci_dma_read(&bm->pci_dev->dev, bm->cur_addr, (uint8_t *)&prd, 8); bm->cur_addr += 8; prd.addr = le32_to_cpu(prd.addr); prd.size = le32_to_cpu(prd.size); @@ -127,11 +128,11 @@ static int bmdma_rw_buf(IDEDMA *dma, int is_write) l = bm->cur_prd_len; if (l > 0) { if (is_write) { - cpu_physical_memory_write(bm->cur_prd_addr, - s->io_buffer + s->io_buffer_index, l); + pci_dma_write(&bm->pci_dev->dev, bm->cur_prd_addr, + s->io_buffer + s->io_buffer_index, l); } else { - cpu_physical_memory_read(bm->cur_prd_addr, - s->io_buffer + s->io_buffer_index, l); + pci_dma_read(&bm->pci_dev->dev, bm->cur_prd_addr, + s->io_buffer + s->io_buffer_index, l); } bm->cur_prd_addr += l; bm->cur_prd_len -= l; @@ -326,7 +327,7 @@ void bmdma_cmd_writeb(BMDMAState *bm, uint32_t val) bm->cmd = val & 0x09; } -static uint64_t bmdma_addr_read(void *opaque, target_phys_addr_t addr, +static uint64_t bmdma_addr_read(void *opaque, dma_addr_t addr, unsigned width) { BMDMAState *bm = opaque; @@ -340,7 +341,7 @@ static uint64_t bmdma_addr_read(void *opaque, target_phys_addr_t addr, return data; } -static void bmdma_addr_write(void *opaque, target_phys_addr_t addr, +static void bmdma_addr_write(void *opaque, dma_addr_t addr, uint64_t data, unsigned width) { BMDMAState *bm = opaque; diff --git a/hw/intel-hda.c b/hw/intel-hda.c index 4272204e03..10769e0f49 100644 --- a/hw/intel-hda.c +++ b/hw/intel-hda.c @@ -24,6 +24,7 @@ #include "audiodev.h" #include "intel-hda.h" #include "intel-hda-defs.h" +#include "dma.h" /* --------------------------------------------------------------------- */ /* hda bus */ @@ -86,7 +87,7 @@ HDACodecDevice *hda_codec_find(HDACodecBus *bus, uint32_t cad) DeviceState *qdev; HDACodecDevice *cdev; - QLIST_FOREACH(qdev, &bus->qbus.children, sibling) { + QTAILQ_FOREACH(qdev, &bus->qbus.children, sibling) { cdev = DO_UPCAST(HDACodecDevice, qdev, qdev); if (cdev->cad == cad) { return cdev; @@ -328,7 +329,7 @@ static void intel_hda_corb_run(IntelHDAState *d) rp = (d->corb_rp + 1) & 0xff; addr = intel_hda_addr(d->corb_lbase, d->corb_ubase); - verb = ldl_le_phys(addr + 4*rp); + verb = ldl_le_pci_dma(&d->pci, addr + 4*rp); d->corb_rp = rp; dprint(d, 2, "%s: [rp 0x%x] verb 0x%08x\n", __FUNCTION__, rp, verb); @@ -360,8 +361,8 @@ static void intel_hda_response(HDACodecDevice *dev, bool solicited, uint32_t res ex = (solicited ? 0 : (1 << 4)) | dev->cad; wp = (d->rirb_wp + 1) & 0xff; addr = intel_hda_addr(d->rirb_lbase, d->rirb_ubase); - stl_le_phys(addr + 8*wp, response); - stl_le_phys(addr + 8*wp + 4, ex); + stl_le_pci_dma(&d->pci, addr + 8*wp, response); + stl_le_pci_dma(&d->pci, addr + 8*wp + 4, ex); d->rirb_wp = wp; dprint(d, 2, "%s: [wp 0x%x] response 0x%x, extra 0x%x\n", @@ -389,18 +390,19 @@ static bool intel_hda_xfer(HDACodecDevice *dev, uint32_t stnr, bool output, { HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, dev->qdev.parent_bus); IntelHDAState *d = container_of(bus, IntelHDAState, codecs); - IntelHDAStream *st = NULL; target_phys_addr_t addr; uint32_t s, copy, left; + IntelHDAStream *st; bool irq = false; - for (s = 0; s < ARRAY_SIZE(d->st); s++) { - if (stnr == ((d->st[s].ctl >> 20) & 0x0f)) { - st = d->st + s; + st = output ? d->st + 4 : d->st; + for (s = 0; s < 4; s++) { + if (stnr == ((st[s].ctl >> 20) & 0x0f)) { + st = st + s; break; } } - if (st == NULL) { + if (s == 4) { return false; } if (st->bpl == NULL) { @@ -425,8 +427,7 @@ static bool intel_hda_xfer(HDACodecDevice *dev, uint32_t stnr, bool output, dprint(d, 3, "dma: entry %d, pos %d/%d, copy %d\n", st->be, st->bp, st->bpl[st->be].len, copy); - cpu_physical_memory_rw(st->bpl[st->be].addr + st->bp, - buf, copy, !output); + pci_dma_rw(&d->pci, st->bpl[st->be].addr + st->bp, buf, copy, !output); st->lpib += copy; st->bp += copy; buf += copy; @@ -448,7 +449,7 @@ static bool intel_hda_xfer(HDACodecDevice *dev, uint32_t stnr, bool output, } if (d->dp_lbase & 0x01) { addr = intel_hda_addr(d->dp_lbase & ~0x01, d->dp_ubase); - stl_le_phys(addr + 8*s, st->lpib); + stl_le_pci_dma(&d->pci, addr + 8*s, st->lpib); } dprint(d, 3, "dma: --\n"); @@ -470,7 +471,7 @@ static void intel_hda_parse_bdl(IntelHDAState *d, IntelHDAStream *st) g_free(st->bpl); st->bpl = g_malloc(sizeof(bpl) * st->bentries); for (i = 0; i < st->bentries; i++, addr += 16) { - cpu_physical_memory_read(addr, buf, 16); + pci_dma_read(&d->pci, addr, buf, 16); st->bpl[i].addr = le64_to_cpu(*(uint64_t *)buf); st->bpl[i].len = le32_to_cpu(*(uint32_t *)(buf + 8)); st->bpl[i].flags = le32_to_cpu(*(uint32_t *)(buf + 12)); @@ -484,15 +485,15 @@ static void intel_hda_parse_bdl(IntelHDAState *d, IntelHDAStream *st) st->bp = 0; } -static void intel_hda_notify_codecs(IntelHDAState *d, uint32_t stream, bool running) +static void intel_hda_notify_codecs(IntelHDAState *d, uint32_t stream, bool running, bool output) { DeviceState *qdev; HDACodecDevice *cdev; - QLIST_FOREACH(qdev, &d->codecs.qbus.children, sibling) { + QTAILQ_FOREACH(qdev, &d->codecs.qbus.children, sibling) { cdev = DO_UPCAST(HDACodecDevice, qdev, qdev); if (cdev->info->stream) { - cdev->info->stream(cdev, stream, running); + cdev->info->stream(cdev, stream, running, output); } } } @@ -566,6 +567,7 @@ static void intel_hda_set_ics(IntelHDAState *d, const IntelHDAReg *reg, uint32_t static void intel_hda_set_st_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old) { + bool output = reg->stream >= 4; IntelHDAStream *st = d->st + reg->stream; if (st->ctl & 0x01) { @@ -581,11 +583,11 @@ static void intel_hda_set_st_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint3 dprint(d, 1, "st #%d: start %d (ring buf %d bytes)\n", reg->stream, stnr, st->cbl); intel_hda_parse_bdl(d, st); - intel_hda_notify_codecs(d, stnr, true); + intel_hda_notify_codecs(d, stnr, true, output); } else { /* stop */ dprint(d, 1, "st #%d: stop %d\n", reg->stream, stnr); - intel_hda_notify_codecs(d, stnr, false); + intel_hda_notify_codecs(d, stnr, false, output); } } intel_hda_update_irq(d); @@ -1112,7 +1114,7 @@ static void intel_hda_reset(DeviceState *dev) d->wall_base_ns = qemu_get_clock_ns(vm_clock); /* reset codecs */ - QLIST_FOREACH(qdev, &d->codecs.qbus.children, sibling) { + QTAILQ_FOREACH(qdev, &d->codecs.qbus.children, sibling) { cdev = DO_UPCAST(HDACodecDevice, qdev, qdev); if (qdev->info->reset) { qdev->info->reset(qdev); diff --git a/hw/intel-hda.h b/hw/intel-hda.h index 4e44e3894f..65fd2a85bb 100644 --- a/hw/intel-hda.h +++ b/hw/intel-hda.h @@ -34,7 +34,7 @@ struct HDACodecDeviceInfo { int (*init)(HDACodecDevice *dev); int (*exit)(HDACodecDevice *dev); void (*command)(HDACodecDevice *dev, uint32_t nid, uint32_t data); - void (*stream)(HDACodecDevice *dev, uint32_t stnr, bool running); + void (*stream)(HDACodecDevice *dev, uint32_t stnr, bool running, bool output); }; void hda_codec_bus_init(DeviceState *dev, HDACodecBus *bus, diff --git a/hw/ioapic.h b/hw/ioapic.h index cb2642ae53..86e63dac74 100644 --- a/hw/ioapic.h +++ b/hw/ioapic.h @@ -17,4 +17,11 @@ * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ +#ifndef HW_IOAPIC_H +#define HW_IOAPIC_H + +#define IOAPIC_NUM_PINS 24 + void ioapic_eoi_broadcast(int vector); + +#endif /* !HW_IOAPIC_H */ diff --git a/hw/isa.h b/hw/isa.h index d3cae350a0..5eb9c78e9e 100644 --- a/hw/isa.h +++ b/hw/isa.h @@ -7,6 +7,8 @@ #include "memory.h" #include "qdev.h" +#define ISA_NUM_IRQS 16 + typedef struct ISABus ISABus; typedef struct ISADevice ISADevice; typedef struct ISADeviceInfo ISADeviceInfo; diff --git a/hw/lance.c b/hw/lance.c index d83e7f57a9..93d5fda35b 100644 --- a/hw/lance.c +++ b/hw/lance.c @@ -97,6 +97,7 @@ static NetClientInfo net_lance_info = { .size = sizeof(NICState), .can_receive = pcnet_can_receive, .receive = pcnet_receive, + .link_status_changed = pcnet_set_link_status, .cleanup = lance_cleanup, }; diff --git a/hw/lm32_pic.c b/hw/lm32_pic.c index 02941a7d0d..8dd005077c 100644 --- a/hw/lm32_pic.c +++ b/hw/lm32_pic.c @@ -39,7 +39,7 @@ struct LM32PicState { typedef struct LM32PicState LM32PicState; static LM32PicState *pic; -void pic_info(Monitor *mon) +void lm32_do_pic_info(Monitor *mon) { if (pic == NULL) { return; @@ -49,7 +49,7 @@ void pic_info(Monitor *mon) pic->im, pic->ip, pic->irq_state); } -void irq_info(Monitor *mon) +void lm32_irq_info(Monitor *mon) { int i; uint32_t count; diff --git a/hw/lm32_pic.h b/hw/lm32_pic.h index e6479b8f63..14456f37cb 100644 --- a/hw/lm32_pic.h +++ b/hw/lm32_pic.h @@ -8,4 +8,7 @@ uint32_t lm32_pic_get_im(DeviceState *d); void lm32_pic_set_ip(DeviceState *d, uint32_t ip); void lm32_pic_set_im(DeviceState *d, uint32_t im); +void lm32_do_pic_info(Monitor *mon); +void lm32_irq_info(Monitor *mon); + #endif /* QEMU_HW_LM32_PIC_H */ diff --git a/hw/lm4549.c b/hw/lm4549.c new file mode 100644 index 0000000000..4d5b83125f --- /dev/null +++ b/hw/lm4549.c @@ -0,0 +1,336 @@ +/* + * LM4549 Audio Codec Interface + * + * Copyright (c) 2011 + * Written by Mathieu Sonet - www.elasticsheep.com + * + * This code is licenced under the GPL. + * + * ***************************************************************** + * + * This driver emulates the LM4549 codec. + * + * It supports only one playback voice and no record voice. + */ + +#include "hw.h" +#include "audio/audio.h" +#include "lm4549.h" + +#if 0 +#define LM4549_DEBUG 1 +#endif + +#if 0 +#define LM4549_DUMP_DAC_INPUT 1 +#endif + +#ifdef LM4549_DEBUG +#define DPRINTF(fmt, ...) \ +do { printf("lm4549: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) do {} while (0) +#endif + +#if defined(LM4549_DUMP_DAC_INPUT) +#include <stdio.h> +static FILE *fp_dac_input; +#endif + +/* LM4549 register list */ +enum { + LM4549_Reset = 0x00, + LM4549_Master_Volume = 0x02, + LM4549_Line_Out_Volume = 0x04, + LM4549_Master_Volume_Mono = 0x06, + LM4549_PC_Beep_Volume = 0x0A, + LM4549_Phone_Volume = 0x0C, + LM4549_Mic_Volume = 0x0E, + LM4549_Line_In_Volume = 0x10, + LM4549_CD_Volume = 0x12, + LM4549_Video_Volume = 0x14, + LM4549_Aux_Volume = 0x16, + LM4549_PCM_Out_Volume = 0x18, + LM4549_Record_Select = 0x1A, + LM4549_Record_Gain = 0x1C, + LM4549_General_Purpose = 0x20, + LM4549_3D_Control = 0x22, + LM4549_Powerdown_Ctrl_Stat = 0x26, + LM4549_Ext_Audio_ID = 0x28, + LM4549_Ext_Audio_Stat_Ctrl = 0x2A, + LM4549_PCM_Front_DAC_Rate = 0x2C, + LM4549_PCM_ADC_Rate = 0x32, + LM4549_Vendor_ID1 = 0x7C, + LM4549_Vendor_ID2 = 0x7E +}; + +static void lm4549_reset(lm4549_state *s) +{ + uint16_t *regfile = s->regfile; + + regfile[LM4549_Reset] = 0x0d50; + regfile[LM4549_Master_Volume] = 0x8008; + regfile[LM4549_Line_Out_Volume] = 0x8000; + regfile[LM4549_Master_Volume_Mono] = 0x8000; + regfile[LM4549_PC_Beep_Volume] = 0x0000; + regfile[LM4549_Phone_Volume] = 0x8008; + regfile[LM4549_Mic_Volume] = 0x8008; + regfile[LM4549_Line_In_Volume] = 0x8808; + regfile[LM4549_CD_Volume] = 0x8808; + regfile[LM4549_Video_Volume] = 0x8808; + regfile[LM4549_Aux_Volume] = 0x8808; + regfile[LM4549_PCM_Out_Volume] = 0x8808; + regfile[LM4549_Record_Select] = 0x0000; + regfile[LM4549_Record_Gain] = 0x8000; + regfile[LM4549_General_Purpose] = 0x0000; + regfile[LM4549_3D_Control] = 0x0101; + regfile[LM4549_Powerdown_Ctrl_Stat] = 0x000f; + regfile[LM4549_Ext_Audio_ID] = 0x0001; + regfile[LM4549_Ext_Audio_Stat_Ctrl] = 0x0000; + regfile[LM4549_PCM_Front_DAC_Rate] = 0xbb80; + regfile[LM4549_PCM_ADC_Rate] = 0xbb80; + regfile[LM4549_Vendor_ID1] = 0x4e53; + regfile[LM4549_Vendor_ID2] = 0x4331; +} + +static void lm4549_audio_transfer(lm4549_state *s) +{ + uint32_t written_bytes, written_samples; + uint32_t i; + + /* Activate the voice */ + AUD_set_active_out(s->voice, 1); + s->voice_is_active = 1; + + /* Try to write the buffer content */ + written_bytes = AUD_write(s->voice, s->buffer, + s->buffer_level * sizeof(uint16_t)); + written_samples = written_bytes >> 1; + +#if defined(LM4549_DUMP_DAC_INPUT) + fwrite(s->buffer, sizeof(uint8_t), written_bytes, fp_dac_input); +#endif + + s->buffer_level -= written_samples; + + if (s->buffer_level > 0) { + /* Move the data back to the start of the buffer */ + for (i = 0; i < s->buffer_level; i++) { + s->buffer[i] = s->buffer[i + written_samples]; + } + } +} + +static void lm4549_audio_out_callback(void *opaque, int free) +{ + lm4549_state *s = (lm4549_state *)opaque; + static uint32_t prev_buffer_level; + +#ifdef LM4549_DEBUG + int size = AUD_get_buffer_size_out(s->voice); + DPRINTF("audio_out_callback size = %i free = %i\n", size, free); +#endif + + /* Detect that no data are consumed + => disable the voice */ + if (s->buffer_level == prev_buffer_level) { + AUD_set_active_out(s->voice, 0); + s->voice_is_active = 0; + } + prev_buffer_level = s->buffer_level; + + /* Check if a buffer transfer is pending */ + if (s->buffer_level == LM4549_BUFFER_SIZE) { + lm4549_audio_transfer(s); + + /* Request more data */ + if (s->data_req_cb != NULL) { + (s->data_req_cb)(s->opaque); + } + } +} + +uint32_t lm4549_read(lm4549_state *s, target_phys_addr_t offset) +{ + uint16_t *regfile = s->regfile; + uint32_t value = 0; + + /* Read the stored value */ + assert(offset < 128); + value = regfile[offset]; + + DPRINTF("read [0x%02x] = 0x%04x\n", offset, value); + + return value; +} + +void lm4549_write(lm4549_state *s, + target_phys_addr_t offset, uint32_t value) +{ + uint16_t *regfile = s->regfile; + + assert(offset < 128); + DPRINTF("write [0x%02x] = 0x%04x\n", offset, value); + + switch (offset) { + case LM4549_Reset: + lm4549_reset(s); + break; + + case LM4549_PCM_Front_DAC_Rate: + regfile[LM4549_PCM_Front_DAC_Rate] = value; + DPRINTF("DAC rate change = %i\n", value); + + /* Re-open a voice with the new sample rate */ + struct audsettings as; + as.freq = value; + as.nchannels = 2; + as.fmt = AUD_FMT_S16; + as.endianness = 0; + + s->voice = AUD_open_out( + &s->card, + s->voice, + "lm4549.out", + s, + lm4549_audio_out_callback, + &as + ); + break; + + case LM4549_Powerdown_Ctrl_Stat: + value &= ~0xf; + value |= regfile[LM4549_Powerdown_Ctrl_Stat] & 0xf; + regfile[LM4549_Powerdown_Ctrl_Stat] = value; + break; + + case LM4549_Ext_Audio_ID: + case LM4549_Vendor_ID1: + case LM4549_Vendor_ID2: + DPRINTF("Write to read-only register 0x%x\n", (int)offset); + break; + + default: + /* Store the new value */ + regfile[offset] = value; + break; + } +} + +uint32_t lm4549_write_samples(lm4549_state *s, uint32_t left, uint32_t right) +{ + /* The left and right samples are in 20-bit resolution. + The LM4549 has 18-bit resolution and only uses the bits [19:2]. + This model supports 16-bit playback. + */ + + if (s->buffer_level >= LM4549_BUFFER_SIZE) { + DPRINTF("write_sample Buffer full\n"); + return 0; + } + + /* Store 16-bit samples in the buffer */ + s->buffer[s->buffer_level++] = (left >> 4); + s->buffer[s->buffer_level++] = (right >> 4); + + if (s->buffer_level == LM4549_BUFFER_SIZE) { + /* Trigger the transfer of the buffer to the audio host */ + lm4549_audio_transfer(s); + } + + return 1; +} + +static int lm4549_post_load(void *opaque, int version_id) +{ + lm4549_state *s = (lm4549_state *)opaque; + uint16_t *regfile = s->regfile; + + /* Re-open a voice with the current sample rate */ + uint32_t freq = regfile[LM4549_PCM_Front_DAC_Rate]; + + DPRINTF("post_load freq = %i\n", freq); + DPRINTF("post_load voice_is_active = %i\n", s->voice_is_active); + + struct audsettings as; + as.freq = freq; + as.nchannels = 2; + as.fmt = AUD_FMT_S16; + as.endianness = 0; + + s->voice = AUD_open_out( + &s->card, + s->voice, + "lm4549.out", + s, + lm4549_audio_out_callback, + &as + ); + + /* Request data */ + if (s->voice_is_active == 1) { + lm4549_audio_out_callback(s, AUD_get_buffer_size_out(s->voice)); + } + + return 0; +} + +void lm4549_init(lm4549_state *s, lm4549_callback data_req_cb, void* opaque) +{ + struct audsettings as; + + /* Store the callback and opaque pointer */ + s->data_req_cb = data_req_cb; + s->opaque = opaque; + + /* Init the registers */ + lm4549_reset(s); + + /* Register an audio card */ + AUD_register_card("lm4549", &s->card); + + /* Open a default voice */ + as.freq = 48000; + as.nchannels = 2; + as.fmt = AUD_FMT_S16; + as.endianness = 0; + + s->voice = AUD_open_out( + &s->card, + s->voice, + "lm4549.out", + s, + lm4549_audio_out_callback, + &as + ); + + AUD_set_volume_out(s->voice, 0, 255, 255); + + s->voice_is_active = 0; + + /* Reset the input buffer */ + memset(s->buffer, 0x00, sizeof(s->buffer)); + s->buffer_level = 0; + +#if defined(LM4549_DUMP_DAC_INPUT) + fp_dac_input = fopen("lm4549_dac_input.pcm", "wb"); + if (!fp_dac_input) { + hw_error("Unable to open lm4549_dac_input.pcm for writing\n"); + } +#endif +} + +const VMStateDescription vmstate_lm4549_state = { + .name = "lm4549_state", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .post_load = &lm4549_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32(voice_is_active, lm4549_state), + VMSTATE_UINT16_ARRAY(regfile, lm4549_state, 128), + VMSTATE_UINT16_ARRAY(buffer, lm4549_state, LM4549_BUFFER_SIZE), + VMSTATE_UINT32(buffer_level, lm4549_state), + VMSTATE_END_OF_LIST() + } +}; diff --git a/hw/lm4549.h b/hw/lm4549.h new file mode 100644 index 0000000000..70d0ac1750 --- /dev/null +++ b/hw/lm4549.h @@ -0,0 +1,43 @@ +/* + * LM4549 Audio Codec Interface + * + * Copyright (c) 2011 + * Written by Mathieu Sonet - www.elasticsheep.com + * + * This code is licenced under the GPL. + * + * ***************************************************************** + */ + +#ifndef HW_LM4549_H +#define HW_LM4549_H + +#include "audio/audio.h" + +typedef void (*lm4549_callback)(void *opaque); + +#define LM4549_BUFFER_SIZE (512 * 2) /* 512 16-bit stereo samples */ + + +typedef struct { + QEMUSoundCard card; + SWVoiceOut *voice; + uint32_t voice_is_active; + + uint16_t regfile[128]; + lm4549_callback data_req_cb; + void *opaque; + + uint16_t buffer[LM4549_BUFFER_SIZE]; + uint32_t buffer_level; +} lm4549_state; + +extern const VMStateDescription vmstate_lm4549_state; + + +void lm4549_init(lm4549_state *s, lm4549_callback data_req, void *opaque); +uint32_t lm4549_read(lm4549_state *s, target_phys_addr_t offset); +void lm4549_write(lm4549_state *s, target_phys_addr_t offset, uint32_t value); +uint32_t lm4549_write_samples(lm4549_state *s, uint32_t left, uint32_t right); + +#endif /* #ifndef HW_LM4549_H */ diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c index e077ec07cc..fcc27d726f 100644 --- a/hw/lsi53c895a.c +++ b/hw/lsi53c895a.c @@ -15,6 +15,8 @@ #include "hw.h" #include "pci.h" #include "scsi.h" +#include "block_int.h" +#include "dma.h" //#define DEBUG_LSI //#define DEBUG_LSI_REG @@ -390,10 +392,7 @@ static inline uint32_t read_dword(LSIState *s, uint32_t addr) { uint32_t buf; - /* XXX: an optimization here used to fast-path the read from scripts - * memory. But that bypasses any iommu. - */ - cpu_physical_memory_read(addr, (uint8_t *)&buf, 4); + pci_dma_read(&s->dev, addr, (uint8_t *)&buf, 4); return cpu_to_le32(buf); } @@ -531,8 +530,8 @@ static void lsi_bad_selection(LSIState *s, uint32_t id) /* Initiate a SCSI layer data transfer. */ static void lsi_do_dma(LSIState *s, int out) { - uint32_t count, id; - target_phys_addr_t addr; + uint32_t count; + dma_addr_t addr; SCSIDevice *dev; assert(s->current); @@ -542,12 +541,8 @@ static void lsi_do_dma(LSIState *s, int out) return; } - id = (s->current->tag >> 8) & 0xf; - dev = s->bus.devs[id]; - if (!dev) { - lsi_bad_selection(s, id); - return; - } + dev = s->current->req->dev; + assert(dev); count = s->dbc; if (count > s->current->dma_len) @@ -562,7 +557,7 @@ static void lsi_do_dma(LSIState *s, int out) else if (s->sbms) addr |= ((uint64_t)s->sbms << 32); - DPRINTF("DMA addr=0x" TARGET_FMT_plx " len=%d\n", addr, count); + DPRINTF("DMA addr=0x" DMA_ADDR_FMT " len=%d\n", addr, count); s->csbc += count; s->dnad += count; s->dbc -= count; @@ -571,9 +566,9 @@ static void lsi_do_dma(LSIState *s, int out) } /* ??? Set SFBR to first data byte. */ if (out) { - cpu_physical_memory_read(addr, s->current->dma_buf, count); + pci_dma_read(&s->dev, addr, s->current->dma_buf, count); } else { - cpu_physical_memory_write(addr, s->current->dma_buf, count); + pci_dma_write(&s->dev, addr, s->current->dma_buf, count); } s->current->dma_len -= count; if (s->current->dma_len == 0) { @@ -766,12 +761,12 @@ static void lsi_do_command(LSIState *s) DPRINTF("Send command len=%d\n", s->dbc); if (s->dbc > 16) s->dbc = 16; - cpu_physical_memory_read(s->dnad, buf, s->dbc); + pci_dma_read(&s->dev, s->dnad, buf, s->dbc); s->sfbr = buf[0]; s->command_complete = 0; id = (s->select_tag >> 8) & 0xf; - dev = s->bus.devs[id]; + dev = scsi_device_find(&s->bus, 0, id, s->current_lun); if (!dev) { lsi_bad_selection(s, id); return; @@ -817,7 +812,7 @@ static void lsi_do_status(LSIState *s) s->dbc = 1; status = s->status; s->sfbr = status; - cpu_physical_memory_write(s->dnad, &status, 1); + pci_dma_write(&s->dev, s->dnad, &status, 1); lsi_set_phase(s, PHASE_MI); s->msg_action = 1; lsi_add_msg_byte(s, 0); /* COMMAND COMPLETE */ @@ -831,7 +826,7 @@ static void lsi_do_msgin(LSIState *s) len = s->msg_len; if (len > s->dbc) len = s->dbc; - cpu_physical_memory_write(s->dnad, s->msg, len); + pci_dma_write(&s->dev, s->dnad, s->msg, len); /* Linux drivers rely on the last byte being in the SIDL. */ s->sidl = s->msg[len - 1]; s->msg_len -= len; @@ -863,7 +858,7 @@ static void lsi_do_msgin(LSIState *s) static uint8_t lsi_get_msgbyte(LSIState *s) { uint8_t data; - cpu_physical_memory_read(s->dnad, &data, 1); + pci_dma_read(&s->dev, s->dnad, &data, 1); s->dnad++; s->dbc--; return data; @@ -1015,8 +1010,8 @@ static void lsi_memcpy(LSIState *s, uint32_t dest, uint32_t src, int count) DPRINTF("memcpy dest 0x%08x src 0x%08x count %d\n", dest, src, count); while (count) { n = (count > LSI_BUF_SIZE) ? LSI_BUF_SIZE : count; - cpu_physical_memory_read(src, buf, n); - cpu_physical_memory_write(dest, buf, n); + pci_dma_read(&s->dev, src, buf, n); + pci_dma_write(&s->dev, dest, buf, n); src += n; dest += n; count -= n; @@ -1084,7 +1079,7 @@ again: /* 32-bit Table indirect */ offset = sxt24(addr); - cpu_physical_memory_read(s->dsa + offset, (uint8_t *)buf, 8); + pci_dma_read(&s->dev, s->dsa + offset, (uint8_t *)buf, 8); /* byte count is stored in bits 0:23 only */ s->dbc = cpu_to_le32(buf[0]) & 0xffffff; s->rbc = s->dbc; @@ -1202,7 +1197,7 @@ again: } s->sstat0 |= LSI_SSTAT0_WOA; s->scntl1 &= ~LSI_SCNTL1_IARB; - if (id >= LSI_MAX_DEVS || !s->bus.devs[id]) { + if (!scsi_device_find(&s->bus, 0, id, 0)) { lsi_bad_selection(s, id); break; } @@ -1443,7 +1438,7 @@ again: n = (insn & 7); reg = (insn >> 16) & 0xff; if (insn & (1 << 24)) { - cpu_physical_memory_read(addr, data, n); + pci_dma_read(&s->dev, addr, data, n); DPRINTF("Load reg 0x%x size %d addr 0x%08x = %08x\n", reg, n, addr, *(int *)data); for (i = 0; i < n; i++) { @@ -1454,7 +1449,7 @@ again: for (i = 0; i < n; i++) { data[i] = lsi_reg_readb(s, reg + i); } - cpu_physical_memory_write(addr, data, n); + pci_dma_write(&s->dev, addr, data, n); } } } @@ -1684,13 +1679,9 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val) if (val & LSI_SCNTL1_RST) { if (!(s->sstat0 & LSI_SSTAT0_RST)) { DeviceState *dev; - int id; - for (id = 0; id < s->bus.ndev; id++) { - if (s->bus.devs[id]) { - dev = &s->bus.devs[id]->qdev; - dev->info->reset(dev); - } + QTAILQ_FOREACH(dev, &s->bus.qbus.children, sibling) { + dev->info->reset(dev); } s->sstat0 |= LSI_SSTAT0_RST; lsi_script_scsi_interrupt(s, LSI_SIST0_RST, 0); @@ -2091,7 +2082,11 @@ static int lsi_scsi_uninit(PCIDevice *d) return 0; } -static const struct SCSIBusOps lsi_scsi_ops = { +static const struct SCSIBusInfo lsi_scsi_info = { + .tcq = true, + .max_target = LSI_MAX_DEVS, + .max_lun = 0, /* LUN support is buggy */ + .transfer_data = lsi_transfer_data, .complete = lsi_command_complete, .cancel = lsi_request_cancelled @@ -2118,7 +2113,7 @@ static int lsi_scsi_init(PCIDevice *dev) pci_register_bar(&s->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->ram_io); QTAILQ_INIT(&s->queue); - scsi_bus_new(&s->bus, &dev->qdev, 1, LSI_MAX_DEVS, &lsi_scsi_ops); + scsi_bus_new(&s->bus, &dev->qdev, &lsi_scsi_info); if (!dev->qdev.hotplugged) { return scsi_bus_legacy_handle_cmdline(&s->bus); } diff --git a/hw/m48t59.c b/hw/m48t59.c index f318e67919..a77937ef68 100644 --- a/hw/m48t59.c +++ b/hw/m48t59.c @@ -480,7 +480,6 @@ static void NVRAM_writeb (void *opaque, uint32_t addr, uint32_t val) { M48t59State *NVRAM = opaque; - addr -= NVRAM->io_base; NVRAM_PRINTF("%s: 0x%08x => 0x%08x\n", __func__, addr, val); switch (addr) { case 0: @@ -492,7 +491,7 @@ static void NVRAM_writeb (void *opaque, uint32_t addr, uint32_t val) NVRAM->addr |= val << 8; break; case 3: - m48t59_write(NVRAM, val, NVRAM->addr); + m48t59_write(NVRAM, NVRAM->addr, val); NVRAM->addr = 0x0000; break; default: @@ -505,7 +504,6 @@ static uint32_t NVRAM_readb (void *opaque, uint32_t addr) M48t59State *NVRAM = opaque; uint32_t retval; - addr -= NVRAM->io_base; switch (addr) { case 3: retval = m48t59_read(NVRAM, NVRAM->addr); diff --git a/hw/mac_dbdma.c b/hw/mac_dbdma.c index 5affdd18a5..1791ec12e1 100644 --- a/hw/mac_dbdma.c +++ b/hw/mac_dbdma.c @@ -661,11 +661,6 @@ void DBDMA_register_channel(void *dbdma, int nchan, qemu_irq irq, ch->io.channel = ch; } -void DBDMA_schedule(void) -{ - qemu_notify_event(); -} - static void dbdma_control_write(DBDMA_channel *ch) { diff --git a/hw/mac_dbdma.h b/hw/mac_dbdma.h index 933e17c5b9..6d1abe6aae 100644 --- a/hw/mac_dbdma.h +++ b/hw/mac_dbdma.h @@ -41,5 +41,4 @@ struct DBDMA_io { void DBDMA_register_channel(void *dbdma, int nchan, qemu_irq irq, DBDMA_rw rw, DBDMA_flush flush, void *opaque); -void DBDMA_schedule(void); void* DBDMA_init (MemoryRegion **dbdma_mem); diff --git a/hw/microblaze_pic_cpu.c b/hw/microblaze_pic_cpu.c index 9ad48b4b95..8b5623ce28 100644 --- a/hw/microblaze_pic_cpu.c +++ b/hw/microblaze_pic_cpu.c @@ -23,16 +23,10 @@ */ #include "hw.h" -#include "pc.h" #include "microblaze_pic_cpu.h" #define D(x) -void pic_info(Monitor *mon) -{} -void irq_info(Monitor *mon) -{} - static void microblaze_pic_cpu_handler(void *opaque, int irq, int level) { CPUState *env = (CPUState *)opaque; diff --git a/hw/omap2.c b/hw/omap2.c index 838c32f371..5197fef2d8 100644 --- a/hw/omap2.c +++ b/hw/omap2.c @@ -2409,6 +2409,11 @@ struct omap_mpu_state_s *omap2420_mpu_init(unsigned long sdram_size, qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPIO_BANK3)); sysbus_connect_irq(busdev, 9, qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPIO_BANK4)); + if (s->mpu_model == omap2430) { + sysbus_connect_irq(busdev, 12, + qdev_get_gpio_in(s->ih[0], + OMAP_INT_243X_GPIO_BANK5)); + } ta = omap_l4ta(s->l4, 3); sysbus_mmio_map(busdev, 0, omap_l4_region_base(ta, 1)); sysbus_mmio_map(busdev, 1, omap_l4_region_base(ta, 0)); diff --git a/hw/opencores_eth.c b/hw/opencores_eth.c new file mode 100644 index 0000000000..2c1e475395 --- /dev/null +++ b/hw/opencores_eth.c @@ -0,0 +1,747 @@ +/* + * OpenCores Ethernet MAC 10/100 + subset of + * National Semiconductors DP83848C 10/100 PHY + * + * http://opencores.org/svnget,ethmac?file=%2Ftrunk%2F%2Fdoc%2Feth_speci.pdf + * http://cache.national.com/ds/DP/DP83848C.pdf + * + * Copyright (c) 2011, Max Filippov, Open Source and Linux Lab. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Open Source and Linux Lab nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "hw.h" +#include "sysbus.h" +#include "net.h" +#include "sysemu.h" +#include "trace.h" + +/* RECSMALL is not used because it breaks tap networking in linux: + * incoming ARP responses are too short + */ +#undef USE_RECSMALL + +#define GET_FIELD(v, field) (((v) & (field)) >> (field ## _LBN)) +#define GET_REGBIT(s, reg, field) ((s)->regs[reg] & (reg ## _ ## field)) +#define GET_REGFIELD(s, reg, field) \ + GET_FIELD((s)->regs[reg], reg ## _ ## field) + +#define SET_FIELD(v, field, data) \ + ((v) = (((v) & ~(field)) | (((data) << (field ## _LBN)) & (field)))) +#define SET_REGFIELD(s, reg, field, data) \ + SET_FIELD((s)->regs[reg], reg ## _ ## field, data) + +/* PHY MII registers */ +enum { + MII_BMCR, + MII_BMSR, + MII_PHYIDR1, + MII_PHYIDR2, + MII_ANAR, + MII_ANLPAR, + MII_REG_MAX = 16, +}; + +typedef struct Mii { + uint16_t regs[MII_REG_MAX]; + bool link_ok; +} Mii; + +static void mii_set_link(Mii *s, bool link_ok) +{ + if (link_ok) { + s->regs[MII_BMSR] |= 0x4; + s->regs[MII_ANLPAR] |= 0x01e1; + } else { + s->regs[MII_BMSR] &= ~0x4; + s->regs[MII_ANLPAR] &= 0x01ff; + } + s->link_ok = link_ok; +} + +static void mii_reset(Mii *s) +{ + memset(s->regs, 0, sizeof(s->regs)); + s->regs[MII_BMCR] = 0x1000; + s->regs[MII_BMSR] = 0x7848; /* no ext regs */ + s->regs[MII_PHYIDR1] = 0x2000; + s->regs[MII_PHYIDR2] = 0x5c90; + s->regs[MII_ANAR] = 0x01e1; + mii_set_link(s, s->link_ok); +} + +static void mii_ro(Mii *s, uint16_t v) +{ +} + +static void mii_write_bmcr(Mii *s, uint16_t v) +{ + if (v & 0x8000) { + mii_reset(s); + } else { + s->regs[MII_BMCR] = v; + } +} + +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, + }; + + if (idx < MII_REG_MAX) { + trace_open_eth_mii_write(idx, v); + if (reg_write[idx]) { + reg_write[idx](s, v); + } else { + s->regs[idx] = v; + } + } +} + +static uint16_t mii_read_host(Mii *s, unsigned idx) +{ + trace_open_eth_mii_read(idx, s->regs[idx]); + return s->regs[idx]; +} + +/* OpenCores Ethernet registers */ +enum { + MODER, + INT_SOURCE, + INT_MASK, + IPGT, + IPGR1, + IPGR2, + PACKETLEN, + COLLCONF, + TX_BD_NUM, + CTRLMODER, + MIIMODER, + MIICOMMAND, + MIIADDRESS, + MIITX_DATA, + MIIRX_DATA, + MIISTATUS, + MAC_ADDR0, + MAC_ADDR1, + HASH0, + HASH1, + TXCTRL, + REG_MAX, +}; + +enum { + MODER_RECSMALL = 0x10000, + MODER_PAD = 0x8000, + MODER_HUGEN = 0x4000, + MODER_RST = 0x800, + MODER_LOOPBCK = 0x80, + MODER_PRO = 0x20, + MODER_IAM = 0x10, + MODER_BRO = 0x8, + MODER_TXEN = 0x2, + MODER_RXEN = 0x1, +}; + +enum { + INT_SOURCE_RXB = 0x4, + INT_SOURCE_TXB = 0x1, +}; + +enum { + PACKETLEN_MINFL = 0xffff0000, + PACKETLEN_MINFL_LBN = 16, + PACKETLEN_MAXFL = 0xffff, + PACKETLEN_MAXFL_LBN = 0, +}; + +enum { + MIICOMMAND_WCTRLDATA = 0x4, + MIICOMMAND_RSTAT = 0x2, + MIICOMMAND_SCANSTAT = 0x1, +}; + +enum { + MIIADDRESS_RGAD = 0x1f00, + MIIADDRESS_RGAD_LBN = 8, + MIIADDRESS_FIAD = 0x1f, + MIIADDRESS_FIAD_LBN = 0, +}; + +enum { + MIITX_DATA_CTRLDATA = 0xffff, + MIITX_DATA_CTRLDATA_LBN = 0, +}; + +enum { + MIIRX_DATA_PRSD = 0xffff, + MIIRX_DATA_PRSD_LBN = 0, +}; + +enum { + MIISTATUS_LINKFAIL = 0x1, + MIISTATUS_LINKFAIL_LBN = 0, +}; + +enum { + MAC_ADDR0_BYTE2 = 0xff000000, + MAC_ADDR0_BYTE2_LBN = 24, + MAC_ADDR0_BYTE3 = 0xff0000, + MAC_ADDR0_BYTE3_LBN = 16, + MAC_ADDR0_BYTE4 = 0xff00, + MAC_ADDR0_BYTE4_LBN = 8, + MAC_ADDR0_BYTE5 = 0xff, + MAC_ADDR0_BYTE5_LBN = 0, +}; + +enum { + MAC_ADDR1_BYTE0 = 0xff00, + MAC_ADDR1_BYTE0_LBN = 8, + MAC_ADDR1_BYTE1 = 0xff, + MAC_ADDR1_BYTE1_LBN = 0, +}; + +enum { + TXD_LEN = 0xffff0000, + TXD_LEN_LBN = 16, + TXD_RD = 0x8000, + TXD_IRQ = 0x4000, + TXD_WR = 0x2000, + TXD_PAD = 0x1000, + TXD_CRC = 0x800, + TXD_UR = 0x100, + TXD_RTRY = 0xf0, + TXD_RTRY_LBN = 4, + TXD_RL = 0x8, + TXD_LC = 0x4, + TXD_DF = 0x2, + TXD_CS = 0x1, +}; + +enum { + RXD_LEN = 0xffff0000, + RXD_LEN_LBN = 16, + RXD_E = 0x8000, + RXD_IRQ = 0x4000, + RXD_WRAP = 0x2000, + RXD_CF = 0x100, + RXD_M = 0x80, + RXD_OR = 0x40, + RXD_IS = 0x20, + RXD_DN = 0x10, + RXD_TL = 0x8, + RXD_SF = 0x4, + RXD_CRC = 0x2, + RXD_LC = 0x1, +}; + +typedef struct desc { + uint32_t len_flags; + uint32_t buf_ptr; +} desc; + +#define DEFAULT_PHY 1 + +typedef struct OpenEthState { + SysBusDevice dev; + NICState *nic; + NICConf conf; + MemoryRegion reg_io; + MemoryRegion desc_io; + qemu_irq irq; + + Mii mii; + uint32_t regs[REG_MAX]; + unsigned tx_desc; + unsigned rx_desc; + desc desc[128]; +} OpenEthState; + +static desc *rx_desc(OpenEthState *s) +{ + return s->desc + s->rx_desc; +} + +static desc *tx_desc(OpenEthState *s) +{ + return s->desc + s->tx_desc; +} + +static void open_eth_update_irq(OpenEthState *s, + uint32_t old, uint32_t new) +{ + if (!old != !new) { + trace_open_eth_update_irq(new); + qemu_set_irq(s->irq, new); + } +} + +static void open_eth_int_source_write(OpenEthState *s, + uint32_t val) +{ + uint32_t old_val = s->regs[INT_SOURCE]; + + s->regs[INT_SOURCE] = val; + open_eth_update_irq(s, old_val & s->regs[INT_MASK], + s->regs[INT_SOURCE] & s->regs[INT_MASK]); +} + +static void open_eth_set_link_status(VLANClientState *nc) +{ + OpenEthState *s = DO_UPCAST(NICState, nc, nc)->opaque; + + if (GET_REGBIT(s, MIICOMMAND, SCANSTAT)) { + SET_REGFIELD(s, MIISTATUS, LINKFAIL, nc->link_down); + } + mii_set_link(&s->mii, !nc->link_down); +} + +static void open_eth_reset(void *opaque) +{ + OpenEthState *s = opaque; + + memset(s->regs, 0, sizeof(s->regs)); + s->regs[MODER] = 0xa000; + s->regs[IPGT] = 0x12; + s->regs[IPGR1] = 0xc; + s->regs[IPGR2] = 0x12; + s->regs[PACKETLEN] = 0x400600; + s->regs[COLLCONF] = 0xf003f; + s->regs[TX_BD_NUM] = 0x40; + s->regs[MIIMODER] = 0x64; + + s->tx_desc = 0; + s->rx_desc = 0x40; + + mii_reset(&s->mii); + open_eth_set_link_status(&s->nic->nc); +} + +static int open_eth_can_receive(VLANClientState *nc) +{ + OpenEthState *s = DO_UPCAST(NICState, nc, nc)->opaque; + + return GET_REGBIT(s, MODER, RXEN) && + (s->regs[TX_BD_NUM] < 0x80) && + (rx_desc(s)->len_flags & RXD_E); +} + +#define POLYNOMIAL 0x04c11db6 + +/* From FreeBSD */ +/* XXX: optimize */ +static unsigned compute_mcast_idx(const uint8_t *ep) +{ + uint32_t crc; + int carry, i, j; + uint8_t b; + + crc = 0xffffffff; + for (i = 0; i < 6; i++) { + b = *ep++; + for (j = 0; j < 8; j++) { + carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01); + crc <<= 1; + b >>= 1; + if (carry) { + crc = ((crc ^ POLYNOMIAL) | carry); + } + } + } + return crc >> 26; +} + +static ssize_t open_eth_receive(VLANClientState *nc, + const uint8_t *buf, size_t size) +{ + OpenEthState *s = DO_UPCAST(NICState, nc, nc)->opaque; + size_t maxfl = GET_REGFIELD(s, PACKETLEN, MAXFL); + size_t minfl = GET_REGFIELD(s, PACKETLEN, MINFL); + size_t fcsl = 4; + bool miss = true; + + trace_open_eth_receive((unsigned)size); + + if (size >= 6) { + static const uint8_t bcast_addr[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; + if (memcmp(buf, bcast_addr, sizeof(bcast_addr)) == 0) { + miss = GET_REGBIT(s, MODER, BRO); + } else if ((buf[0] & 0x1) || GET_REGBIT(s, MODER, IAM)) { + unsigned mcast_idx = compute_mcast_idx(buf); + miss = !(s->regs[HASH0 + mcast_idx / 32] & + (1 << (mcast_idx % 32))); + trace_open_eth_receive_mcast( + mcast_idx, s->regs[HASH0], s->regs[HASH1]); + } else { + miss = GET_REGFIELD(s, MAC_ADDR1, BYTE0) != buf[0] || + GET_REGFIELD(s, MAC_ADDR1, BYTE1) != buf[1] || + GET_REGFIELD(s, MAC_ADDR0, BYTE2) != buf[2] || + GET_REGFIELD(s, MAC_ADDR0, BYTE3) != buf[3] || + GET_REGFIELD(s, MAC_ADDR0, BYTE4) != buf[4] || + GET_REGFIELD(s, MAC_ADDR0, BYTE5) != buf[5]; + } + } + + if (miss && !GET_REGBIT(s, MODER, PRO)) { + trace_open_eth_receive_reject(); + return size; + } + +#ifdef USE_RECSMALL + if (GET_REGBIT(s, MODER, RECSMALL) || size >= minfl) { +#else + { +#endif + static const uint8_t zero[64] = {0}; + desc *desc = rx_desc(s); + size_t copy_size = GET_REGBIT(s, MODER, HUGEN) ? 65536 : maxfl; + + desc->len_flags &= ~(RXD_CF | RXD_M | RXD_OR | + RXD_IS | RXD_DN | RXD_TL | RXD_SF | RXD_CRC | RXD_LC); + + if (copy_size > size) { + copy_size = size; + } else { + fcsl = 0; + } + if (miss) { + desc->len_flags |= RXD_M; + } + if (GET_REGBIT(s, MODER, HUGEN) && size > maxfl) { + desc->len_flags |= RXD_TL; + } +#ifdef USE_RECSMALL + if (size < minfl) { + desc->len_flags |= RXD_SF; + } +#endif + + cpu_physical_memory_write(desc->buf_ptr, buf, copy_size); + + if (GET_REGBIT(s, MODER, PAD) && copy_size < minfl) { + if (minfl - copy_size > fcsl) { + fcsl = 0; + } else { + fcsl -= minfl - copy_size; + } + while (copy_size < minfl) { + size_t zero_sz = minfl - copy_size < sizeof(zero) ? + minfl - copy_size : sizeof(zero); + + cpu_physical_memory_write(desc->buf_ptr + copy_size, + zero, zero_sz); + copy_size += zero_sz; + } + } + + /* There's no FCS in the frames handed to us by the QEMU, zero fill it. + * Don't do it if the frame is cut at the MAXFL or padded with 4 or + * more bytes to the MINFL. + */ + cpu_physical_memory_write(desc->buf_ptr + copy_size, zero, fcsl); + copy_size += fcsl; + + SET_FIELD(desc->len_flags, RXD_LEN, copy_size); + + if ((desc->len_flags & RXD_WRAP) || s->rx_desc == 0x7f) { + s->rx_desc = s->regs[TX_BD_NUM]; + } else { + ++s->rx_desc; + } + desc->len_flags &= ~RXD_E; + + trace_open_eth_receive_desc(desc->buf_ptr, desc->len_flags); + + if (desc->len_flags & RXD_IRQ) { + open_eth_int_source_write(s, + s->regs[INT_SOURCE] | INT_SOURCE_RXB); + } + } + return size; +} + +static void open_eth_cleanup(VLANClientState *nc) +{ +} + +static NetClientInfo net_open_eth_info = { + .type = NET_CLIENT_TYPE_NIC, + .size = sizeof(NICState), + .can_receive = open_eth_can_receive, + .receive = open_eth_receive, + .cleanup = open_eth_cleanup, + .link_status_changed = open_eth_set_link_status, +}; + +static void open_eth_start_xmit(OpenEthState *s, desc *tx) +{ + uint8_t buf[65536]; + unsigned len = GET_FIELD(tx->len_flags, TXD_LEN); + unsigned tx_len = len; + + if ((tx->len_flags & TXD_PAD) && + tx_len < GET_REGFIELD(s, PACKETLEN, MINFL)) { + tx_len = GET_REGFIELD(s, PACKETLEN, MINFL); + } + if (!GET_REGBIT(s, MODER, HUGEN) && + tx_len > GET_REGFIELD(s, PACKETLEN, MAXFL)) { + tx_len = GET_REGFIELD(s, PACKETLEN, MAXFL); + } + + trace_open_eth_start_xmit(tx->buf_ptr, len, tx_len); + + if (len > tx_len) { + len = tx_len; + } + cpu_physical_memory_read(tx->buf_ptr, buf, len); + if (tx_len > len) { + memset(buf + len, 0, tx_len - len); + } + qemu_send_packet(&s->nic->nc, buf, tx_len); + + if (tx->len_flags & TXD_WR) { + s->tx_desc = 0; + } else { + ++s->tx_desc; + if (s->tx_desc >= s->regs[TX_BD_NUM]) { + s->tx_desc = 0; + } + } + tx->len_flags &= ~(TXD_RD | TXD_UR | + TXD_RTRY | TXD_RL | TXD_LC | TXD_DF | TXD_CS); + if (tx->len_flags & TXD_IRQ) { + open_eth_int_source_write(s, s->regs[INT_SOURCE] | INT_SOURCE_TXB); + } + +} + +static void open_eth_check_start_xmit(OpenEthState *s) +{ + desc *tx = tx_desc(s); + if (GET_REGBIT(s, MODER, TXEN) && s->regs[TX_BD_NUM] > 0 && + (tx->len_flags & TXD_RD) && + GET_FIELD(tx->len_flags, TXD_LEN) > 4) { + open_eth_start_xmit(s, tx); + } +} + +static uint64_t open_eth_reg_read(void *opaque, + target_phys_addr_t addr, unsigned int size) +{ + static uint32_t (*reg_read[REG_MAX])(OpenEthState *s) = { + }; + OpenEthState *s = opaque; + unsigned idx = addr / 4; + uint64_t v = 0; + + if (idx < REG_MAX) { + if (reg_read[idx]) { + v = reg_read[idx](s); + } else { + v = s->regs[idx]; + } + } + trace_open_eth_reg_read((uint32_t)addr, (uint32_t)v); + return v; +} + +static void open_eth_ro(OpenEthState *s, uint32_t val) +{ +} + +static void open_eth_moder_host_write(OpenEthState *s, uint32_t val) +{ + uint32_t set = val & ~s->regs[MODER]; + + if (set & MODER_RST) { + open_eth_reset(s); + } + + s->regs[MODER] = val; + + if (set & MODER_RXEN) { + s->rx_desc = s->regs[TX_BD_NUM]; + } + if (set & MODER_TXEN) { + s->tx_desc = 0; + open_eth_check_start_xmit(s); + } +} + +static void open_eth_int_source_host_write(OpenEthState *s, uint32_t val) +{ + uint32_t old = s->regs[INT_SOURCE]; + + s->regs[INT_SOURCE] &= ~val; + open_eth_update_irq(s, old & s->regs[INT_MASK], + s->regs[INT_SOURCE] & s->regs[INT_MASK]); +} + +static void open_eth_int_mask_host_write(OpenEthState *s, uint32_t val) +{ + uint32_t old = s->regs[INT_MASK]; + + s->regs[INT_MASK] = val; + open_eth_update_irq(s, s->regs[INT_SOURCE] & old, + s->regs[INT_SOURCE] & s->regs[INT_MASK]); +} + +static void open_eth_mii_command_host_write(OpenEthState *s, uint32_t val) +{ + unsigned fiad = GET_REGFIELD(s, MIIADDRESS, FIAD); + unsigned rgad = GET_REGFIELD(s, MIIADDRESS, RGAD); + + if (val & MIICOMMAND_WCTRLDATA) { + if (fiad == DEFAULT_PHY) { + mii_write_host(&s->mii, rgad, + GET_REGFIELD(s, MIITX_DATA, CTRLDATA)); + } + } + if (val & MIICOMMAND_RSTAT) { + if (fiad == DEFAULT_PHY) { + SET_REGFIELD(s, MIIRX_DATA, PRSD, + mii_read_host(&s->mii, rgad)); + } else { + s->regs[MIIRX_DATA] = 0xffff; + } + SET_REGFIELD(s, MIISTATUS, LINKFAIL, s->nic->nc.link_down); + } +} + +static void open_eth_mii_tx_host_write(OpenEthState *s, uint32_t val) +{ + SET_REGFIELD(s, MIITX_DATA, CTRLDATA, val); + if (GET_REGFIELD(s, MIIADDRESS, FIAD) == DEFAULT_PHY) { + mii_write_host(&s->mii, GET_REGFIELD(s, MIIADDRESS, RGAD), + GET_REGFIELD(s, MIITX_DATA, CTRLDATA)); + } +} + +static void open_eth_reg_write(void *opaque, + target_phys_addr_t addr, uint64_t val, unsigned int size) +{ + static void (*reg_write[REG_MAX])(OpenEthState *s, uint32_t val) = { + [MODER] = open_eth_moder_host_write, + [INT_SOURCE] = open_eth_int_source_host_write, + [INT_MASK] = open_eth_int_mask_host_write, + [MIICOMMAND] = open_eth_mii_command_host_write, + [MIITX_DATA] = open_eth_mii_tx_host_write, + [MIISTATUS] = open_eth_ro, + }; + OpenEthState *s = opaque; + unsigned idx = addr / 4; + + if (idx < REG_MAX) { + trace_open_eth_reg_write((uint32_t)addr, (uint32_t)val); + if (reg_write[idx]) { + reg_write[idx](s, val); + } else { + s->regs[idx] = val; + } + } +} + +static uint64_t open_eth_desc_read(void *opaque, + target_phys_addr_t addr, unsigned int size) +{ + OpenEthState *s = opaque; + uint64_t v = 0; + + addr &= 0x3ff; + memcpy(&v, (uint8_t *)s->desc + addr, size); + trace_open_eth_desc_read((uint32_t)addr, (uint32_t)v); + return v; +} + +static void open_eth_desc_write(void *opaque, + target_phys_addr_t addr, uint64_t val, unsigned int size) +{ + OpenEthState *s = opaque; + + addr &= 0x3ff; + trace_open_eth_desc_write((uint32_t)addr, (uint32_t)val); + memcpy((uint8_t *)s->desc + addr, &val, size); + open_eth_check_start_xmit(s); +} + + +static MemoryRegionOps open_eth_reg_ops = { + .read = open_eth_reg_read, + .write = open_eth_reg_write, +}; + +static MemoryRegionOps open_eth_desc_ops = { + .read = open_eth_desc_read, + .write = open_eth_desc_write, +}; + +static int sysbus_open_eth_init(SysBusDevice *dev) +{ + OpenEthState *s = DO_UPCAST(OpenEthState, dev, dev); + + memory_region_init_io(&s->reg_io, &open_eth_reg_ops, s, + "open_eth.regs", 0x54); + sysbus_init_mmio_region(dev, &s->reg_io); + + memory_region_init_io(&s->desc_io, &open_eth_desc_ops, s, + "open_eth.desc", 0x400); + sysbus_init_mmio_region(dev, &s->desc_io); + + sysbus_init_irq(dev, &s->irq); + + s->nic = qemu_new_nic(&net_open_eth_info, &s->conf, + s->dev.qdev.info->name, s->dev.qdev.id, s); + return 0; +} + +static void qdev_open_eth_reset(DeviceState *dev) +{ + OpenEthState *d = DO_UPCAST(OpenEthState, dev.qdev, dev); + open_eth_reset(d); +} + +static SysBusDeviceInfo open_eth_info = { + .qdev.name = "open_eth", + .qdev.desc = "Opencores 10/100 Mbit Ethernet", + .qdev.size = sizeof(OpenEthState), + .qdev.reset = qdev_open_eth_reset, + .init = sysbus_open_eth_init, + .qdev.props = (Property[]) { + DEFINE_NIC_PROPERTIES(OpenEthState, conf), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static void open_eth_register_devices(void) +{ + sysbus_register_withprop(&open_eth_info); +} + +device_init(open_eth_register_devices) diff --git a/hw/pc.c b/hw/pc.c index ded475805f..25bfa85022 100644 --- a/hw/pc.c +++ b/hw/pc.c @@ -88,17 +88,16 @@ struct e820_table { static struct e820_table e820_table; struct hpet_fw_config hpet_cfg = {.count = UINT8_MAX}; -void isa_irq_handler(void *opaque, int n, int level) +void gsi_handler(void *opaque, int n, int level) { - IsaIrqState *isa = (IsaIrqState *)opaque; + GSIState *s = opaque; - DPRINTF("isa_irqs: %s irq %d\n", level? "raise" : "lower", n); - if (n < 16) { - qemu_set_irq(isa->i8259[n], level); + DPRINTF("pc: %s GSI %d\n", level ? "raising" : "lowering", n); + if (n < ISA_NUM_IRQS) { + qemu_set_irq(s->i8259_irq[n], level); } - if (isa->ioapic) - qemu_set_irq(isa->ioapic[n], level); -}; + qemu_set_irq(s->ioapic_irq[n], level); +} static void ioport80_write(void *opaque, uint32_t addr, uint32_t data) { @@ -156,9 +155,6 @@ int cpu_get_pic_interrupt(CPUState *env) intno = apic_get_interrupt(env->apic_state); if (intno >= 0) { - /* set irq request if a PIC irq is still pending */ - /* XXX: improve that */ - pic_update_irq(isa_pic); return intno; } /* read the irq from the PIC */ @@ -335,12 +331,12 @@ static void pc_cmos_init_late(void *opaque) void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size, const char *boot_device, - BusState *idebus0, BusState *idebus1, + ISADevice *floppy, BusState *idebus0, BusState *idebus1, ISADevice *s) { int val, nb, nb_heads, max_track, last_sect, i; FDriveType fd_type[2]; - DriveInfo *fd[2]; + BlockDriverState *fd[MAX_FD]; static pc_cmos_init_late_arg arg; /* various important CMOS locations needed by PC/Bochs bios */ @@ -382,14 +378,16 @@ void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size, } /* floppy type */ - for (i = 0; i < 2; i++) { - fd[i] = drive_get(IF_FLOPPY, 0, i); - if (fd[i] && bdrv_is_inserted(fd[i]->bdrv)) { - bdrv_get_floppy_geometry_hint(fd[i]->bdrv, &nb_heads, &max_track, - &last_sect, FDRIVE_DRV_NONE, - &fd_type[i]); - } else { - fd_type[i] = FDRIVE_DRV_NONE; + if (floppy) { + fdc_get_bs(fd, floppy); + for (i = 0; i < 2; i++) { + if (fd[i] && bdrv_is_inserted(fd[i])) { + bdrv_get_floppy_geometry_hint(fd[i], &nb_heads, &max_track, + &last_sect, FDRIVE_DRV_NONE, + &fd_type[i]); + } else { + fd_type[i] = FDRIVE_DRV_NONE; + } } } val = (cmos_get_fd_drive_type(fd_type[0]) << 4) | @@ -943,7 +941,6 @@ static CPUState *pc_new_cpu(const char *cpu_model) exit(1); } if ((env->cpuid_features & CPUID_APIC) || smp_cpus > 1) { - env->cpuid_apic_id = env->cpu_index; env->apic_state = apic_init(env, env->cpuid_apic_id); } qemu_register_reset(pc_cpu_reset, env); @@ -1126,8 +1123,9 @@ static void cpu_request_exit(void *opaque, int irq, int level) } } -void pc_basic_device_init(qemu_irq *isa_irq, +void pc_basic_device_init(qemu_irq *gsi, ISADevice **rtc_state, + ISADevice **floppy, bool no_vmport) { int i; @@ -1145,8 +1143,8 @@ void pc_basic_device_init(qemu_irq *isa_irq, DeviceState *hpet = sysbus_try_create_simple("hpet", HPET_BASE, NULL); if (hpet) { - for (i = 0; i < 24; i++) { - sysbus_connect_irq(sysbus_from_qdev(hpet), i, isa_irq[i]); + for (i = 0; i < GSI_NUM_PINS; i++) { + sysbus_connect_irq(sysbus_from_qdev(hpet), i, gsi[i]); } rtc_irq = qdev_get_gpio_in(hpet, 0); } @@ -1192,7 +1190,7 @@ void pc_basic_device_init(qemu_irq *isa_irq, for(i = 0; i < MAX_FD; i++) { fd[i] = drive_get(IF_FLOPPY, 0, i); } - fdctrl_init_isa(fd); + *floppy = fdctrl_init_isa(fd); } void pc_pci_device_init(PCIBus *pci_bus) diff --git a/hw/pc.h b/hw/pc.h index f3e21b6225..4515006381 100644 --- a/hw/pc.h +++ b/hw/pc.h @@ -8,6 +8,7 @@ #include "fdc.h" #include "net.h" #include "memory.h" +#include "ioapic.h" /* PC-style peripherals (also used by other machines). */ @@ -59,26 +60,24 @@ bool parallel_mm_init(target_phys_addr_t base, int it_shift, qemu_irq irq, /* i8259.c */ -typedef struct PicState2 PicState2; -extern PicState2 *isa_pic; -void pic_set_irq(int irq, int level); -void pic_set_irq_new(void *opaque, int irq, int level); +typedef struct PicState PicState; +extern PicState *isa_pic; qemu_irq *i8259_init(qemu_irq parent_irq); -int pic_read_irq(PicState2 *s); -void pic_update_irq(PicState2 *s); -uint32_t pic_intack_read(PicState2 *s); +int pic_read_irq(PicState *s); +int pic_get_output(PicState *s); void pic_info(Monitor *mon); void irq_info(Monitor *mon); -/* ISA */ -#define IOAPIC_NUM_PINS 0x18 +/* Global System Interrupts */ -typedef struct isa_irq_state { - qemu_irq *i8259; - qemu_irq ioapic[IOAPIC_NUM_PINS]; -} IsaIrqState; +#define GSI_NUM_PINS IOAPIC_NUM_PINS -void isa_irq_handler(void *opaque, int n, int level); +typedef struct GSIState { + qemu_irq i8259_irq[ISA_NUM_IRQS]; + qemu_irq ioapic_irq[IOAPIC_NUM_PINS]; +} GSIState; + +void gsi_handler(void *opaque, int n, int level); /* i8254.c */ @@ -141,13 +140,14 @@ void pc_memory_init(MemoryRegion *system_memory, MemoryRegion **ram_memory); qemu_irq *pc_allocate_cpu_irq(void); void pc_vga_init(PCIBus *pci_bus); -void pc_basic_device_init(qemu_irq *isa_irq, +void pc_basic_device_init(qemu_irq *gsi, ISADevice **rtc_state, + ISADevice **floppy, bool no_vmport); void pc_init_ne2k_isa(NICInfo *nd); void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size, const char *boot_device, - BusState *ide0, BusState *ide1, + ISADevice *floppy, BusState *ide0, BusState *ide1, ISADevice *s); void pc_pci_device_init(PCIBus *pci_bus); diff --git a/hw/pc_piix.c b/hw/pc_piix.c index ce1c87fba9..8c7f2b7337 100644 --- a/hw/pc_piix.c +++ b/hw/pc_piix.c @@ -53,7 +53,7 @@ static const int ide_iobase[MAX_IDE_BUS] = { 0x1f0, 0x170 }; static const int ide_iobase2[MAX_IDE_BUS] = { 0x3f6, 0x376 }; static const int ide_irq[MAX_IDE_BUS] = { 14, 15 }; -static void ioapic_init(IsaIrqState *isa_irq_state) +static void ioapic_init(GSIState *gsi_state) { DeviceState *dev; SysBusDevice *d; @@ -65,7 +65,7 @@ static void ioapic_init(IsaIrqState *isa_irq_state) sysbus_mmio_map(d, 0, 0xfec00000); for (i = 0; i < IOAPIC_NUM_PINS; i++) { - isa_irq_state->ioapic[i] = qdev_get_gpio_in(dev, i); + gsi_state->ioapic_irq[i] = qdev_get_gpio_in(dev, i); } } @@ -87,14 +87,15 @@ static void pc_init1(MemoryRegion *system_memory, PCII440FXState *i440fx_state; int piix3_devfn = -1; qemu_irq *cpu_irq; - qemu_irq *isa_irq; + qemu_irq *gsi; qemu_irq *i8259; qemu_irq *cmos_s3; qemu_irq *smi_irq; - IsaIrqState *isa_irq_state; + GSIState *gsi_state; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; BusState *idebus[MAX_IDE_BUS]; ISADevice *rtc_state; + ISADevice *floppy; MemoryRegion *ram_memory; MemoryRegion *pci_memory; MemoryRegion *rom_memory; @@ -130,11 +131,11 @@ static void pc_init1(MemoryRegion *system_memory, pci_enabled ? rom_memory : system_memory, &ram_memory); } - isa_irq_state = g_malloc0(sizeof(*isa_irq_state)); - isa_irq = qemu_allocate_irqs(isa_irq_handler, isa_irq_state, 24); + gsi_state = g_malloc0(sizeof(*gsi_state)); + gsi = qemu_allocate_irqs(gsi_handler, gsi_state, GSI_NUM_PINS); if (pci_enabled) { - pci_bus = i440fx_init(&i440fx_state, &piix3_devfn, isa_irq, + pci_bus = i440fx_init(&i440fx_state, &piix3_devfn, gsi, system_memory, system_io, ram_size, below_4g_mem_size, 0x100000000ULL - below_4g_mem_size, @@ -149,7 +150,7 @@ static void pc_init1(MemoryRegion *system_memory, isa_bus_new(NULL, system_io); no_hpet = 1; } - isa_bus_irqs(isa_irq); + isa_bus_irqs(gsi); if (!xen_enabled()) { cpu_irq = pc_allocate_cpu_irq(); @@ -158,12 +159,14 @@ static void pc_init1(MemoryRegion *system_memory, i8259 = xen_interrupt_controller_init(); } - isa_irq_state->i8259 = i8259; + for (i = 0; i < ISA_NUM_IRQS; i++) { + gsi_state->i8259_irq[i] = i8259[i]; + } if (pci_enabled) { - ioapic_init(isa_irq_state); + ioapic_init(gsi_state); } - pc_register_ferr_irq(isa_get_irq(13)); + pc_register_ferr_irq(gsi[13]); pc_vga_init(pci_enabled? pci_bus: NULL); @@ -172,7 +175,7 @@ static void pc_init1(MemoryRegion *system_memory, } /* init basic PC hardware */ - pc_basic_device_init(isa_irq, &rtc_state, xen_enabled()); + pc_basic_device_init(gsi, &rtc_state, &floppy, xen_enabled()); for(i = 0; i < nb_nics; i++) { NICInfo *nd = &nd_table[i]; @@ -202,10 +205,10 @@ static void pc_init1(MemoryRegion *system_memory, } } - audio_init(isa_irq, pci_enabled ? pci_bus : NULL); + audio_init(gsi, pci_enabled ? pci_bus : NULL); pc_cmos_init(below_4g_mem_size, above_4g_mem_size, boot_device, - idebus[0], idebus[1], rtc_state); + floppy, idebus[0], idebus[1], rtc_state); if (pci_enabled && usb_enabled) { usb_uhci_piix3_init(pci_bus, piix3_devfn + 2); @@ -222,7 +225,7 @@ static void pc_init1(MemoryRegion *system_memory, smi_irq = qemu_allocate_irqs(pc_acpi_smi_interrupt, first_cpu, 1); /* TODO: Populate SPD eeprom data. */ smbus = piix4_pm_init(pci_bus, piix3_devfn + 3, 0xb100, - isa_get_irq(9), *cmos_s3, *smi_irq, + gsi[9], *cmos_s3, *smi_irq, kvm_enabled()); smbus_eeprom_init(smbus, 8, NULL, 0); } diff --git a/hw/pci-stub.c b/hw/pci-stub.c index 1fb105d51c..636171c16f 100644 --- a/hw/pci-stub.c +++ b/hw/pci-stub.c @@ -21,20 +21,17 @@ #include "sysemu.h" #include "monitor.h" #include "pci.h" +#include "qmp-commands.h" -static void pci_error_message(Monitor *mon) +PciInfoList *qmp_query_pci(Error **errp) { - monitor_printf(mon, "PCI devices not supported\n"); + error_set(errp, QERR_UNSUPPORTED); + return NULL; } -void do_pci_info(Monitor *mon, QObject **ret_data) -{ - pci_error_message(mon); -} - -void do_pci_info_print(Monitor *mon, const QObject *data) +static void pci_error_message(Monitor *mon) { - pci_error_message(mon); + monitor_printf(mon, "PCI devices not supported\n"); } int do_pcie_aer_inejct_error(Monitor *mon, diff --git a/hw/pci.c b/hw/pci.c index 749e8d86ca..399227fc3d 100644 --- a/hw/pci.c +++ b/hw/pci.c @@ -29,8 +29,8 @@ #include "net.h" #include "sysemu.h" #include "loader.h" -#include "qemu-objects.h" #include "range.h" +#include "qmp-commands.h" //#define DEBUG_PCI #ifdef DEBUG_PCI @@ -998,17 +998,8 @@ static void pci_update_mappings(PCIDevice *d) } r->addr = new_addr; if (r->addr != PCI_BAR_UNMAPPED) { - if (r->type & PCI_BASE_ADDRESS_SPACE_IO) { - memory_region_add_subregion_overlap(r->address_space, - r->addr, - r->memory, - 1); - } else { - memory_region_add_subregion_overlap(r->address_space, - r->addr, - r->memory, - 1); - } + memory_region_add_subregion_overlap(r->address_space, + r->addr, r->memory, 1); } } } @@ -1173,276 +1164,194 @@ void pci_for_each_device(PCIBus *bus, int bus_num, } } -static void pci_device_print(Monitor *mon, QDict *device) +static const pci_class_desc *get_class_desc(int class) { - QDict *qdict; - QListEntry *entry; - uint64_t addr, size; - - monitor_printf(mon, " Bus %2" PRId64 ", ", qdict_get_int(device, "bus")); - monitor_printf(mon, "device %3" PRId64 ", function %" PRId64 ":\n", - qdict_get_int(device, "slot"), - qdict_get_int(device, "function")); - monitor_printf(mon, " "); - - qdict = qdict_get_qdict(device, "class_info"); - if (qdict_haskey(qdict, "desc")) { - monitor_printf(mon, "%s", qdict_get_str(qdict, "desc")); - } else { - monitor_printf(mon, "Class %04" PRId64, qdict_get_int(qdict, "class")); - } - - qdict = qdict_get_qdict(device, "id"); - monitor_printf(mon, ": PCI device %04" PRIx64 ":%04" PRIx64 "\n", - qdict_get_int(qdict, "device"), - qdict_get_int(qdict, "vendor")); + const pci_class_desc *desc; - if (qdict_haskey(device, "irq")) { - monitor_printf(mon, " IRQ %" PRId64 ".\n", - qdict_get_int(device, "irq")); + desc = pci_class_descriptions; + while (desc->desc && class != desc->class) { + desc++; } - if (qdict_haskey(device, "pci_bridge")) { - QDict *info; - - qdict = qdict_get_qdict(device, "pci_bridge"); - - info = qdict_get_qdict(qdict, "bus"); - monitor_printf(mon, " BUS %" PRId64 ".\n", - qdict_get_int(info, "number")); - monitor_printf(mon, " secondary bus %" PRId64 ".\n", - qdict_get_int(info, "secondary")); - monitor_printf(mon, " subordinate bus %" PRId64 ".\n", - qdict_get_int(info, "subordinate")); + return desc; +} - info = qdict_get_qdict(qdict, "io_range"); - monitor_printf(mon, " IO range [0x%04"PRIx64", 0x%04"PRIx64"]\n", - qdict_get_int(info, "base"), - qdict_get_int(info, "limit")); +static PciDeviceInfoList *qmp_query_pci_devices(PCIBus *bus, int bus_num); - info = qdict_get_qdict(qdict, "memory_range"); - monitor_printf(mon, - " memory range [0x%08"PRIx64", 0x%08"PRIx64"]\n", - qdict_get_int(info, "base"), - qdict_get_int(info, "limit")); +static PciMemoryRegionList *qmp_query_pci_regions(const PCIDevice *dev) +{ + PciMemoryRegionList *head = NULL, *cur_item = NULL; + int i; - info = qdict_get_qdict(qdict, "prefetchable_range"); - monitor_printf(mon, " prefetchable memory range " - "[0x%08"PRIx64", 0x%08"PRIx64"]\n", - qdict_get_int(info, "base"), - qdict_get_int(info, "limit")); - } + for (i = 0; i < PCI_NUM_REGIONS; i++) { + const PCIIORegion *r = &dev->io_regions[i]; + PciMemoryRegionList *region; - QLIST_FOREACH_ENTRY(qdict_get_qlist(device, "regions"), entry) { - qdict = qobject_to_qdict(qlist_entry_obj(entry)); - monitor_printf(mon, " BAR%d: ", (int) qdict_get_int(qdict, "bar")); + if (!r->size) { + continue; + } - addr = qdict_get_int(qdict, "address"); - size = qdict_get_int(qdict, "size"); + region = g_malloc0(sizeof(*region)); + region->value = g_malloc0(sizeof(*region->value)); - if (!strcmp(qdict_get_str(qdict, "type"), "io")) { - monitor_printf(mon, "I/O at 0x%04"FMT_PCIBUS - " [0x%04"FMT_PCIBUS"].\n", - addr, addr + size - 1); + if (r->type & PCI_BASE_ADDRESS_SPACE_IO) { + region->value->type = g_strdup("io"); } else { - monitor_printf(mon, "%d bit%s memory at 0x%08"FMT_PCIBUS - " [0x%08"FMT_PCIBUS"].\n", - qdict_get_bool(qdict, "mem_type_64") ? 64 : 32, - qdict_get_bool(qdict, "prefetch") ? - " prefetchable" : "", addr, addr + size - 1); + region->value->type = g_strdup("memory"); + region->value->has_prefetch = true; + region->value->prefetch = !!(r->type & PCI_BASE_ADDRESS_MEM_PREFETCH); + region->value->has_mem_type_64 = true; + region->value->mem_type_64 = !!(r->type & PCI_BASE_ADDRESS_MEM_TYPE_64); } - } - monitor_printf(mon, " id \"%s\"\n", qdict_get_str(device, "qdev_id")); + region->value->bar = i; + region->value->address = r->addr; + region->value->size = r->size; - if (qdict_haskey(device, "pci_bridge")) { - qdict = qdict_get_qdict(device, "pci_bridge"); - if (qdict_haskey(qdict, "devices")) { - QListEntry *dev; - QLIST_FOREACH_ENTRY(qdict_get_qlist(qdict, "devices"), dev) { - pci_device_print(mon, qobject_to_qdict(qlist_entry_obj(dev))); - } + /* XXX: waiting for the qapi to support GSList */ + if (!cur_item) { + head = cur_item = region; + } else { + cur_item->next = region; + cur_item = region; } } -} -void do_pci_info_print(Monitor *mon, const QObject *data) -{ - QListEntry *bus, *dev; - - QLIST_FOREACH_ENTRY(qobject_to_qlist(data), bus) { - QDict *qdict = qobject_to_qdict(qlist_entry_obj(bus)); - QLIST_FOREACH_ENTRY(qdict_get_qlist(qdict, "devices"), dev) { - pci_device_print(mon, qobject_to_qdict(qlist_entry_obj(dev))); - } - } + return head; } -static QObject *pci_get_dev_class(const PCIDevice *dev) +static PciBridgeInfo *qmp_query_pci_bridge(PCIDevice *dev, PCIBus *bus, + int bus_num) { - int class; - const pci_class_desc *desc; + PciBridgeInfo *info; - class = pci_get_word(dev->config + PCI_CLASS_DEVICE); - desc = pci_class_descriptions; - while (desc->desc && class != desc->class) - desc++; + info = g_malloc0(sizeof(*info)); - if (desc->desc) { - return qobject_from_jsonf("{ 'desc': %s, 'class': %d }", - desc->desc, class); - } else { - return qobject_from_jsonf("{ 'class': %d }", class); - } -} - -static QObject *pci_get_dev_id(const PCIDevice *dev) -{ - return qobject_from_jsonf("{ 'device': %d, 'vendor': %d }", - pci_get_word(dev->config + PCI_VENDOR_ID), - pci_get_word(dev->config + PCI_DEVICE_ID)); -} + info->bus.number = dev->config[PCI_PRIMARY_BUS]; + info->bus.secondary = dev->config[PCI_SECONDARY_BUS]; + info->bus.subordinate = dev->config[PCI_SUBORDINATE_BUS]; -static QObject *pci_get_regions_list(const PCIDevice *dev) -{ - int i; - QList *regions_list; + info->bus.io_range = g_malloc0(sizeof(*info->bus.io_range)); + info->bus.io_range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_IO); + info->bus.io_range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_IO); - regions_list = qlist_new(); + info->bus.memory_range = g_malloc0(sizeof(*info->bus.memory_range)); + info->bus.memory_range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY); + info->bus.memory_range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY); - for (i = 0; i < PCI_NUM_REGIONS; i++) { - QObject *obj; - const PCIIORegion *r = &dev->io_regions[i]; + info->bus.prefetchable_range = g_malloc0(sizeof(*info->bus.prefetchable_range)); + info->bus.prefetchable_range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); + info->bus.prefetchable_range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); - if (!r->size) { - continue; - } - - if (r->type & PCI_BASE_ADDRESS_SPACE_IO) { - obj = qobject_from_jsonf("{ 'bar': %d, 'type': 'io', " - "'address': %" PRId64 ", " - "'size': %" PRId64 " }", - i, r->addr, r->size); - } else { - int mem_type_64 = r->type & PCI_BASE_ADDRESS_MEM_TYPE_64; - - obj = qobject_from_jsonf("{ 'bar': %d, 'type': 'memory', " - "'mem_type_64': %i, 'prefetch': %i, " - "'address': %" PRId64 ", " - "'size': %" PRId64 " }", - i, mem_type_64, - r->type & PCI_BASE_ADDRESS_MEM_PREFETCH, - r->addr, r->size); + if (dev->config[PCI_SECONDARY_BUS] != 0) { + PCIBus *child_bus = pci_find_bus(bus, dev->config[PCI_SECONDARY_BUS]); + if (child_bus) { + info->has_devices = true; + info->devices = qmp_query_pci_devices(child_bus, dev->config[PCI_SECONDARY_BUS]); } - - qlist_append_obj(regions_list, obj); } - return QOBJECT(regions_list); + return info; } -static QObject *pci_get_devices_list(PCIBus *bus, int bus_num); - -static QObject *pci_get_dev_dict(PCIDevice *dev, PCIBus *bus, int bus_num) +static PciDeviceInfo *qmp_query_pci_device(PCIDevice *dev, PCIBus *bus, + int bus_num) { + const pci_class_desc *desc; + PciDeviceInfo *info; uint8_t type; - QObject *obj; + int class; - obj = qobject_from_jsonf("{ 'bus': %d, 'slot': %d, 'function': %d," "'class_info': %p, 'id': %p, 'regions': %p," - " 'qdev_id': %s }", - bus_num, - PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), - pci_get_dev_class(dev), pci_get_dev_id(dev), - pci_get_regions_list(dev), - dev->qdev.id ? dev->qdev.id : ""); + info = g_malloc0(sizeof(*info)); + info->bus = bus_num; + info->slot = PCI_SLOT(dev->devfn); + info->function = PCI_FUNC(dev->devfn); + + class = pci_get_word(dev->config + PCI_CLASS_DEVICE); + info->class_info.class = class; + desc = get_class_desc(class); + if (desc->desc) { + info->class_info.has_desc = true; + info->class_info.desc = g_strdup(desc->desc); + } + + info->id.vendor = pci_get_word(dev->config + PCI_VENDOR_ID); + info->id.device = pci_get_word(dev->config + PCI_DEVICE_ID); + info->regions = qmp_query_pci_regions(dev); + info->qdev_id = g_strdup(dev->qdev.id ? dev->qdev.id : ""); if (dev->config[PCI_INTERRUPT_PIN] != 0) { - QDict *qdict = qobject_to_qdict(obj); - qdict_put(qdict, "irq", qint_from_int(dev->config[PCI_INTERRUPT_LINE])); + info->has_irq = true; + info->irq = dev->config[PCI_INTERRUPT_LINE]; } type = dev->config[PCI_HEADER_TYPE] & ~PCI_HEADER_TYPE_MULTI_FUNCTION; if (type == PCI_HEADER_TYPE_BRIDGE) { - QDict *qdict; - QObject *pci_bridge; - - pci_bridge = qobject_from_jsonf("{ 'bus': " - "{ 'number': %d, 'secondary': %d, 'subordinate': %d }, " - "'io_range': { 'base': %" PRId64 ", 'limit': %" PRId64 "}, " - "'memory_range': { 'base': %" PRId64 ", 'limit': %" PRId64 "}, " - "'prefetchable_range': { 'base': %" PRId64 ", 'limit': %" PRId64 "} }", - dev->config[PCI_PRIMARY_BUS], dev->config[PCI_SECONDARY_BUS], - dev->config[PCI_SUBORDINATE_BUS], - pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_IO), - pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_IO), - pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY), - pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY), - pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY | - PCI_BASE_ADDRESS_MEM_PREFETCH), - pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY | - PCI_BASE_ADDRESS_MEM_PREFETCH)); - - if (dev->config[PCI_SECONDARY_BUS] != 0) { - PCIBus *child_bus = pci_find_bus(bus, dev->config[PCI_SECONDARY_BUS]); - - if (child_bus) { - qdict = qobject_to_qdict(pci_bridge); - qdict_put_obj(qdict, "devices", - pci_get_devices_list(child_bus, - dev->config[PCI_SECONDARY_BUS])); - } - } - qdict = qobject_to_qdict(obj); - qdict_put_obj(qdict, "pci_bridge", pci_bridge); + info->has_pci_bridge = true; + info->pci_bridge = qmp_query_pci_bridge(dev, bus, bus_num); } - return obj; + return info; } -static QObject *pci_get_devices_list(PCIBus *bus, int bus_num) +static PciDeviceInfoList *qmp_query_pci_devices(PCIBus *bus, int bus_num) { - int devfn; + PciDeviceInfoList *info, *head = NULL, *cur_item = NULL; PCIDevice *dev; - QList *dev_list; - - dev_list = qlist_new(); + int devfn; for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { dev = bus->devices[devfn]; if (dev) { - qlist_append_obj(dev_list, pci_get_dev_dict(dev, bus, bus_num)); + info = g_malloc0(sizeof(*info)); + info->value = qmp_query_pci_device(dev, bus, bus_num); + + /* XXX: waiting for the qapi to support GSList */ + if (!cur_item) { + head = cur_item = info; + } else { + cur_item->next = info; + cur_item = info; + } } } - return QOBJECT(dev_list); + return head; } -static QObject *pci_get_bus_dict(PCIBus *bus, int bus_num) +static PciInfo *qmp_query_pci_bus(PCIBus *bus, int bus_num) { + PciInfo *info = NULL; + bus = pci_find_bus(bus, bus_num); if (bus) { - return qobject_from_jsonf("{ 'bus': %d, 'devices': %p }", - bus_num, pci_get_devices_list(bus, bus_num)); + info = g_malloc0(sizeof(*info)); + info->bus = bus_num; + info->devices = qmp_query_pci_devices(bus, bus_num); } - return NULL; + return info; } -void do_pci_info(Monitor *mon, QObject **ret_data) +PciInfoList *qmp_query_pci(Error **errp) { - QList *bus_list; + PciInfoList *info, *head = NULL, *cur_item = NULL; struct PCIHostBus *host; - bus_list = qlist_new(); - QLIST_FOREACH(host, &host_buses, next) { - QObject *obj = pci_get_bus_dict(host->bus, 0); - if (obj) { - qlist_append_obj(bus_list, obj); + info = g_malloc0(sizeof(*info)); + info->value = qmp_query_pci_bus(host->bus, 0); + + /* XXX: waiting for the qapi to support GSList */ + if (!cur_item) { + head = cur_item = info; + } else { + cur_item->next = info; + cur_item = info; } } - *ret_data = QOBJECT(bus_list); + return head; } static const char * const pci_nic_models[] = { diff --git a/hw/pci.h b/hw/pci.h index 86a81c8273..4b2e7859e6 100644 --- a/hw/pci.h +++ b/hw/pci.h @@ -2,10 +2,10 @@ #define QEMU_PCI_H #include "qemu-common.h" -#include "qobject.h" #include "qdev.h" #include "memory.h" +#include "dma.h" /* PCI includes legacy ISA access. */ #include "isa.h" @@ -271,9 +271,6 @@ int pci_parse_devaddr(const char *addr, int *domp, int *busp, int pci_read_devaddr(Monitor *mon, const char *addr, int *domp, int *busp, unsigned *slotp); -void do_pci_info_print(Monitor *mon, const QObject *data); -void do_pci_info(Monitor *mon, QObject **ret_data); - void pci_device_deassert_intx(PCIDevice *dev); static inline void @@ -487,4 +484,70 @@ static inline uint32_t pci_config_size(const PCIDevice *d) return pci_is_express(d) ? PCIE_CONFIG_SPACE_SIZE : PCI_CONFIG_SPACE_SIZE; } +/* DMA access functions */ +static inline int pci_dma_rw(PCIDevice *dev, dma_addr_t addr, + void *buf, dma_addr_t len, DMADirection dir) +{ + cpu_physical_memory_rw(addr, buf, len, dir == DMA_DIRECTION_FROM_DEVICE); + return 0; +} + +static inline int pci_dma_read(PCIDevice *dev, dma_addr_t addr, + void *buf, dma_addr_t len) +{ + return pci_dma_rw(dev, addr, buf, len, DMA_DIRECTION_TO_DEVICE); +} + +static inline int pci_dma_write(PCIDevice *dev, dma_addr_t addr, + const void *buf, dma_addr_t len) +{ + return pci_dma_rw(dev, addr, (void *) buf, len, DMA_DIRECTION_FROM_DEVICE); +} + +#define PCI_DMA_DEFINE_LDST(_l, _s, _bits) \ + static inline uint##_bits##_t ld##_l##_pci_dma(PCIDevice *dev, \ + dma_addr_t addr) \ + { \ + return ld##_l##_phys(addr); \ + } \ + static inline void st##_s##_pci_dma(PCIDevice *dev, \ + dma_addr_t addr, uint##_bits##_t val) \ + { \ + st##_s##_phys(addr, val); \ + } + +PCI_DMA_DEFINE_LDST(ub, b, 8); +PCI_DMA_DEFINE_LDST(uw_le, w_le, 16) +PCI_DMA_DEFINE_LDST(l_le, l_le, 32); +PCI_DMA_DEFINE_LDST(q_le, q_le, 64); +PCI_DMA_DEFINE_LDST(uw_be, w_be, 16) +PCI_DMA_DEFINE_LDST(l_be, l_be, 32); +PCI_DMA_DEFINE_LDST(q_be, q_be, 64); + +#undef PCI_DMA_DEFINE_LDST + +static inline void *pci_dma_map(PCIDevice *dev, dma_addr_t addr, + dma_addr_t *plen, DMADirection dir) +{ + target_phys_addr_t len = *plen; + void *buf; + + buf = cpu_physical_memory_map(addr, &len, dir == DMA_DIRECTION_FROM_DEVICE); + *plen = len; + return buf; +} + +static inline void pci_dma_unmap(PCIDevice *dev, void *buffer, dma_addr_t len, + DMADirection dir, dma_addr_t access_len) +{ + cpu_physical_memory_unmap(buffer, len, dir == DMA_DIRECTION_FROM_DEVICE, + access_len); +} + +static inline void pci_dma_sglist_init(QEMUSGList *qsg, PCIDevice *dev, + int alloc_hint) +{ + qemu_sglist_init(qsg, alloc_hint); +} + #endif diff --git a/hw/pci_bridge.c b/hw/pci_bridge.c index b6287cdc6d..650d1650c5 100644 --- a/hw/pci_bridge.c +++ b/hw/pci_bridge.c @@ -319,7 +319,7 @@ int pci_bridge_initfn(PCIDevice *dev) sec_bus->parent_dev = dev; sec_bus->map_irq = br->map_irq; sec_bus->address_space_mem = &br->address_space_mem; - memory_region_init(&br->address_space_mem, "pci_pridge_pci", INT64_MAX); + memory_region_init(&br->address_space_mem, "pci_bridge_pci", INT64_MAX); sec_bus->address_space_io = &br->address_space_io; memory_region_init(&br->address_space_io, "pci_bridge_io", 65536); pci_bridge_region_init(br); diff --git a/hw/pcnet-pci.c b/hw/pcnet-pci.c index fb2a00caad..4e164da3ac 100644 --- a/hw/pcnet-pci.c +++ b/hw/pcnet-pci.c @@ -31,6 +31,7 @@ #include "net.h" #include "loader.h" #include "qemu-timer.h" +#include "dma.h" #include "pcnet.h" @@ -55,9 +56,9 @@ static void pcnet_aprom_writeb(void *opaque, uint32_t addr, uint32_t val) #ifdef PCNET_DEBUG printf("pcnet_aprom_writeb addr=0x%08x val=0x%02x\n", addr, val); #endif - /* Check APROMWE bit to enable write access */ - if (pcnet_bcr_readw(s,2) & 0x100) + if (BCR_APROMWE(s)) { s->prom[addr & 15] = val; + } } static uint32_t pcnet_aprom_readb(void *opaque, uint32_t addr) @@ -75,12 +76,24 @@ static uint64_t pcnet_ioport_read(void *opaque, target_phys_addr_t addr, { PCNetState *d = opaque; - if (addr < 16 && size == 1) { - return pcnet_aprom_readb(d, addr); - } else if (addr >= 0x10 && addr < 0x20 && size == 2) { - return pcnet_ioport_readw(d, addr); - } else if (addr >= 0x10 && addr < 0x20 && size == 4) { - return pcnet_ioport_readl(d, addr); + if (addr < 0x10) { + if (!BCR_DWIO(d) && size == 1) { + return pcnet_aprom_readb(d, addr); + } else if (!BCR_DWIO(d) && (addr & 1) == 0 && size == 2) { + return pcnet_aprom_readb(d, addr) | + (pcnet_aprom_readb(d, addr + 1) << 8); + } else if (BCR_DWIO(d) && (addr & 3) == 0 && size == 4) { + return pcnet_aprom_readb(d, addr) | + (pcnet_aprom_readb(d, addr + 1) << 8) | + (pcnet_aprom_readb(d, addr + 2) << 16) | + (pcnet_aprom_readb(d, addr + 3) << 24); + } + } else { + if (size == 2) { + return pcnet_ioport_readw(d, addr); + } else if (size == 4) { + return pcnet_ioport_readl(d, addr); + } } return ((uint64_t)1 << (size * 8)) - 1; } @@ -90,12 +103,24 @@ static void pcnet_ioport_write(void *opaque, target_phys_addr_t addr, { PCNetState *d = opaque; - if (addr < 16 && size == 1) { - return pcnet_aprom_writeb(d, addr, data); - } else if (addr >= 0x10 && addr < 0x20 && size == 2) { - return pcnet_ioport_writew(d, addr, data); - } else if (addr >= 0x10 && addr < 0x20 && size == 4) { - return pcnet_ioport_writel(d, addr, data); + if (addr < 0x10) { + if (!BCR_DWIO(d) && size == 1) { + pcnet_aprom_writeb(d, addr, data); + } else if (!BCR_DWIO(d) && (addr & 1) == 0 && size == 2) { + pcnet_aprom_writeb(d, addr, data & 0xff); + pcnet_aprom_writeb(d, addr + 1, data >> 8); + } else if (BCR_DWIO(d) && (addr & 3) == 0 && size == 4) { + pcnet_aprom_writeb(d, addr, data & 0xff); + pcnet_aprom_writeb(d, addr + 1, (data >> 8) & 0xff); + pcnet_aprom_writeb(d, addr + 2, (data >> 16) & 0xff); + pcnet_aprom_writeb(d, addr + 3, data >> 24); + } + } else { + if (size == 2) { + pcnet_ioport_writew(d, addr, data); + } else if (size == 4) { + pcnet_ioport_writel(d, addr, data); + } } } @@ -230,13 +255,13 @@ static const MemoryRegionOps pcnet_mmio_ops = { static void pci_physical_memory_write(void *dma_opaque, target_phys_addr_t addr, uint8_t *buf, int len, int do_bswap) { - cpu_physical_memory_write(addr, buf, len); + pci_dma_write(dma_opaque, addr, buf, len); } static void pci_physical_memory_read(void *dma_opaque, target_phys_addr_t addr, uint8_t *buf, int len, int do_bswap) { - cpu_physical_memory_read(addr, buf, len); + pci_dma_read(dma_opaque, addr, buf, len); } static void pci_pcnet_cleanup(VLANClientState *nc) @@ -263,6 +288,7 @@ static NetClientInfo net_pci_pcnet_info = { .size = sizeof(NICState), .can_receive = pcnet_can_receive, .receive = pcnet_receive, + .link_status_changed = pcnet_set_link_status, .cleanup = pci_pcnet_cleanup, }; @@ -302,6 +328,7 @@ static int pci_pcnet_init(PCIDevice *pci_dev) s->irq = pci_dev->irq[0]; s->phys_mem_read = pci_physical_memory_read; s->phys_mem_write = pci_physical_memory_write; + s->dma_opaque = pci_dev; if (!pci_dev->qdev.hotplugged) { static int loaded = 0; diff --git a/hw/pcnet.c b/hw/pcnet.c index cf16fd4d01..cba253ba7b 100644 --- a/hw/pcnet.c +++ b/hw/pcnet.c @@ -58,24 +58,6 @@ struct qemu_ether_header { uint16_t ether_type; }; -/* BUS CONFIGURATION REGISTERS */ -#define BCR_MSRDA 0 -#define BCR_MSWRA 1 -#define BCR_MC 2 -#define BCR_LNKST 4 -#define BCR_LED1 5 -#define BCR_LED2 6 -#define BCR_LED3 7 -#define BCR_FDC 9 -#define BCR_BSBC 18 -#define BCR_EECAS 19 -#define BCR_SWS 20 -#define BCR_PLAT 22 - -#define BCR_DWIO(S) !!((S)->bcr[BCR_BSBC] & 0x0080) -#define BCR_SSIZE32(S) !!((S)->bcr[BCR_SWS ] & 0x0100) -#define BCR_SWSTYLE(S) ((S)->bcr[BCR_SWS ] & 0x00FF) - #define CSR_INIT(S) !!(((S)->csr[0])&0x0001) #define CSR_STRT(S) !!(((S)->csr[0])&0x0002) #define CSR_STOP(S) !!(((S)->csr[0])&0x0004) @@ -1215,6 +1197,13 @@ ssize_t pcnet_receive(VLANClientState *nc, const uint8_t *buf, size_t size_) return size_; } +void pcnet_set_link_status(VLANClientState *nc) +{ + PCNetState *d = DO_UPCAST(NICState, nc, nc)->opaque; + + d->lnkst = nc->link_down ? 0 : 0x40; +} + static void pcnet_transmit(PCNetState *s) { target_phys_addr_t xmit_cxda = 0; diff --git a/hw/pcnet.h b/hw/pcnet.h index cd86bde9d0..edc81c90ac 100644 --- a/hw/pcnet.h +++ b/hw/pcnet.h @@ -6,6 +6,25 @@ #include "memory.h" +/* BUS CONFIGURATION REGISTERS */ +#define BCR_MSRDA 0 +#define BCR_MSWRA 1 +#define BCR_MC 2 +#define BCR_LNKST 4 +#define BCR_LED1 5 +#define BCR_LED2 6 +#define BCR_LED3 7 +#define BCR_FDC 9 +#define BCR_BSBC 18 +#define BCR_EECAS 19 +#define BCR_SWS 20 +#define BCR_PLAT 22 + +#define BCR_APROMWE(S) !!((S)->bcr[BCR_MC ] & 0x0100) +#define BCR_DWIO(S) !!((S)->bcr[BCR_BSBC] & 0x0080) +#define BCR_SSIZE32(S) !!((S)->bcr[BCR_SWS ] & 0x0100) +#define BCR_SWSTYLE(S) ((S)->bcr[BCR_SWS ] & 0x00FF) + typedef struct PCNetState_st PCNetState; struct PCNetState_st { @@ -39,6 +58,7 @@ uint32_t pcnet_ioport_readl(void *opaque, uint32_t addr); uint32_t pcnet_bcr_readw(PCNetState *s, uint32_t rap); int pcnet_can_receive(VLANClientState *nc); ssize_t pcnet_receive(VLANClientState *nc, const uint8_t *buf, size_t size_); +void pcnet_set_link_status(VLANClientState *nc); void pcnet_common_cleanup(PCNetState *d); int pcnet_common_init(DeviceState *dev, PCNetState *s, NetClientInfo *info); extern const VMStateDescription vmstate_pcnet; diff --git a/hw/pl041.c b/hw/pl041.c new file mode 100644 index 0000000000..efd52ac42f --- /dev/null +++ b/hw/pl041.c @@ -0,0 +1,636 @@ +/* + * Arm PrimeCell PL041 Advanced Audio Codec Interface + * + * Copyright (c) 2011 + * Written by Mathieu Sonet - www.elasticsheep.com + * + * This code is licenced under the GPL. + * + * ***************************************************************** + * + * This driver emulates the ARM AACI interface + * connected to a LM4549 codec. + * + * Limitations: + * - Supports only a playback on one channel (Versatile/Vexpress) + * - Supports only one TX FIFO in compact-mode or non-compact mode. + * - Supports playback of 12, 16, 18 and 20 bits samples. + * - Record is not supported. + * - The PL041 is hardwired to a LM4549 codec. + * + */ + +#include "sysbus.h" + +#include "pl041.h" +#include "lm4549.h" + +#if 0 +#define PL041_DEBUG_LEVEL 1 +#endif + +#if defined(PL041_DEBUG_LEVEL) && (PL041_DEBUG_LEVEL >= 1) +#define DBG_L1(fmt, ...) \ +do { printf("pl041: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DBG_L1(fmt, ...) \ +do { } while (0) +#endif + +#if defined(PL041_DEBUG_LEVEL) && (PL041_DEBUG_LEVEL >= 2) +#define DBG_L2(fmt, ...) \ +do { printf("pl041: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DBG_L2(fmt, ...) \ +do { } while (0) +#endif + + +#define MAX_FIFO_DEPTH (1024) +#define DEFAULT_FIFO_DEPTH (8) + +#define SLOT1_RW (1 << 19) + +/* This FIFO only stores 20-bit samples on 32-bit words. + So its level is independent of the selected mode */ +typedef struct { + uint32_t level; + uint32_t data[MAX_FIFO_DEPTH]; +} pl041_fifo; + +typedef struct { + pl041_fifo tx_fifo; + uint8_t tx_enabled; + uint8_t tx_compact_mode; + uint8_t tx_sample_size; + + pl041_fifo rx_fifo; + uint8_t rx_enabled; + uint8_t rx_compact_mode; + uint8_t rx_sample_size; +} pl041_channel; + +typedef struct { + SysBusDevice busdev; + MemoryRegion iomem; + qemu_irq irq; + + uint32_t fifo_depth; /* FIFO depth in non-compact mode */ + + pl041_regfile regs; + pl041_channel fifo1; + lm4549_state codec; +} pl041_state; + + +static const unsigned char pl041_default_id[8] = { + 0x41, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 +}; + +#if defined(PL041_DEBUG_LEVEL) +#define REGISTER(name, offset) #name, +static const char *pl041_regs_name[] = { + #include "pl041.hx" +}; +#undef REGISTER +#endif + + +#if defined(PL041_DEBUG_LEVEL) +static const char *get_reg_name(target_phys_addr_t offset) +{ + if (offset <= PL041_dr1_7) { + return pl041_regs_name[offset >> 2]; + } + + return "unknown"; +} +#endif + +static uint8_t pl041_compute_periphid3(pl041_state *s) +{ + uint8_t id3 = 1; /* One channel */ + + /* Add the fifo depth information */ + switch (s->fifo_depth) { + case 8: + id3 |= 0 << 3; + break; + case 32: + id3 |= 1 << 3; + break; + case 64: + id3 |= 2 << 3; + break; + case 128: + id3 |= 3 << 3; + break; + case 256: + id3 |= 4 << 3; + break; + case 512: + id3 |= 5 << 3; + break; + case 1024: + id3 |= 6 << 3; + break; + case 2048: + id3 |= 7 << 3; + break; + } + + return id3; +} + +static void pl041_reset(pl041_state *s) +{ + DBG_L1("pl041_reset\n"); + + memset(&s->regs, 0x00, sizeof(pl041_regfile)); + + s->regs.slfr = SL1TXEMPTY | SL2TXEMPTY | SL12TXEMPTY; + s->regs.sr1 = TXFE | RXFE | TXHE; + s->regs.isr1 = 0; + + memset(&s->fifo1, 0x00, sizeof(s->fifo1)); +} + + +static void pl041_fifo1_write(pl041_state *s, uint32_t value) +{ + pl041_channel *channel = &s->fifo1; + pl041_fifo *fifo = &s->fifo1.tx_fifo; + + /* Push the value in the FIFO */ + if (channel->tx_compact_mode == 0) { + /* Non-compact mode */ + + if (fifo->level < s->fifo_depth) { + /* Pad the value with 0 to obtain a 20-bit sample */ + switch (channel->tx_sample_size) { + case 12: + value = (value << 8) & 0xFFFFF; + break; + case 16: + value = (value << 4) & 0xFFFFF; + break; + case 18: + value = (value << 2) & 0xFFFFF; + break; + case 20: + default: + break; + } + + /* Store the sample in the FIFO */ + fifo->data[fifo->level++] = value; + } +#if defined(PL041_DEBUG_LEVEL) + else { + DBG_L1("fifo1 write: overrun\n"); + } +#endif + } else { + /* Compact mode */ + + if ((fifo->level + 2) < s->fifo_depth) { + uint32_t i = 0; + uint32_t sample = 0; + + for (i = 0; i < 2; i++) { + sample = value & 0xFFFF; + value = value >> 16; + + /* Pad each sample with 0 to obtain a 20-bit sample */ + switch (channel->tx_sample_size) { + case 12: + sample = sample << 8; + break; + case 16: + default: + sample = sample << 4; + break; + } + + /* Store the sample in the FIFO */ + fifo->data[fifo->level++] = sample; + } + } +#if defined(PL041_DEBUG_LEVEL) + else { + DBG_L1("fifo1 write: overrun\n"); + } +#endif + } + + /* Update the status register */ + if (fifo->level > 0) { + s->regs.sr1 &= ~(TXUNDERRUN | TXFE); + } + + if (fifo->level >= (s->fifo_depth / 2)) { + s->regs.sr1 &= ~TXHE; + } + + if (fifo->level >= s->fifo_depth) { + s->regs.sr1 |= TXFF; + } + + DBG_L2("fifo1_push sr1 = 0x%08x\n", s->regs.sr1); +} + +static void pl041_fifo1_transmit(pl041_state *s) +{ + pl041_channel *channel = &s->fifo1; + pl041_fifo *fifo = &s->fifo1.tx_fifo; + uint32_t slots = s->regs.txcr1 & TXSLOT_MASK; + uint32_t written_samples; + + /* Check if FIFO1 transmit is enabled */ + if ((channel->tx_enabled) && (slots & (TXSLOT3 | TXSLOT4))) { + if (fifo->level >= (s->fifo_depth / 2)) { + int i; + + DBG_L1("Transfer FIFO level = %i\n", fifo->level); + + /* Try to transfer the whole FIFO */ + for (i = 0; i < (fifo->level / 2); i++) { + uint32_t left = fifo->data[i * 2]; + uint32_t right = fifo->data[i * 2 + 1]; + + /* Transmit two 20-bit samples to the codec */ + if (lm4549_write_samples(&s->codec, left, right) == 0) { + DBG_L1("Codec buffer full\n"); + break; + } + } + + written_samples = i * 2; + if (written_samples > 0) { + /* Update the FIFO level */ + fifo->level -= written_samples; + + /* Move back the pending samples to the start of the FIFO */ + for (i = 0; i < fifo->level; i++) { + fifo->data[i] = fifo->data[written_samples + i]; + } + + /* Update the status register */ + s->regs.sr1 &= ~TXFF; + + if (fifo->level <= (s->fifo_depth / 2)) { + s->regs.sr1 |= TXHE; + } + + if (fifo->level == 0) { + s->regs.sr1 |= TXFE | TXUNDERRUN; + DBG_L1("Empty FIFO\n"); + } + } + } + } +} + +static void pl041_isr1_update(pl041_state *s) +{ + /* Update ISR1 */ + if (s->regs.sr1 & TXUNDERRUN) { + s->regs.isr1 |= URINTR; + } else { + s->regs.isr1 &= ~URINTR; + } + + if (s->regs.sr1 & TXHE) { + s->regs.isr1 |= TXINTR; + } else { + s->regs.isr1 &= ~TXINTR; + } + + if (!(s->regs.sr1 & TXBUSY) && (s->regs.sr1 & TXFE)) { + s->regs.isr1 |= TXCINTR; + } else { + s->regs.isr1 &= ~TXCINTR; + } + + /* Update the irq state */ + qemu_set_irq(s->irq, ((s->regs.isr1 & s->regs.ie1) > 0) ? 1 : 0); + DBG_L2("Set interrupt sr1 = 0x%08x isr1 = 0x%08x masked = 0x%08x\n", + s->regs.sr1, s->regs.isr1, s->regs.isr1 & s->regs.ie1); +} + +static void pl041_request_data(void *opaque) +{ + pl041_state *s = (pl041_state *)opaque; + + /* Trigger pending transfers */ + pl041_fifo1_transmit(s); + pl041_isr1_update(s); +} + +static uint64_t pl041_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + pl041_state *s = (pl041_state *)opaque; + int value; + + if ((offset >= PL041_periphid0) && (offset <= PL041_pcellid3)) { + if (offset == PL041_periphid3) { + value = pl041_compute_periphid3(s); + } else { + value = pl041_default_id[(offset - PL041_periphid0) >> 2]; + } + + DBG_L1("pl041_read [0x%08x] => 0x%08x\n", offset, value); + return value; + } else if (offset <= PL041_dr4_7) { + value = *((uint32_t *)&s->regs + (offset >> 2)); + } else { + DBG_L1("pl041_read: Reserved offset %x\n", (int)offset); + return 0; + } + + switch (offset) { + case PL041_allints: + value = s->regs.isr1 & 0x7F; + break; + } + + DBG_L1("pl041_read [0x%08x] %s => 0x%08x\n", offset, + get_reg_name(offset), value); + + return value; +} + +static void pl041_write(void *opaque, target_phys_addr_t offset, + uint64_t value, unsigned size) +{ + pl041_state *s = (pl041_state *)opaque; + uint16_t control, data; + uint32_t result; + + DBG_L1("pl041_write [0x%08x] %s <= 0x%08x\n", offset, + get_reg_name(offset), (unsigned int)value); + + /* Write the register */ + if (offset <= PL041_dr4_7) { + *((uint32_t *)&s->regs + (offset >> 2)) = value; + } else { + DBG_L1("pl041_write: Reserved offset %x\n", (int)offset); + return; + } + + /* Execute the actions */ + switch (offset) { + case PL041_txcr1: + { + pl041_channel *channel = &s->fifo1; + + uint32_t txen = s->regs.txcr1 & TXEN; + uint32_t tsize = (s->regs.txcr1 & TSIZE_MASK) >> TSIZE_MASK_BIT; + uint32_t compact_mode = (s->regs.txcr1 & TXCOMPACT) ? 1 : 0; +#if defined(PL041_DEBUG_LEVEL) + uint32_t slots = (s->regs.txcr1 & TXSLOT_MASK) >> TXSLOT_MASK_BIT; + uint32_t txfen = (s->regs.txcr1 & TXFEN) > 0 ? 1 : 0; +#endif + + DBG_L1("=> txen = %i slots = 0x%01x tsize = %i compact = %i " + "txfen = %i\n", txen, slots, tsize, compact_mode, txfen); + + channel->tx_enabled = txen; + channel->tx_compact_mode = compact_mode; + + switch (tsize) { + case 0: + channel->tx_sample_size = 16; + break; + case 1: + channel->tx_sample_size = 18; + break; + case 2: + channel->tx_sample_size = 20; + break; + case 3: + channel->tx_sample_size = 12; + break; + } + + DBG_L1("TX enabled = %i\n", channel->tx_enabled); + DBG_L1("TX compact mode = %i\n", channel->tx_compact_mode); + DBG_L1("TX sample width = %i\n", channel->tx_sample_size); + + /* Check if compact mode is allowed with selected tsize */ + if (channel->tx_compact_mode == 1) { + if ((channel->tx_sample_size == 18) || + (channel->tx_sample_size == 20)) { + channel->tx_compact_mode = 0; + DBG_L1("Compact mode not allowed with 18/20-bit sample size\n"); + } + } + + break; + } + case PL041_sl1tx: + s->regs.slfr &= ~SL1TXEMPTY; + + control = (s->regs.sl1tx >> 12) & 0x7F; + data = (s->regs.sl2tx >> 4) & 0xFFFF; + + if ((s->regs.sl1tx & SLOT1_RW) == 0) { + /* Write operation */ + lm4549_write(&s->codec, control, data); + } else { + /* Read operation */ + result = lm4549_read(&s->codec, control); + + /* Store the returned value */ + s->regs.sl1rx = s->regs.sl1tx & ~SLOT1_RW; + s->regs.sl2rx = result << 4; + + s->regs.slfr &= ~(SL1RXBUSY | SL2RXBUSY); + s->regs.slfr |= SL1RXVALID | SL2RXVALID; + } + break; + + case PL041_sl2tx: + s->regs.sl2tx = value; + s->regs.slfr &= ~SL2TXEMPTY; + break; + + case PL041_intclr: + DBG_L1("=> Clear interrupt intclr = 0x%08x isr1 = 0x%08x\n", + s->regs.intclr, s->regs.isr1); + + if (s->regs.intclr & TXUEC1) { + s->regs.sr1 &= ~TXUNDERRUN; + } + break; + + case PL041_maincr: + { +#if defined(PL041_DEBUG_LEVEL) + char debug[] = " AACIFE SL1RXEN SL1TXEN"; + if (!(value & AACIFE)) { + debug[0] = '!'; + } + if (!(value & SL1RXEN)) { + debug[8] = '!'; + } + if (!(value & SL1TXEN)) { + debug[17] = '!'; + } + DBG_L1("%s\n", debug); +#endif + + if ((s->regs.maincr & AACIFE) == 0) { + pl041_reset(s); + } + break; + } + + case PL041_dr1_0: + case PL041_dr1_1: + case PL041_dr1_2: + case PL041_dr1_3: + pl041_fifo1_write(s, value); + break; + } + + /* Transmit the FIFO content */ + pl041_fifo1_transmit(s); + + /* Update the ISR1 register */ + pl041_isr1_update(s); +} + +static void pl041_device_reset(DeviceState *d) +{ + pl041_state *s = DO_UPCAST(pl041_state, busdev.qdev, d); + + pl041_reset(s); +} + +static const MemoryRegionOps pl041_ops = { + .read = pl041_read, + .write = pl041_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static int pl041_init(SysBusDevice *dev) +{ + pl041_state *s = FROM_SYSBUS(pl041_state, dev); + + DBG_L1("pl041_init 0x%08x\n", (uint32_t)s); + + /* Check the device properties */ + switch (s->fifo_depth) { + case 8: + case 32: + case 64: + case 128: + case 256: + case 512: + case 1024: + case 2048: + break; + case 16: + default: + /* NC FIFO depth of 16 is not allowed because its id bits in + AACIPERIPHID3 overlap with the id for the default NC FIFO depth */ + fprintf(stderr, "pl041: unsupported non-compact fifo depth [%i]\n", + s->fifo_depth); + return -1; + } + + /* Connect the device to the sysbus */ + memory_region_init_io(&s->iomem, &pl041_ops, s, "pl041", 0x1000); + sysbus_init_mmio_region(dev, &s->iomem); + sysbus_init_irq(dev, &s->irq); + + /* Init the codec */ + lm4549_init(&s->codec, &pl041_request_data, (void *)s); + + return 0; +} + +static const VMStateDescription vmstate_pl041_regfile = { + .name = "pl041_regfile", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { +#define REGISTER(name, offset) VMSTATE_UINT32(name, pl041_regfile), + #include "pl041.hx" +#undef REGISTER + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_pl041_fifo = { + .name = "pl041_fifo", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(level, pl041_fifo), + VMSTATE_UINT32_ARRAY(data, pl041_fifo, MAX_FIFO_DEPTH), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_pl041_channel = { + .name = "pl041_channel", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(tx_fifo, pl041_channel, 0, + vmstate_pl041_fifo, pl041_fifo), + VMSTATE_UINT8(tx_enabled, pl041_channel), + VMSTATE_UINT8(tx_compact_mode, pl041_channel), + VMSTATE_UINT8(tx_sample_size, pl041_channel), + VMSTATE_STRUCT(rx_fifo, pl041_channel, 0, + vmstate_pl041_fifo, pl041_fifo), + VMSTATE_UINT8(rx_enabled, pl041_channel), + VMSTATE_UINT8(rx_compact_mode, pl041_channel), + VMSTATE_UINT8(rx_sample_size, pl041_channel), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_pl041 = { + .name = "pl041", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(fifo_depth, pl041_state), + VMSTATE_STRUCT(regs, pl041_state, 0, + vmstate_pl041_regfile, pl041_regfile), + VMSTATE_STRUCT(fifo1, pl041_state, 0, + vmstate_pl041_channel, pl041_channel), + VMSTATE_STRUCT(codec, pl041_state, 0, + vmstate_lm4549_state, lm4549_state), + VMSTATE_END_OF_LIST() + } +}; + +static SysBusDeviceInfo pl041_device_info = { + .init = pl041_init, + .qdev.name = "pl041", + .qdev.size = sizeof(pl041_state), + .qdev.vmsd = &vmstate_pl041, + .qdev.reset = pl041_device_reset, + .qdev.no_user = 1, + .qdev.props = (Property[]) { + /* Non-compact FIFO depth property */ + DEFINE_PROP_UINT32("nc_fifo_depth", pl041_state, + fifo_depth, DEFAULT_FIFO_DEPTH), + DEFINE_PROP_END_OF_LIST(), + }, +}; + +static void pl041_register_device(void) +{ + sysbus_register_withprop(&pl041_device_info); +} + +device_init(pl041_register_device) diff --git a/hw/pl041.h b/hw/pl041.h new file mode 100644 index 0000000000..1f224326e5 --- /dev/null +++ b/hw/pl041.h @@ -0,0 +1,135 @@ +/* + * Arm PrimeCell PL041 Advanced Audio Codec Interface + * + * Copyright (c) 2011 + * Written by Mathieu Sonet - www.elasticsheep.com + * + * This code is licenced under the GPL. + * + * ***************************************************************** + */ + +#ifndef HW_PL041_H +#define HW_PL041_H + +/* Register file */ +#define REGISTER(name, offset) uint32_t name; +typedef struct { + #include "pl041.hx" +} pl041_regfile; +#undef REGISTER + +/* Register addresses */ +#define REGISTER(name, offset) PL041_##name = offset, +enum { + #include "pl041.hx" + + PL041_periphid0 = 0xFE0, + PL041_periphid1 = 0xFE4, + PL041_periphid2 = 0xFE8, + PL041_periphid3 = 0xFEC, + PL041_pcellid0 = 0xFF0, + PL041_pcellid1 = 0xFF4, + PL041_pcellid2 = 0xFF8, + PL041_pcellid3 = 0xFFC, +}; +#undef REGISTER + +/* Register bits */ + +/* IEx */ +#define TXCIE (1 << 0) +#define RXTIE (1 << 1) +#define TXIE (1 << 2) +#define RXIE (1 << 3) +#define RXOIE (1 << 4) +#define TXUIE (1 << 5) +#define RXTOIE (1 << 6) + +/* TXCRx */ +#define TXEN (1 << 0) +#define TXSLOT1 (1 << 1) +#define TXSLOT2 (1 << 2) +#define TXSLOT3 (1 << 3) +#define TXSLOT4 (1 << 4) +#define TXCOMPACT (1 << 15) +#define TXFEN (1 << 16) + +#define TXSLOT_MASK_BIT (1) +#define TXSLOT_MASK (0xFFF << TXSLOT_MASK_BIT) + +#define TSIZE_MASK_BIT (13) +#define TSIZE_MASK (0x3 << TSIZE_MASK_BIT) + +#define TSIZE_16BITS (0x0 << TSIZE_MASK_BIT) +#define TSIZE_18BITS (0x1 << TSIZE_MASK_BIT) +#define TSIZE_20BITS (0x2 << TSIZE_MASK_BIT) +#define TSIZE_12BITS (0x3 << TSIZE_MASK_BIT) + +/* SRx */ +#define RXFE (1 << 0) +#define TXFE (1 << 1) +#define RXHF (1 << 2) +#define TXHE (1 << 3) +#define RXFF (1 << 4) +#define TXFF (1 << 5) +#define RXBUSY (1 << 6) +#define TXBUSY (1 << 7) +#define RXOVERRUN (1 << 8) +#define TXUNDERRUN (1 << 9) +#define RXTIMEOUT (1 << 10) +#define RXTOFE (1 << 11) + +/* ISRx */ +#define TXCINTR (1 << 0) +#define RXTOINTR (1 << 1) +#define TXINTR (1 << 2) +#define RXINTR (1 << 3) +#define ORINTR (1 << 4) +#define URINTR (1 << 5) +#define RXTOFEINTR (1 << 6) + +/* SLFR */ +#define SL1RXBUSY (1 << 0) +#define SL1TXBUSY (1 << 1) +#define SL2RXBUSY (1 << 2) +#define SL2TXBUSY (1 << 3) +#define SL12RXBUSY (1 << 4) +#define SL12TXBUSY (1 << 5) +#define SL1RXVALID (1 << 6) +#define SL1TXEMPTY (1 << 7) +#define SL2RXVALID (1 << 8) +#define SL2TXEMPTY (1 << 9) +#define SL12RXVALID (1 << 10) +#define SL12TXEMPTY (1 << 11) +#define RAWGPIOINT (1 << 12) +#define RWIS (1 << 13) + +/* MAINCR */ +#define AACIFE (1 << 0) +#define LOOPBACK (1 << 1) +#define LOWPOWER (1 << 2) +#define SL1RXEN (1 << 3) +#define SL1TXEN (1 << 4) +#define SL2RXEN (1 << 5) +#define SL2TXEN (1 << 6) +#define SL12RXEN (1 << 7) +#define SL12TXEN (1 << 8) +#define DMAENABLE (1 << 9) + +/* INTCLR */ +#define WISC (1 << 0) +#define RXOEC1 (1 << 1) +#define RXOEC2 (1 << 2) +#define RXOEC3 (1 << 3) +#define RXOEC4 (1 << 4) +#define TXUEC1 (1 << 5) +#define TXUEC2 (1 << 6) +#define TXUEC3 (1 << 7) +#define TXUEC4 (1 << 8) +#define RXTOFEC1 (1 << 9) +#define RXTOFEC2 (1 << 10) +#define RXTOFEC3 (1 << 11) +#define RXTOFEC4 (1 << 12) + +#endif /* #ifndef HW_PL041_H */ diff --git a/hw/pl041.hx b/hw/pl041.hx new file mode 100644 index 0000000000..e972996725 --- /dev/null +++ b/hw/pl041.hx @@ -0,0 +1,81 @@ +/* + * Arm PrimeCell PL041 Advanced Audio Codec Interface + * + * Copyright (c) 2011 + * Written by Mathieu Sonet - www.elasticsheep.com + * + * This code is licenced under the GPL. + * + * ***************************************************************** + */ + +/* PL041 register file description */ + +REGISTER( rxcr1, 0x00 ) +REGISTER( txcr1, 0x04 ) +REGISTER( sr1, 0x08 ) +REGISTER( isr1, 0x0C ) +REGISTER( ie1, 0x10 ) +REGISTER( rxcr2, 0x14 ) +REGISTER( txcr2, 0x18 ) +REGISTER( sr2, 0x1C ) +REGISTER( isr2, 0x20 ) +REGISTER( ie2, 0x24 ) +REGISTER( rxcr3, 0x28 ) +REGISTER( txcr3, 0x2C ) +REGISTER( sr3, 0x30 ) +REGISTER( isr3, 0x34 ) +REGISTER( ie3, 0x38 ) +REGISTER( rxcr4, 0x3C ) +REGISTER( txcr4, 0x40 ) +REGISTER( sr4, 0x44 ) +REGISTER( isr4, 0x48 ) +REGISTER( ie4, 0x4C ) +REGISTER( sl1rx, 0x50 ) +REGISTER( sl1tx, 0x54 ) +REGISTER( sl2rx, 0x58 ) +REGISTER( sl2tx, 0x5C ) +REGISTER( sl12rx, 0x60 ) +REGISTER( sl12tx, 0x64 ) +REGISTER( slfr, 0x68 ) +REGISTER( slistat, 0x6C ) +REGISTER( slien, 0x70 ) +REGISTER( intclr, 0x74 ) +REGISTER( maincr, 0x78 ) +REGISTER( reset, 0x7C ) +REGISTER( sync, 0x80 ) +REGISTER( allints, 0x84 ) +REGISTER( mainfr, 0x88 ) +REGISTER( unused, 0x8C ) +REGISTER( dr1_0, 0x90 ) +REGISTER( dr1_1, 0x94 ) +REGISTER( dr1_2, 0x98 ) +REGISTER( dr1_3, 0x9C ) +REGISTER( dr1_4, 0xA0 ) +REGISTER( dr1_5, 0xA4 ) +REGISTER( dr1_6, 0xA8 ) +REGISTER( dr1_7, 0xAC ) +REGISTER( dr2_0, 0xB0 ) +REGISTER( dr2_1, 0xB4 ) +REGISTER( dr2_2, 0xB8 ) +REGISTER( dr2_3, 0xBC ) +REGISTER( dr2_4, 0xC0 ) +REGISTER( dr2_5, 0xC4 ) +REGISTER( dr2_6, 0xC8 ) +REGISTER( dr2_7, 0xCC ) +REGISTER( dr3_0, 0xD0 ) +REGISTER( dr3_1, 0xD4 ) +REGISTER( dr3_2, 0xD8 ) +REGISTER( dr3_3, 0xDC ) +REGISTER( dr3_4, 0xE0 ) +REGISTER( dr3_5, 0xE4 ) +REGISTER( dr3_6, 0xE8 ) +REGISTER( dr3_7, 0xEC ) +REGISTER( dr4_0, 0xF0 ) +REGISTER( dr4_1, 0xF4 ) +REGISTER( dr4_2, 0xF8 ) +REGISTER( dr4_3, 0xFC ) +REGISTER( dr4_4, 0x100 ) +REGISTER( dr4_5, 0x104 ) +REGISTER( dr4_6, 0x108 ) +REGISTER( dr4_7, 0x10C ) diff --git a/hw/ppc.c b/hw/ppc.c index 25b59dddaa..d29af0bb35 100644 --- a/hw/ppc.c +++ b/hw/ppc.c @@ -662,6 +662,12 @@ static void __cpu_ppc_store_decr (CPUState *env, uint64_t *nextp, LOG_TB("%s: %08" PRIx32 " => %08" PRIx32 "\n", __func__, decr, value); + + if (kvm_enabled()) { + /* KVM handles decrementer exceptions, we don't need our own timer */ + return; + } + now = qemu_get_clock_ns(vm_clock); next = now + muldiv64(value, get_ticks_per_sec(), tb_env->decr_freq); if (is_excp) { diff --git a/hw/ppc_oldworld.c b/hw/ppc_oldworld.c index ebcaafa641..aac3526f55 100644 --- a/hw/ppc_oldworld.c +++ b/hw/ppc_oldworld.c @@ -73,11 +73,13 @@ static void ppc_heathrow_init (ram_addr_t ram_size, const char *initrd_filename, const char *cpu_model) { + MemoryRegion *sysmem = get_system_memory(); CPUState *env = NULL; char *filename; qemu_irq *pic, **heathrow_irqs; int linux_boot, i; - ram_addr_t ram_offset, bios_offset; + MemoryRegion *ram = g_new(MemoryRegion, 1); + MemoryRegion *bios = g_new(MemoryRegion, 1); uint32_t kernel_base, initrd_base, cmdline_base = 0; int32_t kernel_size, initrd_size; PCIBus *pci_bus; @@ -114,15 +116,16 @@ static void ppc_heathrow_init (ram_addr_t ram_size, exit(1); } - ram_offset = qemu_ram_alloc(NULL, "ppc_heathrow.ram", ram_size); - cpu_register_physical_memory(0, ram_size, ram_offset); + memory_region_init_ram(ram, NULL, "ppc_heathrow.ram", ram_size); + memory_region_add_subregion(sysmem, 0, ram); /* allocate and load BIOS */ - bios_offset = qemu_ram_alloc(NULL, "ppc_heathrow.bios", BIOS_SIZE); + memory_region_init_ram(bios, NULL, "ppc_heathrow.bios", BIOS_SIZE); if (bios_name == NULL) bios_name = PROM_FILENAME; filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - cpu_register_physical_memory(PROM_ADDR, BIOS_SIZE, bios_offset | IO_MEM_ROM); + memory_region_set_readonly(bios, true); + memory_region_add_subregion(sysmem, PROM_ADDR, bios); /* Load OpenBIOS (ELF) */ if (filename) { diff --git a/hw/ppc_prep.c b/hw/ppc_prep.c index d26049b1d1..f22d5b98c5 100644 --- a/hw/ppc_prep.c +++ b/hw/ppc_prep.c @@ -116,21 +116,22 @@ static uint32_t speaker_ioport_read (void *opaque, uint32_t addr) /* PCI intack register */ /* Read-only register (?) */ -static void _PPC_intack_write (void *opaque, - target_phys_addr_t addr, uint32_t value) +static void PPC_intack_write (void *opaque, target_phys_addr_t addr, + uint64_t value, unsigned size) { #if 0 - printf("%s: 0x" TARGET_FMT_plx " => 0x%08" PRIx32 "\n", __func__, addr, + printf("%s: 0x" TARGET_FMT_plx " => 0x%08" PRIx64 "\n", __func__, addr, value); #endif } -static inline uint32_t _PPC_intack_read(target_phys_addr_t addr) +static uint64_t PPC_intack_read(void *opaque, target_phys_addr_t addr, + unsigned size) { uint32_t retval = 0; if ((addr & 0xf) == 0) - retval = pic_intack_read(isa_pic); + retval = pic_read_irq(isa_pic); #if 0 printf("%s: 0x" TARGET_FMT_plx " <= %08" PRIx32 "\n", __func__, addr, retval); @@ -139,31 +140,10 @@ static inline uint32_t _PPC_intack_read(target_phys_addr_t addr) return retval; } -static uint32_t PPC_intack_readb (void *opaque, target_phys_addr_t addr) -{ - return _PPC_intack_read(addr); -} - -static uint32_t PPC_intack_readw (void *opaque, target_phys_addr_t addr) -{ - return _PPC_intack_read(addr); -} - -static uint32_t PPC_intack_readl (void *opaque, target_phys_addr_t addr) -{ - return _PPC_intack_read(addr); -} - -static CPUWriteMemoryFunc * const PPC_intack_write[] = { - &_PPC_intack_write, - &_PPC_intack_write, - &_PPC_intack_write, -}; - -static CPUReadMemoryFunc * const PPC_intack_read[] = { - &PPC_intack_readb, - &PPC_intack_readw, - &PPC_intack_readl, +static const MemoryRegionOps PPC_intack_ops = { + .read = PPC_intack_read, + .write = PPC_intack_write, + .endianness = DEVICE_LITTLE_ENDIAN, }; /* PowerPC control and status registers */ @@ -244,17 +224,14 @@ static uint32_t PPC_XCSR_readl (void *opaque, target_phys_addr_t addr) return retval; } -static CPUWriteMemoryFunc * const PPC_XCSR_write[] = { - &PPC_XCSR_writeb, - &PPC_XCSR_writew, - &PPC_XCSR_writel, +static const MemoryRegionOps PPC_XCSR_ops = { + .old_mmio = { + .read = { PPC_XCSR_readb, PPC_XCSR_readw, PPC_XCSR_readl, }, + .write = { PPC_XCSR_writeb, PPC_XCSR_writew, PPC_XCSR_writel, }, + }, + .endianness = DEVICE_LITTLE_ENDIAN, }; -static CPUReadMemoryFunc * const PPC_XCSR_read[] = { - &PPC_XCSR_readb, - &PPC_XCSR_readw, - &PPC_XCSR_readl, -}; #endif /* Fake super-io ports for PREP platform (Intel 82378ZB) */ @@ -503,16 +480,12 @@ static uint32_t PPC_prep_io_readl (void *opaque, target_phys_addr_t addr) return ret; } -static CPUWriteMemoryFunc * const PPC_prep_io_write[] = { - &PPC_prep_io_writeb, - &PPC_prep_io_writew, - &PPC_prep_io_writel, -}; - -static CPUReadMemoryFunc * const PPC_prep_io_read[] = { - &PPC_prep_io_readb, - &PPC_prep_io_readw, - &PPC_prep_io_readl, +static const MemoryRegionOps PPC_prep_io_ops = { + .old_mmio = { + .read = { PPC_prep_io_readb, PPC_prep_io_readw, PPC_prep_io_readl }, + .write = { PPC_prep_io_writeb, PPC_prep_io_writew, PPC_prep_io_writel }, + }, + .endianness = DEVICE_LITTLE_ENDIAN, }; #define NVRAM_SIZE 0x2000 @@ -534,13 +507,19 @@ static void ppc_prep_init (ram_addr_t ram_size, const char *initrd_filename, const char *cpu_model) { + MemoryRegion *sysmem = get_system_memory(); CPUState *env = NULL; char *filename; nvram_t nvram; M48t59State *m48t59; - int PPC_io_memory; + MemoryRegion *PPC_io_memory = g_new(MemoryRegion, 1); + MemoryRegion *intack = g_new(MemoryRegion, 1); +#if 0 + MemoryRegion *xcsr = g_new(MemoryRegion, 1); +#endif int linux_boot, i, nb_nics1, bios_size; - ram_addr_t ram_offset, bios_offset; + MemoryRegion *ram = g_new(MemoryRegion, 1); + MemoryRegion *bios = g_new(MemoryRegion, 1); uint32_t kernel_base, initrd_base; long kernel_size, initrd_size; PCIBus *pci_bus; @@ -574,11 +553,11 @@ static void ppc_prep_init (ram_addr_t ram_size, } /* allocate RAM */ - ram_offset = qemu_ram_alloc(NULL, "ppc_prep.ram", ram_size); - cpu_register_physical_memory(0, ram_size, ram_offset); + memory_region_init_ram(ram, NULL, "ppc_prep.ram", ram_size); + memory_region_add_subregion(sysmem, 0, ram); /* allocate and load BIOS */ - bios_offset = qemu_ram_alloc(NULL, "ppc_prep.bios", BIOS_SIZE); + memory_region_init_ram(bios, NULL, "ppc_prep.bios", BIOS_SIZE); if (bios_name == NULL) bios_name = BIOS_FILENAME; filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); @@ -591,8 +570,8 @@ static void ppc_prep_init (ram_addr_t ram_size, target_phys_addr_t bios_addr; bios_size = (bios_size + 0xfff) & ~0xfff; bios_addr = (uint32_t)(-bios_size); - cpu_register_physical_memory(bios_addr, bios_size, - bios_offset | IO_MEM_ROM); + memory_region_set_readonly(bios, true); + memory_region_add_subregion(sysmem, bios_addr, bios); bios_size = load_image_targphys(filename, bios_addr, bios_size); } if (bios_size < 0 || bios_size > BIOS_SIZE) { @@ -655,10 +634,9 @@ static void ppc_prep_init (ram_addr_t ram_size, isa_bus_irqs(i8259); // pci_bus = i440fx_init(); /* Register 8 MB of ISA IO space (needed for non-contiguous map) */ - PPC_io_memory = cpu_register_io_memory(PPC_prep_io_read, - PPC_prep_io_write, sysctrl, - DEVICE_LITTLE_ENDIAN); - cpu_register_physical_memory(0x80000000, 0x00800000, PPC_io_memory); + memory_region_init_io(PPC_io_memory, &PPC_prep_io_ops, sysctrl, + "ppc-io", 0x00800000); + memory_region_add_subregion(sysmem, 0x80000000, PPC_io_memory); /* init basic PC hardware */ pci_vga_init(pci_bus); @@ -713,15 +691,12 @@ static void ppc_prep_init (ram_addr_t ram_size, register_ioport_read(0x0800, 0x52, 1, &PREP_io_800_readb, sysctrl); register_ioport_write(0x0800, 0x52, 1, &PREP_io_800_writeb, sysctrl); /* PCI intack location */ - PPC_io_memory = cpu_register_io_memory(PPC_intack_read, - PPC_intack_write, NULL, - DEVICE_LITTLE_ENDIAN); - cpu_register_physical_memory(0xBFFFFFF0, 0x4, PPC_io_memory); + memory_region_init_io(intack, &PPC_intack_ops, NULL, "ppc-intack", 4); + memory_region_add_subregion(sysmem, 0xBFFFFFF0, intack); /* PowerPC control and status register group */ #if 0 - PPC_io_memory = cpu_register_io_memory(PPC_XCSR_read, PPC_XCSR_write, - NULL, DEVICE_LITTLE_ENDIAN); - cpu_register_physical_memory(0xFEFF0000, 0x1000, PPC_io_memory); + memory_region_init_io(xcsr, &PPC_XCSR_ops, NULL, "ppc-xcsr", 0x1000); + memory_region_add_subregion(sysmem, 0xFEFF0000, xcsr); #endif if (usb_enabled) { diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c index 5bf8eab897..51b6abddd3 100644 --- a/hw/ppce500_mpc8544ds.c +++ b/hw/ppce500_mpc8544ds.c @@ -229,6 +229,7 @@ static void mpc8544ds_init(ram_addr_t ram_size, const char *cpu_model) { MemoryRegion *address_space_mem = get_system_memory(); + MemoryRegion *ram = g_new(MemoryRegion, 1); PCIBus *pci_bus; CPUState *env = NULL; uint64_t elf_entry; @@ -291,8 +292,8 @@ static void mpc8544ds_init(ram_addr_t ram_size, ram_size &= ~(RAM_SIZES_ALIGN - 1); /* Register Memory */ - cpu_register_physical_memory(0, ram_size, qemu_ram_alloc(NULL, - "mpc8544ds.ram", ram_size)); + memory_region_init_ram(ram, NULL, "mpc8544ds.ram", ram_size); + memory_region_add_subregion(address_space_mem, 0, ram); /* MPIC */ mpic = mpic_init(address_space_mem, MPC8544_MPIC_REGS_BASE, diff --git a/hw/ppce500_pci.c b/hw/ppce500_pci.c index 2db365d0b6..960a5d0c60 100644 --- a/hw/ppce500_pci.c +++ b/hw/ppce500_pci.c @@ -89,6 +89,7 @@ static uint32_t pci_reg_read4(void *opaque, target_phys_addr_t addr) PPCE500PCIState *pci = opaque; unsigned long win; uint32_t value = 0; + int idx; win = addr & 0xfe0; @@ -97,24 +98,44 @@ static uint32_t pci_reg_read4(void *opaque, target_phys_addr_t addr) case PPCE500_PCI_OW2: case PPCE500_PCI_OW3: case PPCE500_PCI_OW4: + idx = (addr >> 5) & 0x7; switch (addr & 0xC) { - case PCI_POTAR: value = pci->pob[(addr >> 5) & 0x7].potar; break; - case PCI_POTEAR: value = pci->pob[(addr >> 5) & 0x7].potear; break; - case PCI_POWBAR: value = pci->pob[(addr >> 5) & 0x7].powbar; break; - case PCI_POWAR: value = pci->pob[(addr >> 5) & 0x7].powar; break; - default: break; + case PCI_POTAR: + value = pci->pob[idx].potar; + break; + case PCI_POTEAR: + value = pci->pob[idx].potear; + break; + case PCI_POWBAR: + value = pci->pob[idx].powbar; + break; + case PCI_POWAR: + value = pci->pob[idx].powar; + break; + default: + break; } break; case PPCE500_PCI_IW3: case PPCE500_PCI_IW2: case PPCE500_PCI_IW1: + idx = ((addr >> 5) & 0x3) - 1; switch (addr & 0xC) { - case PCI_PITAR: value = pci->pib[(addr >> 5) & 0x3].pitar; break; - case PCI_PIWBAR: value = pci->pib[(addr >> 5) & 0x3].piwbar; break; - case PCI_PIWBEAR: value = pci->pib[(addr >> 5) & 0x3].piwbear; break; - case PCI_PIWAR: value = pci->pib[(addr >> 5) & 0x3].piwar; break; - default: break; + case PCI_PITAR: + value = pci->pib[idx].pitar; + break; + case PCI_PIWBAR: + value = pci->pib[idx].piwbar; + break; + case PCI_PIWBEAR: + value = pci->pib[idx].piwbear; + break; + case PCI_PIWAR: + value = pci->pib[idx].piwar; + break; + default: + break; }; break; @@ -142,6 +163,7 @@ static void pci_reg_write4(void *opaque, target_phys_addr_t addr, { PPCE500PCIState *pci = opaque; unsigned long win; + int idx; win = addr & 0xfe0; @@ -153,24 +175,44 @@ static void pci_reg_write4(void *opaque, target_phys_addr_t addr, case PPCE500_PCI_OW2: case PPCE500_PCI_OW3: case PPCE500_PCI_OW4: + idx = (addr >> 5) & 0x7; switch (addr & 0xC) { - case PCI_POTAR: pci->pob[(addr >> 5) & 0x7].potar = value; break; - case PCI_POTEAR: pci->pob[(addr >> 5) & 0x7].potear = value; break; - case PCI_POWBAR: pci->pob[(addr >> 5) & 0x7].powbar = value; break; - case PCI_POWAR: pci->pob[(addr >> 5) & 0x7].powar = value; break; - default: break; + case PCI_POTAR: + pci->pob[idx].potar = value; + break; + case PCI_POTEAR: + pci->pob[idx].potear = value; + break; + case PCI_POWBAR: + pci->pob[idx].powbar = value; + break; + case PCI_POWAR: + pci->pob[idx].powar = value; + break; + default: + break; }; break; case PPCE500_PCI_IW3: case PPCE500_PCI_IW2: case PPCE500_PCI_IW1: + idx = ((addr >> 5) & 0x3) - 1; switch (addr & 0xC) { - case PCI_PITAR: pci->pib[(addr >> 5) & 0x3].pitar = value; break; - case PCI_PIWBAR: pci->pib[(addr >> 5) & 0x3].piwbar = value; break; - case PCI_PIWBEAR: pci->pib[(addr >> 5) & 0x3].piwbear = value; break; - case PCI_PIWAR: pci->pib[(addr >> 5) & 0x3].piwar = value; break; - default: break; + case PCI_PITAR: + pci->pib[idx].pitar = value; + break; + case PCI_PIWBAR: + pci->pib[idx].piwbar = value; + break; + case PCI_PIWBEAR: + pci->pib[idx].piwbear = value; + break; + case PCI_PIWAR: + pci->pib[idx].piwar = value; + break; + default: + break; }; break; diff --git a/hw/ps2.c b/hw/ps2.c index 24228c1539..1d9057bbf5 100644 --- a/hw/ps2.c +++ b/hw/ps2.c @@ -92,6 +92,7 @@ typedef struct { not the keyboard controller. */ int translate; int scancode_set; /* 1=XT, 2=AT, 3=PS/2 */ + int ledstate; } PS2KbdState; typedef struct { @@ -195,11 +196,17 @@ uint32_t ps2_read_data(void *opaque) return val; } +static void ps2_set_ledstate(PS2KbdState *s, int ledstate) +{ + s->ledstate = ledstate; + kbd_put_ledstate(ledstate); +} + static void ps2_reset_keyboard(PS2KbdState *s) { s->scan_enabled = 1; s->scancode_set = 2; - kbd_put_ledstate(0); + ps2_set_ledstate(s, 0); } void ps2_write_keyboard(void *opaque, int val) @@ -274,7 +281,7 @@ void ps2_write_keyboard(void *opaque, int val) s->common.write_cmd = -1; break; case KBD_CMD_SET_LEDS: - kbd_put_ledstate(val); + ps2_set_ledstate(s, val); ps2_queue(&s->common, KBD_REPLY_ACK); s->common.write_cmd = -1; break; @@ -557,6 +564,33 @@ static const VMStateDescription vmstate_ps2_common = { } }; +static bool ps2_keyboard_ledstate_needed(void *opaque) +{ + PS2KbdState *s = opaque; + + return s->ledstate != 0; /* 0 is default state */ +} + +static int ps2_kbd_ledstate_post_load(void *opaque, int version_id) +{ + PS2KbdState *s = opaque; + + kbd_put_ledstate(s->ledstate); + return 0; +} + +static const VMStateDescription vmstate_ps2_keyboard_ledstate = { + .name = "ps2kbd/ledstate", + .version_id = 3, + .minimum_version_id = 2, + .minimum_version_id_old = 2, + .post_load = ps2_kbd_ledstate_post_load, + .fields = (VMStateField []) { + VMSTATE_INT32(ledstate, PS2KbdState), + VMSTATE_END_OF_LIST() + } +}; + static int ps2_kbd_post_load(void* opaque, int version_id) { PS2KbdState *s = (PS2KbdState*)opaque; @@ -578,6 +612,14 @@ static const VMStateDescription vmstate_ps2_keyboard = { VMSTATE_INT32(translate, PS2KbdState), VMSTATE_INT32_V(scancode_set, PS2KbdState,3), VMSTATE_END_OF_LIST() + }, + .subsections = (VMStateSubsection []) { + { + .vmsd = &vmstate_ps2_keyboard_ledstate, + .needed = ps2_keyboard_ledstate_needed, + }, { + /* empty */ + } } }; diff --git a/hw/pxa.h b/hw/pxa.h index 1204165549..7e9838408b 100644 --- a/hw/pxa.h +++ b/hw/pxa.h @@ -122,6 +122,11 @@ typedef struct { CPUState *env; DeviceState *pic; qemu_irq reset; + MemoryRegion sdram; + MemoryRegion internal; + MemoryRegion cm_iomem; + MemoryRegion mm_iomem; + MemoryRegion pm_iomem; DeviceState *dma; DeviceState *gpio; PXA2xxLCDState *lcd; @@ -151,6 +156,7 @@ typedef struct { } PXA2xxState; struct PXA2xxI2SState { + MemoryRegion iomem; qemu_irq irq; qemu_irq rx_dma; qemu_irq tx_dma; diff --git a/hw/pxa2xx.c b/hw/pxa2xx.c index 70d7c8a06d..bfc28a999b 100644 --- a/hw/pxa2xx.c +++ b/hw/pxa2xx.c @@ -88,7 +88,8 @@ static PXASSPDef pxa27x_ssp[] = { #define PCMD0 0x80 /* Power Manager I2C Command register File 0 */ #define PCMD31 0xfc /* Power Manager I2C Command register File 31 */ -static uint32_t pxa2xx_pm_read(void *opaque, target_phys_addr_t addr) +static uint64_t pxa2xx_pm_read(void *opaque, target_phys_addr_t addr, + unsigned size) { PXA2xxState *s = (PXA2xxState *) opaque; @@ -107,7 +108,7 @@ static uint32_t pxa2xx_pm_read(void *opaque, target_phys_addr_t addr) } static void pxa2xx_pm_write(void *opaque, target_phys_addr_t addr, - uint32_t value) + uint64_t value, unsigned size) { PXA2xxState *s = (PXA2xxState *) opaque; @@ -134,16 +135,10 @@ static void pxa2xx_pm_write(void *opaque, target_phys_addr_t addr, } } -static CPUReadMemoryFunc * const pxa2xx_pm_readfn[] = { - pxa2xx_pm_read, - pxa2xx_pm_read, - pxa2xx_pm_read, -}; - -static CPUWriteMemoryFunc * const pxa2xx_pm_writefn[] = { - pxa2xx_pm_write, - pxa2xx_pm_write, - pxa2xx_pm_write, +static const MemoryRegionOps pxa2xx_pm_ops = { + .read = pxa2xx_pm_read, + .write = pxa2xx_pm_write, + .endianness = DEVICE_NATIVE_ENDIAN, }; static const VMStateDescription vmstate_pxa2xx_pm = { @@ -162,7 +157,8 @@ static const VMStateDescription vmstate_pxa2xx_pm = { #define OSCC 0x08 /* Oscillator Configuration register */ #define CCSR 0x0c /* Core Clock Status register */ -static uint32_t pxa2xx_cm_read(void *opaque, target_phys_addr_t addr) +static uint64_t pxa2xx_cm_read(void *opaque, target_phys_addr_t addr, + unsigned size) { PXA2xxState *s = (PXA2xxState *) opaque; @@ -183,7 +179,7 @@ static uint32_t pxa2xx_cm_read(void *opaque, target_phys_addr_t addr) } static void pxa2xx_cm_write(void *opaque, target_phys_addr_t addr, - uint32_t value) + uint64_t value, unsigned size) { PXA2xxState *s = (PXA2xxState *) opaque; @@ -206,16 +202,10 @@ static void pxa2xx_cm_write(void *opaque, target_phys_addr_t addr, } } -static CPUReadMemoryFunc * const pxa2xx_cm_readfn[] = { - pxa2xx_cm_read, - pxa2xx_cm_read, - pxa2xx_cm_read, -}; - -static CPUWriteMemoryFunc * const pxa2xx_cm_writefn[] = { - pxa2xx_cm_write, - pxa2xx_cm_write, - pxa2xx_cm_write, +static const MemoryRegionOps pxa2xx_cm_ops = { + .read = pxa2xx_cm_read, + .write = pxa2xx_cm_write, + .endianness = DEVICE_NATIVE_ENDIAN, }; static const VMStateDescription vmstate_pxa2xx_cm = { @@ -461,7 +451,8 @@ static void pxa2xx_cp14_write(void *opaque, int op2, int reg, int crm, #define BSCNTR3 0x60 /* Memory Buffer Strength Control register 3 */ #define SA1110 0x64 /* SA-1110 Memory Compatibility register */ -static uint32_t pxa2xx_mm_read(void *opaque, target_phys_addr_t addr) +static uint64_t pxa2xx_mm_read(void *opaque, target_phys_addr_t addr, + unsigned size) { PXA2xxState *s = (PXA2xxState *) opaque; @@ -478,7 +469,7 @@ static uint32_t pxa2xx_mm_read(void *opaque, target_phys_addr_t addr) } static void pxa2xx_mm_write(void *opaque, target_phys_addr_t addr, - uint32_t value) + uint64_t value, unsigned size) { PXA2xxState *s = (PXA2xxState *) opaque; @@ -495,16 +486,10 @@ static void pxa2xx_mm_write(void *opaque, target_phys_addr_t addr, } } -static CPUReadMemoryFunc * const pxa2xx_mm_readfn[] = { - pxa2xx_mm_read, - pxa2xx_mm_read, - pxa2xx_mm_read, -}; - -static CPUWriteMemoryFunc * const pxa2xx_mm_writefn[] = { - pxa2xx_mm_write, - pxa2xx_mm_write, - pxa2xx_mm_write, +static const MemoryRegionOps pxa2xx_mm_ops = { + .read = pxa2xx_mm_read, + .write = pxa2xx_mm_write, + .endianness = DEVICE_NATIVE_ENDIAN, }; static const VMStateDescription vmstate_pxa2xx_mm = { @@ -521,6 +506,7 @@ static const VMStateDescription vmstate_pxa2xx_mm = { /* Synchronous Serial Ports */ typedef struct { SysBusDevice busdev; + MemoryRegion iomem; qemu_irq irq; int enable; SSIBus *bus; @@ -627,7 +613,8 @@ static void pxa2xx_ssp_fifo_update(PXA2xxSSPState *s) pxa2xx_ssp_int_update(s); } -static uint32_t pxa2xx_ssp_read(void *opaque, target_phys_addr_t addr) +static uint64_t pxa2xx_ssp_read(void *opaque, target_phys_addr_t addr, + unsigned size) { PXA2xxSSPState *s = (PXA2xxSSPState *) opaque; uint32_t retval; @@ -673,9 +660,10 @@ static uint32_t pxa2xx_ssp_read(void *opaque, target_phys_addr_t addr) } static void pxa2xx_ssp_write(void *opaque, target_phys_addr_t addr, - uint32_t value) + uint64_t value64, unsigned size) { PXA2xxSSPState *s = (PXA2xxSSPState *) opaque; + uint32_t value = value64; switch (addr) { case SSCR0: @@ -762,16 +750,10 @@ static void pxa2xx_ssp_write(void *opaque, target_phys_addr_t addr, } } -static CPUReadMemoryFunc * const pxa2xx_ssp_readfn[] = { - pxa2xx_ssp_read, - pxa2xx_ssp_read, - pxa2xx_ssp_read, -}; - -static CPUWriteMemoryFunc * const pxa2xx_ssp_writefn[] = { - pxa2xx_ssp_write, - pxa2xx_ssp_write, - pxa2xx_ssp_write, +static const MemoryRegionOps pxa2xx_ssp_ops = { + .read = pxa2xx_ssp_read, + .write = pxa2xx_ssp_write, + .endianness = DEVICE_NATIVE_ENDIAN, }; static void pxa2xx_ssp_save(QEMUFile *f, void *opaque) @@ -823,15 +805,12 @@ static int pxa2xx_ssp_load(QEMUFile *f, void *opaque, int version_id) static int pxa2xx_ssp_init(SysBusDevice *dev) { - int iomemtype; PXA2xxSSPState *s = FROM_SYSBUS(PXA2xxSSPState, dev); sysbus_init_irq(dev, &s->irq); - iomemtype = cpu_register_io_memory(pxa2xx_ssp_readfn, - pxa2xx_ssp_writefn, s, - DEVICE_NATIVE_ENDIAN); - sysbus_init_mmio(dev, 0x1000, iomemtype); + memory_region_init_io(&s->iomem, &pxa2xx_ssp_ops, s, "pxa2xx-ssp", 0x1000); + sysbus_init_mmio_region(dev, &s->iomem); register_savevm(&dev->qdev, "pxa2xx_ssp", -1, 0, pxa2xx_ssp_save, pxa2xx_ssp_load, s); @@ -858,6 +837,7 @@ static int pxa2xx_ssp_init(SysBusDevice *dev) typedef struct { SysBusDevice busdev; + MemoryRegion iomem; uint32_t rttr; uint32_t rtsr; uint32_t rtar; @@ -1009,7 +989,8 @@ static inline void pxa2xx_rtc_pi_tick(void *opaque) pxa2xx_rtc_int_update(s); } -static uint32_t pxa2xx_rtc_read(void *opaque, target_phys_addr_t addr) +static uint64_t pxa2xx_rtc_read(void *opaque, target_phys_addr_t addr, + unsigned size) { PXA2xxRTCState *s = (PXA2xxRTCState *) opaque; @@ -1055,9 +1036,10 @@ static uint32_t pxa2xx_rtc_read(void *opaque, target_phys_addr_t addr) } static void pxa2xx_rtc_write(void *opaque, target_phys_addr_t addr, - uint32_t value) + uint64_t value64, unsigned size) { PXA2xxRTCState *s = (PXA2xxRTCState *) opaque; + uint32_t value = value64; switch (addr) { case RTTR: @@ -1157,16 +1139,10 @@ static void pxa2xx_rtc_write(void *opaque, target_phys_addr_t addr, } } -static CPUReadMemoryFunc * const pxa2xx_rtc_readfn[] = { - pxa2xx_rtc_read, - pxa2xx_rtc_read, - pxa2xx_rtc_read, -}; - -static CPUWriteMemoryFunc * const pxa2xx_rtc_writefn[] = { - pxa2xx_rtc_write, - pxa2xx_rtc_write, - pxa2xx_rtc_write, +static const MemoryRegionOps pxa2xx_rtc_ops = { + .read = pxa2xx_rtc_read, + .write = pxa2xx_rtc_write, + .endianness = DEVICE_NATIVE_ENDIAN, }; static int pxa2xx_rtc_init(SysBusDevice *dev) @@ -1174,7 +1150,6 @@ static int pxa2xx_rtc_init(SysBusDevice *dev) PXA2xxRTCState *s = FROM_SYSBUS(PXA2xxRTCState, dev); struct tm tm; int wom; - int iomemtype; s->rttr = 0x7fff; s->rtsr = 0; @@ -1201,9 +1176,8 @@ static int pxa2xx_rtc_init(SysBusDevice *dev) sysbus_init_irq(dev, &s->rtc_irq); - iomemtype = cpu_register_io_memory(pxa2xx_rtc_readfn, - pxa2xx_rtc_writefn, s, DEVICE_NATIVE_ENDIAN); - sysbus_init_mmio(dev, 0x10000, iomemtype); + memory_region_init_io(&s->iomem, &pxa2xx_rtc_ops, s, "pxa2xx-rtc", 0x10000); + sysbus_init_mmio_region(dev, &s->iomem); return 0; } @@ -1272,6 +1246,7 @@ typedef struct { struct PXA2xxI2CState { SysBusDevice busdev; + MemoryRegion iomem; PXA2xxI2CSlaveState *slave; i2c_bus *bus; qemu_irq irq; @@ -1356,7 +1331,8 @@ static int pxa2xx_i2c_tx(i2c_slave *i2c, uint8_t data) return 1; } -static uint32_t pxa2xx_i2c_read(void *opaque, target_phys_addr_t addr) +static uint64_t pxa2xx_i2c_read(void *opaque, target_phys_addr_t addr, + unsigned size) { PXA2xxI2CState *s = (PXA2xxI2CState *) opaque; @@ -1384,9 +1360,10 @@ static uint32_t pxa2xx_i2c_read(void *opaque, target_phys_addr_t addr) } static void pxa2xx_i2c_write(void *opaque, target_phys_addr_t addr, - uint32_t value) + uint64_t value64, unsigned size) { PXA2xxI2CState *s = (PXA2xxI2CState *) opaque; + uint32_t value = value64; int ack; addr -= s->offset; @@ -1453,16 +1430,10 @@ static void pxa2xx_i2c_write(void *opaque, target_phys_addr_t addr, } } -static CPUReadMemoryFunc * const pxa2xx_i2c_readfn[] = { - pxa2xx_i2c_read, - pxa2xx_i2c_read, - pxa2xx_i2c_read, -}; - -static CPUWriteMemoryFunc * const pxa2xx_i2c_writefn[] = { - pxa2xx_i2c_write, - pxa2xx_i2c_write, - pxa2xx_i2c_write, +static const MemoryRegionOps pxa2xx_i2c_ops = { + .read = pxa2xx_i2c_read, + .write = pxa2xx_i2c_write, + .endianness = DEVICE_NATIVE_ENDIAN, }; static const VMStateDescription vmstate_pxa2xx_i2c_slave = { @@ -1536,13 +1507,12 @@ PXA2xxI2CState *pxa2xx_i2c_init(target_phys_addr_t base, static int pxa2xx_i2c_initfn(SysBusDevice *dev) { PXA2xxI2CState *s = FROM_SYSBUS(PXA2xxI2CState, dev); - int iomemtype; s->bus = i2c_init_bus(&dev->qdev, "i2c"); - iomemtype = cpu_register_io_memory(pxa2xx_i2c_readfn, - pxa2xx_i2c_writefn, s, DEVICE_NATIVE_ENDIAN); - sysbus_init_mmio(dev, s->region_size, iomemtype); + memory_region_init_io(&s->iomem, &pxa2xx_i2c_ops, s, + "pxa2xx-i2x", s->region_size); + sysbus_init_mmio_region(dev, &s->iomem); sysbus_init_irq(dev, &s->irq); return 0; @@ -1621,7 +1591,8 @@ static inline void pxa2xx_i2s_update(PXA2xxI2SState *i2s) #define SADIV 0x60 /* Serial Audio Clock Divider register */ #define SADR 0x80 /* Serial Audio Data register */ -static uint32_t pxa2xx_i2s_read(void *opaque, target_phys_addr_t addr) +static uint64_t pxa2xx_i2s_read(void *opaque, target_phys_addr_t addr, + unsigned size) { PXA2xxI2SState *s = (PXA2xxI2SState *) opaque; @@ -1653,7 +1624,7 @@ static uint32_t pxa2xx_i2s_read(void *opaque, target_phys_addr_t addr) } static void pxa2xx_i2s_write(void *opaque, target_phys_addr_t addr, - uint32_t value) + uint64_t value, unsigned size) { PXA2xxI2SState *s = (PXA2xxI2SState *) opaque; uint32_t *sample; @@ -1707,16 +1678,10 @@ static void pxa2xx_i2s_write(void *opaque, target_phys_addr_t addr, } } -static CPUReadMemoryFunc * const pxa2xx_i2s_readfn[] = { - pxa2xx_i2s_read, - pxa2xx_i2s_read, - pxa2xx_i2s_read, -}; - -static CPUWriteMemoryFunc * const pxa2xx_i2s_writefn[] = { - pxa2xx_i2s_write, - pxa2xx_i2s_write, - pxa2xx_i2s_write, +static const MemoryRegionOps pxa2xx_i2s_ops = { + .read = pxa2xx_i2s_read, + .write = pxa2xx_i2s_write, + .endianness = DEVICE_NATIVE_ENDIAN, }; static const VMStateDescription vmstate_pxa2xx_i2s = { @@ -1759,10 +1724,10 @@ static void pxa2xx_i2s_data_req(void *opaque, int tx, int rx) pxa2xx_i2s_update(s); } -static PXA2xxI2SState *pxa2xx_i2s_init(target_phys_addr_t base, +static PXA2xxI2SState *pxa2xx_i2s_init(MemoryRegion *sysmem, + target_phys_addr_t base, qemu_irq irq, qemu_irq rx_dma, qemu_irq tx_dma) { - int iomemtype; PXA2xxI2SState *s = (PXA2xxI2SState *) g_malloc0(sizeof(PXA2xxI2SState)); @@ -1773,9 +1738,9 @@ static PXA2xxI2SState *pxa2xx_i2s_init(target_phys_addr_t base, pxa2xx_i2s_reset(s); - iomemtype = cpu_register_io_memory(pxa2xx_i2s_readfn, - pxa2xx_i2s_writefn, s, DEVICE_NATIVE_ENDIAN); - cpu_register_physical_memory(base, 0x100000, iomemtype); + memory_region_init_io(&s->iomem, &pxa2xx_i2s_ops, s, + "pxa2xx-i2s", 0x100000); + memory_region_add_subregion(sysmem, base, &s->iomem); vmstate_register(NULL, base, &vmstate_pxa2xx_i2s, s); @@ -1784,6 +1749,7 @@ static PXA2xxI2SState *pxa2xx_i2s_init(target_phys_addr_t base, /* PXA Fast Infra-red Communications Port */ struct PXA2xxFIrState { + MemoryRegion iomem; qemu_irq irq; qemu_irq rx_dma; qemu_irq tx_dma; @@ -1854,7 +1820,8 @@ static inline void pxa2xx_fir_update(PXA2xxFIrState *s) #define ICSR1 0x18 /* FICP Status register 1 */ #define ICFOR 0x1c /* FICP FIFO Occupancy Status register */ -static uint32_t pxa2xx_fir_read(void *opaque, target_phys_addr_t addr) +static uint64_t pxa2xx_fir_read(void *opaque, target_phys_addr_t addr, + unsigned size) { PXA2xxFIrState *s = (PXA2xxFIrState *) opaque; uint8_t ret; @@ -1892,9 +1859,10 @@ static uint32_t pxa2xx_fir_read(void *opaque, target_phys_addr_t addr) } static void pxa2xx_fir_write(void *opaque, target_phys_addr_t addr, - uint32_t value) + uint64_t value64, unsigned size) { PXA2xxFIrState *s = (PXA2xxFIrState *) opaque; + uint32_t value = value64; uint8_t ch; switch (addr) { @@ -1936,16 +1904,10 @@ static void pxa2xx_fir_write(void *opaque, target_phys_addr_t addr, } } -static CPUReadMemoryFunc * const pxa2xx_fir_readfn[] = { - pxa2xx_fir_read, - pxa2xx_fir_read, - pxa2xx_fir_read, -}; - -static CPUWriteMemoryFunc * const pxa2xx_fir_writefn[] = { - pxa2xx_fir_write, - pxa2xx_fir_write, - pxa2xx_fir_write, +static const MemoryRegionOps pxa2xx_fir_ops = { + .read = pxa2xx_fir_read, + .write = pxa2xx_fir_write, + .endianness = DEVICE_NATIVE_ENDIAN, }; static int pxa2xx_fir_is_empty(void *opaque) @@ -2019,11 +1981,11 @@ static int pxa2xx_fir_load(QEMUFile *f, void *opaque, int version_id) return 0; } -static PXA2xxFIrState *pxa2xx_fir_init(target_phys_addr_t base, +static PXA2xxFIrState *pxa2xx_fir_init(MemoryRegion *sysmem, + target_phys_addr_t base, qemu_irq irq, qemu_irq rx_dma, qemu_irq tx_dma, CharDriverState *chr) { - int iomemtype; PXA2xxFIrState *s = (PXA2xxFIrState *) g_malloc0(sizeof(PXA2xxFIrState)); @@ -2034,9 +1996,8 @@ static PXA2xxFIrState *pxa2xx_fir_init(target_phys_addr_t base, pxa2xx_fir_reset(s); - iomemtype = cpu_register_io_memory(pxa2xx_fir_readfn, - pxa2xx_fir_writefn, s, DEVICE_NATIVE_ENDIAN); - cpu_register_physical_memory(base, 0x1000, iomemtype); + memory_region_init_io(&s->iomem, &pxa2xx_fir_ops, s, "pxa2xx-fir", 0x1000); + memory_region_add_subregion(sysmem, base, &s->iomem); if (chr) qemu_chr_add_handlers(chr, pxa2xx_fir_is_empty, @@ -2063,7 +2024,7 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space, unsigned int sdram_size, const char *revision) { PXA2xxState *s; - int iomemtype, i; + int i; DriveInfo *dinfo; s = (PXA2xxState *) g_malloc0(sizeof(PXA2xxState)); @@ -2082,12 +2043,11 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space, s->reset = qemu_allocate_irqs(pxa2xx_reset, s, 1)[0]; /* SDRAM & Internal Memory Storage */ - cpu_register_physical_memory(PXA2XX_SDRAM_BASE, - sdram_size, qemu_ram_alloc(NULL, "pxa270.sdram", - sdram_size) | IO_MEM_RAM); - cpu_register_physical_memory(PXA2XX_INTERNAL_BASE, - 0x40000, qemu_ram_alloc(NULL, "pxa270.internal", - 0x40000) | IO_MEM_RAM); + memory_region_init_ram(&s->sdram, NULL, "pxa270.sdram", sdram_size); + memory_region_add_subregion(address_space, PXA2XX_SDRAM_BASE, &s->sdram); + memory_region_init_ram(&s->internal, NULL, "pxa270.internal", 0x40000); + memory_region_add_subregion(address_space, PXA2XX_INTERNAL_BASE, + &s->internal); s->pic = pxa2xx_pic_init(0x40d00000, s->env); @@ -2125,7 +2085,7 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space, } } if (serial_hds[i]) - s->fir = pxa2xx_fir_init(0x40800000, + s->fir = pxa2xx_fir_init(address_space, 0x40800000, qdev_get_gpio_in(s->pic, PXA2XX_PIC_ICP), qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_ICP), qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_ICP), @@ -2137,9 +2097,8 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space, s->cm_base = 0x41300000; s->cm_regs[CCCR >> 2] = 0x02000210; /* 416.0 MHz */ s->clkcfg = 0x00000009; /* Turbo mode active */ - iomemtype = cpu_register_io_memory(pxa2xx_cm_readfn, - pxa2xx_cm_writefn, s, DEVICE_NATIVE_ENDIAN); - cpu_register_physical_memory(s->cm_base, 0x1000, iomemtype); + memory_region_init_io(&s->cm_iomem, &pxa2xx_cm_ops, s, "pxa2xx-cm", 0x1000); + memory_region_add_subregion(address_space, s->cm_base, &s->cm_iomem); vmstate_register(NULL, 0, &vmstate_pxa2xx_cm, s); cpu_arm_set_cp_io(s->env, 14, pxa2xx_cp14_read, pxa2xx_cp14_write, s); @@ -2148,15 +2107,13 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space, s->mm_regs[MDMRS >> 2] = 0x00020002; s->mm_regs[MDREFR >> 2] = 0x03ca4000; s->mm_regs[MECR >> 2] = 0x00000001; /* Two PC Card sockets */ - iomemtype = cpu_register_io_memory(pxa2xx_mm_readfn, - pxa2xx_mm_writefn, s, DEVICE_NATIVE_ENDIAN); - cpu_register_physical_memory(s->mm_base, 0x1000, iomemtype); + memory_region_init_io(&s->mm_iomem, &pxa2xx_mm_ops, s, "pxa2xx-mm", 0x1000); + memory_region_add_subregion(address_space, s->mm_base, &s->mm_iomem); vmstate_register(NULL, 0, &vmstate_pxa2xx_mm, s); s->pm_base = 0x40f00000; - iomemtype = cpu_register_io_memory(pxa2xx_pm_readfn, - pxa2xx_pm_writefn, s, DEVICE_NATIVE_ENDIAN); - cpu_register_physical_memory(s->pm_base, 0x100, iomemtype); + memory_region_init_io(&s->pm_iomem, &pxa2xx_pm_ops, s, "pxa2xx-pm", 0x100); + memory_region_add_subregion(address_space, s->pm_base, &s->pm_iomem); vmstate_register(NULL, 0, &vmstate_pxa2xx_pm, s); for (i = 0; pxa27x_ssp[i].io_base; i ++); @@ -2184,7 +2141,7 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space, s->i2c[1] = pxa2xx_i2c_init(0x40f00100, qdev_get_gpio_in(s->pic, PXA2XX_PIC_PWRI2C), 0xff); - s->i2s = pxa2xx_i2s_init(0x40400000, + s->i2s = pxa2xx_i2s_init(address_space, 0x40400000, qdev_get_gpio_in(s->pic, PXA2XX_PIC_I2S), qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_I2S), qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_I2S)); @@ -2202,7 +2159,7 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space, PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size) { PXA2xxState *s; - int iomemtype, i; + int i; DriveInfo *dinfo; s = (PXA2xxState *) g_malloc0(sizeof(PXA2xxState)); @@ -2215,12 +2172,12 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size) s->reset = qemu_allocate_irqs(pxa2xx_reset, s, 1)[0]; /* SDRAM & Internal Memory Storage */ - cpu_register_physical_memory(PXA2XX_SDRAM_BASE, sdram_size, - qemu_ram_alloc(NULL, "pxa255.sdram", - sdram_size) | IO_MEM_RAM); - cpu_register_physical_memory(PXA2XX_INTERNAL_BASE, PXA2XX_INTERNAL_SIZE, - qemu_ram_alloc(NULL, "pxa255.internal", - PXA2XX_INTERNAL_SIZE) | IO_MEM_RAM); + memory_region_init_ram(&s->sdram, NULL, "pxa255.sdram", sdram_size); + memory_region_add_subregion(address_space, PXA2XX_SDRAM_BASE, &s->sdram); + memory_region_init_ram(&s->internal, NULL, "pxa255.internal", + PXA2XX_INTERNAL_SIZE); + memory_region_add_subregion(address_space, PXA2XX_INTERNAL_BASE, + &s->internal); s->pic = pxa2xx_pic_init(0x40d00000, s->env); @@ -2257,7 +2214,7 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size) } } if (serial_hds[i]) - s->fir = pxa2xx_fir_init(0x40800000, + s->fir = pxa2xx_fir_init(address_space, 0x40800000, qdev_get_gpio_in(s->pic, PXA2XX_PIC_ICP), qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_ICP), qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_ICP), @@ -2269,9 +2226,8 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size) s->cm_base = 0x41300000; s->cm_regs[CCCR >> 2] = 0x02000210; /* 416.0 MHz */ s->clkcfg = 0x00000009; /* Turbo mode active */ - iomemtype = cpu_register_io_memory(pxa2xx_cm_readfn, - pxa2xx_cm_writefn, s, DEVICE_NATIVE_ENDIAN); - cpu_register_physical_memory(s->cm_base, 0x1000, iomemtype); + memory_region_init_io(&s->cm_iomem, &pxa2xx_cm_ops, s, "pxa2xx-cm", 0x1000); + memory_region_add_subregion(address_space, s->cm_base, &s->cm_iomem); vmstate_register(NULL, 0, &vmstate_pxa2xx_cm, s); cpu_arm_set_cp_io(s->env, 14, pxa2xx_cp14_read, pxa2xx_cp14_write, s); @@ -2280,15 +2236,13 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size) s->mm_regs[MDMRS >> 2] = 0x00020002; s->mm_regs[MDREFR >> 2] = 0x03ca4000; s->mm_regs[MECR >> 2] = 0x00000001; /* Two PC Card sockets */ - iomemtype = cpu_register_io_memory(pxa2xx_mm_readfn, - pxa2xx_mm_writefn, s, DEVICE_NATIVE_ENDIAN); - cpu_register_physical_memory(s->mm_base, 0x1000, iomemtype); + memory_region_init_io(&s->mm_iomem, &pxa2xx_mm_ops, s, "pxa2xx-mm", 0x1000); + memory_region_add_subregion(address_space, s->mm_base, &s->mm_iomem); vmstate_register(NULL, 0, &vmstate_pxa2xx_mm, s); s->pm_base = 0x40f00000; - iomemtype = cpu_register_io_memory(pxa2xx_pm_readfn, - pxa2xx_pm_writefn, s, DEVICE_NATIVE_ENDIAN); - cpu_register_physical_memory(s->pm_base, 0x100, iomemtype); + memory_region_init_io(&s->pm_iomem, &pxa2xx_pm_ops, s, "pxa2xx-pm", 0x100); + memory_region_add_subregion(address_space, s->pm_base, &s->pm_iomem); vmstate_register(NULL, 0, &vmstate_pxa2xx_pm, s); for (i = 0; pxa255_ssp[i].io_base; i ++); @@ -2316,7 +2270,7 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size) s->i2c[1] = pxa2xx_i2c_init(0x40f00100, qdev_get_gpio_in(s->pic, PXA2XX_PIC_PWRI2C), 0xff); - s->i2s = pxa2xx_i2s_init(0x40400000, + s->i2s = pxa2xx_i2s_init(address_space, 0x40400000, qdev_get_gpio_in(s->pic, PXA2XX_PIC_I2S), qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_I2S), qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_I2S)); diff --git a/hw/qdev-properties.c b/hw/qdev-properties.c index e0e54aa857..f0b811c806 100644 --- a/hw/qdev-properties.c +++ b/hw/qdev-properties.c @@ -93,6 +93,35 @@ PropertyInfo qdev_prop_uint8 = { .print = print_uint8, }; +/* --- 8bit hex value --- */ + +static int parse_hex8(DeviceState *dev, Property *prop, const char *str) +{ + uint8_t *ptr = qdev_get_prop_ptr(dev, prop); + char *end; + + *ptr = strtoul(str, &end, 16); + if ((*end != '\0') || (end == str)) { + return -EINVAL; + } + + return 0; +} + +static int print_hex8(DeviceState *dev, Property *prop, char *dest, size_t len) +{ + uint8_t *ptr = qdev_get_prop_ptr(dev, prop); + return snprintf(dest, len, "0x%" PRIx8, *ptr); +} + +PropertyInfo qdev_prop_hex8 = { + .name = "hex8", + .type = PROP_TYPE_UINT8, + .size = sizeof(uint8_t), + .parse = parse_hex8, + .print = print_hex8, +}; + /* --- 16bit integer --- */ static int parse_uint16(DeviceState *dev, Property *prop, const char *str) diff --git a/hw/qdev.c b/hw/qdev.c index a223d41cd3..50976dd0c1 100644 --- a/hw/qdev.c +++ b/hw/qdev.c @@ -91,7 +91,7 @@ static DeviceState *qdev_create_from_info(BusState *bus, DeviceInfo *info) qdev_prop_set_defaults(dev, dev->info->props); qdev_prop_set_defaults(dev, dev->parent_bus->info->props); qdev_prop_set_globals(dev); - QLIST_INSERT_HEAD(&bus->children, dev, sibling); + QTAILQ_INSERT_HEAD(&bus->children, dev, sibling); if (qdev_hotplug) { assert(bus->allow_hotplug); dev->hotplugged = 1; @@ -408,7 +408,7 @@ void qdev_free(DeviceState *dev) if (dev->opts) qemu_opts_del(dev->opts); } - QLIST_REMOVE(dev, sibling); + QTAILQ_REMOVE(&dev->parent_bus->children, dev, sibling); for (prop = dev->info->props; prop && prop->name; prop++) { if (prop->info->free) { prop->info->free(dev, prop); @@ -510,7 +510,7 @@ int qbus_walk_children(BusState *bus, qdev_walkerfn *devfn, } } - QLIST_FOREACH(dev, &bus->children, sibling) { + QTAILQ_FOREACH(dev, &bus->children, sibling) { err = qdev_walk_children(dev, devfn, busfn, opaque); if (err < 0) { return err; @@ -560,7 +560,7 @@ static BusState *qbus_find_recursive(BusState *bus, const char *name, return bus; } - QLIST_FOREACH(dev, &bus->children, sibling) { + QTAILQ_FOREACH(dev, &bus->children, sibling) { QLIST_FOREACH(child, &dev->child_bus, sibling) { ret = qbus_find_recursive(child, name, info); if (ret) { @@ -576,7 +576,7 @@ DeviceState *qdev_find_recursive(BusState *bus, const char *id) DeviceState *dev, *ret; BusState *child; - QLIST_FOREACH(dev, &bus->children, sibling) { + QTAILQ_FOREACH(dev, &bus->children, sibling) { if (dev->id && strcmp(dev->id, id) == 0) return dev; QLIST_FOREACH(child, &dev->child_bus, sibling) { @@ -609,7 +609,7 @@ static void qbus_list_dev(BusState *bus) const char *sep = " "; error_printf("devices at \"%s\":", bus->name); - QLIST_FOREACH(dev, &bus->children, sibling) { + QTAILQ_FOREACH(dev, &bus->children, sibling) { error_printf("%s\"%s\"", sep, dev->info->name); if (dev->id) error_printf("/\"%s\"", dev->id); @@ -640,17 +640,17 @@ static DeviceState *qbus_find_dev(BusState *bus, char *elem) * (2) driver name * (3) driver alias, if present */ - QLIST_FOREACH(dev, &bus->children, sibling) { + QTAILQ_FOREACH(dev, &bus->children, sibling) { if (dev->id && strcmp(dev->id, elem) == 0) { return dev; } } - QLIST_FOREACH(dev, &bus->children, sibling) { + QTAILQ_FOREACH(dev, &bus->children, sibling) { if (strcmp(dev->info->name, elem) == 0) { return dev; } } - QLIST_FOREACH(dev, &bus->children, sibling) { + QTAILQ_FOREACH(dev, &bus->children, sibling) { if (dev->info->alias && strcmp(dev->info->alias, elem) == 0) { return dev; } @@ -774,7 +774,7 @@ void qbus_create_inplace(BusState *bus, BusInfo *info, bus->name = buf; } - QLIST_INIT(&bus->children); + QTAILQ_INIT(&bus->children); if (parent) { QLIST_INSERT_HEAD(&parent->child_bus, bus, sibling); parent->num_child_bus++; @@ -809,7 +809,7 @@ void qbus_free(BusState *bus) { DeviceState *dev; - while ((dev = QLIST_FIRST(&bus->children)) != NULL) { + while ((dev = QTAILQ_FIRST(&bus->children)) != NULL) { qdev_free(dev); } if (bus->parent) { @@ -878,7 +878,7 @@ static void qbus_print(Monitor *mon, BusState *bus, int indent) qdev_printf("bus: %s\n", bus->name); indent += 2; qdev_printf("type %s\n", bus->info->name); - QLIST_FOREACH(dev, &bus->children, sibling) { + QTAILQ_FOREACH(dev, &bus->children, sibling) { qdev_print(mon, dev, indent); } } diff --git a/hw/qdev.h b/hw/qdev.h index 8a13ec95cc..36a4198c89 100644 --- a/hw/qdev.h +++ b/hw/qdev.h @@ -42,7 +42,7 @@ struct DeviceState { qemu_irq *gpio_in; QLIST_HEAD(, BusState) child_bus; int num_child_bus; - QLIST_ENTRY(DeviceState) sibling; + QTAILQ_ENTRY(DeviceState) sibling; int instance_id_alias; int alias_required_for_version; }; @@ -73,7 +73,7 @@ struct BusState { const char *name; int allow_hotplug; int qdev_allocated; - QLIST_HEAD(, DeviceState) children; + QTAILQ_HEAD(ChildrenHead, DeviceState) children; QLIST_ENTRY(BusState) sibling; }; @@ -224,6 +224,7 @@ extern PropertyInfo qdev_prop_uint16; extern PropertyInfo qdev_prop_uint32; extern PropertyInfo qdev_prop_int32; extern PropertyInfo qdev_prop_uint64; +extern PropertyInfo qdev_prop_hex8; extern PropertyInfo qdev_prop_hex32; extern PropertyInfo qdev_prop_hex64; extern PropertyInfo qdev_prop_string; @@ -267,6 +268,8 @@ extern PropertyInfo qdev_prop_pci_devfn; DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_int32, int32_t) #define DEFINE_PROP_UINT64(_n, _s, _f, _d) \ DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_uint64, uint64_t) +#define DEFINE_PROP_HEX8(_n, _s, _f, _d) \ + DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_hex8, uint8_t) #define DEFINE_PROP_HEX32(_n, _s, _f, _d) \ DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_hex32, uint32_t) #define DEFINE_PROP_HEX64(_n, _s, _f, _d) \ diff --git a/hw/qxl-render.c b/hw/qxl-render.c index c290739de0..2c51ba9806 100644 --- a/hw/qxl-render.c +++ b/hw/qxl-render.c @@ -28,16 +28,16 @@ static void qxl_flip(PCIQXLDevice *qxl, QXLRect *rect) int len, i; src += (qxl->guest_primary.surface.height - rect->top - 1) * - qxl->guest_primary.stride; - dst += rect->top * qxl->guest_primary.stride; + qxl->guest_primary.abs_stride; + dst += rect->top * qxl->guest_primary.abs_stride; src += rect->left * qxl->guest_primary.bytes_pp; dst += rect->left * qxl->guest_primary.bytes_pp; len = (rect->right - rect->left) * qxl->guest_primary.bytes_pp; for (i = rect->top; i < rect->bottom; i++) { memcpy(dst, src, len); - dst += qxl->guest_primary.stride; - src -= qxl->guest_primary.stride; + dst += qxl->guest_primary.abs_stride; + src -= qxl->guest_primary.abs_stride; } } @@ -45,7 +45,8 @@ void qxl_render_resize(PCIQXLDevice *qxl) { QXLSurfaceCreate *sc = &qxl->guest_primary.surface; - qxl->guest_primary.stride = sc->stride; + qxl->guest_primary.qxl_stride = sc->stride; + qxl->guest_primary.abs_stride = abs(sc->stride); qxl->guest_primary.resized++; switch (sc->format) { case SPICE_SURFACE_FMT_16_555: @@ -75,7 +76,14 @@ void qxl_render_update(PCIQXLDevice *qxl) VGACommonState *vga = &qxl->vga; QXLRect dirty[32], update; void *ptr; - int i; + int i, redraw = 0; + + if (!is_buffer_shared(vga->ds->surface)) { + dprint(qxl, 1, "%s: restoring shared displaysurface\n", __func__); + qxl->guest_primary.resized++; + qxl->guest_primary.commands++; + redraw = 1; + } if (qxl->guest_primary.resized) { qxl->guest_primary.resized = 0; @@ -87,11 +95,11 @@ void qxl_render_update(PCIQXLDevice *qxl) qemu_free_displaysurface(vga->ds); qxl->guest_primary.data = memory_region_get_ram_ptr(&qxl->vga.vram); - if (qxl->guest_primary.stride < 0) { + if (qxl->guest_primary.qxl_stride < 0) { /* spice surface is upside down -> need extra buffer to flip */ - qxl->guest_primary.stride = -qxl->guest_primary.stride; - qxl->guest_primary.flipped = g_malloc(qxl->guest_primary.surface.width * - qxl->guest_primary.stride); + qxl->guest_primary.flipped = + g_malloc(qxl->guest_primary.surface.width * + qxl->guest_primary.abs_stride); ptr = qxl->guest_primary.flipped; } else { ptr = qxl->guest_primary.data; @@ -100,7 +108,7 @@ void qxl_render_update(PCIQXLDevice *qxl) __FUNCTION__, qxl->guest_primary.surface.width, qxl->guest_primary.surface.height, - qxl->guest_primary.stride, + qxl->guest_primary.qxl_stride, qxl->guest_primary.bytes_pp, qxl->guest_primary.bits_pp, qxl->guest_primary.flipped ? "yes" : "no"); @@ -108,7 +116,7 @@ void qxl_render_update(PCIQXLDevice *qxl) qemu_create_displaysurface_from(qxl->guest_primary.surface.width, qxl->guest_primary.surface.height, qxl->guest_primary.bits_pp, - qxl->guest_primary.stride, + qxl->guest_primary.abs_stride, ptr); dpy_resize(vga->ds); } @@ -126,6 +134,10 @@ void qxl_render_update(PCIQXLDevice *qxl) memset(dirty, 0, sizeof(dirty)); qxl_spice_update_area(qxl, 0, &update, dirty, ARRAY_SIZE(dirty), 1, QXL_SYNC); + if (redraw) { + memset(dirty, 0, sizeof(dirty)); + dirty[0] = update; + } for (i = 0; i < ARRAY_SIZE(dirty); i++) { if (qemu_spice_rect_is_empty(dirty+i)) { diff --git a/hw/qxl.c b/hw/qxl.c index 03848edb75..84ffd45c0c 100644 --- a/hw/qxl.c +++ b/hw/qxl.c @@ -18,8 +18,6 @@ * along with this program; if not, see <http://www.gnu.org/licenses/>. */ -#include <pthread.h> - #include "qemu-common.h" #include "qemu-timer.h" #include "qemu-queue.h" @@ -238,6 +236,9 @@ void qxl_spice_reset_image_cache(PCIQXLDevice *qxl) void qxl_spice_reset_cursor(PCIQXLDevice *qxl) { qxl->ssd.worker->reset_cursor(qxl->ssd.worker); + qemu_mutex_lock(&qxl->track_lock); + qxl->guest_cursor = 0; + qemu_mutex_unlock(&qxl->track_lock); } @@ -330,6 +331,7 @@ static void init_qxl_ram(PCIQXLDevice *d) d->ram->magic = cpu_to_le32(QXL_RAM_MAGIC); d->ram->int_pending = cpu_to_le32(0); d->ram->int_mask = cpu_to_le32(0); + d->ram->update_surface = 0; SPICE_RING_INIT(&d->ram->cmd_ring); SPICE_RING_INIT(&d->ram->cursor_ring); SPICE_RING_INIT(&d->ram->release_ring); @@ -402,7 +404,9 @@ static void qxl_track_command(PCIQXLDevice *qxl, struct QXLCommandExt *ext) { QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id); if (cmd->type == QXL_CURSOR_SET) { + qemu_mutex_lock(&qxl->track_lock); qxl->guest_cursor = ext->cmd.data; + qemu_mutex_unlock(&qxl->track_lock); } break; } @@ -1067,6 +1071,7 @@ static int qxl_destroy_primary(PCIQXLDevice *d, qxl_async_io async) d->mode = QXL_MODE_UNDEFINED; qemu_spice_destroy_primary_surface(&d->ssd, 0, async); + qxl_spice_reset_cursor(d); return 1; } @@ -1215,10 +1220,6 @@ async_common: if (!SPICE_RING_IS_EMPTY(&d->ram->release_ring)) { break; } - pthread_yield(); - if (!SPICE_RING_IS_EMPTY(&d->ram->release_ring)) { - break; - } d->oom_running = 1; qxl_spice_oom(d); d->oom_running = 0; @@ -1372,7 +1373,7 @@ static void qxl_send_events(PCIQXLDevice *d, uint32_t events) if ((old_pending & le_events) == le_events) { return; } - if (pthread_self() == d->main) { + if (qemu_thread_is_self(&d->main)) { qxl_update_irq(d); } else { if (write(d->pipe[1], d, 1) != 1) { @@ -1391,7 +1392,7 @@ static void init_pipe_signaling(PCIQXLDevice *d) fcntl(d->pipe[1], F_SETFL, O_NONBLOCK); fcntl(d->pipe[0], F_SETOWN, getpid()); - d->main = pthread_self(); + qemu_thread_get_self(&d->main); qemu_set_fd_handler(d->pipe[0], pipe_read, NULL, d); } @@ -1662,12 +1663,25 @@ static int qxl_pre_load(void *opaque) return 0; } +static void qxl_create_memslots(PCIQXLDevice *d) +{ + int i; + + for (i = 0; i < NUM_MEMSLOTS; i++) { + if (!d->guest_slots[i].active) { + continue; + } + dprint(d, 1, "%s: restoring guest slot %d\n", __func__, i); + qxl_add_memslot(d, i, 0, QXL_SYNC); + } +} + static int qxl_post_load(void *opaque, int version) { PCIQXLDevice* d = opaque; uint8_t *ram_start = d->vga.vram_ptr; QXLCommandExt *cmds; - int in, out, i, newmode; + int in, out, newmode; dprint(d, 1, "%s: start\n", __FUNCTION__); @@ -1684,19 +1698,16 @@ static int qxl_post_load(void *opaque, int version) qxl_mode_to_string(d->mode)); newmode = d->mode; d->mode = QXL_MODE_UNDEFINED; + switch (newmode) { case QXL_MODE_UNDEFINED: break; case QXL_MODE_VGA: + qxl_create_memslots(d); qxl_enter_vga_mode(d); break; case QXL_MODE_NATIVE: - for (i = 0; i < NUM_MEMSLOTS; i++) { - if (!d->guest_slots[i].active) { - continue; - } - qxl_add_memslot(d, i, 0, QXL_SYNC); - } + qxl_create_memslots(d); qxl_create_guest_primary(d, 1, QXL_SYNC); /* replay surface-create and cursor-set commands */ @@ -1710,15 +1721,19 @@ static int qxl_post_load(void *opaque, int version) cmds[out].group_id = MEMSLOT_GROUP_GUEST; out++; } - cmds[out].cmd.data = d->guest_cursor; - cmds[out].cmd.type = QXL_CMD_CURSOR; - cmds[out].group_id = MEMSLOT_GROUP_GUEST; - out++; + if (d->guest_cursor) { + cmds[out].cmd.data = d->guest_cursor; + cmds[out].cmd.type = QXL_CMD_CURSOR; + cmds[out].group_id = MEMSLOT_GROUP_GUEST; + out++; + } qxl_spice_loadvm_commands(d, cmds, out); g_free(cmds); break; case QXL_MODE_COMPAT: + /* note: no need to call qxl_create_memslots, qxl_set_mode + * creates the mem slot. */ qxl_set_mode(d, d->shadow_rom.mode, 1); break; } @@ -1787,6 +1802,19 @@ 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, vram_size, + 64 * 1024 * 1024), + DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision, + QXL_DEFAULT_REVISION), + DEFINE_PROP_UINT32("debug", PCIQXLDevice, debug, 0), + DEFINE_PROP_UINT32("guestdebug", PCIQXLDevice, guestdebug, 0), + DEFINE_PROP_UINT32("cmdlog", PCIQXLDevice, cmdlog, 0), + DEFINE_PROP_END_OF_LIST(), +}; + static PCIDeviceInfo qxl_info_primary = { .qdev.name = "qxl-vga", .qdev.desc = "Spice QXL GPU (primary, vga compatible)", @@ -1799,18 +1827,7 @@ static PCIDeviceInfo qxl_info_primary = { .vendor_id = REDHAT_PCI_VENDOR_ID, .device_id = QXL_DEVICE_ID_STABLE, .class_id = PCI_CLASS_DISPLAY_VGA, - .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, - 64 * 1024 * 1024), - DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram_size, - 64 * 1024 * 1024), - DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision, - QXL_DEFAULT_REVISION), - DEFINE_PROP_UINT32("debug", PCIQXLDevice, debug, 0), - DEFINE_PROP_UINT32("guestdebug", PCIQXLDevice, guestdebug, 0), - DEFINE_PROP_UINT32("cmdlog", PCIQXLDevice, cmdlog, 0), - DEFINE_PROP_END_OF_LIST(), - } + .qdev.props = qxl_properties, }; static PCIDeviceInfo qxl_info_secondary = { @@ -1823,18 +1840,7 @@ static PCIDeviceInfo qxl_info_secondary = { .vendor_id = REDHAT_PCI_VENDOR_ID, .device_id = QXL_DEVICE_ID_STABLE, .class_id = PCI_CLASS_DISPLAY_OTHER, - .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, - 64 * 1024 * 1024), - DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram_size, - 64 * 1024 * 1024), - DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision, - QXL_DEFAULT_REVISION), - DEFINE_PROP_UINT32("debug", PCIQXLDevice, debug, 0), - DEFINE_PROP_UINT32("guestdebug", PCIQXLDevice, guestdebug, 0), - DEFINE_PROP_UINT32("cmdlog", PCIQXLDevice, cmdlog, 0), - DEFINE_PROP_END_OF_LIST(), - } + .qdev.props = qxl_properties, }; static void qxl_register(void) diff --git a/hw/qxl.h b/hw/qxl.h index 868db813f9..766aa6d68e 100644 --- a/hw/qxl.h +++ b/hw/qxl.h @@ -4,6 +4,7 @@ #include "hw.h" #include "pci.h" #include "vga_int.h" +#include "qemu-thread.h" #include "ui/qemu-spice.h" #include "ui/spice-display.h" @@ -47,7 +48,8 @@ typedef struct PCIQXLDevice { QXLSurfaceCreate surface; uint32_t commands; uint32_t resized; - int32_t stride; + int32_t qxl_stride; + uint32_t abs_stride; uint32_t bits_pp; uint32_t bytes_pp; uint8_t *data, *flipped; @@ -63,7 +65,7 @@ typedef struct PCIQXLDevice { QemuMutex track_lock; /* thread signaling */ - pthread_t main; + QemuThread main; int pipe[2]; /* ram pci bar */ diff --git a/hw/r2d.c b/hw/r2d.c index 82377a0a10..b65fd427b7 100644 --- a/hw/r2d.c +++ b/hw/r2d.c @@ -82,6 +82,7 @@ typedef struct { /* output pin */ qemu_irq irl; + MemoryRegion iomem; } r2d_fpga_t; enum r2d_fpga_irq { @@ -168,31 +169,25 @@ r2d_fpga_write(void *opaque, target_phys_addr_t addr, uint32_t value) } } -static CPUReadMemoryFunc * const r2d_fpga_readfn[] = { - r2d_fpga_read, - r2d_fpga_read, - NULL, +static const MemoryRegionOps r2d_fpga_ops = { + .old_mmio = { + .read = { r2d_fpga_read, r2d_fpga_read, NULL, }, + .write = { r2d_fpga_write, r2d_fpga_write, NULL, }, + }, + .endianness = DEVICE_NATIVE_ENDIAN, }; -static CPUWriteMemoryFunc * const r2d_fpga_writefn[] = { - r2d_fpga_write, - r2d_fpga_write, - NULL, -}; - -static qemu_irq *r2d_fpga_init(target_phys_addr_t base, qemu_irq irl) +static qemu_irq *r2d_fpga_init(MemoryRegion *sysmem, + target_phys_addr_t base, qemu_irq irl) { - int iomemtype; r2d_fpga_t *s; s = g_malloc0(sizeof(r2d_fpga_t)); s->irl = irl; - iomemtype = cpu_register_io_memory(r2d_fpga_readfn, - r2d_fpga_writefn, s, - DEVICE_NATIVE_ENDIAN); - cpu_register_physical_memory(base, 0x40, iomemtype); + memory_region_init_io(&s->iomem, &r2d_fpga_ops, s, "r2d-fpga", 0x40); + memory_region_add_subregion(sysmem, base, &s->iomem); return qemu_allocate_irqs(r2d_fpga_irq_set, s, NR_IRQS); } @@ -232,7 +227,7 @@ static void r2d_init(ram_addr_t ram_size, CPUState *env; ResetData *reset_info; struct SH7750State *s; - ram_addr_t sdram_addr; + MemoryRegion *sdram = g_new(MemoryRegion, 1); qemu_irq *irq; DriveInfo *dinfo; int i; @@ -252,11 +247,11 @@ static void r2d_init(ram_addr_t ram_size, qemu_register_reset(main_cpu_reset, reset_info); /* Allocate memory space */ - sdram_addr = qemu_ram_alloc(NULL, "r2d.sdram", SDRAM_SIZE); - cpu_register_physical_memory(SDRAM_BASE, SDRAM_SIZE, sdram_addr); + memory_region_init_ram(sdram, NULL, "r2d.sdram", SDRAM_SIZE); + memory_region_add_subregion(address_space_mem, SDRAM_BASE, sdram); /* Register peripherals */ s = sh7750_init(env); - irq = r2d_fpga_init(0x04000000, sh7750_irl(s)); + irq = r2d_fpga_init(address_space_mem, 0x04000000, sh7750_irl(s)); sysbus_create_varargs("sh_pci", 0x1e200000, irq[PCI_INTA], irq[PCI_INTB], irq[PCI_INTC], irq[PCI_INTD], NULL); diff --git a/hw/realview.c b/hw/realview.c index 11ffb8a824..9a8e63c8f5 100644 --- a/hw/realview.c +++ b/hw/realview.c @@ -18,17 +18,20 @@ #include "boards.h" #include "bitbang_i2c.h" #include "blockdev.h" +#include "exec-memory.h" #define SMP_BOOT_ADDR 0xe0000000 typedef struct { SysBusDevice busdev; + MemoryRegion iomem; bitbang_i2c_interface *bitbang; int out; int in; } RealViewI2CState; -static uint32_t realview_i2c_read(void *opaque, target_phys_addr_t offset) +static uint64_t realview_i2c_read(void *opaque, target_phys_addr_t offset, + unsigned size) { RealViewI2CState *s = (RealViewI2CState *)opaque; @@ -41,7 +44,7 @@ static uint32_t realview_i2c_read(void *opaque, target_phys_addr_t offset) } static void realview_i2c_write(void *opaque, target_phys_addr_t offset, - uint32_t value) + uint64_t value, unsigned size) { RealViewI2CState *s = (RealViewI2CState *)opaque; @@ -59,30 +62,22 @@ static void realview_i2c_write(void *opaque, target_phys_addr_t offset, s->in = bitbang_i2c_set(s->bitbang, BITBANG_I2C_SDA, (s->out & 2) != 0); } -static CPUReadMemoryFunc * const realview_i2c_readfn[] = { - realview_i2c_read, - realview_i2c_read, - realview_i2c_read -}; - -static CPUWriteMemoryFunc * const realview_i2c_writefn[] = { - realview_i2c_write, - realview_i2c_write, - realview_i2c_write +static const MemoryRegionOps realview_i2c_ops = { + .read = realview_i2c_read, + .write = realview_i2c_write, + .endianness = DEVICE_NATIVE_ENDIAN, }; static int realview_i2c_init(SysBusDevice *dev) { RealViewI2CState *s = FROM_SYSBUS(RealViewI2CState, dev); i2c_bus *bus; - int iomemtype; bus = i2c_init_bus(&dev->qdev, "i2c"); s->bitbang = bitbang_i2c_init(bus); - iomemtype = cpu_register_io_memory(realview_i2c_readfn, - realview_i2c_writefn, s, - DEVICE_NATIVE_ENDIAN); - sysbus_init_mmio(dev, 0x1000, iomemtype); + memory_region_init_io(&s->iomem, &realview_i2c_ops, s, + "realview-i2c", 0x1000); + sysbus_init_mmio_region(dev, &s->iomem); return 0; } @@ -125,8 +120,12 @@ static void realview_init(ram_addr_t ram_size, enum realview_board_type board_type) { CPUState *env = NULL; - ram_addr_t ram_offset; - DeviceState *dev, *sysctl, *gpio2; + MemoryRegion *sysmem = get_system_memory(); + MemoryRegion *ram_lo = g_new(MemoryRegion, 1); + MemoryRegion *ram_hi = g_new(MemoryRegion, 1); + MemoryRegion *ram_alias = g_new(MemoryRegion, 1); + MemoryRegion *ram_hack = g_new(MemoryRegion, 1); + DeviceState *dev, *sysctl, *gpio2, *pl041; SysBusDevice *busdev; qemu_irq *irqp; qemu_irq pic[64]; @@ -184,21 +183,21 @@ static void realview_init(ram_addr_t ram_size, /* Core tile RAM. */ low_ram_size = ram_size - 0x20000000; ram_size = 0x20000000; - ram_offset = qemu_ram_alloc(NULL, "realview.lowmem", low_ram_size); - cpu_register_physical_memory(0x20000000, low_ram_size, - ram_offset | IO_MEM_RAM); + memory_region_init_ram(ram_lo, NULL, "realview.lowmem", low_ram_size); + memory_region_add_subregion(sysmem, 0x20000000, ram_lo); } - ram_offset = qemu_ram_alloc(NULL, "realview.highmem", ram_size); + memory_region_init_ram(ram_hi, NULL, "realview.highmem", ram_size); low_ram_size = ram_size; if (low_ram_size > 0x10000000) low_ram_size = 0x10000000; /* SDRAM at address zero. */ - cpu_register_physical_memory(0, low_ram_size, ram_offset | IO_MEM_RAM); + memory_region_init_alias(ram_alias, "realview.alias", + ram_hi, 0, low_ram_size); + memory_region_add_subregion(sysmem, 0, ram_alias); if (is_pb) { /* And again at a high address. */ - cpu_register_physical_memory(0x70000000, ram_size, - ram_offset | IO_MEM_RAM); + memory_region_add_subregion(sysmem, 0x70000000, ram_hi); } else { ram_size = low_ram_size; } @@ -233,6 +232,12 @@ static void realview_init(ram_addr_t ram_size, pic[n] = qdev_get_gpio_in(dev, n); } + pl041 = qdev_create(NULL, "pl041"); + qdev_prop_set_uint32(pl041, "nc_fifo_depth", 512); + qdev_init_nofail(pl041); + sysbus_mmio_map(sysbus_from_qdev(pl041), 0, 0x10004000); + sysbus_connect_irq(sysbus_from_qdev(pl041), 0, pic[19]); + sysbus_create_simple("pl050_keyboard", 0x10006000, pic[20]); sysbus_create_simple("pl050_mouse", 0x10007000, pic[21]); @@ -372,9 +377,8 @@ static void realview_init(ram_addr_t ram_size, startup code. I guess this works on real hardware because the BootROM happens to be in ROM/flash or in memory that isn't clobbered until after Linux boots the secondary CPUs. */ - ram_offset = qemu_ram_alloc(NULL, "realview.hack", 0x1000); - cpu_register_physical_memory(SMP_BOOT_ADDR, 0x1000, - ram_offset | IO_MEM_RAM); + memory_region_init_ram(ram_hack, NULL, "realview.hack", 0x1000); + memory_region_add_subregion(sysmem, SMP_BOOT_ADDR, ram_hack); realview_binfo.ram_size = ram_size; realview_binfo.kernel_filename = kernel_filename; diff --git a/hw/rtl8139.c b/hw/rtl8139.c index 37539508c5..4c379932e3 100644 --- a/hw/rtl8139.c +++ b/hw/rtl8139.c @@ -53,6 +53,7 @@ #include "hw.h" #include "pci.h" +#include "dma.h" #include "qemu-timer.h" #include "net.h" #include "loader.h" @@ -427,9 +428,6 @@ typedef struct RTL8139TallyCounters /* Clears all tally counters */ static void RTL8139TallyCounters_clear(RTL8139TallyCounters* counters); -/* Writes tally counters to specified physical memory address */ -static void RTL8139TallyCounters_physical_memory_write(target_phys_addr_t tc_addr, RTL8139TallyCounters* counters); - typedef struct RTL8139State { PCIDevice dev; uint8_t phys[8]; /* mac address */ @@ -512,6 +510,9 @@ typedef struct RTL8139State { int rtl8139_mmio_io_addr_dummy; } RTL8139State; +/* Writes tally counters to memory via DMA */ +static void RTL8139TallyCounters_dma_write(RTL8139State *s, dma_addr_t tc_addr); + static void rtl8139_set_next_tctr_time(RTL8139State *s, int64_t current_time); static void prom9346_decode_command(EEprom9346 *eeprom, uint8_t command) @@ -773,15 +774,15 @@ static void rtl8139_write_buffer(RTL8139State *s, const void *buf, int size) if (size > wrapped) { - cpu_physical_memory_write( s->RxBuf + s->RxBufAddr, - buf, size-wrapped ); + pci_dma_write(&s->dev, s->RxBuf + s->RxBufAddr, + buf, size-wrapped); } /* reset buffer pointer */ s->RxBufAddr = 0; - cpu_physical_memory_write( s->RxBuf + s->RxBufAddr, - buf + (size-wrapped), wrapped ); + pci_dma_write(&s->dev, s->RxBuf + s->RxBufAddr, + buf + (size-wrapped), wrapped); s->RxBufAddr = wrapped; @@ -790,13 +791,13 @@ static void rtl8139_write_buffer(RTL8139State *s, const void *buf, int size) } /* non-wrapping path or overwrapping enabled */ - cpu_physical_memory_write( s->RxBuf + s->RxBufAddr, buf, size ); + pci_dma_write(&s->dev, s->RxBuf + s->RxBufAddr, buf, size); s->RxBufAddr += size; } #define MIN_BUF_SIZE 60 -static inline target_phys_addr_t rtl8139_addr64(uint32_t low, uint32_t high) +static inline dma_addr_t rtl8139_addr64(uint32_t low, uint32_t high) { #if TARGET_PHYS_ADDR_BITS > 32 return low | ((target_phys_addr_t)high << 32); @@ -979,24 +980,24 @@ static ssize_t rtl8139_do_receive(VLANClientState *nc, const uint8_t *buf, size_ /* w3 high 32bit of Rx buffer ptr */ int descriptor = s->currCPlusRxDesc; - target_phys_addr_t cplus_rx_ring_desc; + dma_addr_t cplus_rx_ring_desc; cplus_rx_ring_desc = rtl8139_addr64(s->RxRingAddrLO, s->RxRingAddrHI); cplus_rx_ring_desc += 16 * descriptor; DPRINTF("+++ C+ mode reading RX descriptor %d from host memory at " - "%08x %08x = "TARGET_FMT_plx"\n", descriptor, s->RxRingAddrHI, + "%08x %08x = "DMA_ADDR_FMT"\n", descriptor, s->RxRingAddrHI, s->RxRingAddrLO, cplus_rx_ring_desc); uint32_t val, rxdw0,rxdw1,rxbufLO,rxbufHI; - cpu_physical_memory_read(cplus_rx_ring_desc, (uint8_t *)&val, 4); + pci_dma_read(&s->dev, cplus_rx_ring_desc, (uint8_t *)&val, 4); rxdw0 = le32_to_cpu(val); - cpu_physical_memory_read(cplus_rx_ring_desc+4, (uint8_t *)&val, 4); + pci_dma_read(&s->dev, cplus_rx_ring_desc+4, (uint8_t *)&val, 4); rxdw1 = le32_to_cpu(val); - cpu_physical_memory_read(cplus_rx_ring_desc+8, (uint8_t *)&val, 4); + pci_dma_read(&s->dev, cplus_rx_ring_desc+8, (uint8_t *)&val, 4); rxbufLO = le32_to_cpu(val); - cpu_physical_memory_read(cplus_rx_ring_desc+12, (uint8_t *)&val, 4); + pci_dma_read(&s->dev, cplus_rx_ring_desc+12, (uint8_t *)&val, 4); rxbufHI = le32_to_cpu(val); DPRINTF("+++ C+ mode RX descriptor %d %08x %08x %08x %08x\n", @@ -1060,16 +1061,16 @@ static ssize_t rtl8139_do_receive(VLANClientState *nc, const uint8_t *buf, size_ return size_; } - target_phys_addr_t rx_addr = rtl8139_addr64(rxbufLO, rxbufHI); + dma_addr_t rx_addr = rtl8139_addr64(rxbufLO, rxbufHI); /* receive/copy to target memory */ if (dot1q_buf) { - cpu_physical_memory_write(rx_addr, buf, 2 * ETHER_ADDR_LEN); - cpu_physical_memory_write(rx_addr + 2 * ETHER_ADDR_LEN, - buf + 2 * ETHER_ADDR_LEN + VLAN_HLEN, - size - 2 * ETHER_ADDR_LEN); + pci_dma_write(&s->dev, rx_addr, buf, 2 * ETHER_ADDR_LEN); + pci_dma_write(&s->dev, rx_addr + 2 * ETHER_ADDR_LEN, + buf + 2 * ETHER_ADDR_LEN + VLAN_HLEN, + size - 2 * ETHER_ADDR_LEN); } else { - cpu_physical_memory_write(rx_addr, buf, size); + pci_dma_write(&s->dev, rx_addr, buf, size); } if (s->CpCmd & CPlusRxChkSum) @@ -1079,7 +1080,7 @@ static ssize_t rtl8139_do_receive(VLANClientState *nc, const uint8_t *buf, size_ /* write checksum */ val = cpu_to_le32(crc32(0, buf, size_)); - cpu_physical_memory_write( rx_addr+size, (uint8_t *)&val, 4); + pci_dma_write(&s->dev, rx_addr+size, (uint8_t *)&val, 4); /* first segment of received packet flag */ #define CP_RX_STATUS_FS (1<<29) @@ -1125,9 +1126,9 @@ static ssize_t rtl8139_do_receive(VLANClientState *nc, const uint8_t *buf, size_ /* update ring data */ val = cpu_to_le32(rxdw0); - cpu_physical_memory_write(cplus_rx_ring_desc, (uint8_t *)&val, 4); + pci_dma_write(&s->dev, cplus_rx_ring_desc, (uint8_t *)&val, 4); val = cpu_to_le32(rxdw1); - cpu_physical_memory_write(cplus_rx_ring_desc+4, (uint8_t *)&val, 4); + pci_dma_write(&s->dev, cplus_rx_ring_desc+4, (uint8_t *)&val, 4); /* update tally counter */ ++s->tally_counters.RxOk; @@ -1307,50 +1308,51 @@ static void RTL8139TallyCounters_clear(RTL8139TallyCounters* counters) counters->TxUndrn = 0; } -static void RTL8139TallyCounters_physical_memory_write(target_phys_addr_t tc_addr, RTL8139TallyCounters* tally_counters) +static void RTL8139TallyCounters_dma_write(RTL8139State *s, dma_addr_t tc_addr) { + RTL8139TallyCounters *tally_counters = &s->tally_counters; uint16_t val16; uint32_t val32; uint64_t val64; val64 = cpu_to_le64(tally_counters->TxOk); - cpu_physical_memory_write(tc_addr + 0, (uint8_t *)&val64, 8); + pci_dma_write(&s->dev, tc_addr + 0, (uint8_t *)&val64, 8); val64 = cpu_to_le64(tally_counters->RxOk); - cpu_physical_memory_write(tc_addr + 8, (uint8_t *)&val64, 8); + pci_dma_write(&s->dev, tc_addr + 8, (uint8_t *)&val64, 8); val64 = cpu_to_le64(tally_counters->TxERR); - cpu_physical_memory_write(tc_addr + 16, (uint8_t *)&val64, 8); + pci_dma_write(&s->dev, tc_addr + 16, (uint8_t *)&val64, 8); val32 = cpu_to_le32(tally_counters->RxERR); - cpu_physical_memory_write(tc_addr + 24, (uint8_t *)&val32, 4); + pci_dma_write(&s->dev, tc_addr + 24, (uint8_t *)&val32, 4); val16 = cpu_to_le16(tally_counters->MissPkt); - cpu_physical_memory_write(tc_addr + 28, (uint8_t *)&val16, 2); + pci_dma_write(&s->dev, tc_addr + 28, (uint8_t *)&val16, 2); val16 = cpu_to_le16(tally_counters->FAE); - cpu_physical_memory_write(tc_addr + 30, (uint8_t *)&val16, 2); + pci_dma_write(&s->dev, tc_addr + 30, (uint8_t *)&val16, 2); val32 = cpu_to_le32(tally_counters->Tx1Col); - cpu_physical_memory_write(tc_addr + 32, (uint8_t *)&val32, 4); + pci_dma_write(&s->dev, tc_addr + 32, (uint8_t *)&val32, 4); val32 = cpu_to_le32(tally_counters->TxMCol); - cpu_physical_memory_write(tc_addr + 36, (uint8_t *)&val32, 4); + pci_dma_write(&s->dev, tc_addr + 36, (uint8_t *)&val32, 4); val64 = cpu_to_le64(tally_counters->RxOkPhy); - cpu_physical_memory_write(tc_addr + 40, (uint8_t *)&val64, 8); + pci_dma_write(&s->dev, tc_addr + 40, (uint8_t *)&val64, 8); val64 = cpu_to_le64(tally_counters->RxOkBrd); - cpu_physical_memory_write(tc_addr + 48, (uint8_t *)&val64, 8); + pci_dma_write(&s->dev, tc_addr + 48, (uint8_t *)&val64, 8); val32 = cpu_to_le32(tally_counters->RxOkMul); - cpu_physical_memory_write(tc_addr + 56, (uint8_t *)&val32, 4); + pci_dma_write(&s->dev, tc_addr + 56, (uint8_t *)&val32, 4); val16 = cpu_to_le16(tally_counters->TxAbt); - cpu_physical_memory_write(tc_addr + 60, (uint8_t *)&val16, 2); + pci_dma_write(&s->dev, tc_addr + 60, (uint8_t *)&val16, 2); val16 = cpu_to_le16(tally_counters->TxUndrn); - cpu_physical_memory_write(tc_addr + 62, (uint8_t *)&val16, 2); + pci_dma_write(&s->dev, tc_addr + 62, (uint8_t *)&val16, 2); } /* Loads values of tally counters from VM state file */ @@ -1842,7 +1844,7 @@ static int rtl8139_transmit_one(RTL8139State *s, int descriptor) DPRINTF("+++ transmit reading %d bytes from host memory at 0x%08x\n", txsize, s->TxAddr[descriptor]); - cpu_physical_memory_read(s->TxAddr[descriptor], txbuffer, txsize); + pci_dma_read(&s->dev, s->TxAddr[descriptor], txbuffer, txsize); /* Mark descriptor as transferred */ s->TxStatus[descriptor] |= TxHostOwns; @@ -1963,25 +1965,24 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) int descriptor = s->currCPlusTxDesc; - target_phys_addr_t cplus_tx_ring_desc = - rtl8139_addr64(s->TxAddr[0], s->TxAddr[1]); + dma_addr_t cplus_tx_ring_desc = rtl8139_addr64(s->TxAddr[0], s->TxAddr[1]); /* Normal priority ring */ cplus_tx_ring_desc += 16 * descriptor; DPRINTF("+++ C+ mode reading TX descriptor %d from host memory at " - "%08x0x%08x = 0x"TARGET_FMT_plx"\n", descriptor, s->TxAddr[1], + "%08x0x%08x = 0x"DMA_ADDR_FMT"\n", descriptor, s->TxAddr[1], s->TxAddr[0], cplus_tx_ring_desc); uint32_t val, txdw0,txdw1,txbufLO,txbufHI; - cpu_physical_memory_read(cplus_tx_ring_desc, (uint8_t *)&val, 4); + pci_dma_read(&s->dev, cplus_tx_ring_desc, (uint8_t *)&val, 4); txdw0 = le32_to_cpu(val); - cpu_physical_memory_read(cplus_tx_ring_desc+4, (uint8_t *)&val, 4); + pci_dma_read(&s->dev, cplus_tx_ring_desc+4, (uint8_t *)&val, 4); txdw1 = le32_to_cpu(val); - cpu_physical_memory_read(cplus_tx_ring_desc+8, (uint8_t *)&val, 4); + pci_dma_read(&s->dev, cplus_tx_ring_desc+8, (uint8_t *)&val, 4); txbufLO = le32_to_cpu(val); - cpu_physical_memory_read(cplus_tx_ring_desc+12, (uint8_t *)&val, 4); + pci_dma_read(&s->dev, cplus_tx_ring_desc+12, (uint8_t *)&val, 4); txbufHI = le32_to_cpu(val); DPRINTF("+++ C+ mode TX descriptor %d %08x %08x %08x %08x\n", descriptor, @@ -2047,7 +2048,7 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) } int txsize = txdw0 & CP_TX_BUFFER_SIZE_MASK; - target_phys_addr_t tx_addr = rtl8139_addr64(txbufLO, txbufHI); + dma_addr_t tx_addr = rtl8139_addr64(txbufLO, txbufHI); /* make sure we have enough space to assemble the packet */ if (!s->cplus_txbuffer) @@ -2086,10 +2087,11 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) /* append more data to the packet */ DPRINTF("+++ C+ mode transmit reading %d bytes from host memory at " - TARGET_FMT_plx" to offset %d\n", txsize, tx_addr, - s->cplus_txbuffer_offset); + DMA_ADDR_FMT" to offset %d\n", txsize, tx_addr, + s->cplus_txbuffer_offset); - cpu_physical_memory_read(tx_addr, s->cplus_txbuffer + s->cplus_txbuffer_offset, txsize); + pci_dma_read(&s->dev, tx_addr, + s->cplus_txbuffer + s->cplus_txbuffer_offset, txsize); s->cplus_txbuffer_offset += txsize; /* seek to next Rx descriptor */ @@ -2116,7 +2118,7 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) /* update ring data */ val = cpu_to_le32(txdw0); - cpu_physical_memory_write(cplus_tx_ring_desc, (uint8_t *)&val, 4); + pci_dma_write(&s->dev, cplus_tx_ring_desc, (uint8_t *)&val, 4); /* Now decide if descriptor being processed is holding the last segment of packet */ if (txdw0 & CP_TX_LS) @@ -2475,7 +2477,7 @@ static void rtl8139_TxStatus_write(RTL8139State *s, uint32_t txRegOffset, uint32 target_phys_addr_t tc_addr = rtl8139_addr64(s->TxStatus[0] & ~0x3f, s->TxStatus[1]); /* dump tally counters to specified memory location */ - RTL8139TallyCounters_physical_memory_write( tc_addr, &s->tally_counters); + RTL8139TallyCounters_dma_write(s, tc_addr); /* mark dump completed */ s->TxStatus[0] &= ~0x8; diff --git a/hw/s390-virtio-bus.c b/hw/s390-virtio-bus.c index e2f3e32aca..0ce6406b6d 100644 --- a/hw/s390-virtio-bus.c +++ b/hw/s390-virtio-bus.c @@ -274,7 +274,7 @@ VirtIOS390Device *s390_virtio_bus_find_vring(VirtIOS390Bus *bus, DeviceState *dev; int i; - QLIST_FOREACH(dev, &bus->bus.children, sibling) { + QTAILQ_FOREACH(dev, &bus->bus.children, sibling) { _dev = (VirtIOS390Device *)dev; for(i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) { if (!virtio_queue_get_addr(_dev->vdev, i)) @@ -297,7 +297,7 @@ VirtIOS390Device *s390_virtio_bus_find_mem(VirtIOS390Bus *bus, ram_addr_t mem) VirtIOS390Device *_dev; DeviceState *dev; - QLIST_FOREACH(dev, &bus->bus.children, sibling) { + QTAILQ_FOREACH(dev, &bus->bus.children, sibling) { _dev = (VirtIOS390Device *)dev; if (_dev->dev_offs == mem) { return _dev; diff --git a/hw/s390-virtio.c b/hw/s390-virtio.c index acbf02604b..60c66e92c4 100644 --- a/hw/s390-virtio.c +++ b/hw/s390-virtio.c @@ -29,6 +29,7 @@ #include "hw/virtio.h" #include "hw/sysbus.h" #include "kvm.h" +#include "exec-memory.h" #include "hw/s390-virtio-bus.h" @@ -62,17 +63,6 @@ static VirtIOS390Bus *s390_bus; static CPUState **ipi_states; -void irq_info(Monitor *mon); -void pic_info(Monitor *mon); - -void irq_info(Monitor *mon) -{ -} - -void pic_info(Monitor *mon) -{ -} - CPUState *s390_cpu_addr2state(uint16_t cpu_addr) { if (cpu_addr >= smp_cpus) { @@ -139,7 +129,8 @@ static void s390_init(ram_addr_t my_ram_size, const char *cpu_model) { CPUState *env = NULL; - ram_addr_t ram_addr; + MemoryRegion *sysmem = get_system_memory(); + MemoryRegion *ram = g_new(MemoryRegion, 1); ram_addr_t kernel_size = 0; ram_addr_t initrd_offset; ram_addr_t initrd_size = 0; @@ -161,8 +152,8 @@ static void s390_init(ram_addr_t my_ram_size, s390_bus = s390_virtio_bus_init(&my_ram_size); /* allocate RAM */ - ram_addr = qemu_ram_alloc(NULL, "s390.ram", my_ram_size); - cpu_register_physical_memory(0, my_ram_size, ram_addr); + memory_region_init_ram(ram, NULL, "s390.ram", my_ram_size); + memory_region_add_subregion(sysmem, 0, ram); /* allocate storage keys */ storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE); diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index aca65a16df..e6ebbd594e 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -8,6 +8,7 @@ static char *scsibus_get_fw_dev_path(DeviceState *dev); static int scsi_req_parse(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf); +static void scsi_req_dequeue(SCSIRequest *req); static int scsi_build_sense(uint8_t *in_buf, int in_len, uint8_t *buf, int len, bool fixed); @@ -16,53 +17,123 @@ static struct BusInfo scsi_bus_info = { .size = sizeof(SCSIBus), .get_fw_dev_path = scsibus_get_fw_dev_path, .props = (Property[]) { + DEFINE_PROP_UINT32("channel", SCSIDevice, channel, 0), DEFINE_PROP_UINT32("scsi-id", SCSIDevice, id, -1), - DEFINE_PROP_UINT32("lun", SCSIDevice, lun, 0), + DEFINE_PROP_UINT32("lun", SCSIDevice, lun, -1), DEFINE_PROP_END_OF_LIST(), }, }; static int next_scsi_bus; /* Create a scsi bus, and attach devices to it. */ -void scsi_bus_new(SCSIBus *bus, DeviceState *host, int tcq, int ndev, - const SCSIBusOps *ops) +void scsi_bus_new(SCSIBus *bus, DeviceState *host, const SCSIBusInfo *info) { qbus_create_inplace(&bus->qbus, &scsi_bus_info, host, NULL); bus->busnr = next_scsi_bus++; - bus->tcq = tcq; - bus->ndev = ndev; - bus->ops = ops; + bus->info = info; bus->qbus.allow_hotplug = 1; } +static void scsi_dma_restart_bh(void *opaque) +{ + SCSIDevice *s = opaque; + SCSIRequest *req, *next; + + qemu_bh_delete(s->bh); + s->bh = NULL; + + QTAILQ_FOREACH_SAFE(req, &s->requests, next, next) { + scsi_req_ref(req); + if (req->retry) { + req->retry = false; + switch (req->cmd.mode) { + case SCSI_XFER_FROM_DEV: + case SCSI_XFER_TO_DEV: + scsi_req_continue(req); + break; + case SCSI_XFER_NONE: + scsi_req_dequeue(req); + scsi_req_enqueue(req); + break; + } + } + scsi_req_unref(req); + } +} + +void scsi_req_retry(SCSIRequest *req) +{ + /* No need to save a reference, because scsi_dma_restart_bh just + * looks at the request list. */ + req->retry = true; +} + +static void scsi_dma_restart_cb(void *opaque, int running, RunState state) +{ + SCSIDevice *s = opaque; + + if (!running) { + return; + } + if (!s->bh) { + s->bh = qemu_bh_new(scsi_dma_restart_bh, s); + qemu_bh_schedule(s->bh); + } +} + static int scsi_qdev_init(DeviceState *qdev, DeviceInfo *base) { SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev); SCSIDeviceInfo *info = DO_UPCAST(SCSIDeviceInfo, qdev, base); SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus); + SCSIDevice *d; int rc = -1; - if (dev->id == -1) { - for (dev->id = 0; dev->id < bus->ndev; dev->id++) { - if (bus->devs[dev->id] == NULL) - break; - } + if (dev->channel > bus->info->max_channel) { + error_report("bad scsi channel id: %d", dev->channel); + goto err; } - if (dev->id >= bus->ndev) { + if (dev->id != -1 && dev->id > bus->info->max_target) { error_report("bad scsi device id: %d", dev->id); goto err; } - if (bus->devs[dev->id]) { - qdev_free(&bus->devs[dev->id]->qdev); + if (dev->id == -1) { + int id = -1; + if (dev->lun == -1) { + dev->lun = 0; + } + do { + d = scsi_device_find(bus, dev->channel, ++id, dev->lun); + } while (d && d->lun == dev->lun && id <= bus->info->max_target); + if (id > bus->info->max_target) { + error_report("no free target"); + goto err; + } + dev->id = id; + } else if (dev->lun == -1) { + int lun = -1; + do { + d = scsi_device_find(bus, dev->channel, dev->id, ++lun); + } while (d && d->lun == lun && lun < bus->info->max_lun); + if (lun > bus->info->max_lun) { + error_report("no free lun"); + goto err; + } + dev->lun = lun; + } else { + d = scsi_device_find(bus, dev->channel, dev->id, dev->lun); + if (dev->lun == d->lun && dev != d) { + qdev_free(&d->qdev); + } } - bus->devs[dev->id] = dev; dev->info = info; QTAILQ_INIT(&dev->requests); rc = dev->info->init(dev); - if (rc != 0) { - bus->devs[dev->id] = NULL; + if (rc == 0) { + dev->vmsentry = qemu_add_vm_change_state_handler(scsi_dma_restart_cb, + dev); } err: @@ -72,13 +143,13 @@ err: static int scsi_qdev_exit(DeviceState *qdev) { SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev); - SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus); - assert(bus->devs[dev->id] != NULL); - if (bus->devs[dev->id]->info->destroy) { - bus->devs[dev->id]->info->destroy(bus->devs[dev->id]); + if (dev->vmsentry) { + qemu_del_vm_change_state_handler(dev->vmsentry); + } + if (dev->info->destroy) { + dev->info->destroy(dev); } - bus->devs[dev->id] = NULL; return 0; } @@ -120,7 +191,7 @@ int scsi_bus_legacy_handle_cmdline(SCSIBus *bus) int res = 0, unit; loc_push_none(&loc); - for (unit = 0; unit < bus->ndev; unit++) { + for (unit = 0; unit < bus->info->max_target; unit++) { dinfo = drive_get(IF_SCSI, bus->busnr, unit); if (dinfo == NULL) { continue; @@ -144,7 +215,7 @@ static int32_t scsi_invalid_command(SCSIRequest *req, uint8_t *buf) return 0; } -struct SCSIReqOps reqops_invalid_opcode = { +static const struct SCSIReqOps reqops_invalid_opcode = { .size = sizeof(SCSIRequest), .send_command = scsi_invalid_command }; @@ -162,7 +233,7 @@ static int32_t scsi_unit_attention(SCSIRequest *req, uint8_t *buf) return 0; } -struct SCSIReqOps reqops_unit_attention = { +static const struct SCSIReqOps reqops_unit_attention = { .size = sizeof(SCSIRequest), .send_command = scsi_unit_attention }; @@ -175,7 +246,7 @@ typedef struct SCSITargetReq SCSITargetReq; struct SCSITargetReq { SCSIRequest req; int len; - uint8_t buf[64]; + uint8_t buf[2056]; }; static void store_lun(uint8_t *outbuf, int lun) @@ -190,23 +261,53 @@ static void store_lun(uint8_t *outbuf, int lun) static bool scsi_target_emulate_report_luns(SCSITargetReq *r) { - int len; + DeviceState *qdev; + int i, len, n; + int channel, id; + bool found_lun0; + if (r->req.cmd.xfer < 16) { return false; } if (r->req.cmd.buf[2] > 2) { return false; } - len = MIN(sizeof r->buf, r->req.cmd.xfer); + channel = r->req.dev->channel; + id = r->req.dev->id; + found_lun0 = false; + n = 0; + QTAILQ_FOREACH(qdev, &r->req.bus->qbus.children, sibling) { + SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev); + + if (dev->channel == channel && dev->id == id) { + if (dev->lun == 0) { + found_lun0 = true; + } + n += 8; + } + } + if (!found_lun0) { + n += 8; + } + len = MIN(n + 8, r->req.cmd.xfer & ~7); + if (len > sizeof(r->buf)) { + /* TODO: > 256 LUNs? */ + return false; + } + memset(r->buf, 0, len); - if (r->req.dev->lun != 0) { - r->buf[3] = 16; - r->len = 24; - store_lun(&r->buf[16], r->req.dev->lun); - } else { - r->buf[3] = 8; - r->len = 16; + stl_be_p(&r->buf, n); + i = found_lun0 ? 8 : 16; + QTAILQ_FOREACH(qdev, &r->req.bus->qbus.children, sibling) { + SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev); + + if (dev->channel == channel && dev->id == id) { + store_lun(&r->buf[i], dev->lun); + i += 8; + } } + assert(i == n + 8); + r->len = len; return true; } @@ -265,7 +366,7 @@ static bool scsi_target_emulate_inquiry(SCSITargetReq *r) r->buf[2] = 5; /* Version */ r->buf[3] = 2 | 0x10; /* HiSup, response data format */ r->buf[4] = r->len - 5; /* Additional Length = (Len - 1) - 4 */ - r->buf[7] = 0x10 | (r->req.bus->tcq ? 0x02 : 0); /* Sync, TCQ. */ + r->buf[7] = 0x10 | (r->req.bus->info->tcq ? 0x02 : 0); /* Sync, TCQ. */ memcpy(&r->buf[8], "QEMU ", 8); memcpy(&r->buf[16], "QEMU TARGET ", 16); strncpy((char *) &r->buf[32], QEMU_VERSION, 4); @@ -295,6 +396,13 @@ static int32_t scsi_target_send_command(SCSIRequest *req, uint8_t *buf) r->len = scsi_device_get_sense(r->req.dev, r->buf, MIN(req->cmd.xfer, sizeof r->buf), (req->cmd.buf[1] & 1) == 0); + if (r->req.dev->sense_is_ua) { + if (r->req.dev->info->unit_attention_reported) { + r->req.dev->info->unit_attention_reported(req->dev); + } + r->req.dev->sense_len = 0; + r->req.dev->sense_is_ua = false; + } break; default: scsi_req_build_sense(req, SENSE_CODE(LUN_NOT_SUPPORTED)); @@ -333,7 +441,7 @@ static uint8_t *scsi_target_get_buf(SCSIRequest *req) return r->buf; } -struct SCSIReqOps reqops_target_command = { +static const struct SCSIReqOps reqops_target_command = { .size = sizeof(SCSITargetReq), .send_command = scsi_target_send_command, .read_data = scsi_target_read_data, @@ -341,8 +449,8 @@ struct SCSIReqOps reqops_target_command = { }; -SCSIRequest *scsi_req_alloc(SCSIReqOps *reqops, SCSIDevice *d, uint32_t tag, - uint32_t lun, void *hba_private) +SCSIRequest *scsi_req_alloc(const SCSIReqOps *reqops, SCSIDevice *d, + uint32_t tag, uint32_t lun, void *hba_private) { SCSIRequest *req; @@ -383,7 +491,13 @@ SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun, (buf[0] != INQUIRY && buf[0] != REPORT_LUNS && buf[0] != GET_CONFIGURATION && - buf[0] != GET_EVENT_STATUS_NOTIFICATION)) { + buf[0] != GET_EVENT_STATUS_NOTIFICATION && + + /* + * If we already have a pending unit attention condition, + * report this one before triggering another one. + */ + !(buf[0] == REQUEST_SENSE && d->sense_is_ua))) { req = scsi_req_alloc(&reqops_unit_attention, d, tag, lun, hba_private); } else if (lun != d->lun || @@ -392,7 +506,7 @@ SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun, req = scsi_req_alloc(&reqops_target_command, d, tag, lun, hba_private); } else { - req = d->info->alloc_req(d, tag, lun, hba_private); + req = d->info->alloc_req(d, tag, lun, buf, hba_private); } } @@ -479,10 +593,15 @@ int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len) * * We assume UA_INTLCK_CTRL to be 00b for HBAs that support autosense, and * 10b for HBAs that do not support it (do not call scsi_req_get_sense). - * In the latter case, scsi_req_complete clears unit attention conditions - * after moving them to the device's sense buffer. + * Here we handle unit attention clearing for UA_INTLCK_CTRL == 00b. */ - scsi_clear_unit_attention(req); + if (req->dev->sense_is_ua) { + if (req->dev->info->unit_attention_reported) { + req->dev->info->unit_attention_reported(req->dev); + } + req->dev->sense_len = 0; + req->dev->sense_is_ua = false; + } return ret; } @@ -522,6 +641,7 @@ int32_t scsi_req_enqueue(SCSIRequest *req) static void scsi_req_dequeue(SCSIRequest *req) { trace_scsi_req_dequeue(req->dev->id, req->lun, req->tag); + req->retry = false; if (req->enqueued) { QTAILQ_REMOVE(&req->dev->requests, req, next); req->enqueued = false; @@ -800,7 +920,7 @@ const struct SCSISense sense_code_SAVING_PARAMS_NOT_SUPPORTED = { }; /* Illegal request, Incompatible medium installed */ -const struct SCSISense sense_code_INCOMPATIBLE_MEDIUM = { +const struct SCSISense sense_code_INCOMPATIBLE_FORMAT = { .key = ILLEGAL_REQUEST, .asc = 0x30, .ascq = 0x00 }; @@ -829,6 +949,11 @@ const struct SCSISense sense_code_RESET = { .key = UNIT_ATTENTION, .asc = 0x29, .ascq = 0x00 }; +/* Unit attention, No medium */ +const struct SCSISense sense_code_UNIT_ATTENTION_NO_MEDIUM = { + .key = UNIT_ATTENTION, .asc = 0x3a, .ascq = 0x00 +}; + /* Unit attention, Medium may have changed */ const struct SCSISense sense_code_MEDIUM_CHANGED = { .key = UNIT_ATTENTION, .asc = 0x28, .ascq = 0x00 @@ -1038,8 +1163,12 @@ void scsi_req_continue(SCSIRequest *req) Once it completes, calling scsi_req_continue will restart I/O. */ void scsi_req_data(SCSIRequest *req, int len) { - trace_scsi_req_data(req->dev->id, req->lun, req->tag, len); - req->bus->ops->transfer_data(req, len); + if (req->io_canceled) { + trace_scsi_req_data_canceled(req->dev->id, req->lun, req->tag, len); + } else { + trace_scsi_req_data(req->dev->id, req->lun, req->tag, len); + req->bus->info->transfer_data(req, len); + } } void scsi_req_print(SCSIRequest *req) @@ -1082,8 +1211,12 @@ void scsi_req_complete(SCSIRequest *req, int status) if (req->sense_len) { memcpy(req->dev->sense, req->sense, req->sense_len); + req->dev->sense_len = req->sense_len; + req->dev->sense_is_ua = (req->ops == &reqops_unit_attention); + } else { + req->dev->sense_len = 0; + req->dev->sense_is_ua = false; } - req->dev->sense_len = req->sense_len; /* * Unit attention state is now stored in the device's sense buffer @@ -1094,29 +1227,40 @@ void scsi_req_complete(SCSIRequest *req, int status) scsi_req_ref(req); scsi_req_dequeue(req); - req->bus->ops->complete(req, req->status); + req->bus->info->complete(req, req->status); scsi_req_unref(req); } void scsi_req_cancel(SCSIRequest *req) { - if (req->ops->cancel_io) { - req->ops->cancel_io(req); + if (!req->enqueued) { + return; } scsi_req_ref(req); scsi_req_dequeue(req); - if (req->bus->ops->cancel) { - req->bus->ops->cancel(req); + req->io_canceled = true; + if (req->ops->cancel_io) { + req->ops->cancel_io(req); + } + if (req->bus->info->cancel) { + req->bus->info->cancel(req); } scsi_req_unref(req); } void scsi_req_abort(SCSIRequest *req, int status) { + if (!req->enqueued) { + return; + } + scsi_req_ref(req); + scsi_req_dequeue(req); + req->io_canceled = true; if (req->ops->cancel_io) { req->ops->cancel_io(req); } scsi_req_complete(req, status); + scsi_req_unref(req); } void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense) @@ -1133,19 +1277,28 @@ void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense) static char *scsibus_get_fw_dev_path(DeviceState *dev) { SCSIDevice *d = DO_UPCAST(SCSIDevice, qdev, dev); - SCSIBus *bus = scsi_bus_from_device(d); char path[100]; - int i; - for (i = 0; i < bus->ndev; i++) { - if (bus->devs[i] == d) { - break; - } - } + snprintf(path, sizeof(path), "%s@%d:%d:%d", qdev_fw_name(dev), + d->channel, d->id, d->lun); - assert(i != bus->ndev); + return strdup(path); +} - snprintf(path, sizeof(path), "%s@%x", qdev_fw_name(dev), i); +SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int id, int lun) +{ + DeviceState *qdev; + SCSIDevice *target_dev = NULL; - return strdup(path); + QTAILQ_FOREACH_REVERSE(qdev, &bus->qbus.children, ChildrenHead, sibling) { + SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev); + + if (dev->channel == channel && dev->id == id) { + if (dev->lun == lun) { + return dev; + } + target_dev = dev; + } + } + return target_dev; } diff --git a/hw/scsi-defs.h b/hw/scsi-defs.h index bfe93922d4..d0a467aab7 100644 --- a/hw/scsi-defs.h +++ b/hw/scsi-defs.h @@ -113,6 +113,7 @@ #define READ_12 0xa8 #define WRITE_12 0xaa #define SERVICE_ACTION_IN_12 0xab +#define READ_DVD_STRUCTURE 0xad #define WRITE_VERIFY_12 0xae #define VERIFY_12 0xaf #define SEARCH_HIGH_12 0xb0 @@ -122,6 +123,8 @@ #define SEND_VOLUME_TAG 0xb6 #define READ_DEFECT_DATA_12 0xb7 #define SET_CD_SPEED 0xbb +#define MECHANISM_STATUS 0xbd +#define READ_CD 0xbe /* * SERVICE ACTION IN subcodes @@ -188,3 +191,90 @@ #define TYPE_INACTIVE 0x20 #define TYPE_NO_LUN 0x7f +/* Mode page codes for mode sense/set */ +#define MODE_PAGE_R_W_ERROR 0x01 +#define MODE_PAGE_HD_GEOMETRY 0x04 +#define MODE_PAGE_FLEXIBLE_DISK_GEOMETRY 0x05 +#define MODE_PAGE_CACHING 0x08 +#define MODE_PAGE_AUDIO_CTL 0x0e +#define MODE_PAGE_POWER 0x1a +#define MODE_PAGE_FAULT_FAIL 0x1c +#define MODE_PAGE_TO_PROTECT 0x1d +#define MODE_PAGE_CAPABILITIES 0x2a +#define MODE_PAGE_ALLS 0x3f +/* Not in Mt. Fuji, but in ATAPI 2.6 -- depricated now in favor + * of MODE_PAGE_SENSE_POWER */ +#define MODE_PAGE_CDROM 0x0d + +/* Event notification classes for GET EVENT STATUS NOTIFICATION */ +#define GESN_NO_EVENTS 0 +#define GESN_OPERATIONAL_CHANGE 1 +#define GESN_POWER_MANAGEMENT 2 +#define GESN_EXTERNAL_REQUEST 3 +#define GESN_MEDIA 4 +#define GESN_MULTIPLE_HOSTS 5 +#define GESN_DEVICE_BUSY 6 + +/* Event codes for MEDIA event status notification */ +#define MEC_NO_CHANGE 0 +#define MEC_EJECT_REQUESTED 1 +#define MEC_NEW_MEDIA 2 +#define MEC_MEDIA_REMOVAL 3 /* only for media changers */ +#define MEC_MEDIA_CHANGED 4 /* only for media changers */ +#define MEC_BG_FORMAT_COMPLETED 5 /* MRW or DVD+RW b/g format completed */ +#define MEC_BG_FORMAT_RESTARTED 6 /* MRW or DVD+RW b/g format restarted */ + +#define MS_TRAY_OPEN 1 +#define MS_MEDIA_PRESENT 2 + +/* + * Based on values from <linux/cdrom.h> but extending CD_MINS + * to the maximum common size allowed by the Orange's Book ATIP + * + * 90 and 99 min CDs are also available but using them as the + * upper limit reduces the effectiveness of the heuristic to + * detect DVDs burned to less than 25% of their maximum capacity + */ + +/* Some generally useful CD-ROM information */ +#define CD_MINS 80 /* max. minutes per CD */ +#define CD_SECS 60 /* seconds per minute */ +#define CD_FRAMES 75 /* frames per second */ +#define CD_FRAMESIZE 2048 /* bytes per frame, "cooked" mode */ +#define CD_MAX_BYTES (CD_MINS * CD_SECS * CD_FRAMES * CD_FRAMESIZE) +#define CD_MAX_SECTORS (CD_MAX_BYTES / 512) + +/* + * The MMC values are not IDE specific and might need to be moved + * to a common header if they are also needed for the SCSI emulation + */ + +/* Profile list from MMC-6 revision 1 table 91 */ +#define MMC_PROFILE_NONE 0x0000 +#define MMC_PROFILE_CD_ROM 0x0008 +#define MMC_PROFILE_CD_R 0x0009 +#define MMC_PROFILE_CD_RW 0x000A +#define MMC_PROFILE_DVD_ROM 0x0010 +#define MMC_PROFILE_DVD_R_SR 0x0011 +#define MMC_PROFILE_DVD_RAM 0x0012 +#define MMC_PROFILE_DVD_RW_RO 0x0013 +#define MMC_PROFILE_DVD_RW_SR 0x0014 +#define MMC_PROFILE_DVD_R_DL_SR 0x0015 +#define MMC_PROFILE_DVD_R_DL_JR 0x0016 +#define MMC_PROFILE_DVD_RW_DL 0x0017 +#define MMC_PROFILE_DVD_DDR 0x0018 +#define MMC_PROFILE_DVD_PLUS_RW 0x001A +#define MMC_PROFILE_DVD_PLUS_R 0x001B +#define MMC_PROFILE_DVD_PLUS_RW_DL 0x002A +#define MMC_PROFILE_DVD_PLUS_R_DL 0x002B +#define MMC_PROFILE_BD_ROM 0x0040 +#define MMC_PROFILE_BD_R_SRM 0x0041 +#define MMC_PROFILE_BD_R_RRM 0x0042 +#define MMC_PROFILE_BD_RE 0x0043 +#define MMC_PROFILE_HDDVD_ROM 0x0050 +#define MMC_PROFILE_HDDVD_R 0x0051 +#define MMC_PROFILE_HDDVD_RAM 0x0052 +#define MMC_PROFILE_HDDVD_RW 0x0053 +#define MMC_PROFILE_HDDVD_R_DL 0x0058 +#define MMC_PROFILE_HDDVD_RW_DL 0x005A +#define MMC_PROFILE_INVALID 0xFFFF diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 69095780ac..1c04872af7 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -39,15 +39,13 @@ do { fprintf(stderr, "scsi-disk: " fmt , ## __VA_ARGS__); } while (0) #include "blockdev.h" #include "block_int.h" +#ifdef __linux +#include <scsi/sg.h> +#endif + #define SCSI_DMA_BUF_SIZE 131072 #define SCSI_MAX_INQUIRY_LEN 256 -#define SCSI_REQ_STATUS_RETRY 0x01 -#define SCSI_REQ_STATUS_RETRY_TYPE_MASK 0x06 -#define SCSI_REQ_STATUS_RETRY_READ 0x00 -#define SCSI_REQ_STATUS_RETRY_WRITE 0x02 -#define SCSI_REQ_STATUS_RETRY_FLUSH 0x04 - typedef struct SCSIDiskState SCSIDiskState; typedef struct SCSIDiskReq { @@ -58,19 +56,15 @@ typedef struct SCSIDiskReq { uint32_t buflen; struct iovec iov; QEMUIOVector qiov; - uint32_t status; BlockAcctCookie acct; } SCSIDiskReq; struct SCSIDiskState { SCSIDevice qdev; - BlockDriverState *bs; - /* The qemu block layer uses a fixed 512 byte sector size. - This is the number of 512 byte blocks in a single scsi sector. */ - int cluster_size; uint32_t removable; - uint64_t max_lba; + bool media_changed; + bool media_event; QEMUBH *bh; char *version; char *serial; @@ -78,8 +72,7 @@ struct SCSIDiskState bool tray_locked; }; -static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type); -static int scsi_disk_emulate_command(SCSIDiskReq *r); +static int scsi_handle_rw_error(SCSIDiskReq *r, int error); static void scsi_free_request(SCSIRequest *req) { @@ -107,6 +100,11 @@ static void scsi_cancel_io(SCSIRequest *req) DPRINTF("Cancel tag=0x%x\n", req->tag); if (r->req.aiocb) { bdrv_aio_cancel(r->req.aiocb); + + /* This reference was left in by scsi_*_data. We take ownership of + * it the moment scsi_req_cancel is called, independent of whether + * bdrv_aio_cancel completes the request or not. */ + scsi_req_unref(&r->req); } r->req.aiocb = NULL; } @@ -117,7 +115,7 @@ static uint32_t scsi_init_iovec(SCSIDiskReq *r) if (!r->iov.iov_base) { r->buflen = SCSI_DMA_BUF_SIZE; - r->iov.iov_base = qemu_blockalign(s->bs, r->buflen); + r->iov.iov_base = qemu_blockalign(s->qdev.conf.bs, r->buflen); } r->iov.iov_len = MIN(r->sector_count * 512, r->buflen); qemu_iovec_init_external(&r->qiov, &r->iov, 1); @@ -132,12 +130,12 @@ static void scsi_read_complete(void * opaque, int ret) if (r->req.aiocb != NULL) { r->req.aiocb = NULL; - bdrv_acct_done(s->bs, &r->acct); + bdrv_acct_done(s->qdev.conf.bs, &r->acct); } if (ret) { - if (scsi_handle_rw_error(r, -ret, SCSI_REQ_STATUS_RETRY_READ)) { - return; + if (scsi_handle_rw_error(r, -ret)) { + goto done; } } @@ -147,6 +145,11 @@ static void scsi_read_complete(void * opaque, int ret) r->sector += n; r->sector_count -= n; scsi_req_data(&r->req, r->qiov.size); + +done: + if (!r->req.io_canceled) { + scsi_req_unref(&r->req); + } } static void scsi_flush_complete(void * opaque, int ret) @@ -156,16 +159,21 @@ static void scsi_flush_complete(void * opaque, int ret) if (r->req.aiocb != NULL) { r->req.aiocb = NULL; - bdrv_acct_done(s->bs, &r->acct); + bdrv_acct_done(s->qdev.conf.bs, &r->acct); } if (ret < 0) { - if (scsi_handle_rw_error(r, -ret, SCSI_REQ_STATUS_RETRY_FLUSH)) { - return; + if (scsi_handle_rw_error(r, -ret)) { + goto done; } } scsi_req_complete(&r->req, GOOD); + +done: + if (!r->req.io_canceled) { + scsi_req_unref(&r->req); + } } /* Read more data from scsi device into buffer. */ @@ -191,6 +199,8 @@ static void scsi_read_data(SCSIRequest *req) /* No data transfer may already be in progress */ assert(r->req.aiocb == NULL); + /* The request is used as the AIO opaque value, so add a ref. */ + scsi_req_ref(&r->req); if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { DPRINTF("Data transfer direction invalid\n"); scsi_read_complete(r, -EINVAL); @@ -199,38 +209,48 @@ static void scsi_read_data(SCSIRequest *req) if (s->tray_open) { scsi_read_complete(r, -ENOMEDIUM); + return; } + n = scsi_init_iovec(r); - bdrv_acct_start(s->bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ); - r->req.aiocb = bdrv_aio_readv(s->bs, r->sector, &r->qiov, n, + bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ); + r->req.aiocb = bdrv_aio_readv(s->qdev.conf.bs, r->sector, &r->qiov, n, scsi_read_complete, r); if (r->req.aiocb == NULL) { scsi_read_complete(r, -EIO); } } -static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type) +/* + * scsi_handle_rw_error has two return values. 0 means that the error + * must be ignored, 1 means that the error has been processed and the + * caller should not do anything else for this request. Note that + * scsi_handle_rw_error always manages its reference counts, independent + * of the return value. + */ +static int scsi_handle_rw_error(SCSIDiskReq *r, int error) { - int is_read = (type == SCSI_REQ_STATUS_RETRY_READ); + int is_read = (r->req.cmd.xfer == SCSI_XFER_FROM_DEV); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - BlockErrorAction action = bdrv_get_on_error(s->bs, is_read); + BlockErrorAction action = bdrv_get_on_error(s->qdev.conf.bs, is_read); if (action == BLOCK_ERR_IGNORE) { - bdrv_mon_event(s->bs, BDRV_ACTION_IGNORE, is_read); + bdrv_mon_event(s->qdev.conf.bs, BDRV_ACTION_IGNORE, is_read); return 0; } if ((error == ENOSPC && action == BLOCK_ERR_STOP_ENOSPC) || action == BLOCK_ERR_STOP_ANY) { - type &= SCSI_REQ_STATUS_RETRY_TYPE_MASK; - r->status |= SCSI_REQ_STATUS_RETRY | type; - - bdrv_mon_event(s->bs, BDRV_ACTION_STOP, is_read); + bdrv_mon_event(s->qdev.conf.bs, BDRV_ACTION_STOP, is_read); vm_stop(RUN_STATE_IO_ERROR); - bdrv_iostatus_set_err(s->bs, error); + bdrv_iostatus_set_err(s->qdev.conf.bs, error); + scsi_req_retry(&r->req); } else { switch (error) { + case ENOMEDIUM: + scsi_check_condition(r, SENSE_CODE(NO_MEDIUM)); + break; case ENOMEM: scsi_check_condition(r, SENSE_CODE(TARGET_FAILURE)); break; @@ -241,7 +261,7 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type) scsi_check_condition(r, SENSE_CODE(IO_ERROR)); break; } - bdrv_mon_event(s->bs, BDRV_ACTION_REPORT, is_read); + bdrv_mon_event(s->qdev.conf.bs, BDRV_ACTION_REPORT, is_read); } return 1; } @@ -254,12 +274,12 @@ static void scsi_write_complete(void * opaque, int ret) if (r->req.aiocb != NULL) { r->req.aiocb = NULL; - bdrv_acct_done(s->bs, &r->acct); + bdrv_acct_done(s->qdev.conf.bs, &r->acct); } if (ret) { - if (scsi_handle_rw_error(r, -ret, SCSI_REQ_STATUS_RETRY_WRITE)) { - return; + if (scsi_handle_rw_error(r, -ret)) { + goto done; } } @@ -273,6 +293,11 @@ static void scsi_write_complete(void * opaque, int ret) DPRINTF("Write complete tag=0x%x more=%d\n", r->req.tag, r->qiov.size); scsi_req_data(&r->req, r->qiov.size); } + +done: + if (!r->req.io_canceled) { + scsi_req_unref(&r->req); + } } static void scsi_write_data(SCSIRequest *req) @@ -284,6 +309,8 @@ static void scsi_write_data(SCSIRequest *req) /* No data transfer may already be in progress */ assert(r->req.aiocb == NULL); + /* The request is used as the AIO opaque value, so add a ref. */ + scsi_req_ref(&r->req); if (r->req.cmd.mode != SCSI_XFER_TO_DEV) { DPRINTF("Data transfer direction invalid\n"); scsi_write_complete(r, -EINVAL); @@ -294,9 +321,10 @@ static void scsi_write_data(SCSIRequest *req) if (n) { if (s->tray_open) { scsi_write_complete(r, -ENOMEDIUM); + return; } - bdrv_acct_start(s->bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_WRITE); - r->req.aiocb = bdrv_aio_writev(s->bs, r->sector, &r->qiov, n, + bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_WRITE); + r->req.aiocb = bdrv_aio_writev(s->qdev.conf.bs, r->sector, &r->qiov, n, scsi_write_complete, r); if (r->req.aiocb == NULL) { scsi_write_complete(r, -ENOMEM); @@ -307,54 +335,6 @@ static void scsi_write_data(SCSIRequest *req) } } -static void scsi_dma_restart_bh(void *opaque) -{ - SCSIDiskState *s = opaque; - SCSIRequest *req; - SCSIDiskReq *r; - - qemu_bh_delete(s->bh); - s->bh = NULL; - - QTAILQ_FOREACH(req, &s->qdev.requests, next) { - r = DO_UPCAST(SCSIDiskReq, req, req); - if (r->status & SCSI_REQ_STATUS_RETRY) { - int status = r->status; - int ret; - - r->status &= - ~(SCSI_REQ_STATUS_RETRY | SCSI_REQ_STATUS_RETRY_TYPE_MASK); - - switch (status & SCSI_REQ_STATUS_RETRY_TYPE_MASK) { - case SCSI_REQ_STATUS_RETRY_READ: - scsi_read_data(&r->req); - break; - case SCSI_REQ_STATUS_RETRY_WRITE: - scsi_write_data(&r->req); - break; - case SCSI_REQ_STATUS_RETRY_FLUSH: - ret = scsi_disk_emulate_command(r); - if (ret == 0) { - scsi_req_complete(&r->req, GOOD); - } - } - } - } -} - -static void scsi_dma_restart_cb(void *opaque, int running, RunState state) -{ - SCSIDiskState *s = opaque; - - if (!running) - return; - - if (!s->bh) { - s->bh = qemu_bh_new(scsi_dma_restart_bh, s); - qemu_bh_schedule(s->bh); - } -} - /* Return a pointer to the data buffer. */ static uint8_t *scsi_get_buf(SCSIRequest *req) { @@ -383,11 +363,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) return -1; } - if (s->qdev.type == TYPE_ROM) { - outbuf[buflen++] = 5; - } else { - outbuf[buflen++] = 0; - } + outbuf[buflen++] = s->qdev.type & 0x1f; outbuf[buflen++] = page_code ; // this page outbuf[buflen++] = 0x00; @@ -399,8 +375,9 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) "buffer size %zd\n", req->cmd.xfer); pages = buflen++; outbuf[buflen++] = 0x00; // list of supported pages (this page) - if (s->serial) + if (s->serial) { outbuf[buflen++] = 0x80; // unit serial number + } outbuf[buflen++] = 0x83; // device identification if (s->qdev.type == TYPE_DISK) { outbuf[buflen++] = 0xb0; // block limits @@ -419,10 +396,12 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) } l = strlen(s->serial); - if (l > req->cmd.xfer) + if (l > req->cmd.xfer) { l = req->cmd.xfer; - if (l > 20) + } + if (l > 20) { l = 20; + } DPRINTF("Inquiry EVPD[Serial number] " "buffer size %zd\n", req->cmd.xfer); @@ -435,10 +414,11 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) case 0x83: /* Device identification page, mandatory */ { int max_len = 255 - 8; - int id_len = strlen(bdrv_get_device_name(s->bs)); + int id_len = strlen(bdrv_get_device_name(s->qdev.conf.bs)); - if (id_len > max_len) + if (id_len > max_len) { id_len = max_len; + } DPRINTF("Inquiry EVPD[Device identification] " "buffer size %zd\n", req->cmd.xfer); @@ -448,7 +428,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) outbuf[buflen++] = 0; // reserved outbuf[buflen++] = id_len; // length of data following - memcpy(outbuf+buflen, bdrv_get_device_name(s->bs), id_len); + memcpy(outbuf+buflen, bdrv_get_device_name(s->qdev.conf.bs), id_len); buflen += id_len; break; } @@ -521,17 +501,16 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) } buflen = req->cmd.xfer; - if (buflen > SCSI_MAX_INQUIRY_LEN) + if (buflen > SCSI_MAX_INQUIRY_LEN) { buflen = SCSI_MAX_INQUIRY_LEN; - + } memset(outbuf, 0, buflen); outbuf[0] = s->qdev.type & 0x1f; + outbuf[1] = s->removable ? 0x80 : 0; if (s->qdev.type == TYPE_ROM) { - outbuf[1] = 0x80; memcpy(&outbuf[16], "QEMU CD-ROM ", 16); } else { - outbuf[1] = s->removable ? 0x80 : 0; memcpy(&outbuf[16], "QEMU HARDDISK ", 16); } memcpy(&outbuf[8], "QEMU ", 8); @@ -555,17 +534,250 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) } /* Sync data transfer and TCQ. */ - outbuf[7] = 0x10 | (req->bus->tcq ? 0x02 : 0); + outbuf[7] = 0x10 | (req->bus->info->tcq ? 0x02 : 0); return buflen; } +static inline bool media_is_dvd(SCSIDiskState *s) +{ + uint64_t nb_sectors; + if (s->qdev.type != TYPE_ROM) { + return false; + } + if (!bdrv_is_inserted(s->qdev.conf.bs)) { + return false; + } + bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors); + return nb_sectors > CD_MAX_SECTORS; +} + +static inline bool media_is_cd(SCSIDiskState *s) +{ + uint64_t nb_sectors; + if (s->qdev.type != TYPE_ROM) { + return false; + } + if (!bdrv_is_inserted(s->qdev.conf.bs)) { + return false; + } + bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors); + return nb_sectors <= CD_MAX_SECTORS; +} + +static int scsi_read_dvd_structure(SCSIDiskState *s, SCSIDiskReq *r, + uint8_t *outbuf) +{ + static const int rds_caps_size[5] = { + [0] = 2048 + 4, + [1] = 4 + 4, + [3] = 188 + 4, + [4] = 2048 + 4, + }; + + uint8_t media = r->req.cmd.buf[1]; + uint8_t layer = r->req.cmd.buf[6]; + uint8_t format = r->req.cmd.buf[7]; + int size = -1; + + if (s->qdev.type != TYPE_ROM) { + return -1; + } + if (media != 0) { + scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); + return -1; + } + + if (format != 0xff) { + if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) { + scsi_check_condition(r, SENSE_CODE(NO_MEDIUM)); + return -1; + } + if (media_is_cd(s)) { + scsi_check_condition(r, SENSE_CODE(INCOMPATIBLE_FORMAT)); + return -1; + } + if (format >= ARRAY_SIZE(rds_caps_size)) { + return -1; + } + size = rds_caps_size[format]; + memset(outbuf, 0, size); + } + + switch (format) { + case 0x00: { + /* Physical format information */ + uint64_t nb_sectors; + if (layer != 0) { + goto fail; + } + bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors); + + outbuf[4] = 1; /* DVD-ROM, part version 1 */ + outbuf[5] = 0xf; /* 120mm disc, minimum rate unspecified */ + outbuf[6] = 1; /* one layer, read-only (per MMC-2 spec) */ + outbuf[7] = 0; /* default densities */ + + stl_be_p(&outbuf[12], (nb_sectors >> 2) - 1); /* end sector */ + stl_be_p(&outbuf[16], (nb_sectors >> 2) - 1); /* l0 end sector */ + break; + } + + case 0x01: /* DVD copyright information, all zeros */ + break; + + case 0x03: /* BCA information - invalid field for no BCA info */ + return -1; + + case 0x04: /* DVD disc manufacturing information, all zeros */ + break; + + case 0xff: { /* List capabilities */ + int i; + size = 4; + for (i = 0; i < ARRAY_SIZE(rds_caps_size); i++) { + if (!rds_caps_size[i]) { + continue; + } + outbuf[size] = i; + outbuf[size + 1] = 0x40; /* Not writable, readable */ + stw_be_p(&outbuf[size + 2], rds_caps_size[i]); + size += 4; + } + break; + } + + default: + return -1; + } + + /* Size of buffer, not including 2 byte size field */ + stw_be_p(outbuf, size - 2); + return size; + +fail: + return -1; +} + +static int scsi_event_status_media(SCSIDiskState *s, uint8_t *outbuf) +{ + uint8_t event_code, media_status; + + media_status = 0; + if (s->tray_open) { + media_status = MS_TRAY_OPEN; + } else if (bdrv_is_inserted(s->qdev.conf.bs)) { + media_status = MS_MEDIA_PRESENT; + } + + /* Event notification descriptor */ + event_code = MEC_NO_CHANGE; + if (media_status != MS_TRAY_OPEN && s->media_event) { + event_code = MEC_NEW_MEDIA; + s->media_event = false; + } + + outbuf[0] = event_code; + outbuf[1] = media_status; + + /* These fields are reserved, just clear them. */ + outbuf[2] = 0; + outbuf[3] = 0; + return 4; +} + +static int scsi_get_event_status_notification(SCSIDiskState *s, SCSIDiskReq *r, + uint8_t *outbuf) +{ + int size; + uint8_t *buf = r->req.cmd.buf; + uint8_t notification_class_request = buf[4]; + if (s->qdev.type != TYPE_ROM) { + return -1; + } + if ((buf[1] & 1) == 0) { + /* asynchronous */ + return -1; + } + + size = 4; + outbuf[0] = outbuf[1] = 0; + outbuf[3] = 1 << GESN_MEDIA; /* supported events */ + if (notification_class_request & (1 << GESN_MEDIA)) { + outbuf[2] = GESN_MEDIA; + size += scsi_event_status_media(s, &outbuf[size]); + } else { + outbuf[2] = 0x80; + } + stw_be_p(outbuf, size - 4); + return size; +} + +static int scsi_get_configuration(SCSIDiskState *s, uint8_t *outbuf) +{ + int current; + + if (s->qdev.type != TYPE_ROM) { + return -1; + } + current = media_is_dvd(s) ? MMC_PROFILE_DVD_ROM : MMC_PROFILE_CD_ROM; + memset(outbuf, 0, 40); + stl_be_p(&outbuf[0], 36); /* Bytes after the data length field */ + stw_be_p(&outbuf[6], current); + /* outbuf[8] - outbuf[19]: Feature 0 - Profile list */ + outbuf[10] = 0x03; /* persistent, current */ + outbuf[11] = 8; /* two profiles */ + stw_be_p(&outbuf[12], MMC_PROFILE_DVD_ROM); + outbuf[14] = (current == MMC_PROFILE_DVD_ROM); + stw_be_p(&outbuf[16], MMC_PROFILE_CD_ROM); + outbuf[18] = (current == MMC_PROFILE_CD_ROM); + /* outbuf[20] - outbuf[31]: Feature 1 - Core feature */ + stw_be_p(&outbuf[20], 1); + outbuf[22] = 0x08 | 0x03; /* version 2, persistent, current */ + outbuf[23] = 8; + stl_be_p(&outbuf[24], 1); /* SCSI */ + outbuf[28] = 1; /* DBE = 1, mandatory */ + /* outbuf[32] - outbuf[39]: Feature 3 - Removable media feature */ + stw_be_p(&outbuf[32], 3); + outbuf[34] = 0x08 | 0x03; /* version 2, persistent, current */ + outbuf[35] = 4; + outbuf[36] = 0x39; /* tray, load=1, eject=1, unlocked at powerup, lock=1 */ + /* TODO: Random readable, CD read, DVD read, drive serial number, + power management */ + return 40; +} + +static int scsi_emulate_mechanism_status(SCSIDiskState *s, uint8_t *outbuf) +{ + if (s->qdev.type != TYPE_ROM) { + return -1; + } + memset(outbuf, 0, 8); + outbuf[5] = 1; /* CD-ROM */ + return 8; +} + static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, int page_control) { - BlockDriverState *bdrv = s->bs; + static const int mode_sense_valid[0x3f] = { + [MODE_PAGE_HD_GEOMETRY] = (1 << TYPE_DISK), + [MODE_PAGE_FLEXIBLE_DISK_GEOMETRY] = (1 << TYPE_DISK), + [MODE_PAGE_CACHING] = (1 << TYPE_DISK) | (1 << TYPE_ROM), + [MODE_PAGE_R_W_ERROR] = (1 << TYPE_DISK) | (1 << TYPE_ROM), + [MODE_PAGE_AUDIO_CTL] = (1 << TYPE_ROM), + [MODE_PAGE_CAPABILITIES] = (1 << TYPE_ROM), + }; + + BlockDriverState *bdrv = s->qdev.conf.bs; int cylinders, heads, secs; uint8_t *p = *p_outbuf; + if ((mode_sense_valid[page] & (1 << s->qdev.type)) == 0) { + return -1; + } + + p[0] = page; + /* * If Changeable Values are requested, a mask denoting those mode parameters * that are changeable shall be returned. As we currently don't support @@ -573,11 +785,7 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, * The buffer was already menset to zero by the caller of this function. */ switch (page) { - case 4: /* Rigid disk device geometry page. */ - if (s->qdev.type == TYPE_ROM) { - return -1; - } - p[0] = 4; + case MODE_PAGE_HD_GEOMETRY: p[1] = 0x16; if (page_control == 1) { /* Changeable Values */ break; @@ -608,11 +816,7 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, p[21] = 5400 & 0xff; break; - case 5: /* Flexible disk device geometry page. */ - if (s->qdev.type == TYPE_ROM) { - return -1; - } - p[0] = 5; + case MODE_PAGE_FLEXIBLE_DISK_GEOMETRY: p[1] = 0x1e; if (page_control == 1) { /* Changeable Values */ break; @@ -624,7 +828,7 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, bdrv_get_geometry_hint(bdrv, &cylinders, &heads, &secs); p[4] = heads & 0xff; p[5] = secs & 0xff; - p[6] = s->cluster_size * 2; + p[6] = s->qdev.blocksize >> 8; p[8] = (cylinders >> 8) & 0xff; p[9] = cylinders & 0xff; /* Write precomp start cylinder, disabled */ @@ -650,28 +854,37 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, p[29] = 5400 & 0xff; break; - case 8: /* Caching page. */ + case MODE_PAGE_CACHING: p[0] = 8; p[1] = 0x12; if (page_control == 1) { /* Changeable Values */ break; } - if (bdrv_enable_write_cache(s->bs)) { + if (bdrv_enable_write_cache(s->qdev.conf.bs)) { p[2] = 4; /* WCE */ } break; - case 0x2a: /* CD Capabilities and Mechanical Status page. */ - if (s->qdev.type != TYPE_ROM) { - return -1; + case MODE_PAGE_R_W_ERROR: + p[1] = 10; + p[2] = 0x80; /* Automatic Write Reallocation Enabled */ + if (s->qdev.type == TYPE_ROM) { + p[3] = 0x20; /* Read Retry Count */ } - p[0] = 0x2a; + break; + + case MODE_PAGE_AUDIO_CTL: + p[1] = 14; + break; + + case MODE_PAGE_CAPABILITIES: p[1] = 0x14; if (page_control == 1) { /* Changeable Values */ break; } - p[2] = 3; // CD-R & CD-RW read - p[3] = 0; // Writing not supported + + p[2] = 0x3b; /* CD-R & CD-RW read */ + p[3] = 0; /* Writing not supported */ p[4] = 0x7f; /* Audio, composite, digital out, mode 2 form 1&2, multi session */ p[5] = 0xff; /* CD DA, DA accurate, RW supported, @@ -681,17 +894,17 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, /* Locking supported, jumper present, eject, tray */ p[7] = 0; /* no volume & mute control, no changer */ - p[8] = (50 * 176) >> 8; // 50x read speed + p[8] = (50 * 176) >> 8; /* 50x read speed */ p[9] = (50 * 176) & 0xff; - p[10] = 0 >> 8; // No volume - p[11] = 0 & 0xff; - p[12] = 2048 >> 8; // 2M buffer + p[10] = 2 >> 8; /* Two volume levels */ + p[11] = 2 & 0xff; + p[12] = 2048 >> 8; /* 2M buffer */ p[13] = 2048 & 0xff; - p[14] = (16 * 176) >> 8; // 16x read speed current + p[14] = (16 * 176) >> 8; /* 16x read speed current */ p[15] = (16 * 176) & 0xff; - p[18] = (16 * 176) >> 8; // 16x write speed + p[18] = (16 * 176) >> 8; /* 16x write speed */ p[19] = (16 * 176) & 0xff; - p[20] = (16 * 176) >> 8; // 16x write speed current + p[20] = (16 * 176) >> 8; /* 16x write speed current */ p[21] = (16 * 176) & 0xff; break; @@ -719,7 +932,7 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf) memset(outbuf, 0, r->req.cmd.xfer); p = outbuf; - if (bdrv_is_read_only(s->bs)) { + if (bdrv_is_read_only(s->qdev.conf.bs)) { dev_specific_param = 0x80; /* Readonly. */ } else { dev_specific_param = 0x00; @@ -737,23 +950,24 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf) p += 8; } - bdrv_get_geometry(s->bs, &nb_sectors); + bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors); if (!dbd && nb_sectors) { if (r->req.cmd.buf[0] == MODE_SENSE) { outbuf[3] = 8; /* Block descriptor length */ } else { /* MODE_SENSE_10 */ outbuf[7] = 8; /* Block descriptor length */ } - nb_sectors /= s->cluster_size; - if (nb_sectors > 0xffffff) + nb_sectors /= (s->qdev.blocksize / 512); + if (nb_sectors > 0xffffff) { nb_sectors = 0; + } p[0] = 0; /* media density code */ p[1] = (nb_sectors >> 16) & 0xff; p[2] = (nb_sectors >> 8) & 0xff; p[3] = nb_sectors & 0xff; p[4] = 0; /* reserved */ p[5] = 0; /* bytes 5-7 are the sector size in bytes */ - p[6] = s->cluster_size * 2; + p[6] = s->qdev.blocksize >> 8; p[7] = 0; p += 8; } @@ -787,8 +1001,9 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf) outbuf[0] = ((buflen - 2) >> 8) & 0xff; outbuf[1] = (buflen - 2) & 0xff; } - if (buflen > r->req.cmd.xfer) + if (buflen > r->req.cmd.xfer) { buflen = r->req.cmd.xfer; + } return buflen; } @@ -801,9 +1016,9 @@ static int scsi_disk_emulate_read_toc(SCSIRequest *req, uint8_t *outbuf) msf = req->cmd.buf[1] & 2; format = req->cmd.buf[2] & 0xf; start_track = req->cmd.buf[6]; - bdrv_get_geometry(s->bs, &nb_sectors); + bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors); DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, format, msf >> 1); - nb_sectors /= s->cluster_size; + nb_sectors /= s->qdev.blocksize / 512; switch (format) { case 0: toclen = cdrom_read_toc(nb_sectors, outbuf, msf, start_track); @@ -822,8 +1037,9 @@ static int scsi_disk_emulate_read_toc(SCSIRequest *req, uint8_t *outbuf) default: return -1; } - if (toclen > req->cmd.xfer) + if (toclen > req->cmd.xfer) { toclen = req->cmd.xfer; + } return toclen; } @@ -837,12 +1053,12 @@ static int scsi_disk_emulate_start_stop(SCSIDiskReq *r) if (s->qdev.type == TYPE_ROM && loej) { if (!start && !s->tray_open && s->tray_locked) { scsi_check_condition(r, - bdrv_is_inserted(s->bs) + bdrv_is_inserted(s->qdev.conf.bs) ? SENSE_CODE(ILLEGAL_REQ_REMOVAL_PREVENTED) : SENSE_CODE(NOT_READY_REMOVAL_PREVENTED)); return -1; } - bdrv_eject(s->bs, !start); + bdrv_eject(s->qdev.conf.bs, !start); s->tray_open = !start; } return 0; @@ -869,46 +1085,54 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r) goto illegal_request; } r->buflen = MAX(4096, req->cmd.xfer); - r->iov.iov_base = qemu_blockalign(s->bs, r->buflen); + r->iov.iov_base = qemu_blockalign(s->qdev.conf.bs, r->buflen); } outbuf = r->iov.iov_base; switch (req->cmd.buf[0]) { case TEST_UNIT_READY: - if (s->tray_open || !bdrv_is_inserted(s->bs)) + if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) { goto not_ready; + } break; case INQUIRY: buflen = scsi_disk_emulate_inquiry(req, outbuf); - if (buflen < 0) + if (buflen < 0) { goto illegal_request; + } break; case MODE_SENSE: case MODE_SENSE_10: buflen = scsi_disk_emulate_mode_sense(r, outbuf); - if (buflen < 0) + if (buflen < 0) { goto illegal_request; + } break; case READ_TOC: buflen = scsi_disk_emulate_read_toc(req, outbuf); - if (buflen < 0) + if (buflen < 0) { goto illegal_request; + } break; case RESERVE: - if (req->cmd.buf[1] & 1) + if (req->cmd.buf[1] & 1) { goto illegal_request; + } break; case RESERVE_10: - if (req->cmd.buf[1] & 3) + if (req->cmd.buf[1] & 3) { goto illegal_request; + } break; case RELEASE: - if (req->cmd.buf[1] & 1) + if (req->cmd.buf[1] & 1) { goto illegal_request; + } break; case RELEASE_10: - if (req->cmd.buf[1] & 3) + if (req->cmd.buf[1] & 3) { goto illegal_request; + } break; case START_STOP: if (scsi_disk_emulate_start_stop(r) < 0) { @@ -917,52 +1141,78 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r) break; case ALLOW_MEDIUM_REMOVAL: s->tray_locked = req->cmd.buf[4] & 1; - bdrv_lock_medium(s->bs, req->cmd.buf[4] & 1); + bdrv_lock_medium(s->qdev.conf.bs, req->cmd.buf[4] & 1); break; case READ_CAPACITY_10: /* The normal LEN field for this command is zero. */ memset(outbuf, 0, 8); - bdrv_get_geometry(s->bs, &nb_sectors); - if (!nb_sectors) + bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors); + if (!nb_sectors) { goto not_ready; - nb_sectors /= s->cluster_size; + } + if ((req->cmd.buf[8] & 1) == 0 && req->cmd.lba) { + goto illegal_request; + } + nb_sectors /= s->qdev.blocksize / 512; /* Returned value is the address of the last sector. */ nb_sectors--; /* Remember the new size for read/write sanity checking. */ - s->max_lba = nb_sectors; + s->qdev.max_lba = nb_sectors; /* Clip to 2TB, instead of returning capacity modulo 2TB. */ - if (nb_sectors > UINT32_MAX) + if (nb_sectors > UINT32_MAX) { nb_sectors = UINT32_MAX; + } outbuf[0] = (nb_sectors >> 24) & 0xff; outbuf[1] = (nb_sectors >> 16) & 0xff; outbuf[2] = (nb_sectors >> 8) & 0xff; outbuf[3] = nb_sectors & 0xff; outbuf[4] = 0; outbuf[5] = 0; - outbuf[6] = s->cluster_size * 2; + outbuf[6] = s->qdev.blocksize >> 8; outbuf[7] = 0; buflen = 8; break; + case MECHANISM_STATUS: + buflen = scsi_emulate_mechanism_status(s, outbuf); + if (buflen < 0) { + goto illegal_request; + } + break; case GET_CONFIGURATION: - memset(outbuf, 0, 8); - /* ??? This should probably return much more information. For now - just return the basic header indicating the CD-ROM profile. */ - outbuf[7] = 8; // CD-ROM - buflen = 8; + buflen = scsi_get_configuration(s, outbuf); + if (buflen < 0) { + goto illegal_request; + } + break; + case GET_EVENT_STATUS_NOTIFICATION: + buflen = scsi_get_event_status_notification(s, r, outbuf); + if (buflen < 0) { + goto illegal_request; + } + break; + case READ_DVD_STRUCTURE: + buflen = scsi_read_dvd_structure(s, r, outbuf); + if (buflen < 0) { + goto illegal_request; + } break; case SERVICE_ACTION_IN_16: /* Service Action In subcommands. */ if ((req->cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) { DPRINTF("SAI READ CAPACITY(16)\n"); memset(outbuf, 0, req->cmd.xfer); - bdrv_get_geometry(s->bs, &nb_sectors); - if (!nb_sectors) + bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors); + if (!nb_sectors) { goto not_ready; - nb_sectors /= s->cluster_size; + } + if ((req->cmd.buf[14] & 1) == 0 && req->cmd.lba) { + goto illegal_request; + } + nb_sectors /= s->qdev.blocksize / 512; /* Returned value is the address of the last sector. */ nb_sectors--; /* Remember the new size for read/write sanity checking. */ - s->max_lba = nb_sectors; + s->qdev.max_lba = nb_sectors; outbuf[0] = (nb_sectors >> 56) & 0xff; outbuf[1] = (nb_sectors >> 48) & 0xff; outbuf[2] = (nb_sectors >> 40) & 0xff; @@ -973,7 +1223,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r) outbuf[7] = nb_sectors & 0xff; outbuf[8] = 0; outbuf[9] = 0; - outbuf[10] = s->cluster_size * 2; + outbuf[10] = s->qdev.blocksize >> 8; outbuf[11] = 0; outbuf[12] = 0; outbuf[13] = get_physical_block_exp(&s->qdev.conf); @@ -998,7 +1248,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r) return buflen; not_ready: - if (s->tray_open || !bdrv_is_inserted(s->bs)) { + if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) { scsi_check_condition(r, SENSE_CODE(NO_MEDIUM)); } else { scsi_check_condition(r, SENSE_CODE(LUN_NOT_READY)); @@ -1051,7 +1301,10 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) case ALLOW_MEDIUM_REMOVAL: case READ_CAPACITY_10: case READ_TOC: + case READ_DVD_STRUCTURE: case GET_CONFIGURATION: + case GET_EVENT_STATUS_NOTIFICATION: + case MECHANISM_STATUS: case SERVICE_ACTION_IN_16: case VERIFY_10: rc = scsi_disk_emulate_command(r); @@ -1062,8 +1315,10 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) r->iov.iov_len = rc; break; case SYNCHRONIZE_CACHE: - bdrv_acct_start(s->bs, &r->acct, 0, BDRV_ACCT_FLUSH); - r->req.aiocb = bdrv_aio_flush(s->bs, scsi_flush_complete, r); + /* The request is used as the AIO opaque value, so add a ref. */ + scsi_req_ref(&r->req); + bdrv_acct_start(s->qdev.conf.bs, &r->acct, 0, BDRV_ACCT_FLUSH); + r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_flush_complete, r); if (r->req.aiocb == NULL) { scsi_flush_complete(r, -EIO); } @@ -1074,10 +1329,11 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) case READ_16: len = r->req.cmd.xfer / s->qdev.blocksize; DPRINTF("Read (sector %" PRId64 ", count %d)\n", r->req.cmd.lba, len); - if (r->req.cmd.lba > s->max_lba) + if (r->req.cmd.lba > s->qdev.max_lba) { goto illegal_lba; - r->sector = r->req.cmd.lba * s->cluster_size; - r->sector_count = len * s->cluster_size; + } + r->sector = r->req.cmd.lba * (s->qdev.blocksize / 512); + r->sector_count = len * (s->qdev.blocksize / 512); break; case WRITE_6: case WRITE_10: @@ -1090,10 +1346,11 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) DPRINTF("Write %s(sector %" PRId64 ", count %d)\n", (command & 0xe) == 0xe ? "And Verify " : "", r->req.cmd.lba, len); - if (r->req.cmd.lba > s->max_lba) + if (r->req.cmd.lba > s->qdev.max_lba) { goto illegal_lba; - r->sector = r->req.cmd.lba * s->cluster_size; - r->sector_count = len * s->cluster_size; + } + r->sector = r->req.cmd.lba * (s->qdev.blocksize / 512); + r->sector_count = len * (s->qdev.blocksize / 512); break; case MODE_SELECT: DPRINTF("Mode Select(6) (len %lu)\n", (long)r->req.cmd.xfer); @@ -1115,7 +1372,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) case SEEK_10: DPRINTF("Seek(%d) (sector %" PRId64 ")\n", command == SEEK_6 ? 6 : 10, r->req.cmd.lba); - if (r->req.cmd.lba > s->max_lba) { + if (r->req.cmd.lba > s->qdev.max_lba) { goto illegal_lba; } break; @@ -1125,7 +1382,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) DPRINTF("WRITE SAME(16) (sector %" PRId64 ", count %d)\n", r->req.cmd.lba, len); - if (r->req.cmd.lba > s->max_lba) { + if (r->req.cmd.lba > s->qdev.max_lba) { goto illegal_lba; } @@ -1136,8 +1393,9 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) goto fail; } - rc = bdrv_discard(s->bs, r->req.cmd.lba * s->cluster_size, - len * s->cluster_size); + rc = bdrv_discard(s->qdev.conf.bs, + r->req.cmd.lba * (s->qdev.blocksize / 512), + len * (s->qdev.blocksize / 512)); if (rc < 0) { /* XXX: better error code ?*/ goto fail; @@ -1164,8 +1422,9 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { return -len; } else { - if (!r->sector_count) + if (!r->sector_count) { r->sector_count = -1; + } return len; } } @@ -1177,12 +1436,12 @@ static void scsi_disk_reset(DeviceState *dev) scsi_device_purge_requests(&s->qdev, SENSE_CODE(RESET)); - bdrv_get_geometry(s->bs, &nb_sectors); - nb_sectors /= s->cluster_size; + bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors); + nb_sectors /= s->qdev.blocksize / 512; if (nb_sectors) { nb_sectors--; } - s->max_lba = nb_sectors; + s->qdev.max_lba = nb_sectors; } static void scsi_destroy(SCSIDevice *dev) @@ -1195,7 +1454,22 @@ static void scsi_destroy(SCSIDevice *dev) static void scsi_cd_change_media_cb(void *opaque, bool load) { - ((SCSIDiskState *)opaque)->tray_open = !load; + SCSIDiskState *s = opaque; + + /* + * When a CD gets changed, we have to report an ejected state and + * then a loaded state to guests so that they detect tray + * open/close and media change events. Guests that do not use + * GET_EVENT_STATUS_NOTIFICATION to detect such tray open/close + * states rely on this behavior. + * + * media_changed governs the state machine used for unit attention + * report. media_event is used by GET EVENT STATUS NOTIFICATION. + */ + s->media_changed = load; + s->tray_open = !load; + s->qdev.unit_attention = SENSE_CODE(UNIT_ATTENTION_NO_MEDIUM); + s->media_event = true; } static bool scsi_cd_is_tray_open(void *opaque) @@ -1214,7 +1488,16 @@ static const BlockDevOps scsi_cd_block_ops = { .is_medium_locked = scsi_cd_is_medium_locked, }; -static int scsi_initfn(SCSIDevice *dev, uint8_t scsi_type) +static void scsi_disk_unit_attention_reported(SCSIDevice *dev) +{ + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); + if (s->media_changed) { + s->media_changed = false; + s->qdev.unit_attention = SENSE_CODE(MEDIUM_CHANGED); + } +} + +static int scsi_initfn(SCSIDevice *dev) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); DriveInfo *dinfo; @@ -1223,16 +1506,15 @@ static int scsi_initfn(SCSIDevice *dev, uint8_t scsi_type) error_report("scsi-disk: drive property not set"); return -1; } - s->bs = s->qdev.conf.bs; - if (scsi_type == TYPE_DISK && !bdrv_is_inserted(s->bs)) { + if (!s->removable && !bdrv_is_inserted(s->qdev.conf.bs)) { error_report("Device needs media, but drive is empty"); return -1; } if (!s->serial) { /* try to fall back to value set with legacy -drive serial=... */ - dinfo = drive_get_by_blockdev(s->bs); + dinfo = drive_get_by_blockdev(s->qdev.conf.bs); if (*dinfo->serial) { s->serial = g_strdup(dinfo->serial); } @@ -1242,56 +1524,55 @@ static int scsi_initfn(SCSIDevice *dev, uint8_t scsi_type) s->version = g_strdup(QEMU_VERSION); } - if (bdrv_is_sg(s->bs)) { + if (bdrv_is_sg(s->qdev.conf.bs)) { error_report("scsi-disk: unwanted /dev/sg*"); return -1; } - if (scsi_type == TYPE_ROM) { - bdrv_set_dev_ops(s->bs, &scsi_cd_block_ops, s); - s->qdev.blocksize = 2048; - } else if (scsi_type == TYPE_DISK) { - s->qdev.blocksize = s->qdev.conf.logical_block_size; - } else { - error_report("scsi-disk: Unhandled SCSI type %02x", scsi_type); - return -1; + if (s->removable) { + bdrv_set_dev_ops(s->qdev.conf.bs, &scsi_cd_block_ops, s); } - s->cluster_size = s->qdev.blocksize / 512; - bdrv_set_buffer_alignment(s->bs, s->qdev.blocksize); + bdrv_set_buffer_alignment(s->qdev.conf.bs, s->qdev.blocksize); - s->qdev.type = scsi_type; - qemu_add_vm_change_state_handler(scsi_dma_restart_cb, s); - bdrv_iostatus_enable(s->bs); + bdrv_iostatus_enable(s->qdev.conf.bs); add_boot_device_path(s->qdev.conf.bootindex, &dev->qdev, ",0"); return 0; } static int scsi_hd_initfn(SCSIDevice *dev) { - return scsi_initfn(dev, TYPE_DISK); + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); + s->qdev.blocksize = s->qdev.conf.logical_block_size; + s->qdev.type = TYPE_DISK; + return scsi_initfn(&s->qdev); } static int scsi_cd_initfn(SCSIDevice *dev) { - return scsi_initfn(dev, TYPE_ROM); + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); + s->qdev.blocksize = 2048; + s->qdev.type = TYPE_ROM; + s->removable = true; + return scsi_initfn(&s->qdev); } static int scsi_disk_initfn(SCSIDevice *dev) { DriveInfo *dinfo; - uint8_t scsi_type; if (!dev->conf.bs) { - scsi_type = TYPE_DISK; /* will die in scsi_initfn() */ - } else { - dinfo = drive_get_by_blockdev(dev->conf.bs); - scsi_type = dinfo->media_cd ? TYPE_ROM : TYPE_DISK; + return scsi_initfn(dev); /* ... and die there */ } - return scsi_initfn(dev, scsi_type); + dinfo = drive_get_by_blockdev(dev->conf.bs); + if (dinfo->media_cd) { + return scsi_cd_initfn(dev); + } else { + return scsi_hd_initfn(dev); + } } -static SCSIReqOps scsi_disk_reqops = { +static const SCSIReqOps scsi_disk_reqops = { .size = sizeof(SCSIDiskReq), .free_req = scsi_free_request, .send_command = scsi_send_command, @@ -1301,8 +1582,8 @@ static SCSIReqOps scsi_disk_reqops = { .get_buf = scsi_get_buf, }; -static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, - uint32_t lun, void *hba_private) +static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun, + uint8_t *buf, void *hba_private) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); SCSIRequest *req; @@ -1311,6 +1592,105 @@ static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, return req; } +#ifdef __linux__ +static int get_device_type(SCSIDiskState *s) +{ + BlockDriverState *bdrv = s->qdev.conf.bs; + uint8_t cmd[16]; + uint8_t buf[36]; + uint8_t sensebuf[8]; + sg_io_hdr_t io_header; + int ret; + + memset(cmd, 0, sizeof(cmd)); + memset(buf, 0, sizeof(buf)); + cmd[0] = INQUIRY; + cmd[4] = sizeof(buf); + + memset(&io_header, 0, sizeof(io_header)); + io_header.interface_id = 'S'; + io_header.dxfer_direction = SG_DXFER_FROM_DEV; + io_header.dxfer_len = sizeof(buf); + io_header.dxferp = buf; + io_header.cmdp = cmd; + io_header.cmd_len = sizeof(cmd); + io_header.mx_sb_len = sizeof(sensebuf); + io_header.sbp = sensebuf; + io_header.timeout = 6000; /* XXX */ + + ret = bdrv_ioctl(bdrv, SG_IO, &io_header); + if (ret < 0 || io_header.driver_status || io_header.host_status) { + return -1; + } + s->qdev.type = buf[0]; + s->removable = (buf[1] & 0x80) != 0; + return 0; +} + +static int scsi_block_initfn(SCSIDevice *dev) +{ + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); + int sg_version; + int rc; + + if (!s->qdev.conf.bs) { + error_report("scsi-block: drive property not set"); + return -1; + } + + /* check we are using a driver managing SG_IO (version 3 and after) */ + if (bdrv_ioctl(s->qdev.conf.bs, SG_GET_VERSION_NUM, &sg_version) < 0 || + sg_version < 30000) { + error_report("scsi-block: scsi generic interface too old"); + return -1; + } + + /* get device type from INQUIRY data */ + rc = get_device_type(s); + if (rc < 0) { + error_report("scsi-block: INQUIRY failed"); + return -1; + } + + /* Make a guess for the block size, we'll fix it when the guest sends. + * READ CAPACITY. If they don't, they likely would assume these sizes + * anyway. (TODO: check in /sys). + */ + if (s->qdev.type == TYPE_ROM || s->qdev.type == TYPE_WORM) { + s->qdev.blocksize = 2048; + } else { + s->qdev.blocksize = 512; + } + return scsi_initfn(&s->qdev); +} + +static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag, + uint32_t lun, uint8_t *buf, + void *hba_private) +{ + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); + + switch (buf[0]) { + case READ_6: + case READ_10: + case READ_12: + case READ_16: + case WRITE_6: + case WRITE_10: + case WRITE_12: + case WRITE_16: + case WRITE_VERIFY_10: + case WRITE_VERIFY_12: + case WRITE_VERIFY_16: + return scsi_req_alloc(&scsi_disk_reqops, &s->qdev, tag, lun, + hba_private); + } + + return scsi_req_alloc(&scsi_generic_req_ops, &s->qdev, tag, lun, + hba_private); +} +#endif + #define DEFINE_SCSI_DISK_PROPERTIES() \ DEFINE_BLOCK_PROPERTIES(SCSIDiskState, qdev.conf), \ DEFINE_PROP_STRING("ver", SCSIDiskState, version), \ @@ -1326,6 +1706,7 @@ static SCSIDeviceInfo scsi_disk_info[] = { .init = scsi_hd_initfn, .destroy = scsi_destroy, .alloc_req = scsi_new_request, + .unit_attention_reported = scsi_disk_unit_attention_reported, .qdev.props = (Property[]) { DEFINE_SCSI_DISK_PROPERTIES(), DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false), @@ -1340,10 +1721,26 @@ static SCSIDeviceInfo scsi_disk_info[] = { .init = scsi_cd_initfn, .destroy = scsi_destroy, .alloc_req = scsi_new_request, + .unit_attention_reported = scsi_disk_unit_attention_reported, .qdev.props = (Property[]) { DEFINE_SCSI_DISK_PROPERTIES(), DEFINE_PROP_END_OF_LIST(), }, +#ifdef __linux__ + },{ + .qdev.name = "scsi-block", + .qdev.fw_name = "disk", + .qdev.desc = "SCSI block device passthrough", + .qdev.size = sizeof(SCSIDiskState), + .qdev.reset = scsi_disk_reset, + .init = scsi_block_initfn, + .destroy = scsi_destroy, + .alloc_req = scsi_block_new_request, + .qdev.props = (Property[]) { + DEFINE_SCSI_DISK_PROPERTIES(), + DEFINE_PROP_END_OF_LIST(), + }, +#endif },{ .qdev.name = "scsi-disk", /* legacy -device scsi-disk */ .qdev.fw_name = "disk", @@ -1353,6 +1750,7 @@ static SCSIDeviceInfo scsi_disk_info[] = { .init = scsi_disk_initfn, .destroy = scsi_destroy, .alloc_req = scsi_new_request, + .unit_attention_reported = scsi_disk_unit_attention_reported, .qdev.props = (Property[]) { DEFINE_SCSI_DISK_PROPERTIES(), DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false), diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c index 8f6b70df2b..9594cc1276 100644 --- a/hw/scsi-generic.c +++ b/hw/scsi-generic.c @@ -39,15 +39,18 @@ do { fprintf(stderr, "scsi-generic: " fmt , ## __VA_ARGS__); } while (0) #define SCSI_SENSE_BUF_SIZE 96 -#define SG_ERR_DRIVER_TIMEOUT 0x06 -#define SG_ERR_DRIVER_SENSE 0x08 +#define SG_ERR_DRIVER_TIMEOUT 0x06 +#define SG_ERR_DRIVER_SENSE 0x08 + +#define SG_ERR_DID_OK 0x00 +#define SG_ERR_DID_NO_CONNECT 0x01 +#define SG_ERR_DID_BUS_BUSY 0x02 +#define SG_ERR_DID_TIME_OUT 0x03 #ifndef MAX_UINT #define MAX_UINT ((unsigned int)-1) #endif -typedef struct SCSIGenericState SCSIGenericState; - typedef struct SCSIGenericReq { SCSIRequest req; uint8_t *buf; @@ -56,12 +59,6 @@ typedef struct SCSIGenericReq { sg_io_hdr_t io_header; } SCSIGenericReq; -struct SCSIGenericState -{ - SCSIDevice qdev; - BlockDriverState *bs; -}; - static void scsi_free_request(SCSIRequest *req) { SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); @@ -76,8 +73,9 @@ static void scsi_command_complete(void *opaque, int ret) SCSIGenericReq *r = (SCSIGenericReq *)opaque; r->req.aiocb = NULL; - if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) + if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) { r->req.sense_len = r->io_header.sb_len_wr; + } if (ret != 0) { switch (ret) { @@ -94,9 +92,15 @@ static void scsi_command_complete(void *opaque, int ret) break; } } else { - if (r->io_header.driver_status & SG_ERR_DRIVER_TIMEOUT) { + if (r->io_header.host_status == SG_ERR_DID_NO_CONNECT || + r->io_header.host_status == SG_ERR_DID_BUS_BUSY || + r->io_header.host_status == SG_ERR_DID_TIME_OUT || + (r->io_header.driver_status & SG_ERR_DRIVER_TIMEOUT)) { status = BUSY; BADF("Driver Timeout\n"); + } else if (r->io_header.host_status) { + status = CHECK_CONDITION; + scsi_req_build_sense(&r->req, SENSE_CODE(I_T_NEXUS_LOSS)); } else if (r->io_header.status) { status = r->io_header.status; } else if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) { @@ -109,6 +113,9 @@ static void scsi_command_complete(void *opaque, int ret) r, r->req.tag, status); scsi_req_complete(&r->req, status); + if (!r->req.io_canceled) { + scsi_req_unref(&r->req); + } } /* Cancel a pending data transfer. */ @@ -119,6 +126,11 @@ static void scsi_cancel_io(SCSIRequest *req) DPRINTF("Cancel tag=0x%x\n", req->tag); if (r->req.aiocb) { bdrv_aio_cancel(r->req.aiocb); + + /* This reference was left in by scsi_*_data. We take ownership of + * it independent of whether bdrv_aio_cancel completes the request + * or not. */ + scsi_req_unref(&r->req); } r->req.aiocb = NULL; } @@ -151,6 +163,7 @@ static int execute_command(BlockDriverState *bdrv, static void scsi_read_complete(void * opaque, int ret) { SCSIGenericReq *r = (SCSIGenericReq *)opaque; + SCSIDevice *s = r->req.dev; int len; r->req.aiocb = NULL; @@ -166,7 +179,21 @@ static void scsi_read_complete(void * opaque, int ret) if (len == 0) { scsi_command_complete(r, 0); } else { + /* Snoop READ CAPACITY output to set the blocksize. */ + if (r->req.cmd.buf[0] == READ_CAPACITY_10) { + s->blocksize = ldl_be_p(&r->buf[4]); + s->max_lba = ldl_be_p(&r->buf[0]); + } else if (r->req.cmd.buf[0] == SERVICE_ACTION_IN_16 && + (r->req.cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) { + s->blocksize = ldl_be_p(&r->buf[8]); + s->max_lba = ldq_be_p(&r->buf[0]); + } + bdrv_set_buffer_alignment(s->conf.bs, s->blocksize); + scsi_req_data(&r->req, len); + if (!r->req.io_canceled) { + scsi_req_unref(&r->req); + } } } @@ -174,26 +201,28 @@ static void scsi_read_complete(void * opaque, int ret) static void scsi_read_data(SCSIRequest *req) { SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); - SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev); + SCSIDevice *s = r->req.dev; int ret; DPRINTF("scsi_read_data 0x%x\n", req->tag); + + /* The request is used as the AIO opaque value, so add a ref. */ + scsi_req_ref(&r->req); if (r->len == -1) { scsi_command_complete(r, 0); return; } - ret = execute_command(s->bs, r, SG_DXFER_FROM_DEV, scsi_read_complete); + ret = execute_command(s->conf.bs, r, SG_DXFER_FROM_DEV, scsi_read_complete); if (ret < 0) { scsi_command_complete(r, ret); - return; } } static void scsi_write_complete(void * opaque, int ret) { SCSIGenericReq *r = (SCSIGenericReq *)opaque; - SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev); + SCSIDevice *s = r->req.dev; DPRINTF("scsi_write_complete() ret = %d\n", ret); r->req.aiocb = NULL; @@ -204,9 +233,9 @@ static void scsi_write_complete(void * opaque, int ret) } if (r->req.cmd.buf[0] == MODE_SELECT && r->req.cmd.buf[4] == 12 && - s->qdev.type == TYPE_TAPE) { - s->qdev.blocksize = (r->buf[9] << 16) | (r->buf[10] << 8) | r->buf[11]; - DPRINTF("block size %d\n", s->qdev.blocksize); + s->type == TYPE_TAPE) { + s->blocksize = (r->buf[9] << 16) | (r->buf[10] << 8) | r->buf[11]; + DPRINTF("block size %d\n", s->blocksize); } scsi_command_complete(r, ret); @@ -216,8 +245,8 @@ static void scsi_write_complete(void * opaque, int ret) The transfer may complete asynchronously. */ static void scsi_write_data(SCSIRequest *req) { - SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev); SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); + SCSIDevice *s = r->req.dev; int ret; DPRINTF("scsi_write_data 0x%x\n", req->tag); @@ -227,7 +256,9 @@ static void scsi_write_data(SCSIRequest *req) return; } - ret = execute_command(s->bs, r, SG_DXFER_TO_DEV, scsi_write_complete); + /* The request is used as the AIO opaque value, so add a ref. */ + scsi_req_ref(&r->req); + ret = execute_command(s->conf.bs, r, SG_DXFER_TO_DEV, scsi_write_complete); if (ret < 0) { scsi_command_complete(r, ret); } @@ -241,19 +272,6 @@ static uint8_t *scsi_get_buf(SCSIRequest *req) return r->buf; } -static void scsi_req_fixup(SCSIRequest *req) -{ - switch(req->cmd.buf[0]) { - case REWIND: - case START_STOP: - if (req->dev->type == TYPE_TAPE) { - /* force IMMED, otherwise qemu waits end of command */ - req->cmd.buf[1] = 0x01; - } - break; - } -} - /* Execute a scsi command. Returns the length of the data expected by the command. This will be Positive for data transfers from the device (eg. disk reads), negative for transfers to the device (eg. disk writes), @@ -261,12 +279,10 @@ static void scsi_req_fixup(SCSIRequest *req) static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd) { - SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev); SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); + SCSIDevice *s = r->req.dev; int ret; - scsi_req_fixup(&r->req); - DPRINTF("Command: lun=%d tag=0x%x len %zd data=0x%02x", lun, tag, r->req.cmd.xfer, cmd[0]); @@ -285,7 +301,9 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd) g_free(r->buf); r->buflen = 0; r->buf = NULL; - ret = execute_command(s->bs, r, SG_DXFER_NONE, scsi_command_complete); + /* The request is used as the AIO opaque value, so add a ref. */ + scsi_req_ref(&r->req); + ret = execute_command(s->conf.bs, r, SG_DXFER_NONE, scsi_command_complete); if (ret < 0) { scsi_command_complete(r, ret); return 0; @@ -310,36 +328,6 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd) } } -static int get_blocksize(BlockDriverState *bdrv) -{ - uint8_t cmd[10]; - uint8_t buf[8]; - uint8_t sensebuf[8]; - sg_io_hdr_t io_header; - int ret; - - memset(cmd, 0, sizeof(cmd)); - memset(buf, 0, sizeof(buf)); - cmd[0] = READ_CAPACITY_10; - - memset(&io_header, 0, sizeof(io_header)); - io_header.interface_id = 'S'; - io_header.dxfer_direction = SG_DXFER_FROM_DEV; - io_header.dxfer_len = sizeof(buf); - io_header.dxferp = buf; - io_header.cmdp = cmd; - io_header.cmd_len = sizeof(cmd); - io_header.mx_sb_len = sizeof(sensebuf); - io_header.sbp = sensebuf; - io_header.timeout = 6000; /* XXX */ - - ret = bdrv_ioctl(bdrv, SG_IO, &io_header); - if (ret < 0) - return -1; - - return (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7]; -} - static int get_stream_blocksize(BlockDriverState *bdrv) { uint8_t cmd[6]; @@ -365,89 +353,92 @@ static int get_stream_blocksize(BlockDriverState *bdrv) io_header.timeout = 6000; /* XXX */ ret = bdrv_ioctl(bdrv, SG_IO, &io_header); - if (ret < 0) + if (ret < 0 || io_header.driver_status || io_header.host_status) { return -1; - + } return (buf[9] << 16) | (buf[10] << 8) | buf[11]; } static void scsi_generic_reset(DeviceState *dev) { - SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev.qdev, dev); + SCSIDevice *s = DO_UPCAST(SCSIDevice, qdev, dev); - scsi_device_purge_requests(&s->qdev, SENSE_CODE(RESET)); + scsi_device_purge_requests(s, SENSE_CODE(RESET)); } -static void scsi_destroy(SCSIDevice *d) +static void scsi_destroy(SCSIDevice *s) { - SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d); - - scsi_device_purge_requests(&s->qdev, SENSE_CODE(NO_SENSE)); - blockdev_mark_auto_del(s->qdev.conf.bs); + scsi_device_purge_requests(s, SENSE_CODE(NO_SENSE)); + blockdev_mark_auto_del(s->conf.bs); } -static int scsi_generic_initfn(SCSIDevice *dev) +static int scsi_generic_initfn(SCSIDevice *s) { - SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, dev); int sg_version; struct sg_scsi_id scsiid; - if (!s->qdev.conf.bs) { + if (!s->conf.bs) { error_report("scsi-generic: drive property not set"); return -1; } - s->bs = s->qdev.conf.bs; /* check we are really using a /dev/sg* file */ - if (!bdrv_is_sg(s->bs)) { + if (!bdrv_is_sg(s->conf.bs)) { error_report("scsi-generic: not /dev/sg*"); return -1; } - if (bdrv_get_on_error(s->bs, 0) != BLOCK_ERR_STOP_ENOSPC) { + if (bdrv_get_on_error(s->conf.bs, 0) != BLOCK_ERR_STOP_ENOSPC) { error_report("Device doesn't support drive option werror"); return -1; } - if (bdrv_get_on_error(s->bs, 1) != BLOCK_ERR_REPORT) { + if (bdrv_get_on_error(s->conf.bs, 1) != BLOCK_ERR_REPORT) { error_report("Device doesn't support drive option rerror"); return -1; } /* check we are using a driver managing SG_IO (version 3 and after */ - if (bdrv_ioctl(s->bs, SG_GET_VERSION_NUM, &sg_version) < 0 || + if (bdrv_ioctl(s->conf.bs, SG_GET_VERSION_NUM, &sg_version) < 0 || sg_version < 30000) { error_report("scsi-generic: scsi generic interface too old"); return -1; } /* get LUN of the /dev/sg? */ - if (bdrv_ioctl(s->bs, SG_GET_SCSI_ID, &scsiid)) { + if (bdrv_ioctl(s->conf.bs, SG_GET_SCSI_ID, &scsiid)) { error_report("scsi-generic: SG_GET_SCSI_ID ioctl failed"); return -1; } /* define device state */ - s->qdev.type = scsiid.scsi_type; - DPRINTF("device type %d\n", s->qdev.type); - if (s->qdev.type == TYPE_TAPE) { - s->qdev.blocksize = get_stream_blocksize(s->bs); - if (s->qdev.blocksize == -1) - s->qdev.blocksize = 0; - } else { - s->qdev.blocksize = get_blocksize(s->bs); - /* removable media returns 0 if not present */ - if (s->qdev.blocksize <= 0) { - if (s->qdev.type == TYPE_ROM || s->qdev.type == TYPE_WORM) - s->qdev.blocksize = 2048; - else - s->qdev.blocksize = 512; + s->type = scsiid.scsi_type; + DPRINTF("device type %d\n", s->type); + switch (s->type) { + case TYPE_TAPE: + s->blocksize = get_stream_blocksize(s->conf.bs); + if (s->blocksize == -1) { + s->blocksize = 0; } + break; + + /* Make a guess for block devices, we'll fix it when the guest sends. + * READ CAPACITY. If they don't, they likely would assume these sizes + * anyway. (TODO: they could also send MODE SENSE). + */ + case TYPE_ROM: + case TYPE_WORM: + s->blocksize = 2048; + break; + default: + s->blocksize = 512; + break; } - DPRINTF("block size %d\n", s->qdev.blocksize); + + DPRINTF("block size %d\n", s->blocksize); return 0; } -static SCSIReqOps scsi_generic_req_ops = { +const SCSIReqOps scsi_generic_req_ops = { .size = sizeof(SCSIGenericReq), .free_req = scsi_free_request, .send_command = scsi_send_command, @@ -458,7 +449,7 @@ static SCSIReqOps scsi_generic_req_ops = { }; static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun, - void *hba_private) + uint8_t *buf, void *hba_private) { SCSIRequest *req; @@ -469,13 +460,13 @@ static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun, static SCSIDeviceInfo scsi_generic_info = { .qdev.name = "scsi-generic", .qdev.desc = "pass through generic scsi device (/dev/sg*)", - .qdev.size = sizeof(SCSIGenericState), + .qdev.size = sizeof(SCSIDevice), .qdev.reset = scsi_generic_reset, .init = scsi_generic_initfn, .destroy = scsi_destroy, .alloc_req = scsi_new_request, .qdev.props = (Property[]) { - DEFINE_BLOCK_PROPERTIES(SCSIGenericState, qdev.conf), + DEFINE_BLOCK_PROPERTIES(SCSIDevice, conf), DEFINE_PROP_END_OF_LIST(), }, }; diff --git a/hw/scsi.h b/hw/scsi.h index e8dcabfa28..ff8fdd0962 100644 --- a/hw/scsi.h +++ b/hw/scsi.h @@ -3,13 +3,14 @@ #include "qdev.h" #include "block.h" +#include "sysemu.h" #define MAX_SCSI_DEVS 255 #define SCSI_CMD_BUF_SIZE 16 typedef struct SCSIBus SCSIBus; -typedef struct SCSIBusOps SCSIBusOps; +typedef struct SCSIBusInfo SCSIBusInfo; typedef struct SCSICommand SCSICommand; typedef struct SCSIDevice SCSIDevice; typedef struct SCSIDeviceInfo SCSIDeviceInfo; @@ -41,7 +42,7 @@ struct SCSICommand { struct SCSIRequest { SCSIBus *bus; SCSIDevice *dev; - SCSIReqOps *ops; + const SCSIReqOps *ops; uint32_t refcount; uint32_t tag; uint32_t lun; @@ -51,6 +52,8 @@ struct SCSIRequest { uint8_t sense[SCSI_SENSE_BUF_SIZE]; uint32_t sense_len; bool enqueued; + bool io_canceled; + bool retry; void *hba_private; QTAILQ_ENTRY(SCSIRequest) next; }; @@ -58,16 +61,21 @@ struct SCSIRequest { struct SCSIDevice { DeviceState qdev; + VMChangeStateEntry *vmsentry; + QEMUBH *bh; uint32_t id; BlockConf conf; SCSIDeviceInfo *info; SCSISense unit_attention; + bool sense_is_ua; uint8_t sense[SCSI_SENSE_BUF_SIZE]; uint32_t sense_len; QTAILQ_HEAD(, SCSIRequest) requests; + uint32_t channel; uint32_t lun; int blocksize; int type; + uint64_t max_lba; }; /* cdrom.c */ @@ -91,11 +99,13 @@ struct SCSIDeviceInfo { scsi_qdev_initfn init; void (*destroy)(SCSIDevice *s); SCSIRequest *(*alloc_req)(SCSIDevice *s, uint32_t tag, uint32_t lun, - void *hba_private); - SCSIReqOps reqops; + uint8_t *buf, void *hba_private); + void (*unit_attention_reported)(SCSIDevice *s); }; -struct SCSIBusOps { +struct SCSIBusInfo { + int tcq; + int max_channel, max_target, max_lun; void (*transfer_data)(SCSIRequest *req, uint32_t arg); void (*complete)(SCSIRequest *req, uint32_t arg); void (*cancel)(SCSIRequest *req); @@ -106,14 +116,10 @@ struct SCSIBus { int busnr; SCSISense unit_attention; - int tcq, ndev; - const SCSIBusOps *ops; - - SCSIDevice *devs[MAX_SCSI_DEVS]; + const SCSIBusInfo *info; }; -void scsi_bus_new(SCSIBus *bus, DeviceState *host, int tcq, int ndev, - const SCSIBusOps *ops); +void scsi_bus_new(SCSIBus *bus, DeviceState *host, const SCSIBusInfo *info); void scsi_qdev_register(SCSIDeviceInfo *info); static inline SCSIBus *scsi_bus_from_device(SCSIDevice *d) @@ -159,6 +165,8 @@ extern const struct SCSISense sense_code_IO_ERROR; extern const struct SCSISense sense_code_I_T_NEXUS_LOSS; /* Command aborted, Logical Unit failure */ extern const struct SCSISense sense_code_LUN_FAILURE; +/* LUN not ready, Medium not present */ +extern const struct SCSISense sense_code_UNIT_ATTENTION_NO_MEDIUM; /* Unit attention, Power on, reset or bus device reset occurred */ extern const struct SCSISense sense_code_RESET; /* Unit attention, Medium may have changed*/ @@ -172,8 +180,8 @@ extern const struct SCSISense sense_code_DEVICE_INTERNAL_RESET; int scsi_sense_valid(SCSISense sense); -SCSIRequest *scsi_req_alloc(SCSIReqOps *reqops, SCSIDevice *d, uint32_t tag, - uint32_t lun, void *hba_private); +SCSIRequest *scsi_req_alloc(const SCSIReqOps *reqops, SCSIDevice *d, + uint32_t tag, uint32_t lun, void *hba_private); SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun, uint8_t *buf, void *hba_private); int32_t scsi_req_enqueue(SCSIRequest *req); @@ -190,7 +198,12 @@ uint8_t *scsi_req_get_buf(SCSIRequest *req); int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len); void scsi_req_abort(SCSIRequest *req, int status); void scsi_req_cancel(SCSIRequest *req); +void scsi_req_retry(SCSIRequest *req); void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense); int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed); +SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int target, int lun); + +/* scsi-generic.c. */ +extern const SCSIReqOps scsi_generic_req_ops; #endif diff --git a/hw/shix.c b/hw/shix.c index 638bf16e34..dbf47642df 100644 --- a/hw/shix.c +++ b/hw/shix.c @@ -28,7 +28,6 @@ More information in target-sh4/README.sh4 */ #include "hw.h" -#include "pc.h" #include "sh.h" #include "sysemu.h" #include "boards.h" @@ -37,16 +36,6 @@ #define BIOS_FILENAME "shix_bios.bin" #define BIOS_ADDRESS 0xA0000000 -void irq_info(Monitor *mon) -{ - /* XXXXX */ -} - -void pic_info(Monitor *mon) -{ - /* XXXXX */ -} - static void shix_init(ram_addr_t ram_size, const char *boot_device, const char *kernel_filename, const char *kernel_cmdline, diff --git a/hw/sm501.c b/hw/sm501.c index a7ed6fadf1..297bc9c318 100644 --- a/hw/sm501.c +++ b/hw/sm501.c @@ -459,7 +459,7 @@ typedef struct SM501State { target_phys_addr_t base; uint32_t local_mem_size_index; uint8_t * local_mem; - ram_addr_t local_mem_offset; + MemoryRegion local_mem_region; uint32_t last_width; uint32_t last_height; @@ -726,7 +726,8 @@ static void sm501_2d_operation(SM501State * s) } } -static uint32_t sm501_system_config_read(void *opaque, target_phys_addr_t addr) +static uint64_t sm501_system_config_read(void *opaque, target_phys_addr_t addr, + unsigned size) { SM501State * s = (SM501State *)opaque; uint32_t ret = 0; @@ -778,12 +779,12 @@ static uint32_t sm501_system_config_read(void *opaque, target_phys_addr_t addr) return ret; } -static void sm501_system_config_write(void *opaque, - target_phys_addr_t addr, uint32_t value) +static void sm501_system_config_write(void *opaque, target_phys_addr_t addr, + uint64_t value, unsigned size) { SM501State * s = (SM501State *)opaque; SM501_DPRINTF("sm501 system config regs : write addr=%x, val=%x\n", - addr, value); + (uint32_t)addr, (uint32_t)value); switch(addr) { case SM501_SYSTEM_CONTROL: @@ -821,21 +822,19 @@ static void sm501_system_config_write(void *opaque, default: printf("sm501 system config : not implemented register write." - " addr=%x, val=%x\n", (int)addr, value); + " addr=%x, val=%x\n", (int)addr, (uint32_t)value); abort(); } } -static CPUReadMemoryFunc * const sm501_system_config_readfn[] = { - NULL, - NULL, - &sm501_system_config_read, -}; - -static CPUWriteMemoryFunc * const sm501_system_config_writefn[] = { - NULL, - NULL, - &sm501_system_config_write, +static const MemoryRegionOps sm501_system_config_ops = { + .read = sm501_system_config_read, + .write = sm501_system_config_write, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, }; static uint32_t sm501_palette_read(void *opaque, target_phys_addr_t addr) @@ -864,7 +863,8 @@ static void sm501_palette_write(void *opaque, *(uint32_t*)&s->dc_palette[addr] = value; } -static uint32_t sm501_disp_ctrl_read(void *opaque, target_phys_addr_t addr) +static uint64_t sm501_disp_ctrl_read(void *opaque, target_phys_addr_t addr, + unsigned size) { SM501State * s = (SM501State *)opaque; uint32_t ret = 0; @@ -958,13 +958,12 @@ static uint32_t sm501_disp_ctrl_read(void *opaque, target_phys_addr_t addr) return ret; } -static void sm501_disp_ctrl_write(void *opaque, - target_phys_addr_t addr, - uint32_t value) +static void sm501_disp_ctrl_write(void *opaque, target_phys_addr_t addr, + uint64_t value, unsigned size) { SM501State * s = (SM501State *)opaque; SM501_DPRINTF("sm501 disp ctrl regs : write addr=%x, val=%x\n", - addr, value); + (unsigned)addr, (unsigned)value); switch(addr) { case SM501_DC_PANEL_CONTROL: @@ -1059,24 +1058,23 @@ static void sm501_disp_ctrl_write(void *opaque, default: printf("sm501 disp ctrl : not implemented register write." - " addr=%x, val=%x\n", (int)addr, value); + " addr=%x, val=%x\n", (int)addr, (unsigned)value); abort(); } } -static CPUReadMemoryFunc * const sm501_disp_ctrl_readfn[] = { - NULL, - NULL, - &sm501_disp_ctrl_read, -}; - -static CPUWriteMemoryFunc * const sm501_disp_ctrl_writefn[] = { - NULL, - NULL, - &sm501_disp_ctrl_write, +static const MemoryRegionOps sm501_disp_ctrl_ops = { + .read = sm501_disp_ctrl_read, + .write = sm501_disp_ctrl_write, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, }; -static uint32_t sm501_2d_engine_read(void *opaque, target_phys_addr_t addr) +static uint64_t sm501_2d_engine_read(void *opaque, target_phys_addr_t addr, + unsigned size) { SM501State * s = (SM501State *)opaque; uint32_t ret = 0; @@ -1095,12 +1093,12 @@ static uint32_t sm501_2d_engine_read(void *opaque, target_phys_addr_t addr) return ret; } -static void sm501_2d_engine_write(void *opaque, - target_phys_addr_t addr, uint32_t value) +static void sm501_2d_engine_write(void *opaque, target_phys_addr_t addr, + uint64_t value, unsigned size) { SM501State * s = (SM501State *)opaque; SM501_DPRINTF("sm501 2d engine regs : write addr=%x, val=%x\n", - addr, value); + (unsigned)addr, (unsigned)value); switch(addr) { case SM501_2D_SOURCE: @@ -1148,21 +1146,19 @@ static void sm501_2d_engine_write(void *opaque, break; default: printf("sm501 2d engine : not implemented register write." - " addr=%x, val=%x\n", (int)addr, value); + " addr=%x, val=%x\n", (int)addr, (unsigned)value); abort(); } } -static CPUReadMemoryFunc * const sm501_2d_engine_readfn[] = { - NULL, - NULL, - &sm501_2d_engine_read, -}; - -static CPUWriteMemoryFunc * const sm501_2d_engine_writefn[] = { - NULL, - NULL, - &sm501_2d_engine_write, +static const MemoryRegionOps sm501_2d_engine_ops = { + .read = sm501_2d_engine_read, + .write = sm501_2d_engine_write, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, }; /* draw line functions for all console modes */ @@ -1276,7 +1272,7 @@ static void sm501_draw_crt(SM501State * s) int y_start = -1; ram_addr_t page_min = ~0l; ram_addr_t page_max = 0l; - ram_addr_t offset = s->local_mem_offset; + ram_addr_t offset = 0; /* choose draw_line function */ switch (s->dc_crt_control & 3) { @@ -1333,7 +1329,8 @@ static void sm501_draw_crt(SM501State * s) /* check dirty flags for each line */ for (page = page0; page <= page1; page += TARGET_PAGE_SIZE) - if (cpu_physical_memory_get_dirty(page, VGA_DIRTY_FLAG)) + if (memory_region_get_dirty(&s->local_mem_region, page, + DIRTY_MEMORY_VGA)) update = 1; /* draw line and change status */ @@ -1372,8 +1369,9 @@ static void sm501_draw_crt(SM501State * s) /* clear dirty flags */ if (page_min != ~0l) { - cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE, - VGA_DIRTY_FLAG); + memory_region_reset_dirty(&s->local_mem_region, + page_min, page_max + TARGET_PAGE_SIZE, + DIRTY_MEMORY_VGA); } } @@ -1390,9 +1388,9 @@ void sm501_init(MemoryRegion *address_space_mem, uint32_t base, { SM501State * s; DeviceState *dev; - int sm501_system_config_index; - int sm501_disp_ctrl_index; - int sm501_2d_engine_index; + MemoryRegion *sm501_system_config = g_new(MemoryRegion, 1); + MemoryRegion *sm501_disp_ctrl = g_new(MemoryRegion, 1); + MemoryRegion *sm501_2d_engine = g_new(MemoryRegion, 1); /* allocate management data region */ s = (SM501State *)g_malloc0(sizeof(SM501State)); @@ -1407,27 +1405,26 @@ void sm501_init(MemoryRegion *address_space_mem, uint32_t base, s->dc_crt_control = 0x00010000; /* allocate local memory */ - s->local_mem_offset = qemu_ram_alloc(NULL, "sm501.local", local_mem_bytes); - s->local_mem = qemu_get_ram_ptr(s->local_mem_offset); - cpu_register_physical_memory(base, local_mem_bytes, s->local_mem_offset); + memory_region_init_ram(&s->local_mem_region, NULL, "sm501.local", + local_mem_bytes); + s->local_mem = memory_region_get_ram_ptr(&s->local_mem_region); + memory_region_add_subregion(address_space_mem, base, &s->local_mem_region); /* map mmio */ - sm501_system_config_index - = cpu_register_io_memory(sm501_system_config_readfn, - sm501_system_config_writefn, s, - DEVICE_NATIVE_ENDIAN); - cpu_register_physical_memory(base + MMIO_BASE_OFFSET, - 0x6c, sm501_system_config_index); - sm501_disp_ctrl_index = cpu_register_io_memory(sm501_disp_ctrl_readfn, - sm501_disp_ctrl_writefn, s, - DEVICE_NATIVE_ENDIAN); - cpu_register_physical_memory(base + MMIO_BASE_OFFSET + SM501_DC, - 0x1000, sm501_disp_ctrl_index); - sm501_2d_engine_index = cpu_register_io_memory(sm501_2d_engine_readfn, - sm501_2d_engine_writefn, s, - DEVICE_NATIVE_ENDIAN); - cpu_register_physical_memory(base + MMIO_BASE_OFFSET + SM501_2D_ENGINE, - 0x54, sm501_2d_engine_index); + memory_region_init_io(sm501_system_config, &sm501_system_config_ops, s, + "sm501-system-config", 0x6c); + memory_region_add_subregion(address_space_mem, base + MMIO_BASE_OFFSET, + sm501_system_config); + memory_region_init_io(sm501_disp_ctrl, &sm501_disp_ctrl_ops, s, + "sm501-disp-ctrl", 0x1000); + memory_region_add_subregion(address_space_mem, + base + MMIO_BASE_OFFSET + SM501_DC, + sm501_disp_ctrl); + memory_region_init_io(sm501_2d_engine, &sm501_2d_engine_ops, s, + "sm501-2d-engine", 0x54); + memory_region_add_subregion(address_space_mem, + base + MMIO_BASE_OFFSET + SM501_2D_ENGINE, + sm501_2d_engine); /* bridge to usb host emulation module */ dev = qdev_create(NULL, "sysbus-ohci"); diff --git a/hw/spapr.c b/hw/spapr.c index b1189755d3..bdaa938b6b 100644 --- a/hw/spapr.c +++ b/hw/spapr.c @@ -29,6 +29,9 @@ #include "elf.h" #include "net.h" #include "blockdev.h" +#include "cpus.h" +#include "kvm.h" +#include "kvm_ppc.h" #include "hw/boards.h" #include "hw/ppc.h" @@ -36,10 +39,14 @@ #include "hw/spapr.h" #include "hw/spapr_vio.h" +#include "hw/spapr_pci.h" #include "hw/xics.h" #include "kvm.h" #include "kvm_ppc.h" +#include "pci.h" + +#include "exec-memory.h" #include <libfdt.h> @@ -57,6 +64,11 @@ #define MAX_CPUS 256 #define XICS_IRQS 1024 +#define SPAPR_PCI_BUID 0x800000020000001ULL +#define SPAPR_PCI_MEM_WIN_ADDR (0x10000000000ULL + 0xA0000000) +#define SPAPR_PCI_MEM_WIN_SIZE 0x20000000 +#define SPAPR_PCI_IO_WIN_ADDR (0x10000000000ULL + 0x80000000) + #define PHANDLE_XICP 0x00001111 sPAPREnvironment *spapr; @@ -86,6 +98,7 @@ qemu_irq spapr_allocate_irq(uint32_t hint, uint32_t *irq_num) } static void *spapr_create_fdt_skel(const char *cpu_model, + target_phys_addr_t rma_size, target_phys_addr_t initrd_base, target_phys_addr_t initrd_size, const char *boot_device, @@ -94,7 +107,9 @@ static void *spapr_create_fdt_skel(const char *cpu_model, { void *fdt; CPUState *env; - uint64_t mem_reg_property[] = { 0, cpu_to_be64(ram_size) }; + uint64_t mem_reg_property_rma[] = { 0, cpu_to_be64(rma_size) }; + uint64_t mem_reg_property_nonrma[] = { cpu_to_be64(rma_size), + cpu_to_be64(ram_size - rma_size) }; uint32_t start_prop = cpu_to_be32(initrd_base); uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size); uint32_t pft_size_prop[] = {0, cpu_to_be32(hash_shift)}; @@ -103,6 +118,7 @@ static void *spapr_create_fdt_skel(const char *cpu_model, uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(smp_cpus)}; int i; char *modelname; + int smt = kvmppc_smt_threads(); #define _FDT(exp) \ do { \ @@ -137,17 +153,35 @@ static void *spapr_create_fdt_skel(const char *cpu_model, &end_prop, sizeof(end_prop)))); _FDT((fdt_property_string(fdt, "qemu,boot-device", boot_device))); + /* + * Because we don't always invoke any firmware, we can't rely on + * that to do BAR allocation. Long term, we should probably do + * that ourselves, but for now, this setting (plus advertising the + * current BARs as 0) causes sufficiently recent kernels to to the + * BAR assignment themselves */ + _FDT((fdt_property_cell(fdt, "linux,pci-probe-only", 0))); + _FDT((fdt_end_node(fdt))); - /* memory node */ + /* memory node(s) */ _FDT((fdt_begin_node(fdt, "memory@0"))); _FDT((fdt_property_string(fdt, "device_type", "memory"))); - _FDT((fdt_property(fdt, "reg", - mem_reg_property, sizeof(mem_reg_property)))); - + _FDT((fdt_property(fdt, "reg", mem_reg_property_rma, + sizeof(mem_reg_property_rma)))); _FDT((fdt_end_node(fdt))); + if (ram_size > rma_size) { + char mem_name[32]; + + sprintf(mem_name, "memory@%" PRIx64, (uint64_t)rma_size); + _FDT((fdt_begin_node(fdt, mem_name))); + _FDT((fdt_property_string(fdt, "device_type", "memory"))); + _FDT((fdt_property(fdt, "reg", mem_reg_property_nonrma, + sizeof(mem_reg_property_nonrma)))); + _FDT((fdt_end_node(fdt))); + } + /* cpus */ _FDT((fdt_begin_node(fdt, "cpus"))); @@ -162,13 +196,18 @@ static void *spapr_create_fdt_skel(const char *cpu_model, for (env = first_cpu; env != NULL; env = env->next_cpu) { int index = env->cpu_index; - uint32_t gserver_prop[] = {cpu_to_be32(index), 0}; /* HACK! */ + uint32_t servers_prop[smp_threads]; + uint32_t gservers_prop[smp_threads * 2]; char *nodename; 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 cpufreq = kvm_enabled() ? kvmppc_get_clockfreq() : 1000000000; + if ((index % smt) != 0) { + continue; + } + if (asprintf(&nodename, "%s@%x", modelname, index) < 0) { fprintf(stderr, "Allocation failure\n"); exit(1); @@ -193,15 +232,41 @@ static void *spapr_create_fdt_skel(const char *cpu_model, pft_size_prop, sizeof(pft_size_prop)))); _FDT((fdt_property_string(fdt, "status", "okay"))); _FDT((fdt_property(fdt, "64-bit", NULL, 0))); - _FDT((fdt_property_cell(fdt, "ibm,ppc-interrupt-server#s", index))); + + /* Build interrupt servers and gservers properties */ + for (i = 0; i < smp_threads; i++) { + servers_prop[i] = cpu_to_be32(index + i); + /* Hack, direct the group queues back to cpu 0 */ + gservers_prop[i*2] = cpu_to_be32(index + i); + gservers_prop[i*2 + 1] = 0; + } + _FDT((fdt_property(fdt, "ibm,ppc-interrupt-server#s", + servers_prop, sizeof(servers_prop)))); _FDT((fdt_property(fdt, "ibm,ppc-interrupt-gserver#s", - gserver_prop, sizeof(gserver_prop)))); + gservers_prop, sizeof(gservers_prop)))); if (env->mmu_model & POWERPC_MMU_1TSEG) { _FDT((fdt_property(fdt, "ibm,processor-segment-sizes", segs, sizeof(segs)))); } + /* Advertise VMX/VSX (vector extensions) if available + * 0 / no property == no vector extensions + * 1 == VMX / Altivec available + * 2 == VSX available */ + if (env->insns_flags & PPC_ALTIVEC) { + uint32_t vmx = (env->insns_flags2 & PPC2_VSX) ? 2 : 1; + + _FDT((fdt_property_cell(fdt, "ibm,vmx", vmx))); + } + + /* Advertise DFP (Decimal Floating Point) if available + * 0 / no property == no DFP + * 1 == DFP available */ + if (env->insns_flags2 & PPC2_DFP) { + _FDT((fdt_property_cell(fdt, "ibm,dfp", 1))); + } + _FDT((fdt_end_node(fdt))); } @@ -258,6 +323,7 @@ static void spapr_finalize_fdt(sPAPREnvironment *spapr, { int ret; void *fdt; + sPAPRPHBState *phb; fdt = g_malloc(FDT_MAX_SIZE); @@ -270,6 +336,15 @@ static void spapr_finalize_fdt(sPAPREnvironment *spapr, exit(1); } + QLIST_FOREACH(phb, &spapr->phbs, list) { + ret = spapr_populate_pci_devices(phb, PHANDLE_XICP, fdt); + } + + if (ret < 0) { + fprintf(stderr, "couldn't setup PCI devices in fdt\n"); + exit(1); + } + /* RTAS */ ret = spapr_rtas_device_tree_setup(fdt, rtas_addr, rtas_size); if (ret < 0) { @@ -324,7 +399,9 @@ static void ppc_spapr_init(ram_addr_t ram_size, { CPUState *env; int i; - ram_addr_t ram_offset; + MemoryRegion *sysmem = get_system_memory(); + MemoryRegion *ram = g_new(MemoryRegion, 1); + target_phys_addr_t rma_alloc_size, rma_size; uint32_t initrd_base; long kernel_size, initrd_size, fw_size; long pteg_shift = 17; @@ -333,15 +410,28 @@ static void ppc_spapr_init(ram_addr_t ram_size, spapr = g_malloc(sizeof(*spapr)); cpu_ppc_hypercall = emulate_spapr_hypercall; - /* We place the device tree just below either the top of RAM, or - * 2GB, so that it can be processed with 32-bit code if - * necessary */ - spapr->fdt_addr = MIN(ram_size, 0x80000000) - FDT_MAX_SIZE; + /* Allocate RMA if necessary */ + rma_alloc_size = kvmppc_alloc_rma("ppc_spapr.rma", sysmem); + + if (rma_alloc_size == -1) { + hw_error("qemu: Unable to create RMA\n"); + exit(1); + } + if (rma_alloc_size && (rma_alloc_size < ram_size)) { + rma_size = rma_alloc_size; + } else { + rma_size = ram_size; + } + + /* We place the device tree just below either the top of the RMA, + * or just below 2GB, whichever is lowere, so that it can be + * processed with 32-bit real mode code if necessary */ + spapr->fdt_addr = MIN(rma_size, 0x80000000) - FDT_MAX_SIZE; spapr->rtas_addr = spapr->fdt_addr - RTAS_MAX_SIZE; /* init CPUs */ if (cpu_model == NULL) { - cpu_model = "POWER7"; + cpu_model = kvm_enabled() ? "host" : "POWER7"; } for (i = 0; i < smp_cpus; i++) { env = cpu_init(cpu_model); @@ -361,8 +451,13 @@ static void ppc_spapr_init(ram_addr_t ram_size, /* allocate RAM */ spapr->ram_limit = ram_size; - ram_offset = qemu_ram_alloc(NULL, "ppc_spapr.ram", spapr->ram_limit); - cpu_register_physical_memory(0, ram_size, ram_offset); + if (spapr->ram_limit > rma_alloc_size) { + ram_addr_t nonrma_base = rma_alloc_size; + ram_addr_t nonrma_size = spapr->ram_limit - rma_alloc_size; + + memory_region_init_ram(ram, NULL, "ppc_spapr.ram", nonrma_size); + memory_region_add_subregion(sysmem, nonrma_base, ram); + } /* allocate hash page table. For now we always make this 16mb, * later we should probably make it scale to the size of guest @@ -408,6 +503,12 @@ static void ppc_spapr_init(ram_addr_t ram_size, } } + /* Set up PCI */ + spapr_create_phb(spapr, "pci", SPAPR_PCI_BUID, + SPAPR_PCI_MEM_WIN_ADDR, + SPAPR_PCI_MEM_WIN_SIZE, + SPAPR_PCI_IO_WIN_ADDR); + for (i = 0; i < nb_nics; i++) { NICInfo *nd = &nd_table[i]; @@ -418,10 +519,7 @@ static void ppc_spapr_init(ram_addr_t ram_size, if (strcmp(nd->model, "ibmveth") == 0) { spapr_vlan_create(spapr->vio_bus, 0x1000 + i, nd); } else { - fprintf(stderr, "pSeries (sPAPR) platform does not support " - "NIC model '%s' (only ibmveth is supported)\n", - nd->model); - exit(1); + pci_nic_init_nofail(&nd_table[i], nd->model, NULL); } } @@ -486,7 +584,7 @@ static void ppc_spapr_init(ram_addr_t ram_size, } /* Prepare the device tree */ - spapr->fdt_skel = spapr_create_fdt_skel(cpu_model, + spapr->fdt_skel = spapr_create_fdt_skel(cpu_model, rma_size, initrd_base, initrd_size, boot_device, kernel_cmdline, pteg_shift + 7); diff --git a/hw/spapr.h b/hw/spapr.h index 6657c336f6..df88f6abad 100644 --- a/hw/spapr.h +++ b/hw/spapr.h @@ -4,10 +4,12 @@ #include "hw/xics.h" struct VIOsPAPRBus; +struct sPAPRPHBState; struct icp_state; typedef struct sPAPREnvironment { struct VIOsPAPRBus *vio_bus; + QLIST_HEAD(, sPAPRPHBState) phbs; struct icp_state *icp; target_phys_addr_t ram_limit; diff --git a/hw/spapr_pci.c b/hw/spapr_pci.c new file mode 100644 index 0000000000..7162588543 --- /dev/null +++ b/hw/spapr_pci.c @@ -0,0 +1,508 @@ +/* + * QEMU sPAPR PCI host originated from Uninorth PCI host + * + * Copyright (c) 2011 Alexey Kardashevskiy, IBM Corporation. + * Copyright (C) 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 "hw.h" +#include "pci.h" +#include "pci_host.h" +#include "hw/spapr.h" +#include "hw/spapr_pci.h" +#include "exec-memory.h" +#include <libfdt.h> + +#include "hw/pci_internals.h" + +static const uint32_t bars[] = { + PCI_BASE_ADDRESS_0, PCI_BASE_ADDRESS_1, + PCI_BASE_ADDRESS_2, PCI_BASE_ADDRESS_3, + PCI_BASE_ADDRESS_4, PCI_BASE_ADDRESS_5 + /*, PCI_ROM_ADDRESS*/ +}; + +static PCIDevice *find_dev(sPAPREnvironment *spapr, + uint64_t buid, uint32_t config_addr) +{ + DeviceState *qdev; + int devfn = (config_addr >> 8) & 0xFF; + sPAPRPHBState *phb; + + QLIST_FOREACH(phb, &spapr->phbs, list) { + if (phb->buid != buid) { + continue; + } + + QTAILQ_FOREACH(qdev, &phb->host_state.bus->qbus.children, sibling) { + PCIDevice *dev = (PCIDevice *)qdev; + if (dev->devfn == devfn) { + return dev; + } + } + } + + return NULL; +} + +static void rtas_ibm_read_pci_config(sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, + uint32_t nret, target_ulong rets) +{ + uint32_t val, size, addr; + uint64_t buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + PCIDevice *dev = find_dev(spapr, buid, rtas_ld(args, 0)); + + if (!dev) { + rtas_st(rets, 0, -1); + return; + } + size = rtas_ld(args, 3); + addr = rtas_ld(args, 0) & 0xFF; + val = pci_default_read_config(dev, addr, size); + rtas_st(rets, 0, 0); + rtas_st(rets, 1, val); +} + +static void rtas_read_pci_config(sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, + uint32_t nret, target_ulong rets) +{ + uint32_t val, size, addr; + PCIDevice *dev = find_dev(spapr, 0, rtas_ld(args, 0)); + + if (!dev) { + rtas_st(rets, 0, -1); + return; + } + size = rtas_ld(args, 1); + addr = rtas_ld(args, 0) & 0xFF; + val = pci_default_read_config(dev, addr, size); + rtas_st(rets, 0, 0); + rtas_st(rets, 1, val); +} + +static void rtas_ibm_write_pci_config(sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, + uint32_t nret, target_ulong rets) +{ + uint32_t val, size, addr; + uint64_t buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + PCIDevice *dev = find_dev(spapr, buid, rtas_ld(args, 0)); + + if (!dev) { + rtas_st(rets, 0, -1); + return; + } + val = rtas_ld(args, 4); + size = rtas_ld(args, 3); + addr = rtas_ld(args, 0) & 0xFF; + pci_default_write_config(dev, addr, val, size); + rtas_st(rets, 0, 0); +} + +static void rtas_write_pci_config(sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, + uint32_t nret, target_ulong rets) +{ + uint32_t val, size, addr; + PCIDevice *dev = find_dev(spapr, 0, rtas_ld(args, 0)); + + if (!dev) { + rtas_st(rets, 0, -1); + return; + } + val = rtas_ld(args, 2); + size = rtas_ld(args, 1); + addr = rtas_ld(args, 0) & 0xFF; + pci_default_write_config(dev, addr, val, size); + rtas_st(rets, 0, 0); +} + +static int pci_spapr_map_irq(PCIDevice *pci_dev, int irq_num) +{ + /* + * Here we need to convert pci_dev + irq_num to some unique value + * which is less than number of IRQs on the specific bus (now it + * is 16). At the moment irq_num == device_id (number of the + * slot?) + * FIXME: we should swizzle in fn and irq_num + */ + return (pci_dev->devfn >> 3) % SPAPR_PCI_NUM_LSI; +} + +static void pci_spapr_set_irq(void *opaque, int irq_num, int level) +{ + /* + * Here we use the number returned by pci_spapr_map_irq to find a + * corresponding qemu_irq. + */ + sPAPRPHBState *phb = opaque; + + qemu_set_irq(phb->lsi_table[irq_num].qirq, level); +} + +static int spapr_phb_init(SysBusDevice *s) +{ + sPAPRPHBState *phb = FROM_SYSBUS(sPAPRPHBState, s); + int i; + + /* Initialize the LSI table */ + for (i = 0; i < SPAPR_PCI_NUM_LSI; i++) { + qemu_irq qirq; + uint32_t num; + + qirq = spapr_allocate_irq(0, &num); + if (!qirq) { + return -1; + } + + phb->lsi_table[i].dt_irq = num; + phb->lsi_table[i].qirq = qirq; + } + + return 0; +} + +static int spapr_main_pci_host_init(PCIDevice *d) +{ + return 0; +} + +static PCIDeviceInfo spapr_main_pci_host_info = { + .qdev.name = "spapr-pci-host-bridge", + .qdev.size = sizeof(PCIDevice), + .init = spapr_main_pci_host_init, +}; + +static void spapr_register_devices(void) +{ + sysbus_register_dev("spapr-pci-host-bridge", sizeof(sPAPRPHBState), + spapr_phb_init); + pci_qdev_register(&spapr_main_pci_host_info); +} + +device_init(spapr_register_devices) + +static uint64_t spapr_io_read(void *opaque, target_phys_addr_t addr, + unsigned size) +{ + switch (size) { + case 1: + return cpu_inb(addr); + case 2: + return cpu_inw(addr); + case 4: + return cpu_inl(addr); + } + assert(0); +} + +static void spapr_io_write(void *opaque, target_phys_addr_t addr, + uint64_t data, unsigned size) +{ + switch (size) { + case 1: + cpu_outb(addr, data); + return; + case 2: + cpu_outw(addr, data); + return; + case 4: + cpu_outl(addr, data); + return; + } + assert(0); +} + +static MemoryRegionOps spapr_io_ops = { + .endianness = DEVICE_LITTLE_ENDIAN, + .read = spapr_io_read, + .write = spapr_io_write +}; + +void spapr_create_phb(sPAPREnvironment *spapr, + const char *busname, uint64_t buid, + uint64_t mem_win_addr, uint64_t mem_win_size, + uint64_t io_win_addr) +{ + DeviceState *dev; + SysBusDevice *s; + sPAPRPHBState *phb; + PCIBus *bus; + char namebuf[strlen(busname)+11]; + + dev = qdev_create(NULL, "spapr-pci-host-bridge"); + qdev_init_nofail(dev); + s = sysbus_from_qdev(dev); + phb = FROM_SYSBUS(sPAPRPHBState, s); + + phb->mem_win_addr = mem_win_addr; + + sprintf(namebuf, "%s-mem", busname); + memory_region_init(&phb->memspace, namebuf, INT64_MAX); + + sprintf(namebuf, "%s-memwindow", busname); + memory_region_init_alias(&phb->memwindow, namebuf, &phb->memspace, + SPAPR_PCI_MEM_WIN_BUS_OFFSET, mem_win_size); + memory_region_add_subregion(get_system_memory(), mem_win_addr, + &phb->memwindow); + + phb->io_win_addr = io_win_addr; + + /* On ppc, we only have MMIO no specific IO space from the CPU + * perspective. In theory we ought to be able to embed the PCI IO + * memory region direction in the system memory space. However, + * if any of the IO BAR subregions use the old_portio mechanism, + * that won't be processed properly unless accessed from the + * system io address space. This hack to bounce things via + * system_io works around the problem until all the users of + * old_portion are updated */ + sprintf(namebuf, "%s-io", busname); + memory_region_init(&phb->iospace, namebuf, SPAPR_PCI_IO_WIN_SIZE); + /* FIXME: fix to support multiple PHBs */ + memory_region_add_subregion(get_system_io(), 0, &phb->iospace); + + sprintf(namebuf, "%s-iowindow", busname); + memory_region_init_io(&phb->iowindow, &spapr_io_ops, phb, + namebuf, SPAPR_PCI_IO_WIN_SIZE); + memory_region_add_subregion(get_system_memory(), io_win_addr, + &phb->iowindow); + + phb->host_state.bus = bus = pci_register_bus(&phb->busdev.qdev, busname, + pci_spapr_set_irq, + pci_spapr_map_irq, + phb, + &phb->memspace, &phb->iospace, + PCI_DEVFN(0, 0), + SPAPR_PCI_NUM_LSI); + + spapr_rtas_register("read-pci-config", rtas_read_pci_config); + spapr_rtas_register("write-pci-config", rtas_write_pci_config); + spapr_rtas_register("ibm,read-pci-config", rtas_ibm_read_pci_config); + spapr_rtas_register("ibm,write-pci-config", rtas_ibm_write_pci_config); + + QLIST_INSERT_HEAD(&spapr->phbs, phb, list); + + /* pci_bus_set_mem_base(bus, mem_va_start - SPAPR_PCI_MEM_BAR_START); */ +} + +/* Macros to operate with address in OF binding to PCI */ +#define b_x(x, p, l) (((x) & ((1<<(l))-1)) << (p)) +#define b_n(x) b_x((x), 31, 1) /* 0 if relocatable */ +#define b_p(x) b_x((x), 30, 1) /* 1 if prefetchable */ +#define b_t(x) b_x((x), 29, 1) /* 1 if the address is aliased */ +#define b_ss(x) b_x((x), 24, 2) /* the space code */ +#define b_bbbbbbbb(x) b_x((x), 16, 8) /* bus number */ +#define b_ddddd(x) b_x((x), 11, 5) /* device number */ +#define b_fff(x) b_x((x), 8, 3) /* function number */ +#define b_rrrrrrrr(x) b_x((x), 0, 8) /* register number */ + +static uint32_t regtype_to_ss(uint8_t type) +{ + if (type & PCI_BASE_ADDRESS_MEM_TYPE_64) { + return 3; + } + if (type == PCI_BASE_ADDRESS_SPACE_IO) { + return 1; + } + return 2; +} + +int spapr_populate_pci_devices(sPAPRPHBState *phb, + uint32_t xics_phandle, + void *fdt) +{ + PCIBus *bus = phb->host_state.bus; + int bus_off, node_off = 0, devid, fn, i, n, devices; + DeviceState *qdev; + char nodename[256]; + struct { + uint32_t hi; + uint64_t addr; + uint64_t size; + } __attribute__((packed)) reg[PCI_NUM_REGIONS + 1], + assigned_addresses[PCI_NUM_REGIONS]; + uint32_t bus_range[] = { cpu_to_be32(0), cpu_to_be32(0xff) }; + struct { + uint32_t hi; + uint64_t child; + uint64_t parent; + uint64_t size; + } __attribute__((packed)) ranges[] = { + { + cpu_to_be32(b_ss(1)), cpu_to_be64(0), + cpu_to_be64(phb->io_win_addr), + cpu_to_be64(memory_region_size(&phb->iospace)), + }, + { + cpu_to_be32(b_ss(2)), cpu_to_be64(SPAPR_PCI_MEM_WIN_BUS_OFFSET), + cpu_to_be64(phb->mem_win_addr), + cpu_to_be64(memory_region_size(&phb->memwindow)), + }, + }; + uint64_t bus_reg[] = { cpu_to_be64(phb->buid), 0 }; + uint32_t interrupt_map_mask[] = { + cpu_to_be32(b_ddddd(-1)|b_fff(-1)), 0x0, 0x0, 0x0}; + uint32_t interrupt_map[bus->nirq][7]; + + /* Start populating the FDT */ + sprintf(nodename, "pci@%" PRIx64, phb->buid); + bus_off = fdt_add_subnode(fdt, 0, nodename); + if (bus_off < 0) { + return bus_off; + } + +#define _FDT(exp) \ + do { \ + int ret = (exp); \ + if (ret < 0) { \ + return ret; \ + } \ + } while (0) + + /* Write PHB properties */ + _FDT(fdt_setprop_string(fdt, bus_off, "device_type", "pci")); + _FDT(fdt_setprop_string(fdt, bus_off, "compatible", "IBM,Logical_PHB")); + _FDT(fdt_setprop_cell(fdt, bus_off, "#address-cells", 0x3)); + _FDT(fdt_setprop_cell(fdt, bus_off, "#size-cells", 0x2)); + _FDT(fdt_setprop_cell(fdt, bus_off, "#interrupt-cells", 0x1)); + _FDT(fdt_setprop(fdt, bus_off, "used-by-rtas", NULL, 0)); + _FDT(fdt_setprop(fdt, bus_off, "bus-range", &bus_range, sizeof(bus_range))); + _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(fdt, bus_off, "interrupt-map-mask", + &interrupt_map_mask, sizeof(interrupt_map_mask))); + + /* Populate PCI devices and allocate IRQs */ + devices = 0; + QTAILQ_FOREACH(qdev, &bus->qbus.children, sibling) { + PCIDevice *dev = DO_UPCAST(PCIDevice, qdev, qdev); + int irq_index = pci_spapr_map_irq(dev, 0); + uint32_t *irqmap = interrupt_map[devices]; + uint8_t *config = dev->config; + + devid = dev->devfn >> 3; + fn = dev->devfn & 7; + + sprintf(nodename, "pci@%u,%u", devid, fn); + + /* Allocate interrupt from the map */ + if (devid > bus->nirq) { + printf("Unexpected behaviour in spapr_populate_pci_devices," + "wrong devid %u\n", devid); + exit(-1); + } + irqmap[0] = cpu_to_be32(b_ddddd(devid)|b_fff(fn)); + irqmap[1] = 0; + irqmap[2] = 0; + irqmap[3] = 0; + irqmap[4] = cpu_to_be32(xics_phandle); + irqmap[5] = cpu_to_be32(phb->lsi_table[irq_index].dt_irq); + irqmap[6] = cpu_to_be32(0x8); + + /* Add node to FDT */ + node_off = fdt_add_subnode(fdt, bus_off, nodename); + if (node_off < 0) { + return node_off; + } + + _FDT(fdt_setprop_cell(fdt, node_off, "vendor-id", + pci_get_word(&config[PCI_VENDOR_ID]))); + _FDT(fdt_setprop_cell(fdt, node_off, "device-id", + pci_get_word(&config[PCI_DEVICE_ID]))); + _FDT(fdt_setprop_cell(fdt, node_off, "revision-id", + pci_get_byte(&config[PCI_REVISION_ID]))); + _FDT(fdt_setprop_cell(fdt, node_off, "class-code", + pci_get_long(&config[PCI_CLASS_REVISION]) >> 8)); + _FDT(fdt_setprop_cell(fdt, node_off, "subsystem-id", + pci_get_word(&config[PCI_SUBSYSTEM_ID]))); + _FDT(fdt_setprop_cell(fdt, node_off, "subsystem-vendor-id", + pci_get_word(&config[PCI_SUBSYSTEM_VENDOR_ID]))); + + /* Config space region comes first */ + reg[0].hi = cpu_to_be32( + b_n(0) | + b_p(0) | + b_t(0) | + b_ss(0/*config*/) | + b_bbbbbbbb(0) | + b_ddddd(devid) | + b_fff(fn)); + reg[0].addr = 0; + reg[0].size = 0; + + n = 0; + for (i = 0; i < PCI_NUM_REGIONS; ++i) { + if (0 == dev->io_regions[i].size) { + continue; + } + + reg[n+1].hi = cpu_to_be32( + b_n(0) | + b_p(0) | + b_t(0) | + b_ss(regtype_to_ss(dev->io_regions[i].type)) | + b_bbbbbbbb(0) | + b_ddddd(devid) | + b_fff(fn) | + b_rrrrrrrr(bars[i])); + reg[n+1].addr = 0; + reg[n+1].size = cpu_to_be64(dev->io_regions[i].size); + + assigned_addresses[n].hi = cpu_to_be32( + b_n(1) | + b_p(0) | + b_t(0) | + b_ss(regtype_to_ss(dev->io_regions[i].type)) | + b_bbbbbbbb(0) | + b_ddddd(devid) | + b_fff(fn) | + b_rrrrrrrr(bars[i])); + + /* + * Writing zeroes to assigned_addresses causes the guest kernel to + * reassign BARs + */ + assigned_addresses[n].addr = cpu_to_be64(dev->io_regions[i].addr); + assigned_addresses[n].size = reg[n+1].size; + + ++n; + } + _FDT(fdt_setprop(fdt, node_off, "reg", reg, sizeof(reg[0])*(n+1))); + _FDT(fdt_setprop(fdt, node_off, "assigned-addresses", + assigned_addresses, + sizeof(assigned_addresses[0])*(n))); + _FDT(fdt_setprop_cell(fdt, node_off, "interrupts", + pci_get_byte(&config[PCI_INTERRUPT_PIN]))); + + ++devices; + } + + /* Write interrupt map */ + _FDT(fdt_setprop(fdt, bus_off, "interrupt-map", &interrupt_map, + devices * sizeof(interrupt_map[0]))); + + return 0; +} diff --git a/hw/spapr_pci.h b/hw/spapr_pci.h new file mode 100644 index 0000000000..213340c915 --- /dev/null +++ b/hw/spapr_pci.h @@ -0,0 +1,61 @@ +/* + * QEMU SPAPR PCI BUS definitions + * + * Copyright (c) 2011 Alexey Kardashevskiy <aik@au1.ibm.com> + * + * 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/>. + */ +#if !defined(__HW_SPAPR_H__) +#error Please include spapr.h before this file! +#endif + +#if !defined(__HW_SPAPR_PCI_H__) +#define __HW_SPAPR_PCI_H__ + +#include "hw/pci_host.h" +#include "hw/xics.h" + +#define SPAPR_PCI_NUM_LSI 16 + +typedef struct sPAPRPHBState { + SysBusDevice busdev; + PCIHostState host_state; + + uint64_t buid; + + MemoryRegion memspace, iospace; + target_phys_addr_t mem_win_addr, io_win_addr; + MemoryRegion memwindow, iowindow; + + struct { + uint32_t dt_irq; + qemu_irq qirq; + } lsi_table[SPAPR_PCI_NUM_LSI]; + + QLIST_ENTRY(sPAPRPHBState) list; +} sPAPRPHBState; + +#define SPAPR_PCI_MEM_WIN_BUS_OFFSET 0x80000000ULL +#define SPAPR_PCI_IO_WIN_SIZE 0x10000 + +void spapr_create_phb(sPAPREnvironment *spapr, + const char *busname, uint64_t buid, + uint64_t mem_win_addr, uint64_t mem_win_size, + uint64_t io_win_addr); + +int spapr_populate_pci_devices(sPAPRPHBState *phb, + uint32_t xics_phandle, + void *fdt); + +#endif /* __HW_SPAPR_PCI_H__ */ diff --git a/hw/spapr_vio.c b/hw/spapr_vio.c index 35818e18f1..25cfc9d912 100644 --- a/hw/spapr_vio.c +++ b/hw/spapr_vio.c @@ -63,7 +63,7 @@ VIOsPAPRDevice *spapr_vio_find_by_reg(VIOsPAPRBus *bus, uint32_t reg) DeviceState *qdev; VIOsPAPRDevice *dev = NULL; - QLIST_FOREACH(qdev, &bus->bus.children, sibling) { + QTAILQ_FOREACH(qdev, &bus->bus.children, sibling) { dev = (VIOsPAPRDevice *)qdev; if (dev->reg == reg) { break; @@ -165,7 +165,13 @@ static void rtce_init(VIOsPAPRDevice *dev) * sizeof(VIOsPAPR_RTCE); if (size) { - dev->rtce_table = g_malloc0(size); + dev->rtce_table = kvmppc_create_spapr_tce(dev->reg, + dev->rtce_window_size, + &dev->kvmtce_fd); + + if (!dev->rtce_table) { + dev->rtce_table = g_malloc0(size); + } } } @@ -588,7 +594,7 @@ static void rtas_quiesce(sPAPREnvironment *spapr, uint32_t token, return; } - QLIST_FOREACH(qdev, &bus->bus.children, sibling) { + QTAILQ_FOREACH(qdev, &bus->bus.children, sibling) { dev = (VIOsPAPRDevice *)qdev; spapr_vio_quiesce_one(dev); } @@ -726,7 +732,7 @@ int spapr_populate_vdevice(VIOsPAPRBus *bus, void *fdt) DeviceState *qdev; int ret = 0; - QLIST_FOREACH(qdev, &bus->bus.children, sibling) { + QTAILQ_FOREACH(qdev, &bus->bus.children, sibling) { VIOsPAPRDevice *dev = (VIOsPAPRDevice *)qdev; ret = vio_make_devnode(dev, fdt); diff --git a/hw/spapr_vio.h b/hw/spapr_vio.h index 4fe5f742c2..a325a5f4b3 100644 --- a/hw/spapr_vio.h +++ b/hw/spapr_vio.h @@ -57,6 +57,7 @@ typedef struct VIOsPAPRDevice { target_ulong signal_state; uint32_t rtce_window_size; VIOsPAPR_RTCE *rtce_table; + int kvmtce_fd; VIOsPAPR_CRQ crq; } VIOsPAPRDevice; diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c index e8426d7c5e..00e2d2d5d3 100644 --- a/hw/spapr_vscsi.c +++ b/hw/spapr_vscsi.c @@ -129,11 +129,38 @@ static void vscsi_put_req(vscsi_req *req) req->active = 0; } -static void vscsi_decode_id_lun(uint64_t srp_lun, int *id, int *lun) +static SCSIDevice *vscsi_device_find(SCSIBus *bus, uint64_t srp_lun, int *lun) { - /* XXX Figure that one out properly ! This is crackpot */ - *id = (srp_lun >> 56) & 0x7f; - *lun = (srp_lun >> 48) & 0xff; + int channel = 0, id = 0; + +retry: + switch (srp_lun >> 62) { + case 0: + if ((srp_lun >> 56) != 0) { + channel = (srp_lun >> 56) & 0x3f; + id = (srp_lun >> 48) & 0xff; + srp_lun <<= 16; + goto retry; + } + *lun = (srp_lun >> 48) & 0xff; + break; + + case 1: + *lun = (srp_lun >> 48) & 0x3fff; + break; + case 2: + channel = (srp_lun >> 53) & 0x7; + id = (srp_lun >> 56) & 0x3f; + *lun = (srp_lun >> 48) & 0x1f; + break; + case 3: + *lun = -1; + return NULL; + default: + abort(); + } + + return scsi_device_find(bus, channel, id, *lun); } static int vscsi_send_iu(VSCSIState *s, vscsi_req *req, @@ -582,14 +609,11 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req) { union srp_iu *srp = &req->iu.srp; SCSIDevice *sdev; - int n, id, lun; + int n, lun; - vscsi_decode_id_lun(be64_to_cpu(srp->cmd.lun), &id, &lun); - - /* Qemu vs. linux issue with LUNs to be sorted out ... */ - sdev = (id < 8 && lun < 16) ? s->bus.devs[id] : NULL; + sdev = vscsi_device_find(&s->bus, be64_to_cpu(srp->cmd.lun), &lun); if (!sdev) { - dprintf("VSCSI: Command for id %d with no drive\n", id); + dprintf("VSCSI: Command for lun %08" PRIx64 " with no drive\n", be64_to_cpu(srp->cmd.lun)); if (srp->cmd.cdb[0] == INQUIRY) { vscsi_inquiry_no_target(s, req); } else { @@ -862,7 +886,12 @@ static int vscsi_do_crq(struct VIOsPAPRDevice *dev, uint8_t *crq_data) return 0; } -static const struct SCSIBusOps vscsi_scsi_ops = { +static const struct SCSIBusInfo vscsi_scsi_info = { + .tcq = true, + .max_channel = 7, /* logical unit addressing format */ + .max_target = 63, + .max_lun = 31, + .transfer_data = vscsi_transfer_data, .complete = vscsi_command_complete, .cancel = vscsi_request_cancelled @@ -883,8 +912,7 @@ static int spapr_vscsi_init(VIOsPAPRDevice *dev) dev->crq.SendFunc = vscsi_do_crq; - scsi_bus_new(&s->bus, &dev->qdev, 1, VSCSI_REQ_LIMIT, - &vscsi_scsi_ops); + scsi_bus_new(&s->bus, &dev->qdev, &vscsi_scsi_info); if (!dev->qdev.hotplugged) { scsi_bus_legacy_handle_cmdline(&s->bus); } diff --git a/hw/spitz.c b/hw/spitz.c index 6f8a94ceb3..23f9d41ff7 100644 --- a/hw/spitz.c +++ b/hw/spitz.c @@ -49,6 +49,7 @@ typedef struct { SysBusDevice busdev; + MemoryRegion iomem; DeviceState *nand; uint8_t ctl; uint8_t manf_id; @@ -56,7 +57,7 @@ typedef struct { ECCState ecc; } SLNANDState; -static uint32_t sl_readb(void *opaque, target_phys_addr_t addr) +static uint64_t sl_read(void *opaque, target_phys_addr_t addr, unsigned size) { SLNANDState *s = (SLNANDState *) opaque; int ryby; @@ -86,6 +87,10 @@ static uint32_t sl_readb(void *opaque, target_phys_addr_t addr) return s->ctl; case FLASH_FLASHIO: + if (size == 4) { + return ecc_digest(&s->ecc, nand_getio(s->nand)) | + (ecc_digest(&s->ecc, nand_getio(s->nand)) << 16); + } return ecc_digest(&s->ecc, nand_getio(s->nand)); default: @@ -94,19 +99,8 @@ static uint32_t sl_readb(void *opaque, target_phys_addr_t addr) return 0; } -static uint32_t sl_readl(void *opaque, target_phys_addr_t addr) -{ - SLNANDState *s = (SLNANDState *) opaque; - - if (addr == FLASH_FLASHIO) - return ecc_digest(&s->ecc, nand_getio(s->nand)) | - (ecc_digest(&s->ecc, nand_getio(s->nand)) << 16); - - return sl_readb(opaque, addr); -} - -static void sl_writeb(void *opaque, target_phys_addr_t addr, - uint32_t value) +static void sl_write(void *opaque, target_phys_addr_t addr, + uint64_t value, unsigned size) { SLNANDState *s = (SLNANDState *) opaque; @@ -140,15 +134,10 @@ enum { FLASH_1024M, }; -static CPUReadMemoryFunc * const sl_readfn[] = { - sl_readb, - sl_readb, - sl_readl, -}; -static CPUWriteMemoryFunc * const sl_writefn[] = { - sl_writeb, - sl_writeb, - sl_writeb, +static const MemoryRegionOps sl_ops = { + .read = sl_read, + .write = sl_write, + .endianness = DEVICE_NATIVE_ENDIAN, }; static void sl_flash_register(PXA2xxState *cpu, int size) @@ -168,7 +157,6 @@ static void sl_flash_register(PXA2xxState *cpu, int size) } static int sl_nand_init(SysBusDevice *dev) { - int iomemtype; SLNANDState *s; DriveInfo *nand; @@ -178,10 +166,8 @@ static int sl_nand_init(SysBusDevice *dev) { nand = drive_get(IF_MTD, 0, 0); s->nand = nand_init(nand ? nand->bdrv : NULL, s->manf_id, s->chip_id); - iomemtype = cpu_register_io_memory(sl_readfn, - sl_writefn, s, DEVICE_NATIVE_ENDIAN); - - sysbus_init_mmio(dev, 0x40, iomemtype); + memory_region_init_io(&s->iomem, &sl_ops, s, "sl", 0x40); + sysbus_init_mmio_region(dev, &s->iomem); return 0; } @@ -898,6 +884,7 @@ static void spitz_common_init(ram_addr_t ram_size, PXA2xxState *cpu; DeviceState *scp0, *scp1 = NULL; MemoryRegion *address_space_mem = get_system_memory(); + MemoryRegion *rom = g_new(MemoryRegion, 1); if (!cpu_model) cpu_model = (model == terrier) ? "pxa270-c5" : "pxa270-c0"; @@ -907,8 +894,9 @@ static void spitz_common_init(ram_addr_t ram_size, sl_flash_register(cpu, (model == spitz) ? FLASH_128M : FLASH_1024M); - cpu_register_physical_memory(0, SPITZ_ROM, - qemu_ram_alloc(NULL, "spitz.rom", SPITZ_ROM) | IO_MEM_ROM); + memory_region_init_ram(rom, NULL, "spitz.rom", SPITZ_ROM); + memory_region_set_readonly(rom, true); + memory_region_add_subregion(address_space_mem, 0, rom); /* Setup peripherals */ spitz_keyboard_register(cpu); diff --git a/hw/ssi.c b/hw/ssi.c index 3f4c5f9f06..9842fe7472 100644 --- a/hw/ssi.c +++ b/hw/ssi.c @@ -25,8 +25,8 @@ static int ssi_slave_init(DeviceState *dev, DeviceInfo *base_info) SSIBus *bus; bus = FROM_QBUS(SSIBus, qdev_get_parent_bus(dev)); - if (QLIST_FIRST(&bus->qbus.children) != dev - || QLIST_NEXT(dev, sibling) != NULL) { + if (QTAILQ_FIRST(&bus->qbus.children) != dev + || QTAILQ_NEXT(dev, sibling) != NULL) { hw_error("Too many devices on SSI bus"); } @@ -61,7 +61,7 @@ uint32_t ssi_transfer(SSIBus *bus, uint32_t val) { DeviceState *dev; SSISlave *slave; - dev = QLIST_FIRST(&bus->qbus.children); + dev = QTAILQ_FIRST(&bus->qbus.children); if (!dev) { return 0; } diff --git a/hw/strongarm.c b/hw/strongarm.c index 6097ea2c18..a3d908051f 100644 --- a/hw/strongarm.c +++ b/hw/strongarm.c @@ -68,6 +68,7 @@ static struct { /* Interrupt Controller */ typedef struct { SysBusDevice busdev; + MemoryRegion iomem; qemu_irq irq; qemu_irq fiq; @@ -109,7 +110,8 @@ static void strongarm_pic_set_irq(void *opaque, int irq, int level) strongarm_pic_update(s); } -static uint32_t strongarm_pic_mem_read(void *opaque, target_phys_addr_t offset) +static uint64_t strongarm_pic_mem_read(void *opaque, target_phys_addr_t offset, + unsigned size) { StrongARMPICState *s = opaque; @@ -134,7 +136,7 @@ static uint32_t strongarm_pic_mem_read(void *opaque, target_phys_addr_t offset) } static void strongarm_pic_mem_write(void *opaque, target_phys_addr_t offset, - uint32_t value) + uint64_t value, unsigned size) { StrongARMPICState *s = opaque; @@ -156,27 +158,19 @@ static void strongarm_pic_mem_write(void *opaque, target_phys_addr_t offset, strongarm_pic_update(s); } -static CPUReadMemoryFunc * const strongarm_pic_readfn[] = { - strongarm_pic_mem_read, - strongarm_pic_mem_read, - strongarm_pic_mem_read, -}; - -static CPUWriteMemoryFunc * const strongarm_pic_writefn[] = { - strongarm_pic_mem_write, - strongarm_pic_mem_write, - strongarm_pic_mem_write, +static const MemoryRegionOps strongarm_pic_ops = { + .read = strongarm_pic_mem_read, + .write = strongarm_pic_mem_write, + .endianness = DEVICE_NATIVE_ENDIAN, }; static int strongarm_pic_initfn(SysBusDevice *dev) { StrongARMPICState *s = FROM_SYSBUS(StrongARMPICState, dev); - int iomemtype; qdev_init_gpio_in(&dev->qdev, strongarm_pic_set_irq, SA_PIC_SRCS); - iomemtype = cpu_register_io_memory(strongarm_pic_readfn, - strongarm_pic_writefn, s, DEVICE_NATIVE_ENDIAN); - sysbus_init_mmio(dev, 0x1000, iomemtype); + memory_region_init_io(&s->iomem, &strongarm_pic_ops, s, "pic", 0x1000); + sysbus_init_mmio_region(dev, &s->iomem); sysbus_init_irq(dev, &s->irq); sysbus_init_irq(dev, &s->fiq); @@ -229,6 +223,7 @@ static SysBusDeviceInfo strongarm_pic_info = { typedef struct { SysBusDevice busdev; + MemoryRegion iomem; uint32_t rttr; uint32_t rtsr; uint32_t rtar; @@ -287,7 +282,8 @@ static inline void strongarm_rtc_hz_tick(void *opaque) strongarm_rtc_int_update(s); } -static uint32_t strongarm_rtc_read(void *opaque, target_phys_addr_t addr) +static uint64_t strongarm_rtc_read(void *opaque, target_phys_addr_t addr, + unsigned size) { StrongARMRTCState *s = opaque; @@ -309,7 +305,7 @@ static uint32_t strongarm_rtc_read(void *opaque, target_phys_addr_t addr) } static void strongarm_rtc_write(void *opaque, target_phys_addr_t addr, - uint32_t value) + uint64_t value, unsigned size) { StrongARMRTCState *s = opaque; uint32_t old_rtsr; @@ -349,23 +345,16 @@ static void strongarm_rtc_write(void *opaque, target_phys_addr_t addr, } } -static CPUReadMemoryFunc * const strongarm_rtc_readfn[] = { - strongarm_rtc_read, - strongarm_rtc_read, - strongarm_rtc_read, -}; - -static CPUWriteMemoryFunc * const strongarm_rtc_writefn[] = { - strongarm_rtc_write, - strongarm_rtc_write, - strongarm_rtc_write, +static const MemoryRegionOps strongarm_rtc_ops = { + .read = strongarm_rtc_read, + .write = strongarm_rtc_write, + .endianness = DEVICE_NATIVE_ENDIAN, }; static int strongarm_rtc_init(SysBusDevice *dev) { StrongARMRTCState *s = FROM_SYSBUS(StrongARMRTCState, dev); struct tm tm; - int iomemtype; s->rttr = 0x0; s->rtsr = 0; @@ -381,9 +370,8 @@ static int strongarm_rtc_init(SysBusDevice *dev) sysbus_init_irq(dev, &s->rtc_irq); sysbus_init_irq(dev, &s->rtc_hz_irq); - iomemtype = cpu_register_io_memory(strongarm_rtc_readfn, - strongarm_rtc_writefn, s, DEVICE_NATIVE_ENDIAN); - sysbus_init_mmio(dev, 0x10000, iomemtype); + memory_region_init_io(&s->iomem, &strongarm_rtc_ops, s, "rtc", 0x10000); + sysbus_init_mmio_region(dev, &s->iomem); return 0; } @@ -443,6 +431,7 @@ static SysBusDeviceInfo strongarm_rtc_sysbus_info = { typedef struct StrongARMGPIOInfo StrongARMGPIOInfo; struct StrongARMGPIOInfo { SysBusDevice busdev; + MemoryRegion iomem; qemu_irq handler[28]; qemu_irq irqs[11]; qemu_irq irqX; @@ -507,7 +496,8 @@ static void strongarm_gpio_handler_update(StrongARMGPIOInfo *s) s->prev_level = level; } -static uint32_t strongarm_gpio_read(void *opaque, target_phys_addr_t offset) +static uint64_t strongarm_gpio_read(void *opaque, target_phys_addr_t offset, + unsigned size) { StrongARMGPIOInfo *s = opaque; @@ -548,8 +538,8 @@ static uint32_t strongarm_gpio_read(void *opaque, target_phys_addr_t offset) return 0; } -static void strongarm_gpio_write(void *opaque, - target_phys_addr_t offset, uint32_t value) +static void strongarm_gpio_write(void *opaque, target_phys_addr_t offset, + uint64_t value, unsigned size) { StrongARMGPIOInfo *s = opaque; @@ -592,16 +582,10 @@ static void strongarm_gpio_write(void *opaque, } } -static CPUReadMemoryFunc * const strongarm_gpio_readfn[] = { - strongarm_gpio_read, - strongarm_gpio_read, - strongarm_gpio_read -}; - -static CPUWriteMemoryFunc * const strongarm_gpio_writefn[] = { - strongarm_gpio_write, - strongarm_gpio_write, - strongarm_gpio_write +static const MemoryRegionOps strongarm_gpio_ops = { + .read = strongarm_gpio_read, + .write = strongarm_gpio_write, + .endianness = DEVICE_NATIVE_ENDIAN, }; static DeviceState *strongarm_gpio_init(target_phys_addr_t base, @@ -623,7 +607,6 @@ static DeviceState *strongarm_gpio_init(target_phys_addr_t base, static int strongarm_gpio_initfn(SysBusDevice *dev) { - int iomemtype; StrongARMGPIOInfo *s; int i; @@ -632,10 +615,9 @@ static int strongarm_gpio_initfn(SysBusDevice *dev) qdev_init_gpio_in(&dev->qdev, strongarm_gpio_set, 28); qdev_init_gpio_out(&dev->qdev, s->handler, 28); - iomemtype = cpu_register_io_memory(strongarm_gpio_readfn, - strongarm_gpio_writefn, s, DEVICE_NATIVE_ENDIAN); + memory_region_init_io(&s->iomem, &strongarm_gpio_ops, s, "gpio", 0x1000); - sysbus_init_mmio(dev, 0x1000, iomemtype); + sysbus_init_mmio_region(dev, &s->iomem); for (i = 0; i < 11; i++) { sysbus_init_irq(dev, &s->irqs[i]); } @@ -678,6 +660,7 @@ static SysBusDeviceInfo strongarm_gpio_info = { typedef struct StrongARMPPCInfo StrongARMPPCInfo; struct StrongARMPPCInfo { SysBusDevice busdev; + MemoryRegion iomem; qemu_irq handler[28]; uint32_t ilevel; @@ -716,7 +699,8 @@ static void strongarm_ppc_handler_update(StrongARMPPCInfo *s) s->prev_level = level; } -static uint32_t strongarm_ppc_read(void *opaque, target_phys_addr_t offset) +static uint64_t strongarm_ppc_read(void *opaque, target_phys_addr_t offset, + unsigned size) { StrongARMPPCInfo *s = opaque; @@ -745,8 +729,8 @@ static uint32_t strongarm_ppc_read(void *opaque, target_phys_addr_t offset) return 0; } -static void strongarm_ppc_write(void *opaque, - target_phys_addr_t offset, uint32_t value) +static void strongarm_ppc_write(void *opaque, target_phys_addr_t offset, + uint64_t value, unsigned size) { StrongARMPPCInfo *s = opaque; @@ -778,21 +762,14 @@ static void strongarm_ppc_write(void *opaque, } } -static CPUReadMemoryFunc * const strongarm_ppc_readfn[] = { - strongarm_ppc_read, - strongarm_ppc_read, - strongarm_ppc_read -}; - -static CPUWriteMemoryFunc * const strongarm_ppc_writefn[] = { - strongarm_ppc_write, - strongarm_ppc_write, - strongarm_ppc_write +static const MemoryRegionOps strongarm_ppc_ops = { + .read = strongarm_ppc_read, + .write = strongarm_ppc_write, + .endianness = DEVICE_NATIVE_ENDIAN, }; static int strongarm_ppc_init(SysBusDevice *dev) { - int iomemtype; StrongARMPPCInfo *s; s = FROM_SYSBUS(StrongARMPPCInfo, dev); @@ -800,10 +777,9 @@ static int strongarm_ppc_init(SysBusDevice *dev) qdev_init_gpio_in(&dev->qdev, strongarm_ppc_set, 22); qdev_init_gpio_out(&dev->qdev, s->handler, 22); - iomemtype = cpu_register_io_memory(strongarm_ppc_readfn, - strongarm_ppc_writefn, s, DEVICE_NATIVE_ENDIAN); + memory_region_init_io(&s->iomem, &strongarm_ppc_ops, s, "ppc", 0x1000); - sysbus_init_mmio(dev, 0x1000, iomemtype); + sysbus_init_mmio_region(dev, &s->iomem); return 0; } @@ -871,6 +847,7 @@ static SysBusDeviceInfo strongarm_ppc_info = { typedef struct { SysBusDevice busdev; + MemoryRegion iomem; CharDriverState *chr; qemu_irq irq; @@ -1079,7 +1056,8 @@ static void strongarm_uart_tx(void *opaque) strongarm_uart_update_int_status(s); } -static uint32_t strongarm_uart_read(void *opaque, target_phys_addr_t addr) +static uint64_t strongarm_uart_read(void *opaque, target_phys_addr_t addr, + unsigned size) { StrongARMUARTState *s = opaque; uint16_t ret; @@ -1121,7 +1099,7 @@ static uint32_t strongarm_uart_read(void *opaque, target_phys_addr_t addr) } static void strongarm_uart_write(void *opaque, target_phys_addr_t addr, - uint32_t value) + uint64_t value, unsigned size) { StrongARMUARTState *s = opaque; @@ -1176,26 +1154,18 @@ static void strongarm_uart_write(void *opaque, target_phys_addr_t addr, } } -static CPUReadMemoryFunc * const strongarm_uart_readfn[] = { - strongarm_uart_read, - strongarm_uart_read, - strongarm_uart_read, -}; - -static CPUWriteMemoryFunc * const strongarm_uart_writefn[] = { - strongarm_uart_write, - strongarm_uart_write, - strongarm_uart_write, +static const MemoryRegionOps strongarm_uart_ops = { + .read = strongarm_uart_read, + .write = strongarm_uart_write, + .endianness = DEVICE_NATIVE_ENDIAN, }; static int strongarm_uart_init(SysBusDevice *dev) { StrongARMUARTState *s = FROM_SYSBUS(StrongARMUARTState, dev); - int iomemtype; - iomemtype = cpu_register_io_memory(strongarm_uart_readfn, - strongarm_uart_writefn, s, DEVICE_NATIVE_ENDIAN); - sysbus_init_mmio(dev, 0x10000, iomemtype); + memory_region_init_io(&s->iomem, &strongarm_uart_ops, s, "uart", 0x10000); + sysbus_init_mmio_region(dev, &s->iomem); sysbus_init_irq(dev, &s->irq); s->rx_timeout_timer = qemu_new_timer_ns(vm_clock, strongarm_uart_rx_to, s); @@ -1288,6 +1258,7 @@ static SysBusDeviceInfo strongarm_uart_info = { /* Synchronous Serial Ports */ typedef struct { SysBusDevice busdev; + MemoryRegion iomem; qemu_irq irq; SSIBus *bus; @@ -1355,7 +1326,8 @@ static void strongarm_ssp_fifo_update(StrongARMSSPState *s) strongarm_ssp_int_update(s); } -static uint32_t strongarm_ssp_read(void *opaque, target_phys_addr_t addr) +static uint64_t strongarm_ssp_read(void *opaque, target_phys_addr_t addr, + unsigned size) { StrongARMSSPState *s = opaque; uint32_t retval; @@ -1388,7 +1360,7 @@ static uint32_t strongarm_ssp_read(void *opaque, target_phys_addr_t addr) } static void strongarm_ssp_write(void *opaque, target_phys_addr_t addr, - uint32_t value) + uint64_t value, unsigned size) { StrongARMSSPState *s = opaque; @@ -1397,7 +1369,7 @@ static void strongarm_ssp_write(void *opaque, target_phys_addr_t addr, s->sscr[0] = value & 0xffbf; if ((s->sscr[0] & SSCR0_SSE) && SSCR0_DSS(value) < 4) { printf("%s: Wrong data size: %i bits\n", __func__, - SSCR0_DSS(value)); + (int)SSCR0_DSS(value)); } if (!(value & SSCR0_SSE)) { s->sssr = 0; @@ -1452,16 +1424,10 @@ static void strongarm_ssp_write(void *opaque, target_phys_addr_t addr, } } -static CPUReadMemoryFunc * const strongarm_ssp_readfn[] = { - strongarm_ssp_read, - strongarm_ssp_read, - strongarm_ssp_read, -}; - -static CPUWriteMemoryFunc * const strongarm_ssp_writefn[] = { - strongarm_ssp_write, - strongarm_ssp_write, - strongarm_ssp_write, +static const MemoryRegionOps strongarm_ssp_ops = { + .read = strongarm_ssp_read, + .write = strongarm_ssp_write, + .endianness = DEVICE_NATIVE_ENDIAN, }; static int strongarm_ssp_post_load(void *opaque, int version_id) @@ -1475,15 +1441,12 @@ static int strongarm_ssp_post_load(void *opaque, int version_id) static int strongarm_ssp_init(SysBusDevice *dev) { - int iomemtype; StrongARMSSPState *s = FROM_SYSBUS(StrongARMSSPState, dev); sysbus_init_irq(dev, &s->irq); - iomemtype = cpu_register_io_memory(strongarm_ssp_readfn, - strongarm_ssp_writefn, s, - DEVICE_NATIVE_ENDIAN); - sysbus_init_mmio(dev, 0x1000, iomemtype); + memory_region_init_io(&s->iomem, &strongarm_ssp_ops, s, "ssp", 0x1000); + sysbus_init_mmio_region(dev, &s->iomem); s->bus = ssi_create_bus(&dev->qdev, "ssi"); return 0; @@ -1523,7 +1486,8 @@ static SysBusDeviceInfo strongarm_ssp_info = { }; /* Main CPU functions */ -StrongARMState *sa1110_init(unsigned int sdram_size, const char *rev) +StrongARMState *sa1110_init(MemoryRegion *sysmem, + unsigned int sdram_size, const char *rev) { StrongARMState *s; qemu_irq *pic; @@ -1547,9 +1511,8 @@ StrongARMState *sa1110_init(unsigned int sdram_size, const char *rev) exit(1); } - cpu_register_physical_memory(SA_SDCS0, - sdram_size, qemu_ram_alloc(NULL, "strongarm.sdram", - sdram_size) | IO_MEM_RAM); + memory_region_init_ram(&s->sdram, NULL, "strongarm.sdram", sdram_size); + memory_region_add_subregion(sysmem, SA_SDCS0, &s->sdram); pic = arm_pic_init_cpu(s->env); s->pic = sysbus_create_varargs("strongarm_pic", 0x90050000, diff --git a/hw/strongarm.h b/hw/strongarm.h index a81b110e23..684f61bee3 100644 --- a/hw/strongarm.h +++ b/hw/strongarm.h @@ -1,6 +1,8 @@ #ifndef _STRONGARM_H #define _STRONGARM_H +#include "memory.h" + #define SA_CS0 0x00000000 #define SA_CS1 0x08000000 #define SA_CS2 0x10000000 @@ -52,6 +54,7 @@ enum { typedef struct { CPUState *env; + MemoryRegion sdram; DeviceState *pic; DeviceState *gpio; DeviceState *ppc; @@ -59,6 +62,7 @@ typedef struct { SSIBus *ssp_bus; } StrongARMState; -StrongARMState *sa1110_init(unsigned int sdram_size, const char *rev); +StrongARMState *sa1110_init(MemoryRegion *sysmem, + unsigned int sdram_size, const char *rev); #endif diff --git a/hw/sun4m.c b/hw/sun4m.c index dcaed38773..314edc4d87 100644 --- a/hw/sun4m.c +++ b/hw/sun4m.c @@ -216,13 +216,13 @@ static void nvram_init(M48t59State *nvram, uint8_t *macaddr, static DeviceState *slavio_intctl; -void pic_info(Monitor *mon) +void sun4m_pic_info(Monitor *mon) { if (slavio_intctl) slavio_pic_info(mon, slavio_intctl); } -void irq_info(Monitor *mon) +void sun4m_irq_info(Monitor *mon) { if (slavio_intctl) slavio_irq_info(mon, slavio_intctl); @@ -593,19 +593,25 @@ static void idreg_init(target_phys_addr_t addr) cpu_physical_memory_write_rom(addr, idreg_data, sizeof(idreg_data)); } +typedef struct IDRegState { + SysBusDevice busdev; + MemoryRegion mem; +} IDRegState; + static int idreg_init1(SysBusDevice *dev) { - ram_addr_t idreg_offset; + IDRegState *s = FROM_SYSBUS(IDRegState, dev); - idreg_offset = qemu_ram_alloc(NULL, "sun4m.idreg", sizeof(idreg_data)); - sysbus_init_mmio(dev, sizeof(idreg_data), idreg_offset | IO_MEM_ROM); + memory_region_init_ram(&s->mem, NULL, "sun4m.idreg", sizeof(idreg_data)); + memory_region_set_readonly(&s->mem, true); + sysbus_init_mmio_region(dev, &s->mem); return 0; } static SysBusDeviceInfo idreg_info = { .init = idreg_init1, .qdev.name = "macio_idreg", - .qdev.size = sizeof(SysBusDevice), + .qdev.size = sizeof(IDRegState), }; static void idreg_register_devices(void) @@ -615,6 +621,11 @@ static void idreg_register_devices(void) device_init(idreg_register_devices); +typedef struct AFXState { + SysBusDevice busdev; + MemoryRegion mem; +} AFXState; + /* SS-5 TCX AFX register */ static void afx_init(target_phys_addr_t addr) { @@ -630,17 +641,17 @@ static void afx_init(target_phys_addr_t addr) static int afx_init1(SysBusDevice *dev) { - ram_addr_t afx_offset; + AFXState *s = FROM_SYSBUS(AFXState, dev); - afx_offset = qemu_ram_alloc(NULL, "sun4m.afx", 4); - sysbus_init_mmio(dev, 4, afx_offset | IO_MEM_RAM); + memory_region_init_ram(&s->mem, NULL, "sun4m.afx", 4); + sysbus_init_mmio_region(dev, &s->mem); return 0; } static SysBusDeviceInfo afx_info = { .init = afx_init1, .qdev.name = "tcx_afx", - .qdev.size = sizeof(SysBusDevice), + .qdev.size = sizeof(AFXState), }; static void afx_register_devices(void) @@ -650,6 +661,11 @@ static void afx_register_devices(void) device_init(afx_register_devices); +typedef struct PROMState { + SysBusDevice busdev; + MemoryRegion prom; +} PROMState; + /* Boot PROM (OpenBIOS) */ static uint64_t translate_prom_address(void *opaque, uint64_t addr) { @@ -693,17 +709,18 @@ static void prom_init(target_phys_addr_t addr, const char *bios_name) static int prom_init1(SysBusDevice *dev) { - ram_addr_t prom_offset; + PROMState *s = FROM_SYSBUS(PROMState, dev); - prom_offset = qemu_ram_alloc(NULL, "sun4m.prom", PROM_SIZE_MAX); - sysbus_init_mmio(dev, PROM_SIZE_MAX, prom_offset | IO_MEM_ROM); + memory_region_init_ram(&s->prom, NULL, "sun4m.prom", PROM_SIZE_MAX); + memory_region_set_readonly(&s->prom, true); + sysbus_init_mmio_region(dev, &s->prom); return 0; } static SysBusDeviceInfo prom_info = { .init = prom_init1, .qdev.name = "openprom", - .qdev.size = sizeof(SysBusDevice), + .qdev.size = sizeof(PROMState), .qdev.props = (Property[]) { {/* end of property list */} } @@ -719,19 +736,17 @@ device_init(prom_register_devices); typedef struct RamDevice { SysBusDevice busdev; + MemoryRegion ram; uint64_t size; } RamDevice; /* System RAM */ static int ram_init1(SysBusDevice *dev) { - ram_addr_t RAM_size, ram_offset; RamDevice *d = FROM_SYSBUS(RamDevice, dev); - RAM_size = d->size; - - ram_offset = qemu_ram_alloc(NULL, "sun4m.ram", RAM_size); - sysbus_init_mmio(dev, RAM_size, ram_offset); + memory_region_init_ram(&d->ram, NULL, "sun4m.ram", d->size); + sysbus_init_mmio_region(dev, &d->ram); return 0; } diff --git a/hw/sun4m.h b/hw/sun4m.h index ce97ee5a79..504c3af413 100644 --- a/hw/sun4m.h +++ b/hw/sun4m.h @@ -30,6 +30,10 @@ void slavio_irq_info(Monitor *mon, DeviceState *dev); void sun4c_pic_info(Monitor *mon, void *opaque); void sun4c_irq_info(Monitor *mon, void *opaque); +/* sun4m.c */ +void sun4m_pic_info(Monitor *mon); +void sun4m_irq_info(Monitor *mon); + /* sparc32_dma.c */ #include "sparc32_dma.h" diff --git a/hw/sun4u.c b/hw/sun4u.c index 88c633d491..eaaefe3c94 100644 --- a/hw/sun4u.c +++ b/hw/sun4u.c @@ -243,14 +243,6 @@ static unsigned long sun4u_load_kernel(const char *kernel_filename, return kernel_size; } -void pic_info(Monitor *mon) -{ -} - -void irq_info(Monitor *mon) -{ -} - void cpu_check_irqs(CPUState *env) { uint32_t pil = env->pil_in | @@ -582,6 +574,11 @@ static void pci_ebus_register(void) device_init(pci_ebus_register); +typedef struct PROMState { + SysBusDevice busdev; + MemoryRegion prom; +} PROMState; + static uint64_t translate_prom_address(void *opaque, uint64_t addr) { target_phys_addr_t *base_addr = (target_phys_addr_t *)opaque; @@ -625,17 +622,18 @@ static void prom_init(target_phys_addr_t addr, const char *bios_name) static int prom_init1(SysBusDevice *dev) { - ram_addr_t prom_offset; + PROMState *s = FROM_SYSBUS(PROMState, dev); - prom_offset = qemu_ram_alloc(NULL, "sun4u.prom", PROM_SIZE_MAX); - sysbus_init_mmio(dev, PROM_SIZE_MAX, prom_offset | IO_MEM_ROM); + memory_region_init_ram(&s->prom, NULL, "sun4u.prom", PROM_SIZE_MAX); + memory_region_set_readonly(&s->prom, true); + sysbus_init_mmio_region(dev, &s->prom); return 0; } static SysBusDeviceInfo prom_info = { .init = prom_init1, .qdev.name = "openprom", - .qdev.size = sizeof(SysBusDevice), + .qdev.size = sizeof(PROMState), .qdev.props = (Property[]) { {/* end of property list */} } @@ -652,19 +650,17 @@ device_init(prom_register_devices); typedef struct RamDevice { SysBusDevice busdev; + MemoryRegion ram; uint64_t size; } RamDevice; /* System RAM */ static int ram_init1(SysBusDevice *dev) { - ram_addr_t RAM_size, ram_offset; RamDevice *d = FROM_SYSBUS(RamDevice, dev); - RAM_size = d->size; - - ram_offset = qemu_ram_alloc(NULL, "sun4u.ram", RAM_size); - sysbus_init_mmio(dev, RAM_size, ram_offset); + memory_region_init_ram(&d->ram, NULL, "sun4u.ram", d->size); + sysbus_init_mmio_region(dev, &d->ram); return 0; } diff --git a/hw/syborg.c b/hw/syborg.c index bc200e48aa..248de54c4e 100644 --- a/hw/syborg.c +++ b/hw/syborg.c @@ -26,6 +26,7 @@ #include "boards.h" #include "arm-misc.h" #include "net.h" +#include "exec-memory.h" static struct arm_boot_info syborg_binfo; @@ -35,9 +36,10 @@ static void syborg_init(ram_addr_t ram_size, const char *initrd_filename, const char *cpu_model) { CPUState *env; + MemoryRegion *sysmem = get_system_memory(); + MemoryRegion *ram = g_new(MemoryRegion, 1); qemu_irq *cpu_pic; qemu_irq pic[64]; - ram_addr_t ram_addr; DeviceState *dev; int i; @@ -50,8 +52,8 @@ static void syborg_init(ram_addr_t ram_size, } /* RAM at address zero. */ - ram_addr = qemu_ram_alloc(NULL, "syborg.ram", ram_size); - cpu_register_physical_memory(0, ram_size, ram_addr | IO_MEM_RAM); + memory_region_init_ram(ram, NULL, "syborg.ram", ram_size); + memory_region_add_subregion(sysmem, 0, ram); cpu_pic = arm_pic_init_cpu(env); dev = sysbus_create_simple("syborg,interrupt", 0xC0000000, diff --git a/hw/sysbus.c b/hw/sysbus.c index 4fab5a41b2..fd2fc6a51d 100644 --- a/hw/sysbus.c +++ b/hw/sysbus.c @@ -198,6 +198,7 @@ DeviceState *sysbus_create_varargs(const char *name, sysbus_connect_irq(s, n, irq); n++; } + va_end(va); return dev; } @@ -229,6 +230,7 @@ DeviceState *sysbus_try_create_varargs(const char *name, sysbus_connect_irq(s, n, irq); n++; } + va_end(va); return dev; } diff --git a/hw/tc6393xb.c b/hw/tc6393xb.c index c28005a86b..c144dcf5ff 100644 --- a/hw/tc6393xb.c +++ b/hw/tc6393xb.c @@ -79,6 +79,7 @@ #define NAND_MODE_ECC_RST 0x60 struct TC6393xbState { + MemoryRegion iomem; qemu_irq irq; qemu_irq *sub_irqs; struct { @@ -122,7 +123,7 @@ struct TC6393xbState { ECCState ecc; DisplayState *ds; - ram_addr_t vram_addr; + MemoryRegion vram; uint16_t *vram_ptr; uint32_t scr_width, scr_height; /* in pixels */ qemu_irq l3v; @@ -495,7 +496,9 @@ static void tc6393xb_update_display(void *opaque) } -static uint32_t tc6393xb_readb(void *opaque, target_phys_addr_t addr) { +static uint64_t tc6393xb_readb(void *opaque, target_phys_addr_t addr, + unsigned size) +{ TC6393xbState *s = opaque; switch (addr >> 8) { @@ -516,7 +519,8 @@ static uint32_t tc6393xb_readb(void *opaque, target_phys_addr_t addr) { return 0; } -static void tc6393xb_writeb(void *opaque, target_phys_addr_t addr, uint32_t value) { +static void tc6393xb_writeb(void *opaque, target_phys_addr_t addr, + uint64_t value, unsigned size) { TC6393xbState *s = opaque; switch (addr >> 8) { @@ -532,51 +536,21 @@ static void tc6393xb_writeb(void *opaque, target_phys_addr_t addr, uint32_t valu tc6393xb_nand_writeb(s, addr & 0xff, value); else fprintf(stderr, "tc6393xb: unhandled write at %08x: %02x\n", - (uint32_t) addr, value & 0xff); -} - -static uint32_t tc6393xb_readw(void *opaque, target_phys_addr_t addr) -{ - return (tc6393xb_readb(opaque, addr) & 0xff) | - (tc6393xb_readb(opaque, addr + 1) << 8); -} - -static uint32_t tc6393xb_readl(void *opaque, target_phys_addr_t addr) -{ - return (tc6393xb_readb(opaque, addr) & 0xff) | - ((tc6393xb_readb(opaque, addr + 1) & 0xff) << 8) | - ((tc6393xb_readb(opaque, addr + 2) & 0xff) << 16) | - ((tc6393xb_readb(opaque, addr + 3) & 0xff) << 24); + (uint32_t) addr, (int)value & 0xff); } -static void tc6393xb_writew(void *opaque, target_phys_addr_t addr, uint32_t value) +TC6393xbState *tc6393xb_init(MemoryRegion *sysmem, uint32_t base, qemu_irq irq) { - tc6393xb_writeb(opaque, addr, value); - tc6393xb_writeb(opaque, addr + 1, value >> 8); -} - -static void tc6393xb_writel(void *opaque, target_phys_addr_t addr, uint32_t value) -{ - tc6393xb_writeb(opaque, addr, value); - tc6393xb_writeb(opaque, addr + 1, value >> 8); - tc6393xb_writeb(opaque, addr + 2, value >> 16); - tc6393xb_writeb(opaque, addr + 3, value >> 24); -} - -TC6393xbState *tc6393xb_init(uint32_t base, qemu_irq irq) -{ - int iomemtype; TC6393xbState *s; DriveInfo *nand; - CPUReadMemoryFunc * const tc6393xb_readfn[] = { - tc6393xb_readb, - tc6393xb_readw, - tc6393xb_readl, - }; - CPUWriteMemoryFunc * const tc6393xb_writefn[] = { - tc6393xb_writeb, - tc6393xb_writew, - tc6393xb_writel, + static const MemoryRegionOps tc6393xb_ops = { + .read = tc6393xb_readb, + .write = tc6393xb_writeb, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, }; s = (TC6393xbState *) g_malloc0(sizeof(TC6393xbState)); @@ -591,13 +565,12 @@ TC6393xbState *tc6393xb_init(uint32_t base, qemu_irq irq) nand = drive_get(IF_MTD, 0, 0); s->flash = nand_init(nand ? nand->bdrv : NULL, NAND_MFR_TOSHIBA, 0x76); - iomemtype = cpu_register_io_memory(tc6393xb_readfn, - tc6393xb_writefn, s, DEVICE_NATIVE_ENDIAN); - cpu_register_physical_memory(base, 0x10000, iomemtype); + memory_region_init_io(&s->iomem, &tc6393xb_ops, s, "tc6393xb", 0x10000); + memory_region_add_subregion(sysmem, base, &s->iomem); - s->vram_addr = qemu_ram_alloc(NULL, "tc6393xb.vram", 0x100000); - s->vram_ptr = qemu_get_ram_ptr(s->vram_addr); - cpu_register_physical_memory(base + 0x100000, 0x100000, s->vram_addr); + memory_region_init_ram(&s->vram, NULL, "tc6393xb.vram", 0x100000); + s->vram_ptr = memory_region_get_ram_ptr(&s->vram); + memory_region_add_subregion(sysmem, base + 0x100000, &s->vram); s->scr_width = 480; s->scr_height = 640; s->ds = graphic_console_init(tc6393xb_update_display, diff --git a/hw/tcx.c b/hw/tcx.c index 309600d24c..cd24100e14 100644 --- a/hw/tcx.c +++ b/hw/tcx.c @@ -40,7 +40,15 @@ typedef struct TCXState { DisplayState *ds; uint8_t *vram; uint32_t *vram24, *cplane; - ram_addr_t vram_offset, vram24_offset, cplane_offset; + MemoryRegion vram_mem; + MemoryRegion vram_8bit; + MemoryRegion vram_24bit; + MemoryRegion vram_cplane; + MemoryRegion dac; + MemoryRegion tec; + MemoryRegion thc24; + MemoryRegion thc8; + ram_addr_t vram24_offset, cplane_offset; uint32_t vram_size; uint32_t palette[256]; uint8_t r[256], g[256], b[256]; @@ -56,7 +64,7 @@ static void tcx_set_dirty(TCXState *s) unsigned int i; for (i = 0; i < MAXX * MAXY; i += TARGET_PAGE_SIZE) { - cpu_physical_memory_set_dirty(s->vram_offset + i); + memory_region_set_dirty(&s->vram_mem, i); } } @@ -65,8 +73,8 @@ static void tcx24_set_dirty(TCXState *s) unsigned int i; for (i = 0; i < MAXX * MAXY * 4; i += TARGET_PAGE_SIZE) { - cpu_physical_memory_set_dirty(s->vram24_offset + i); - cpu_physical_memory_set_dirty(s->cplane_offset + i); + memory_region_set_dirty(&s->vram_mem, s->vram24_offset + i); + memory_region_set_dirty(&s->vram_mem, s->cplane_offset + i); } } @@ -174,16 +182,18 @@ static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d, } } -static inline int check_dirty(ram_addr_t page, ram_addr_t page24, +static inline int check_dirty(TCXState *s, ram_addr_t page, ram_addr_t page24, ram_addr_t cpage) { int ret; unsigned int off; - ret = cpu_physical_memory_get_dirty(page, VGA_DIRTY_FLAG); + ret = memory_region_get_dirty(&s->vram_mem, page, DIRTY_MEMORY_VGA); for (off = 0; off < TARGET_PAGE_SIZE * 4; off += TARGET_PAGE_SIZE) { - ret |= cpu_physical_memory_get_dirty(page24 + off, VGA_DIRTY_FLAG); - ret |= cpu_physical_memory_get_dirty(cpage + off, VGA_DIRTY_FLAG); + ret |= memory_region_get_dirty(&s->vram_mem, page24 + off, + DIRTY_MEMORY_VGA); + ret |= memory_region_get_dirty(&s->vram_mem, cpage + off, + DIRTY_MEMORY_VGA); } return ret; } @@ -192,16 +202,17 @@ static inline void reset_dirty(TCXState *ts, ram_addr_t page_min, ram_addr_t page_max, ram_addr_t page24, ram_addr_t cpage) { - cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE, - VGA_DIRTY_FLAG); - page_min -= ts->vram_offset; - page_max -= ts->vram_offset; - cpu_physical_memory_reset_dirty(page24 + page_min * 4, - page24 + page_max * 4 + TARGET_PAGE_SIZE, - VGA_DIRTY_FLAG); - cpu_physical_memory_reset_dirty(cpage + page_min * 4, - cpage + page_max * 4 + TARGET_PAGE_SIZE, - VGA_DIRTY_FLAG); + memory_region_reset_dirty(&ts->vram_mem, + page_min, page_max + TARGET_PAGE_SIZE, + DIRTY_MEMORY_VGA); + memory_region_reset_dirty(&ts->vram_mem, + page24 + page_min * 4, + page24 + page_max * 4 + TARGET_PAGE_SIZE, + DIRTY_MEMORY_VGA); + memory_region_reset_dirty(&ts->vram_mem, + cpage + page_min * 4, + cpage + page_max * 4 + TARGET_PAGE_SIZE, + DIRTY_MEMORY_VGA); } /* Fixed line length 1024 allows us to do nice tricks not possible on @@ -216,7 +227,7 @@ static void tcx_update_display(void *opaque) if (ds_get_bits_per_pixel(ts->ds) == 0) return; - page = ts->vram_offset; + page = 0; y_start = -1; page_min = -1; page_max = 0; @@ -242,7 +253,7 @@ static void tcx_update_display(void *opaque) } for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE) { - if (cpu_physical_memory_get_dirty(page, VGA_DIRTY_FLAG)) { + if (memory_region_get_dirty(&ts->vram_mem, page, DIRTY_MEMORY_VGA)) { if (y_start < 0) y_start = y; if (page < page_min) @@ -279,8 +290,9 @@ static void tcx_update_display(void *opaque) } /* reset modified pages */ if (page_max >= page_min) { - cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE, - VGA_DIRTY_FLAG); + memory_region_reset_dirty(&ts->vram_mem, + page_min, page_max + TARGET_PAGE_SIZE, + DIRTY_MEMORY_VGA); } } @@ -294,7 +306,7 @@ static void tcx24_update_display(void *opaque) if (ds_get_bits_per_pixel(ts->ds) != 32) return; - page = ts->vram_offset; + page = 0; page24 = ts->vram24_offset; cpage = ts->cplane_offset; y_start = -1; @@ -309,7 +321,7 @@ static void tcx24_update_display(void *opaque) for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE, page24 += TARGET_PAGE_SIZE, cpage += TARGET_PAGE_SIZE) { - if (check_dirty(page, page24, cpage)) { + if (check_dirty(ts, page, page24, cpage)) { if (y_start < 0) y_start = y; if (page < page_min) @@ -421,18 +433,20 @@ static void tcx_reset(DeviceState *d) s->r[255] = s->g[255] = s->b[255] = 255; update_palette_entries(s, 0, 256); memset(s->vram, 0, MAXX*MAXY); - cpu_physical_memory_reset_dirty(s->vram_offset, s->vram_offset + - MAXX * MAXY * (1 + 4 + 4), VGA_DIRTY_FLAG); + memory_region_reset_dirty(&s->vram_mem, 0, MAXX * MAXY * (1 + 4 + 4), + DIRTY_MEMORY_VGA); s->dac_index = 0; s->dac_state = 0; } -static uint32_t tcx_dac_readl(void *opaque, target_phys_addr_t addr) +static uint64_t tcx_dac_readl(void *opaque, target_phys_addr_t addr, + unsigned size) { return 0; } -static void tcx_dac_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +static void tcx_dac_writel(void *opaque, target_phys_addr_t addr, uint64_t val, + unsigned size) { TCXState *s = opaque; @@ -468,77 +482,77 @@ static void tcx_dac_writel(void *opaque, target_phys_addr_t addr, uint32_t val) return; } -static CPUReadMemoryFunc * const tcx_dac_read[3] = { - NULL, - NULL, - tcx_dac_readl, +static const MemoryRegionOps tcx_dac_ops = { + .read = tcx_dac_readl, + .write = tcx_dac_writel, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, }; -static CPUWriteMemoryFunc * const tcx_dac_write[3] = { - NULL, - NULL, - tcx_dac_writel, -}; - -static uint32_t tcx_dummy_readl(void *opaque, target_phys_addr_t addr) +static uint64_t dummy_readl(void *opaque, target_phys_addr_t addr, + unsigned size) { return 0; } -static void tcx_dummy_writel(void *opaque, target_phys_addr_t addr, - uint32_t val) +static void dummy_writel(void *opaque, target_phys_addr_t addr, + uint64_t val, unsigned size) { } -static CPUReadMemoryFunc * const tcx_dummy_read[3] = { - NULL, - NULL, - tcx_dummy_readl, -}; - -static CPUWriteMemoryFunc * const tcx_dummy_write[3] = { - NULL, - NULL, - tcx_dummy_writel, +static const MemoryRegionOps dummy_ops = { + .read = dummy_readl, + .write = dummy_writel, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, }; static int tcx_init1(SysBusDevice *dev) { TCXState *s = FROM_SYSBUS(TCXState, dev); - int io_memory, dummy_memory; - ram_addr_t vram_offset; + ram_addr_t vram_offset = 0; int size; uint8_t *vram_base; - vram_offset = qemu_ram_alloc(NULL, "tcx.vram", s->vram_size * (1 + 4 + 4)); - vram_base = qemu_get_ram_ptr(vram_offset); - s->vram_offset = vram_offset; + memory_region_init_ram(&s->vram_mem, NULL, "tcx.vram", + s->vram_size * (1 + 4 + 4)); + vram_base = memory_region_get_ram_ptr(&s->vram_mem); /* 8-bit plane */ s->vram = vram_base; size = s->vram_size; - sysbus_init_mmio(dev, size, s->vram_offset); + memory_region_init_alias(&s->vram_8bit, "tcx.vram.8bit", + &s->vram_mem, vram_offset, size); + sysbus_init_mmio_region(dev, &s->vram_8bit); vram_offset += size; vram_base += size; /* DAC */ - io_memory = cpu_register_io_memory(tcx_dac_read, tcx_dac_write, s, - DEVICE_NATIVE_ENDIAN); - sysbus_init_mmio(dev, TCX_DAC_NREGS, io_memory); + memory_region_init_io(&s->dac, &tcx_dac_ops, s, "tcx.dac", TCX_DAC_NREGS); + sysbus_init_mmio_region(dev, &s->dac); /* TEC (dummy) */ - dummy_memory = cpu_register_io_memory(tcx_dummy_read, tcx_dummy_write, - s, DEVICE_NATIVE_ENDIAN); - sysbus_init_mmio(dev, TCX_TEC_NREGS, dummy_memory); + memory_region_init_io(&s->tec, &dummy_ops, s, "tcx.tec", TCX_TEC_NREGS); + sysbus_init_mmio_region(dev, &s->tec); /* THC: NetBSD writes here even with 8-bit display: dummy */ - sysbus_init_mmio(dev, TCX_THC_NREGS_24, dummy_memory); + memory_region_init_io(&s->thc24, &dummy_ops, s, "tcx.thc24", + TCX_THC_NREGS_24); + sysbus_init_mmio_region(dev, &s->thc24); if (s->depth == 24) { /* 24-bit plane */ size = s->vram_size * 4; s->vram24 = (uint32_t *)vram_base; s->vram24_offset = vram_offset; - sysbus_init_mmio(dev, size, vram_offset); + memory_region_init_alias(&s->vram_24bit, "tcx.vram.24bit", + &s->vram_mem, vram_offset, size); + sysbus_init_mmio_region(dev, &s->vram_24bit); vram_offset += size; vram_base += size; @@ -546,14 +560,18 @@ static int tcx_init1(SysBusDevice *dev) size = s->vram_size * 4; s->cplane = (uint32_t *)vram_base; s->cplane_offset = vram_offset; - sysbus_init_mmio(dev, size, vram_offset); + memory_region_init_alias(&s->vram_cplane, "tcx.vram.cplane", + &s->vram_mem, vram_offset, size); + sysbus_init_mmio_region(dev, &s->vram_cplane); s->ds = graphic_console_init(tcx24_update_display, tcx24_invalidate_display, tcx24_screen_dump, NULL, s); } else { /* THC 8 bit (dummy) */ - sysbus_init_mmio(dev, TCX_THC_NREGS_8, dummy_memory); + memory_region_init_io(&s->thc8, &dummy_ops, s, "tcx.thc8", + TCX_THC_NREGS_8); + sysbus_init_mmio_region(dev, &s->thc8); s->ds = graphic_console_init(tcx_update_display, tcx_invalidate_display, diff --git a/hw/tosa.c b/hw/tosa.c index 92702d148a..b992b994c7 100644 --- a/hw/tosa.c +++ b/hw/tosa.c @@ -220,7 +220,7 @@ static void tosa_init(ram_addr_t ram_size, cpu_register_physical_memory(0, TOSA_ROM, qemu_ram_alloc(NULL, "tosa.rom", TOSA_ROM) | IO_MEM_ROM); - tmio = tc6393xb_init(0x10000000, + tmio = tc6393xb_init(address_space_mem, 0x10000000, qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_TC6393XB_INT)); scp0 = sysbus_create_simple("scoop", 0x08800000, NULL); diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c index bd374c1de6..cdd5aae1e9 100644 --- a/hw/usb-ehci.c +++ b/hw/usb-ehci.c @@ -1101,12 +1101,13 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val) // TODO : Put in common header file, duplication from usb-ohci.c /* Get an array of dwords from main memory */ -static inline int get_dwords(uint32_t addr, uint32_t *buf, int num) +static inline int get_dwords(EHCIState *ehci, uint32_t addr, + uint32_t *buf, int num) { int i; for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { - cpu_physical_memory_rw(addr,(uint8_t *)buf, sizeof(*buf), 0); + pci_dma_read(&ehci->dev, addr, (uint8_t *)buf, sizeof(*buf)); *buf = le32_to_cpu(*buf); } @@ -1114,13 +1115,14 @@ static inline int get_dwords(uint32_t addr, uint32_t *buf, int num) } /* Put an array of dwords in to main memory */ -static inline int put_dwords(uint32_t addr, uint32_t *buf, int num) +static inline int put_dwords(EHCIState *ehci, uint32_t addr, + uint32_t *buf, int num) { int i; for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { uint32_t tmp = cpu_to_le32(*buf); - cpu_physical_memory_rw(addr,(uint8_t *)&tmp, sizeof(tmp), 1); + pci_dma_write(&ehci->dev, addr, (uint8_t *)&tmp, sizeof(tmp)); } return 1; @@ -1169,7 +1171,8 @@ static int ehci_qh_do_overlay(EHCIQueue *q) q->qh.bufptr[1] &= ~BUFPTR_CPROGMASK_MASK; q->qh.bufptr[2] &= ~BUFPTR_FRAMETAG_MASK; - put_dwords(NLPTR_GET(q->qhaddr), (uint32_t *) &q->qh, sizeof(EHCIqh) >> 2); + put_dwords(q->ehci, NLPTR_GET(q->qhaddr), (uint32_t *) &q->qh, + sizeof(EHCIqh) >> 2); return 0; } @@ -1177,12 +1180,12 @@ static int ehci_qh_do_overlay(EHCIQueue *q) static int ehci_init_transfer(EHCIQueue *q) { uint32_t cpage, offset, bytes, plen; - target_phys_addr_t page; + dma_addr_t page; cpage = get_field(q->qh.token, QTD_TOKEN_CPAGE); bytes = get_field(q->qh.token, QTD_TOKEN_TBYTES); offset = q->qh.bufptr[0] & ~QTD_BUFPTR_MASK; - qemu_sglist_init(&q->sgl, 5); + pci_dma_sglist_init(&q->sgl, &q->ehci->dev, 5); while (bytes > 0) { if (cpage > 4) { @@ -1428,7 +1431,7 @@ static int ehci_process_itd(EHCIState *ehci, return USB_RET_PROCERR; } - qemu_sglist_init(&ehci->isgl, 2); + pci_dma_sglist_init(&ehci->isgl, &ehci->dev, 2); if (off + len > 4096) { /* transfer crosses page border */ uint32_t len2 = off + len - 4096; @@ -1532,7 +1535,8 @@ static int ehci_state_waitlisthead(EHCIState *ehci, int async) /* Find the head of the list (4.9.1.1) */ for(i = 0; i < MAX_QH; i++) { - get_dwords(NLPTR_GET(entry), (uint32_t *) &qh, sizeof(EHCIqh) >> 2); + get_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &qh, + sizeof(EHCIqh) >> 2); ehci_trace_qh(NULL, NLPTR_GET(entry), &qh); if (qh.epchar & QH_EPCHAR_H) { @@ -1629,7 +1633,8 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) goto out; } - get_dwords(NLPTR_GET(q->qhaddr), (uint32_t *) &q->qh, sizeof(EHCIqh) >> 2); + get_dwords(ehci, NLPTR_GET(q->qhaddr), + (uint32_t *) &q->qh, sizeof(EHCIqh) >> 2); ehci_trace_qh(q, NLPTR_GET(q->qhaddr), &q->qh); if (q->async == EHCI_ASYNC_INFLIGHT) { @@ -1698,7 +1703,7 @@ static int ehci_state_fetchitd(EHCIState *ehci, int async) assert(!async); entry = ehci_get_fetch_addr(ehci, async); - get_dwords(NLPTR_GET(entry),(uint32_t *) &itd, + get_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &itd, sizeof(EHCIitd) >> 2); ehci_trace_itd(ehci, entry, &itd); @@ -1706,8 +1711,8 @@ static int ehci_state_fetchitd(EHCIState *ehci, int async) return -1; } - put_dwords(NLPTR_GET(entry), (uint32_t *) &itd, - sizeof(EHCIitd) >> 2); + put_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &itd, + sizeof(EHCIitd) >> 2); ehci_set_fetch_addr(ehci, async, itd.next); ehci_set_state(ehci, async, EST_FETCHENTRY); @@ -1722,7 +1727,7 @@ static int ehci_state_fetchsitd(EHCIState *ehci, int async) assert(!async); entry = ehci_get_fetch_addr(ehci, async); - get_dwords(NLPTR_GET(entry), (uint32_t *)&sitd, + get_dwords(ehci, NLPTR_GET(entry), (uint32_t *)&sitd, sizeof(EHCIsitd) >> 2); ehci_trace_sitd(ehci, entry, &sitd); @@ -1784,7 +1789,8 @@ static int ehci_state_fetchqtd(EHCIQueue *q, int async) { int again = 0; - get_dwords(NLPTR_GET(q->qtdaddr),(uint32_t *) &q->qtd, sizeof(EHCIqtd) >> 2); + get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &q->qtd, + sizeof(EHCIqtd) >> 2); ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), &q->qtd); if (q->qtd.token & QTD_TOKEN_ACTIVE) { @@ -1827,7 +1833,7 @@ static void ehci_flush_qh(EHCIQueue *q) uint32_t dwords = sizeof(EHCIqh) >> 2; uint32_t addr = NLPTR_GET(q->qhaddr); - put_dwords(addr + 3 * sizeof(uint32_t), qh + 3, dwords - 3); + put_dwords(q->ehci, addr + 3 * sizeof(uint32_t), qh + 3, dwords - 3); } static int ehci_state_execute(EHCIQueue *q, int async) @@ -1947,8 +1953,8 @@ static int ehci_state_writeback(EHCIQueue *q, int async) /* Write back the QTD from the QH area */ ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), (EHCIqtd*) &q->qh.next_qtd); - put_dwords(NLPTR_GET(q->qtdaddr),(uint32_t *) &q->qh.next_qtd, - sizeof(EHCIqtd) >> 2); + put_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &q->qh.next_qtd, + sizeof(EHCIqtd) >> 2); /* * EHCI specs say go horizontal here. @@ -2148,7 +2154,7 @@ static void ehci_advance_periodic_state(EHCIState *ehci) } list |= ((ehci->frindex & 0x1ff8) >> 1); - cpu_physical_memory_rw(list, (uint8_t *) &entry, sizeof entry, 0); + pci_dma_read(&ehci->dev, list, (uint8_t *) &entry, sizeof entry); entry = le32_to_cpu(entry); DPRINTF("PERIODIC state adv fr=%d. [%08X] -> %08X\n", diff --git a/hw/usb-hub.c b/hw/usb-hub.c index 09c65160c2..3eb0f1aa0a 100644 --- a/hw/usb-hub.c +++ b/hw/usb-hub.c @@ -127,8 +127,8 @@ static const USBDescDevice desc_device_hub = { static const USBDesc desc_hub = { .id = { - .idVendor = 0, - .idProduct = 0, + .idVendor = 0x0409, + .idProduct = 0x55aa, .bcdDevice = 0x0101, .iManufacturer = STR_MANUFACTURER, .iProduct = STR_PRODUCT, @@ -163,6 +163,7 @@ static void usb_hub_attach(USBPort *port1) } else { port->wPortStatus &= ~PORT_STAT_LOW_SPEED; } + usb_wakeup(&s->dev); } static void usb_hub_detach(USBPort *port1) diff --git a/hw/usb-msd.c b/hw/usb-msd.c index 08d2d2ac77..b7341778e6 100644 --- a/hw/usb-msd.c +++ b/hw/usb-msd.c @@ -162,8 +162,8 @@ static const USBDescDevice desc_device_high = { static const USBDesc desc = { .id = { - .idVendor = 0, - .idProduct = 0, + .idVendor = 0x46f4, /* CRC16() of "QEMU" */ + .idProduct = 0x0001, .bcdDevice = 0, .iManufacturer = STR_MANUFACTURER, .iProduct = STR_PRODUCT, @@ -495,7 +495,11 @@ static void usb_msd_password_cb(void *opaque, int err) qdev_unplug(&s->dev.qdev); } -static const struct SCSIBusOps usb_msd_scsi_ops = { +static const struct SCSIBusInfo usb_msd_scsi_info = { + .tcq = false, + .max_target = 0, + .max_lun = 0, + .transfer_data = usb_msd_transfer_data, .complete = usb_msd_command_complete, .cancel = usb_msd_request_cancelled @@ -536,7 +540,7 @@ static int usb_msd_initfn(USBDevice *dev) } usb_desc_init(dev); - scsi_bus_new(&s->bus, &s->dev.qdev, 0, 1, &usb_msd_scsi_ops); + scsi_bus_new(&s->bus, &s->dev.qdev, &usb_msd_scsi_info); s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, bs, 0, !!s->removable); if (!s->scsi_dev) { return -1; diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c index 171d7870b7..f9e3ea5bfc 100644 --- a/hw/usb-uhci.c +++ b/hw/usb-uhci.c @@ -178,7 +178,7 @@ static UHCIAsync *uhci_async_alloc(UHCIState *s) async->done = 0; async->isoc = 0; usb_packet_init(&async->packet); - qemu_sglist_init(&async->sgl, 1); + pci_dma_sglist_init(&async->sgl, &s->dev, 1); return async; } @@ -876,7 +876,7 @@ static void uhci_async_complete(USBPort *port, USBPacket *packet) uint32_t link = async->td; uint32_t int_mask = 0, val; - cpu_physical_memory_read(link & ~0xf, (uint8_t *) &td, sizeof(td)); + pci_dma_read(&s->dev, link & ~0xf, (uint8_t *) &td, sizeof(td)); le32_to_cpus(&td.link); le32_to_cpus(&td.ctrl); le32_to_cpus(&td.token); @@ -888,8 +888,8 @@ static void uhci_async_complete(USBPort *port, USBPacket *packet) /* update the status bits of the TD */ val = cpu_to_le32(td.ctrl); - cpu_physical_memory_write((link & ~0xf) + 4, - (const uint8_t *)&val, sizeof(val)); + pci_dma_write(&s->dev, (link & ~0xf) + 4, + (const uint8_t *)&val, sizeof(val)); uhci_async_free(s, async); } else { async->done = 1; @@ -952,7 +952,7 @@ static void uhci_process_frame(UHCIState *s) DPRINTF("uhci: processing frame %d addr 0x%x\n" , s->frnum, frame_addr); - cpu_physical_memory_read(frame_addr, (uint8_t *)&link, 4); + pci_dma_read(&s->dev, frame_addr, (uint8_t *)&link, 4); le32_to_cpus(&link); int_mask = 0; @@ -976,7 +976,7 @@ static void uhci_process_frame(UHCIState *s) break; } - cpu_physical_memory_read(link & ~0xf, (uint8_t *) &qh, sizeof(qh)); + pci_dma_read(&s->dev, link & ~0xf, (uint8_t *) &qh, sizeof(qh)); le32_to_cpus(&qh.link); le32_to_cpus(&qh.el_link); @@ -996,7 +996,7 @@ static void uhci_process_frame(UHCIState *s) } /* TD */ - cpu_physical_memory_read(link & ~0xf, (uint8_t *) &td, sizeof(td)); + pci_dma_read(&s->dev, link & ~0xf, (uint8_t *) &td, sizeof(td)); le32_to_cpus(&td.link); le32_to_cpus(&td.ctrl); le32_to_cpus(&td.token); @@ -1010,8 +1010,8 @@ static void uhci_process_frame(UHCIState *s) if (old_td_ctrl != td.ctrl) { /* update the status bits of the TD */ val = cpu_to_le32(td.ctrl); - cpu_physical_memory_write((link & ~0xf) + 4, - (const uint8_t *)&val, sizeof(val)); + pci_dma_write(&s->dev, (link & ~0xf) + 4, + (const uint8_t *)&val, sizeof(val)); } if (ret < 0) { @@ -1039,8 +1039,8 @@ static void uhci_process_frame(UHCIState *s) /* update QH element link */ qh.el_link = link; val = cpu_to_le32(qh.el_link); - cpu_physical_memory_write((curr_qh & ~0xf) + 4, - (const uint8_t *)&val, sizeof(val)); + pci_dma_write(&s->dev, (curr_qh & ~0xf) + 4, + (const uint8_t *)&val, sizeof(val)); if (!depth_first(link)) { /* done with this QH */ diff --git a/hw/versatilepb.c b/hw/versatilepb.c index 68402cc479..6370600bb3 100644 --- a/hw/versatilepb.c +++ b/hw/versatilepb.c @@ -182,6 +182,7 @@ static void versatile_init(ram_addr_t ram_size, qemu_irq sic[32]; DeviceState *dev, *sysctl; SysBusDevice *busdev; + DeviceState *pl041; PCIBus *pci_bus; NICInfo *nd; int n; @@ -273,6 +274,13 @@ static void versatile_init(ram_addr_t ram_size, /* Add PL031 Real Time Clock. */ sysbus_create_simple("pl031", 0x101e8000, pic[10]); + /* Add PL041 AACI Interface to the LM4549 codec */ + pl041 = qdev_create(NULL, "pl041"); + qdev_prop_set_uint32(pl041, "nc_fifo_depth", 512); + qdev_init_nofail(pl041); + sysbus_mmio_map(sysbus_from_qdev(pl041), 0, 0x10004000); + sysbus_connect_irq(sysbus_from_qdev(pl041), 0, sic[24]); + /* Memory map for Versatile/PB: */ /* 0x10000000 System registers. */ /* 0x10001000 PCI controller config registers. */ diff --git a/hw/vexpress.c b/hw/vexpress.c index c9766dd0c4..0940a26d73 100644 --- a/hw/vexpress.c +++ b/hw/vexpress.c @@ -41,7 +41,7 @@ static void vexpress_a9_init(ram_addr_t ram_size, { CPUState *env = NULL; ram_addr_t ram_offset, vram_offset, sram_offset; - DeviceState *dev, *sysctl; + DeviceState *dev, *sysctl, *pl041; SysBusDevice *busdev; qemu_irq *irqp; qemu_irq pic[64]; @@ -118,6 +118,11 @@ static void vexpress_a9_init(ram_addr_t ram_size, /* 0x10001000 SP810 system control */ /* 0x10002000 serial bus PCI */ /* 0x10004000 PL041 audio */ + pl041 = qdev_create(NULL, "pl041"); + qdev_prop_set_uint32(pl041, "nc_fifo_depth", 512); + qdev_init_nofail(pl041); + sysbus_mmio_map(sysbus_from_qdev(pl041), 0, 0x10004000); + sysbus_connect_irq(sysbus_from_qdev(pl041), 0, pic[11]); dev = sysbus_create_varargs("pl181", 0x10005000, pic[9], pic[10], NULL); /* Wire up MMC card detect and read-only signals */ diff --git a/hw/virtio-balloon.c b/hw/virtio-balloon.c index 5f8f4bdb9f..e24a2bf1f3 100644 --- a/hw/virtio-balloon.c +++ b/hw/virtio-balloon.c @@ -18,22 +18,14 @@ #include "virtio.h" #include "pc.h" #include "cpu.h" -#include "monitor.h" #include "balloon.h" #include "virtio-balloon.h" #include "kvm.h" -#include "qlist.h" -#include "qint.h" -#include "qstring.h" #if defined(__linux__) #include <sys/mman.h> #endif -/* Disable guest-provided stats by now (https://bugzilla.redhat.com/show_bug.cgi?id=623903) */ -#define ENABLE_GUEST_STATS 0 - - typedef struct VirtIOBalloon { VirtIODevice vdev; @@ -43,8 +35,6 @@ typedef struct VirtIOBalloon uint64_t stats[VIRTIO_BALLOON_S_NR]; VirtQueueElement stats_vq_elem; size_t stats_vq_offset; - MonitorCompletion *stats_callback; - void *stats_opaque_callback_data; DeviceState *qdev; } VirtIOBalloon; @@ -76,31 +66,6 @@ static inline void reset_stats(VirtIOBalloon *dev) for (i = 0; i < VIRTIO_BALLOON_S_NR; dev->stats[i++] = -1); } -static void stat_put(QDict *dict, const char *label, uint64_t val) -{ - if (val != -1) - qdict_put(dict, label, qint_from_int(val)); -} - -static QObject *get_stats_qobject(VirtIOBalloon *dev) -{ - QDict *dict = qdict_new(); - uint64_t actual = ram_size - ((uint64_t) dev->actual << - VIRTIO_BALLOON_PFN_SHIFT); - - stat_put(dict, "actual", actual); -#if ENABLE_GUEST_STATS - stat_put(dict, "mem_swapped_in", dev->stats[VIRTIO_BALLOON_S_SWAP_IN]); - stat_put(dict, "mem_swapped_out", dev->stats[VIRTIO_BALLOON_S_SWAP_OUT]); - stat_put(dict, "major_page_faults", dev->stats[VIRTIO_BALLOON_S_MAJFLT]); - stat_put(dict, "minor_page_faults", dev->stats[VIRTIO_BALLOON_S_MINFLT]); - stat_put(dict, "free_mem", dev->stats[VIRTIO_BALLOON_S_MEMFREE]); - stat_put(dict, "total_mem", dev->stats[VIRTIO_BALLOON_S_MEMTOT]); -#endif - - return QOBJECT(dict); -} - static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq) { VirtIOBalloon *s = to_virtio_balloon(vdev); @@ -131,20 +96,6 @@ static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq) } } -static void complete_stats_request(VirtIOBalloon *vb) -{ - QObject *stats; - - if (!vb->stats_opaque_callback_data) - return; - - stats = get_stats_qobject(vb); - vb->stats_callback(vb->stats_opaque_callback_data, stats); - qobject_decref(stats); - vb->stats_opaque_callback_data = NULL; - vb->stats_callback = NULL; -} - static void virtio_balloon_receive_stats(VirtIODevice *vdev, VirtQueue *vq) { VirtIOBalloon *s = DO_UPCAST(VirtIOBalloon, vdev, vdev); @@ -172,8 +123,6 @@ static void virtio_balloon_receive_stats(VirtIODevice *vdev, VirtQueue *vq) s->stats[tag] = val; } s->stats_vq_offset = offset; - - complete_stats_request(s); } static void virtio_balloon_get_config(VirtIODevice *vdev, uint8_t *config_data) @@ -202,32 +151,33 @@ static uint32_t virtio_balloon_get_features(VirtIODevice *vdev, uint32_t f) return f; } -static void virtio_balloon_stat(void *opaque, MonitorCompletion cb, - void *cb_data) +static void virtio_balloon_stat(void *opaque, BalloonInfo *info) { VirtIOBalloon *dev = opaque; - /* For now, only allow one request at a time. This restriction can be - * removed later by queueing callback and data pairs. +#if 0 + /* Disable guest-provided stats for now. For more details please check: + * https://bugzilla.redhat.com/show_bug.cgi?id=623903 + * + * If you do enable it (which is probably not going to happen as we + * need a new command for it), remember that you also need to fill the + * appropriate members of the BalloonInfo structure so that the stats + * are returned to the client. */ - if (dev->stats_callback != NULL) { - return; - } - dev->stats_callback = cb; - dev->stats_opaque_callback_data = cb_data; - - if (ENABLE_GUEST_STATS - && (dev->vdev.guest_features & (1 << VIRTIO_BALLOON_F_STATS_VQ))) { + if (dev->vdev.guest_features & (1 << VIRTIO_BALLOON_F_STATS_VQ)) { virtqueue_push(dev->svq, &dev->stats_vq_elem, dev->stats_vq_offset); virtio_notify(&dev->vdev, dev->svq); return; } +#endif /* Stats are not supported. Clear out any stale values that might * have been set by a more featureful guest kernel. */ reset_stats(dev); - complete_stats_request(dev); + + info->actual = ram_size - ((uint64_t) dev->actual << + VIRTIO_BALLOON_PFN_SHIFT); } static void virtio_balloon_to_target(void *opaque, ram_addr_t target) diff --git a/hw/virtio-blk.c b/hw/virtio-blk.c index 2a5d1a92b3..19e89e71ee 100644 --- a/hw/virtio-blk.c +++ b/hw/virtio-blk.c @@ -16,6 +16,7 @@ #include "trace.h" #include "blockdev.h" #include "virtio-blk.h" +#include "scsi-defs.h" #ifdef __linux__ # include <scsi/sg.h> #endif @@ -231,7 +232,20 @@ static void virtio_blk_handle_scsi(VirtIOBlockReq *req) status = VIRTIO_BLK_S_OK; } - stl_p(&req->scsi->errors, hdr.status); + /* + * From SCSI-Generic-HOWTO: "Some lower level drivers (e.g. ide-scsi) + * clear the masked_status field [hence status gets cleared too, see + * block/scsi_ioctl.c] even when a CHECK_CONDITION or COMMAND_TERMINATED + * status has occurred. However they do set DRIVER_SENSE in driver_status + * field. Also a (sb_len_wr > 0) indicates there is a sense buffer. + */ + if (hdr.status == 0 && hdr.sb_len_wr > 0) { + hdr.status = CHECK_CONDITION; + } + + stl_p(&req->scsi->errors, + hdr.status | (hdr.msg_status << 8) | + (hdr.host_status << 16) | (hdr.driver_status << 24)); stl_p(&req->scsi->residual, hdr.resid); stl_p(&req->scsi->sense_len, hdr.sb_len_wr); stl_p(&req->scsi->data_len, hdr.dxfer_len); diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c index df27c198b0..ca5923c495 100644 --- a/hw/virtio-pci.c +++ b/hw/virtio-pci.c @@ -83,11 +83,6 @@ /* Flags track per-device state like workarounds for quirks in older guests. */ #define VIRTIO_PCI_FLAG_BUS_MASTER_BUG (1 << 0) -/* Performance improves when virtqueue kick processing is decoupled from the - * vcpu thread using ioeventfd for some devices. */ -#define VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT 1 -#define VIRTIO_PCI_FLAG_USE_IOEVENTFD (1 << VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT) - /* QEMU doesn't strictly need write barriers since everything runs in * lock-step. We'll leave the calls to wmb() in though to make it obvious for * KVM or if kqemu gets SMP support. diff --git a/hw/virtio-pci.h b/hw/virtio-pci.h index 14c10f7d67..f8404de92b 100644 --- a/hw/virtio-pci.h +++ b/hw/virtio-pci.h @@ -18,6 +18,11 @@ #include "virtio-net.h" #include "virtio-serial.h" +/* Performance improves when virtqueue kick processing is decoupled from the + * vcpu thread using ioeventfd for some devices. */ +#define VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT 1 +#define VIRTIO_PCI_FLAG_USE_IOEVENTFD (1 << VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT) + typedef struct { PCIDevice pci_dev; VirtIODevice *vdev; diff --git a/hw/xen_disk.c b/hw/xen_disk.c index 8a9fac499b..286bbac54a 100644 --- a/hw/xen_disk.c +++ b/hw/xen_disk.c @@ -620,7 +620,7 @@ static void blk_alloc(struct XenDevice *xendev) static int blk_init(struct XenDevice *xendev) { struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); - int index, qflags, have_barriers, info = 0; + int index, qflags, info = 0; /* read xenstore entries */ if (blkdev->params == NULL) { @@ -706,7 +706,6 @@ static int blk_init(struct XenDevice *xendev) blkdev->bs->drv ? blkdev->bs->drv->format_name : "-"); blkdev->file_size = 0; } - have_barriers = blkdev->bs->drv && blkdev->bs->drv->bdrv_flush ? 1 : 0; xen_be_printf(xendev, 1, "type \"%s\", fileproto \"%s\", filename \"%s\"," " size %" PRId64 " (%" PRId64 " MB)\n", @@ -714,7 +713,7 @@ static int blk_init(struct XenDevice *xendev) blkdev->file_size, blkdev->file_size >> 20); /* fill info */ - xenstore_write_be_int(&blkdev->xendev, "feature-barrier", have_barriers); + xenstore_write_be_int(&blkdev->xendev, "feature-barrier", 1); xenstore_write_be_int(&blkdev->xendev, "info", info); xenstore_write_be_int(&blkdev->xendev, "sector-size", blkdev->file_blk); xenstore_write_be_int(&blkdev->xendev, "sectors", diff --git a/hw/xtensa_lx60.c b/hw/xtensa_lx60.c new file mode 100644 index 0000000000..3cebca1cfc --- /dev/null +++ b/hw/xtensa_lx60.c @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2011, Max Filippov, Open Source and Linux Lab. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Open Source and Linux Lab nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "sysemu.h" +#include "boards.h" +#include "loader.h" +#include "elf.h" +#include "memory.h" +#include "exec-memory.h" +#include "pc.h" +#include "sysbus.h" + +typedef struct Lx60FpgaState { + MemoryRegion iomem; + uint32_t leds; + uint32_t switches; +} Lx60FpgaState; + +static void lx60_fpga_reset(void *opaque) +{ + Lx60FpgaState *s = opaque; + + s->leds = 0; + s->switches = 0; +} + +static uint64_t lx60_fpga_read(void *opaque, target_phys_addr_t addr, + unsigned size) +{ + Lx60FpgaState *s = opaque; + + switch (addr) { + case 0x0: /*build date code*/ + return 0x27092011; + + case 0x4: /*processor clock frequency, Hz*/ + return 10000000; + + case 0x8: /*LEDs (off = 0, on = 1)*/ + return s->leds; + + case 0xc: /*DIP switches (off = 0, on = 1)*/ + return s->switches; + } + return 0; +} + +static void lx60_fpga_write(void *opaque, target_phys_addr_t addr, + uint64_t val, unsigned size) +{ + Lx60FpgaState *s = opaque; + + switch (addr) { + case 0x8: /*LEDs (off = 0, on = 1)*/ + s->leds = val; + break; + + case 0x10: /*board reset*/ + if (val == 0xdead) { + qemu_system_reset_request(); + } + break; + } +} + +static const MemoryRegionOps lx60_fpga_ops = { + .read = lx60_fpga_read, + .write = lx60_fpga_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static Lx60FpgaState *lx60_fpga_init(MemoryRegion *address_space, + target_phys_addr_t base) +{ + Lx60FpgaState *s = g_malloc(sizeof(Lx60FpgaState)); + + memory_region_init_io(&s->iomem, &lx60_fpga_ops, s, + "lx60-fpga", 0x10000); + memory_region_add_subregion(address_space, base, &s->iomem); + lx60_fpga_reset(s); + qemu_register_reset(lx60_fpga_reset, s); + return s; +} + +static void lx60_net_init(MemoryRegion *address_space, + target_phys_addr_t base, + target_phys_addr_t descriptors, + target_phys_addr_t buffers, + qemu_irq irq, NICInfo *nd) +{ + DeviceState *dev; + SysBusDevice *s; + MemoryRegion *ram; + + dev = qdev_create(NULL, "open_eth"); + qdev_set_nic_properties(dev, nd); + qdev_init_nofail(dev); + + s = sysbus_from_qdev(dev); + sysbus_connect_irq(s, 0, irq); + memory_region_add_subregion(address_space, base, + sysbus_mmio_get_region(s, 0)); + memory_region_add_subregion(address_space, descriptors, + sysbus_mmio_get_region(s, 1)); + + ram = g_malloc(sizeof(*ram)); + memory_region_init_ram(ram, NULL, "open_eth.ram", 16384); + memory_region_add_subregion(address_space, buffers, ram); +} + +static uint64_t translate_phys_addr(void *env, uint64_t addr) +{ + return cpu_get_phys_page_debug(env, addr); +} + +static void lx60_reset(void *env) +{ + cpu_reset(env); +} + +static void lx60_init(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename, const char *cpu_model) +{ +#ifdef TARGET_WORDS_BIGENDIAN + int be = 1; +#else + int be = 0; +#endif + MemoryRegion *system_memory = get_system_memory(); + CPUState *env = NULL; + MemoryRegion *ram, *rom, *system_io; + int n; + + for (n = 0; n < smp_cpus; n++) { + env = cpu_init(cpu_model); + if (!env) { + fprintf(stderr, "Unable to find CPU definition\n"); + exit(1); + } + env->sregs[PRID] = n; + qemu_register_reset(lx60_reset, env); + /* Need MMU initialized prior to ELF loading, + * so that ELF gets loaded into virtual addresses + */ + cpu_reset(env); + } + + ram = g_malloc(sizeof(*ram)); + memory_region_init_ram(ram, NULL, "xtensa.sram", ram_size); + memory_region_add_subregion(system_memory, 0, ram); + + rom = g_malloc(sizeof(*rom)); + memory_region_init_ram(rom, NULL, "xtensa.rom", 0x1000); + memory_region_add_subregion(system_memory, 0xfe000000, rom); + + system_io = g_malloc(sizeof(*system_io)); + memory_region_init(system_io, "system.io", 224 * 1024 * 1024); + memory_region_add_subregion(system_memory, 0xf0000000, system_io); + lx60_fpga_init(system_io, 0x0d020000); + if (nd_table[0].vlan) { + lx60_net_init(system_io, 0x0d030000, 0x0d030400, 0x0d800000, + xtensa_get_extint(env, 1), nd_table); + } + + if (!serial_hds[0]) { + serial_hds[0] = qemu_chr_new("serial0", "null", NULL); + } + + serial_mm_init(system_io, 0x0d050020, 2, xtensa_get_extint(env, 0), + 115200, serial_hds[0], DEVICE_NATIVE_ENDIAN); + + if (kernel_filename) { + uint64_t elf_entry; + uint64_t elf_lowaddr; + int success = load_elf(kernel_filename, translate_phys_addr, env, + &elf_entry, &elf_lowaddr, NULL, be, ELF_MACHINE, 0); + if (success > 0) { + env->pc = elf_entry; + } + } +} + +static void xtensa_lx60_init(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename, const char *cpu_model) +{ + if (!cpu_model) { + cpu_model = "dc232b"; + } + lx60_init(ram_size, boot_device, kernel_filename, kernel_cmdline, + initrd_filename, cpu_model); +} + +static QEMUMachine xtensa_lx60_machine = { + .name = "lx60", + .desc = "lx60 EVB (dc232b)", + .init = xtensa_lx60_init, + .max_cpus = 4, +}; + +static void xtensa_lx60_machine_init(void) +{ + qemu_register_machine(&xtensa_lx60_machine); +} + +machine_init(xtensa_lx60_machine_init); diff --git a/hw/xtensa_pic.c b/hw/xtensa_pic.c index e5085ea9e7..71d5fc89d4 100644 --- a/hw/xtensa_pic.c +++ b/hw/xtensa_pic.c @@ -26,19 +26,9 @@ */ #include "hw.h" -#include "pc.h" #include "qemu-log.h" #include "qemu-timer.h" -/* Stub functions for hardware that doesn't exist. */ -void pic_info(Monitor *mon) -{ -} - -void irq_info(Monitor *mon) -{ -} - void xtensa_advance_ccount(CPUState *env, uint32_t d) { uint32_t old_ccount = env->sregs[CCOUNT]; @@ -157,3 +147,15 @@ void xtensa_irq_init(CPUState *env) qemu_new_timer_ns(vm_clock, &xtensa_ccompare_cb, env); } } + +void *xtensa_get_extint(CPUState *env, unsigned extint) +{ + if (extint < env->config->nextint) { + unsigned irq = env->config->extint[extint]; + return env->irq_inputs[irq]; + } else { + qemu_log("%s: trying to acquire invalid external interrupt %d\n", + __func__, extint); + return NULL; + } +} diff --git a/hw/xtensa_sample.c b/hw/xtensa_sample.c deleted file mode 100644 index 31a6f70825..0000000000 --- a/hw/xtensa_sample.c +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c) 2011, Max Filippov, Open Source and Linux Lab. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the Open Source and Linux Lab nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "sysemu.h" -#include "boards.h" -#include "loader.h" -#include "elf.h" -#include "memory.h" -#include "exec-memory.h" - -static void xtensa_sample_reset(void *env) -{ - cpu_reset(env); -} - -static void xtensa_init(ram_addr_t ram_size, - const char *boot_device, - const char *kernel_filename, const char *kernel_cmdline, - const char *initrd_filename, const char *cpu_model) -{ - CPUState *env = NULL; - MemoryRegion *ram; - const size_t dram_size = 0x10000; - const size_t iram_size = 0x20000; - int n; - - for (n = 0; n < smp_cpus; n++) { - env = cpu_init(cpu_model); - if (!env) { - fprintf(stderr, "Unable to find CPU definition\n"); - exit(1); - } - qemu_register_reset(xtensa_sample_reset, env); - env->sregs[PRID] = n; - } - - ram = g_malloc(sizeof(*ram)); - memory_region_init_ram(ram, NULL, "xtensa.ram", - dram_size + iram_size + ram_size); - memory_region_add_subregion(get_system_memory(), - 0x60000000 - dram_size - iram_size, ram); - - if (kernel_filename) { - uint64_t elf_entry; - uint64_t elf_lowaddr; -#ifdef TARGET_WORDS_BIGENDIAN - int success = load_elf(kernel_filename, NULL, NULL, &elf_entry, - &elf_lowaddr, NULL, 1, ELF_MACHINE, 0); -#else - int success = load_elf(kernel_filename, NULL, NULL, &elf_entry, - &elf_lowaddr, NULL, 0, ELF_MACHINE, 0); -#endif - if (success > 0) { - env->pc = elf_entry; - } - } -} - -static void xtensa_sample_init(ram_addr_t ram_size, - const char *boot_device, - const char *kernel_filename, const char *kernel_cmdline, - const char *initrd_filename, const char *cpu_model) -{ - if (!cpu_model) { - cpu_model = "sample-xtensa-core"; - } - xtensa_init(ram_size, boot_device, kernel_filename, kernel_cmdline, - initrd_filename, cpu_model); -} - -static QEMUMachine xtensa_sample_machine = { - .name = "sample-xtensa-machine", - .desc = "Sample Xtensa machine (sample Xtensa core)", - .init = xtensa_sample_init, - .max_cpus = 4, -}; - -static void xtensa_sample_machine_init(void) -{ - qemu_register_machine(&xtensa_sample_machine); -} - -machine_init(xtensa_sample_machine_init); diff --git a/hw/xtensa_dc232b.c b/hw/xtensa_sim.c index 015d6aaa6b..a94e4e561e 100644 --- a/hw/xtensa_dc232b.c +++ b/hw/xtensa_sim.c @@ -37,12 +37,12 @@ static uint64_t translate_phys_addr(void *env, uint64_t addr) return cpu_get_phys_page_debug(env, addr); } -static void dc232b_reset(void *env) +static void sim_reset(void *env) { cpu_reset(env); } -static void dc232b_init(ram_addr_t ram_size, +static void sim_init(ram_addr_t ram_size, const char *boot_device, const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, const char *cpu_model) @@ -58,11 +58,11 @@ static void dc232b_init(ram_addr_t ram_size, exit(1); } env->sregs[PRID] = n; - qemu_register_reset(dc232b_reset, env); + qemu_register_reset(sim_reset, env); /* Need MMU initialized prior to ELF loading, * so that ELF gets loaded into virtual addresses */ - dc232b_reset(env); + sim_reset(env); } ram = g_malloc(sizeof(*ram)); @@ -89,7 +89,7 @@ static void dc232b_init(ram_addr_t ram_size, } } -static void xtensa_dc232b_init(ram_addr_t ram_size, +static void xtensa_sim_init(ram_addr_t ram_size, const char *boot_device, const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, const char *cpu_model) @@ -97,20 +97,20 @@ static void xtensa_dc232b_init(ram_addr_t ram_size, if (!cpu_model) { cpu_model = "dc232b"; } - dc232b_init(ram_size, boot_device, kernel_filename, kernel_cmdline, + sim_init(ram_size, boot_device, kernel_filename, kernel_cmdline, initrd_filename, cpu_model); } -static QEMUMachine xtensa_dc232b_machine = { - .name = "dc232b", - .desc = "Diamond 232L Standard Core Rev.B (LE) (dc232b)", - .init = xtensa_dc232b_init, +static QEMUMachine xtensa_sim_machine = { + .name = "sim", + .desc = "sim machine (dc232b)", + .init = xtensa_sim_init, .max_cpus = 4, }; -static void xtensa_dc232b_machine_init(void) +static void xtensa_sim_machine_init(void) { - qemu_register_machine(&xtensa_dc232b_machine); + qemu_register_machine(&xtensa_sim_machine); } -machine_init(xtensa_dc232b_machine_init); +machine_init(xtensa_sim_machine_init); |