diff options
309 files changed, 13175 insertions, 3791 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 35c260d549..21043e4b4a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -380,7 +380,7 @@ New World M: Alexander Graf <agraf@suse.de> L: qemu-ppc@nongnu.org S: Maintained -F: hw/ppc_newworld.c +F: hw/ppc/mac_newworld.c F: hw/unin_pci.c F: hw/dec_pci.[hc] @@ -388,14 +388,14 @@ Old World M: Alexander Graf <agraf@suse.de> L: qemu-ppc@nongnu.org S: Maintained -F: hw/ppc_oldworld.c +F: hw/ppc/mac_oldworld.c F: hw/grackle_pci.c PReP M: Andreas Färber <andreas.faerber@web.de> L: qemu-ppc@nongnu.org S: Odd Fixes -F: hw/ppc_prep.c +F: hw/ppc/prep.c F: hw/prep_pci.[hc] F: hw/pc87312.[hc] @@ -103,6 +103,9 @@ defconfig: -include config-all-devices.mak -include config-all-disas.mak +CONFIG_SOFTMMU := $(if $(filter %-softmmu,$(TARGET_DIRS)),y) +CONFIG_USER_ONLY := $(if $(filter %-user,$(TARGET_DIRS)),y) +CONFIG_ALL=y ifneq ($(wildcard config-host.mak),) include $(SRC_PATH)/Makefile.objs @@ -133,11 +136,7 @@ pixman/Makefile: $(SRC_PATH)/pixman/configure $(SRC_PATH)/pixman/configure: (cd $(SRC_PATH)/pixman; autoreconf -v --install) -$(SUBDIR_RULES): libqemuutil.a libqemustub.a - -$(filter %-softmmu,$(SUBDIR_RULES)): $(universal-obj-y) $(common-obj-y) $(extra-obj-y) - -$(filter %-user,$(SUBDIR_RULES)): $(universal-obj-y) $(user-obj-y) +$(SUBDIR_RULES): libqemuutil.a libqemustub.a $(common-obj-y) ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS)) romsubdir-%: @@ -214,9 +213,9 @@ clean: # avoid old build problems by removing potentially incorrect old files rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h rm -f qemu-options.def - find . -name '*.[od]' -type f -exec rm -f {} + - rm -f *.a *.lo $(TOOLS) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~ - rm -f *.la + find . -name '*.[oda]' -type f -exec rm -f {} + + find . -name '*.l[oa]' -type f -exec rm -f {} + + rm -f $(TOOLS) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~ rm -Rf .libs rm -f qemu-img-cmds.h @# May not be present in GENERATED_HEADERS diff --git a/Makefile.objs b/Makefile.objs index d465a72030..21e9c911f5 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -34,10 +34,20 @@ CONFIG_REALLY_VIRTFS=y endif ###################################################################### +# smartcard + +libcacard-y += libcacard/cac.o libcacard/event.o +libcacard-y += libcacard/vcard.o libcacard/vreader.o +libcacard-y += libcacard/vcard_emul_nss.o +libcacard-y += libcacard/vcard_emul_type.o +libcacard-y += libcacard/card_7816.o + +###################################################################### # Target independent part of system emulation. The long term path is to # suppress *all* target specific code in case of system emulation, i.e. a # single QEMU executable should support all CPUs and machines. +ifeq ($(CONFIG_SOFTMMU),y) common-obj-y = $(block-obj-y) blockdev.o blockdev-nbd.o block/ common-obj-y += net/ common-obj-y += readline.o @@ -45,12 +55,11 @@ common-obj-$(CONFIG_WIN32) += os-win32.o common-obj-$(CONFIG_POSIX) += os-posix.o common-obj-$(CONFIG_LINUX) += fsdev/ -extra-obj-$(CONFIG_LINUX) += fsdev/ common-obj-y += migration.o migration-tcp.o common-obj-y += qemu-char.o #aio.o common-obj-y += block-migration.o -common-obj-y += page_cache.o +common-obj-y += page_cache.o xbzrle.o common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o @@ -58,7 +67,6 @@ common-obj-$(CONFIG_SPICE) += spice-qemu-char.o common-obj-y += audio/ common-obj-y += hw/ -extra-obj-y += hw/ common-obj-y += ui/ common-obj-y += bt-host.o bt-vhci.o @@ -71,20 +79,7 @@ common-obj-$(CONFIG_SLIRP) += slirp/ common-obj-y += backends/ -###################################################################### -# libseccomp -ifeq ($(CONFIG_SECCOMP),y) -common-obj-y += qemu-seccomp.o -endif - -###################################################################### -# smartcard - -libcacard-y += libcacard/cac.o libcacard/event.o -libcacard-y += libcacard/vcard.o libcacard/vreader.o -libcacard-y += libcacard/vcard_emul_nss.o -libcacard-y += libcacard/vcard_emul_type.o -libcacard-y += libcacard/card_7816.o +common-obj-$(CONFIG_SECCOMP) += qemu-seccomp.o common-obj-$(CONFIG_SMARTCARD_NSS) += $(libcacard-y) @@ -93,15 +88,15 @@ common-obj-$(CONFIG_SMARTCARD_NSS) += $(libcacard-y) common-obj-y += qmp-marshal.o qapi-visit.o qapi-types.o common-obj-y += qmp.o hmp.o +endif ####################################################################### # Target-independent parts used in system and user emulation -universal-obj-y = -universal-obj-y += qemu-log.o -universal-obj-y += tcg-runtime.o -universal-obj-y += hw/ -universal-obj-y += qom/ -universal-obj-y += disas/ +common-obj-y += qemu-log.o +common-obj-y += tcg-runtime.o +common-obj-y += hw/ +common-obj-y += qom/ +common-obj-y += disas/ ###################################################################### # guest agent @@ -121,7 +116,5 @@ nested-vars += \ util-obj-y \ qga-obj-y \ block-obj-y \ - common-obj-y \ - universal-obj-y \ - extra-obj-y + common-obj-y dummy := $(call unnest-vars) diff --git a/Makefile.target b/Makefile.target index eb84b1f8e3..760da1edf6 100644 --- a/Makefile.target +++ b/Makefile.target @@ -145,8 +145,7 @@ nested-vars += obj-y include $(SRC_PATH)/Makefile.objs all-obj-y = $(obj-y) -all-obj-y += $(addprefix ../, $(universal-obj-y)) -all-obj-$(CONFIG_SOFTMMU) += $(addprefix ../, $(common-obj-y)) +all-obj-y += $(addprefix ../, $(common-obj-y)) ifdef QEMU_PROGW # The linker builds a windows executable. Make also a console executable. @@ -1 +1 @@ -1.3.50 +1.3.90 diff --git a/arch_init.c b/arch_init.c index dada6ded1a..8da868b988 100644 --- a/arch_init.c +++ b/arch_init.c @@ -851,9 +851,6 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) qemu_get_buffer(f, host, TARGET_PAGE_SIZE); } else if (flags & RAM_SAVE_FLAG_XBZRLE) { - if (!migrate_use_xbzrle()) { - return -EINVAL; - } void *host = host_from_stream_offset(f, addr, flags); if (!host) { return -EINVAL; diff --git a/block-migration.c b/block-migration.c index 6acf3e1682..9ac7de6d78 100644 --- a/block-migration.c +++ b/block-migration.c @@ -23,7 +23,8 @@ #include "sysemu/blockdev.h" #include <assert.h> -#define BLOCK_SIZE (BDRV_SECTORS_PER_DIRTY_CHUNK << BDRV_SECTOR_BITS) +#define BLOCK_SIZE (1 << 20) +#define BDRV_SECTORS_PER_DIRTY_CHUNK (BLOCK_SIZE >> BDRV_SECTOR_BITS) #define BLK_MIG_FLAG_DEVICE_BLOCK 0x01 #define BLK_MIG_FLAG_EOS 0x02 @@ -254,7 +255,7 @@ static void set_dirty_tracking(int enable) BlkMigDevState *bmds; QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { - bdrv_set_dirty_tracking(bmds->bs, enable); + bdrv_set_dirty_tracking(bmds->bs, enable ? BLOCK_SIZE : 0); } } @@ -478,7 +479,7 @@ static int64_t get_remaining_dirty(void) dirty += bdrv_get_dirty_count(bmds->bs); } - return dirty * BLOCK_SIZE; + return dirty << BDRV_SECTOR_BITS; } static void blk_mig_cleanup(void) @@ -1286,7 +1286,6 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest, bs_dest->iostatus = bs_src->iostatus; /* dirty bitmap */ - bs_dest->dirty_count = bs_src->dirty_count; bs_dest->dirty_bitmap = bs_src->dirty_bitmap; /* job */ @@ -1674,10 +1673,10 @@ static void tracked_request_begin(BdrvTrackedRequest *req, /** * Round a region to cluster boundaries */ -static void round_to_clusters(BlockDriverState *bs, - int64_t sector_num, int nb_sectors, - int64_t *cluster_sector_num, - int *cluster_nb_sectors) +void bdrv_round_to_clusters(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + int64_t *cluster_sector_num, + int *cluster_nb_sectors) { BlockDriverInfo bdi; @@ -1719,8 +1718,8 @@ static void coroutine_fn wait_for_overlapping_requests(BlockDriverState *bs, * CoR read and write operations are atomic and guest writes cannot * interleave between them. */ - round_to_clusters(bs, sector_num, nb_sectors, - &cluster_sector_num, &cluster_nb_sectors); + bdrv_round_to_clusters(bs, sector_num, nb_sectors, + &cluster_sector_num, &cluster_nb_sectors); do { retry = false; @@ -2035,36 +2034,6 @@ int bdrv_read_unthrottled(BlockDriverState *bs, int64_t sector_num, return ret; } -#define BITS_PER_LONG (sizeof(unsigned long) * 8) - -static void set_dirty_bitmap(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, int dirty) -{ - int64_t start, end; - unsigned long val, idx, bit; - - start = sector_num / BDRV_SECTORS_PER_DIRTY_CHUNK; - end = (sector_num + nb_sectors - 1) / BDRV_SECTORS_PER_DIRTY_CHUNK; - - for (; start <= end; start++) { - idx = start / BITS_PER_LONG; - bit = start % BITS_PER_LONG; - val = bs->dirty_bitmap[idx]; - if (dirty) { - if (!(val & (1UL << bit))) { - bs->dirty_count++; - val |= 1UL << bit; - } - } else { - if (val & (1UL << bit)) { - bs->dirty_count--; - val &= ~(1UL << bit); - } - } - bs->dirty_bitmap[idx] = val; - } -} - /* Return < 0 if error. Important errors are: -EIO generic I/O error (may happen for all errors) -ENOMEDIUM No media inserted. @@ -2216,8 +2185,8 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BlockDriverState *bs, /* Cover entire cluster so no additional backing file I/O is required when * allocating cluster in the image file. */ - round_to_clusters(bs, sector_num, nb_sectors, - &cluster_sector_num, &cluster_nb_sectors); + bdrv_round_to_clusters(bs, sector_num, nb_sectors, + &cluster_sector_num, &cluster_nb_sectors); trace_bdrv_co_do_copy_on_readv(bs, sector_num, nb_sectors, cluster_sector_num, cluster_nb_sectors); @@ -2831,7 +2800,9 @@ int coroutine_fn bdrv_co_is_allocated_above(BlockDriverState *top, * * [sector_num+x, nr_sectors] allocated. */ - if (n > pnum_inter) { + if (n > pnum_inter && + (intermediate == top || + sector_num + pnum_inter < intermediate->total_sectors)) { n = pnum_inter; } @@ -2863,8 +2834,9 @@ BlockInfo *bdrv_query_info(BlockDriverState *bs) if (bs->dirty_bitmap) { info->has_dirty = true; info->dirty = g_malloc0(sizeof(*info->dirty)); - info->dirty->count = bdrv_get_dirty_count(bs) * - BDRV_SECTORS_PER_DIRTY_CHUNK * BDRV_SECTOR_SIZE; + info->dirty->count = bdrv_get_dirty_count(bs) * BDRV_SECTOR_SIZE; + info->dirty->granularity = + ((int64_t) BDRV_SECTOR_SIZE << hbitmap_granularity(bs->dirty_bitmap)); } if (bs->drv) { @@ -4173,7 +4145,7 @@ int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num, } if (bs->dirty_bitmap) { - set_dirty_bitmap(bs, sector_num, nb_sectors, 0); + bdrv_reset_dirty(bs, sector_num, nb_sectors); } if (bs->drv->bdrv_co_discard) { @@ -4331,22 +4303,20 @@ bool bdrv_qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov) return true; } -void bdrv_set_dirty_tracking(BlockDriverState *bs, int enable) +void bdrv_set_dirty_tracking(BlockDriverState *bs, int granularity) { int64_t bitmap_size; - bs->dirty_count = 0; - if (enable) { - if (!bs->dirty_bitmap) { - bitmap_size = (bdrv_getlength(bs) >> BDRV_SECTOR_BITS) + - BDRV_SECTORS_PER_DIRTY_CHUNK * BITS_PER_LONG - 1; - bitmap_size /= BDRV_SECTORS_PER_DIRTY_CHUNK * BITS_PER_LONG; + assert((granularity & (granularity - 1)) == 0); - bs->dirty_bitmap = g_new0(unsigned long, bitmap_size); - } + if (granularity) { + granularity >>= BDRV_SECTOR_BITS; + assert(!bs->dirty_bitmap); + bitmap_size = (bdrv_getlength(bs) >> BDRV_SECTOR_BITS); + bs->dirty_bitmap = hbitmap_alloc(bitmap_size, ffs(granularity) - 1); } else { if (bs->dirty_bitmap) { - g_free(bs->dirty_bitmap); + hbitmap_free(bs->dirty_bitmap); bs->dirty_bitmap = NULL; } } @@ -4354,67 +4324,37 @@ void bdrv_set_dirty_tracking(BlockDriverState *bs, int enable) int bdrv_get_dirty(BlockDriverState *bs, int64_t sector) { - int64_t chunk = sector / (int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK; - - if (bs->dirty_bitmap && - (sector << BDRV_SECTOR_BITS) < bdrv_getlength(bs)) { - return !!(bs->dirty_bitmap[chunk / BITS_PER_LONG] & - (1UL << (chunk % BITS_PER_LONG))); + if (bs->dirty_bitmap) { + return hbitmap_get(bs->dirty_bitmap, sector); } else { return 0; } } -int64_t bdrv_get_next_dirty(BlockDriverState *bs, int64_t sector) +void bdrv_dirty_iter_init(BlockDriverState *bs, HBitmapIter *hbi) { - int64_t chunk; - int bit, elem; - - /* Avoid an infinite loop. */ - assert(bs->dirty_count > 0); - - sector = (sector | (BDRV_SECTORS_PER_DIRTY_CHUNK - 1)) + 1; - chunk = sector / (int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK; - - QEMU_BUILD_BUG_ON(sizeof(bs->dirty_bitmap[0]) * 8 != BITS_PER_LONG); - elem = chunk / BITS_PER_LONG; - bit = chunk % BITS_PER_LONG; - for (;;) { - if (sector >= bs->total_sectors) { - sector = 0; - bit = elem = 0; - } - if (bit == 0 && bs->dirty_bitmap[elem] == 0) { - sector += BDRV_SECTORS_PER_DIRTY_CHUNK * BITS_PER_LONG; - elem++; - } else { - if (bs->dirty_bitmap[elem] & (1UL << bit)) { - return sector; - } - sector += BDRV_SECTORS_PER_DIRTY_CHUNK; - if (++bit == BITS_PER_LONG) { - bit = 0; - elem++; - } - } - } + hbitmap_iter_init(hbi, bs->dirty_bitmap, 0); } void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector, int nr_sectors) { - set_dirty_bitmap(bs, cur_sector, nr_sectors, 1); + hbitmap_set(bs->dirty_bitmap, cur_sector, nr_sectors); } void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector, int nr_sectors) { - set_dirty_bitmap(bs, cur_sector, nr_sectors, 0); + hbitmap_reset(bs->dirty_bitmap, cur_sector, nr_sectors); } int64_t bdrv_get_dirty_count(BlockDriverState *bs) { - return bs->dirty_count; + if (bs->dirty_bitmap) { + return hbitmap_count(bs->dirty_bitmap); + } else { + return 0; + } } void bdrv_set_in_use(BlockDriverState *bs, int in_use) diff --git a/block/bochs.c b/block/bochs.c index 1b1d9cdbe5..a6eb33da42 100644 --- a/block/bochs.c +++ b/block/bochs.c @@ -114,11 +114,13 @@ static int bochs_open(BlockDriverState *bs, int flags) int i; struct bochs_header bochs; struct bochs_header_v1 header_v1; + int ret; bs->read_only = 1; // no write support yet - if (bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)) != sizeof(bochs)) { - goto fail; + ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)); + if (ret < 0) { + return ret; } if (strcmp(bochs.magic, HEADER_MAGIC) || @@ -126,7 +128,7 @@ static int bochs_open(BlockDriverState *bs, int flags) strcmp(bochs.subtype, GROWING_TYPE) || ((le32_to_cpu(bochs.version) != HEADER_VERSION) && (le32_to_cpu(bochs.version) != HEADER_V1))) { - goto fail; + return -EMEDIUMTYPE; } if (le32_to_cpu(bochs.version) == HEADER_V1) { @@ -138,9 +140,13 @@ static int bochs_open(BlockDriverState *bs, int flags) s->catalog_size = le32_to_cpu(bochs.extra.redolog.catalog); s->catalog_bitmap = g_malloc(s->catalog_size * 4); - if (bdrv_pread(bs->file, le32_to_cpu(bochs.header), s->catalog_bitmap, - s->catalog_size * 4) != s->catalog_size * 4) - goto fail; + + ret = bdrv_pread(bs->file, le32_to_cpu(bochs.header), s->catalog_bitmap, + s->catalog_size * 4); + if (ret < 0) { + goto fail; + } + for (i = 0; i < s->catalog_size; i++) le32_to_cpus(&s->catalog_bitmap[i]); @@ -153,8 +159,10 @@ static int bochs_open(BlockDriverState *bs, int flags) qemu_co_mutex_init(&s->lock); return 0; - fail: - return -1; + +fail: + g_free(s->catalog_bitmap); + return ret; } static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num) diff --git a/block/cloop.c b/block/cloop.c index 5a0d0d805f..8fe13e92a1 100644 --- a/block/cloop.c +++ b/block/cloop.c @@ -57,27 +57,32 @@ static int cloop_open(BlockDriverState *bs, int flags) { BDRVCloopState *s = bs->opaque; uint32_t offsets_size, max_compressed_block_size = 1, i; + int ret; bs->read_only = 1; /* read header */ - if (bdrv_pread(bs->file, 128, &s->block_size, 4) < 4) { - goto cloop_close; + ret = bdrv_pread(bs->file, 128, &s->block_size, 4); + if (ret < 0) { + return ret; } s->block_size = be32_to_cpu(s->block_size); - if (bdrv_pread(bs->file, 128 + 4, &s->n_blocks, 4) < 4) { - goto cloop_close; + ret = bdrv_pread(bs->file, 128 + 4, &s->n_blocks, 4); + if (ret < 0) { + return ret; } s->n_blocks = be32_to_cpu(s->n_blocks); /* read offsets */ offsets_size = s->n_blocks * sizeof(uint64_t); s->offsets = g_malloc(offsets_size); - if (bdrv_pread(bs->file, 128 + 4 + 4, s->offsets, offsets_size) < - offsets_size) { - goto cloop_close; + + ret = bdrv_pread(bs->file, 128 + 4 + 4, s->offsets, offsets_size); + if (ret < 0) { + goto fail; } + for(i=0;i<s->n_blocks;i++) { s->offsets[i] = be64_to_cpu(s->offsets[i]); if (i > 0) { @@ -92,7 +97,8 @@ static int cloop_open(BlockDriverState *bs, int flags) s->compressed_block = g_malloc(max_compressed_block_size + 1); s->uncompressed_block = g_malloc(s->block_size); if (inflateInit(&s->zstream) != Z_OK) { - goto cloop_close; + ret = -EINVAL; + goto fail; } s->current_block = s->n_blocks; @@ -101,8 +107,11 @@ static int cloop_open(BlockDriverState *bs, int flags) qemu_co_mutex_init(&s->lock); return 0; -cloop_close: - return -1; +fail: + g_free(s->offsets); + g_free(s->compressed_block); + g_free(s->uncompressed_block); + return ret; } static inline int cloop_read_block(BlockDriverState *bs, int block_num) diff --git a/block/cow.c b/block/cow.c index a33ce950d4..4baf9042fe 100644 --- a/block/cow.c +++ b/block/cow.c @@ -73,7 +73,7 @@ static int cow_open(BlockDriverState *bs, int flags) } if (be32_to_cpu(cow_header.magic) != COW_MAGIC) { - ret = -EINVAL; + ret = -EMEDIUMTYPE; goto fail; } diff --git a/block/dmg.c b/block/dmg.c index ac397dc8f7..6d85801a84 100644 --- a/block/dmg.c +++ b/block/dmg.c @@ -57,29 +57,42 @@ static int dmg_probe(const uint8_t *buf, int buf_size, const char *filename) return 0; } -static off_t read_off(BlockDriverState *bs, int64_t offset) +static int read_uint64(BlockDriverState *bs, int64_t offset, uint64_t *result) { - uint64_t buffer; - if (bdrv_pread(bs->file, offset, &buffer, 8) < 8) - return 0; - return be64_to_cpu(buffer); + uint64_t buffer; + int ret; + + ret = bdrv_pread(bs->file, offset, &buffer, 8); + if (ret < 0) { + return ret; + } + + *result = be64_to_cpu(buffer); + return 0; } -static off_t read_uint32(BlockDriverState *bs, int64_t offset) +static int read_uint32(BlockDriverState *bs, int64_t offset, uint32_t *result) { - uint32_t buffer; - if (bdrv_pread(bs->file, offset, &buffer, 4) < 4) - return 0; - return be32_to_cpu(buffer); + uint32_t buffer; + int ret; + + ret = bdrv_pread(bs->file, offset, &buffer, 4); + if (ret < 0) { + return ret; + } + + *result = be32_to_cpu(buffer); + return 0; } static int dmg_open(BlockDriverState *bs, int flags) { BDRVDMGState *s = bs->opaque; - off_t info_begin,info_end,last_in_offset,last_out_offset; - uint32_t count; + uint64_t info_begin,info_end,last_in_offset,last_out_offset; + uint32_t count, tmp; uint32_t max_compressed_size=1,max_sectors_per_chunk=1,i; int64_t offset; + int ret; bs->read_only = 1; s->n_chunks = 0; @@ -88,21 +101,32 @@ static int dmg_open(BlockDriverState *bs, int flags) /* read offset of info blocks */ offset = bdrv_getlength(bs->file); if (offset < 0) { + ret = offset; goto fail; } offset -= 0x1d8; - info_begin = read_off(bs, offset); - if (info_begin == 0) { - goto fail; + ret = read_uint64(bs, offset, &info_begin); + if (ret < 0) { + goto fail; + } else if (info_begin == 0) { + ret = -EINVAL; + goto fail; } - if (read_uint32(bs, info_begin) != 0x100) { + ret = read_uint32(bs, info_begin, &tmp); + if (ret < 0) { + goto fail; + } else if (tmp != 0x100) { + ret = -EINVAL; goto fail; } - count = read_uint32(bs, info_begin + 4); - if (count == 0) { + ret = read_uint32(bs, info_begin + 4, &count); + if (ret < 0) { + goto fail; + } else if (count == 0) { + ret = -EINVAL; goto fail; } info_end = info_begin + count; @@ -114,12 +138,20 @@ static int dmg_open(BlockDriverState *bs, int flags) while (offset < info_end) { uint32_t type; - count = read_uint32(bs, offset); - if(count==0) - goto fail; + ret = read_uint32(bs, offset, &count); + if (ret < 0) { + goto fail; + } else if (count == 0) { + ret = -EINVAL; + goto fail; + } offset += 4; - type = read_uint32(bs, offset); + ret = read_uint32(bs, offset, &type); + if (ret < 0) { + goto fail; + } + if (type == 0x6d697368 && count >= 244) { int new_size, chunk_count; @@ -134,8 +166,11 @@ static int dmg_open(BlockDriverState *bs, int flags) s->sectors = g_realloc(s->sectors, new_size); s->sectorcounts = g_realloc(s->sectorcounts, new_size); - for(i=s->n_chunks;i<s->n_chunks+chunk_count;i++) { - s->types[i] = read_uint32(bs, offset); + for (i = s->n_chunks; i < s->n_chunks + chunk_count; i++) { + ret = read_uint32(bs, offset, &s->types[i]); + if (ret < 0) { + goto fail; + } offset += 4; if(s->types[i]!=0x80000005 && s->types[i]!=1 && s->types[i]!=2) { if(s->types[i]==0xffffffff) { @@ -149,17 +184,31 @@ static int dmg_open(BlockDriverState *bs, int flags) } offset += 4; - s->sectors[i] = last_out_offset+read_off(bs, offset); - offset += 8; - - s->sectorcounts[i] = read_off(bs, offset); - offset += 8; - - s->offsets[i] = last_in_offset+read_off(bs, offset); - offset += 8; - - s->lengths[i] = read_off(bs, offset); - offset += 8; + ret = read_uint64(bs, offset, &s->sectors[i]); + if (ret < 0) { + goto fail; + } + s->sectors[i] += last_out_offset; + offset += 8; + + ret = read_uint64(bs, offset, &s->sectorcounts[i]); + if (ret < 0) { + goto fail; + } + offset += 8; + + ret = read_uint64(bs, offset, &s->offsets[i]); + if (ret < 0) { + goto fail; + } + s->offsets[i] += last_in_offset; + offset += 8; + + ret = read_uint64(bs, offset, &s->lengths[i]); + if (ret < 0) { + goto fail; + } + offset += 8; if(s->lengths[i]>max_compressed_size) max_compressed_size = s->lengths[i]; @@ -173,15 +222,25 @@ static int dmg_open(BlockDriverState *bs, int flags) /* initialize zlib engine */ s->compressed_chunk = g_malloc(max_compressed_size+1); s->uncompressed_chunk = g_malloc(512*max_sectors_per_chunk); - if(inflateInit(&s->zstream) != Z_OK) - goto fail; + if(inflateInit(&s->zstream) != Z_OK) { + ret = -EINVAL; + goto fail; + } s->current_chunk = s->n_chunks; qemu_co_mutex_init(&s->lock); return 0; + fail: - return -1; + g_free(s->types); + g_free(s->offsets); + g_free(s->lengths); + g_free(s->sectors); + g_free(s->sectorcounts); + g_free(s->compressed_chunk); + g_free(s->uncompressed_chunk); + return ret; } static inline int is_sector_in_chunk(BDRVDMGState* s, @@ -296,15 +355,15 @@ static coroutine_fn int dmg_co_read(BlockDriverState *bs, int64_t sector_num, static void dmg_close(BlockDriverState *bs) { BDRVDMGState *s = bs->opaque; - if(s->n_chunks>0) { - free(s->types); - free(s->offsets); - free(s->lengths); - free(s->sectors); - free(s->sectorcounts); - } - free(s->compressed_chunk); - free(s->uncompressed_chunk); + + g_free(s->types); + g_free(s->offsets); + g_free(s->lengths); + g_free(s->sectors); + g_free(s->sectorcounts); + g_free(s->compressed_chunk); + g_free(s->uncompressed_chunk); + inflateEnd(&s->zstream); } diff --git a/block/mirror.c b/block/mirror.c index 6180aa30e5..a62ad86c28 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -15,17 +15,17 @@ #include "block/blockjob.h" #include "block/block_int.h" #include "qemu/ratelimit.h" +#include "qemu/bitmap.h" -enum { - /* - * Size of data buffer for populating the image file. This should be large - * enough to process multiple clusters in a single call, so that populating - * contiguous regions of the image is efficient. - */ - BLOCK_SIZE = 512 * BDRV_SECTORS_PER_DIRTY_CHUNK, /* in bytes */ -}; +#define SLICE_TIME 100000000ULL /* ns */ +#define MAX_IN_FLIGHT 16 -#define SLICE_TIME 100000000ULL /* ns */ +/* The mirroring buffer is a list of granularity-sized chunks. + * Free chunks are organized in a list. + */ +typedef struct MirrorBuffer { + QSIMPLEQ_ENTRY(MirrorBuffer) next; +} MirrorBuffer; typedef struct MirrorBlockJob { BlockJob common; @@ -36,9 +36,26 @@ typedef struct MirrorBlockJob { bool synced; bool should_complete; int64_t sector_num; + int64_t granularity; + size_t buf_size; + unsigned long *cow_bitmap; + HBitmapIter hbi; uint8_t *buf; + QSIMPLEQ_HEAD(, MirrorBuffer) buf_free; + int buf_free_count; + + unsigned long *in_flight_bitmap; + int in_flight; + int ret; } MirrorBlockJob; +typedef struct MirrorOp { + MirrorBlockJob *s; + QEMUIOVector qiov; + int64_t sector_num; + int nb_sectors; +} MirrorOp; + static BlockErrorAction mirror_error_action(MirrorBlockJob *s, bool read, int error) { @@ -52,51 +69,234 @@ static BlockErrorAction mirror_error_action(MirrorBlockJob *s, bool read, } } -static int coroutine_fn mirror_iteration(MirrorBlockJob *s, - BlockErrorAction *p_action) +static void mirror_iteration_done(MirrorOp *op, int ret) { - BlockDriverState *source = s->common.bs; - BlockDriverState *target = s->target; - QEMUIOVector qiov; - int ret, nb_sectors; - int64_t end; - struct iovec iov; + MirrorBlockJob *s = op->s; + struct iovec *iov; + int64_t chunk_num; + int i, nb_chunks, sectors_per_chunk; + + trace_mirror_iteration_done(s, op->sector_num, op->nb_sectors, ret); + + s->in_flight--; + iov = op->qiov.iov; + for (i = 0; i < op->qiov.niov; i++) { + MirrorBuffer *buf = (MirrorBuffer *) iov[i].iov_base; + QSIMPLEQ_INSERT_TAIL(&s->buf_free, buf, next); + s->buf_free_count++; + } - end = s->common.len >> BDRV_SECTOR_BITS; - s->sector_num = bdrv_get_next_dirty(source, s->sector_num); - nb_sectors = MIN(BDRV_SECTORS_PER_DIRTY_CHUNK, end - s->sector_num); - bdrv_reset_dirty(source, s->sector_num, nb_sectors); + sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS; + chunk_num = op->sector_num / sectors_per_chunk; + nb_chunks = op->nb_sectors / sectors_per_chunk; + bitmap_clear(s->in_flight_bitmap, chunk_num, nb_chunks); + if (s->cow_bitmap && ret >= 0) { + bitmap_set(s->cow_bitmap, chunk_num, nb_chunks); + } - /* Copy the dirty cluster. */ - iov.iov_base = s->buf; - iov.iov_len = nb_sectors * 512; - qemu_iovec_init_external(&qiov, &iov, 1); + g_slice_free(MirrorOp, op); + qemu_coroutine_enter(s->common.co, NULL); +} - trace_mirror_one_iteration(s, s->sector_num, nb_sectors); - ret = bdrv_co_readv(source, s->sector_num, nb_sectors, &qiov); +static void mirror_write_complete(void *opaque, int ret) +{ + MirrorOp *op = opaque; + MirrorBlockJob *s = op->s; if (ret < 0) { - *p_action = mirror_error_action(s, true, -ret); - goto fail; + BlockDriverState *source = s->common.bs; + BlockErrorAction action; + + bdrv_set_dirty(source, op->sector_num, op->nb_sectors); + action = mirror_error_action(s, false, -ret); + if (action == BDRV_ACTION_REPORT && s->ret >= 0) { + s->ret = ret; + } } - ret = bdrv_co_writev(target, s->sector_num, nb_sectors, &qiov); + mirror_iteration_done(op, ret); +} + +static void mirror_read_complete(void *opaque, int ret) +{ + MirrorOp *op = opaque; + MirrorBlockJob *s = op->s; if (ret < 0) { - *p_action = mirror_error_action(s, false, -ret); - s->synced = false; - goto fail; + BlockDriverState *source = s->common.bs; + BlockErrorAction action; + + bdrv_set_dirty(source, op->sector_num, op->nb_sectors); + action = mirror_error_action(s, true, -ret); + if (action == BDRV_ACTION_REPORT && s->ret >= 0) { + s->ret = ret; + } + + mirror_iteration_done(op, ret); + return; + } + bdrv_aio_writev(s->target, op->sector_num, &op->qiov, op->nb_sectors, + mirror_write_complete, op); +} + +static void coroutine_fn mirror_iteration(MirrorBlockJob *s) +{ + BlockDriverState *source = s->common.bs; + int nb_sectors, sectors_per_chunk, nb_chunks; + int64_t end, sector_num, next_chunk, next_sector, hbitmap_next_sector; + MirrorOp *op; + + s->sector_num = hbitmap_iter_next(&s->hbi); + if (s->sector_num < 0) { + bdrv_dirty_iter_init(source, &s->hbi); + s->sector_num = hbitmap_iter_next(&s->hbi); + trace_mirror_restart_iter(s, bdrv_get_dirty_count(source)); + assert(s->sector_num >= 0); + } + + hbitmap_next_sector = s->sector_num; + sector_num = s->sector_num; + sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS; + end = s->common.len >> BDRV_SECTOR_BITS; + + /* Extend the QEMUIOVector to include all adjacent blocks that will + * be copied in this operation. + * + * We have to do this if we have no backing file yet in the destination, + * and the cluster size is very large. Then we need to do COW ourselves. + * The first time a cluster is copied, copy it entirely. Note that, + * because both the granularity and the cluster size are powers of two, + * the number of sectors to copy cannot exceed one cluster. + * + * We also want to extend the QEMUIOVector to include more adjacent + * dirty blocks if possible, to limit the number of I/O operations and + * run efficiently even with a small granularity. + */ + nb_chunks = 0; + nb_sectors = 0; + next_sector = sector_num; + next_chunk = sector_num / sectors_per_chunk; + + /* Wait for I/O to this cluster (from a previous iteration) to be done. */ + while (test_bit(next_chunk, s->in_flight_bitmap)) { + trace_mirror_yield_in_flight(s, sector_num, s->in_flight); + qemu_coroutine_yield(); + } + + do { + int added_sectors, added_chunks; + + if (!bdrv_get_dirty(source, next_sector) || + test_bit(next_chunk, s->in_flight_bitmap)) { + assert(nb_sectors > 0); + break; + } + + added_sectors = sectors_per_chunk; + if (s->cow_bitmap && !test_bit(next_chunk, s->cow_bitmap)) { + bdrv_round_to_clusters(s->target, + next_sector, added_sectors, + &next_sector, &added_sectors); + + /* On the first iteration, the rounding may make us copy + * sectors before the first dirty one. + */ + if (next_sector < sector_num) { + assert(nb_sectors == 0); + sector_num = next_sector; + next_chunk = next_sector / sectors_per_chunk; + } + } + + added_sectors = MIN(added_sectors, end - (sector_num + nb_sectors)); + added_chunks = (added_sectors + sectors_per_chunk - 1) / sectors_per_chunk; + + /* When doing COW, it may happen that there is not enough space for + * a full cluster. Wait if that is the case. + */ + while (nb_chunks == 0 && s->buf_free_count < added_chunks) { + trace_mirror_yield_buf_busy(s, nb_chunks, s->in_flight); + qemu_coroutine_yield(); + } + if (s->buf_free_count < nb_chunks + added_chunks) { + trace_mirror_break_buf_busy(s, nb_chunks, s->in_flight); + break; + } + + /* We have enough free space to copy these sectors. */ + bitmap_set(s->in_flight_bitmap, next_chunk, added_chunks); + + nb_sectors += added_sectors; + nb_chunks += added_chunks; + next_sector += added_sectors; + next_chunk += added_chunks; + } while (next_sector < end); + + /* Allocate a MirrorOp that is used as an AIO callback. */ + op = g_slice_new(MirrorOp); + op->s = s; + op->sector_num = sector_num; + op->nb_sectors = nb_sectors; + + /* Now make a QEMUIOVector taking enough granularity-sized chunks + * from s->buf_free. + */ + qemu_iovec_init(&op->qiov, nb_chunks); + next_sector = sector_num; + while (nb_chunks-- > 0) { + MirrorBuffer *buf = QSIMPLEQ_FIRST(&s->buf_free); + QSIMPLEQ_REMOVE_HEAD(&s->buf_free, next); + s->buf_free_count--; + qemu_iovec_add(&op->qiov, buf, s->granularity); + + /* Advance the HBitmapIter in parallel, so that we do not examine + * the same sector twice. + */ + if (next_sector > hbitmap_next_sector && bdrv_get_dirty(source, next_sector)) { + hbitmap_next_sector = hbitmap_iter_next(&s->hbi); + } + + next_sector += sectors_per_chunk; } - return 0; -fail: - /* Try again later. */ - bdrv_set_dirty(source, s->sector_num, nb_sectors); - return ret; + bdrv_reset_dirty(source, sector_num, nb_sectors); + + /* Copy the dirty cluster. */ + s->in_flight++; + trace_mirror_one_iteration(s, sector_num, nb_sectors); + bdrv_aio_readv(source, sector_num, &op->qiov, nb_sectors, + mirror_read_complete, op); +} + +static void mirror_free_init(MirrorBlockJob *s) +{ + int granularity = s->granularity; + size_t buf_size = s->buf_size; + uint8_t *buf = s->buf; + + assert(s->buf_free_count == 0); + QSIMPLEQ_INIT(&s->buf_free); + while (buf_size != 0) { + MirrorBuffer *cur = (MirrorBuffer *)buf; + QSIMPLEQ_INSERT_TAIL(&s->buf_free, cur, next); + s->buf_free_count++; + buf_size -= granularity; + buf += granularity; + } +} + +static void mirror_drain(MirrorBlockJob *s) +{ + while (s->in_flight > 0) { + qemu_coroutine_yield(); + } } static void coroutine_fn mirror_run(void *opaque) { MirrorBlockJob *s = opaque; BlockDriverState *bs = s->common.bs; - int64_t sector_num, end; + int64_t sector_num, end, sectors_per_chunk, length; + uint64_t last_pause_ns; + BlockDriverInfo bdi; + char backing_filename[1024]; int ret = 0; int n; @@ -105,20 +305,39 @@ static void coroutine_fn mirror_run(void *opaque) } s->common.len = bdrv_getlength(bs); - if (s->common.len < 0) { + if (s->common.len <= 0) { block_job_completed(&s->common, s->common.len); return; } + length = (bdrv_getlength(bs) + s->granularity - 1) / s->granularity; + s->in_flight_bitmap = bitmap_new(length); + + /* If we have no backing file yet in the destination, we cannot let + * the destination do COW. Instead, we copy sectors around the + * dirty data if needed. We need a bitmap to do that. + */ + bdrv_get_backing_filename(s->target, backing_filename, + sizeof(backing_filename)); + if (backing_filename[0] && !s->target->backing_hd) { + bdrv_get_info(s->target, &bdi); + if (s->granularity < bdi.cluster_size) { + s->buf_size = MAX(s->buf_size, bdi.cluster_size); + s->cow_bitmap = bitmap_new(length); + } + } + end = s->common.len >> BDRV_SECTOR_BITS; - s->buf = qemu_blockalign(bs, BLOCK_SIZE); + s->buf = qemu_blockalign(bs, s->buf_size); + sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS; + mirror_free_init(s); if (s->mode != MIRROR_SYNC_MODE_NONE) { /* First part, loop on the sectors and initialize the dirty bitmap. */ BlockDriverState *base; base = s->mode == MIRROR_SYNC_MODE_FULL ? NULL : bs->backing_hd; for (sector_num = 0; sector_num < end; ) { - int64_t next = (sector_num | (BDRV_SECTORS_PER_DIRTY_CHUNK - 1)) + 1; + int64_t next = (sector_num | (sectors_per_chunk - 1)) + 1; ret = bdrv_co_is_allocated_above(bs, base, sector_num, next - sector_num, &n); @@ -136,24 +355,40 @@ static void coroutine_fn mirror_run(void *opaque) } } - s->sector_num = -1; + bdrv_dirty_iter_init(bs, &s->hbi); + last_pause_ns = qemu_get_clock_ns(rt_clock); for (;;) { uint64_t delay_ns; int64_t cnt; bool should_complete; + if (s->ret < 0) { + ret = s->ret; + goto immediate_exit; + } + cnt = bdrv_get_dirty_count(bs); - if (cnt != 0) { - BlockErrorAction action = BDRV_ACTION_REPORT; - ret = mirror_iteration(s, &action); - if (ret < 0 && action == BDRV_ACTION_REPORT) { - goto immediate_exit; + + /* Note that even when no rate limit is applied we need to yield + * periodically with no pending I/O so that qemu_aio_flush() returns. + * We do so every SLICE_TIME nanoseconds, or when there is an error, + * or when the source is clean, whichever comes first. + */ + if (qemu_get_clock_ns(rt_clock) - last_pause_ns < SLICE_TIME && + s->common.iostatus == BLOCK_DEVICE_IO_STATUS_OK) { + if (s->in_flight == MAX_IN_FLIGHT || s->buf_free_count == 0 || + (cnt == 0 && s->in_flight > 0)) { + trace_mirror_yield(s, s->in_flight, s->buf_free_count, cnt); + qemu_coroutine_yield(); + continue; + } else if (cnt != 0) { + mirror_iteration(s); + continue; } - cnt = bdrv_get_dirty_count(bs); } should_complete = false; - if (cnt == 0) { + if (s->in_flight == 0 && cnt == 0) { trace_mirror_before_flush(s); ret = bdrv_flush(s->target); if (ret < 0) { @@ -196,23 +431,20 @@ static void coroutine_fn mirror_run(void *opaque) trace_mirror_before_sleep(s, cnt, s->synced); if (!s->synced) { /* Publish progress */ - s->common.offset = end * BDRV_SECTOR_SIZE - cnt * BLOCK_SIZE; + s->common.offset = (end - cnt) * BDRV_SECTOR_SIZE; if (s->common.speed) { - delay_ns = ratelimit_calculate_delay(&s->limit, BDRV_SECTORS_PER_DIRTY_CHUNK); + delay_ns = ratelimit_calculate_delay(&s->limit, sectors_per_chunk); } else { delay_ns = 0; } - /* Note that even when no rate limit is applied we need to yield - * with no pending I/O here so that bdrv_drain_all() returns. - */ block_job_sleep_ns(&s->common, rt_clock, delay_ns); if (block_job_is_cancelled(&s->common)) { break; } } else if (!should_complete) { - delay_ns = (cnt == 0 ? SLICE_TIME : 0); + delay_ns = (s->in_flight == 0 && cnt == 0 ? SLICE_TIME : 0); block_job_sleep_ns(&s->common, rt_clock, delay_ns); } else if (cnt == 0) { /* The two disks are in sync. Exit and report successful @@ -222,11 +454,24 @@ static void coroutine_fn mirror_run(void *opaque) s->common.cancelled = false; break; } + last_pause_ns = qemu_get_clock_ns(rt_clock); } immediate_exit: + if (s->in_flight > 0) { + /* We get here only if something went wrong. Either the job failed, + * or it was cancelled prematurely so that we do not guarantee that + * the target is a copy of the source. + */ + assert(ret < 0 || (!s->synced && block_job_is_cancelled(&s->common))); + mirror_drain(s); + } + + assert(s->in_flight == 0); qemu_vfree(s->buf); - bdrv_set_dirty_tracking(bs, false); + g_free(s->cow_bitmap); + g_free(s->in_flight_bitmap); + bdrv_set_dirty_tracking(bs, 0); bdrv_iostatus_disable(s->target); if (s->should_complete && ret == 0) { if (bdrv_get_flags(s->target) != bdrv_get_flags(s->common.bs)) { @@ -288,14 +533,28 @@ static BlockJobType mirror_job_type = { }; void mirror_start(BlockDriverState *bs, BlockDriverState *target, - int64_t speed, MirrorSyncMode mode, - BlockdevOnError on_source_error, + int64_t speed, int64_t granularity, int64_t buf_size, + MirrorSyncMode mode, BlockdevOnError on_source_error, BlockdevOnError on_target_error, BlockDriverCompletionFunc *cb, void *opaque, Error **errp) { MirrorBlockJob *s; + if (granularity == 0) { + /* Choose the default granularity based on the target file's cluster + * size, clamped between 4k and 64k. */ + BlockDriverInfo bdi; + if (bdrv_get_info(target, &bdi) >= 0 && bdi.cluster_size != 0) { + granularity = MAX(4096, bdi.cluster_size); + granularity = MIN(65536, granularity); + } else { + granularity = 65536; + } + } + + assert ((granularity & (granularity - 1)) == 0); + if ((on_source_error == BLOCKDEV_ON_ERROR_STOP || on_source_error == BLOCKDEV_ON_ERROR_ENOSPC) && !bdrv_iostatus_is_enabled(bs)) { @@ -312,7 +571,10 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target, s->on_target_error = on_target_error; s->target = target; s->mode = mode; - bdrv_set_dirty_tracking(bs, true); + s->granularity = granularity; + s->buf_size = MAX(buf_size, granularity); + + bdrv_set_dirty_tracking(bs, granularity); bdrv_set_enable_write_cache(s->target, true); bdrv_set_on_error(s->target, on_target_error, on_target_error); bdrv_iostatus_enable(s->target); diff --git a/block/parallels.c b/block/parallels.c index 377375046f..8688f6ca4c 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -73,14 +73,18 @@ static int parallels_open(BlockDriverState *bs, int flags) BDRVParallelsState *s = bs->opaque; int i; struct parallels_header ph; + int ret; bs->read_only = 1; // no write support yet - if (bdrv_pread(bs->file, 0, &ph, sizeof(ph)) != sizeof(ph)) + ret = bdrv_pread(bs->file, 0, &ph, sizeof(ph)); + if (ret < 0) { goto fail; + } if (memcmp(ph.magic, HEADER_MAGIC, 16) || - (le32_to_cpu(ph.version) != HEADER_VERSION)) { + (le32_to_cpu(ph.version) != HEADER_VERSION)) { + ret = -EMEDIUMTYPE; goto fail; } @@ -90,18 +94,21 @@ static int parallels_open(BlockDriverState *bs, int flags) s->catalog_size = le32_to_cpu(ph.catalog_entries); s->catalog_bitmap = g_malloc(s->catalog_size * 4); - if (bdrv_pread(bs->file, 64, s->catalog_bitmap, s->catalog_size * 4) != - s->catalog_size * 4) - goto fail; + + ret = bdrv_pread(bs->file, 64, s->catalog_bitmap, s->catalog_size * 4); + if (ret < 0) { + goto fail; + } + for (i = 0; i < s->catalog_size; i++) le32_to_cpus(&s->catalog_bitmap[i]); qemu_co_mutex_init(&s->lock); return 0; + fail: - if (s->catalog_bitmap) - g_free(s->catalog_bitmap); - return -1; + g_free(s->catalog_bitmap); + return ret; } static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num) diff --git a/block/qcow.c b/block/qcow.c index 4276610afd..a7135eea47 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -112,7 +112,7 @@ static int qcow_open(BlockDriverState *bs, int flags) be64_to_cpus(&header.l1_table_offset); if (header.magic != QCOW_MAGIC) { - ret = -EINVAL; + ret = -EMEDIUMTYPE; goto fail; } if (header.version != QCOW_VERSION) { diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 6a95aa6c92..bc1784c30e 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -737,11 +737,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, * l1_table_offset when it is the current s->l1_table_offset! Be careful * when changing this! */ if (l1_table_offset != s->l1_table_offset) { - if (l1_size2 != 0) { - l1_table = g_malloc0(align_offset(l1_size2, 512)); - } else { - l1_table = NULL; - } + l1_table = g_malloc0(align_offset(l1_size2, 512)); l1_allocated = 1; if (bdrv_pread(bs->file, l1_table_offset, l1_table, l1_size2) != l1_size2) diff --git a/block/qcow2.c b/block/qcow2.c index f6abff6111..7610e569e3 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -311,7 +311,7 @@ static int qcow2_open(BlockDriverState *bs, int flags) be32_to_cpus(&header.nb_snapshots); if (header.magic != QCOW_MAGIC) { - ret = -EINVAL; + ret = -EMEDIUMTYPE; goto fail; } if (header.version < 2 || header.version > 3) { diff --git a/block/qed.c b/block/qed.c index cf85d8f2b4..b8515e58b3 100644 --- a/block/qed.c +++ b/block/qed.c @@ -390,7 +390,7 @@ static int bdrv_qed_open(BlockDriverState *bs, int flags) qed_header_le_to_cpu(&le_header, &s->header); if (s->header.magic != QED_MAGIC) { - return -EINVAL; + return -EMEDIUMTYPE; } if (s->header.features & ~QED_FEATURE_MASK) { /* image uses unsupported feature bits */ diff --git a/block/raw-posix.c b/block/raw-posix.c index 657af95637..8b6b92608b 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -345,11 +345,20 @@ static int raw_reopen_prepare(BDRVReopenState *state, raw_s->fd = -1; - int fcntl_flags = O_APPEND | O_ASYNC | O_NONBLOCK; + int fcntl_flags = O_APPEND | O_NONBLOCK; #ifdef O_NOATIME fcntl_flags |= O_NOATIME; #endif +#ifdef O_ASYNC + /* Not all operating systems have O_ASYNC, and those that don't + * will not let us track the state into raw_s->open_flags (typically + * you achieve the same effect with an ioctl, for example I_SETSIG + * on Solaris). But we do not use O_ASYNC, so that's fine. + */ + assert((s->open_flags & O_ASYNC) == 0); +#endif + if ((raw_s->open_flags & ~fcntl_flags) == (s->open_flags & ~fcntl_flags)) { /* dup the original fd */ /* TODO: use qemu fcntl wrapper */ diff --git a/block/sheepdog.c b/block/sheepdog.c index 3e49bb83bb..d466b232d7 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -145,7 +145,7 @@ typedef struct SheepdogVdiReq { uint32_t id; uint32_t data_length; uint64_t vdi_size; - uint32_t base_vdi_id; + uint32_t vdi_id; uint32_t copies; uint32_t snapid; uint32_t pad[3]; @@ -1201,7 +1201,7 @@ static int do_sd_create(char *filename, int64_t vdi_size, memset(&hdr, 0, sizeof(hdr)); hdr.opcode = SD_OP_NEW_VDI; - hdr.base_vdi_id = base_vid; + hdr.vdi_id = base_vid; wlen = SD_MAX_VDI_LEN; @@ -1384,6 +1384,7 @@ static void sd_close(BlockDriverState *bs) memset(&hdr, 0, sizeof(hdr)); hdr.opcode = SD_OP_RELEASE_VDI; + hdr.vdi_id = s->inode.vdi_id; wlen = strlen(s->name) + 1; hdr.data_length = wlen; hdr.flags = SD_FLAG_CMD_WRITE; diff --git a/block/vdi.c b/block/vdi.c index 021abaa227..87c691b504 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -246,7 +246,7 @@ static void vdi_header_print(VdiHeader *header) { char uuid[37]; logout("text %s", header->text); - logout("signature 0x%04x\n", header->signature); + logout("signature 0x%08x\n", header->signature); logout("header size 0x%04x\n", header->header_size); logout("image type 0x%04x\n", header->image_type); logout("image flags 0x%04x\n", header->image_flags); @@ -369,10 +369,12 @@ static int vdi_open(BlockDriverState *bs, int flags) BDRVVdiState *s = bs->opaque; VdiHeader header; size_t bmap_size; + int ret; logout("\n"); - if (bdrv_read(bs->file, 0, (uint8_t *)&header, 1) < 0) { + ret = bdrv_read(bs->file, 0, (uint8_t *)&header, 1); + if (ret < 0) { goto fail; } @@ -390,33 +392,45 @@ static int vdi_open(BlockDriverState *bs, int flags) header.disk_size &= ~(SECTOR_SIZE - 1); } - if (header.version != VDI_VERSION_1_1) { + if (header.signature != VDI_SIGNATURE) { + logout("bad vdi signature %08x\n", header.signature); + ret = -EMEDIUMTYPE; + goto fail; + } else if (header.version != VDI_VERSION_1_1) { logout("unsupported version %u.%u\n", header.version >> 16, header.version & 0xffff); + ret = -ENOTSUP; goto fail; } else if (header.offset_bmap % SECTOR_SIZE != 0) { /* We only support block maps which start on a sector boundary. */ logout("unsupported block map offset 0x%x B\n", header.offset_bmap); + ret = -ENOTSUP; goto fail; } else if (header.offset_data % SECTOR_SIZE != 0) { /* We only support data blocks which start on a sector boundary. */ logout("unsupported data offset 0x%x B\n", header.offset_data); + ret = -ENOTSUP; goto fail; } else if (header.sector_size != SECTOR_SIZE) { logout("unsupported sector size %u B\n", header.sector_size); + ret = -ENOTSUP; goto fail; } else if (header.block_size != 1 * MiB) { logout("unsupported block size %u B\n", header.block_size); + ret = -ENOTSUP; goto fail; } else if (header.disk_size > (uint64_t)header.blocks_in_image * header.block_size) { logout("unsupported disk size %" PRIu64 " B\n", header.disk_size); + ret = -ENOTSUP; goto fail; } else if (!uuid_is_null(header.uuid_link)) { logout("link uuid != 0, unsupported\n"); + ret = -ENOTSUP; goto fail; } else if (!uuid_is_null(header.uuid_parent)) { logout("parent uuid != 0, unsupported\n"); + ret = -ENOTSUP; goto fail; } @@ -429,10 +443,9 @@ static int vdi_open(BlockDriverState *bs, int flags) bmap_size = header.blocks_in_image * sizeof(uint32_t); bmap_size = (bmap_size + SECTOR_SIZE - 1) / SECTOR_SIZE; - if (bmap_size > 0) { - s->bmap = g_malloc(bmap_size * SECTOR_SIZE); - } - if (bdrv_read(bs->file, s->bmap_sector, (uint8_t *)s->bmap, bmap_size) < 0) { + s->bmap = g_malloc(bmap_size * SECTOR_SIZE); + ret = bdrv_read(bs->file, s->bmap_sector, (uint8_t *)s->bmap, bmap_size); + if (ret < 0) { goto fail_free_bmap; } @@ -448,7 +461,7 @@ static int vdi_open(BlockDriverState *bs, int flags) g_free(s->bmap); fail: - return -1; + return ret; } static int vdi_reopen_prepare(BDRVReopenState *state, diff --git a/block/vmdk.c b/block/vmdk.c index 19298c2a3e..aef1abcb4f 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -616,7 +616,7 @@ static int vmdk_open_sparse(BlockDriverState *bs, return vmdk_open_vmdk4(bs, file, flags); break; default: - return -EINVAL; + return -EMEDIUMTYPE; break; } } @@ -641,7 +641,7 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, * RW [size in sectors] SPARSE "file-name.vmdk" */ flat_offset = -1; - ret = sscanf(p, "%10s %" SCNd64 " %10s %511s %" SCNd64, + ret = sscanf(p, "%10s %" SCNd64 " %10s \"%511[^\n\r\"]\" %" SCNd64, access, §ors, type, fname, &flat_offset); if (ret < 4 || strcmp(access, "RW")) { goto next_line; @@ -653,14 +653,6 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, return -EINVAL; } - /* trim the quotation marks around */ - if (fname[0] == '"') { - memmove(fname, fname + 1, strlen(fname)); - if (strlen(fname) <= 1 || fname[strlen(fname) - 1] != '"') { - return -EINVAL; - } - fname[strlen(fname) - 1] = '\0'; - } if (sectors <= 0 || (strcmp(type, "FLAT") && strcmp(type, "SPARSE")) || (strcmp(access, "RW"))) { @@ -718,7 +710,7 @@ static int vmdk_open_desc_file(BlockDriverState *bs, int flags, } buf[2047] = '\0'; if (vmdk_parse_description(buf, "createType", ct, sizeof(ct))) { - return -EINVAL; + return -EMEDIUMTYPE; } if (strcmp(ct, "monolithicFlat") && strcmp(ct, "twoGbMaxExtentSparse") && @@ -1442,6 +1434,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options) int fd, idx = 0; char desc[BUF_SIZE]; int64_t total_size = 0, filesize; + const char *adapter_type = NULL; const char *backing_file = NULL; const char *fmt = NULL; int flags = 0; @@ -1453,6 +1446,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options) const char *desc_extent_line; char parent_desc_line[BUF_SIZE] = ""; uint32_t parent_cid = 0xffffffff; + uint32_t number_heads = 16; const char desc_template[] = "# Disk DescriptorFile\n" "version=1\n" @@ -1469,9 +1463,9 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options) "\n" "ddb.virtualHWVersion = \"%d\"\n" "ddb.geometry.cylinders = \"%" PRId64 "\"\n" - "ddb.geometry.heads = \"16\"\n" + "ddb.geometry.heads = \"%d\"\n" "ddb.geometry.sectors = \"63\"\n" - "ddb.adapterType = \"ide\"\n"; + "ddb.adapterType = \"%s\"\n"; if (filename_decompose(filename, path, prefix, postfix, PATH_MAX)) { return -EINVAL; @@ -1480,6 +1474,8 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options) while (options && options->name) { if (!strcmp(options->name, BLOCK_OPT_SIZE)) { total_size = options->value.n; + } else if (!strcmp(options->name, BLOCK_OPT_ADAPTER_TYPE)) { + adapter_type = options->value.s; } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) { backing_file = options->value.s; } else if (!strcmp(options->name, BLOCK_OPT_COMPAT6)) { @@ -1489,6 +1485,20 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options) } options++; } + if (!adapter_type) { + adapter_type = "ide"; + } else if (strcmp(adapter_type, "ide") && + strcmp(adapter_type, "buslogic") && + strcmp(adapter_type, "lsilogic") && + strcmp(adapter_type, "legacyESX")) { + fprintf(stderr, "VMDK: Unknown adapter type: '%s'.\n", adapter_type); + return -EINVAL; + } + if (strcmp(adapter_type, "ide") != 0) { + /* that's the number of heads with which vmware operates when + creating, exporting, etc. vmdk files with a non-ide adapter type */ + number_heads = 255; + } if (!fmt) { /* Default format to monolithicSparse */ fmt = "monolithicSparse"; @@ -1576,7 +1586,8 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options) parent_desc_line, ext_desc_lines, (flags & BLOCK_FLAG_COMPAT6 ? 6 : 4), - total_size / (int64_t)(63 * 16 * 512)); + total_size / (int64_t)(63 * number_heads * 512), number_heads, + adapter_type); if (split || flat) { fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, @@ -1661,6 +1672,12 @@ static QEMUOptionParameter vmdk_create_options[] = { .help = "Virtual disk size" }, { + .name = BLOCK_OPT_ADAPTER_TYPE, + .type = OPT_STRING, + .help = "Virtual adapter type, can be one of " + "ide (default), lsilogic, buslogic or legacyESX" + }, + { .name = BLOCK_OPT_BACKING_FILE, .type = OPT_STRING, .help = "File name of a base image" diff --git a/block/vpc.c b/block/vpc.c index 7948609e50..82229ef5a0 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -163,24 +163,33 @@ static int vpc_open(BlockDriverState *bs, int flags) struct vhd_dyndisk_header* dyndisk_header; uint8_t buf[HEADER_SIZE]; uint32_t checksum; - int err = -1; int disk_type = VHD_DYNAMIC; + int ret; - if (bdrv_pread(bs->file, 0, s->footer_buf, HEADER_SIZE) != HEADER_SIZE) + ret = bdrv_pread(bs->file, 0, s->footer_buf, HEADER_SIZE); + if (ret < 0) { goto fail; + } footer = (struct vhd_footer*) s->footer_buf; if (strncmp(footer->creator, "conectix", 8)) { int64_t offset = bdrv_getlength(bs->file); - if (offset < HEADER_SIZE) { + if (offset < 0) { + ret = offset; + goto fail; + } else if (offset < HEADER_SIZE) { + ret = -EINVAL; goto fail; } + /* If a fixed disk, the footer is found only at the end of the file */ - if (bdrv_pread(bs->file, offset-HEADER_SIZE, s->footer_buf, HEADER_SIZE) - != HEADER_SIZE) { + ret = bdrv_pread(bs->file, offset-HEADER_SIZE, s->footer_buf, + HEADER_SIZE); + if (ret < 0) { goto fail; } if (strncmp(footer->creator, "conectix", 8)) { + ret = -EMEDIUMTYPE; goto fail; } disk_type = VHD_FIXED; @@ -203,19 +212,21 @@ static int vpc_open(BlockDriverState *bs, int flags) /* Allow a maximum disk size of approximately 2 TB */ if (bs->total_sectors >= 65535LL * 255 * 255) { - err = -EFBIG; + ret = -EFBIG; goto fail; } if (disk_type == VHD_DYNAMIC) { - if (bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), buf, - HEADER_SIZE) != HEADER_SIZE) { + ret = bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), buf, + HEADER_SIZE); + if (ret < 0) { goto fail; } dyndisk_header = (struct vhd_dyndisk_header *) buf; if (strncmp(dyndisk_header->magic, "cxsparse", 8)) { + ret = -EINVAL; goto fail; } @@ -226,8 +237,10 @@ static int vpc_open(BlockDriverState *bs, int flags) s->pagetable = g_malloc(s->max_table_entries * 4); s->bat_offset = be64_to_cpu(dyndisk_header->table_offset); - if (bdrv_pread(bs->file, s->bat_offset, s->pagetable, - s->max_table_entries * 4) != s->max_table_entries * 4) { + + ret = bdrv_pread(bs->file, s->bat_offset, s->pagetable, + s->max_table_entries * 4); + if (ret < 0) { goto fail; } @@ -265,8 +278,13 @@ static int vpc_open(BlockDriverState *bs, int flags) migrate_add_blocker(s->migration_blocker); return 0; - fail: - return err; + +fail: + g_free(s->pagetable); +#ifdef CACHE + g_free(s->pageentry_u8); +#endif + return ret; } static int vpc_reopen_prepare(BDRVReopenState *state, diff --git a/blockdev.c b/blockdev.c index 9126587c45..63e6f1eafa 100644 --- a/blockdev.c +++ b/blockdev.c @@ -617,8 +617,13 @@ DriveInfo *drive_init(QemuOpts *opts, BlockInterfaceType block_default_type) ret = bdrv_open(dinfo->bdrv, file, bdrv_flags, drv); if (ret < 0) { - error_report("could not open disk image %s: %s", - file, strerror(-ret)); + if (ret == -EMEDIUMTYPE) { + error_report("could not open disk image %s: not in %s format", + file, drv->format_name); + } else { + error_report("could not open disk image %s: %s", + file, strerror(-ret)); + } goto err; } @@ -642,21 +647,17 @@ void do_commit(Monitor *mon, const QDict *qdict) if (!strcmp(device, "all")) { ret = bdrv_commit_all(); - if (ret == -EBUSY) { - qerror_report(QERR_DEVICE_IN_USE, device); - return; - } } else { bs = bdrv_find(device); if (!bs) { - qerror_report(QERR_DEVICE_NOT_FOUND, device); + monitor_printf(mon, "Device '%s' not found\n", device); return; } ret = bdrv_commit(bs); - if (ret == -EBUSY) { - qerror_report(QERR_DEVICE_IN_USE, device); - return; - } + } + if (ret < 0) { + monitor_printf(mon, "'commit' error for '%s': %s\n", device, + strerror(-ret)); } } @@ -1188,16 +1189,19 @@ void qmp_block_commit(const char *device, drive_get_ref(drive_get_by_blockdev(bs)); } +#define DEFAULT_MIRROR_BUF_SIZE (10 << 20) + void qmp_drive_mirror(const char *device, const char *target, bool has_format, const char *format, enum MirrorSyncMode sync, bool has_mode, enum NewImageMode mode, bool has_speed, int64_t speed, + bool has_granularity, uint32_t granularity, + bool has_buf_size, int64_t buf_size, bool has_on_source_error, BlockdevOnError on_source_error, bool has_on_target_error, BlockdevOnError on_target_error, Error **errp) { - BlockDriverInfo bdi; BlockDriverState *bs; BlockDriverState *source, *target_bs; BlockDriver *proto_drv; @@ -1219,6 +1223,21 @@ void qmp_drive_mirror(const char *device, const char *target, if (!has_mode) { mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS; } + if (!has_granularity) { + granularity = 0; + } + if (!has_buf_size) { + buf_size = DEFAULT_MIRROR_BUF_SIZE; + } + + if (granularity != 0 && (granularity < 512 || granularity > 1048576 * 64)) { + error_set(errp, QERR_INVALID_PARAMETER, device); + return; + } + if (granularity & (granularity - 1)) { + error_set(errp, QERR_INVALID_PARAMETER, device); + return; + } bs = bdrv_find(device); if (!bs) { @@ -1259,11 +1278,11 @@ void qmp_drive_mirror(const char *device, const char *target, return; } + bdrv_get_geometry(bs, &size); + size *= 512; if (sync == MIRROR_SYNC_MODE_FULL && mode != NEW_IMAGE_MODE_EXISTING) { /* create new image w/o backing file */ assert(format && drv); - bdrv_get_geometry(bs, &size); - size *= 512; bdrv_img_create(target, format, NULL, NULL, NULL, size, flags, &local_err); } else { @@ -1276,7 +1295,7 @@ void qmp_drive_mirror(const char *device, const char *target, bdrv_img_create(target, format, source->filename, source->drv->format_name, - NULL, -1, flags, &local_err); + NULL, size, flags, &local_err); break; default: abort(); @@ -1288,6 +1307,9 @@ void qmp_drive_mirror(const char *device, const char *target, return; } + /* Mirroring takes care of copy-on-write using the source's backing + * file. + */ target_bs = bdrv_new(""); ret = bdrv_open(target_bs, target, flags | BDRV_O_NO_BACKING, drv); @@ -1297,18 +1319,8 @@ void qmp_drive_mirror(const char *device, const char *target, return; } - /* We need a backing file if we will copy parts of a cluster. */ - if (bdrv_get_info(target_bs, &bdi) >= 0 && bdi.cluster_size != 0 && - bdi.cluster_size >= BDRV_SECTORS_PER_DIRTY_CHUNK * 512) { - ret = bdrv_open_backing_file(target_bs); - if (ret < 0) { - bdrv_delete(target_bs); - error_set(errp, QERR_OPEN_FILE_FAILED, target); - return; - } - } - - mirror_start(bs, target_bs, speed, sync, on_source_error, on_target_error, + mirror_start(bs, target_bs, speed, granularity, buf_size, sync, + on_source_error, on_target_error, block_job_cb, bs, &local_err); if (local_err != NULL) { bdrv_delete(target_bs); diff --git a/bsd-user/main.c b/bsd-user/main.c index 1dc033046b..ae24723710 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -917,7 +917,7 @@ int main(int argc, char **argv) fprintf(stderr, "Unable to find CPU definition\n"); exit(1); } -#if defined(TARGET_I386) || defined(TARGET_SPARC) || defined(TARGET_PPC) +#if defined(TARGET_SPARC) || defined(TARGET_PPC) cpu_reset(ENV_GET_CPU(env)); #endif thread_env = env; diff --git a/bsd-user/mmap.c b/bsd-user/mmap.c index 5d6cffc458..aae8ea10be 100644 --- a/bsd-user/mmap.c +++ b/bsd-user/mmap.c @@ -74,7 +74,7 @@ void mmap_unlock(void) } #endif -void *qemu_vmalloc(size_t size) +static void *bsd_vmalloc(size_t size) { void *p; mmap_lock(); @@ -98,7 +98,7 @@ void *g_malloc(size_t size) { char * p; size += 16; - p = qemu_vmalloc(size); + p = bsd_vmalloc(size); *(size_t *)p = size; return p + 16; } @@ -1180,7 +1180,7 @@ fi z_version=`cut -f3 -d. $source_path/VERSION` if test -z "$werror" ; then - if test "$z_version" = "50" -a \ + if test -d "$source_path/.git" -a \ "$linux" = "yes" ; then werror="yes" else @@ -1434,7 +1434,7 @@ fi if test "$seccomp" != "no" ; then if $pkg_config --atleast-version=1.0.0 libseccomp --modversion >/dev/null 2>&1; then - LIBS=`$pkg_config --libs libseccomp` + libs_softmmu="$libs_softmmu `$pkg_config --libs libseccomp`" seccomp="yes" else if test "$seccomp" = "yes"; then @@ -2388,7 +2388,7 @@ fi ########################################## # opengl probe, used by milkymist-tmu2 if test "$opengl" != "no" ; then - opengl_libs="-lGL" + opengl_libs="-lGL -lX11" cat > $TMPC << EOF #include <X11/Xlib.h> #include <GL/gl.h> @@ -517,7 +517,7 @@ static void qemu_init_sigbus(void) prctl(PR_MCE_KILL, PR_MCE_KILL_SET, PR_MCE_KILL_EARLY, 0, 0); } -static void qemu_kvm_eat_signals(CPUArchState *env) +static void qemu_kvm_eat_signals(CPUState *cpu) { struct timespec ts = { 0, 0 }; siginfo_t siginfo; @@ -538,7 +538,7 @@ static void qemu_kvm_eat_signals(CPUArchState *env) switch (r) { case SIGBUS: - if (kvm_on_sigbus_vcpu(env, siginfo.si_code, siginfo.si_addr)) { + if (kvm_on_sigbus_vcpu(cpu, siginfo.si_code, siginfo.si_addr)) { sigbus_reraise(); } break; @@ -560,7 +560,7 @@ static void qemu_init_sigbus(void) { } -static void qemu_kvm_eat_signals(CPUArchState *env) +static void qemu_kvm_eat_signals(CPUState *cpu) { } #endif /* !CONFIG_LINUX */ @@ -727,7 +727,7 @@ static void qemu_kvm_wait_io_event(CPUArchState *env) qemu_cond_wait(cpu->halt_cond, &qemu_global_mutex); } - qemu_kvm_eat_signals(env); + qemu_kvm_eat_signals(cpu); qemu_wait_io_event_common(cpu); } diff --git a/disas/Makefile.objs b/disas/Makefile.objs index 3f5c5b9a21..ed75f9a99b 100644 --- a/disas/Makefile.objs +++ b/disas/Makefile.objs @@ -1,18 +1,18 @@ -universal-obj-$(CONFIG_ALPHA_DIS) += alpha.o -universal-obj-$(CONFIG_ARM_DIS) += arm.o -universal-obj-$(CONFIG_CRIS_DIS) += cris.o -universal-obj-$(CONFIG_HPPA_DIS) += hppa.o -universal-obj-$(CONFIG_I386_DIS) += i386.o -universal-obj-$(CONFIG_IA64_DIS) += ia64.o -universal-obj-$(CONFIG_M68K_DIS) += m68k.o -universal-obj-$(CONFIG_MICROBLAZE_DIS) += microblaze.o -universal-obj-$(CONFIG_MIPS_DIS) += mips.o -universal-obj-$(CONFIG_PPC_DIS) += ppc.o -universal-obj-$(CONFIG_S390_DIS) += s390.o -universal-obj-$(CONFIG_SH4_DIS) += sh4.o -universal-obj-$(CONFIG_SPARC_DIS) += sparc.o -universal-obj-$(CONFIG_LM32_DIS) += lm32.o +common-obj-$(CONFIG_ALPHA_DIS) += alpha.o +common-obj-$(CONFIG_ARM_DIS) += arm.o +common-obj-$(CONFIG_CRIS_DIS) += cris.o +common-obj-$(CONFIG_HPPA_DIS) += hppa.o +common-obj-$(CONFIG_I386_DIS) += i386.o +common-obj-$(CONFIG_IA64_DIS) += ia64.o +common-obj-$(CONFIG_M68K_DIS) += m68k.o +common-obj-$(CONFIG_MICROBLAZE_DIS) += microblaze.o +common-obj-$(CONFIG_MIPS_DIS) += mips.o +common-obj-$(CONFIG_PPC_DIS) += ppc.o +common-obj-$(CONFIG_S390_DIS) += s390.o +common-obj-$(CONFIG_SH4_DIS) += sh4.o +common-obj-$(CONFIG_SPARC_DIS) += sparc.o +common-obj-$(CONFIG_LM32_DIS) += lm32.o # TODO: As long as the TCG interpreter and its generated code depend # on the QEMU target, we cannot compile the disassembler here. -#universal-obj-$(CONFIG_TCI_DIS) += tci.o +#common-obj-$(CONFIG_TCI_DIS) += tci.o diff --git a/docs/tracing.txt b/docs/tracing.txt index 453cc4a63d..14db3bffb4 100644 --- a/docs/tracing.txt +++ b/docs/tracing.txt @@ -23,7 +23,7 @@ for debugging, profiling, and observing execution. 4. Pretty-print the binary trace file: - ./simpletrace.py trace-events trace-* + ./scripts/simpletrace.py trace-events trace-* == Trace events == @@ -198,7 +198,7 @@ The "simple" backend produces binary trace files that can be formatted with the simpletrace.py script. The script takes the "trace-events" file and the binary trace: - ./simpletrace.py trace-events trace-12345 + ./scripts/simpletrace.py trace-events trace-12345 You must ensure that the same "trace-events" file was used to build QEMU, otherwise trace event declarations may have changed and output will not be diff --git a/docs/virtio-balloon-stats.txt b/docs/virtio-balloon-stats.txt new file mode 100644 index 0000000000..f74612f468 --- /dev/null +++ b/docs/virtio-balloon-stats.txt @@ -0,0 +1,104 @@ +virtio balloon memory statistics +================================ + +The virtio balloon driver supports guest memory statistics reporting. These +statistics are available to QEMU users as QOM (QEMU Object Model) device +properties via a polling mechanism. + +Before querying the available stats, clients first have to enable polling. +This is done by writing a time interval value (in seconds) to the +guest-stats-polling-interval property. This value can be: + + > 0 enables polling in the specified interval. If polling is already + enabled, the polling time interval is changed to the new value + + 0 disables polling. Previous polled statistics are still valid and + can be queried. + +Once polling is enabled, the virtio-balloon device in QEMU will start +polling the guest's balloon driver for new stats in the specified time +interval. + +To retrieve those stats, clients have to query the guest-stats property, +which will return a dictionary containing: + + o A key named 'stats', containing all available stats. If the guest + doesn't support a particular stat, or if it couldn't be retrieved, + its value will be -1. Currently, the following stats are supported: + + - stat-swap-in + - stat-swap-out + - stat-major-faults + - stat-minor-faults + - stat-free-memory + - stat-total-memory + + o A key named last-update, which contains the last stats update + timestamp in seconds. Since this timestamp is generated by the host, + a buggy guest can't influence its value + +It's also important to note the following: + + - Previously polled statistics remain available even if the polling is + later disabled + + - As noted above, if a guest doesn't support a particular stat its value + will always be -1. However, it's also possible that a guest temporarily + couldn't update one or even all stats. If this happens, just wait for + the next update + + - Polling can be enabled even if the guest doesn't have stats support + or the balloon driver wasn't loaded in the guest. If this is the case + and stats are queried, an error will be returned + + - The polling timer is only re-armed when the guest responds to the + statistics request. This means that if a (buggy) guest doesn't ever + respond to the request the timer will never be re-armed, which has + the same effect as disabling polling + +Here are a few examples. QEMU is started with '-balloon virtio', which +generates '/machine/peripheral-anon/device[1]' as the QOM path for the +balloon device. + +Enable polling with 2 seconds interval: + +{ "execute": "qom-set", + "arguments": { "path": "/machine/peripheral-anon/device[1]", + "property": "guest-stats-polling-interval", "value": 2 } } + +{ "return": {} } + +Change polling to 10 seconds: + +{ "execute": "qom-set", + "arguments": { "path": "/machine/peripheral-anon/device[1]", + "property": "guest-stats-polling-interval", "value": 10 } } + +{ "return": {} } + +Get stats: + +{ "execute": "qom-get", + "arguments": { "path": "/machine/peripheral-anon/device[1]", + "property": "guest-stats" } } +{ + "return": { + "stats": { + "stat-swap-out": 0, + "stat-free-memory": 844943360, + "stat-minor-faults": 219028, + "stat-major-faults": 235, + "stat-total-memory": 1044406272, + "stat-swap-in": 0 + }, + "last-update": 1358529861 + } +} + +Disable polling: + +{ "execute": "qom-set", + "arguments": { "path": "/machine/peripheral-anon/device[1]", + "property": "stats-polling-interval", "value": 0 } } + +{ "return": {} } diff --git a/fpu/softfloat.c b/fpu/softfloat.c index ac3d150015..83ccc4b8cd 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -2234,7 +2234,7 @@ float32 float32_muladd(float32 a, float32 b, float32 c, int flags STATUS_PARAM) } } /* Zero plus something non-zero : just return the something */ - return make_float32(float32_val(c) ^ (signflip << 31)); + return packFloat32(cSign ^ signflip, cExp, cSig); } if (aExp == 0) { @@ -3787,7 +3787,7 @@ float64 float64_muladd(float64 a, float64 b, float64 c, int flags STATUS_PARAM) } } /* Zero plus something non-zero : just return the something */ - return make_float64(float64_val(c) ^ ((uint64_t)signflip << 63)); + return packFloat64(cSign ^ signflip, cExp, cSig); } if (aExp == 0) { diff --git a/fsdev/Makefile.objs b/fsdev/Makefile.objs index ee16ca600c..206289c49f 100644 --- a/fsdev/Makefile.objs +++ b/fsdev/Makefile.objs @@ -1,10 +1,10 @@ ifeq ($(CONFIG_REALLY_VIRTFS),y) common-obj-y = qemu-fsdev.o virtio-9p-marshal.o - -# Toplevel always builds this; targets without virtio will put it in -# common-obj-y -extra-obj-y = qemu-fsdev-dummy.o else common-obj-y = qemu-fsdev-dummy.o endif common-obj-y += qemu-fsdev-opts.o + +# Toplevel always builds this; targets without virtio will put it in +# common-obj-y +common-obj-$(CONFIG_ALL) += qemu-fsdev-dummy.o diff --git a/fsdev/virtfs-proxy-helper.c b/fsdev/virtfs-proxy-helper.c index 6b9afd32d5..36f66163b2 100644 --- a/fsdev/virtfs-proxy-helper.c +++ b/fsdev/virtfs-proxy-helper.c @@ -1039,7 +1039,7 @@ int main(int argc, char **argv) } switch (c) { case 'p': - rpath = strdup(optarg); + rpath = g_strdup(optarg); break; case 'n': is_daemon = false; @@ -1048,7 +1048,7 @@ int main(int argc, char **argv) sock = atoi(optarg); break; case 's': - sock_name = strdup(optarg); + sock_name = g_strdup(optarg); break; case 'u': own_u = atoi(optarg); diff --git a/hmp-commands.hx b/hmp-commands.hx index 0934b9b915..64008a92d1 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -837,6 +837,44 @@ STEXI @item nmi @var{cpu} @findex nmi Inject an NMI on the given CPU (x86 only). + +ETEXI + + { + .name = "ringbuf_write", + .args_type = "device:s,data:s", + .params = "device data", + .help = "Write to a ring buffer character device", + .mhandler.cmd = hmp_ringbuf_write, + }, + +STEXI +@item ringbuf_write @var{device} @var{data} +@findex ringbuf_write +Write @var{data} to ring buffer character device @var{device}. +@var{data} must be a UTF-8 string. + +ETEXI + + { + .name = "ringbuf_read", + .args_type = "device:s,size:i", + .params = "device size", + .help = "Read from a ring buffer character device", + .mhandler.cmd = hmp_ringbuf_read, + }, + +STEXI +@item ringbuf_read @var{device} +@findex ringbuf_read +Read and print up to @var{size} bytes from ring buffer character +device @var{device}. +Certain non-printable characters are printed \uXXXX, where XXXX is the +character code in hexadecimal. Character \ is printed \\. +Bug: can screw up when the buffer contains invalid UTF-8 sequences, +NUL characters, after the ring buffer lost data, and when reading +stops because the size limit is reached. + ETEXI { @@ -1484,37 +1522,38 @@ passed since 1970, i.e. unix epoch. @end table ETEXI - { - .name = "chardev-add", - .args_type = "args:s", - .params = "args", - .help = "add chardev", - .mhandler.cmd = hmp_chardev_add, - }, - -STEXI -@item chardev_add args -@findex chardev_add - -chardev_add accepts the same parameters as the -chardev command line switch. - -ETEXI - - { - .name = "chardev-remove", - .args_type = "id:s", - .params = "id", - .help = "remove chardev", - .mhandler.cmd = hmp_chardev_remove, - }, - -STEXI -@item chardev_remove id -@findex chardev_remove - -Removes the chardev @var{id}. - -ETEXI +HXCOMM Disabled for now, because it isn't built on top of QMP's chardev-add +HXCOMM { +HXCOMM .name = "chardev-add", +HXCOMM .args_type = "args:s", +HXCOMM .params = "args", +HXCOMM .help = "add chardev", +HXCOMM .mhandler.cmd = hmp_chardev_add, +HXCOMM }, +HXCOMM +HXCOMM STEXI +HXCOMM @item chardev_add args +HXCOMM @findex chardev_add +HXCOMM +HXCOMM chardev_add accepts the same parameters as the -chardev command line switch. +HXCOMM +HXCOMM ETEXI +HXCOMM +HXCOMM { +HXCOMM .name = "chardev-remove", +HXCOMM .args_type = "id:s", +HXCOMM .params = "id", +HXCOMM .help = "remove chardev", +HXCOMM .mhandler.cmd = hmp_chardev_remove, +HXCOMM }, +HXCOMM +HXCOMM STEXI +HXCOMM @item chardev_remove id +HXCOMM @findex chardev_remove +HXCOMM +HXCOMM Removes the chardev @var{id}. +HXCOMM +HXCOMM ETEXI { .name = "info", @@ -465,29 +465,7 @@ void hmp_info_balloon(Monitor *mon, const QDict *qdict) return; } - monitor_printf(mon, "balloon: actual=%" PRId64, info->actual >> 20); - if (info->has_mem_swapped_in) { - monitor_printf(mon, " mem_swapped_in=%" PRId64, info->mem_swapped_in); - } - if (info->has_mem_swapped_out) { - monitor_printf(mon, " mem_swapped_out=%" PRId64, info->mem_swapped_out); - } - if (info->has_major_page_faults) { - monitor_printf(mon, " major_page_faults=%" PRId64, - info->major_page_faults); - } - if (info->has_minor_page_faults) { - monitor_printf(mon, " minor_page_faults=%" PRId64, - info->minor_page_faults); - } - if (info->has_free_mem) { - monitor_printf(mon, " free_mem=%" PRId64, info->free_mem); - } - if (info->has_total_mem) { - monitor_printf(mon, " total_mem=%" PRId64, info->total_mem); - } - - monitor_printf(mon, "\n"); + monitor_printf(mon, "balloon: actual=%" PRId64 "\n", info->actual >> 20); qapi_free_BalloonInfo(info); } @@ -684,6 +662,48 @@ void hmp_pmemsave(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, &errp); } +void hmp_ringbuf_write(Monitor *mon, const QDict *qdict) +{ + const char *chardev = qdict_get_str(qdict, "device"); + const char *data = qdict_get_str(qdict, "data"); + Error *errp = NULL; + + qmp_ringbuf_write(chardev, data, false, 0, &errp); + + hmp_handle_error(mon, &errp); +} + +void hmp_ringbuf_read(Monitor *mon, const QDict *qdict) +{ + uint32_t size = qdict_get_int(qdict, "size"); + const char *chardev = qdict_get_str(qdict, "device"); + char *data; + Error *errp = NULL; + int i; + + data = qmp_ringbuf_read(chardev, size, false, 0, &errp); + if (errp) { + monitor_printf(mon, "%s\n", error_get_pretty(errp)); + error_free(errp); + return; + } + + for (i = 0; data[i]; i++) { + unsigned char ch = data[i]; + + if (ch == '\\') { + monitor_printf(mon, "\\\\"); + } else if ((ch < 0x20 && ch != '\n' && ch != '\t') || ch == 0x7F) { + monitor_printf(mon, "\\u%04X", ch); + } else { + monitor_printf(mon, "%c", ch); + } + + } + monitor_printf(mon, "\n"); + g_free(data); +} + static void hmp_cont_cb(void *opaque, int err) { if (!err) { @@ -796,7 +816,7 @@ void hmp_drive_mirror(Monitor *mon, const QDict *qdict) qmp_drive_mirror(device, filename, !!format, format, full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP, - true, mode, false, 0, + true, mode, false, 0, false, 0, false, 0, false, 0, false, 0, &errp); hmp_handle_error(mon, &errp); } @@ -880,7 +900,7 @@ void hmp_migrate_set_capability(Monitor *mon, const QDict *qdict) qapi_free_MigrationCapabilityStatusList(caps); if (err) { - monitor_printf(mon, "migrate_set_parameter: %s\n", + monitor_printf(mon, "migrate_set_capability: %s\n", error_get_pretty(err)); error_free(err); } @@ -43,6 +43,8 @@ void hmp_system_powerdown(Monitor *mon, const QDict *qdict); void hmp_cpu(Monitor *mon, const QDict *qdict); void hmp_memsave(Monitor *mon, const QDict *qdict); void hmp_pmemsave(Monitor *mon, const QDict *qdict); +void hmp_ringbuf_write(Monitor *mon, const QDict *qdict); +void hmp_ringbuf_read(Monitor *mon, const QDict *qdict); void hmp_cont(Monitor *mon, const QDict *qdict); void hmp_system_wakeup(Monitor *mon, const QDict *qdict); void hmp_inject_nmi(Monitor *mon, const QDict *qdict); diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c index 6f427dfc5d..74155fb61e 100644 --- a/hw/9pfs/virtio-9p-device.c +++ b/hw/9pfs/virtio-9p-device.c @@ -85,11 +85,7 @@ VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf) } s->ctx.export_flags = fse->export_flags; - if (fse->path) { - s->ctx.fs_root = g_strdup(fse->path); - } else { - s->ctx.fs_root = NULL; - } + s->ctx.fs_root = g_strdup(fse->path); s->ctx.exops.get_st_gen = NULL; len = strlen(conf->tag); if (len > MAX_TAG_LEN - 1) { @@ -98,7 +94,7 @@ VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf) exit(1); } - s->tag = strdup(conf->tag); + s->tag = g_strdup(conf->tag); s->ctx.uid = -1; s->ops = fse->ops; diff --git a/hw/9pfs/virtio-9p-local.c b/hw/9pfs/virtio-9p-local.c index 113602144c..f1b1c83a22 100644 --- a/hw/9pfs/virtio-9p-local.c +++ b/hw/9pfs/virtio-9p-local.c @@ -46,7 +46,7 @@ static const char *local_mapped_attr_path(FsContext *ctx, const char *path, char *buffer) { char *dir_name; - char *tmp_path = strdup(path); + char *tmp_path = g_strdup(path); char *base_name = basename(tmp_path); /* NULL terminate the directory */ @@ -55,7 +55,7 @@ static const char *local_mapped_attr_path(FsContext *ctx, snprintf(buffer, PATH_MAX, "%s/%s/%s/%s", ctx->fs_root, dir_name, VIRTFS_META_DIR, base_name); - free(tmp_path); + g_free(tmp_path); return buffer; } @@ -130,7 +130,7 @@ static int local_create_mapped_attr_dir(FsContext *ctx, const char *path) { int err; char attr_dir[PATH_MAX]; - char *tmp_path = strdup(path); + char *tmp_path = g_strdup(path); snprintf(attr_dir, PATH_MAX, "%s/%s/%s", ctx->fs_root, dirname(tmp_path), VIRTFS_META_DIR); @@ -139,7 +139,7 @@ static int local_create_mapped_attr_dir(FsContext *ctx, const char *path) if (err < 0 && errno == EEXIST) { err = 0; } - free(tmp_path); + g_free(tmp_path); return err; } diff --git a/hw/9pfs/virtio-9p.c b/hw/9pfs/virtio-9p.c index 0aaf0d2de0..d3ea820eae 100644 --- a/hw/9pfs/virtio-9p.c +++ b/hw/9pfs/virtio-9p.c @@ -327,7 +327,7 @@ static int free_fid(V9fsPDU *pdu, V9fsFidState *fidp) return retval; } -static void put_fid(V9fsPDU *pdu, V9fsFidState *fidp) +static int put_fid(V9fsPDU *pdu, V9fsFidState *fidp) { BUG_ON(!fidp->ref); fidp->ref--; @@ -348,8 +348,9 @@ static void put_fid(V9fsPDU *pdu, V9fsFidState *fidp) pdu->s->migration_blocker = NULL; } } - free_fid(pdu, fidp); + return free_fid(pdu, fidp); } + return 0; } static V9fsFidState *clunk_fid(V9fsState *s, int32_t fid) @@ -1537,9 +1538,10 @@ static void v9fs_clunk(void *opaque) * free the fid. */ fidp->ref++; - err = offset; - - put_fid(pdu, fidp); + err = put_fid(pdu, fidp); + if (!err) { + err = offset; + } out_nofid: complete_pdu(s, pdu, err); } @@ -3101,11 +3103,7 @@ static void v9fs_xattrcreate(void *opaque) xattr_fidp->fs.xattr.flags = flags; v9fs_string_init(&xattr_fidp->fs.xattr.name); v9fs_string_copy(&xattr_fidp->fs.xattr.name, &name); - if (size) { - xattr_fidp->fs.xattr.value = g_malloc(size); - } else { - xattr_fidp->fs.xattr.value = NULL; - } + xattr_fidp->fs.xattr.value = g_malloc(size); err = offset; put_fid(pdu, file_fidp); out_nofid: diff --git a/hw/Makefile.objs b/hw/Makefile.objs index 23ac24977e..447e32a42e 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -1,9 +1,10 @@ # core qdev-related obj files, also used by *-user: -universal-obj-y += qdev.o qdev-properties.o +common-obj-y += qdev.o qdev-properties.o # irq.o needed for qdev GPIO handling: -universal-obj-y += irq.o +common-obj-y += irq.o -common-obj-y = usb/ ide/ pci/ +ifeq ($(CONFIG_SOFTMMU),y) +common-obj-y += usb/ ide/ pci/ common-obj-y += loader.o common-obj-$(CONFIG_VIRTIO) += virtio-console.o common-obj-$(CONFIG_VIRTIO) += virtio-rng.o @@ -43,8 +44,6 @@ common-obj-$(CONFIG_I8259) += i8259_common.o i8259.o common-obj-y += fifo.o common-obj-y += pam.o -extra-obj-y += pci/ - # PPC devices common-obj-$(CONFIG_PREP_PCI) += prep_pci.o common-obj-$(CONFIG_I82378) += i82378.o @@ -217,3 +216,4 @@ obj-$(CONFIG_LINUX) += vfio_pci.o endif $(obj)/baum.o: QEMU_CFLAGS += $(SDL_CFLAGS) +endif diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c index 0d33849e95..65b26013bd 100644 --- a/hw/acpi_piix4.c +++ b/hw/acpi_piix4.c @@ -235,7 +235,7 @@ static int acpi_load_old(QEMUFile *f, void *opaque, int version_id) qemu_get_be16s(f, &s->ar.pm1.evt.en); qemu_get_be16s(f, &s->ar.pm1.cnt.cnt); - ret = vmstate_load_state(f, &vmstate_apm, opaque, 1); + ret = vmstate_load_state(f, &vmstate_apm, &s->apm, 1); if (ret) { return ret; } @@ -253,7 +253,7 @@ static int acpi_load_old(QEMUFile *f, void *opaque, int version_id) qemu_get_be16s(f, &temp); } - ret = vmstate_load_state(f, &vmstate_pci_status, opaque, 1); + ret = vmstate_load_state(f, &vmstate_pci_status, &s->pci0_status, 1); return ret; } @@ -48,16 +48,21 @@ do { printf("ADB: " fmt , ## __VA_ARGS__); } while (0) #define ADB_CMD_CHANGE_ID_AND_ENABLE 0x00 /* ADB default device IDs (upper 4 bits of ADB command byte) */ -#define ADB_DONGLE 1 -#define ADB_KEYBOARD 2 -#define ADB_MOUSE 3 -#define ADB_TABLET 4 -#define ADB_MODEM 5 -#define ADB_MISC 7 +#define ADB_DEVID_DONGLE 1 +#define ADB_DEVID_KEYBOARD 2 +#define ADB_DEVID_MOUSE 3 +#define ADB_DEVID_TABLET 4 +#define ADB_DEVID_MODEM 5 +#define ADB_DEVID_MISC 7 /* error codes */ #define ADB_RET_NOTPRESENT (-2) +static void adb_device_reset(ADBDevice *d) +{ + qdev_reset_all(DEVICE(d)); +} + int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len) { ADBDevice *d; @@ -66,18 +71,17 @@ int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len) cmd = buf[0] & 0xf; if (cmd == ADB_BUSRESET) { for(i = 0; i < s->nb_devices; i++) { - d = &s->devices[i]; - if (d->devreset) { - d->devreset(d); - } + d = s->devices[i]; + adb_device_reset(d); } return 0; } devaddr = buf[0] >> 4; for(i = 0; i < s->nb_devices; i++) { - d = &s->devices[i]; + d = s->devices[i]; if (d->devaddr == devaddr) { - return d->devreq(d, obuf, buf, len); + ADBDeviceClass *adc = ADB_DEVICE_GET_CLASS(d); + return adc->devreq(d, obuf, buf, len); } } return ADB_RET_NOTPRESENT; @@ -94,7 +98,7 @@ int adb_poll(ADBBusState *s, uint8_t *obuf) for(i = 0; i < s->nb_devices; i++) { if (s->poll_index >= s->nb_devices) s->poll_index = 0; - d = &s->devices[s->poll_index]; + d = s->devices[s->poll_index]; buf[0] = ADB_READREG | (d->devaddr << 4); olen = adb_request(s, obuf + 1, buf, 1); /* if there is data, we poll again the same device */ @@ -108,32 +112,67 @@ int adb_poll(ADBBusState *s, uint8_t *obuf) return olen; } -static ADBDevice *adb_register_device(ADBBusState *s, int devaddr, - ADBDeviceRequest *devreq, - ADBDeviceReset *devreset, - void *opaque) +static const TypeInfo adb_bus_type_info = { + .name = TYPE_ADB_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(ADBBusState), +}; + +static void adb_device_realizefn(DeviceState *dev, Error **errp) { - ADBDevice *d; - if (s->nb_devices >= MAX_ADB_DEVICES) - return NULL; - d = &s->devices[s->nb_devices++]; - d->bus = s; - d->devaddr = devaddr; - d->devreq = devreq; - d->devreset = devreset; - d->opaque = opaque; - qemu_register_reset((QEMUResetHandler *)devreset, d); - return d; + ADBDevice *d = ADB_DEVICE(dev); + ADBBusState *bus = ADB_BUS(qdev_get_parent_bus(dev)); + + if (bus->nb_devices >= MAX_ADB_DEVICES) { + return; + } + + bus->devices[bus->nb_devices++] = d; } +static void adb_device_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = adb_device_realizefn; + dc->bus_type = TYPE_ADB_BUS; +} + +static const TypeInfo adb_device_type_info = { + .name = TYPE_ADB_DEVICE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(ADBDevice), + .abstract = true, + .class_init = adb_device_class_init, +}; + /***************************************************************/ /* Keyboard ADB device */ +#define ADB_KEYBOARD(obj) OBJECT_CHECK(KBDState, (obj), TYPE_ADB_KEYBOARD) + typedef struct KBDState { + /*< private >*/ + ADBDevice parent_obj; + /*< public >*/ + uint8_t data[128]; int rptr, wptr, count; } KBDState; +#define ADB_KEYBOARD_CLASS(class) \ + OBJECT_CLASS_CHECK(ADBKeyboardClass, (class), TYPE_ADB_KEYBOARD) +#define ADB_KEYBOARD_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ADBKeyboardClass, (obj), TYPE_ADB_KEYBOARD) + +typedef struct ADBKeyboardClass { + /*< private >*/ + ADBDeviceClass parent_class; + /*< public >*/ + + DeviceRealize parent_realize; +} ADBKeyboardClass; + static const uint8_t pc_to_adb_keycode[256] = { 0, 53, 18, 19, 20, 21, 23, 22, 26, 28, 25, 29, 27, 24, 51, 48, 12, 13, 14, 15, 17, 16, 32, 34, 31, 35, 33, 30, 36, 54, 0, 1, @@ -155,8 +194,7 @@ static const uint8_t pc_to_adb_keycode[256] = { static void adb_kbd_put_keycode(void *opaque, int keycode) { - ADBDevice *d = opaque; - KBDState *s = d->opaque; + KBDState *s = opaque; if (s->count < sizeof(s->data)) { s->data[s->wptr] = keycode; @@ -169,7 +207,7 @@ static void adb_kbd_put_keycode(void *opaque, int keycode) static int adb_kbd_poll(ADBDevice *d, uint8_t *obuf) { static int ext_keycode; - KBDState *s = d->opaque; + KBDState *s = ADB_KEYBOARD(d); int adb_keycode, keycode; int olen; @@ -203,7 +241,7 @@ static int adb_kbd_poll(ADBDevice *d, uint8_t *obuf) static int adb_kbd_request(ADBDevice *d, uint8_t *obuf, const uint8_t *buf, int len) { - KBDState *s = d->opaque; + KBDState *s = ADB_KEYBOARD(d); int cmd, reg, olen; if ((buf[0] & 0x0f) == ADB_FLUSH) { @@ -275,41 +313,90 @@ static const VMStateDescription vmstate_adb_kbd = { } }; -static int adb_kbd_reset(ADBDevice *d) +static void adb_kbd_reset(DeviceState *dev) { - KBDState *s = d->opaque; + ADBDevice *d = ADB_DEVICE(dev); + KBDState *s = ADB_KEYBOARD(dev); d->handler = 1; - d->devaddr = ADB_KEYBOARD; - memset(s, 0, sizeof(KBDState)); - - return 0; + d->devaddr = ADB_DEVID_KEYBOARD; + memset(s->data, 0, sizeof(s->data)); + s->rptr = 0; + s->wptr = 0; + s->count = 0; } -void adb_kbd_init(ADBBusState *bus) +static void adb_kbd_realizefn(DeviceState *dev, Error **errp) { - ADBDevice *d; - KBDState *s; - s = g_malloc0(sizeof(KBDState)); - d = adb_register_device(bus, ADB_KEYBOARD, adb_kbd_request, - adb_kbd_reset, s); + ADBDevice *d = ADB_DEVICE(dev); + ADBKeyboardClass *akc = ADB_KEYBOARD_GET_CLASS(dev); + + akc->parent_realize(dev, errp); + qemu_add_kbd_event_handler(adb_kbd_put_keycode, d); - vmstate_register(NULL, -1, &vmstate_adb_kbd, s); } +static void adb_kbd_initfn(Object *obj) +{ + ADBDevice *d = ADB_DEVICE(obj); + + d->devaddr = ADB_DEVID_KEYBOARD; +} + +static void adb_kbd_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + ADBDeviceClass *adc = ADB_DEVICE_CLASS(oc); + ADBKeyboardClass *akc = ADB_KEYBOARD_CLASS(oc); + + akc->parent_realize = dc->realize; + dc->realize = adb_kbd_realizefn; + + adc->devreq = adb_kbd_request; + dc->reset = adb_kbd_reset; + dc->vmsd = &vmstate_adb_kbd; +} + +static const TypeInfo adb_kbd_type_info = { + .name = TYPE_ADB_KEYBOARD, + .parent = TYPE_ADB_DEVICE, + .instance_size = sizeof(KBDState), + .instance_init = adb_kbd_initfn, + .class_init = adb_kbd_class_init, + .class_size = sizeof(ADBKeyboardClass), +}; + /***************************************************************/ /* Mouse ADB device */ +#define ADB_MOUSE(obj) OBJECT_CHECK(MouseState, (obj), TYPE_ADB_MOUSE) + typedef struct MouseState { + /*< public >*/ + ADBDevice parent_obj; + /*< private >*/ + int buttons_state, last_buttons_state; int dx, dy, dz; } MouseState; +#define ADB_MOUSE_CLASS(class) \ + OBJECT_CLASS_CHECK(ADBMouseClass, (class), TYPE_ADB_MOUSE) +#define ADB_MOUSE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ADBMouseClass, (obj), TYPE_ADB_MOUSE) + +typedef struct ADBMouseClass { + /*< public >*/ + ADBDeviceClass parent_class; + /*< private >*/ + + DeviceRealize parent_realize; +} ADBMouseClass; + static void adb_mouse_event(void *opaque, int dx1, int dy1, int dz1, int buttons_state) { - ADBDevice *d = opaque; - MouseState *s = d->opaque; + MouseState *s = opaque; s->dx += dx1; s->dy += dy1; @@ -320,7 +407,7 @@ static void adb_mouse_event(void *opaque, static int adb_mouse_poll(ADBDevice *d, uint8_t *obuf) { - MouseState *s = d->opaque; + MouseState *s = ADB_MOUSE(d); int dx, dy; if (s->last_buttons_state == s->buttons_state && @@ -359,7 +446,7 @@ static int adb_mouse_poll(ADBDevice *d, uint8_t *obuf) static int adb_mouse_request(ADBDevice *d, uint8_t *obuf, const uint8_t *buf, int len) { - MouseState *s = d->opaque; + MouseState *s = ADB_MOUSE(d); int cmd, reg, olen; if ((buf[0] & 0x0f) == ADB_FLUSH) { @@ -416,15 +503,15 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf, return olen; } -static int adb_mouse_reset(ADBDevice *d) +static void adb_mouse_reset(DeviceState *dev) { - MouseState *s = d->opaque; + ADBDevice *d = ADB_DEVICE(dev); + MouseState *s = ADB_MOUSE(dev); d->handler = 2; - d->devaddr = ADB_MOUSE; - memset(s, 0, sizeof(MouseState)); - - return 0; + d->devaddr = ADB_DEVID_MOUSE; + s->last_buttons_state = s->buttons_state = 0; + s->dx = s->dy = s->dz = 0; } static const VMStateDescription vmstate_adb_mouse = { @@ -442,14 +529,53 @@ static const VMStateDescription vmstate_adb_mouse = { } }; -void adb_mouse_init(ADBBusState *bus) +static void adb_mouse_realizefn(DeviceState *dev, Error **errp) { - ADBDevice *d; - MouseState *s; + MouseState *s = ADB_MOUSE(dev); + ADBMouseClass *amc = ADB_MOUSE_GET_CLASS(dev); + + amc->parent_realize(dev, errp); + + qemu_add_mouse_event_handler(adb_mouse_event, s, 0, "QEMU ADB Mouse"); +} + +static void adb_mouse_initfn(Object *obj) +{ + ADBDevice *d = ADB_DEVICE(obj); + + d->devaddr = ADB_DEVID_MOUSE; +} + +static void adb_mouse_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + ADBDeviceClass *adc = ADB_DEVICE_CLASS(oc); + ADBMouseClass *amc = ADB_MOUSE_CLASS(oc); + + amc->parent_realize = dc->realize; + dc->realize = adb_mouse_realizefn; - s = g_malloc0(sizeof(MouseState)); - d = adb_register_device(bus, ADB_MOUSE, adb_mouse_request, - adb_mouse_reset, s); - qemu_add_mouse_event_handler(adb_mouse_event, d, 0, "QEMU ADB Mouse"); - vmstate_register(NULL, -1, &vmstate_adb_mouse, s); + adc->devreq = adb_mouse_request; + dc->reset = adb_mouse_reset; + dc->vmsd = &vmstate_adb_mouse; } + +static const TypeInfo adb_mouse_type_info = { + .name = TYPE_ADB_MOUSE, + .parent = TYPE_ADB_DEVICE, + .instance_size = sizeof(MouseState), + .instance_init = adb_mouse_initfn, + .class_init = adb_mouse_class_init, + .class_size = sizeof(ADBMouseClass), +}; + + +static void adb_register_types(void) +{ + type_register_static(&adb_bus_type_info); + type_register_static(&adb_device_type_info); + type_register_static(&adb_kbd_type_info); + type_register_static(&adb_mouse_type_info); +} + +type_init(adb_register_types) @@ -26,38 +26,62 @@ #if !defined(__ADB_H__) #define __ADB_H__ +#include "qdev.h" + #define MAX_ADB_DEVICES 16 #define ADB_MAX_OUT_LEN 16 +typedef struct ADBBusState ADBBusState; typedef struct ADBDevice ADBDevice; /* buf = NULL means polling */ typedef int ADBDeviceRequest(ADBDevice *d, uint8_t *buf_out, const uint8_t *buf, int len); -typedef int ADBDeviceReset(ADBDevice *d); + +#define TYPE_ADB_DEVICE "adb-device" +#define ADB_DEVICE(obj) OBJECT_CHECK(ADBDevice, (obj), TYPE_ADB_DEVICE) struct ADBDevice { - struct ADBBusState *bus; + /*< private >*/ + DeviceState parent_obj; + /*< public >*/ + int devaddr; int handler; - ADBDeviceRequest *devreq; - ADBDeviceReset *devreset; - void *opaque; }; -typedef struct ADBBusState { - ADBDevice devices[MAX_ADB_DEVICES]; +#define ADB_DEVICE_CLASS(cls) \ + OBJECT_CLASS_CHECK(ADBDeviceClass, (cls), TYPE_ADB_DEVICE) +#define ADB_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ADBDeviceClass, (obj), TYPE_ADB_DEVICE) + +typedef struct ADBDeviceClass { + /*< private >*/ + DeviceClass parent_class; + /*< public >*/ + + ADBDeviceRequest *devreq; +} ADBDeviceClass; + +#define TYPE_ADB_BUS "apple-desktop-bus" +#define ADB_BUS(obj) OBJECT_CHECK(ADBBusState, (obj), TYPE_ADB_BUS) + +struct ADBBusState { + /*< private >*/ + BusState parent_obj; + /*< public >*/ + + ADBDevice *devices[MAX_ADB_DEVICES]; int nb_devices; int poll_index; -} ADBBusState; +}; int adb_request(ADBBusState *s, uint8_t *buf_out, const uint8_t *buf, int len); int adb_poll(ADBBusState *s, uint8_t *buf_out); -void adb_kbd_init(ADBBusState *bus); -void adb_mouse_init(ADBBusState *bus); +#define TYPE_ADB_KEYBOARD "adb-keyboard" +#define TYPE_ADB_MOUSE "adb-mouse" -extern ADBBusState adb_bus; #endif /* !defined(__ADB_H__) */ diff --git a/hw/arm_boot.c b/hw/arm_boot.c index 115f583876..4065424d60 100644 --- a/hw/arm_boot.c +++ b/hw/arm_boot.c @@ -441,9 +441,12 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) * we point to the kernel args. */ if (info->dtb_filename) { - /* Place the DTB after the initrd in memory */ - hwaddr dtb_start = TARGET_PAGE_ALIGN(info->initrd_start + - initrd_size); + /* Place the DTB after the initrd in memory. Note that some + * kernels will trash anything in the 4K page the initrd + * ends in, so make sure the DTB isn't caught up in that. + */ + hwaddr dtb_start = QEMU_ALIGN_UP(info->initrd_start + initrd_size, + 4096); if (load_dtb(dtb_start, info)) { exit(1); } diff --git a/hw/arm_sysctl.c b/hw/arm_sysctl.c index 755a5df2c9..7ecb7da54b 100644 --- a/hw/arm_sysctl.c +++ b/hw/arm_sysctl.c @@ -199,6 +199,7 @@ static void arm_sysctl_write(void *opaque, hwaddr offset, switch (offset) { case 0x08: /* LED */ s->leds = val; + break; case 0x0c: /* OSC0 */ case 0x10: /* OSC1 */ case 0x14: /* OSC2 */ @@ -295,6 +296,7 @@ static void arm_sysctl_write(void *opaque, hwaddr offset, /* On VExpress this register is unimplemented and will RAZ/WI */ break; } + break; case 0x54: /* CLCDSER */ case 0x64: /* DMAPSR0 */ case 0x68: /* DMAPSR1 */ @@ -332,6 +334,7 @@ static void arm_sysctl_write(void *opaque, hwaddr offset, default: s->sys_cfgstat |= 2; /* error */ } + s->sys_cfgctrl &= ~(1 << 31); return; case 0xa8: /* SYS_CFGSTAT */ if (board_id(s) != BOARD_ID_VEXPRESS) { diff --git a/hw/block-common.c b/hw/block-common.c index 0f1b64ec95..d21ec3ada1 100644 --- a/hw/block-common.c +++ b/hw/block-common.c @@ -18,9 +18,7 @@ void blkconf_serial(BlockConf *conf, char **serial) if (!*serial) { /* try to fall back to value set with legacy -drive serial=... */ dinfo = drive_get_by_blockdev(conf->bs); - if (dinfo->serial) { - *serial = g_strdup(dinfo->serial); - } + *serial = g_strdup(dinfo->serial); } } diff --git a/hw/boards.h b/hw/boards.h index 3ff9665b1f..3813d4e551 100644 --- a/hw/boards.h +++ b/hw/boards.h @@ -33,6 +33,7 @@ typedef struct QEMUMachine { unsigned int no_serial:1, no_parallel:1, use_virtcon:1, + use_sclp:1, no_floppy:1, no_cdrom:1, no_sdcard:1; diff --git a/hw/cadence_gem.c b/hw/cadence_gem.c index 0d834422df..ab86c1702d 100644 --- a/hw/cadence_gem.c +++ b/hw/cadence_gem.c @@ -389,10 +389,10 @@ static void gem_init_register_masks(GemState *s) */ static void phy_update_link(GemState *s) { - DB_PRINT("down %d\n", s->nic->nc.link_down); + DB_PRINT("down %d\n", qemu_get_queue(s->nic)->link_down); /* Autonegotiation status mirrors link status. */ - if (s->nic->nc.link_down) { + if (qemu_get_queue(s->nic)->link_down) { s->phy_regs[PHY_REG_STATUS] &= ~(PHY_REG_STATUS_ANEGCMPL | PHY_REG_STATUS_LINK); s->phy_regs[PHY_REG_INT_ST] |= PHY_REG_INT_ST_LINKC; @@ -409,7 +409,7 @@ static int gem_can_receive(NetClientState *nc) { GemState *s; - s = DO_UPCAST(NICState, nc, nc)->opaque; + s = qemu_get_nic_opaque(nc); DB_PRINT("\n"); @@ -612,7 +612,7 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) uint8_t rxbuf[2048]; uint8_t *rxbuf_ptr; - s = DO_UPCAST(NICState, nc, nc)->opaque; + s = qemu_get_nic_opaque(nc); /* Do nothing if receive is not enabled. */ if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_RXENA)) { @@ -687,14 +687,15 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) packet_desc_addr = s->rx_desc_addr; while (1) { - DB_PRINT("read descriptor 0x%x\n", packet_desc_addr); + DB_PRINT("read descriptor 0x%x\n", (unsigned)packet_desc_addr); /* read current descriptor */ cpu_physical_memory_read(packet_desc_addr, (uint8_t *)&desc[0], sizeof(desc)); /* Descriptor owned by software ? */ if (rx_desc_get_ownership(desc) == 1) { - DB_PRINT("descriptor 0x%x owned by sw.\n", packet_desc_addr); + DB_PRINT("descriptor 0x%x owned by sw.\n", + (unsigned)packet_desc_addr); s->regs[GEM_RXSTATUS] |= GEM_RXSTATUS_NOBUF; /* Handle interrupt consequences */ gem_update_int_status(s); @@ -709,7 +710,7 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) */ if (rx_desc_get_buffer(desc) == 0) { DB_PRINT("Invalid RX buffer (NULL) for descriptor 0x%x\n", - packet_desc_addr); + (unsigned)packet_desc_addr); break; } @@ -749,7 +750,7 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) s->rx_desc_addr += 8; } - DB_PRINT("set SOF, OWN on descriptor 0x%08x\n", packet_desc_addr); + DB_PRINT("set SOF, OWN on descriptor 0x%08x\n", (unsigned)packet_desc_addr); /* Count it */ gem_receive_updatestats(s, buf, size); @@ -861,7 +862,8 @@ static void gem_transmit(GemState *s) */ if ((tx_desc_get_buffer(desc) == 0) || (tx_desc_get_length(desc) == 0)) { - DB_PRINT("Invalid TX descriptor @ 0x%x\n", packet_desc_addr); + DB_PRINT("Invalid TX descriptor @ 0x%x\n", + (unsigned)packet_desc_addr); break; } @@ -906,9 +908,10 @@ static void gem_transmit(GemState *s) /* Send the packet somewhere */ if (s->phy_loop) { - gem_receive(&s->nic->nc, tx_packet, total_bytes); + gem_receive(qemu_get_queue(s->nic), tx_packet, total_bytes); } else { - qemu_send_packet(&s->nic->nc, tx_packet, total_bytes); + qemu_send_packet(qemu_get_queue(s->nic), tx_packet, + total_bytes); } /* Prepare for next packet */ @@ -1031,10 +1034,11 @@ static uint64_t gem_read(void *opaque, hwaddr offset, unsigned size) offset >>= 2; retval = s->regs[offset]; - DB_PRINT("offset: 0x%04x read: 0x%08x\n", offset*4, retval); + DB_PRINT("offset: 0x%04x read: 0x%08x\n", (unsigned)offset*4, retval); switch (offset) { case GEM_ISR: + DB_PRINT("lowering irq on ISR read\n"); qemu_set_irq(s->irq, 0); break; case GEM_PHYMNTNC: @@ -1073,7 +1077,7 @@ static void gem_write(void *opaque, hwaddr offset, uint64_t val, GemState *s = (GemState *)opaque; uint32_t readonly; - DB_PRINT("offset: 0x%04x write: 0x%08x ", offset, (unsigned)val); + DB_PRINT("offset: 0x%04x write: 0x%08x ", (unsigned)offset, (unsigned)val); offset >>= 2; /* Squash bits which are read only in write value */ @@ -1148,7 +1152,7 @@ static const MemoryRegionOps gem_ops = { static void gem_cleanup(NetClientState *nc) { - GemState *s = DO_UPCAST(NICState, nc, nc)->opaque; + GemState *s = qemu_get_nic_opaque(nc); DB_PRINT("\n"); s->nic = NULL; @@ -1157,7 +1161,7 @@ static void gem_cleanup(NetClientState *nc) static void gem_set_link(NetClientState *nc) { DB_PRINT("\n"); - phy_update_link(DO_UPCAST(NICState, nc, nc)->opaque); + phy_update_link(qemu_get_nic_opaque(nc)); } static NetClientInfo net_gem_info = { diff --git a/hw/cadence_ttc.c b/hw/cadence_ttc.c index 2a8fadd810..67028a3f75 100644 --- a/hw/cadence_ttc.c +++ b/hw/cadence_ttc.c @@ -302,7 +302,7 @@ static uint64_t cadence_ttc_read(void *opaque, hwaddr offset, { uint32_t ret = cadence_ttc_read_imp(opaque, offset); - DB_PRINT("addr: %08x data: %08x\n", offset, ret); + DB_PRINT("addr: %08x data: %08x\n", (unsigned)offset, (unsigned)ret); return ret; } @@ -311,7 +311,7 @@ static void cadence_ttc_write(void *opaque, hwaddr offset, { CadenceTimerState *s = cadence_timer_from_addr(opaque, offset); - DB_PRINT("addr: %08x data %08x\n", offset, (unsigned)value); + DB_PRINT("addr: %08x data %08x\n", (unsigned)offset, (unsigned)value); cadence_timer_sync(s); @@ -23,7 +23,7 @@ * THE SOFTWARE. */ #include "hw.h" -#include "ppc_mac.h" +#include "ppc/mac.h" #include "adb.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" @@ -108,50 +108,6 @@ /* CUDA returns time_t's offset from Jan 1, 1904, not 1970 */ #define RTC_OFFSET 2082844800 -typedef struct CUDATimer { - int index; - uint16_t latch; - uint16_t counter_value; /* counter value at load time */ - int64_t load_time; - int64_t next_irq_time; - QEMUTimer *timer; -} CUDATimer; - -typedef struct CUDAState { - MemoryRegion mem; - /* cuda registers */ - uint8_t b; /* B-side data */ - uint8_t a; /* A-side data */ - uint8_t dirb; /* B-side direction (1=output) */ - uint8_t dira; /* A-side direction (1=output) */ - uint8_t sr; /* Shift register */ - uint8_t acr; /* Auxiliary control register */ - uint8_t pcr; /* Peripheral control register */ - uint8_t ifr; /* Interrupt flag register */ - uint8_t ier; /* Interrupt enable register */ - uint8_t anh; /* A-side data, no handshake */ - - CUDATimer timers[2]; - - uint32_t tick_offset; - - uint8_t last_b; /* last value of B register */ - uint8_t last_acr; /* last value of B register */ - - int data_in_size; - int data_in_index; - int data_out_index; - - qemu_irq irq; - uint8_t autopoll; - uint8_t data_in[128]; - uint8_t data_out[16]; - QEMUTimer *adb_poll_timer; -} CUDAState; - -static CUDAState cuda_state; -ADBBusState adb_bus; - static void cuda_update(CUDAState *s); static void cuda_receive_packet_from_host(CUDAState *s, const uint8_t *data, int len); @@ -501,7 +457,7 @@ static void cuda_adb_poll(void *opaque) uint8_t obuf[ADB_MAX_OUT_LEN + 2]; int olen; - olen = adb_poll(&adb_bus, obuf + 2); + olen = adb_poll(&s->adb_bus, obuf + 2); if (olen > 0) { obuf[0] = ADB_PACKET; obuf[1] = 0x40; /* polled data */ @@ -597,7 +553,7 @@ static void cuda_receive_packet_from_host(CUDAState *s, { uint8_t obuf[ADB_MAX_OUT_LEN + 2]; int olen; - olen = adb_request(&adb_bus, obuf + 2, data + 1, len - 1); + olen = adb_request(&s->adb_bus, obuf + 2, data + 1, len - 1); if (olen > 0) { obuf[0] = ADB_PACKET; obuf[1] = 0x00; @@ -701,9 +657,9 @@ static const VMStateDescription vmstate_cuda = { } }; -static void cuda_reset(void *opaque) +static void cuda_reset(DeviceState *dev) { - CUDAState *s = opaque; + CUDAState *s = CUDA(dev); s->b = 0; s->a = 0; @@ -728,25 +684,57 @@ static void cuda_reset(void *opaque) set_counter(s, &s->timers[1], 0xffff); } -void cuda_init (MemoryRegion **cuda_mem, qemu_irq irq) +static void cuda_realizefn(DeviceState *dev, Error **errp) { + CUDAState *s = CUDA(dev); struct tm tm; - CUDAState *s = &cuda_state; - - s->irq = irq; - s->timers[0].index = 0; s->timers[0].timer = qemu_new_timer_ns(vm_clock, cuda_timer1, s); - s->timers[1].index = 1; - qemu_get_timedate(&tm, 0); s->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET; s->adb_poll_timer = qemu_new_timer_ns(vm_clock, cuda_adb_poll, s); +} + +static void cuda_initfn(Object *obj) +{ + SysBusDevice *d = SYS_BUS_DEVICE(obj); + CUDAState *s = CUDA(obj); + int i; + memory_region_init_io(&s->mem, &cuda_ops, s, "cuda", 0x2000); + sysbus_init_mmio(d, &s->mem); + sysbus_init_irq(d, &s->irq); + + for (i = 0; i < ARRAY_SIZE(s->timers); i++) { + s->timers[i].index = i; + } - *cuda_mem = &s->mem; - vmstate_register(NULL, -1, &vmstate_cuda, s); - qemu_register_reset(cuda_reset, s); + qbus_create_inplace((BusState *)&s->adb_bus, TYPE_ADB_BUS, DEVICE(obj), + "adb.0"); } + +static void cuda_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = cuda_realizefn; + dc->reset = cuda_reset; + dc->vmsd = &vmstate_cuda; +} + +static const TypeInfo cuda_type_info = { + .name = TYPE_CUDA, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(CUDAState), + .instance_init = cuda_initfn, + .class_init = cuda_class_init, +}; + +static void cuda_register_types(void) +{ + type_register_static(&cuda_type_info); +} + +type_init(cuda_register_types) diff --git a/hw/dp8393x.c b/hw/dp8393x.c index b5014501df..808157b38b 100644 --- a/hw/dp8393x.c +++ b/hw/dp8393x.c @@ -339,6 +339,7 @@ static void do_receiver_disable(dp8393xState *s) static void do_transmit_packets(dp8393xState *s) { + NetClientState *nc = qemu_get_queue(s->nic); uint16_t data[12]; int width, size; int tx_len, len; @@ -408,13 +409,13 @@ static void do_transmit_packets(dp8393xState *s) if (s->regs[SONIC_RCR] & (SONIC_RCR_LB1 | SONIC_RCR_LB0)) { /* Loopback */ s->regs[SONIC_TCR] |= SONIC_TCR_CRSL; - if (s->nic->nc.info->can_receive(&s->nic->nc)) { + if (nc->info->can_receive(nc)) { s->loopback_packet = 1; - s->nic->nc.info->receive(&s->nic->nc, s->tx_buffer, tx_len); + nc->info->receive(nc, s->tx_buffer, tx_len); } } else { /* Transmit packet */ - qemu_send_packet(&s->nic->nc, s->tx_buffer, tx_len); + qemu_send_packet(nc, s->tx_buffer, tx_len); } s->regs[SONIC_TCR] |= SONIC_TCR_PTX; @@ -675,7 +676,7 @@ static const MemoryRegionOps dp8393x_ops = { static int nic_can_receive(NetClientState *nc) { - dp8393xState *s = DO_UPCAST(NICState, nc, nc)->opaque; + dp8393xState *s = qemu_get_nic_opaque(nc); if (!(s->regs[SONIC_CR] & SONIC_CR_RXEN)) return 0; @@ -724,7 +725,7 @@ static int receive_filter(dp8393xState *s, const uint8_t * buf, int size) static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size) { - dp8393xState *s = DO_UPCAST(NICState, nc, nc)->opaque; + dp8393xState *s = qemu_get_nic_opaque(nc); uint16_t data[10]; int packet_type; uint32_t available, address; @@ -860,7 +861,7 @@ static void nic_reset(void *opaque) static void nic_cleanup(NetClientState *nc) { - dp8393xState *s = DO_UPCAST(NICState, nc, nc)->opaque; + dp8393xState *s = qemu_get_nic_opaque(nc); memory_region_del_subregion(s->address_space, &s->mmio); memory_region_destroy(&s->mmio); @@ -899,11 +900,11 @@ void dp83932_init(NICInfo *nd, hwaddr base, int it_shift, s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */ s->conf.macaddr = nd->macaddr; - s->conf.peer = nd->netdev; + s->conf.peers.ncs[0] = nd->netdev; s->nic = qemu_new_nic(&net_dp83932_info, &s->conf, nd->model, nd->name, s); - qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); qemu_register_reset(nic_reset, s); nic_reset(s); diff --git a/hw/ds1338.c b/hw/ds1338.c index 379220638e..6f70538eb3 100644 --- a/hw/ds1338.c +++ b/hw/ds1338.c @@ -198,7 +198,7 @@ static int ds1338_init(I2CSlave *i2c) static void ds1338_reset(DeviceState *dev) { - DS1338State *s = FROM_I2C_SLAVE(DS1338State, I2C_SLAVE_FROM_QDEV(dev)); + DS1338State *s = FROM_I2C_SLAVE(DS1338State, I2C_SLAVE(dev)); /* The clock is running and synchronized with the host */ s->offset = 0; diff --git a/hw/e1000.c b/hw/e1000.c index ef06ca1894..d6fe815eda 100644 --- a/hw/e1000.c +++ b/hw/e1000.c @@ -166,12 +166,6 @@ static void set_phy_ctrl(E1000State *s, int index, uint16_t val) { if ((val & MII_CR_AUTO_NEG_EN) && (val & MII_CR_RESTART_AUTO_NEG)) { - /* no need auto-negotiation if link was down */ - if (s->nic->nc.link_down) { - s->phy_reg[PHY_STATUS] |= MII_SR_AUTONEG_COMPLETE; - return; - } - s->nic->nc.link_down = true; e1000_link_down(s); s->phy_reg[PHY_STATUS] &= ~MII_SR_AUTONEG_COMPLETE; DBGOUT(PHY, "Start link auto negotiation\n"); @@ -183,8 +177,9 @@ static void e1000_autoneg_timer(void *opaque) { E1000State *s = opaque; - s->nic->nc.link_down = false; - e1000_link_up(s); + if (!qemu_get_queue(s->nic)->link_down) { + e1000_link_up(s); + } s->phy_reg[PHY_STATUS] |= MII_SR_AUTONEG_COMPLETE; DBGOUT(PHY, "Auto negotiation is completed\n"); } @@ -237,7 +232,17 @@ set_interrupt_cause(E1000State *s, int index, uint32_t val) val |= E1000_ICR_INT_ASSERTED; } s->mac_reg[ICR] = val; + + /* + * Make sure ICR and ICS registers have the same value. + * The spec says that the ICS register is write-only. However in practice, + * on real hardware ICS is readable, and for reads it has the same value as + * ICR (except that ICS does not have the clear on read behaviour of ICR). + * + * The VxWorks PRO/1000 driver uses this behaviour. + */ s->mac_reg[ICS] = val; + qemu_set_irq(s->dev.irq[0], (s->mac_reg[IMS] & s->mac_reg[ICR]) != 0); } @@ -286,7 +291,7 @@ static void e1000_reset(void *opaque) d->rxbuf_min_shift = 1; memset(&d->tx, 0, sizeof d->tx); - if (d->nic->nc.link_down) { + if (qemu_get_queue(d->nic)->link_down) { e1000_link_down(d); } @@ -314,7 +319,7 @@ set_rx_control(E1000State *s, int index, uint32_t val) s->rxbuf_min_shift = ((val / E1000_RCTL_RDMTS_QUAT) & 3) + 1; DBGOUT(RX, "RCTL: %d, mac_reg[RCTL] = 0x%x\n", s->mac_reg[RDT], s->mac_reg[RCTL]); - qemu_flush_queued_packets(&s->nic->nc); + qemu_flush_queued_packets(qemu_get_queue(s->nic)); } static void @@ -465,10 +470,11 @@ fcs_len(E1000State *s) static void e1000_send_packet(E1000State *s, const uint8_t *buf, int size) { + NetClientState *nc = qemu_get_queue(s->nic); if (s->phy_reg[PHY_CTRL] & MII_CR_LOOPBACK) { - s->nic->nc.info->receive(&s->nic->nc, buf, size); + nc->info->receive(nc, buf, size); } else { - qemu_send_packet(&s->nic->nc, buf, size); + qemu_send_packet(nc, buf, size); } } @@ -742,7 +748,7 @@ receive_filter(E1000State *s, const uint8_t *buf, int size) static void e1000_set_link_status(NetClientState *nc) { - E1000State *s = DO_UPCAST(NICState, nc, nc)->opaque; + E1000State *s = qemu_get_nic_opaque(nc); uint32_t old_status = s->mac_reg[STATUS]; if (nc->link_down) { @@ -776,9 +782,10 @@ static bool e1000_has_rxbufs(E1000State *s, size_t total_size) static int e1000_can_receive(NetClientState *nc) { - E1000State *s = DO_UPCAST(NICState, nc, nc)->opaque; + E1000State *s = qemu_get_nic_opaque(nc); - return (s->mac_reg[RCTL] & E1000_RCTL_EN) && e1000_has_rxbufs(s, 1); + return (s->mac_reg[STATUS] & E1000_STATUS_LU) && + (s->mac_reg[RCTL] & E1000_RCTL_EN) && e1000_has_rxbufs(s, 1); } static uint64_t rx_desc_base(E1000State *s) @@ -792,7 +799,7 @@ static uint64_t rx_desc_base(E1000State *s) static ssize_t e1000_receive(NetClientState *nc, const uint8_t *buf, size_t size) { - E1000State *s = DO_UPCAST(NICState, nc, nc)->opaque; + E1000State *s = qemu_get_nic_opaque(nc); struct e1000_rx_desc desc; dma_addr_t base; unsigned int n, rdt; @@ -804,8 +811,13 @@ e1000_receive(NetClientState *nc, const uint8_t *buf, size_t size) size_t desc_size; size_t total_size; - if (!(s->mac_reg[RCTL] & E1000_RCTL_EN)) + if (!(s->mac_reg[STATUS] & E1000_STATUS_LU)) { + return -1; + } + + if (!(s->mac_reg[RCTL] & E1000_RCTL_EN)) { return -1; + } /* Pad to minimum Ethernet frame length */ if (size < sizeof(min_buf)) { @@ -953,7 +965,7 @@ set_rdt(E1000State *s, int index, uint32_t val) { s->mac_reg[index] = val & 0xffff; if (e1000_has_rxbufs(s, 1)) { - qemu_flush_queued_packets(&s->nic->nc); + qemu_flush_queued_packets(qemu_get_queue(s->nic)); } } @@ -1104,13 +1116,37 @@ static bool is_version_1(void *opaque, int version_id) return version_id == 1; } +static void e1000_pre_save(void *opaque) +{ + E1000State *s = opaque; + NetClientState *nc = qemu_get_queue(s->nic); + /* + * If link is down and auto-negotiation is ongoing, complete + * auto-negotiation immediately. This allows is to look at + * MII_SR_AUTONEG_COMPLETE to infer link status on load. + */ + if (nc->link_down && + s->phy_reg[PHY_CTRL] & MII_CR_AUTO_NEG_EN && + s->phy_reg[PHY_CTRL] & MII_CR_RESTART_AUTO_NEG) { + s->phy_reg[PHY_STATUS] |= MII_SR_AUTONEG_COMPLETE; + } +} + static int e1000_post_load(void *opaque, int version_id) { E1000State *s = opaque; + NetClientState *nc = qemu_get_queue(s->nic); /* nc.link_down can't be migrated, so infer link_down according - * to link status bit in mac_reg[STATUS] */ - s->nic->nc.link_down = (s->mac_reg[STATUS] & E1000_STATUS_LU) == 0; + * to link status bit in mac_reg[STATUS]. + * Alternatively, restart link negotiation if it was in progress. */ + nc->link_down = (s->mac_reg[STATUS] & E1000_STATUS_LU) == 0; + if (s->phy_reg[PHY_CTRL] & MII_CR_AUTO_NEG_EN && + s->phy_reg[PHY_CTRL] & MII_CR_RESTART_AUTO_NEG && + !(s->phy_reg[PHY_STATUS] & MII_SR_AUTONEG_COMPLETE)) { + nc->link_down = false; + qemu_mod_timer(s->autoneg_timer, qemu_get_clock_ms(vm_clock) + 500); + } return 0; } @@ -1120,6 +1156,7 @@ static const VMStateDescription vmstate_e1000 = { .version_id = 2, .minimum_version_id = 1, .minimum_version_id_old = 1, + .pre_save = e1000_pre_save, .post_load = e1000_post_load, .fields = (VMStateField []) { VMSTATE_PCI_DEVICE(dev, E1000State), @@ -1228,7 +1265,7 @@ e1000_mmio_setup(E1000State *d) static void e1000_cleanup(NetClientState *nc) { - E1000State *s = DO_UPCAST(NICState, nc, nc)->opaque; + E1000State *s = qemu_get_nic_opaque(nc); s->nic = NULL; } @@ -1242,7 +1279,7 @@ pci_e1000_uninit(PCIDevice *dev) qemu_free_timer(d->autoneg_timer); memory_region_destroy(&d->mmio); memory_region_destroy(&d->io); - qemu_del_net_client(&d->nic->nc); + qemu_del_nic(d->nic); } static NetClientInfo net_e1000_info = { @@ -1289,7 +1326,7 @@ static int pci_e1000_init(PCIDevice *pci_dev) d->nic = qemu_new_nic(&net_e1000_info, &d->conf, object_get_typename(OBJECT(d)), d->dev.qdev.id, d); - qemu_format_nic_info_str(&d->nic->nc, macaddr); + qemu_format_nic_info_str(qemu_get_queue(d->nic), macaddr); add_boot_device_path(d->conf.bootindex, &pci_dev->qdev, "/ethernet-phy@0"); diff --git a/hw/eepro100.c b/hw/eepro100.c index 6bbefb505f..5d237968e7 100644 --- a/hw/eepro100.c +++ b/hw/eepro100.c @@ -828,7 +828,7 @@ static void tx_command(EEPRO100State *s) } } TRACE(RXTX, logout("%p sending frame, len=%d,%s\n", s, size, nic_dump(buf, size))); - qemu_send_packet(&s->nic->nc, buf, size); + qemu_send_packet(qemu_get_queue(s->nic), buf, size); s->statistics.tx_good_frames++; /* Transmit with bad status would raise an CX/TNO interrupt. * (82557 only). Emulation never has bad status. */ @@ -1036,7 +1036,7 @@ static void eepro100_ru_command(EEPRO100State * s, uint8_t val) } set_ru_state(s, ru_ready); s->ru_offset = e100_read_reg4(s, SCBPointer); - qemu_flush_queued_packets(&s->nic->nc); + qemu_flush_queued_packets(qemu_get_queue(s->nic)); TRACE(OTHER, logout("val=0x%02x (rx start)\n", val)); break; case RX_RESUME: @@ -1619,7 +1619,7 @@ static const MemoryRegionOps eepro100_ops = { static int nic_can_receive(NetClientState *nc) { - EEPRO100State *s = DO_UPCAST(NICState, nc, nc)->opaque; + EEPRO100State *s = qemu_get_nic_opaque(nc); TRACE(RXTX, logout("%p\n", s)); return get_ru_state(s) == ru_ready; #if 0 @@ -1633,7 +1633,7 @@ static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size) * - Magic packets should set bit 30 in power management driver register. * - Interesting packets should set bit 29 in power management driver register. */ - EEPRO100State *s = DO_UPCAST(NICState, nc, nc)->opaque; + EEPRO100State *s = qemu_get_nic_opaque(nc); uint16_t rfd_status = 0xa000; #if defined(CONFIG_PAD_RECEIVED_FRAMES) uint8_t min_buf[60]; @@ -1835,7 +1835,7 @@ static const VMStateDescription vmstate_eepro100 = { static void nic_cleanup(NetClientState *nc) { - EEPRO100State *s = DO_UPCAST(NICState, nc, nc)->opaque; + EEPRO100State *s = qemu_get_nic_opaque(nc); s->nic = NULL; } @@ -1849,7 +1849,7 @@ static void pci_nic_uninit(PCIDevice *pci_dev) memory_region_destroy(&s->flash_bar); vmstate_unregister(&pci_dev->qdev, s->vmstate, s); eeprom93xx_free(&pci_dev->qdev, s->eeprom); - qemu_del_net_client(&s->nic->nc); + qemu_del_nic(s->nic); } static NetClientInfo net_eepro100_info = { @@ -1895,14 +1895,14 @@ static int e100_nic_init(PCIDevice *pci_dev) s->nic = qemu_new_nic(&net_eepro100_info, &s->conf, object_get_typename(OBJECT(pci_dev)), pci_dev->qdev.id, s); - qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); - TRACE(OTHER, logout("%s\n", s->nic->nc.info_str)); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); + TRACE(OTHER, logout("%s\n", qemu_get_queue(s->nic)->info_str)); qemu_register_reset(nic_reset, s); s->vmstate = g_malloc(sizeof(vmstate_eepro100)); memcpy(s->vmstate, &vmstate_eepro100, sizeof(vmstate_eepro100)); - s->vmstate->name = s->nic->nc.model; + s->vmstate->name = qemu_get_queue(s->nic)->model; vmstate_register(&pci_dev->qdev, -1, s->vmstate, s); add_boot_device_path(s->conf.bootindex, &pci_dev->qdev, "/ethernet-phy@0"); diff --git a/hw/etraxfs_eth.c b/hw/etraxfs_eth.c index 0b474c0843..ad36411193 100644 --- a/hw/etraxfs_eth.c +++ b/hw/etraxfs_eth.c @@ -523,7 +523,7 @@ static int eth_can_receive(NetClientState *nc) static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size) { unsigned char sa_bcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - struct fs_eth *eth = DO_UPCAST(NICState, nc, nc)->opaque; + struct fs_eth *eth = qemu_get_nic_opaque(nc); int use_ma0 = eth->regs[RW_REC_CTRL] & 1; int use_ma1 = eth->regs[RW_REC_CTRL] & 2; int r_bcast = eth->regs[RW_REC_CTRL] & 8; @@ -555,13 +555,13 @@ static int eth_tx_push(void *opaque, unsigned char *buf, int len, bool eop) struct fs_eth *eth = opaque; D(printf("%s buf=%p len=%d\n", __func__, buf, len)); - qemu_send_packet(ð->nic->nc, buf, len); + qemu_send_packet(qemu_get_queue(eth->nic), buf, len); return len; } static void eth_set_link(NetClientState *nc) { - struct fs_eth *eth = DO_UPCAST(NICState, nc, nc)->opaque; + struct fs_eth *eth = qemu_get_nic_opaque(nc); D(printf("%s %d\n", __func__, nc->link_down)); eth->phy.link = !nc->link_down; } @@ -578,7 +578,7 @@ static const MemoryRegionOps eth_ops = { static void eth_cleanup(NetClientState *nc) { - struct fs_eth *eth = DO_UPCAST(NICState, nc, nc)->opaque; + struct fs_eth *eth = qemu_get_nic_opaque(nc); /* Disconnect the client. */ eth->dma_out->client.push = NULL; @@ -616,7 +616,8 @@ static int fs_eth_init(SysBusDevice *dev) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_etraxfs_info, &s->conf, object_get_typename(OBJECT(s)), dev->qdev.id, s); - qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); + tdk_init(&s->phy); mdio_attach(&s->mdio_bus, &s->phy, s->phyaddr); diff --git a/hw/fw_cfg.c b/hw/fw_cfg.c index e4dc7c3c31..02618f2480 100644 --- a/hw/fw_cfg.c +++ b/hw/fw_cfg.c @@ -54,16 +54,17 @@ struct FWCfgState { #define JPG_FILE 0 #define BMP_FILE 1 -static char *read_splashfile(char *filename, int *file_sizep, int *file_typep) +static char *read_splashfile(char *filename, size_t *file_sizep, + int *file_typep) { GError *err = NULL; gboolean res; gchar *content; - int file_type = -1; - unsigned int filehead = 0; + int file_type; + unsigned int filehead; int bmp_bpp; - res = g_file_get_contents(filename, &content, (gsize *)file_sizep, &err); + res = g_file_get_contents(filename, &content, file_sizep, &err); if (res == FALSE) { error_report("failed to read splash file '%s'", filename); g_error_free(err); @@ -111,8 +112,8 @@ static void fw_cfg_bootsplash(FWCfgState *s) const char *boot_splash_filename = NULL; char *p; char *filename, *file_data; - int file_size; - int file_type = -1; + size_t file_size; + int file_type; const char *temp; /* get user configuration */ @@ -503,7 +504,6 @@ FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port, fw_cfg_add_bytes(s, FW_CFG_UUID, qemu_uuid, 16); fw_cfg_add_i16(s, FW_CFG_NOGRAPHIC, (uint16_t)(display_type == DT_NOGRAPHIC)); fw_cfg_add_i16(s, FW_CFG_NB_CPUS, (uint16_t)smp_cpus); - fw_cfg_add_i16(s, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); fw_cfg_add_i16(s, FW_CFG_BOOT_MENU, (uint16_t)boot_menu); fw_cfg_bootsplash(s); fw_cfg_reboot(s); diff --git a/hw/grackle_pci.c b/hw/grackle_pci.c index 948416632a..95639d5735 100644 --- a/hw/grackle_pci.c +++ b/hw/grackle_pci.c @@ -24,7 +24,7 @@ */ #include "pci/pci_host.h" -#include "ppc_mac.h" +#include "ppc/mac.h" #include "pci/pci.h" /* debug Grackle */ diff --git a/hw/heathrow_pic.c b/hw/heathrow_pic.c index b9ec8e7b4d..c0a71c3d5f 100644 --- a/hw/heathrow_pic.c +++ b/hw/heathrow_pic.c @@ -23,7 +23,7 @@ * THE SOFTWARE. */ #include "hw.h" -#include "ppc_mac.h" +#include "ppc/mac.h" /* debug PIC */ //#define DEBUG_PIC @@ -92,7 +92,7 @@ int i2c_start_transfer(i2c_bus *bus, uint8_t address, int recv) QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) { DeviceState *qdev = kid->child; - I2CSlave *candidate = I2C_SLAVE_FROM_QDEV(qdev); + I2CSlave *candidate = I2C_SLAVE(qdev); if (candidate->address == address) { slave = candidate; break; @@ -204,7 +204,7 @@ const VMStateDescription vmstate_i2c_slave = { static int i2c_slave_qdev_init(DeviceState *dev) { - I2CSlave *s = I2C_SLAVE_FROM_QDEV(dev); + I2CSlave *s = I2C_SLAVE(dev); I2CSlaveClass *sc = I2C_SLAVE_GET_CLASS(s); return sc->init(s); @@ -59,7 +59,6 @@ void i2c_nack(i2c_bus *bus); int i2c_send(i2c_bus *bus, uint8_t data); int i2c_recv(i2c_bus *bus); -#define I2C_SLAVE_FROM_QDEV(dev) DO_UPCAST(I2CSlave, qdev, dev) #define FROM_I2C_SLAVE(type, dev) DO_UPCAST(type, i2c, dev) DeviceState *i2c_create_slave(i2c_bus *bus, const char *name, uint8_t addr); @@ -18,6 +18,7 @@ void ich9_lpc_set_irq(void *opaque, int irq_num, int level); int ich9_lpc_map_irq(PCIDevice *pci_dev, int intx); +PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin); void ich9_lpc_pm_init(PCIDevice *pci_lpc, qemu_irq cmos_s3); PCIBus *ich9_d2pbr_init(PCIBus *bus, int devfn, int sec_bus); i2c_bus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base); @@ -19,15 +19,8 @@ PCIDevice *pci_piix3_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); PCIDevice *pci_piix4_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); void vt82c686b_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); -/* ide-macio.c */ -MemoryRegion *pmac_ide_init (DriveInfo **hd_table, qemu_irq irq, - void *dbdma, int channel, qemu_irq dma_irq); - /* ide-mmio.c */ -void mmio_ide_init (hwaddr membase, hwaddr membase2, - MemoryRegion *address_space, - qemu_irq irq, int shift, - DriveInfo *hd0, DriveInfo *hd1); +void mmio_ide_init_drives(DeviceState *dev, DriveInfo *hd0, DriveInfo *hd1); int ide_get_geometry(BusState *bus, int unit, int16_t *cyls, int8_t *heads, int8_t *secs); diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 21f50ea5be..ad0094f532 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -241,7 +241,7 @@ static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val) if ((pr->cmd & PORT_CMD_FIS_ON) && !s->dev[port].init_d2h_sent) { ahci_init_d2h(&s->dev[port]); - s->dev[port].init_d2h_sent = 1; + s->dev[port].init_d2h_sent = true; } check_cmd(s, port); @@ -494,7 +494,7 @@ static void ahci_reset_port(AHCIState *s, int port) pr->scr_err = 0; pr->scr_act = 0; d->busy_slot = -1; - d->init_d2h_sent = 0; + d->init_d2h_sent = false; ide_state = &s->dev[port].port.ifs[0]; if (!ide_state->bs) { @@ -946,7 +946,7 @@ static int handle_cmd(AHCIState *s, int port, int slot) ide_state->hcyl = 0xeb; debug_print_fis(ide_state->io_buffer, 0x10); ide_state->feature = IDE_FEATURE_DMA; - s->dev[port].done_atapi_packet = 0; + s->dev[port].done_atapi_packet = false; /* XXX send PIO setup FIS */ } @@ -991,7 +991,7 @@ static int ahci_start_transfer(IDEDMA *dma) if (is_atapi && !ad->done_atapi_packet) { /* already prepopulated iobuffer */ - ad->done_atapi_packet = 1; + ad->done_atapi_packet = true; goto out; } @@ -1035,11 +1035,10 @@ out: static void ahci_start_dma(IDEDMA *dma, IDEState *s, BlockDriverCompletionFunc *dma_cb) { +#ifdef DEBUG_AHCI AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); - +#endif DPRINTF(ad->port_no, "\n"); - ad->dma_cb = dma_cb; - ad->dma_status |= BM_STATUS_DMAING; s->io_buffer_offset = 0; dma_cb(s, 0); } @@ -1095,7 +1094,6 @@ static int ahci_dma_set_unit(IDEDMA *dma, int unit) static int ahci_dma_add_status(IDEDMA *dma, int status) { AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); - ad->dma_status |= status; DPRINTF(ad->port_no, "set status: %x\n", status); if (status & BM_STATUS_INT) { @@ -1114,8 +1112,6 @@ static int ahci_dma_set_inactive(IDEDMA *dma) /* update d2h status */ ahci_write_fis_d2h(ad, NULL); - ad->dma_cb = NULL; - if (!ad->check_bh) { /* maybe we still have something to process, check later */ ad->check_bh = qemu_bh_new(ahci_check_cmd_bh, ad); @@ -1203,6 +1199,82 @@ void ahci_reset(AHCIState *s) } } +static const VMStateDescription vmstate_ahci_device = { + .name = "ahci port", + .version_id = 1, + .fields = (VMStateField []) { + VMSTATE_IDE_BUS(port, AHCIDevice), + VMSTATE_UINT32(port_state, AHCIDevice), + VMSTATE_UINT32(finished, AHCIDevice), + VMSTATE_UINT32(port_regs.lst_addr, AHCIDevice), + VMSTATE_UINT32(port_regs.lst_addr_hi, AHCIDevice), + VMSTATE_UINT32(port_regs.fis_addr, AHCIDevice), + VMSTATE_UINT32(port_regs.fis_addr_hi, AHCIDevice), + VMSTATE_UINT32(port_regs.irq_stat, AHCIDevice), + VMSTATE_UINT32(port_regs.irq_mask, AHCIDevice), + VMSTATE_UINT32(port_regs.cmd, AHCIDevice), + VMSTATE_UINT32(port_regs.tfdata, AHCIDevice), + VMSTATE_UINT32(port_regs.sig, AHCIDevice), + VMSTATE_UINT32(port_regs.scr_stat, AHCIDevice), + VMSTATE_UINT32(port_regs.scr_ctl, AHCIDevice), + VMSTATE_UINT32(port_regs.scr_err, AHCIDevice), + VMSTATE_UINT32(port_regs.scr_act, AHCIDevice), + VMSTATE_UINT32(port_regs.cmd_issue, AHCIDevice), + VMSTATE_BOOL(done_atapi_packet, AHCIDevice), + VMSTATE_INT32(busy_slot, AHCIDevice), + VMSTATE_BOOL(init_d2h_sent, AHCIDevice), + VMSTATE_END_OF_LIST() + }, +}; + +static int ahci_state_post_load(void *opaque, int version_id) +{ + int i; + struct AHCIDevice *ad; + AHCIState *s = opaque; + + for (i = 0; i < s->ports; i++) { + ad = &s->dev[i]; + AHCIPortRegs *pr = &ad->port_regs; + + map_page(&ad->lst, + ((uint64_t)pr->lst_addr_hi << 32) | pr->lst_addr, 1024); + map_page(&ad->res_fis, + ((uint64_t)pr->fis_addr_hi << 32) | pr->fis_addr, 256); + /* + * All pending i/o should be flushed out on a migrate. However, + * we might not have cleared the busy_slot since this is done + * in a bh. Also, issue i/o against any slots that are pending. + */ + if ((ad->busy_slot != -1) && + !(ad->port.ifs[0].status & (BUSY_STAT|DRQ_STAT))) { + pr->cmd_issue &= ~(1 << ad->busy_slot); + ad->busy_slot = -1; + } + check_cmd(s, i); + } + + return 0; +} + +const VMStateDescription vmstate_ahci = { + .name = "ahci", + .version_id = 1, + .post_load = ahci_state_post_load, + .fields = (VMStateField []) { + VMSTATE_STRUCT_VARRAY_POINTER_INT32(dev, AHCIState, ports, + vmstate_ahci_device, AHCIDevice), + VMSTATE_UINT32(control_regs.cap, AHCIState), + VMSTATE_UINT32(control_regs.ghc, AHCIState), + VMSTATE_UINT32(control_regs.irqstatus, AHCIState), + VMSTATE_UINT32(control_regs.impl, AHCIState), + VMSTATE_UINT32(control_regs.version, AHCIState), + VMSTATE_UINT32(idp_index, AHCIState), + VMSTATE_INT32(ports, AHCIState), + VMSTATE_END_OF_LIST() + }, +}; + typedef struct SysbusAHCIState { SysBusDevice busdev; AHCIState ahci; @@ -1211,7 +1283,11 @@ typedef struct SysbusAHCIState { static const VMStateDescription vmstate_sysbus_ahci = { .name = "sysbus-ahci", - .unmigratable = 1, + .unmigratable = 1, /* Still buggy under I/O load */ + .fields = (VMStateField []) { + VMSTATE_AHCI(ahci, AHCIPCIState), + VMSTATE_END_OF_LIST() + }, }; static void sysbus_ahci_reset(DeviceState *dev) diff --git a/hw/ide/ahci.h b/hw/ide/ahci.h index 1200a56ada..85f37fe99d 100644 --- a/hw/ide/ahci.h +++ b/hw/ide/ahci.h @@ -281,11 +281,9 @@ struct AHCIDevice { QEMUBH *check_bh; uint8_t *lst; uint8_t *res_fis; - int dma_status; - int done_atapi_packet; - int busy_slot; - int init_d2h_sent; - BlockDriverCompletionFunc *dma_cb; + bool done_atapi_packet; + int32_t busy_slot; + bool init_d2h_sent; AHCICmdHdr *cur_cmd; NCQTransferState ncq_tfs[AHCI_MAX_CMDS]; }; @@ -297,7 +295,7 @@ typedef struct AHCIState { MemoryRegion idp; /* Index-Data Pair I/O port space */ unsigned idp_offset; /* Offset of index in I/O port space */ uint32_t idp_index; /* Current IDP index */ - int ports; + int32_t ports; qemu_irq irq; DMAContext *dma; } AHCIState; @@ -307,6 +305,16 @@ typedef struct AHCIPCIState { AHCIState ahci; } AHCIPCIState; +extern const VMStateDescription vmstate_ahci; + +#define VMSTATE_AHCI(_field, _state) { \ + .name = (stringify(_field)), \ + .size = sizeof(AHCIState), \ + .vmsd = &vmstate_ahci, \ + .flags = VMS_STRUCT, \ + .offset = vmstate_offset_value(_state, _field, AHCIState), \ +} + typedef struct NCQFrame { uint8_t fis_type; uint8_t c; diff --git a/hw/ide/core.c b/hw/ide/core.c index 14ad0799c3..3743dc3b55 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -1149,8 +1149,10 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) } ide_set_irq(s->bus); break; + case WIN_VERIFY_EXT: - lba48 = 1; + lba48 = 1; + /* fall through */ case WIN_VERIFY: case WIN_VERIFY_ONCE: /* do sector number check ? */ @@ -1158,8 +1160,10 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) s->status = READY_STAT | SEEK_STAT; ide_set_irq(s->bus); break; + case WIN_READ_EXT: - lba48 = 1; + lba48 = 1; + /* fall through */ case WIN_READ: case WIN_READ_ONCE: if (s->drive_kind == IDE_CD) { @@ -1173,8 +1177,10 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) s->req_nb_sectors = 1; ide_sector_read(s); break; + case WIN_WRITE_EXT: - lba48 = 1; + lba48 = 1; + /* fall through */ case WIN_WRITE: case WIN_WRITE_ONCE: case CFA_WRITE_SECT_WO_ERASE: @@ -1189,8 +1195,10 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) ide_transfer_start(s, s->io_buffer, 512, ide_sector_write); s->media_changed = 1; break; + case WIN_MULTREAD_EXT: - lba48 = 1; + lba48 = 1; + /* fall through */ case WIN_MULTREAD: if (!s->bs) { goto abort_cmd; @@ -1202,8 +1210,10 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) s->req_nb_sectors = s->mult_sectors; ide_sector_read(s); break; + case WIN_MULTWRITE_EXT: - lba48 = 1; + lba48 = 1; + /* fall through */ case WIN_MULTWRITE: case CFA_WRITE_MULTI_WO_ERASE: if (!s->bs) { @@ -1222,8 +1232,10 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) ide_transfer_start(s, s->io_buffer, 512 * n, ide_sector_write); s->media_changed = 1; break; + case WIN_READDMA_EXT: - lba48 = 1; + lba48 = 1; + /* fall through */ case WIN_READDMA: case WIN_READDMA_ONCE: if (!s->bs) { @@ -1232,8 +1244,10 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) ide_cmd_lba48_transform(s, lba48); ide_sector_start_dma(s, IDE_DMA_READ); break; + case WIN_WRITEDMA_EXT: - lba48 = 1; + lba48 = 1; + /* fall through */ case WIN_WRITEDMA: case WIN_WRITEDMA_ONCE: if (!s->bs) { @@ -1243,14 +1257,17 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) ide_sector_start_dma(s, IDE_DMA_WRITE); s->media_changed = 1; break; + case WIN_READ_NATIVE_MAX_EXT: - lba48 = 1; + lba48 = 1; + /* fall through */ case WIN_READ_NATIVE_MAX: ide_cmd_lba48_transform(s, lba48); ide_set_sector(s, s->nb_sectors - 1); s->status = READY_STAT | SEEK_STAT; ide_set_irq(s->bus); break; + case WIN_CHECKPOWERMODE1: case WIN_CHECKPOWERMODE2: s->error = 0; diff --git a/hw/ide/ich.c b/hw/ide/ich.c index 1fb803d340..cc30adc701 100644 --- a/hw/ide/ich.c +++ b/hw/ide/ich.c @@ -79,9 +79,15 @@ #define ICH9_IDP_INDEX 0x10 #define ICH9_IDP_INDEX_LOG2 0x04 -static const VMStateDescription vmstate_ahci = { - .name = "ahci", - .unmigratable = 1, +static const VMStateDescription vmstate_ich9_ahci = { + .name = "ich9_ahci", + .unmigratable = 1, /* Still buggy under I/O load */ + .version_id = 1, + .fields = (VMStateField []) { + VMSTATE_PCI_DEVICE(card, AHCIPCIState), + VMSTATE_AHCI(ahci, AHCIPCIState), + VMSTATE_END_OF_LIST() + }, }; static void pci_ich9_reset(DeviceState *dev) @@ -152,7 +158,7 @@ static void ich_ahci_class_init(ObjectClass *klass, void *data) k->device_id = PCI_DEVICE_ID_INTEL_82801IR; k->revision = 0x02; k->class_id = PCI_CLASS_STORAGE_SATA; - dc->vmsd = &vmstate_ahci; + dc->vmsd = &vmstate_ich9_ahci; dc->reset = pci_ich9_reset; } diff --git a/hw/ide/macio.c b/hw/ide/macio.c index d8f9b4bce1..375c46f9da 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include <hw/hw.h> -#include <hw/ppc_mac.h> -#include <hw/mac_dbdma.h> +#include "hw/hw.h" +#include "hw/ppc/mac.h" +#include "hw/mac_dbdma.h" #include "block/block.h" #include "sysemu/dma.h" @@ -33,12 +33,6 @@ /***********************************************************/ /* MacIO based PowerPC IDE */ -typedef struct MACIOIDEState { - MemoryRegion mem; - IDEBus bus; - BlockDriverAIOCB *aiocb; -} MACIOIDEState; - #define MACIO_PAGE_SIZE 4096 static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) @@ -321,30 +315,70 @@ static const VMStateDescription vmstate_pmac = { } }; -static void pmac_ide_reset(void *opaque) +static void macio_ide_reset(DeviceState *dev) { - MACIOIDEState *d = opaque; + MACIOIDEState *d = MACIO_IDE(dev); ide_bus_reset(&d->bus); } -/* hd_table must contain 4 block drivers */ -/* PowerMac uses memory mapped registers, not I/O. Return the memory - I/O index to access the ide. */ -MemoryRegion *pmac_ide_init (DriveInfo **hd_table, qemu_irq irq, - void *dbdma, int channel, qemu_irq dma_irq) +static void macio_ide_realizefn(DeviceState *dev, Error **errp) { - MACIOIDEState *d; + MACIOIDEState *s = MACIO_IDE(dev); + + ide_init2(&s->bus, s->irq); +} + +static void macio_ide_initfn(Object *obj) +{ + SysBusDevice *d = SYS_BUS_DEVICE(obj); + MACIOIDEState *s = MACIO_IDE(obj); + + ide_bus_new(&s->bus, DEVICE(obj), 0); + memory_region_init_io(&s->mem, &pmac_ide_ops, s, "pmac-ide", 0x1000); + sysbus_init_mmio(d, &s->mem); + sysbus_init_irq(d, &s->irq); + sysbus_init_irq(d, &s->dma_irq); +} + +static void macio_ide_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = macio_ide_realizefn; + dc->reset = macio_ide_reset; + dc->vmsd = &vmstate_pmac; +} - d = g_malloc0(sizeof(MACIOIDEState)); - ide_init2_with_non_qdev_drives(&d->bus, hd_table[0], hd_table[1], irq); +static const TypeInfo macio_ide_type_info = { + .name = TYPE_MACIO_IDE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MACIOIDEState), + .instance_init = macio_ide_initfn, + .class_init = macio_ide_class_init, +}; - if (dbdma) - DBDMA_register_channel(dbdma, channel, dma_irq, pmac_ide_transfer, pmac_ide_flush, d); +static void macio_ide_register_types(void) +{ + type_register_static(&macio_ide_type_info); +} - memory_region_init_io(&d->mem, &pmac_ide_ops, d, "pmac-ide", 0x1000); - vmstate_register(NULL, 0, &vmstate_pmac, d); - qemu_register_reset(pmac_ide_reset, d); +/* hd_table must contain 4 block drivers */ +void macio_ide_init_drives(MACIOIDEState *s, DriveInfo **hd_table) +{ + int i; - return &d->mem; + for (i = 0; i < 2; i++) { + if (hd_table[i]) { + ide_create_drive(&s->bus, i, hd_table[i]); + } + } } + +void macio_ide_register_dma(MACIOIDEState *s, void *dbdma, int channel) +{ + DBDMA_register_channel(dbdma, channel, s->dma_irq, + pmac_ide_transfer, pmac_ide_flush, s); +} + +type_init(macio_ide_register_types) diff --git a/hw/ide/mmio.c b/hw/ide/mmio.c index eb59976eda..ce88c3a651 100644 --- a/hw/ide/mmio.c +++ b/hw/ide/mmio.c @@ -22,7 +22,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include <hw/hw.h> +#include "hw/hw.h" +#include "hw/sysbus.h" #include "block/block.h" #include "sysemu/dma.h" @@ -34,15 +35,24 @@ * dedicated ide controller, which is often seen on embedded boards. */ -typedef struct { +#define TYPE_MMIO_IDE "mmio-ide" +#define MMIO_IDE(obj) OBJECT_CHECK(MMIOState, (obj), TYPE_MMIO_IDE) + +typedef struct MMIOIDEState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + IDEBus bus; - int shift; + + uint32_t shift; + qemu_irq irq; MemoryRegion iomem1, iomem2; } MMIOState; -static void mmio_ide_reset(void *opaque) +static void mmio_ide_reset(DeviceState *dev) { - MMIOState *s = opaque; + MMIOState *s = MMIO_IDE(dev); ide_bus_reset(&s->bus); } @@ -107,24 +117,68 @@ static const VMStateDescription vmstate_ide_mmio = { } }; -void mmio_ide_init (hwaddr membase, hwaddr membase2, - MemoryRegion *address_space, - qemu_irq irq, int shift, - DriveInfo *hd0, DriveInfo *hd1) +static void mmio_ide_realizefn(DeviceState *dev, Error **errp) { - MMIOState *s = g_malloc0(sizeof(MMIOState)); + SysBusDevice *d = SYS_BUS_DEVICE(dev); + MMIOState *s = MMIO_IDE(dev); - ide_init2_with_non_qdev_drives(&s->bus, hd0, hd1, irq); - - s->shift = shift; + ide_init2(&s->bus, s->irq); memory_region_init_io(&s->iomem1, &mmio_ide_ops, s, - "ide-mmio.1", 16 << shift); + "ide-mmio.1", 16 << s->shift); memory_region_init_io(&s->iomem2, &mmio_ide_cs_ops, s, - "ide-mmio.2", 2 << shift); - memory_region_add_subregion(address_space, membase, &s->iomem1); - memory_region_add_subregion(address_space, membase2, &s->iomem2); - vmstate_register(NULL, 0, &vmstate_ide_mmio, s); - qemu_register_reset(mmio_ide_reset, s); + "ide-mmio.2", 2 << s->shift); + sysbus_init_mmio(d, &s->iomem1); + sysbus_init_mmio(d, &s->iomem2); +} + +static void mmio_ide_initfn(Object *obj) +{ + SysBusDevice *d = SYS_BUS_DEVICE(obj); + MMIOState *s = MMIO_IDE(obj); + + ide_bus_new(&s->bus, DEVICE(obj), 0); + sysbus_init_irq(d, &s->irq); +} + +static Property mmio_ide_properties[] = { + DEFINE_PROP_UINT32("shift", MMIOState, shift, 0), + DEFINE_PROP_END_OF_LIST() +}; + +static void mmio_ide_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = mmio_ide_realizefn; + dc->reset = mmio_ide_reset; + dc->props = mmio_ide_properties; + dc->vmsd = &vmstate_ide_mmio; +} + +static const TypeInfo mmio_ide_type_info = { + .name = TYPE_MMIO_IDE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MMIOState), + .instance_init = mmio_ide_initfn, + .class_init = mmio_ide_class_init, +}; + +static void mmio_ide_register_types(void) +{ + type_register_static(&mmio_ide_type_info); +} + +void mmio_ide_init_drives(DeviceState *dev, DriveInfo *hd0, DriveInfo *hd1) +{ + MMIOState *s = MMIO_IDE(dev); + + if (hd0 != NULL) { + ide_create_drive(&s->bus, 0, hd0); + } + if (hd1 != NULL) { + ide_create_drive(&s->bus, 1, hd1); + } } +type_init(mmio_ide_register_types) @@ -82,7 +82,7 @@ void isa_register_portio_list(ISADevice *dev, uint16_t start, static inline ISABus *isa_bus_from_device(ISADevice *d) { - return DO_UPCAST(ISABus, qbus, d->qdev.parent_bus); + return ISA_BUS(qdev_get_parent_bus(DEVICE(d))); } extern hwaddr isa_mem_base; diff --git a/hw/lan9118.c b/hw/lan9118.c index 6596979d8b..0e844e535c 100644 --- a/hw/lan9118.c +++ b/hw/lan9118.c @@ -341,7 +341,7 @@ static void lan9118_update(lan9118_state *s) static void lan9118_mac_changed(lan9118_state *s) { - qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); } static void lan9118_reload_eeprom(lan9118_state *s) @@ -373,7 +373,7 @@ static void phy_update_irq(lan9118_state *s) static void phy_update_link(lan9118_state *s) { /* Autonegotiation status mirrors link status. */ - if (s->nic->nc.link_down) { + if (qemu_get_queue(s->nic)->link_down) { s->phy_status &= ~0x0024; s->phy_int |= PHY_INT_DOWN; } else { @@ -386,7 +386,7 @@ static void phy_update_link(lan9118_state *s) static void lan9118_set_link(NetClientState *nc) { - phy_update_link(DO_UPCAST(NICState, nc, nc)->opaque); + phy_update_link(qemu_get_nic_opaque(nc)); } static void phy_reset(lan9118_state *s) @@ -512,7 +512,7 @@ static int lan9118_filter(lan9118_state *s, const uint8_t *addr) static ssize_t lan9118_receive(NetClientState *nc, const uint8_t *buf, size_t size) { - lan9118_state *s = DO_UPCAST(NICState, nc, nc)->opaque; + lan9118_state *s = qemu_get_nic_opaque(nc); int fifo_len; int offset; int src_pos; @@ -657,9 +657,9 @@ static void do_tx_packet(lan9118_state *s) /* FIXME: Honor TX disable, and allow queueing of packets. */ if (s->phy_control & 0x4000) { /* This assumes the receive routine doesn't touch the VLANClient. */ - lan9118_receive(&s->nic->nc, s->txp->data, s->txp->len); + lan9118_receive(qemu_get_queue(s->nic), s->txp->data, s->txp->len); } else { - qemu_send_packet(&s->nic->nc, s->txp->data, s->txp->len); + qemu_send_packet(qemu_get_queue(s->nic), s->txp->data, s->txp->len); } s->txp->fifo_used = 0; @@ -1306,7 +1306,7 @@ static const MemoryRegionOps lan9118_16bit_mem_ops = { static void lan9118_cleanup(NetClientState *nc) { - lan9118_state *s = DO_UPCAST(NICState, nc, nc)->opaque; + lan9118_state *s = qemu_get_nic_opaque(nc); s->nic = NULL; } @@ -1335,7 +1335,7 @@ static int lan9118_init1(SysBusDevice *dev) s->nic = qemu_new_nic(&net_lan9118_info, &s->conf, object_get_typename(OBJECT(dev)), dev->qdev.id, s); - qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); s->eeprom[0] = 0xa5; for (i = 0; i < 6; i++) { s->eeprom[i + 1] = s->conf.macaddr.a[i]; diff --git a/hw/lance.c b/hw/lance.c index a5997fd64e..4b92425299 100644 --- a/hw/lance.c +++ b/hw/lance.c @@ -87,7 +87,7 @@ static const MemoryRegionOps lance_mem_ops = { static void lance_cleanup(NetClientState *nc) { - PCNetState *d = DO_UPCAST(NICState, nc, nc)->opaque; + PCNetState *d = qemu_get_nic_opaque(nc); pcnet_common_cleanup(d); } diff --git a/hw/lm832x.c b/hw/lm832x.c index af49dd68bf..94b8ae06d8 100644 --- a/hw/lm832x.c +++ b/hw/lm832x.c @@ -476,7 +476,7 @@ static int lm8323_init(I2CSlave *i2c) void lm832x_key_event(DeviceState *dev, int key, int state) { - LM823KbdState *s = FROM_I2C_SLAVE(LM823KbdState, I2C_SLAVE_FROM_QDEV(dev)); + LM823KbdState *s = FROM_I2C_SLAVE(LM823KbdState, I2C_SLAVE(dev)); if ((s->status & INT_ERROR) && (s->error & ERR_FIFOOVR)) return; diff --git a/hw/lpc_ich9.c b/hw/lpc_ich9.c index 16843d76bc..e25689bf87 100644 --- a/hw/lpc_ich9.c +++ b/hw/lpc_ich9.c @@ -158,6 +158,7 @@ static void ich9_cc_write(void *opaque, hwaddr addr, ich9_cc_addr_len(&addr, &len); memcpy(lpc->chip_config + addr, &val, len); + pci_bus_fire_intx_routing_notifier(lpc->d.bus); ich9_cc_update(lpc); } @@ -286,6 +287,32 @@ int ich9_lpc_map_irq(PCIDevice *pci_dev, int intx) return lpc->irr[PCI_SLOT(pci_dev->devfn)][intx]; } +PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin) +{ + ICH9LPCState *lpc = opaque; + PCIINTxRoute route; + int pic_irq; + int pic_dis; + + assert(0 <= pirq_pin); + assert(pirq_pin < ICH9_LPC_NB_PIRQS); + + route.mode = PCI_INTX_ENABLED; + ich9_lpc_pic_irq(lpc, pirq_pin, &pic_irq, &pic_dis); + if (!pic_dis) { + if (pic_irq < ICH9_LPC_PIC_NUM_PINS) { + route.irq = pic_irq; + } else { + route.mode = PCI_INTX_DISABLED; + route.irq = -1; + } + } else { + route.irq = ich9_pirq_to_gsi(pirq_pin); + } + + return route; +} + static int ich9_lpc_sci_irq(ICH9LPCState *lpc) { switch (lpc->d.config[ICH9_LPC_ACPI_CTRL] & @@ -405,6 +432,12 @@ static void ich9_lpc_config_write(PCIDevice *d, if (ranges_overlap(addr, len, ICH9_LPC_RCBA, 4)) { ich9_lpc_rcba_update(lpc, rbca_old); } + if (ranges_overlap(addr, len, ICH9_LPC_PIRQA_ROUT, 4)) { + pci_bus_fire_intx_routing_notifier(lpc->d.bus); + } + if (ranges_overlap(addr, len, ICH9_LPC_PIRQE_ROUT, 4)) { + pci_bus_fire_intx_routing_notifier(lpc->d.bus); + } } static void ich9_lpc_reset(DeviceState *qdev) diff --git a/hw/m25p80.c b/hw/m25p80.c index d39265632b..788c19608c 100644 --- a/hw/m25p80.c +++ b/hw/m25p80.c @@ -358,6 +358,8 @@ static void complete_collecting_data(Flash *s) s->cur_addr |= s->data[1] << 8; s->cur_addr |= s->data[2]; + s->state = STATE_IDLE; + switch (s->cmd_in_progress) { case DPP: case QPP: diff --git a/hw/mac_nvram.c b/hw/mac_nvram.c index 71093c2b10..25121fa482 100644 --- a/hw/mac_nvram.c +++ b/hw/mac_nvram.c @@ -25,7 +25,7 @@ #include "hw.h" #include "firmware_abi.h" #include "sysemu/sysemu.h" -#include "ppc_mac.h" +#include "ppc/mac.h" /* debug NVR */ //#define DEBUG_NVR @@ -37,37 +37,29 @@ #define NVR_DPRINTF(fmt, ...) #endif -struct MacIONVRAMState { - uint32_t size; - MemoryRegion mem; - unsigned int it_shift; - uint8_t *data; -}; - #define DEF_SYSTEM_SIZE 0xc10 /* Direct access to NVRAM */ -uint32_t macio_nvram_read (void *opaque, uint32_t addr) +uint8_t macio_nvram_read(MacIONVRAMState *s, uint32_t addr) { - MacIONVRAMState *s = opaque; uint32_t ret; - if (addr < s->size) + if (addr < s->size) { ret = s->data[addr]; - else + } else { ret = -1; - NVR_DPRINTF("read addr %04x val %x\n", addr, ret); + } + NVR_DPRINTF("read addr %04" PRIx32 " val %" PRIx8 "\n", addr, ret); return ret; } -void macio_nvram_write (void *opaque, uint32_t addr, uint32_t val) +void macio_nvram_write(MacIONVRAMState *s, uint32_t addr, uint8_t val) { - MacIONVRAMState *s = opaque; - - NVR_DPRINTF("write addr %04x val %x\n", addr, val); - if (addr < s->size) + NVR_DPRINTF("write addr %04" PRIx32 " val %" PRIx8 "\n", addr, val); + if (addr < s->size) { s->data[addr] = val; + } } /* macio style NVRAM device */ @@ -78,7 +70,7 @@ static void macio_nvram_writeb(void *opaque, hwaddr addr, addr = (addr >> s->it_shift) & (s->size - 1); s->data[addr] = value; - NVR_DPRINTF("writeb addr %04x val %x\n", (int)addr, value); + NVR_DPRINTF("writeb addr %04" PHYS_PRIx " val %" PRIx64 "\n", addr, value); } static uint64_t macio_nvram_readb(void *opaque, hwaddr addr, @@ -97,7 +89,7 @@ static uint64_t macio_nvram_readb(void *opaque, hwaddr addr, static const MemoryRegionOps macio_nvram_ops = { .read = macio_nvram_readb, .write = macio_nvram_writeb, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_BIG_ENDIAN, }; static const VMStateDescription vmstate_macio_nvram = { @@ -112,32 +104,56 @@ static const VMStateDescription vmstate_macio_nvram = { }; -static void macio_nvram_reset(void *opaque) +static void macio_nvram_reset(DeviceState *dev) { } -MacIONVRAMState *macio_nvram_init (hwaddr size, - unsigned int it_shift) +static void macio_nvram_realizefn(DeviceState *dev, Error **errp) { - MacIONVRAMState *s; + SysBusDevice *d = SYS_BUS_DEVICE(dev); + MacIONVRAMState *s = MACIO_NVRAM(dev); - s = g_malloc0(sizeof(MacIONVRAMState)); - s->data = g_malloc0(size); - s->size = size; - s->it_shift = it_shift; + s->data = g_malloc0(s->size); memory_region_init_io(&s->mem, &macio_nvram_ops, s, "macio-nvram", - size << it_shift); - vmstate_register(NULL, -1, &vmstate_macio_nvram, s); - qemu_register_reset(macio_nvram_reset, s); + s->size << s->it_shift); + sysbus_init_mmio(d, &s->mem); +} + +static void macio_nvram_unrealizefn(DeviceState *dev, Error **errp) +{ + MacIONVRAMState *s = MACIO_NVRAM(dev); - return s; + g_free(s->data); } -void macio_nvram_setup_bar(MacIONVRAMState *s, MemoryRegion *bar, - hwaddr mem_base) +static Property macio_nvram_properties[] = { + DEFINE_PROP_UINT32("size", MacIONVRAMState, size, 0), + DEFINE_PROP_UINT32("it_shift", MacIONVRAMState, it_shift, 0), + DEFINE_PROP_END_OF_LIST() +}; + +static void macio_nvram_class_init(ObjectClass *oc, void *data) { - memory_region_add_subregion(bar, mem_base, &s->mem); + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = macio_nvram_realizefn; + dc->unrealize = macio_nvram_unrealizefn; + dc->reset = macio_nvram_reset; + dc->vmsd = &vmstate_macio_nvram; + dc->props = macio_nvram_properties; +} + +static const TypeInfo macio_nvram_type_info = { + .name = TYPE_MACIO_NVRAM, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MacIONVRAMState), + .class_init = macio_nvram_class_init, +}; + +static void macio_nvram_register_types(void) +{ + type_register_static(&macio_nvram_type_info); } /* Set up a system OpenBIOS NVRAM partition */ @@ -176,3 +192,5 @@ void pmac_format_nvram_partition (MacIONVRAMState *nvr, int len) end = len; OpenBIOS_finish_partition(part_header, end - start); } + +type_init(macio_nvram_register_types) diff --git a/hw/macio.c b/hw/macio.c index 675a71c051..74bdcd1039 100644 --- a/hw/macio.c +++ b/hw/macio.c @@ -23,118 +23,283 @@ * THE SOFTWARE. */ #include "hw.h" -#include "ppc_mac.h" +#include "ppc/mac.h" #include "pci/pci.h" +#include "mac_dbdma.h" #include "escc.h" +#define TYPE_MACIO "macio" +#define MACIO(obj) OBJECT_CHECK(MacIOState, (obj), TYPE_MACIO) + typedef struct MacIOState { + /*< private >*/ PCIDevice parent; - int is_oldworld; + /*< public >*/ + MemoryRegion bar; + CUDAState cuda; + void *dbdma; MemoryRegion *pic_mem; - MemoryRegion *dbdma_mem; - MemoryRegion *cuda_mem; MemoryRegion *escc_mem; - void *nvram; - int nb_ide; - MemoryRegion *ide_mem[4]; } MacIOState; +#define OLDWORLD_MACIO(obj) \ + OBJECT_CHECK(OldWorldMacIOState, (obj), TYPE_OLDWORLD_MACIO) + +typedef struct OldWorldMacIOState { + /*< private >*/ + MacIOState parent_obj; + /*< public >*/ + + qemu_irq irqs[3]; + + MacIONVRAMState nvram; + MACIOIDEState ide; +} OldWorldMacIOState; + +#define NEWWORLD_MACIO(obj) \ + OBJECT_CHECK(NewWorldMacIOState, (obj), TYPE_NEWWORLD_MACIO) + +typedef struct NewWorldMacIOState { + /*< private >*/ + MacIOState parent_obj; + /*< public >*/ + qemu_irq irqs[5]; + MACIOIDEState ide[2]; +} NewWorldMacIOState; + static void macio_bar_setup(MacIOState *macio_state) { - int i; MemoryRegion *bar = &macio_state->bar; - memory_region_init(bar, "macio", 0x80000); - if (macio_state->pic_mem) { - if (macio_state->is_oldworld) { - /* Heathrow PIC */ - memory_region_add_subregion(bar, 0x00000, macio_state->pic_mem); - } else { - /* OpenPIC */ - memory_region_add_subregion(bar, 0x40000, macio_state->pic_mem); - } - } - if (macio_state->dbdma_mem) { - memory_region_add_subregion(bar, 0x08000, macio_state->dbdma_mem); - } if (macio_state->escc_mem) { memory_region_add_subregion(bar, 0x13000, macio_state->escc_mem); } - if (macio_state->cuda_mem) { - memory_region_add_subregion(bar, 0x16000, macio_state->cuda_mem); +} + +static int macio_common_initfn(PCIDevice *d) +{ + MacIOState *s = MACIO(d); + SysBusDevice *sysbus_dev; + int ret; + + d->config[0x3d] = 0x01; // interrupt on pin 1 + + ret = qdev_init(DEVICE(&s->cuda)); + if (ret < 0) { + return ret; } - for (i = 0; i < macio_state->nb_ide; i++) { - if (macio_state->ide_mem[i]) { - memory_region_add_subregion(bar, 0x1f000 + (i * 0x1000), - macio_state->ide_mem[i]); - } + sysbus_dev = SYS_BUS_DEVICE(&s->cuda); + memory_region_add_subregion(&s->bar, 0x16000, + sysbus_mmio_get_region(sysbus_dev, 0)); + + macio_bar_setup(s); + pci_register_bar(d, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar); + + return 0; +} + +static int macio_oldworld_initfn(PCIDevice *d) +{ + MacIOState *s = MACIO(d); + OldWorldMacIOState *os = OLDWORLD_MACIO(d); + SysBusDevice *sysbus_dev; + int ret = macio_common_initfn(d); + if (ret < 0) { + return ret; + } + + sysbus_dev = SYS_BUS_DEVICE(&s->cuda); + sysbus_connect_irq(sysbus_dev, 0, os->irqs[0]); + + ret = qdev_init(DEVICE(&os->nvram)); + if (ret < 0) { + return ret; } - if (macio_state->nvram != NULL) - macio_nvram_setup_bar(macio_state->nvram, bar, 0x60000); + sysbus_dev = SYS_BUS_DEVICE(&os->nvram); + memory_region_add_subregion(&s->bar, 0x60000, + sysbus_mmio_get_region(sysbus_dev, 0)); + pmac_format_nvram_partition(&os->nvram, os->nvram.size); + + if (s->pic_mem) { + /* Heathrow PIC */ + memory_region_add_subregion(&s->bar, 0x00000, s->pic_mem); + } + + sysbus_dev = SYS_BUS_DEVICE(&os->ide); + sysbus_connect_irq(sysbus_dev, 0, os->irqs[1]); + sysbus_connect_irq(sysbus_dev, 1, os->irqs[2]); + macio_ide_register_dma(&os->ide, s->dbdma, 0x16); + ret = qdev_init(DEVICE(&os->ide)); + if (ret < 0) { + return ret; + } + + return 0; } -static int macio_initfn(PCIDevice *d) +static void macio_oldworld_init(Object *obj) { - d->config[0x3d] = 0x01; // interrupt on pin 1 + MacIOState *s = MACIO(obj); + OldWorldMacIOState *os = OLDWORLD_MACIO(obj); + DeviceState *dev; + + qdev_init_gpio_out(DEVICE(obj), os->irqs, ARRAY_SIZE(os->irqs)); + + object_initialize(&os->nvram, TYPE_MACIO_NVRAM); + dev = DEVICE(&os->nvram); + qdev_prop_set_uint32(dev, "size", 0x2000); + qdev_prop_set_uint32(dev, "it_shift", 4); + + object_initialize(&os->ide, TYPE_MACIO_IDE); + qdev_set_parent_bus(DEVICE(&os->ide), sysbus_get_default()); + memory_region_add_subregion(&s->bar, 0x1f000 + (1 * 0x1000), &os->ide.mem); + object_property_add_child(obj, "ide", OBJECT(&os->ide), NULL); +} + +static int macio_newworld_initfn(PCIDevice *d) +{ + MacIOState *s = MACIO(d); + NewWorldMacIOState *ns = NEWWORLD_MACIO(d); + SysBusDevice *sysbus_dev; + int ret = macio_common_initfn(d); + if (ret < 0) { + return ret; + } + + sysbus_dev = SYS_BUS_DEVICE(&s->cuda); + sysbus_connect_irq(sysbus_dev, 0, ns->irqs[0]); + + if (s->pic_mem) { + /* OpenPIC */ + memory_region_add_subregion(&s->bar, 0x40000, s->pic_mem); + } + + sysbus_dev = SYS_BUS_DEVICE(&ns->ide[0]); + sysbus_connect_irq(sysbus_dev, 0, ns->irqs[1]); + sysbus_connect_irq(sysbus_dev, 1, ns->irqs[2]); + macio_ide_register_dma(&ns->ide[0], s->dbdma, 0x16); + ret = qdev_init(DEVICE(&ns->ide[0])); + if (ret < 0) { + return ret; + } + + sysbus_dev = SYS_BUS_DEVICE(&ns->ide[1]); + sysbus_connect_irq(sysbus_dev, 0, ns->irqs[3]); + sysbus_connect_irq(sysbus_dev, 1, ns->irqs[4]); + macio_ide_register_dma(&ns->ide[0], s->dbdma, 0x1a); + ret = qdev_init(DEVICE(&ns->ide[1])); + if (ret < 0) { + return ret; + } + return 0; } +static void macio_newworld_init(Object *obj) +{ + MacIOState *s = MACIO(obj); + NewWorldMacIOState *ns = NEWWORLD_MACIO(obj); + int i; + gchar *name; + + qdev_init_gpio_out(DEVICE(obj), ns->irqs, ARRAY_SIZE(ns->irqs)); + + for (i = 0; i < 2; i++) { + object_initialize(&ns->ide[i], TYPE_MACIO_IDE); + qdev_set_parent_bus(DEVICE(&ns->ide[i]), sysbus_get_default()); + memory_region_add_subregion(&s->bar, 0x1f000 + ((i + 1) * 0x1000), + &ns->ide[i].mem); + name = g_strdup_printf("ide[%i]", i); + object_property_add_child(obj, name, OBJECT(&ns->ide[i]), NULL); + g_free(name); + } +} + +static void macio_instance_init(Object *obj) +{ + MacIOState *s = MACIO(obj); + MemoryRegion *dbdma_mem; + + memory_region_init(&s->bar, "macio", 0x80000); + + object_initialize(&s->cuda, TYPE_CUDA); + qdev_set_parent_bus(DEVICE(&s->cuda), sysbus_get_default()); + object_property_add_child(obj, "cuda", OBJECT(&s->cuda), NULL); + + s->dbdma = DBDMA_init(&dbdma_mem); + memory_region_add_subregion(&s->bar, 0x08000, dbdma_mem); +} + +static void macio_oldworld_class_init(ObjectClass *oc, void *data) +{ + PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc); + + pdc->init = macio_oldworld_initfn; + pdc->device_id = PCI_DEVICE_ID_APPLE_343S1201; +} + +static void macio_newworld_class_init(ObjectClass *oc, void *data) +{ + PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc); + + pdc->init = macio_newworld_initfn; + pdc->device_id = PCI_DEVICE_ID_APPLE_UNI_N_KEYL; +} + static void macio_class_init(ObjectClass *klass, void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = macio_initfn; k->vendor_id = PCI_VENDOR_ID_APPLE; k->class_id = PCI_CLASS_OTHERS << 8; } -static const TypeInfo macio_info = { - .name = "macio", +static const TypeInfo macio_oldworld_type_info = { + .name = TYPE_OLDWORLD_MACIO, + .parent = TYPE_MACIO, + .instance_size = sizeof(OldWorldMacIOState), + .instance_init = macio_oldworld_init, + .class_init = macio_oldworld_class_init, +}; + +static const TypeInfo macio_newworld_type_info = { + .name = TYPE_NEWWORLD_MACIO, + .parent = TYPE_MACIO, + .instance_size = sizeof(NewWorldMacIOState), + .instance_init = macio_newworld_init, + .class_init = macio_newworld_class_init, +}; + +static const TypeInfo macio_type_info = { + .name = TYPE_MACIO, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(MacIOState), + .instance_init = macio_instance_init, + .abstract = true, .class_init = macio_class_init, }; static void macio_register_types(void) { - type_register_static(&macio_info); + type_register_static(&macio_type_info); + type_register_static(&macio_oldworld_type_info); + type_register_static(&macio_newworld_type_info); } type_init(macio_register_types) -void macio_init (PCIBus *bus, int device_id, int is_oldworld, - MemoryRegion *pic_mem, MemoryRegion *dbdma_mem, - MemoryRegion *cuda_mem, void *nvram, - int nb_ide, MemoryRegion **ide_mem, - MemoryRegion *escc_mem) +void macio_init(PCIDevice *d, + MemoryRegion *pic_mem, + MemoryRegion *escc_mem) { - PCIDevice *d; - MacIOState *macio_state; - int i; + MacIOState *macio_state = MACIO(d); - d = pci_create_simple(bus, -1, "macio"); - - macio_state = DO_UPCAST(MacIOState, parent, d); - macio_state->is_oldworld = is_oldworld; macio_state->pic_mem = pic_mem; - macio_state->dbdma_mem = dbdma_mem; - macio_state->cuda_mem = cuda_mem; macio_state->escc_mem = escc_mem; - macio_state->nvram = nvram; - if (nb_ide > 4) - nb_ide = 4; - macio_state->nb_ide = nb_ide; - for (i = 0; i < nb_ide; i++) - macio_state->ide_mem[i] = ide_mem[i]; - for (; i < 4; i++) - macio_state->ide_mem[i] = NULL; /* Note: this code is strongly inspirated from the corresponding code in PearPC */ - pci_config_set_device_id(d->config, device_id); - - macio_bar_setup(macio_state); - pci_register_bar(d, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &macio_state->bar); + qdev_init_nofail(DEVICE(d)); } diff --git a/hw/max7310.c b/hw/max7310.c index de2221ba01..c2df0b49eb 100644 --- a/hw/max7310.c +++ b/hw/max7310.c @@ -25,7 +25,7 @@ typedef struct { static void max7310_reset(DeviceState *dev) { - MAX7310State *s = FROM_I2C_SLAVE(MAX7310State, I2C_SLAVE_FROM_QDEV(dev)); + MAX7310State *s = FROM_I2C_SLAVE(MAX7310State, I2C_SLAVE(dev)); s->level &= s->direction; s->direction = 0xff; s->polarity = 0xf0; diff --git a/hw/mcf_fec.c b/hw/mcf_fec.c index 2423f64bf6..8e60f09fbb 100644 --- a/hw/mcf_fec.c +++ b/hw/mcf_fec.c @@ -174,7 +174,7 @@ static void mcf_fec_do_tx(mcf_fec_state *s) if (bd.flags & FEC_BD_L) { /* Last buffer in frame. */ DPRINTF("Sending packet\n"); - qemu_send_packet(&s->nic->nc, frame, len); + qemu_send_packet(qemu_get_queue(s->nic), frame, len); ptr = frame; frame_size = 0; s->eir |= FEC_INT_TXF; @@ -353,13 +353,13 @@ static void mcf_fec_write(void *opaque, hwaddr addr, static int mcf_fec_can_receive(NetClientState *nc) { - mcf_fec_state *s = DO_UPCAST(NICState, nc, nc)->opaque; + mcf_fec_state *s = qemu_get_nic_opaque(nc); return s->rx_enabled; } static ssize_t mcf_fec_receive(NetClientState *nc, const uint8_t *buf, size_t size) { - mcf_fec_state *s = DO_UPCAST(NICState, nc, nc)->opaque; + mcf_fec_state *s = qemu_get_nic_opaque(nc); mcf_fec_bd bd; uint32_t flags = 0; uint32_t addr; @@ -441,7 +441,7 @@ static const MemoryRegionOps mcf_fec_ops = { static void mcf_fec_cleanup(NetClientState *nc) { - mcf_fec_state *s = DO_UPCAST(NICState, nc, nc)->opaque; + mcf_fec_state *s = qemu_get_nic_opaque(nc); memory_region_del_subregion(s->sysmem, &s->iomem); memory_region_destroy(&s->iomem); @@ -472,9 +472,9 @@ void mcf_fec_init(MemoryRegion *sysmem, NICInfo *nd, memory_region_add_subregion(sysmem, base, &s->iomem); s->conf.macaddr = nd->macaddr; - s->conf.peer = nd->netdev; + s->conf.peers.ncs[0] = nd->netdev; s->nic = qemu_new_nic(&net_mcf_fec_info, &s->conf, nd->model, nd->name, s); - qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); } diff --git a/hw/milkymist-minimac2.c b/hw/milkymist-minimac2.c index 43d6c195eb..9992dcceaf 100644 --- a/hw/milkymist-minimac2.c +++ b/hw/milkymist-minimac2.c @@ -257,7 +257,7 @@ static void minimac2_tx(MilkymistMinimac2State *s) trace_milkymist_minimac2_tx_frame(txcount - 12); /* send packet, skipping preamble and sfd */ - qemu_send_packet_raw(&s->nic->nc, buf + 8, txcount - 12); + qemu_send_packet_raw(qemu_get_queue(s->nic), buf + 8, txcount - 12); s->regs[R_TXCOUNT] = 0; @@ -280,7 +280,7 @@ static void update_rx_interrupt(MilkymistMinimac2State *s) static ssize_t minimac2_rx(NetClientState *nc, const uint8_t *buf, size_t size) { - MilkymistMinimac2State *s = DO_UPCAST(NICState, nc, nc)->opaque; + MilkymistMinimac2State *s = qemu_get_nic_opaque(nc); uint32_t r_count; uint32_t r_state; @@ -410,7 +410,7 @@ static const MemoryRegionOps minimac2_ops = { static int minimac2_can_rx(NetClientState *nc) { - MilkymistMinimac2State *s = DO_UPCAST(NICState, nc, nc)->opaque; + MilkymistMinimac2State *s = qemu_get_nic_opaque(nc); if (s->regs[R_STATE0] == STATE_LOADED) { return 1; @@ -424,7 +424,7 @@ static int minimac2_can_rx(NetClientState *nc) static void minimac2_cleanup(NetClientState *nc) { - MilkymistMinimac2State *s = DO_UPCAST(NICState, nc, nc)->opaque; + MilkymistMinimac2State *s = qemu_get_nic_opaque(nc); s->nic = NULL; } @@ -480,7 +480,7 @@ static int milkymist_minimac2_init(SysBusDevice *dev) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_milkymist_minimac2_info, &s->conf, object_get_typename(OBJECT(dev)), dev->qdev.id, s); - qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); return 0; } diff --git a/hw/mipsnet.c b/hw/mipsnet.c index feac8159ee..ff6bf7fdcb 100644 --- a/hw/mipsnet.c +++ b/hw/mipsnet.c @@ -64,7 +64,7 @@ static int mipsnet_buffer_full(MIPSnetState *s) static int mipsnet_can_receive(NetClientState *nc) { - MIPSnetState *s = DO_UPCAST(NICState, nc, nc)->opaque; + MIPSnetState *s = qemu_get_nic_opaque(nc); if (s->busy) return 0; @@ -73,7 +73,7 @@ static int mipsnet_can_receive(NetClientState *nc) static ssize_t mipsnet_receive(NetClientState *nc, const uint8_t *buf, size_t size) { - MIPSnetState *s = DO_UPCAST(NICState, nc, nc)->opaque; + MIPSnetState *s = qemu_get_nic_opaque(nc); trace_mipsnet_receive(size); if (!mipsnet_can_receive(nc)) @@ -173,7 +173,7 @@ static void mipsnet_ioport_write(void *opaque, hwaddr addr, if (s->tx_written == s->tx_count) { /* Send buffer. */ trace_mipsnet_send(s->tx_count); - qemu_send_packet(&s->nic->nc, s->tx_buffer, s->tx_count); + qemu_send_packet(qemu_get_queue(s->nic), s->tx_buffer, s->tx_count); s->tx_count = s->tx_written = 0; s->intctl |= MIPSNET_INTCTL_TXDONE; s->busy = 1; @@ -211,7 +211,7 @@ static const VMStateDescription vmstate_mipsnet = { static void mipsnet_cleanup(NetClientState *nc) { - MIPSnetState *s = DO_UPCAST(NICState, nc, nc)->opaque; + MIPSnetState *s = qemu_get_nic_opaque(nc); s->nic = NULL; } @@ -241,7 +241,7 @@ static int mipsnet_sysbus_init(SysBusDevice *dev) s->nic = qemu_new_nic(&net_mipsnet_info, &s->conf, object_get_typename(OBJECT(dev)), dev->qdev.id, s); - qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); return 0; } diff --git a/hw/musicpal.c b/hw/musicpal.c index 7ac0a918fb..272cb80303 100644 --- a/hw/musicpal.c +++ b/hw/musicpal.c @@ -190,7 +190,7 @@ static int eth_can_receive(NetClientState *nc) static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size) { - mv88w8618_eth_state *s = DO_UPCAST(NICState, nc, nc)->opaque; + mv88w8618_eth_state *s = qemu_get_nic_opaque(nc); uint32_t desc_addr; mv88w8618_rx_desc desc; int i; @@ -257,7 +257,7 @@ static void eth_send(mv88w8618_eth_state *s, int queue_index) len = desc.bytes; if (len < 2048) { cpu_physical_memory_read(desc.buffer, buf, len); - qemu_send_packet(&s->nic->nc, buf, len); + qemu_send_packet(qemu_get_queue(s->nic), buf, len); } desc.cmdstat &= ~MP_ETH_TX_OWN; s->icr |= 1 << (MP_ETH_IRQ_TXLO_BIT - queue_index); @@ -369,7 +369,7 @@ static const MemoryRegionOps mv88w8618_eth_ops = { static void eth_cleanup(NetClientState *nc) { - mv88w8618_eth_state *s = DO_UPCAST(NICState, nc, nc)->opaque; + mv88w8618_eth_state *s = qemu_get_nic_opaque(nc); s->nic = NULL; } diff --git a/hw/ne2000-isa.c b/hw/ne2000-isa.c index 7c11229f1a..342c6bdad1 100644 --- a/hw/ne2000-isa.c +++ b/hw/ne2000-isa.c @@ -38,7 +38,7 @@ typedef struct ISANE2000State { static void isa_ne2000_cleanup(NetClientState *nc) { - NE2000State *s = DO_UPCAST(NICState, nc, nc)->opaque; + NE2000State *s = qemu_get_nic_opaque(nc); s->nic = NULL; } @@ -77,7 +77,7 @@ static int isa_ne2000_initfn(ISADevice *dev) s->nic = qemu_new_nic(&net_ne2000_isa_info, &s->c, object_get_typename(OBJECT(dev)), dev->qdev.id, s); - qemu_format_nic_info_str(&s->nic->nc, s->c.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->c.macaddr.a); return 0; } diff --git a/hw/ne2000.c b/hw/ne2000.c index 872115c454..3dd1c844e8 100644 --- a/hw/ne2000.c +++ b/hw/ne2000.c @@ -167,7 +167,7 @@ static int ne2000_buffer_full(NE2000State *s) int ne2000_can_receive(NetClientState *nc) { - NE2000State *s = DO_UPCAST(NICState, nc, nc)->opaque; + NE2000State *s = qemu_get_nic_opaque(nc); if (s->cmd & E8390_STOP) return 1; @@ -178,7 +178,7 @@ int ne2000_can_receive(NetClientState *nc) ssize_t ne2000_receive(NetClientState *nc, const uint8_t *buf, size_t size_) { - NE2000State *s = DO_UPCAST(NICState, nc, nc)->opaque; + NE2000State *s = qemu_get_nic_opaque(nc); int size = size_; uint8_t *p; unsigned int total_len, next, avail, len, index, mcast_idx; @@ -300,7 +300,8 @@ static void ne2000_ioport_write(void *opaque, uint32_t addr, uint32_t val) index -= NE2000_PMEM_SIZE; /* fail safe: check range on the transmitted length */ if (index + s->tcnt <= NE2000_PMEM_END) { - qemu_send_packet(&s->nic->nc, s->mem + index, s->tcnt); + qemu_send_packet(qemu_get_queue(s->nic), s->mem + index, + s->tcnt); } /* signal end of transfer */ s->tsr = ENTSR_PTX; @@ -705,7 +706,7 @@ void ne2000_setup_io(NE2000State *s, unsigned size) static void ne2000_cleanup(NetClientState *nc) { - NE2000State *s = DO_UPCAST(NICState, nc, nc)->opaque; + NE2000State *s = qemu_get_nic_opaque(nc); s->nic = NULL; } @@ -737,7 +738,7 @@ static int pci_ne2000_init(PCIDevice *pci_dev) s->nic = qemu_new_nic(&net_ne2000_info, &s->c, object_get_typename(OBJECT(pci_dev)), pci_dev->qdev.id, s); - qemu_format_nic_info_str(&s->nic->nc, s->c.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->c.macaddr.a); add_boot_device_path(s->c.bootindex, &pci_dev->qdev, "/ethernet-phy@0"); @@ -750,7 +751,7 @@ static void pci_ne2000_exit(PCIDevice *pci_dev) NE2000State *s = &d->ne2000; memory_region_destroy(&s->io); - qemu_del_net_client(&s->nic->nc); + qemu_del_nic(s->nic); } static Property ne2000_properties[] = { diff --git a/hw/omap1.c b/hw/omap1.c index 1870f4dfed..623b101f80 100644 --- a/hw/omap1.c +++ b/hw/omap1.c @@ -529,6 +529,7 @@ static uint64_t omap_ulpd_pm_read(void *opaque, hwaddr addr, case 0x28: /* Reserved */ case 0x2c: /* Reserved */ OMAP_BAD_REG(addr); + /* fall through */ case 0x00: /* COUNTER_32_LSB */ case 0x04: /* COUNTER_32_MSB */ case 0x08: /* COUNTER_HIGH_FREQ_LSB */ @@ -633,6 +634,7 @@ static void omap_ulpd_pm_write(void *opaque, hwaddr addr, case 0x28: /* Reserved */ case 0x2c: /* Reserved */ OMAP_BAD_REG(addr); + /* fall through */ case 0x24: /* SETUP_ANALOG_CELL3_ULPD1 */ case 0x38: /* COUNTER_32_FIQ */ case 0x48: /* LOCL_TIME */ @@ -1089,6 +1091,7 @@ static void omap_mpui_write(void *opaque, hwaddr addr, /* Not in OMAP310 */ case 0x14: /* DSP_STATUS */ OMAP_RO_REG(addr); + break; case 0x18: /* DSP_BOOT_CONFIG */ case 0x1c: /* DSP_MPUI_CONFIG */ break; diff --git a/hw/omap_dma.c b/hw/omap_dma.c index aec5874311..0c878b6ef2 100644 --- a/hw/omap_dma.c +++ b/hw/omap_dma.c @@ -1709,19 +1709,25 @@ static uint64_t omap_dma4_read(void *opaque, hwaddr addr, case 0x14: /* DMA4_IRQSTATUS_L3 */ irqn ++; + /* fall through */ case 0x10: /* DMA4_IRQSTATUS_L2 */ irqn ++; + /* fall through */ case 0x0c: /* DMA4_IRQSTATUS_L1 */ irqn ++; + /* fall through */ case 0x08: /* DMA4_IRQSTATUS_L0 */ return s->irqstat[irqn]; case 0x24: /* DMA4_IRQENABLE_L3 */ irqn ++; + /* fall through */ case 0x20: /* DMA4_IRQENABLE_L2 */ irqn ++; + /* fall through */ case 0x1c: /* DMA4_IRQENABLE_L1 */ irqn ++; + /* fall through */ case 0x18: /* DMA4_IRQENABLE_L0 */ return s->irqen[irqn]; @@ -1856,10 +1862,13 @@ static void omap_dma4_write(void *opaque, hwaddr addr, switch (addr) { case 0x14: /* DMA4_IRQSTATUS_L3 */ irqn ++; + /* fall through */ case 0x10: /* DMA4_IRQSTATUS_L2 */ irqn ++; + /* fall through */ case 0x0c: /* DMA4_IRQSTATUS_L1 */ irqn ++; + /* fall through */ case 0x08: /* DMA4_IRQSTATUS_L0 */ s->irqstat[irqn] &= ~value; if (!s->irqstat[irqn]) @@ -1868,10 +1877,13 @@ static void omap_dma4_write(void *opaque, hwaddr addr, case 0x24: /* DMA4_IRQENABLE_L3 */ irqn ++; + /* fall through */ case 0x20: /* DMA4_IRQENABLE_L2 */ irqn ++; + /* fall through */ case 0x1c: /* DMA4_IRQENABLE_L1 */ irqn ++; + /* fall through */ case 0x18: /* DMA4_IRQENABLE_L0 */ s->irqen[irqn] = value; return; diff --git a/hw/omap_spi.c b/hw/omap_spi.c index 42d5149a2b..8ff01ed99d 100644 --- a/hw/omap_spi.c +++ b/hw/omap_spi.c @@ -167,32 +167,47 @@ static uint64_t omap_mcspi_read(void *opaque, hwaddr addr, return s->control; case 0x68: ch ++; + /* fall through */ case 0x54: ch ++; + /* fall through */ case 0x40: ch ++; + /* fall through */ case 0x2c: /* MCSPI_CHCONF */ return s->ch[ch].config; case 0x6c: ch ++; + /* fall through */ case 0x58: ch ++; + /* fall through */ case 0x44: ch ++; + /* fall through */ case 0x30: /* MCSPI_CHSTAT */ return s->ch[ch].status; case 0x70: ch ++; + /* fall through */ case 0x5c: ch ++; + /* fall through */ case 0x48: ch ++; + /* fall through */ case 0x34: /* MCSPI_CHCTRL */ return s->ch[ch].control; case 0x74: ch ++; + /* fall through */ case 0x60: ch ++; + /* fall through */ case 0x4c: ch ++; + /* fall through */ case 0x38: /* MCSPI_TX */ return s->ch[ch].tx; case 0x78: ch ++; + /* fall through */ case 0x64: ch ++; + /* fall through */ case 0x50: ch ++; + /* fall through */ case 0x3c: /* MCSPI_RX */ s->ch[ch].status &= ~(1 << 0); /* RXS */ ret = s->ch[ch].rx; @@ -269,8 +284,11 @@ static void omap_mcspi_write(void *opaque, hwaddr addr, break; case 0x68: ch ++; + /* fall through */ case 0x54: ch ++; + /* fall through */ case 0x40: ch ++; + /* fall through */ case 0x2c: /* MCSPI_CHCONF */ if ((value ^ s->ch[ch].config) & (3 << 14)) /* DMAR | DMAW */ omap_mcspi_dmarequest_update(s->ch + ch); @@ -283,8 +301,11 @@ static void omap_mcspi_write(void *opaque, hwaddr addr, break; case 0x70: ch ++; + /* fall through */ case 0x5c: ch ++; + /* fall through */ case 0x48: ch ++; + /* fall through */ case 0x34: /* MCSPI_CHCTRL */ if (value & ~s->ch[ch].control & 1) { /* EN */ s->ch[ch].control |= 1; @@ -294,8 +315,11 @@ static void omap_mcspi_write(void *opaque, hwaddr addr, break; case 0x74: ch ++; + /* fall through */ case 0x60: ch ++; + /* fall through */ case 0x4c: ch ++; + /* fall through */ case 0x38: /* MCSPI_TX */ s->ch[ch].tx = value; s->ch[ch].status &= ~(1 << 1); /* TXS */ diff --git a/hw/opencores_eth.c b/hw/opencores_eth.c index 746a959f6b..f9ba5eeaba 100644 --- a/hw/opencores_eth.c +++ b/hw/opencores_eth.c @@ -313,7 +313,7 @@ static void open_eth_int_source_write(OpenEthState *s, static void open_eth_set_link_status(NetClientState *nc) { - OpenEthState *s = DO_UPCAST(NICState, nc, nc)->opaque; + OpenEthState *s = qemu_get_nic_opaque(nc); if (GET_REGBIT(s, MIICOMMAND, SCANSTAT)) { SET_REGFIELD(s, MIISTATUS, LINKFAIL, nc->link_down); @@ -339,12 +339,12 @@ static void open_eth_reset(void *opaque) s->rx_desc = 0x40; mii_reset(&s->mii); - open_eth_set_link_status(&s->nic->nc); + open_eth_set_link_status(qemu_get_queue(s->nic)); } static int open_eth_can_receive(NetClientState *nc) { - OpenEthState *s = DO_UPCAST(NICState, nc, nc)->opaque; + OpenEthState *s = qemu_get_nic_opaque(nc); return GET_REGBIT(s, MODER, RXEN) && (s->regs[TX_BD_NUM] < 0x80) && @@ -354,7 +354,7 @@ static int open_eth_can_receive(NetClientState *nc) static ssize_t open_eth_receive(NetClientState *nc, const uint8_t *buf, size_t size) { - OpenEthState *s = DO_UPCAST(NICState, nc, nc)->opaque; + OpenEthState *s = qemu_get_nic_opaque(nc); size_t maxfl = GET_REGFIELD(s, PACKETLEN, MAXFL); size_t minfl = GET_REGFIELD(s, PACKETLEN, MINFL); size_t fcsl = 4; @@ -499,7 +499,7 @@ static void open_eth_start_xmit(OpenEthState *s, desc *tx) if (tx_len > len) { memset(buf + len, 0, tx_len - len); } - qemu_send_packet(&s->nic->nc, buf, tx_len); + qemu_send_packet(qemu_get_queue(s->nic), buf, tx_len); if (tx->len_flags & TXD_WR) { s->tx_desc = 0; @@ -606,7 +606,7 @@ static void open_eth_mii_command_host_write(OpenEthState *s, uint32_t val) } else { s->regs[MIIRX_DATA] = 0xffff; } - SET_REGFIELD(s, MIISTATUS, LINKFAIL, s->nic->nc.link_down); + SET_REGFIELD(s, MIISTATUS, LINKFAIL, qemu_get_queue(s->nic)->link_down); } } diff --git a/hw/openpic.c b/hw/openpic.c index d414f47b7d..20a479c794 100644 --- a/hw/openpic.c +++ b/hw/openpic.c @@ -34,7 +34,7 @@ * */ #include "hw.h" -#include "ppc_mac.h" +#include "ppc/mac.h" #include "pci/pci.h" #include "openpic.h" #include "sysbus.h" @@ -56,7 +56,7 @@ static const int debug_openpic = 0; } \ } while (0) -#define MAX_CPU 15 +#define MAX_CPU 32 #define MAX_SRC 256 #define MAX_TMR 4 #define MAX_IPI 4 @@ -66,6 +66,7 @@ static const int debug_openpic = 0; /* OpenPIC capability flags */ #define OPENPIC_FLAG_IDR_CRIT (1 << 0) +#define OPENPIC_FLAG_ILR (2 << 0) /* OpenPIC address map */ #define OPENPIC_GLB_REG_START 0x0 @@ -74,6 +75,8 @@ static const int debug_openpic = 0; #define OPENPIC_TMR_REG_SIZE 0x220 #define OPENPIC_MSI_REG_START 0x1600 #define OPENPIC_MSI_REG_SIZE 0x200 +#define OPENPIC_SUMMARY_REG_START 0x3800 +#define OPENPIC_SUMMARY_REG_SIZE 0x800 #define OPENPIC_SRC_REG_START 0x10000 #define OPENPIC_SRC_REG_SIZE (MAX_SRC * 0x20) #define OPENPIC_CPU_REG_START 0x20000 @@ -94,33 +97,17 @@ static const int debug_openpic = 0; /* First doorbell IRQ */ #define RAVEN_DBL_IRQ (RAVEN_IPI_IRQ + (RAVEN_MAX_CPU * RAVEN_MAX_IPI)) -/* FSL_MPIC_20 */ -#define FSL_MPIC_20_MAX_CPU 1 -#define FSL_MPIC_20_MAX_EXT 12 -#define FSL_MPIC_20_MAX_INT 64 -#define FSL_MPIC_20_MAX_IRQ MAX_IRQ +typedef struct FslMpicInfo { + int max_ext; +} FslMpicInfo; -/* Interrupt definitions */ -/* IRQs, accessible through the IRQ region */ -#define FSL_MPIC_20_EXT_IRQ 0x00 -#define FSL_MPIC_20_INT_IRQ 0x10 -#define FSL_MPIC_20_MSG_IRQ 0xb0 -#define FSL_MPIC_20_MSI_IRQ 0xe0 -/* These are available through separate regions, but - for simplicity's sake mapped into the same number space */ -#define FSL_MPIC_20_TMR_IRQ 0x100 -#define FSL_MPIC_20_IPI_IRQ 0x104 +static FslMpicInfo fsl_mpic_20 = { + .max_ext = 12, +}; -/* - * Block Revision Register1 (BRR1): QEMU does not fully emulate - * any version on MPIC. So to start with, set the IP version to 0. - * - * NOTE: This is Freescale MPIC specific register. Keep it here till - * this code is refactored for different variants of OPENPIC and MPIC. - */ -#define FSL_BRR1_IPID (0x0040 << 16) /* 16 bit IP-block ID */ -#define FSL_BRR1_IPMJ (0x00 << 8) /* 8 bit IP major number */ -#define FSL_BRR1_IPMN 0x00 /* 8 bit IP minor number */ +static FslMpicInfo fsl_mpic_42 = { + .max_ext = 12, +}; #define FRR_NIRQ_SHIFT 16 #define FRR_NCPU_SHIFT 8 @@ -146,6 +133,49 @@ static const int debug_openpic = 0; #define IDR_P1_SHIFT 1 #define IDR_P0_SHIFT 0 +#define ILR_INTTGT_MASK 0x000000ff +#define ILR_INTTGT_INT 0x00 +#define ILR_INTTGT_CINT 0x01 /* critical */ +#define ILR_INTTGT_MCP 0x02 /* machine check */ + +/* The currently supported INTTGT values happen to be the same as QEMU's + * openpic output codes, but don't depend on this. The output codes + * could change (unlikely, but...) or support could be added for + * more INTTGT values. + */ +static const int inttgt_output[][2] = { + { ILR_INTTGT_INT, OPENPIC_OUTPUT_INT }, + { ILR_INTTGT_CINT, OPENPIC_OUTPUT_CINT }, + { ILR_INTTGT_MCP, OPENPIC_OUTPUT_MCK }, +}; + +static int inttgt_to_output(int inttgt) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(inttgt_output); i++) { + if (inttgt_output[i][0] == inttgt) { + return inttgt_output[i][1]; + } + } + + fprintf(stderr, "%s: unsupported inttgt %d\n", __func__, inttgt); + return OPENPIC_OUTPUT_INT; +} + +static int output_to_inttgt(int output) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(inttgt_output); i++) { + if (inttgt_output[i][1] == output) { + return inttgt_output[i][0]; + } + } + + abort(); +} + #define MSIIR_OFFSET 0x140 #define MSIIR_SRS_SHIFT 29 #define MSIIR_SRS_MASK (0x7 << MSIIR_SRS_SHIFT) @@ -230,6 +260,7 @@ typedef struct OpenPICState { MemoryRegion mem; /* Behavior control */ + FslMpicInfo *fsl; uint32_t model; uint32_t flags; uint32_t nb_irqs; @@ -243,7 +274,7 @@ typedef struct OpenPICState { uint32_t mpic_mode_mask; /* Sub-regions */ - MemoryRegion sub_io_mem[5]; + MemoryRegion sub_io_mem[6]; /* Global registers */ uint32_t frr; /* Feature reporting register */ @@ -436,13 +467,13 @@ static void openpic_update_irq(OpenPICState *opp, int n_IRQ) src->ivpr &= ~IVPR_ACTIVITY_MASK; } - if (src->idr == 0) { + if (src->destmask == 0) { /* No target */ DPRINTF("%s: IRQ %d has no target\n", __func__, n_IRQ); return; } - if (src->idr == (1 << src->last_cpu)) { + if (src->destmask == (1 << src->last_cpu)) { /* Only one CPU is allowed to receive this IRQ */ IRQ_local_pipe(opp, src->last_cpu, n_IRQ, active, was_active); } else if (!(src->ivpr & IVPR_MODE_MASK)) { @@ -558,6 +589,15 @@ static inline uint32_t read_IRQreg_idr(OpenPICState *opp, int n_IRQ) return opp->src[n_IRQ].idr; } +static inline uint32_t read_IRQreg_ilr(OpenPICState *opp, int n_IRQ) +{ + if (opp->flags & OPENPIC_FLAG_ILR) { + return output_to_inttgt(opp->src[n_IRQ].output); + } + + return 0xffffffff; +} + static inline uint32_t read_IRQreg_ivpr(OpenPICState *opp, int n_IRQ) { return opp->src[n_IRQ].ivpr; @@ -608,6 +648,19 @@ static inline void write_IRQreg_idr(OpenPICState *opp, int n_IRQ, uint32_t val) } } +static inline void write_IRQreg_ilr(OpenPICState *opp, int n_IRQ, uint32_t val) +{ + if (opp->flags & OPENPIC_FLAG_ILR) { + IRQSource *src = &opp->src[n_IRQ]; + + src->output = inttgt_to_output(val & ILR_INTTGT_MASK); + DPRINTF("Set ILR %d to 0x%08x, output %d\n", n_IRQ, src->idr, + src->output); + + /* TODO: on MPIC v4.0 only, set nomask for non-INT */ + } +} + static inline void write_IRQreg_ivpr(OpenPICState *opp, int n_IRQ, uint32_t val) { uint32_t mask; @@ -792,19 +845,23 @@ static void openpic_tmr_write(void *opaque, hwaddr addr, uint64_t val, OpenPICState *opp = opaque; int idx; + addr += 0x10f0; + DPRINTF("%s: addr %#" HWADDR_PRIx " <= %08" PRIx64 "\n", __func__, addr, val); if (addr & 0xF) { return; } - idx = (addr >> 6) & 0x3; - addr = addr & 0x30; - if (addr == 0x0) { + if (addr == 0x10f0) { /* TFRR */ opp->tfrr = val; return; } + + idx = (addr >> 6) & 0x3; + addr = addr & 0x30; + switch (addr & 0x30) { case 0x00: /* TCCR */ break; @@ -870,17 +927,20 @@ static void openpic_src_write(void *opaque, hwaddr addr, uint64_t val, DPRINTF("%s: addr %#" HWADDR_PRIx " <= %08" PRIx64 "\n", __func__, addr, val); - if (addr & 0xF) { - return; - } - addr = addr & 0xFFF0; + + addr = addr & 0xffff; idx = addr >> 5; - if (addr & 0x10) { - /* EXDE / IFEDE / IEEDE */ - write_IRQreg_idr(opp, idx, val); - } else { - /* EXVP / IFEVP / IEEVP */ + + switch (addr & 0x1f) { + case 0x00: write_IRQreg_ivpr(opp, idx, val); + break; + case 0x10: + write_IRQreg_idr(opp, idx, val); + break; + case 0x18: + write_IRQreg_ilr(opp, idx, val); + break; } } @@ -892,20 +952,23 @@ static uint64_t openpic_src_read(void *opaque, uint64_t addr, unsigned len) DPRINTF("%s: addr %#" HWADDR_PRIx "\n", __func__, addr); retval = 0xFFFFFFFF; - if (addr & 0xF) { - return retval; - } - addr = addr & 0xFFF0; + + addr = addr & 0xffff; idx = addr >> 5; - if (addr & 0x10) { - /* EXDE / IFEDE / IEEDE */ - retval = read_IRQreg_idr(opp, idx); - } else { - /* EXVP / IFEVP / IEEVP */ + + switch (addr & 0x1f) { + case 0x00: retval = read_IRQreg_ivpr(opp, idx); + break; + case 0x10: + retval = read_IRQreg_idr(opp, idx); + break; + case 0x18: + retval = read_IRQreg_ilr(opp, idx); + break; } - DPRINTF("%s: => 0x%08x\n", __func__, retval); + DPRINTF("%s: => 0x%08x\n", __func__, retval); return retval; } @@ -973,6 +1036,26 @@ static uint64_t openpic_msi_read(void *opaque, hwaddr addr, unsigned size) return r; } +static uint64_t openpic_summary_read(void *opaque, hwaddr addr, unsigned size) +{ + uint64_t r = 0; + + DPRINTF("%s: addr %#" HWADDR_PRIx "\n", __func__, addr); + + /* TODO: EISR/EIMR */ + + return r; +} + +static void openpic_summary_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + DPRINTF("%s: addr %#" HWADDR_PRIx " <= 0x%08" PRIx64 "\n", + __func__, addr, val); + + /* TODO: EISR/EIMR */ +} + static void openpic_cpu_write_internal(void *opaque, hwaddr addr, uint32_t val, int idx) { @@ -1000,8 +1083,7 @@ static void openpic_cpu_write_internal(void *opaque, hwaddr addr, case 0x70: idx = (addr - 0x40) >> 4; /* we use IDE as mask which CPUs to deliver the IPI to still. */ - write_IRQreg_idr(opp, opp->irq_ipi0 + idx, - opp->src[opp->irq_ipi0 + idx].idr | val); + opp->src[opp->irq_ipi0 + idx].destmask |= val; openpic_set_irq(opp, opp->irq_ipi0 + idx, 1); openpic_set_irq(opp, opp->irq_ipi0 + idx, 0); break; @@ -1101,8 +1183,8 @@ static uint32_t openpic_iack(OpenPICState *opp, IRQDest *dst, int cpu) } if ((irq >= opp->irq_ipi0) && (irq < (opp->irq_ipi0 + MAX_IPI))) { - src->idr &= ~(1 << cpu); - if (src->idr && !src->level) { + src->destmask &= ~(1 << cpu); + if (src->destmask && !src->level) { /* trigger on CPUs that didn't know about it yet */ openpic_set_irq(opp, irq, 1); openpic_set_irq(opp, irq, 0); @@ -1239,19 +1321,19 @@ static const MemoryRegionOps openpic_src_ops_be = { }, }; -static const MemoryRegionOps openpic_msi_ops_le = { +static const MemoryRegionOps openpic_msi_ops_be = { .read = openpic_msi_read, .write = openpic_msi_write, - .endianness = DEVICE_LITTLE_ENDIAN, + .endianness = DEVICE_BIG_ENDIAN, .impl = { .min_access_size = 4, .max_access_size = 4, }, }; -static const MemoryRegionOps openpic_msi_ops_be = { - .read = openpic_msi_read, - .write = openpic_msi_write, +static const MemoryRegionOps openpic_summary_ops_be = { + .read = openpic_summary_read, + .write = openpic_summary_write, .endianness = DEVICE_BIG_ENDIAN, .impl = { .min_access_size = 4, @@ -1307,6 +1389,7 @@ static void openpic_save(QEMUFile* f, void *opaque) for (i = 0; i < opp->max_irq; i++) { qemu_put_be32s(f, &opp->src[i].ivpr); qemu_put_be32s(f, &opp->src[i].idr); + qemu_get_be32s(f, &opp->src[i].destmask); qemu_put_sbe32s(f, &opp->src[i].last_cpu); qemu_put_sbe32s(f, &opp->src[i].pending); } @@ -1372,6 +1455,7 @@ static int openpic_load(QEMUFile* f, void *opaque, int version_id) qemu_get_be32s(f, &opp->src[i].ivpr); qemu_get_be32s(f, &opp->src[i].idr); + qemu_get_be32s(f, &opp->src[i].destmask); qemu_get_sbe32s(f, &opp->src[i].last_cpu); qemu_get_sbe32s(f, &opp->src[i].pending); } @@ -1382,78 +1466,128 @@ static int openpic_load(QEMUFile* f, void *opaque, int version_id) typedef struct MemReg { const char *name; MemoryRegionOps const *ops; - bool map; hwaddr start_addr; ram_addr_t size; } MemReg; +static void fsl_common_init(OpenPICState *opp) +{ + int i; + int virq = MAX_SRC; + + opp->vid = VID_REVISION_1_2; + opp->vir = VIR_GENERIC; + opp->vector_mask = 0xFFFF; + opp->tfrr_reset = 0; + opp->ivpr_reset = IVPR_MASK_MASK; + opp->idr_reset = 1 << 0; + opp->max_irq = MAX_IRQ; + + opp->irq_ipi0 = virq; + virq += MAX_IPI; + opp->irq_tim0 = virq; + virq += MAX_TMR; + + assert(virq <= MAX_IRQ); + + opp->irq_msi = 224; + + msi_supported = true; + for (i = 0; i < opp->fsl->max_ext; i++) { + opp->src[i].level = false; + } + + /* Internal interrupts, including message and MSI */ + for (i = 16; i < MAX_SRC; i++) { + opp->src[i].type = IRQ_TYPE_FSLINT; + opp->src[i].level = true; + } + + /* timers and IPIs */ + for (i = MAX_SRC; i < virq; i++) { + opp->src[i].type = IRQ_TYPE_FSLSPECIAL; + opp->src[i].level = false; + } +} + +static void map_list(OpenPICState *opp, const MemReg *list, int *count) +{ + while (list->name) { + assert(*count < ARRAY_SIZE(opp->sub_io_mem)); + + memory_region_init_io(&opp->sub_io_mem[*count], list->ops, opp, + list->name, list->size); + + memory_region_add_subregion(&opp->mem, list->start_addr, + &opp->sub_io_mem[*count]); + + (*count)++; + list++; + } +} + static int openpic_init(SysBusDevice *dev) { OpenPICState *opp = FROM_SYSBUS(typeof (*opp), dev); int i, j; - MemReg list_le[] = { - {"glb", &openpic_glb_ops_le, true, + int list_count = 0; + static const MemReg list_le[] = { + {"glb", &openpic_glb_ops_le, OPENPIC_GLB_REG_START, OPENPIC_GLB_REG_SIZE}, - {"tmr", &openpic_tmr_ops_le, true, + {"tmr", &openpic_tmr_ops_le, OPENPIC_TMR_REG_START, OPENPIC_TMR_REG_SIZE}, - {"msi", &openpic_msi_ops_le, true, - OPENPIC_MSI_REG_START, OPENPIC_MSI_REG_SIZE}, - {"src", &openpic_src_ops_le, true, + {"src", &openpic_src_ops_le, OPENPIC_SRC_REG_START, OPENPIC_SRC_REG_SIZE}, - {"cpu", &openpic_cpu_ops_le, true, + {"cpu", &openpic_cpu_ops_le, OPENPIC_CPU_REG_START, OPENPIC_CPU_REG_SIZE}, + {NULL} }; - MemReg list_be[] = { - {"glb", &openpic_glb_ops_be, true, + static const MemReg list_be[] = { + {"glb", &openpic_glb_ops_be, OPENPIC_GLB_REG_START, OPENPIC_GLB_REG_SIZE}, - {"tmr", &openpic_tmr_ops_be, true, + {"tmr", &openpic_tmr_ops_be, OPENPIC_TMR_REG_START, OPENPIC_TMR_REG_SIZE}, - {"msi", &openpic_msi_ops_be, true, - OPENPIC_MSI_REG_START, OPENPIC_MSI_REG_SIZE}, - {"src", &openpic_src_ops_be, true, + {"src", &openpic_src_ops_be, OPENPIC_SRC_REG_START, OPENPIC_SRC_REG_SIZE}, - {"cpu", &openpic_cpu_ops_be, true, + {"cpu", &openpic_cpu_ops_be, OPENPIC_CPU_REG_START, OPENPIC_CPU_REG_SIZE}, + {NULL} + }; + static const MemReg list_fsl[] = { + {"msi", &openpic_msi_ops_be, + OPENPIC_MSI_REG_START, OPENPIC_MSI_REG_SIZE}, + {"summary", &openpic_summary_ops_be, + OPENPIC_SUMMARY_REG_START, OPENPIC_SUMMARY_REG_SIZE}, + {NULL} }; - MemReg *list; + + memory_region_init(&opp->mem, "openpic", 0x40000); switch (opp->model) { case OPENPIC_MODEL_FSL_MPIC_20: default: + opp->fsl = &fsl_mpic_20; + opp->brr1 = 0x00400200; opp->flags |= OPENPIC_FLAG_IDR_CRIT; opp->nb_irqs = 80; - opp->vid = VID_REVISION_1_2; - opp->vir = VIR_GENERIC; - opp->vector_mask = 0xFFFF; - opp->tfrr_reset = 0; - opp->ivpr_reset = IVPR_MASK_MASK; - opp->idr_reset = 1 << 0; - opp->max_irq = FSL_MPIC_20_MAX_IRQ; - opp->irq_ipi0 = FSL_MPIC_20_IPI_IRQ; - opp->irq_tim0 = FSL_MPIC_20_TMR_IRQ; - opp->irq_msi = FSL_MPIC_20_MSI_IRQ; - opp->brr1 = FSL_BRR1_IPID | FSL_BRR1_IPMJ | FSL_BRR1_IPMN; - /* XXX really only available as of MPIC 4.0 */ - opp->mpic_mode_mask = GCR_MODE_PROXY; + opp->mpic_mode_mask = GCR_MODE_MIXED; - msi_supported = true; - list = list_be; + fsl_common_init(opp); + map_list(opp, list_be, &list_count); + map_list(opp, list_fsl, &list_count); - for (i = 0; i < FSL_MPIC_20_MAX_EXT; i++) { - opp->src[i].level = false; - } + break; - /* Internal interrupts, including message and MSI */ - for (i = 16; i < MAX_SRC; i++) { - opp->src[i].type = IRQ_TYPE_FSLINT; - opp->src[i].level = true; - } + case OPENPIC_MODEL_FSL_MPIC_42: + opp->fsl = &fsl_mpic_42; + opp->brr1 = 0x00400402; + opp->flags |= OPENPIC_FLAG_ILR; + opp->nb_irqs = 196; + opp->mpic_mode_mask = GCR_MODE_PROXY; - /* timers and IPIs */ - for (i = MAX_SRC; i < MAX_IRQ; i++) { - opp->src[i].type = IRQ_TYPE_FSLSPECIAL; - opp->src[i].level = false; - } + fsl_common_init(opp); + map_list(opp, list_be, &list_count); + map_list(opp, list_fsl, &list_count); break; @@ -1470,29 +1604,14 @@ static int openpic_init(SysBusDevice *dev) opp->irq_tim0 = RAVEN_TMR_IRQ; opp->brr1 = -1; opp->mpic_mode_mask = GCR_MODE_MIXED; - list = list_le; - /* Don't map MSI region */ - list[2].map = false; /* Only UP supported today */ if (opp->nb_cpus != 1) { return -EINVAL; } - break; - } - - memory_region_init(&opp->mem, "openpic", 0x40000); - for (i = 0; i < ARRAY_SIZE(list_le); i++) { - if (!list[i].map) { - continue; - } - - memory_region_init_io(&opp->sub_io_mem[i], list[i].ops, opp, - list[i].name, list[i].size); - - memory_region_add_subregion(&opp->mem, list[i].start_addr, - &opp->sub_io_mem[i]); + map_list(opp, list_le, &list_count); + break; } for (i = 0; i < opp->nb_cpus; i++) { diff --git a/hw/openpic.h b/hw/openpic.h index e226d7b563..9dcaf0e7cd 100644 --- a/hw/openpic.h +++ b/hw/openpic.h @@ -13,5 +13,6 @@ enum { #define OPENPIC_MODEL_RAVEN 0 #define OPENPIC_MODEL_FSL_MPIC_20 1 +#define OPENPIC_MODEL_FSL_MPIC_42 2 #endif /* __OPENPIC_H__ */ @@ -527,11 +527,11 @@ type_init(port92_register_types) static void handle_a20_line_change(void *opaque, int irq, int level) { - CPUX86State *cpu = opaque; + X86CPU *cpu = opaque; /* XXX: send to all CPUs ? */ /* XXX: add logic to handle multiple A20 line sources */ - cpu_x86_set_a20(cpu, level); + x86_cpu_set_a20(cpu, level); } int e820_add_entry(uint64_t address, uint64_t length, uint32_t type) @@ -551,6 +551,18 @@ int e820_add_entry(uint64_t address, uint64_t length, uint32_t type) return index; } +/* Calculates the limit to CPU APIC ID values + * + * This function returns the limit for the APIC ID value, so that all + * CPU APIC IDs are < pc_apic_id_limit(). + * + * This is used for FW_CFG_MAX_CPUS. See comments on bochs_bios_init(). + */ +static unsigned int pc_apic_id_limit(unsigned int max_cpus) +{ + return x86_cpu_apic_id_from_index(max_cpus - 1) + 1; +} + static void *bochs_bios_init(void) { void *fw_cfg; @@ -558,9 +570,24 @@ static void *bochs_bios_init(void) size_t smbios_len; uint64_t *numa_fw_cfg; int i, j; + unsigned int apic_id_limit = pc_apic_id_limit(max_cpus); fw_cfg = fw_cfg_init(BIOS_CFG_IOPORT, BIOS_CFG_IOPORT + 1, 0, 0); - + /* FW_CFG_MAX_CPUS is a bit confusing/problematic on x86: + * + * SeaBIOS needs FW_CFG_MAX_CPUS for CPU hotplug, but the CPU hotplug + * QEMU<->SeaBIOS interface is not based on the "CPU index", but on the APIC + * ID of hotplugged CPUs[1]. This means that FW_CFG_MAX_CPUS is not the + * "maximum number of CPUs", but the "limit to the APIC ID values SeaBIOS + * may see". + * + * So, this means we must not use max_cpus, here, but the maximum possible + * APIC ID value, plus one. + * + * [1] The only kind of "CPU identifier" used between SeaBIOS and QEMU is + * the APIC ID, not the "CPU index" + */ + fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)apic_id_limit); fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1); fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); fw_cfg_add_bytes(fw_cfg, FW_CFG_ACPI_TABLES, @@ -579,21 +606,24 @@ static void *bochs_bios_init(void) * of nodes, one word for each VCPU->node and one word for each node to * hold the amount of memory. */ - numa_fw_cfg = g_new0(uint64_t, 1 + max_cpus + nb_numa_nodes); + numa_fw_cfg = g_new0(uint64_t, 1 + apic_id_limit + nb_numa_nodes); numa_fw_cfg[0] = cpu_to_le64(nb_numa_nodes); for (i = 0; i < max_cpus; i++) { + unsigned int apic_id = x86_cpu_apic_id_from_index(i); + assert(apic_id < apic_id_limit); for (j = 0; j < nb_numa_nodes; j++) { if (test_bit(i, node_cpumask[j])) { - numa_fw_cfg[i + 1] = cpu_to_le64(j); + numa_fw_cfg[apic_id + 1] = cpu_to_le64(j); break; } } } for (i = 0; i < nb_numa_nodes; i++) { - numa_fw_cfg[max_cpus + 1 + i] = cpu_to_le64(node_mem[i]); + numa_fw_cfg[apic_id_limit + 1 + i] = cpu_to_le64(node_mem[i]); } fw_cfg_add_bytes(fw_cfg, FW_CFG_NUMA, numa_fw_cfg, - (1 + max_cpus + nb_numa_nodes) * sizeof(*numa_fw_cfg)); + (1 + apic_id_limit + nb_numa_nodes) * + sizeof(*numa_fw_cfg)); return fw_cfg; } @@ -1055,7 +1085,8 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, } } - a20_line = qemu_allocate_irqs(handle_a20_line_change, first_cpu, 2); + a20_line = qemu_allocate_irqs(handle_a20_line_change, + x86_env_get_cpu(first_cpu), 2); i8042 = isa_create_simple(isa_bus, "i8042"); i8042_setup_a20_line(i8042, &a20_line[0]); if (!no_vmport) { diff --git a/hw/pc_piix.c b/hw/pc_piix.c index 0a6923dcef..0af436cfaf 100644 --- a/hw/pc_piix.c +++ b/hw/pc_piix.c @@ -235,10 +235,18 @@ static void pc_init_pci(QEMUMachineInitArgs *args) static void pc_init_pci_1_3(QEMUMachineInitArgs *args) { - enable_kvm_pv_eoi(); + enable_compat_apic_id_mode(); pc_init_pci(args); } +/* PC machine init function for pc-0.14 to pc-1.2 */ +static void pc_init_pci_1_2(QEMUMachineInitArgs *args) +{ + disable_kvm_pv_eoi(); + pc_init_pci_1_3(args); +} + +/* PC init function for pc-0.10 to pc-0.13, and reused by xenfv */ static void pc_init_pci_no_kvmclock(QEMUMachineInitArgs *args) { ram_addr_t ram_size = args->ram_size; @@ -247,6 +255,8 @@ static void pc_init_pci_no_kvmclock(QEMUMachineInitArgs *args) const char *kernel_cmdline = args->kernel_cmdline; const char *initrd_filename = args->initrd_filename; const char *boot_device = args->boot_device; + disable_kvm_pv_eoi(); + enable_compat_apic_id_mode(); pc_init1(get_system_memory(), get_system_io(), ram_size, boot_device, @@ -264,6 +274,8 @@ static void pc_init_isa(QEMUMachineInitArgs *args) const char *boot_device = args->boot_device; if (cpu_model == NULL) cpu_model = "486"; + disable_kvm_pv_eoi(); + enable_compat_apic_id_mode(); pc_init1(get_system_memory(), get_system_io(), ram_size, boot_device, @@ -286,7 +298,7 @@ static QEMUMachine pc_i440fx_machine_v1_4 = { .name = "pc-i440fx-1.4", .alias = "pc", .desc = "Standard PC (i440FX + PIIX, 1996)", - .init = pc_init_pci_1_3, + .init = pc_init_pci, .max_cpus = 255, .is_default = 1, DEFAULT_MACHINE_OPTIONS, @@ -297,6 +309,14 @@ static QEMUMachine pc_i440fx_machine_v1_4 = { .driver = "usb-tablet",\ .property = "usb_version",\ .value = stringify(1),\ + },{\ + .driver = "virtio-net-pci",\ + .property = "ctrl_mac_addr",\ + .value = "off", \ + },{ \ + .driver = "virtio-net-pci", \ + .property = "mq", \ + .value = "off", \ } static QEMUMachine pc_machine_v1_3 = { @@ -342,7 +362,7 @@ static QEMUMachine pc_machine_v1_3 = { static QEMUMachine pc_machine_v1_2 = { .name = "pc-1.2", .desc = "Standard PC", - .init = pc_init_pci, + .init = pc_init_pci_1_2, .max_cpus = 255, .compat_props = (GlobalProperty[]) { PC_COMPAT_1_2, @@ -386,7 +406,7 @@ static QEMUMachine pc_machine_v1_2 = { static QEMUMachine pc_machine_v1_1 = { .name = "pc-1.1", .desc = "Standard PC", - .init = pc_init_pci, + .init = pc_init_pci_1_2, .max_cpus = 255, .compat_props = (GlobalProperty[]) { PC_COMPAT_1_1, @@ -422,7 +442,7 @@ static QEMUMachine pc_machine_v1_1 = { static QEMUMachine pc_machine_v1_0 = { .name = "pc-1.0", .desc = "Standard PC", - .init = pc_init_pci, + .init = pc_init_pci_1_2, .max_cpus = 255, .compat_props = (GlobalProperty[]) { PC_COMPAT_1_0, @@ -438,7 +458,7 @@ static QEMUMachine pc_machine_v1_0 = { static QEMUMachine pc_machine_v0_15 = { .name = "pc-0.15", .desc = "Standard PC", - .init = pc_init_pci, + .init = pc_init_pci_1_2, .max_cpus = 255, .compat_props = (GlobalProperty[]) { PC_COMPAT_0_15, @@ -471,7 +491,7 @@ static QEMUMachine pc_machine_v0_15 = { static QEMUMachine pc_machine_v0_14 = { .name = "pc-0.14", .desc = "Standard PC", - .init = pc_init_pci, + .init = pc_init_pci_1_2, .max_cpus = 255, .compat_props = (GlobalProperty[]) { PC_COMPAT_0_14, diff --git a/hw/pc_q35.c b/hw/pc_q35.c index d82353e84f..6f5ff8dcae 100644 --- a/hw/pc_q35.c +++ b/hw/pc_q35.c @@ -147,6 +147,7 @@ static void pc_q35_init(QEMUMachineInitArgs *args) ich9_lpc->ioapic = gsi_state->ioapic_irq; pci_bus_irqs(host_bus, ich9_lpc_set_irq, ich9_lpc_map_irq, ich9_lpc, ICH9_LPC_NB_PIRQS); + pci_bus_set_route_irq_fn(host_bus, ich9_route_intx_pin_to_irq); isa_bus = ich9_lpc->isa_bus; /*end early*/ diff --git a/hw/pci/Makefile.objs b/hw/pci/Makefile.objs index fe965fe2f6..1cd6cde2ee 100644 --- a/hw/pci/Makefile.objs +++ b/hw/pci/Makefile.objs @@ -6,4 +6,4 @@ common-obj-$(CONFIG_PCI) += pci_host.o pcie_host.o common-obj-$(CONFIG_PCI) += pcie.o pcie_aer.o pcie_port.o common-obj-$(CONFIG_NO_PCI) += pci-stub.o -extra-obj-y += pci-stub.o +common-obj-$(CONFIG_ALL) += pci-stub.o diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 5fd1bcf08e..905dc4a219 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -274,13 +274,12 @@ int pci_find_domain(const PCIBus *bus) return -1; } -void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent, +static void pci_bus_init(PCIBus *bus, DeviceState *parent, const char *name, MemoryRegion *address_space_mem, MemoryRegion *address_space_io, uint8_t devfn_min) { - qbus_create_inplace(&bus->qbus, TYPE_PCI_BUS, parent, name); assert(PCI_FUNC(devfn_min) == 0); bus->devfn_min = devfn_min; bus->address_space_mem = address_space_mem; @@ -293,6 +292,17 @@ void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent, vmstate_register(NULL, -1, &vmstate_pcibus, bus); } +void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent, + const char *name, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io, + uint8_t devfn_min) +{ + qbus_create_inplace(bus, TYPE_PCI_BUS, parent, name); + pci_bus_init(bus, parent, name, address_space_mem, + address_space_io, devfn_min); +} + PCIBus *pci_bus_new(DeviceState *parent, const char *name, MemoryRegion *address_space_mem, MemoryRegion *address_space_io, @@ -300,10 +310,9 @@ PCIBus *pci_bus_new(DeviceState *parent, const char *name, { PCIBus *bus; - bus = g_malloc0(sizeof(*bus)); - pci_bus_new_inplace(bus, parent, name, address_space_mem, - address_space_io, devfn_min); - OBJECT(bus)->free = g_free; + bus = PCI_BUS(qbus_create(TYPE_PCI_BUS, parent, name)); + pci_bus_init(bus, parent, name, address_space_mem, + address_space_io, devfn_min); return bus; } diff --git a/hw/pcnet-pci.c b/hw/pcnet-pci.c index a94f642136..df63b22463 100644 --- a/hw/pcnet-pci.c +++ b/hw/pcnet-pci.c @@ -266,7 +266,7 @@ static void pci_physical_memory_read(void *dma_opaque, hwaddr addr, static void pci_pcnet_cleanup(NetClientState *nc) { - PCNetState *d = DO_UPCAST(NICState, nc, nc)->opaque; + PCNetState *d = qemu_get_nic_opaque(nc); pcnet_common_cleanup(d); } @@ -279,7 +279,7 @@ static void pci_pcnet_uninit(PCIDevice *dev) memory_region_destroy(&d->io_bar); qemu_del_timer(d->state.poll_timer); qemu_free_timer(d->state.poll_timer); - qemu_del_net_client(&d->state.nic->nc); + qemu_del_nic(d->state.nic); } static NetClientInfo net_pci_pcnet_info = { diff --git a/hw/pcnet.c b/hw/pcnet.c index 30f100007a..e0de1e3458 100644 --- a/hw/pcnet.c +++ b/hw/pcnet.c @@ -1006,7 +1006,7 @@ static int pcnet_tdte_poll(PCNetState *s) int pcnet_can_receive(NetClientState *nc) { - PCNetState *s = DO_UPCAST(NICState, nc, nc)->opaque; + PCNetState *s = qemu_get_nic_opaque(nc); if (CSR_STOP(s) || CSR_SPND(s)) return 0; @@ -1017,7 +1017,7 @@ int pcnet_can_receive(NetClientState *nc) ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_) { - PCNetState *s = DO_UPCAST(NICState, nc, nc)->opaque; + PCNetState *s = qemu_get_nic_opaque(nc); int is_padr = 0, is_bcast = 0, is_ladr = 0; uint8_t buf1[60]; int remaining; @@ -1199,7 +1199,7 @@ ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_) void pcnet_set_link_status(NetClientState *nc) { - PCNetState *d = DO_UPCAST(NICState, nc, nc)->opaque; + PCNetState *d = qemu_get_nic_opaque(nc); d->lnkst = nc->link_down ? 0 : 0x40; } @@ -1261,11 +1261,12 @@ static void pcnet_transmit(PCNetState *s) if (BCR_SWSTYLE(s) == 1) add_crc = !GET_FIELD(tmd.status, TMDS, NOFCS); s->looptest = add_crc ? PCNET_LOOPTEST_CRC : PCNET_LOOPTEST_NOCRC; - pcnet_receive(&s->nic->nc, s->buffer, s->xmit_pos); + pcnet_receive(qemu_get_queue(s->nic), s->buffer, s->xmit_pos); s->looptest = 0; } else if (s->nic) - qemu_send_packet(&s->nic->nc, s->buffer, s->xmit_pos); + qemu_send_packet(qemu_get_queue(s->nic), s->buffer, + s->xmit_pos); s->csr[0] &= ~0x0008; /* clear TDMD */ s->csr[4] |= 0x0004; /* set TXSTRT */ @@ -1730,7 +1731,7 @@ int pcnet_common_init(DeviceState *dev, PCNetState *s, NetClientInfo *info) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(info, &s->conf, object_get_typename(OBJECT(dev)), dev->id, s); - qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); add_boot_device_path(s->conf.bootindex, dev, "/ethernet-phy@0"); diff --git a/hw/pflash_cfi02.c b/hw/pflash_cfi02.c index b4220c1896..44bd4654f0 100644 --- a/hw/pflash_cfi02.c +++ b/hw/pflash_cfi02.c @@ -157,6 +157,7 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, DPRINTF("%s: unknown command state: %x\n", __func__, pfl->cmd); pfl->wcycle = 0; pfl->cmd = 0; + /* fall through to the read code */ case 0x80: /* We accept reads during second unlock sequence... */ case 0x00: diff --git a/hw/piix_pci.c b/hw/piix_pci.c index 3d79c73fda..6c77e493e4 100644 --- a/hw/piix_pci.c +++ b/hw/piix_pci.c @@ -31,6 +31,7 @@ #include "qemu/range.h" #include "xen.h" #include "pam.h" +#include "sysemu/sysemu.h" /* * I440FX chipset data sheet. @@ -46,6 +47,12 @@ typedef struct I440FXState { #define XEN_PIIX_NUM_PIRQS 128ULL #define PIIX_PIRQC 0x60 +/* + * Reset Control Register: PCI-accessible ISA-Compatible Register at address + * 0xcf9, provided by the PCI/ISA bridge (PIIX3 PCI function 0, 8086:7000). + */ +#define RCR_IOPORT 0xcf9 + typedef struct PIIX3State { PCIDevice dev; @@ -67,6 +74,12 @@ typedef struct PIIX3State { /* This member isn't used. Just for save/load compatibility */ int32_t pci_irq_levels_vmstate[PIIX_NUM_PIRQS]; + + /* Reset Control Register contents */ + uint8_t rcr; + + /* IO memory region for Reset Control Register (RCR_IOPORT) */ + MemoryRegion rcr_mem; } PIIX3State; struct PCII440FXState { @@ -442,6 +455,7 @@ static void piix3_reset(void *opaque) pci_conf[0xae] = 0x00; d->pic_levels = 0; + d->rcr = 0; } static int piix3_post_load(void *opaque, int version_id) @@ -462,6 +476,23 @@ static void piix3_pre_save(void *opaque) } } +static bool piix3_rcr_needed(void *opaque) +{ + PIIX3State *piix3 = opaque; + + return (piix3->rcr != 0); +} + +static const VMStateDescription vmstate_piix3_rcr = { + .name = "PIIX3/rcr", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField []) { + VMSTATE_UINT8(rcr, PIIX3State), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_piix3 = { .name = "PIIX3", .version_id = 3, @@ -469,12 +500,44 @@ static const VMStateDescription vmstate_piix3 = { .minimum_version_id_old = 2, .post_load = piix3_post_load, .pre_save = piix3_pre_save, - .fields = (VMStateField []) { + .fields = (VMStateField[]) { VMSTATE_PCI_DEVICE(dev, PIIX3State), VMSTATE_INT32_ARRAY_V(pci_irq_levels_vmstate, PIIX3State, PIIX_NUM_PIRQS, 3), VMSTATE_END_OF_LIST() + }, + .subsections = (VMStateSubsection[]) { + { + .vmsd = &vmstate_piix3_rcr, + .needed = piix3_rcr_needed, + }, + { 0 } + } +}; + + +static void rcr_write(void *opaque, hwaddr addr, uint64_t val, unsigned len) +{ + PIIX3State *d = opaque; + + if (val & 4) { + qemu_system_reset_request(); + return; } + d->rcr = val & 2; /* keep System Reset type only */ +} + +static uint64_t rcr_read(void *opaque, hwaddr addr, unsigned len) +{ + PIIX3State *d = opaque; + + return d->rcr; +} + +static const MemoryRegionOps rcr_ops = { + .read = rcr_read, + .write = rcr_write, + .endianness = DEVICE_LITTLE_ENDIAN }; static int piix3_initfn(PCIDevice *dev) @@ -482,6 +545,11 @@ static int piix3_initfn(PCIDevice *dev) PIIX3State *d = DO_UPCAST(PIIX3State, dev, dev); isa_bus_new(&d->dev.qdev, pci_address_space_io(dev)); + + memory_region_init_io(&d->rcr_mem, &rcr_ops, d, "piix3-reset-control", 1); + memory_region_add_subregion_overlap(pci_address_space_io(dev), RCR_IOPORT, + &d->rcr_mem, 1); + qemu_register_reset(piix3_reset, d); return 0; } diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index afdcc0e531..f7620509a9 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -2,11 +2,6 @@ obj-y = ppc.o ppc_booke.o # PREP target obj-y += mc146818rtc.o -obj-y += ppc_prep.o -# OldWorld PowerMac -obj-y += ppc_oldworld.o -# NewWorld PowerMac -obj-y += ppc_newworld.o # IBM pSeries (sPAPR) obj-$(CONFIG_PSERIES) += spapr.o spapr_hcall.o spapr_rtas.o spapr_vio.o obj-$(CONFIG_PSERIES) += xics.o spapr_vty.o spapr_llan.o spapr_vscsi.o @@ -28,4 +23,11 @@ obj-y += xilinx_ethlite.o obj-y := $(addprefix ../,$(obj-y)) +# PReP +obj-y += prep.o +# OldWorld PowerMac +obj-y += mac_oldworld.o +# NewWorld PowerMac +obj-y += mac_newworld.o +# e500 obj-$(CONFIG_FDT) += e500.o mpc8544ds.o e500plat.o diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 9ccf4d1840..b7474c05f9 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -297,7 +297,7 @@ static int ppce500_load_device_tree(CPUPPCState *env, snprintf(mpic, sizeof(mpic), "%s/pic@%llx", soc, MPC8544_MPIC_REGS_OFFSET); qemu_devtree_add_subnode(fdt, mpic); qemu_devtree_setprop_string(fdt, mpic, "device_type", "open-pic"); - qemu_devtree_setprop_string(fdt, mpic, "compatible", "chrp,open-pic"); + qemu_devtree_setprop_string(fdt, mpic, "compatible", "fsl,mpic"); qemu_devtree_setprop_cells(fdt, mpic, "reg", MPC8544_MPIC_REGS_OFFSET, 0x40000); qemu_devtree_setprop_cell(fdt, mpic, "#address-cells", 0); @@ -505,7 +505,7 @@ void ppce500_init(PPCE500Params *params) irqs[i][OPENPIC_OUTPUT_CINT] = input[PPCE500_INPUT_CINT]; env->spr[SPR_BOOKE_PIR] = cs->cpu_index = i; env->mpic_iack = MPC8544_CCSRBAR_BASE + - MPC8544_MPIC_REGS_OFFSET + 0x200A0; + MPC8544_MPIC_REGS_OFFSET + 0xa0; ppc_booke_timers_init(cpu, 400000000, PPC_TIMER_E500); @@ -545,7 +545,7 @@ void ppce500_init(PPCE500Params *params) mpic = g_new(qemu_irq, 256); dev = qdev_create(NULL, "openpic"); qdev_prop_set_uint32(dev, "nb_cpus", smp_cpus); - qdev_prop_set_uint32(dev, "model", OPENPIC_MODEL_FSL_MPIC_20); + qdev_prop_set_uint32(dev, "model", params->mpic_version); qdev_init_nofail(dev); s = SYS_BUS_DEVICE(dev); diff --git a/hw/ppc/e500.h b/hw/ppc/e500.h index f5ff27385b..226c93d248 100644 --- a/hw/ppc/e500.h +++ b/hw/ppc/e500.h @@ -16,6 +16,8 @@ typedef struct PPCE500Params { /* required -- must at least add toplevel board compatible */ void (*fixup_devtree)(struct PPCE500Params *params, void *fdt); + + int mpic_version; } PPCE500Params; void ppce500_init(PPCE500Params *params); diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c index 2dcc4a9852..25ac4b1dae 100644 --- a/hw/ppc/e500plat.c +++ b/hw/ppc/e500plat.c @@ -15,6 +15,7 @@ #include "../boards.h" #include "sysemu/device_tree.h" #include "hw/pci/pci.h" +#include "hw/openpic.h" static void e500plat_fixup_devtree(PPCE500Params *params, void *fdt) { @@ -44,6 +45,7 @@ static void e500plat_init(QEMUMachineInitArgs *args) .pci_first_slot = 0x1, .pci_nr_slots = PCI_SLOT_MAX - 1, .fixup_devtree = e500plat_fixup_devtree, + .mpic_version = OPENPIC_MODEL_FSL_MPIC_42, }; ppce500_init(¶ms); @@ -53,7 +55,7 @@ static QEMUMachine e500plat_machine = { .name = "ppce500", .desc = "generic paravirt e500 platform", .init = e500plat_init, - .max_cpus = 15, + .max_cpus = 32, DEFAULT_MACHINE_OPTIONS, }; diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h new file mode 100644 index 0000000000..b17107b797 --- /dev/null +++ b/hw/ppc/mac.h @@ -0,0 +1,181 @@ +/* + * QEMU PowerMac emulation shared definitions and prototypes + * + * Copyright (c) 2004-2007 Fabrice Bellard + * Copyright (c) 2007 Jocelyn Mayer + * + * 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. + */ +#if !defined(__PPC_MAC_H__) +#define __PPC_MAC_H__ + +#include "exec/memory.h" +#include "hw/sysbus.h" +#include "hw/ide/internal.h" +#include "hw/adb.h" + +/* SMP is not enabled, for now */ +#define MAX_CPUS 1 + +#define BIOS_SIZE (1024 * 1024) +#define BIOS_FILENAME "ppc_rom.bin" +#define NVRAM_SIZE 0x2000 +#define PROM_FILENAME "openbios-ppc" +#define PROM_ADDR 0xfff00000 + +#define KERNEL_LOAD_ADDR 0x01000000 +#define KERNEL_GAP 0x00100000 + +#define ESCC_CLOCK 3686400 + +/* Cuda */ +#define TYPE_CUDA "cuda" +#define CUDA(obj) OBJECT_CHECK(CUDAState, (obj), TYPE_CUDA) + +/** + * CUDATimer: + * @counter_value: counter value at load time + */ +typedef struct CUDATimer { + int index; + uint16_t latch; + uint16_t counter_value; + int64_t load_time; + int64_t next_irq_time; + QEMUTimer *timer; +} CUDATimer; + +/** + * CUDAState: + * @b: B-side data + * @a: A-side data + * @dirb: B-side direction (1=output) + * @dira: A-side direction (1=output) + * @sr: Shift register + * @acr: Auxiliary control register + * @pcr: Peripheral control register + * @ifr: Interrupt flag register + * @ier: Interrupt enable register + * @anh: A-side data, no handshake + * @last_b: last value of B register + * @last_acr: last value of ACR register + */ +typedef struct CUDAState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + MemoryRegion mem; + /* cuda registers */ + uint8_t b; + uint8_t a; + uint8_t dirb; + uint8_t dira; + uint8_t sr; + uint8_t acr; + uint8_t pcr; + uint8_t ifr; + uint8_t ier; + uint8_t anh; + + ADBBusState adb_bus; + CUDATimer timers[2]; + + uint32_t tick_offset; + + uint8_t last_b; + uint8_t last_acr; + + int data_in_size; + int data_in_index; + int data_out_index; + + qemu_irq irq; + uint8_t autopoll; + uint8_t data_in[128]; + uint8_t data_out[16]; + QEMUTimer *adb_poll_timer; +} CUDAState; + +/* MacIO */ +#define TYPE_OLDWORLD_MACIO "macio-oldworld" +#define TYPE_NEWWORLD_MACIO "macio-newworld" + +#define TYPE_MACIO_IDE "macio-ide" +#define MACIO_IDE(obj) OBJECT_CHECK(MACIOIDEState, (obj), TYPE_MACIO_IDE) + +typedef struct MACIOIDEState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + qemu_irq irq; + qemu_irq dma_irq; + + MemoryRegion mem; + IDEBus bus; + BlockDriverAIOCB *aiocb; +} MACIOIDEState; + +void macio_ide_init_drives(MACIOIDEState *ide, DriveInfo **hd_table); +void macio_ide_register_dma(MACIOIDEState *ide, void *dbdma, int channel); + +void macio_init(PCIDevice *dev, + MemoryRegion *pic_mem, + MemoryRegion *escc_mem); + +/* Heathrow PIC */ +qemu_irq *heathrow_pic_init(MemoryRegion **pmem, + int nb_cpus, qemu_irq **irqs); + +/* Grackle PCI */ +#define TYPE_GRACKLE_PCI_HOST_BRIDGE "grackle-pcihost" +PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io); + +/* UniNorth PCI */ +PCIBus *pci_pmac_init(qemu_irq *pic, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io); +PCIBus *pci_pmac_u3_init(qemu_irq *pic, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io); + +/* Mac NVRAM */ +#define TYPE_MACIO_NVRAM "macio-nvram" +#define MACIO_NVRAM(obj) \ + OBJECT_CHECK(MacIONVRAMState, (obj), TYPE_MACIO_NVRAM) + +typedef struct MacIONVRAMState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + uint32_t size; + uint32_t it_shift; + + MemoryRegion mem; + uint8_t *data; +} MacIONVRAMState; + +void pmac_format_nvram_partition (MacIONVRAMState *nvr, int len); +uint8_t macio_nvram_read(MacIONVRAMState *s, uint32_t addr); +void macio_nvram_write(MacIONVRAMState *s, uint32_t addr, uint8_t val); +#endif /* !defined(__PPC_MAC_H__) */ diff --git a/hw/ppc_newworld.c b/hw/ppc/mac_newworld.c index b1973f18ff..065ea871b3 100644 --- a/hw/ppc_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -46,28 +46,28 @@ * 0001:05:0c.0 IDE interface [0101]: Broadcom K2 SATA [1166:0240] * */ -#include "hw.h" -#include "ppc.h" -#include "ppc_mac.h" -#include "adb.h" -#include "mac_dbdma.h" -#include "nvram.h" -#include "pci/pci.h" +#include "hw/hw.h" +#include "hw/ppc.h" +#include "hw/ppc/mac.h" +#include "hw/adb.h" +#include "hw/mac_dbdma.h" +#include "hw/nvram.h" +#include "hw/pci/pci.h" #include "net/net.h" #include "sysemu/sysemu.h" -#include "boards.h" -#include "fw_cfg.h" -#include "escc.h" -#include "openpic.h" -#include "ide.h" -#include "loader.h" +#include "hw/boards.h" +#include "hw/fw_cfg.h" +#include "hw/escc.h" +#include "hw/openpic.h" +#include "hw/ide.h" +#include "hw/loader.h" #include "elf.h" #include "sysemu/kvm.h" #include "kvm_ppc.h" #include "hw/usb.h" #include "sysemu/blockdev.h" #include "exec/address-spaces.h" -#include "sysbus.h" +#include "hw/sysbus.h" #define MAX_IDE_BUS 2 #define CFG_ADDR 0xf0000510 @@ -147,15 +147,16 @@ static void ppc_core99_init(QEMUMachineInitArgs *args) hwaddr kernel_base, initrd_base, cmdline_base = 0; long kernel_size, initrd_size; PCIBus *pci_bus; + PCIDevice *macio; + MACIOIDEState *macio_ide; + BusState *adb_bus; MacIONVRAMState *nvr; int bios_size; - MemoryRegion *pic_mem, *dbdma_mem, *cuda_mem, *escc_mem; + MemoryRegion *pic_mem, *escc_mem; MemoryRegion *escc_bar = g_new(MemoryRegion, 1); - MemoryRegion *ide_mem[3]; int ppc_boot_device; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; void *fw_cfg; - void *dbdma; int machine_arch; SysBusDevice *s; DeviceState *dev; @@ -362,20 +363,31 @@ static void ppc_core99_init(QEMUMachineInitArgs *args) pci_nic_init_nofail(&nd_table[i], "ne2k_pci", NULL); ide_drive_get(hd, MAX_IDE_BUS); - dbdma = DBDMA_init(&dbdma_mem); - /* We only emulate 2 out of 3 IDE controllers for now */ - ide_mem[0] = NULL; - ide_mem[1] = pmac_ide_init(hd, pic[0x0d], dbdma, 0x16, pic[0x02]); - ide_mem[2] = pmac_ide_init(&hd[MAX_IDE_DEVS], pic[0x0e], dbdma, 0x1a, pic[0x02]); + macio = pci_create(pci_bus, -1, TYPE_NEWWORLD_MACIO); + dev = DEVICE(macio); + qdev_connect_gpio_out(dev, 0, pic[0x19]); /* CUDA */ + qdev_connect_gpio_out(dev, 1, pic[0x0d]); /* IDE */ + qdev_connect_gpio_out(dev, 2, pic[0x02]); /* IDE DMA */ + qdev_connect_gpio_out(dev, 3, pic[0x0e]); /* IDE */ + qdev_connect_gpio_out(dev, 4, pic[0x02]); /* IDE DMA */ + macio_init(macio, pic_mem, escc_bar); - cuda_init(&cuda_mem, pic[0x19]); + /* We only emulate 2 out of 3 IDE controllers for now */ + macio_ide = MACIO_IDE(object_resolve_path_component(OBJECT(macio), + "ide[0]")); + macio_ide_init_drives(macio_ide, hd); - adb_kbd_init(&adb_bus); - adb_mouse_init(&adb_bus); + macio_ide = MACIO_IDE(object_resolve_path_component(OBJECT(macio), + "ide[1]")); + macio_ide_init_drives(macio_ide, &hd[MAX_IDE_DEVS]); - macio_init(pci_bus, PCI_DEVICE_ID_APPLE_UNI_N_KEYL, 0, pic_mem, - dbdma_mem, cuda_mem, NULL, 3, ide_mem, escc_bar); + dev = DEVICE(object_resolve_path_component(OBJECT(macio), "cuda")); + adb_bus = qdev_get_child_bus(dev, "adb.0"); + dev = qdev_create(adb_bus, TYPE_ADB_KEYBOARD); + qdev_init_nofail(dev); + dev = qdev_create(adb_bus, TYPE_ADB_MOUSE); + qdev_init_nofail(dev); if (usb_enabled(machine_arch == ARCH_MAC99_U3)) { pci_create_simple(pci_bus, -1, "pci-ohci"); @@ -391,12 +403,17 @@ static void ppc_core99_init(QEMUMachineInitArgs *args) graphic_depth = 15; /* The NewWorld NVRAM is not located in the MacIO device */ - nvr = macio_nvram_init(0x2000, 1); + dev = qdev_create(NULL, TYPE_MACIO_NVRAM); + qdev_prop_set_uint32(dev, "size", 0x2000); + qdev_prop_set_uint32(dev, "it_shift", 1); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xFFF04000); + nvr = MACIO_NVRAM(dev); pmac_format_nvram_partition(nvr, 0x2000); - macio_nvram_setup_bar(nvr, get_system_memory(), 0xFFF04000); /* No PCI init: the BIOS will do it */ fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2); + fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1); fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, machine_arch); diff --git a/hw/ppc_oldworld.c b/hw/ppc/mac_oldworld.c index de34e7530a..2778e45879 100644 --- a/hw/ppc_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -23,21 +23,20 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "ppc.h" -#include "ppc_mac.h" -#include "adb.h" -#include "mac_dbdma.h" -#include "nvram.h" +#include "hw/hw.h" +#include "hw/ppc.h" +#include "mac.h" +#include "hw/adb.h" +#include "hw/nvram.h" #include "sysemu/sysemu.h" #include "net/net.h" -#include "isa.h" -#include "pci/pci.h" -#include "boards.h" -#include "fw_cfg.h" -#include "escc.h" -#include "ide.h" -#include "loader.h" +#include "hw/isa.h" +#include "hw/pci/pci.h" +#include "hw/boards.h" +#include "hw/fw_cfg.h" +#include "hw/escc.h" +#include "hw/ide.h" +#include "hw/loader.h" #include "elf.h" #include "sysemu/kvm.h" #include "kvm_ppc.h" @@ -90,14 +89,16 @@ static void ppc_heathrow_init(QEMUMachineInitArgs *args) uint32_t kernel_base, initrd_base, cmdline_base = 0; int32_t kernel_size, initrd_size; PCIBus *pci_bus; - MacIONVRAMState *nvr; + PCIDevice *macio; + MACIOIDEState *macio_ide; + DeviceState *dev; + BusState *adb_bus; int bios_size; - MemoryRegion *pic_mem, *dbdma_mem, *cuda_mem; - MemoryRegion *escc_mem, *escc_bar = g_new(MemoryRegion, 1), *ide_mem[2]; + MemoryRegion *pic_mem; + MemoryRegion *escc_mem, *escc_bar = g_new(MemoryRegion, 1); uint16_t ppc_boot_device; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; void *fw_cfg; - void *dbdma; linux_boot = (kernel_filename != NULL); @@ -263,10 +264,17 @@ static void ppc_heathrow_init(QEMUMachineInitArgs *args) ide_drive_get(hd, MAX_IDE_BUS); + macio = pci_create(pci_bus, -1, TYPE_OLDWORLD_MACIO); + dev = DEVICE(macio); + qdev_connect_gpio_out(dev, 0, pic[0x12]); /* CUDA */ + qdev_connect_gpio_out(dev, 1, pic[0x0D]); /* IDE */ + qdev_connect_gpio_out(dev, 2, pic[0x02]); /* IDE DMA */ + macio_init(macio, pic_mem, escc_bar); + /* First IDE channel is a MAC IDE on the MacIO bus */ - dbdma = DBDMA_init(&dbdma_mem); - ide_mem[0] = NULL; - ide_mem[1] = pmac_ide_init(hd, pic[0x0D], dbdma, 0x16, pic[0x02]); + macio_ide = MACIO_IDE(object_resolve_path_component(OBJECT(macio), + "ide")); + macio_ide_init_drives(macio_ide, hd); /* Second IDE channel is a CMD646 on the PCI bus */ hd[0] = hd[MAX_IDE_DEVS]; @@ -274,17 +282,12 @@ static void ppc_heathrow_init(QEMUMachineInitArgs *args) hd[3] = hd[2] = NULL; pci_cmd646_ide_init(pci_bus, hd, 0); - /* cuda also initialize ADB */ - cuda_init(&cuda_mem, pic[0x12]); - - adb_kbd_init(&adb_bus); - adb_mouse_init(&adb_bus); - - nvr = macio_nvram_init(0x2000, 4); - pmac_format_nvram_partition(nvr, 0x2000); - - macio_init(pci_bus, PCI_DEVICE_ID_APPLE_343S1201, 1, pic_mem, - dbdma_mem, cuda_mem, nvr, 2, ide_mem, escc_bar); + dev = DEVICE(object_resolve_path_component(OBJECT(macio), "cuda")); + adb_bus = qdev_get_child_bus(dev, "adb.0"); + dev = qdev_create(adb_bus, TYPE_ADB_KEYBOARD); + qdev_init_nofail(dev); + dev = qdev_create(adb_bus, TYPE_ADB_MOUSE); + qdev_init_nofail(dev); if (usb_enabled(false)) { pci_create_simple(pci_bus, -1, "pci-ohci"); @@ -296,6 +299,7 @@ static void ppc_heathrow_init(QEMUMachineInitArgs *args) /* No PCI init: the BIOS will do it */ fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2); + fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1); fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, ARCH_HEATHROW); diff --git a/hw/ppc/mpc8544ds.c b/hw/ppc/mpc8544ds.c index 8e05e55c87..e25c70b1f3 100644 --- a/hw/ppc/mpc8544ds.c +++ b/hw/ppc/mpc8544ds.c @@ -14,6 +14,7 @@ #include "e500.h" #include "../boards.h" #include "sysemu/device_tree.h" +#include "hw/openpic.h" static void mpc8544ds_fixup_devtree(PPCE500Params *params, void *fdt) { @@ -43,6 +44,7 @@ static void mpc8544ds_init(QEMUMachineInitArgs *args) .pci_first_slot = 0x11, .pci_nr_slots = 2, .fixup_devtree = mpc8544ds_fixup_devtree, + .mpic_version = OPENPIC_MODEL_FSL_MPIC_20, }; ppce500_init(¶ms); diff --git a/hw/ppc_prep.c b/hw/ppc/prep.c index a35fbedbdc..e06dded003 100644 --- a/hw/ppc_prep.c +++ b/hw/ppc/prep.c @@ -21,23 +21,23 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "nvram.h" -#include "pc.h" -#include "serial.h" -#include "fdc.h" +#include "hw/hw.h" +#include "hw/nvram.h" +#include "hw/pc.h" +#include "hw/serial.h" +#include "hw/fdc.h" #include "net/net.h" #include "sysemu/sysemu.h" -#include "isa.h" -#include "pci/pci.h" -#include "pci/pci_host.h" -#include "ppc.h" -#include "boards.h" +#include "hw/isa.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" +#include "hw/ppc.h" +#include "hw/boards.h" #include "qemu/log.h" -#include "ide.h" -#include "loader.h" -#include "mc146818rtc.h" -#include "pc87312.h" +#include "hw/ide.h" +#include "hw/loader.h" +#include "hw/mc146818rtc.h" +#include "hw/pc87312.h" #include "sysemu/blockdev.h" #include "sysemu/arch_init.h" #include "exec/address-spaces.h" diff --git a/hw/ppc_mac.h b/hw/ppc_mac.h deleted file mode 100644 index 89c7d66386..0000000000 --- a/hw/ppc_mac.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * QEMU PowerMac emulation shared definitions and prototypes - * - * Copyright (c) 2004-2007 Fabrice Bellard - * Copyright (c) 2007 Jocelyn Mayer - * - * 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. - */ -#if !defined(__PPC_MAC_H__) -#define __PPC_MAC_H__ - -#include "exec/memory.h" - -/* SMP is not enabled, for now */ -#define MAX_CPUS 1 - -#define BIOS_SIZE (1024 * 1024) -#define BIOS_FILENAME "ppc_rom.bin" -#define NVRAM_SIZE 0x2000 -#define PROM_FILENAME "openbios-ppc" -#define PROM_ADDR 0xfff00000 - -#define KERNEL_LOAD_ADDR 0x01000000 -#define KERNEL_GAP 0x00100000 - -#define ESCC_CLOCK 3686400 - -/* Cuda */ -void cuda_init (MemoryRegion **cuda_mem, qemu_irq irq); - -/* MacIO */ -void macio_init (PCIBus *bus, int device_id, int is_oldworld, - MemoryRegion *pic_mem, MemoryRegion *dbdma_mem, - MemoryRegion *cuda_mem, void *nvram, - int nb_ide, MemoryRegion **ide_mem, MemoryRegion *escc_mem); - -/* Heathrow PIC */ -qemu_irq *heathrow_pic_init(MemoryRegion **pmem, - int nb_cpus, qemu_irq **irqs); - -/* Grackle PCI */ -#define TYPE_GRACKLE_PCI_HOST_BRIDGE "grackle-pcihost" -PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io); - -/* UniNorth PCI */ -PCIBus *pci_pmac_init(qemu_irq *pic, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io); -PCIBus *pci_pmac_u3_init(qemu_irq *pic, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io); - -/* Mac NVRAM */ -typedef struct MacIONVRAMState MacIONVRAMState; - -MacIONVRAMState *macio_nvram_init (hwaddr size, - unsigned int it_shift); -void macio_nvram_setup_bar(MacIONVRAMState *s, MemoryRegion *bar, - hwaddr mem_base); -void pmac_format_nvram_partition (MacIONVRAMState *nvr, int len); -uint32_t macio_nvram_read (void *opaque, uint32_t addr); -void macio_nvram_write (void *opaque, uint32_t addr, uint32_t val); -#endif /* !defined(__PPC_MAC_H__) */ diff --git a/hw/prep_pci.c b/hw/prep_pci.c index 212a2ac4f1..52ee5d9401 100644 --- a/hw/prep_pci.c +++ b/hw/prep_pci.c @@ -2,6 +2,7 @@ * QEMU PREP PCI host * * Copyright (c) 2006 Fabrice Bellard + * Copyright (c) 2011-2013 Andreas Färber * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,12 +25,21 @@ #include "hw.h" #include "pci/pci.h" +#include "pci/pci_bus.h" #include "pci/pci_host.h" #include "pc.h" #include "exec/address-spaces.h" +#define TYPE_RAVEN_PCI_DEVICE "raven" #define TYPE_RAVEN_PCI_HOST_BRIDGE "raven-pcihost" +#define RAVEN_PCI_DEVICE(obj) \ + OBJECT_CHECK(RavenPCIState, (obj), TYPE_RAVEN_PCI_DEVICE) + +typedef struct RavenPCIState { + PCIDevice dev; +} RavenPCIState; + #define RAVEN_PCI_HOST_BRIDGE(obj) \ OBJECT_CHECK(PREPPCIState, (obj), TYPE_RAVEN_PCI_HOST_BRIDGE) @@ -38,12 +48,10 @@ typedef struct PRePPCIState { MemoryRegion intack; qemu_irq irq[4]; + PCIBus pci_bus; + RavenPCIState pci_dev; } PREPPCIState; -typedef struct RavenPCIState { - PCIDevice dev; -} RavenPCIState; - static inline uint32_t PPC_PCIIO_config(hwaddr addr) { int i; @@ -103,23 +111,19 @@ static void prep_set_irq(void *opaque, int irq_num, int level) qemu_set_irq(pic[irq_num] , level); } -static int raven_pcihost_init(SysBusDevice *dev) +static void raven_pcihost_realizefn(DeviceState *d, Error **errp) { + SysBusDevice *dev = SYS_BUS_DEVICE(d); PCIHostState *h = PCI_HOST_BRIDGE(dev); PREPPCIState *s = RAVEN_PCI_HOST_BRIDGE(dev); MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *address_space_io = get_system_io(); - PCIBus *bus; int i; for (i = 0; i < 4; i++) { sysbus_init_irq(dev, &s->irq[i]); } - bus = pci_register_bus(DEVICE(dev), NULL, - prep_set_irq, prep_map_irq, s->irq, - address_space_mem, address_space_io, 0, 4); - h->bus = bus; + pci_bus_irqs(&s->pci_bus, prep_set_irq, prep_map_irq, s->irq, 4); memory_region_init_io(&h->conf_mem, &pci_host_conf_be_ops, s, "pci-conf-idx", 1); @@ -136,9 +140,29 @@ static int raven_pcihost_init(SysBusDevice *dev) memory_region_init_io(&s->intack, &PPC_intack_ops, s, "pci-intack", 1); memory_region_add_subregion(address_space_mem, 0xbffffff0, &s->intack); - pci_create_simple(bus, 0, "raven"); - return 0; + /* TODO Remove once realize propagates to child devices. */ + object_property_set_bool(OBJECT(&s->pci_dev), true, "realized", errp); +} + +static void raven_pcihost_initfn(Object *obj) +{ + PCIHostState *h = PCI_HOST_BRIDGE(obj); + PREPPCIState *s = RAVEN_PCI_HOST_BRIDGE(obj); + MemoryRegion *address_space_mem = get_system_memory(); + MemoryRegion *address_space_io = get_system_io(); + DeviceState *pci_dev; + + pci_bus_new_inplace(&s->pci_bus, DEVICE(obj), NULL, + address_space_mem, address_space_io, 0); + h->bus = &s->pci_bus; + + object_initialize(&s->pci_dev, TYPE_RAVEN_PCI_DEVICE); + pci_dev = DEVICE(&s->pci_dev); + qdev_set_parent_bus(pci_dev, BUS(&s->pci_bus)); + object_property_set_int(OBJECT(&s->pci_dev), PCI_DEVFN(0, 0), "addr", + NULL); + qdev_prop_set_bit(pci_dev, "multifunction", false); } static int raven_init(PCIDevice *d) @@ -176,7 +200,7 @@ static void raven_class_init(ObjectClass *klass, void *data) } static const TypeInfo raven_info = { - .name = "raven", + .name = TYPE_RAVEN_PCI_DEVICE, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(RavenPCIState), .class_init = raven_class_init, @@ -184,10 +208,9 @@ static const TypeInfo raven_info = { static void raven_pcihost_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - k->init = raven_pcihost_init; + dc->realize = raven_pcihost_realizefn; dc->fw_name = "pci"; dc->no_user = 1; } @@ -196,6 +219,7 @@ static const TypeInfo raven_pcihost_info = { .name = TYPE_RAVEN_PCI_HOST_BRIDGE, .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(PREPPCIState), + .instance_init = raven_pcihost_initfn, .class_init = raven_pcihost_class_init, }; diff --git a/hw/pxa2xx.c b/hw/pxa2xx.c index 2367c6a4a4..d303320d42 100644 --- a/hw/pxa2xx.c +++ b/hw/pxa2xx.c @@ -343,23 +343,23 @@ static int pxa2xx_cpccnt_read(CPUARMState *env, const ARMCPRegInfo *ri, } static const ARMCPRegInfo pxa_cp_reginfo[] = { - /* cp14 crn==1: perf registers */ - { .name = "CPPMNC", .cp = 14, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 0, + /* cp14 crm==1: perf registers */ + { .name = "CPPMNC", .cp = 14, .crn = 0, .crm = 1, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .readfn = pxa2xx_cppmnc_read, .writefn = pxa2xx_cppmnc_write }, { .name = "CPCCNT", .cp = 14, .crn = 1, .crm = 1, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .readfn = pxa2xx_cpccnt_read, .writefn = arm_cp_write_ignore }, - { .name = "CPINTEN", .cp = 14, .crn = 1, .crm = 4, .opc1 = 0, .opc2 = 0, + { .name = "CPINTEN", .cp = 14, .crn = 4, .crm = 1, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "CPFLAG", .cp = 14, .crn = 1, .crm = 5, .opc1 = 0, .opc2 = 0, + { .name = "CPFLAG", .cp = 14, .crn = 5, .crm = 1, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "CPEVTSEL", .cp = 14, .crn = 1, .crm = 8, .opc1 = 0, .opc2 = 0, + { .name = "CPEVTSEL", .cp = 14, .crn = 8, .crm = 1, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - /* cp14 crn==2: performance count registers */ - { .name = "CPPMN0", .cp = 14, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0, + /* cp14 crm==2: performance count registers */ + { .name = "CPPMN0", .cp = 14, .crn = 0, .crm = 2, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "CPPMN1", .cp = 14, .crn = 2, .crm = 1, .opc1 = 0, .opc2 = 0, + { .name = "CPPMN1", .cp = 14, .crn = 1, .crm = 2, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "CPPMN2", .cp = 14, .crn = 2, .crm = 2, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, @@ -1468,7 +1468,7 @@ PXA2xxI2CState *pxa2xx_i2c_init(hwaddr base, s = FROM_SYSBUS(PXA2xxI2CState, i2c_dev); /* FIXME: Should the slave device really be on a separate bus? */ dev = i2c_create_slave(i2c_init_bus(NULL, "dummy"), "pxa2xx-i2c-slave", 0); - s->slave = FROM_I2C_SLAVE(PXA2xxI2CSlaveState, I2C_SLAVE_FROM_QDEV(dev)); + s->slave = FROM_I2C_SLAVE(PXA2xxI2CSlaveState, I2C_SLAVE(dev)); s->slave->host = s; return s; diff --git a/hw/pxa2xx_timer.c b/hw/pxa2xx_timer.c index 32c1872680..5c9d2e8bc6 100644 --- a/hw/pxa2xx_timer.c +++ b/hw/pxa2xx_timer.c @@ -157,17 +157,27 @@ static uint64_t pxa2xx_timer_read(void *opaque, hwaddr offset, switch (offset) { case OSMR3: tm ++; + /* fall through */ case OSMR2: tm ++; + /* fall through */ case OSMR1: tm ++; + /* fall through */ case OSMR0: return s->timer[tm].value; case OSMR11: tm ++; + /* fall through */ case OSMR10: tm ++; + /* fall through */ case OSMR9: tm ++; + /* fall through */ case OSMR8: tm ++; + /* fall through */ case OSMR7: tm ++; + /* fall through */ case OSMR6: tm ++; + /* fall through */ case OSMR5: tm ++; + /* fall through */ case OSMR4: if (!pxa2xx_timer_has_tm4(s)) goto badreg; @@ -176,12 +186,19 @@ static uint64_t pxa2xx_timer_read(void *opaque, hwaddr offset, return s->clock + muldiv64(qemu_get_clock_ns(vm_clock) - s->lastload, s->freq, get_ticks_per_sec()); case OSCR11: tm ++; + /* fall through */ case OSCR10: tm ++; + /* fall through */ case OSCR9: tm ++; + /* fall through */ case OSCR8: tm ++; + /* fall through */ case OSCR7: tm ++; + /* fall through */ case OSCR6: tm ++; + /* fall through */ case OSCR5: tm ++; + /* fall through */ case OSCR4: if (!pxa2xx_timer_has_tm4(s)) goto badreg; @@ -207,12 +224,19 @@ static uint64_t pxa2xx_timer_read(void *opaque, hwaddr offset, case OWER: return s->reset3; case OMCR11: tm ++; + /* fall through */ case OMCR10: tm ++; + /* fall through */ case OMCR9: tm ++; + /* fall through */ case OMCR8: tm ++; + /* fall through */ case OMCR7: tm ++; + /* fall through */ case OMCR6: tm ++; + /* fall through */ case OMCR5: tm ++; + /* fall through */ case OMCR4: if (!pxa2xx_timer_has_tm4(s)) goto badreg; @@ -235,19 +259,29 @@ static void pxa2xx_timer_write(void *opaque, hwaddr offset, switch (offset) { case OSMR3: tm ++; + /* fall through */ case OSMR2: tm ++; + /* fall through */ case OSMR1: tm ++; + /* fall through */ case OSMR0: s->timer[tm].value = value; pxa2xx_timer_update(s, qemu_get_clock_ns(vm_clock)); break; case OSMR11: tm ++; + /* fall through */ case OSMR10: tm ++; + /* fall through */ case OSMR9: tm ++; + /* fall through */ case OSMR8: tm ++; + /* fall through */ case OSMR7: tm ++; + /* fall through */ case OSMR6: tm ++; + /* fall through */ case OSMR5: tm ++; + /* fall through */ case OSMR4: if (!pxa2xx_timer_has_tm4(s)) goto badreg; @@ -261,12 +295,19 @@ static void pxa2xx_timer_write(void *opaque, hwaddr offset, pxa2xx_timer_update(s, s->lastload); break; case OSCR11: tm ++; + /* fall through */ case OSCR10: tm ++; + /* fall through */ case OSCR9: tm ++; + /* fall through */ case OSCR8: tm ++; + /* fall through */ case OSCR7: tm ++; + /* fall through */ case OSCR6: tm ++; + /* fall through */ case OSCR5: tm ++; + /* fall through */ case OSCR4: if (!pxa2xx_timer_has_tm4(s)) goto badreg; @@ -291,8 +332,11 @@ static void pxa2xx_timer_write(void *opaque, hwaddr offset, s->reset3 = value; break; case OMCR7: tm ++; + /* fall through */ case OMCR6: tm ++; + /* fall through */ case OMCR5: tm ++; + /* fall through */ case OMCR4: if (!pxa2xx_timer_has_tm4(s)) goto badreg; @@ -306,8 +350,11 @@ static void pxa2xx_timer_write(void *opaque, hwaddr offset, } break; case OMCR11: tm ++; + /* fall through */ case OMCR10: tm ++; + /* fall through */ case OMCR9: tm ++; + /* fall through */ case OMCR8: tm += 4; if (!pxa2xx_timer_has_tm4(s)) goto badreg; diff --git a/hw/qdev-core.h b/hw/qdev-core.h index d1b8e37d80..2486f36853 100644 --- a/hw/qdev-core.h +++ b/hw/qdev-core.h @@ -231,7 +231,7 @@ DeviceState *qdev_find_recursive(BusState *bus, const char *id); typedef int (qbus_walkerfn)(BusState *bus, void *opaque); typedef int (qdev_walkerfn)(DeviceState *dev, void *opaque); -void qbus_create_inplace(BusState *bus, const char *typename, +void qbus_create_inplace(void *bus, const char *typename, DeviceState *parent, const char *name); BusState *qbus_create(const char *typename, DeviceState *parent, const char *name); /* Returns > 0 if either devfn or busfn skip walk somewhere in cursion, diff --git a/hw/qdev-monitor.c b/hw/qdev-monitor.c index 4e2a92b9dd..4f9a6eb39a 100644 --- a/hw/qdev-monitor.c +++ b/hw/qdev-monitor.c @@ -591,6 +591,7 @@ int do_device_add(Monitor *mon, const QDict *qdict, QObject **ret_data) { Error *local_err = NULL; QemuOpts *opts; + DeviceState *dev; opts = qemu_opts_from_qdict(qemu_find_opts("device"), qdict, &local_err); if (error_is_set(&local_err)) { @@ -602,10 +603,12 @@ int do_device_add(Monitor *mon, const QDict *qdict, QObject **ret_data) qemu_opts_del(opts); return 0; } - if (!qdev_device_add(opts)) { + dev = qdev_device_add(opts); + if (!dev) { qemu_opts_del(opts); return -1; } + object_unref(OBJECT(dev)); return 0; } diff --git a/hw/qdev-properties-system.c b/hw/qdev-properties-system.c index ce0f7933e6..ce3af22193 100644 --- a/hw/qdev-properties-system.c +++ b/hw/qdev-properties-system.c @@ -173,16 +173,47 @@ PropertyInfo qdev_prop_chr = { static int parse_netdev(DeviceState *dev, const char *str, void **ptr) { - NetClientState *netdev = qemu_find_netdev(str); + NICPeers *peers_ptr = (NICPeers *)ptr; + NICConf *conf = container_of(peers_ptr, NICConf, peers); + NetClientState **ncs = peers_ptr->ncs; + NetClientState *peers[MAX_QUEUE_NUM]; + int queues, i = 0; + int ret; - if (netdev == NULL) { - return -ENOENT; + queues = qemu_find_net_clients_except(str, peers, + NET_CLIENT_OPTIONS_KIND_NIC, + MAX_QUEUE_NUM); + if (queues == 0) { + ret = -ENOENT; + goto err; } - if (netdev->peer) { - return -EEXIST; + + if (queues > MAX_QUEUE_NUM) { + ret = -E2BIG; + goto err; + } + + for (i = 0; i < queues; i++) { + if (peers[i] == NULL) { + ret = -ENOENT; + goto err; + } + + if (peers[i]->peer) { + ret = -EEXIST; + goto err; + } + + ncs[i] = peers[i]; + ncs[i]->queue_index = i; } - *ptr = netdev; + + conf->queues = queues; + return 0; + +err: + return ret; } static const char *print_netdev(void *ptr) @@ -249,7 +280,8 @@ static void set_vlan(Object *obj, Visitor *v, void *opaque, { DeviceState *dev = DEVICE(obj); Property *prop = opaque; - NetClientState **ptr = qdev_get_prop_ptr(dev, prop); + NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop); + NetClientState **ptr = &peers_ptr->ncs[0]; Error *local_err = NULL; int32_t id; NetClientState *hubport; diff --git a/hw/qdev-properties.h b/hw/qdev-properties.h index ddcf774506..20c67f3443 100644 --- a/hw/qdev-properties.h +++ b/hw/qdev-properties.h @@ -31,7 +31,7 @@ extern PropertyInfo qdev_prop_pci_host_devaddr; .name = (_name), \ .info = &(_prop), \ .offset = offsetof(_state, _field) \ - + type_check(_type,typeof_field(_state, _field)), \ + + type_check(_type, typeof_field(_state, _field)), \ } #define DEFINE_PROP_DEFAULT(_name, _state, _field, _defval, _prop, _type) { \ .name = (_name), \ @@ -77,9 +77,9 @@ extern PropertyInfo qdev_prop_pci_host_devaddr; #define DEFINE_PROP_STRING(_n, _s, _f) \ DEFINE_PROP(_n, _s, _f, qdev_prop_string, char*) #define DEFINE_PROP_NETDEV(_n, _s, _f) \ - DEFINE_PROP(_n, _s, _f, qdev_prop_netdev, NetClientState*) + DEFINE_PROP(_n, _s, _f, qdev_prop_netdev, NICPeers) #define DEFINE_PROP_VLAN(_n, _s, _f) \ - DEFINE_PROP(_n, _s, _f, qdev_prop_vlan, NetClientState*) + DEFINE_PROP(_n, _s, _f, qdev_prop_vlan, NICPeers) #define DEFINE_PROP_DRIVE(_n, _s, _f) \ DEFINE_PROP(_n, _s, _f, qdev_prop_drive, BlockDriverState *) #define DEFINE_PROP_MACADDR(_n, _s, _f) \ @@ -64,7 +64,10 @@ static void bus_remove_child(BusState *bus, DeviceState *child) snprintf(name, sizeof(name), "child[%d]", kid->index); QTAILQ_REMOVE(&bus->children, kid, sibling); + + /* This gives back ownership of kid->child back to us. */ object_property_del(OBJECT(bus), name, NULL); + object_unref(OBJECT(kid->child)); g_free(kid); return; } @@ -82,9 +85,11 @@ static void bus_add_child(BusState *bus, DeviceState *child) kid->index = bus->max_index++; kid->child = child; + object_ref(OBJECT(kid->child)); QTAILQ_INSERT_HEAD(&bus->children, kid, sibling); + /* This transfers ownership of kid->child to the property. */ snprintf(name, sizeof(name), "child[%d]", kid->index); object_property_add_link(OBJECT(bus), name, object_get_typename(OBJECT(child)), @@ -95,6 +100,7 @@ static void bus_add_child(BusState *bus, DeviceState *child) void qdev_set_parent_bus(DeviceState *dev, BusState *bus) { dev->parent_bus = bus; + object_ref(OBJECT(bus)); bus_add_child(bus, dev); } @@ -137,7 +143,7 @@ DeviceState *qdev_try_create(BusState *bus, const char *type) } qdev_set_parent_bus(dev, bus); - + object_unref(OBJECT(dev)); return dev; } @@ -261,7 +267,7 @@ void qdev_init_nofail(DeviceState *dev) /* Unlink device from bus and free the structure. */ void qdev_free(DeviceState *dev) { - object_delete(OBJECT(dev)); + object_unparent(OBJECT(dev)); } void qdev_machine_creation_done(void) @@ -390,14 +396,16 @@ DeviceState *qdev_find_recursive(BusState *bus, const char *id) return NULL; } -static void qbus_realize(BusState *bus) +static void qbus_realize(BusState *bus, DeviceState *parent, const char *name) { const char *typename = object_get_typename(OBJECT(bus)); char *buf; int i,len; - if (bus->name) { - /* use supplied name */ + bus->parent = parent; + + if (name) { + bus->name = g_strdup(name); } else if (bus->parent && bus->parent->id) { /* parent device has id -> use it for bus name */ len = strlen(bus->parent->id) + 16; @@ -419,6 +427,7 @@ static void qbus_realize(BusState *bus) QLIST_INSERT_HEAD(&bus->parent->child_bus, bus, sibling); bus->parent->num_child_bus++; object_property_add_child(OBJECT(bus->parent), bus->name, OBJECT(bus), NULL); + object_unref(OBJECT(bus)); } else if (bus != sysbus_get_default()) { /* TODO: once all bus devices are qdevified, only reset handler for main_system_bus should be registered here. */ @@ -426,14 +435,30 @@ static void qbus_realize(BusState *bus) } } -void qbus_create_inplace(BusState *bus, const char *typename, +static void bus_unparent(Object *obj) +{ + BusState *bus = BUS(obj); + BusChild *kid; + + while ((kid = QTAILQ_FIRST(&bus->children)) != NULL) { + DeviceState *dev = kid->child; + qdev_free(dev); + } + if (bus->parent) { + QLIST_REMOVE(bus, sibling); + bus->parent->num_child_bus--; + bus->parent = NULL; + } else { + assert(bus != sysbus_get_default()); /* main_system_bus is never freed */ + qemu_unregister_reset(qbus_reset_all_fn, bus); + } +} + +void qbus_create_inplace(void *bus, const char *typename, DeviceState *parent, const char *name) { object_initialize(bus, typename); - - bus->parent = parent; - bus->name = name ? g_strdup(name) : NULL; - qbus_realize(bus); + qbus_realize(bus, parent, name); } BusState *qbus_create(const char *typename, DeviceState *parent, const char *name) @@ -441,17 +466,14 @@ BusState *qbus_create(const char *typename, DeviceState *parent, const char *nam BusState *bus; bus = BUS(object_new(typename)); - - bus->parent = parent; - bus->name = name ? g_strdup(name) : NULL; - qbus_realize(bus); + qbus_realize(bus, parent, name); return bus; } void qbus_free(BusState *bus) { - object_delete(OBJECT(bus)); + object_unparent(OBJECT(bus)); } static char *bus_get_fw_dev_path(BusState *bus, DeviceState *dev) @@ -718,23 +740,8 @@ static void device_initfn(Object *obj) static void device_finalize(Object *obj) { DeviceState *dev = DEVICE(obj); - BusState *bus; - DeviceClass *dc = DEVICE_GET_CLASS(dev); - - if (dev->realized) { - while (dev->num_child_bus) { - bus = QLIST_FIRST(&dev->child_bus); - qbus_free(bus); - } - if (qdev_get_vmsd(dev)) { - vmstate_unregister(dev, qdev_get_vmsd(dev), dev); - } - if (dc->exit) { - dc->exit(dev); - } - if (dev->opts) { - qemu_opts_del(dev->opts); - } + if (dev->opts) { + qemu_opts_del(dev->opts); } } @@ -751,9 +758,25 @@ static void device_class_base_init(ObjectClass *class, void *data) static void device_unparent(Object *obj) { DeviceState *dev = DEVICE(obj); + DeviceClass *dc = DEVICE_GET_CLASS(dev); + BusState *bus; - if (dev->parent_bus != NULL) { + while (dev->num_child_bus) { + bus = QLIST_FIRST(&dev->child_bus); + qbus_free(bus); + } + if (dev->realized) { + if (qdev_get_vmsd(dev)) { + vmstate_unregister(dev, qdev_get_vmsd(dev), dev); + } + if (dc->exit) { + dc->exit(dev); + } + } + if (dev->parent_bus) { bus_remove_child(dev->parent_bus, dev); + object_unref(OBJECT(dev->parent_bus)); + dev->parent_bus = NULL; } } @@ -804,22 +827,15 @@ static void qbus_initfn(Object *obj) QTAILQ_INIT(&bus->children); } +static void bus_class_init(ObjectClass *class, void *data) +{ + class->unparent = bus_unparent; +} + static void qbus_finalize(Object *obj) { BusState *bus = BUS(obj); - BusChild *kid; - while ((kid = QTAILQ_FIRST(&bus->children)) != NULL) { - DeviceState *dev = kid->child; - qdev_free(dev); - } - if (bus->parent) { - QLIST_REMOVE(bus, sibling); - bus->parent->num_child_bus--; - } else { - assert(bus != sysbus_get_default()); /* main_system_bus is never freed */ - qemu_unregister_reset(qbus_reset_all_fn, bus); - } g_free((char *)bus->name); } @@ -831,6 +847,7 @@ static const TypeInfo bus_info = { .class_size = sizeof(BusClass), .instance_init = qbus_initfn, .instance_finalize = qbus_finalize, + .class_init = bus_class_init, }; static void qdev_register_types(void) @@ -276,8 +276,14 @@ static void r2d_init(QEMUMachineInitArgs *args) /* onboard CF (True IDE mode, Master only). */ dinfo = drive_get(IF_IDE, 0, 0); - mmio_ide_init(0x14001000, 0x1400080c, address_space_mem, irq[CF_IDE], 1, - dinfo, NULL); + dev = qdev_create(NULL, "mmio-ide"); + busdev = SYS_BUS_DEVICE(dev); + sysbus_connect_irq(busdev, 0, irq[CF_IDE]); + qdev_prop_set_uint32(dev, "shift", 1); + qdev_init_nofail(dev); + sysbus_mmio_map(busdev, 0, 0x14001000); + sysbus_mmio_map(busdev, 1, 0x1400080c); + mmio_ide_init_drives(dev, dinfo, NULL); /* onboard flash memory */ dinfo = drive_get(IF_PFLASH, 0, 0); diff --git a/hw/rtl8139.c b/hw/rtl8139.c index cfbf3f47c1..d7716beb9e 100644 --- a/hw/rtl8139.c +++ b/hw/rtl8139.c @@ -786,7 +786,7 @@ static bool rtl8139_cp_rx_valid(RTL8139State *s) static int rtl8139_can_receive(NetClientState *nc) { - RTL8139State *s = DO_UPCAST(NICState, nc, nc)->opaque; + RTL8139State *s = qemu_get_nic_opaque(nc); int avail; /* Receive (drop) packets if card is disabled. */ @@ -808,7 +808,7 @@ static int rtl8139_can_receive(NetClientState *nc) static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t size_, int do_interrupt) { - RTL8139State *s = DO_UPCAST(NICState, nc, nc)->opaque; + RTL8139State *s = qemu_get_nic_opaque(nc); /* size is the length of the buffer passed to the driver */ int size = size_; const uint8_t *dot1q_buf = NULL; @@ -1259,7 +1259,7 @@ static void rtl8139_reset(DeviceState *d) //s->BasicModeStatus |= 0x0040; /* UTP medium */ s->BasicModeStatus |= 0x0020; /* autonegotiation completed */ /* preserve link state */ - s->BasicModeStatus |= s->nic->nc.link_down ? 0 : 0x04; + s->BasicModeStatus |= qemu_get_queue(s->nic)->link_down ? 0 : 0x04; s->NWayAdvert = 0x05e1; /* all modes, full duplex */ s->NWayLPAR = 0x05e1; /* all modes, full duplex */ @@ -1787,7 +1787,7 @@ static void rtl8139_transfer_frame(RTL8139State *s, uint8_t *buf, int size, } DPRINTF("+++ transmit loopback mode\n"); - rtl8139_do_receive(&s->nic->nc, buf, size, do_interrupt); + rtl8139_do_receive(qemu_get_queue(s->nic), buf, size, do_interrupt); if (iov) { g_free(buf2); @@ -1796,9 +1796,9 @@ static void rtl8139_transfer_frame(RTL8139State *s, uint8_t *buf, int size, else { if (iov) { - qemu_sendv_packet(&s->nic->nc, iov, 3); + qemu_sendv_packet(qemu_get_queue(s->nic), iov, 3); } else { - qemu_send_packet(&s->nic->nc, buf, size); + qemu_send_packet(qemu_get_queue(s->nic), buf, size); } } } @@ -3230,7 +3230,7 @@ static int rtl8139_post_load(void *opaque, int version_id) /* nc.link_down can't be migrated, so infer link_down according * to link status bit in BasicModeStatus */ - s->nic->nc.link_down = (s->BasicModeStatus & 0x04) == 0; + qemu_get_queue(s->nic)->link_down = (s->BasicModeStatus & 0x04) == 0; return 0; } @@ -3429,7 +3429,7 @@ static void rtl8139_timer(void *opaque) static void rtl8139_cleanup(NetClientState *nc) { - RTL8139State *s = DO_UPCAST(NICState, nc, nc)->opaque; + RTL8139State *s = qemu_get_nic_opaque(nc); s->nic = NULL; } @@ -3446,12 +3446,12 @@ static void pci_rtl8139_uninit(PCIDevice *dev) } qemu_del_timer(s->timer); qemu_free_timer(s->timer); - qemu_del_net_client(&s->nic->nc); + qemu_del_nic(s->nic); } static void rtl8139_set_link_status(NetClientState *nc) { - RTL8139State *s = DO_UPCAST(NICState, nc, nc)->opaque; + RTL8139State *s = qemu_get_nic_opaque(nc); if (nc->link_down) { s->BasicModeStatus &= ~0x04; @@ -3503,7 +3503,7 @@ static int pci_rtl8139_init(PCIDevice *dev) s->nic = qemu_new_nic(&net_rtl8139_info, &s->conf, object_get_typename(OBJECT(dev)), dev->qdev.id, s); - qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); s->cplus_txbuffer = NULL; s->cplus_txbuffer_len = 0; diff --git a/hw/s390x/Makefile.objs b/hw/s390x/Makefile.objs index 1b40c2e66e..9f2f41989c 100644 --- a/hw/s390x/Makefile.objs +++ b/hw/s390x/Makefile.objs @@ -1,8 +1,9 @@ obj-y = s390-virtio-bus.o s390-virtio.o - -obj-y := $(addprefix ../,$(obj-y)) obj-y += s390-virtio-hcall.o obj-y += sclp.o obj-y += event-facility.o obj-y += sclpquiesce.o sclpconsole.o obj-y += ipl.o +obj-y += css.o +obj-y += s390-virtio-ccw.o +obj-y += virtio-ccw.o diff --git a/hw/s390x/css.c b/hw/s390x/css.c new file mode 100644 index 0000000000..3244201fc7 --- /dev/null +++ b/hw/s390x/css.c @@ -0,0 +1,1277 @@ +/* + * Channel subsystem base support. + * + * Copyright 2012 IBM Corp. + * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#include <hw/qdev.h> +#include "qemu/bitops.h" +#include "cpu.h" +#include "ioinst.h" +#include "css.h" +#include "trace.h" + +typedef struct CrwContainer { + CRW crw; + QTAILQ_ENTRY(CrwContainer) sibling; +} CrwContainer; + +typedef struct ChpInfo { + uint8_t in_use; + uint8_t type; + uint8_t is_virtual; +} ChpInfo; + +typedef struct SubchSet { + SubchDev *sch[MAX_SCHID + 1]; + unsigned long schids_used[BITS_TO_LONGS(MAX_SCHID + 1)]; + unsigned long devnos_used[BITS_TO_LONGS(MAX_SCHID + 1)]; +} SubchSet; + +typedef struct CssImage { + SubchSet *sch_set[MAX_SSID + 1]; + ChpInfo chpids[MAX_CHPID + 1]; +} CssImage; + +typedef struct ChannelSubSys { + QTAILQ_HEAD(, CrwContainer) pending_crws; + bool do_crw_mchk; + bool crws_lost; + uint8_t max_cssid; + uint8_t max_ssid; + bool chnmon_active; + uint64_t chnmon_area; + CssImage *css[MAX_CSSID + 1]; + uint8_t default_cssid; +} ChannelSubSys; + +static ChannelSubSys *channel_subsys; + +int css_create_css_image(uint8_t cssid, bool default_image) +{ + trace_css_new_image(cssid, default_image ? "(default)" : ""); + if (cssid > MAX_CSSID) { + return -EINVAL; + } + if (channel_subsys->css[cssid]) { + return -EBUSY; + } + channel_subsys->css[cssid] = g_malloc0(sizeof(CssImage)); + if (default_image) { + channel_subsys->default_cssid = cssid; + } + return 0; +} + +static uint16_t css_build_subchannel_id(SubchDev *sch) +{ + if (channel_subsys->max_cssid > 0) { + return (sch->cssid << 8) | (1 << 3) | (sch->ssid << 1) | 1; + } + return (sch->ssid << 1) | 1; +} + +static void css_inject_io_interrupt(SubchDev *sch) +{ + S390CPU *cpu = s390_cpu_addr2state(0); + uint8_t isc = (sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ISC) >> 11; + + trace_css_io_interrupt(sch->cssid, sch->ssid, sch->schid, + sch->curr_status.pmcw.intparm, isc, ""); + s390_io_interrupt(cpu, + css_build_subchannel_id(sch), + sch->schid, + sch->curr_status.pmcw.intparm, + (0x80 >> isc) << 24); +} + +void css_conditional_io_interrupt(SubchDev *sch) +{ + /* + * If the subchannel is not currently status pending, make it pending + * with alert status. + */ + if (!(sch->curr_status.scsw.ctrl & SCSW_STCTL_STATUS_PEND)) { + S390CPU *cpu = s390_cpu_addr2state(0); + uint8_t isc = (sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ISC) >> 11; + + trace_css_io_interrupt(sch->cssid, sch->ssid, sch->schid, + sch->curr_status.pmcw.intparm, isc, + "(unsolicited)"); + sch->curr_status.scsw.ctrl &= ~SCSW_CTRL_MASK_STCTL; + sch->curr_status.scsw.ctrl |= + SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; + /* Inject an I/O interrupt. */ + s390_io_interrupt(cpu, + css_build_subchannel_id(sch), + sch->schid, + sch->curr_status.pmcw.intparm, + (0x80 >> isc) << 24); + } +} + +static void sch_handle_clear_func(SubchDev *sch) +{ + PMCW *p = &sch->curr_status.pmcw; + SCSW *s = &sch->curr_status.scsw; + int path; + + /* Path management: In our simple css, we always choose the only path. */ + path = 0x80; + + /* Reset values prior to 'issueing the clear signal'. */ + p->lpum = 0; + p->pom = 0xff; + s->flags &= ~SCSW_FLAGS_MASK_PNO; + + /* We always 'attempt to issue the clear signal', and we always succeed. */ + sch->orb = NULL; + sch->channel_prog = 0x0; + sch->last_cmd_valid = false; + s->ctrl &= ~SCSW_ACTL_CLEAR_PEND; + s->ctrl |= SCSW_STCTL_STATUS_PEND; + + s->dstat = 0; + s->cstat = 0; + p->lpum = path; + +} + +static void sch_handle_halt_func(SubchDev *sch) +{ + + PMCW *p = &sch->curr_status.pmcw; + SCSW *s = &sch->curr_status.scsw; + int path; + + /* Path management: In our simple css, we always choose the only path. */ + path = 0x80; + + /* We always 'attempt to issue the halt signal', and we always succeed. */ + sch->orb = NULL; + sch->channel_prog = 0x0; + sch->last_cmd_valid = false; + s->ctrl &= ~SCSW_ACTL_HALT_PEND; + s->ctrl |= SCSW_STCTL_STATUS_PEND; + + if ((s->ctrl & (SCSW_ACTL_SUBCH_ACTIVE | SCSW_ACTL_DEVICE_ACTIVE)) || + !((s->ctrl & SCSW_ACTL_START_PEND) || + (s->ctrl & SCSW_ACTL_SUSP))) { + s->dstat = SCSW_DSTAT_DEVICE_END; + } + s->cstat = 0; + p->lpum = path; + +} + +static void copy_sense_id_to_guest(SenseId *dest, SenseId *src) +{ + int i; + + dest->reserved = src->reserved; + dest->cu_type = cpu_to_be16(src->cu_type); + dest->cu_model = src->cu_model; + dest->dev_type = cpu_to_be16(src->dev_type); + dest->dev_model = src->dev_model; + dest->unused = src->unused; + for (i = 0; i < ARRAY_SIZE(dest->ciw); i++) { + dest->ciw[i].type = src->ciw[i].type; + dest->ciw[i].command = src->ciw[i].command; + dest->ciw[i].count = cpu_to_be16(src->ciw[i].count); + } +} + +static CCW1 copy_ccw_from_guest(hwaddr addr) +{ + CCW1 tmp; + CCW1 ret; + + cpu_physical_memory_read(addr, &tmp, sizeof(tmp)); + ret.cmd_code = tmp.cmd_code; + ret.flags = tmp.flags; + ret.count = be16_to_cpu(tmp.count); + ret.cda = be32_to_cpu(tmp.cda); + + return ret; +} + +static int css_interpret_ccw(SubchDev *sch, hwaddr ccw_addr) +{ + int ret; + bool check_len; + int len; + CCW1 ccw; + + if (!ccw_addr) { + return -EIO; + } + + ccw = copy_ccw_from_guest(ccw_addr); + + /* Check for invalid command codes. */ + if ((ccw.cmd_code & 0x0f) == 0) { + return -EINVAL; + } + if (((ccw.cmd_code & 0x0f) == CCW_CMD_TIC) && + ((ccw.cmd_code & 0xf0) != 0)) { + return -EINVAL; + } + + if (ccw.flags & CCW_FLAG_SUSPEND) { + return -EINPROGRESS; + } + + check_len = !((ccw.flags & CCW_FLAG_SLI) && !(ccw.flags & CCW_FLAG_DC)); + + /* Look at the command. */ + switch (ccw.cmd_code) { + case CCW_CMD_NOOP: + /* Nothing to do. */ + ret = 0; + break; + case CCW_CMD_BASIC_SENSE: + if (check_len) { + if (ccw.count != sizeof(sch->sense_data)) { + ret = -EINVAL; + break; + } + } + len = MIN(ccw.count, sizeof(sch->sense_data)); + cpu_physical_memory_write(ccw.cda, sch->sense_data, len); + sch->curr_status.scsw.count = ccw.count - len; + memset(sch->sense_data, 0, sizeof(sch->sense_data)); + ret = 0; + break; + case CCW_CMD_SENSE_ID: + { + SenseId sense_id; + + copy_sense_id_to_guest(&sense_id, &sch->id); + /* Sense ID information is device specific. */ + if (check_len) { + if (ccw.count != sizeof(sense_id)) { + ret = -EINVAL; + break; + } + } + len = MIN(ccw.count, sizeof(sense_id)); + /* + * Only indicate 0xff in the first sense byte if we actually + * have enough place to store at least bytes 0-3. + */ + if (len >= 4) { + sense_id.reserved = 0xff; + } else { + sense_id.reserved = 0; + } + cpu_physical_memory_write(ccw.cda, &sense_id, len); + sch->curr_status.scsw.count = ccw.count - len; + ret = 0; + break; + } + case CCW_CMD_TIC: + if (sch->last_cmd_valid && (sch->last_cmd.cmd_code == CCW_CMD_TIC)) { + ret = -EINVAL; + break; + } + if (ccw.flags & (CCW_FLAG_CC | CCW_FLAG_DC)) { + ret = -EINVAL; + break; + } + sch->channel_prog = ccw.cda; + ret = -EAGAIN; + break; + default: + if (sch->ccw_cb) { + /* Handle device specific commands. */ + ret = sch->ccw_cb(sch, ccw); + } else { + ret = -ENOSYS; + } + break; + } + sch->last_cmd = ccw; + sch->last_cmd_valid = true; + if (ret == 0) { + if (ccw.flags & CCW_FLAG_CC) { + sch->channel_prog += 8; + ret = -EAGAIN; + } + } + + return ret; +} + +static void sch_handle_start_func(SubchDev *sch) +{ + + PMCW *p = &sch->curr_status.pmcw; + SCSW *s = &sch->curr_status.scsw; + ORB *orb = sch->orb; + int path; + int ret; + + /* Path management: In our simple css, we always choose the only path. */ + path = 0x80; + + if (!(s->ctrl & SCSW_ACTL_SUSP)) { + /* Look at the orb and try to execute the channel program. */ + p->intparm = orb->intparm; + if (!(orb->lpm & path)) { + /* Generate a deferred cc 3 condition. */ + s->flags |= SCSW_FLAGS_MASK_CC; + s->ctrl &= ~SCSW_CTRL_MASK_STCTL; + s->ctrl |= (SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND); + return; + } + } else { + s->ctrl &= ~(SCSW_ACTL_SUSP | SCSW_ACTL_RESUME_PEND); + } + sch->last_cmd_valid = false; + do { + ret = css_interpret_ccw(sch, sch->channel_prog); + switch (ret) { + case -EAGAIN: + /* ccw chain, continue processing */ + break; + case 0: + /* success */ + s->ctrl &= ~SCSW_ACTL_START_PEND; + s->ctrl &= ~SCSW_CTRL_MASK_STCTL; + s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | + SCSW_STCTL_STATUS_PEND; + s->dstat = SCSW_DSTAT_CHANNEL_END | SCSW_DSTAT_DEVICE_END; + break; + case -ENOSYS: + /* unsupported command, generate unit check (command reject) */ + s->ctrl &= ~SCSW_ACTL_START_PEND; + s->dstat = SCSW_DSTAT_UNIT_CHECK; + /* Set sense bit 0 in ecw0. */ + sch->sense_data[0] = 0x80; + s->ctrl &= ~SCSW_CTRL_MASK_STCTL; + s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | + SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; + break; + case -EFAULT: + /* memory problem, generate channel data check */ + s->ctrl &= ~SCSW_ACTL_START_PEND; + s->cstat = SCSW_CSTAT_DATA_CHECK; + s->ctrl &= ~SCSW_CTRL_MASK_STCTL; + s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | + SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; + break; + case -EBUSY: + /* subchannel busy, generate deferred cc 1 */ + s->flags &= ~SCSW_FLAGS_MASK_CC; + s->flags |= (1 << 8); + s->ctrl &= ~SCSW_CTRL_MASK_STCTL; + s->ctrl |= SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; + break; + case -EINPROGRESS: + /* channel program has been suspended */ + s->ctrl &= ~SCSW_ACTL_START_PEND; + s->ctrl |= SCSW_ACTL_SUSP; + break; + default: + /* error, generate channel program check */ + s->ctrl &= ~SCSW_ACTL_START_PEND; + s->cstat = SCSW_CSTAT_PROG_CHECK; + s->ctrl &= ~SCSW_CTRL_MASK_STCTL; + s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | + SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; + break; + } + } while (ret == -EAGAIN); + +} + +/* + * On real machines, this would run asynchronously to the main vcpus. + * We might want to make some parts of the ssch handling (interpreting + * read/writes) asynchronous later on if we start supporting more than + * our current very simple devices. + */ +static void do_subchannel_work(SubchDev *sch) +{ + + SCSW *s = &sch->curr_status.scsw; + + if (s->ctrl & SCSW_FCTL_CLEAR_FUNC) { + sch_handle_clear_func(sch); + } else if (s->ctrl & SCSW_FCTL_HALT_FUNC) { + sch_handle_halt_func(sch); + } else if (s->ctrl & SCSW_FCTL_START_FUNC) { + sch_handle_start_func(sch); + } else { + /* Cannot happen. */ + return; + } + css_inject_io_interrupt(sch); +} + +static void copy_pmcw_to_guest(PMCW *dest, const PMCW *src) +{ + int i; + + dest->intparm = cpu_to_be32(src->intparm); + dest->flags = cpu_to_be16(src->flags); + dest->devno = cpu_to_be16(src->devno); + dest->lpm = src->lpm; + dest->pnom = src->pnom; + dest->lpum = src->lpum; + dest->pim = src->pim; + dest->mbi = cpu_to_be16(src->mbi); + dest->pom = src->pom; + dest->pam = src->pam; + for (i = 0; i < ARRAY_SIZE(dest->chpid); i++) { + dest->chpid[i] = src->chpid[i]; + } + dest->chars = cpu_to_be32(src->chars); +} + +static void copy_scsw_to_guest(SCSW *dest, const SCSW *src) +{ + dest->flags = cpu_to_be16(src->flags); + dest->ctrl = cpu_to_be16(src->ctrl); + dest->cpa = cpu_to_be32(src->cpa); + dest->dstat = src->dstat; + dest->cstat = src->cstat; + dest->count = cpu_to_be16(src->count); +} + +static void copy_schib_to_guest(SCHIB *dest, const SCHIB *src) +{ + int i; + + copy_pmcw_to_guest(&dest->pmcw, &src->pmcw); + copy_scsw_to_guest(&dest->scsw, &src->scsw); + dest->mba = cpu_to_be64(src->mba); + for (i = 0; i < ARRAY_SIZE(dest->mda); i++) { + dest->mda[i] = src->mda[i]; + } +} + +int css_do_stsch(SubchDev *sch, SCHIB *schib) +{ + /* Use current status. */ + copy_schib_to_guest(schib, &sch->curr_status); + return 0; +} + +static void copy_pmcw_from_guest(PMCW *dest, const PMCW *src) +{ + int i; + + dest->intparm = be32_to_cpu(src->intparm); + dest->flags = be16_to_cpu(src->flags); + dest->devno = be16_to_cpu(src->devno); + dest->lpm = src->lpm; + dest->pnom = src->pnom; + dest->lpum = src->lpum; + dest->pim = src->pim; + dest->mbi = be16_to_cpu(src->mbi); + dest->pom = src->pom; + dest->pam = src->pam; + for (i = 0; i < ARRAY_SIZE(dest->chpid); i++) { + dest->chpid[i] = src->chpid[i]; + } + dest->chars = be32_to_cpu(src->chars); +} + +static void copy_scsw_from_guest(SCSW *dest, const SCSW *src) +{ + dest->flags = be16_to_cpu(src->flags); + dest->ctrl = be16_to_cpu(src->ctrl); + dest->cpa = be32_to_cpu(src->cpa); + dest->dstat = src->dstat; + dest->cstat = src->cstat; + dest->count = be16_to_cpu(src->count); +} + +static void copy_schib_from_guest(SCHIB *dest, const SCHIB *src) +{ + int i; + + copy_pmcw_from_guest(&dest->pmcw, &src->pmcw); + copy_scsw_from_guest(&dest->scsw, &src->scsw); + dest->mba = be64_to_cpu(src->mba); + for (i = 0; i < ARRAY_SIZE(dest->mda); i++) { + dest->mda[i] = src->mda[i]; + } +} + +int css_do_msch(SubchDev *sch, SCHIB *orig_schib) +{ + SCSW *s = &sch->curr_status.scsw; + PMCW *p = &sch->curr_status.pmcw; + int ret; + SCHIB schib; + + if (!(sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_DNV)) { + ret = 0; + goto out; + } + + if (s->ctrl & SCSW_STCTL_STATUS_PEND) { + ret = -EINPROGRESS; + goto out; + } + + if (s->ctrl & + (SCSW_FCTL_START_FUNC|SCSW_FCTL_HALT_FUNC|SCSW_FCTL_CLEAR_FUNC)) { + ret = -EBUSY; + goto out; + } + + copy_schib_from_guest(&schib, orig_schib); + /* Only update the program-modifiable fields. */ + p->intparm = schib.pmcw.intparm; + p->flags &= ~(PMCW_FLAGS_MASK_ISC | PMCW_FLAGS_MASK_ENA | + PMCW_FLAGS_MASK_LM | PMCW_FLAGS_MASK_MME | + PMCW_FLAGS_MASK_MP); + p->flags |= schib.pmcw.flags & + (PMCW_FLAGS_MASK_ISC | PMCW_FLAGS_MASK_ENA | + PMCW_FLAGS_MASK_LM | PMCW_FLAGS_MASK_MME | + PMCW_FLAGS_MASK_MP); + p->lpm = schib.pmcw.lpm; + p->mbi = schib.pmcw.mbi; + p->pom = schib.pmcw.pom; + p->chars &= ~(PMCW_CHARS_MASK_MBFC | PMCW_CHARS_MASK_CSENSE); + p->chars |= schib.pmcw.chars & + (PMCW_CHARS_MASK_MBFC | PMCW_CHARS_MASK_CSENSE); + sch->curr_status.mba = schib.mba; + + ret = 0; + +out: + return ret; +} + +int css_do_xsch(SubchDev *sch) +{ + SCSW *s = &sch->curr_status.scsw; + PMCW *p = &sch->curr_status.pmcw; + int ret; + + if (!(p->flags & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA))) { + ret = -ENODEV; + goto out; + } + + if (!(s->ctrl & SCSW_CTRL_MASK_FCTL) || + ((s->ctrl & SCSW_CTRL_MASK_FCTL) != SCSW_FCTL_START_FUNC) || + (!(s->ctrl & + (SCSW_ACTL_RESUME_PEND | SCSW_ACTL_START_PEND | SCSW_ACTL_SUSP))) || + (s->ctrl & SCSW_ACTL_SUBCH_ACTIVE)) { + ret = -EINPROGRESS; + goto out; + } + + if (s->ctrl & SCSW_CTRL_MASK_STCTL) { + ret = -EBUSY; + goto out; + } + + /* Cancel the current operation. */ + s->ctrl &= ~(SCSW_FCTL_START_FUNC | + SCSW_ACTL_RESUME_PEND | + SCSW_ACTL_START_PEND | + SCSW_ACTL_SUSP); + sch->channel_prog = 0x0; + sch->last_cmd_valid = false; + sch->orb = NULL; + s->dstat = 0; + s->cstat = 0; + ret = 0; + +out: + return ret; +} + +int css_do_csch(SubchDev *sch) +{ + SCSW *s = &sch->curr_status.scsw; + PMCW *p = &sch->curr_status.pmcw; + int ret; + + if (!(p->flags & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA))) { + ret = -ENODEV; + goto out; + } + + /* Trigger the clear function. */ + s->ctrl &= ~(SCSW_CTRL_MASK_FCTL | SCSW_CTRL_MASK_ACTL); + s->ctrl |= SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_CLEAR_FUNC; + + do_subchannel_work(sch); + ret = 0; + +out: + return ret; +} + +int css_do_hsch(SubchDev *sch) +{ + SCSW *s = &sch->curr_status.scsw; + PMCW *p = &sch->curr_status.pmcw; + int ret; + + if (!(p->flags & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA))) { + ret = -ENODEV; + goto out; + } + + if (((s->ctrl & SCSW_CTRL_MASK_STCTL) == SCSW_STCTL_STATUS_PEND) || + (s->ctrl & (SCSW_STCTL_PRIMARY | + SCSW_STCTL_SECONDARY | + SCSW_STCTL_ALERT))) { + ret = -EINPROGRESS; + goto out; + } + + if (s->ctrl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { + ret = -EBUSY; + goto out; + } + + /* Trigger the halt function. */ + s->ctrl |= SCSW_FCTL_HALT_FUNC; + s->ctrl &= ~SCSW_FCTL_START_FUNC; + if (((s->ctrl & SCSW_CTRL_MASK_ACTL) == + (SCSW_ACTL_SUBCH_ACTIVE | SCSW_ACTL_DEVICE_ACTIVE)) && + ((s->ctrl & SCSW_CTRL_MASK_STCTL) == SCSW_STCTL_INTERMEDIATE)) { + s->ctrl &= ~SCSW_STCTL_STATUS_PEND; + } + s->ctrl |= SCSW_ACTL_HALT_PEND; + + do_subchannel_work(sch); + ret = 0; + +out: + return ret; +} + +static void css_update_chnmon(SubchDev *sch) +{ + if (!(sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_MME)) { + /* Not active. */ + return; + } + /* The counter is conveniently located at the beginning of the struct. */ + if (sch->curr_status.pmcw.chars & PMCW_CHARS_MASK_MBFC) { + /* Format 1, per-subchannel area. */ + uint32_t count; + + count = ldl_phys(sch->curr_status.mba); + count++; + stl_phys(sch->curr_status.mba, count); + } else { + /* Format 0, global area. */ + uint32_t offset; + uint16_t count; + + offset = sch->curr_status.pmcw.mbi << 5; + count = lduw_phys(channel_subsys->chnmon_area + offset); + count++; + stw_phys(channel_subsys->chnmon_area + offset, count); + } +} + +int css_do_ssch(SubchDev *sch, ORB *orb) +{ + SCSW *s = &sch->curr_status.scsw; + PMCW *p = &sch->curr_status.pmcw; + int ret; + + if (!(p->flags & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA))) { + ret = -ENODEV; + goto out; + } + + if (s->ctrl & SCSW_STCTL_STATUS_PEND) { + ret = -EINPROGRESS; + goto out; + } + + if (s->ctrl & (SCSW_FCTL_START_FUNC | + SCSW_FCTL_HALT_FUNC | + SCSW_FCTL_CLEAR_FUNC)) { + ret = -EBUSY; + goto out; + } + + /* If monitoring is active, update counter. */ + if (channel_subsys->chnmon_active) { + css_update_chnmon(sch); + } + sch->orb = orb; + sch->channel_prog = orb->cpa; + /* Trigger the start function. */ + s->ctrl |= (SCSW_FCTL_START_FUNC | SCSW_ACTL_START_PEND); + s->flags &= ~SCSW_FLAGS_MASK_PNO; + + do_subchannel_work(sch); + ret = 0; + +out: + return ret; +} + +static void copy_irb_to_guest(IRB *dest, const IRB *src) +{ + int i; + + copy_scsw_to_guest(&dest->scsw, &src->scsw); + + for (i = 0; i < ARRAY_SIZE(dest->esw); i++) { + dest->esw[i] = cpu_to_be32(src->esw[i]); + } + for (i = 0; i < ARRAY_SIZE(dest->ecw); i++) { + dest->ecw[i] = cpu_to_be32(src->ecw[i]); + } + for (i = 0; i < ARRAY_SIZE(dest->emw); i++) { + dest->emw[i] = cpu_to_be32(src->emw[i]); + } +} + +int css_do_tsch(SubchDev *sch, IRB *target_irb) +{ + SCSW *s = &sch->curr_status.scsw; + PMCW *p = &sch->curr_status.pmcw; + uint16_t stctl; + uint16_t fctl; + uint16_t actl; + IRB irb; + int ret; + + if (!(p->flags & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA))) { + ret = 3; + goto out; + } + + stctl = s->ctrl & SCSW_CTRL_MASK_STCTL; + fctl = s->ctrl & SCSW_CTRL_MASK_FCTL; + actl = s->ctrl & SCSW_CTRL_MASK_ACTL; + + /* Prepare the irb for the guest. */ + memset(&irb, 0, sizeof(IRB)); + + /* Copy scsw from current status. */ + memcpy(&irb.scsw, s, sizeof(SCSW)); + if (stctl & SCSW_STCTL_STATUS_PEND) { + if (s->cstat & (SCSW_CSTAT_DATA_CHECK | + SCSW_CSTAT_CHN_CTRL_CHK | + SCSW_CSTAT_INTF_CTRL_CHK)) { + irb.scsw.flags |= SCSW_FLAGS_MASK_ESWF; + irb.esw[0] = 0x04804000; + } else { + irb.esw[0] = 0x00800000; + } + /* If a unit check is pending, copy sense data. */ + if ((s->dstat & SCSW_DSTAT_UNIT_CHECK) && + (p->chars & PMCW_CHARS_MASK_CSENSE)) { + irb.scsw.flags |= SCSW_FLAGS_MASK_ESWF | SCSW_FLAGS_MASK_ECTL; + memcpy(irb.ecw, sch->sense_data, sizeof(sch->sense_data)); + irb.esw[1] = 0x02000000 | (sizeof(sch->sense_data) << 8); + } + } + /* Store the irb to the guest. */ + copy_irb_to_guest(target_irb, &irb); + + /* Clear conditions on subchannel, if applicable. */ + if (stctl & SCSW_STCTL_STATUS_PEND) { + s->ctrl &= ~SCSW_CTRL_MASK_STCTL; + if ((stctl != (SCSW_STCTL_INTERMEDIATE | SCSW_STCTL_STATUS_PEND)) || + ((fctl & SCSW_FCTL_HALT_FUNC) && + (actl & SCSW_ACTL_SUSP))) { + s->ctrl &= ~SCSW_CTRL_MASK_FCTL; + } + if (stctl != (SCSW_STCTL_INTERMEDIATE | SCSW_STCTL_STATUS_PEND)) { + s->flags &= ~SCSW_FLAGS_MASK_PNO; + s->ctrl &= ~(SCSW_ACTL_RESUME_PEND | + SCSW_ACTL_START_PEND | + SCSW_ACTL_HALT_PEND | + SCSW_ACTL_CLEAR_PEND | + SCSW_ACTL_SUSP); + } else { + if ((actl & SCSW_ACTL_SUSP) && + (fctl & SCSW_FCTL_START_FUNC)) { + s->flags &= ~SCSW_FLAGS_MASK_PNO; + if (fctl & SCSW_FCTL_HALT_FUNC) { + s->ctrl &= ~(SCSW_ACTL_RESUME_PEND | + SCSW_ACTL_START_PEND | + SCSW_ACTL_HALT_PEND | + SCSW_ACTL_CLEAR_PEND | + SCSW_ACTL_SUSP); + } else { + s->ctrl &= ~SCSW_ACTL_RESUME_PEND; + } + } + } + /* Clear pending sense data. */ + if (p->chars & PMCW_CHARS_MASK_CSENSE) { + memset(sch->sense_data, 0 , sizeof(sch->sense_data)); + } + } + + ret = ((stctl & SCSW_STCTL_STATUS_PEND) == 0); + +out: + return ret; +} + +static void copy_crw_to_guest(CRW *dest, const CRW *src) +{ + dest->flags = cpu_to_be16(src->flags); + dest->rsid = cpu_to_be16(src->rsid); +} + +int css_do_stcrw(CRW *crw) +{ + CrwContainer *crw_cont; + int ret; + + crw_cont = QTAILQ_FIRST(&channel_subsys->pending_crws); + if (crw_cont) { + QTAILQ_REMOVE(&channel_subsys->pending_crws, crw_cont, sibling); + copy_crw_to_guest(crw, &crw_cont->crw); + g_free(crw_cont); + ret = 0; + } else { + /* List was empty, turn crw machine checks on again. */ + memset(crw, 0, sizeof(*crw)); + channel_subsys->do_crw_mchk = true; + ret = 1; + } + + return ret; +} + +int css_do_tpi(IOIntCode *int_code, int lowcore) +{ + /* No pending interrupts for !KVM. */ + return 0; + } + +int css_collect_chp_desc(int m, uint8_t cssid, uint8_t f_chpid, uint8_t l_chpid, + int rfmt, void *buf) +{ + int i, desc_size; + uint32_t words[8]; + uint32_t chpid_type_word; + CssImage *css; + + if (!m && !cssid) { + css = channel_subsys->css[channel_subsys->default_cssid]; + } else { + css = channel_subsys->css[cssid]; + } + if (!css) { + return 0; + } + desc_size = 0; + for (i = f_chpid; i <= l_chpid; i++) { + if (css->chpids[i].in_use) { + chpid_type_word = 0x80000000 | (css->chpids[i].type << 8) | i; + if (rfmt == 0) { + words[0] = cpu_to_be32(chpid_type_word); + words[1] = 0; + memcpy(buf + desc_size, words, 8); + desc_size += 8; + } else if (rfmt == 1) { + words[0] = cpu_to_be32(chpid_type_word); + words[1] = 0; + words[2] = 0; + words[3] = 0; + words[4] = 0; + words[5] = 0; + words[6] = 0; + words[7] = 0; + memcpy(buf + desc_size, words, 32); + desc_size += 32; + } + } + } + return desc_size; +} + +void css_do_schm(uint8_t mbk, int update, int dct, uint64_t mbo) +{ + /* dct is currently ignored (not really meaningful for our devices) */ + /* TODO: Don't ignore mbk. */ + if (update && !channel_subsys->chnmon_active) { + /* Enable measuring. */ + channel_subsys->chnmon_area = mbo; + channel_subsys->chnmon_active = true; + } + if (!update && channel_subsys->chnmon_active) { + /* Disable measuring. */ + channel_subsys->chnmon_area = 0; + channel_subsys->chnmon_active = false; + } +} + +int css_do_rsch(SubchDev *sch) +{ + SCSW *s = &sch->curr_status.scsw; + PMCW *p = &sch->curr_status.pmcw; + int ret; + + if (!(p->flags & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA))) { + ret = -ENODEV; + goto out; + } + + if (s->ctrl & SCSW_STCTL_STATUS_PEND) { + ret = -EINPROGRESS; + goto out; + } + + if (((s->ctrl & SCSW_CTRL_MASK_FCTL) != SCSW_FCTL_START_FUNC) || + (s->ctrl & SCSW_ACTL_RESUME_PEND) || + (!(s->ctrl & SCSW_ACTL_SUSP))) { + ret = -EINVAL; + goto out; + } + + /* If monitoring is active, update counter. */ + if (channel_subsys->chnmon_active) { + css_update_chnmon(sch); + } + + s->ctrl |= SCSW_ACTL_RESUME_PEND; + do_subchannel_work(sch); + ret = 0; + +out: + return ret; +} + +int css_do_rchp(uint8_t cssid, uint8_t chpid) +{ + uint8_t real_cssid; + + if (cssid > channel_subsys->max_cssid) { + return -EINVAL; + } + if (channel_subsys->max_cssid == 0) { + real_cssid = channel_subsys->default_cssid; + } else { + real_cssid = cssid; + } + if (!channel_subsys->css[real_cssid]) { + return -EINVAL; + } + + if (!channel_subsys->css[real_cssid]->chpids[chpid].in_use) { + return -ENODEV; + } + + if (!channel_subsys->css[real_cssid]->chpids[chpid].is_virtual) { + fprintf(stderr, + "rchp unsupported for non-virtual chpid %x.%02x!\n", + real_cssid, chpid); + return -ENODEV; + } + + /* We don't really use a channel path, so we're done here. */ + css_queue_crw(CRW_RSC_CHP, CRW_ERC_INIT, + channel_subsys->max_cssid > 0 ? 1 : 0, chpid); + if (channel_subsys->max_cssid > 0) { + css_queue_crw(CRW_RSC_CHP, CRW_ERC_INIT, 0, real_cssid << 8); + } + return 0; +} + +bool css_schid_final(uint8_t cssid, uint8_t ssid, uint16_t schid) +{ + SubchSet *set; + + if (cssid > MAX_CSSID || ssid > MAX_SSID || !channel_subsys->css[cssid] || + !channel_subsys->css[cssid]->sch_set[ssid]) { + return true; + } + set = channel_subsys->css[cssid]->sch_set[ssid]; + return schid > find_last_bit(set->schids_used, + (MAX_SCHID + 1) / sizeof(unsigned long)); +} + +static int css_add_virtual_chpid(uint8_t cssid, uint8_t chpid, uint8_t type) +{ + CssImage *css; + + trace_css_chpid_add(cssid, chpid, type); + if (cssid > MAX_CSSID) { + return -EINVAL; + } + css = channel_subsys->css[cssid]; + if (!css) { + return -EINVAL; + } + if (css->chpids[chpid].in_use) { + return -EEXIST; + } + css->chpids[chpid].in_use = 1; + css->chpids[chpid].type = type; + css->chpids[chpid].is_virtual = 1; + + css_generate_chp_crws(cssid, chpid); + + return 0; +} + +void css_sch_build_virtual_schib(SubchDev *sch, uint8_t chpid, uint8_t type) +{ + PMCW *p = &sch->curr_status.pmcw; + SCSW *s = &sch->curr_status.scsw; + int i; + CssImage *css = channel_subsys->css[sch->cssid]; + + assert(css != NULL); + memset(p, 0, sizeof(PMCW)); + p->flags |= PMCW_FLAGS_MASK_DNV; + p->devno = sch->devno; + /* single path */ + p->pim = 0x80; + p->pom = 0xff; + p->pam = 0x80; + p->chpid[0] = chpid; + if (!css->chpids[chpid].in_use) { + css_add_virtual_chpid(sch->cssid, chpid, type); + } + + memset(s, 0, sizeof(SCSW)); + sch->curr_status.mba = 0; + for (i = 0; i < ARRAY_SIZE(sch->curr_status.mda); i++) { + sch->curr_status.mda[i] = 0; + } +} + +SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid, uint16_t schid) +{ + uint8_t real_cssid; + + real_cssid = (!m && (cssid == 0)) ? channel_subsys->default_cssid : cssid; + + if (!channel_subsys->css[real_cssid]) { + return NULL; + } + + if (!channel_subsys->css[real_cssid]->sch_set[ssid]) { + return NULL; + } + + return channel_subsys->css[real_cssid]->sch_set[ssid]->sch[schid]; +} + +bool css_subch_visible(SubchDev *sch) +{ + if (sch->ssid > channel_subsys->max_ssid) { + return false; + } + + if (sch->cssid != channel_subsys->default_cssid) { + return (channel_subsys->max_cssid > 0); + } + + return true; +} + +bool css_present(uint8_t cssid) +{ + return (channel_subsys->css[cssid] != NULL); +} + +bool css_devno_used(uint8_t cssid, uint8_t ssid, uint16_t devno) +{ + if (!channel_subsys->css[cssid]) { + return false; + } + if (!channel_subsys->css[cssid]->sch_set[ssid]) { + return false; + } + + return !!test_bit(devno, + channel_subsys->css[cssid]->sch_set[ssid]->devnos_used); +} + +void css_subch_assign(uint8_t cssid, uint8_t ssid, uint16_t schid, + uint16_t devno, SubchDev *sch) +{ + CssImage *css; + SubchSet *s_set; + + trace_css_assign_subch(sch ? "assign" : "deassign", cssid, ssid, schid, + devno); + if (!channel_subsys->css[cssid]) { + fprintf(stderr, + "Suspicious call to %s (%x.%x.%04x) for non-existing css!\n", + __func__, cssid, ssid, schid); + return; + } + css = channel_subsys->css[cssid]; + + if (!css->sch_set[ssid]) { + css->sch_set[ssid] = g_malloc0(sizeof(SubchSet)); + } + s_set = css->sch_set[ssid]; + + s_set->sch[schid] = sch; + if (sch) { + set_bit(schid, s_set->schids_used); + set_bit(devno, s_set->devnos_used); + } else { + clear_bit(schid, s_set->schids_used); + clear_bit(devno, s_set->devnos_used); + } +} + +void css_queue_crw(uint8_t rsc, uint8_t erc, int chain, uint16_t rsid) +{ + CrwContainer *crw_cont; + + trace_css_crw(rsc, erc, rsid, chain ? "(chained)" : ""); + /* TODO: Maybe use a static crw pool? */ + crw_cont = g_try_malloc0(sizeof(CrwContainer)); + if (!crw_cont) { + channel_subsys->crws_lost = true; + return; + } + crw_cont->crw.flags = (rsc << 8) | erc; + if (chain) { + crw_cont->crw.flags |= CRW_FLAGS_MASK_C; + } + crw_cont->crw.rsid = rsid; + if (channel_subsys->crws_lost) { + crw_cont->crw.flags |= CRW_FLAGS_MASK_R; + channel_subsys->crws_lost = false; + } + + QTAILQ_INSERT_TAIL(&channel_subsys->pending_crws, crw_cont, sibling); + + if (channel_subsys->do_crw_mchk) { + S390CPU *cpu = s390_cpu_addr2state(0); + + channel_subsys->do_crw_mchk = false; + /* Inject crw pending machine check. */ + s390_crw_mchk(cpu); + } +} + +void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid, + int hotplugged, int add) +{ + uint8_t guest_cssid; + bool chain_crw; + + if (add && !hotplugged) { + return; + } + if (channel_subsys->max_cssid == 0) { + /* Default cssid shows up as 0. */ + guest_cssid = (cssid == channel_subsys->default_cssid) ? 0 : cssid; + } else { + /* Show real cssid to the guest. */ + guest_cssid = cssid; + } + /* + * Only notify for higher subchannel sets/channel subsystems if the + * guest has enabled it. + */ + if ((ssid > channel_subsys->max_ssid) || + (guest_cssid > channel_subsys->max_cssid) || + ((channel_subsys->max_cssid == 0) && + (cssid != channel_subsys->default_cssid))) { + return; + } + chain_crw = (channel_subsys->max_ssid > 0) || + (channel_subsys->max_cssid > 0); + css_queue_crw(CRW_RSC_SUBCH, CRW_ERC_IPI, chain_crw ? 1 : 0, schid); + if (chain_crw) { + css_queue_crw(CRW_RSC_SUBCH, CRW_ERC_IPI, 0, + (guest_cssid << 8) | (ssid << 4)); + } +} + +void css_generate_chp_crws(uint8_t cssid, uint8_t chpid) +{ + /* TODO */ +} + +int css_enable_mcsse(void) +{ + trace_css_enable_facility("mcsse"); + channel_subsys->max_cssid = MAX_CSSID; + return 0; +} + +int css_enable_mss(void) +{ + trace_css_enable_facility("mss"); + channel_subsys->max_ssid = MAX_SSID; + return 0; +} + +static void css_init(void) +{ + channel_subsys = g_malloc0(sizeof(*channel_subsys)); + QTAILQ_INIT(&channel_subsys->pending_crws); + channel_subsys->do_crw_mchk = true; + channel_subsys->crws_lost = false; + channel_subsys->chnmon_active = false; +} +machine_init(css_init); + +void css_reset_sch(SubchDev *sch) +{ + PMCW *p = &sch->curr_status.pmcw; + + p->intparm = 0; + p->flags &= ~(PMCW_FLAGS_MASK_ISC | PMCW_FLAGS_MASK_ENA | + PMCW_FLAGS_MASK_LM | PMCW_FLAGS_MASK_MME | + PMCW_FLAGS_MASK_MP | PMCW_FLAGS_MASK_TF); + p->flags |= PMCW_FLAGS_MASK_DNV; + p->devno = sch->devno; + p->pim = 0x80; + p->lpm = p->pim; + p->pnom = 0; + p->lpum = 0; + p->mbi = 0; + p->pom = 0xff; + p->pam = 0x80; + p->chars &= ~(PMCW_CHARS_MASK_MBFC | PMCW_CHARS_MASK_XMWME | + PMCW_CHARS_MASK_CSENSE); + + memset(&sch->curr_status.scsw, 0, sizeof(sch->curr_status.scsw)); + sch->curr_status.mba = 0; + + sch->channel_prog = 0x0; + sch->last_cmd_valid = false; + sch->orb = NULL; +} + +void css_reset(void) +{ + CrwContainer *crw_cont; + + /* Clean up monitoring. */ + channel_subsys->chnmon_active = false; + channel_subsys->chnmon_area = 0; + + /* Clear pending CRWs. */ + while ((crw_cont = QTAILQ_FIRST(&channel_subsys->pending_crws))) { + QTAILQ_REMOVE(&channel_subsys->pending_crws, crw_cont, sibling); + g_free(crw_cont); + } + channel_subsys->do_crw_mchk = true; + channel_subsys->crws_lost = false; + + /* Reset maximum ids. */ + channel_subsys->max_cssid = 0; + channel_subsys->max_ssid = 0; +} diff --git a/hw/s390x/css.h b/hw/s390x/css.h new file mode 100644 index 0000000000..85ed05d0f5 --- /dev/null +++ b/hw/s390x/css.h @@ -0,0 +1,99 @@ +/* + * Channel subsystem structures and definitions. + * + * Copyright 2012 IBM Corp. + * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#ifndef CSS_H +#define CSS_H + +#include "ioinst.h" + +/* Channel subsystem constants. */ +#define MAX_SCHID 65535 +#define MAX_SSID 3 +#define MAX_CSSID 254 /* 255 is reserved */ +#define MAX_CHPID 255 + +#define MAX_CIWS 62 + +typedef struct CIW { + uint8_t type; + uint8_t command; + uint16_t count; +} QEMU_PACKED CIW; + +typedef struct SenseId { + /* common part */ + uint8_t reserved; /* always 0x'FF' */ + uint16_t cu_type; /* control unit type */ + uint8_t cu_model; /* control unit model */ + uint16_t dev_type; /* device type */ + uint8_t dev_model; /* device model */ + uint8_t unused; /* padding byte */ + /* extended part */ + CIW ciw[MAX_CIWS]; /* variable # of CIWs */ +} QEMU_PACKED SenseId; + +/* Channel measurements, from linux/drivers/s390/cio/cmf.c. */ +typedef struct CMB { + uint16_t ssch_rsch_count; + uint16_t sample_count; + uint32_t device_connect_time; + uint32_t function_pending_time; + uint32_t device_disconnect_time; + uint32_t control_unit_queuing_time; + uint32_t device_active_only_time; + uint32_t reserved[2]; +} QEMU_PACKED CMB; + +typedef struct CMBE { + uint32_t ssch_rsch_count; + uint32_t sample_count; + uint32_t device_connect_time; + uint32_t function_pending_time; + uint32_t device_disconnect_time; + uint32_t control_unit_queuing_time; + uint32_t device_active_only_time; + uint32_t device_busy_time; + uint32_t initial_command_response_time; + uint32_t reserved[7]; +} QEMU_PACKED CMBE; + +struct SubchDev { + /* channel-subsystem related things: */ + uint8_t cssid; + uint8_t ssid; + uint16_t schid; + uint16_t devno; + SCHIB curr_status; + uint8_t sense_data[32]; + hwaddr channel_prog; + CCW1 last_cmd; + bool last_cmd_valid; + ORB *orb; + /* transport-provided data: */ + int (*ccw_cb) (SubchDev *, CCW1); + SenseId id; + void *driver_data; +}; + +typedef SubchDev *(*css_subch_cb_func)(uint8_t m, uint8_t cssid, uint8_t ssid, + uint16_t schid); +int css_create_css_image(uint8_t cssid, bool default_image); +bool css_devno_used(uint8_t cssid, uint8_t ssid, uint16_t devno); +void css_subch_assign(uint8_t cssid, uint8_t ssid, uint16_t schid, + uint16_t devno, SubchDev *sch); +void css_sch_build_virtual_schib(SubchDev *sch, uint8_t chpid, uint8_t type); +void css_reset(void); +void css_reset_sch(SubchDev *sch); +void css_queue_crw(uint8_t rsc, uint8_t erc, int chain, uint16_t rsid); +void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid, + int hotplugged, int add); +void css_generate_chp_crws(uint8_t cssid, uint8_t chpid); +#endif diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index 7cbbf99fde..206d552e16 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -58,10 +58,12 @@ typedef struct S390IPLState { static void s390_ipl_cpu(uint64_t pswaddr) { - CPUS390XState *env = &S390_CPU(qemu_get_cpu(0))->env; + S390CPU *cpu = S390_CPU(qemu_get_cpu(0)); + CPUS390XState *env = &cpu->env; + env->psw.addr = pswaddr; env->psw.mask = IPL_PSW_MASK; - s390_add_running_cpu(env); + s390_add_running_cpu(cpu); } static int s390_ipl_init(SysBusDevice *dev) @@ -159,7 +161,7 @@ static void s390_ipl_class_init(ObjectClass *klass, void *data) dc->no_user = 1; } -static TypeInfo s390_ipl_info = { +static const TypeInfo s390_ipl_info = { .class_init = s390_ipl_class_init, .parent = TYPE_SYS_BUS_DEVICE, .name = "s390-ipl", diff --git a/hw/s390-virtio-bus.c b/hw/s390x/s390-virtio-bus.c index b5d1f2be16..d4677814ca 100644 --- a/hw/s390-virtio-bus.c +++ b/hw/s390x/s390-virtio-bus.c @@ -17,12 +17,12 @@ * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ -#include "hw.h" +#include "hw/hw.h" #include "block/block.h" #include "sysemu/sysemu.h" -#include "boards.h" +#include "hw/boards.h" #include "monitor/monitor.h" -#include "loader.h" +#include "hw/loader.h" #include "elf.h" #include "hw/virtio.h" #include "hw/virtio-rng.h" @@ -31,7 +31,7 @@ #include "hw/sysbus.h" #include "sysemu/kvm.h" -#include "hw/s390-virtio-bus.h" +#include "hw/s390x/s390-virtio-bus.h" #include "hw/virtio-bus.h" /* #define DEBUG_S390 */ @@ -113,12 +113,10 @@ VirtIOS390Bus *s390_virtio_bus_init(ram_addr_t *ram_size) static void s390_virtio_irq(S390CPU *cpu, int config_change, uint64_t token) { - CPUS390XState *env = &cpu->env; - if (kvm_enabled()) { kvm_s390_virtio_irq(cpu, config_change, token); } else { - cpu_inject_ext(env, VIRTIO_EXT_CODE, config_change, token); + cpu_inject_ext(cpu, VIRTIO_EXT_CODE, config_change, token); } } @@ -508,6 +506,13 @@ static int s390_virtio_busdev_init(DeviceState *dev) return _info->init(_dev); } +static void s390_virtio_busdev_reset(DeviceState *dev) +{ + VirtIOS390Device *_dev = (VirtIOS390Device *)dev; + + virtio_reset(_dev->vdev); +} + static void virtio_s390_device_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -515,6 +520,7 @@ static void virtio_s390_device_class_init(ObjectClass *klass, void *data) dc->init = s390_virtio_busdev_init; dc->bus_type = TYPE_S390_VIRTIO_BUS; dc->unplug = qdev_simple_unplug_cb; + dc->reset = s390_virtio_busdev_reset; } static const TypeInfo virtio_s390_device_info = { diff --git a/hw/s390-virtio-bus.h b/hw/s390x/s390-virtio-bus.h index 438b37fd82..4aacf83998 100644 --- a/hw/s390-virtio-bus.h +++ b/hw/s390x/s390-virtio-bus.h @@ -19,12 +19,12 @@ #ifndef HW_S390_VIRTIO_BUS_H #define HW_S390_VIRTIO_BUS_H 1 -#include "virtio-blk.h" -#include "virtio-net.h" -#include "virtio-rng.h" -#include "virtio-serial.h" -#include "virtio-scsi.h" -#include "virtio-bus.h" +#include "hw/virtio-blk.h" +#include "hw/virtio-net.h" +#include "hw/virtio-rng.h" +#include "hw/virtio-serial.h" +#include "hw/virtio-scsi.h" +#include "hw/virtio-bus.h" #define VIRTIO_DEV_OFFS_TYPE 0 /* 8 bits */ #define VIRTIO_DEV_OFFS_NUM_VQ 1 /* 8 bits */ diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c new file mode 100644 index 0000000000..6549211820 --- /dev/null +++ b/hw/s390x/s390-virtio-ccw.c @@ -0,0 +1,134 @@ +/* + * virtio ccw machine + * + * Copyright 2012 IBM Corp. + * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#include "hw/boards.h" +#include "exec/address-spaces.h" +#include "s390-virtio.h" +#include "sclp.h" +#include "ioinst.h" +#include "css.h" +#include "virtio-ccw.h" + +static int virtio_ccw_hcall_notify(const uint64_t *args) +{ + uint64_t subch_id = args[0]; + uint64_t queue = args[1]; + SubchDev *sch; + int cssid, ssid, schid, m; + + if (ioinst_disassemble_sch_ident(subch_id, &m, &cssid, &ssid, &schid)) { + return -EINVAL; + } + sch = css_find_subch(m, cssid, ssid, schid); + if (!sch || !css_subch_visible(sch)) { + return -EINVAL; + } + virtio_queue_notify(virtio_ccw_get_vdev(sch), queue); + return 0; + +} + +static int virtio_ccw_hcall_early_printk(const uint64_t *args) +{ + uint64_t mem = args[0]; + + if (mem < ram_size) { + /* Early printk */ + return 0; + } + return -EINVAL; +} + +static void virtio_ccw_register_hcalls(void) +{ + s390_register_virtio_hypercall(KVM_S390_VIRTIO_CCW_NOTIFY, + virtio_ccw_hcall_notify); + /* Tolerate early printk. */ + s390_register_virtio_hypercall(KVM_S390_VIRTIO_NOTIFY, + virtio_ccw_hcall_early_printk); +} + +static void ccw_init(QEMUMachineInitArgs *args) +{ + ram_addr_t my_ram_size = args->ram_size; + MemoryRegion *sysmem = get_system_memory(); + MemoryRegion *ram = g_new(MemoryRegion, 1); + int shift = 0; + uint8_t *storage_keys; + int ret; + VirtualCssBus *css_bus; + + /* s390x ram size detection needs a 16bit multiplier + an increment. So + guests > 64GB can be specified in 2MB steps etc. */ + while ((my_ram_size >> (20 + shift)) > 65535) { + shift++; + } + my_ram_size = my_ram_size >> (20 + shift) << (20 + shift); + + /* lets propagate the changed ram size into the global variable. */ + ram_size = my_ram_size; + + /* get a BUS */ + css_bus = virtual_css_bus_init(); + s390_sclp_init(); + s390_init_ipl_dev(args->kernel_filename, args->kernel_cmdline, + args->initrd_filename); + + /* register hypercalls */ + virtio_ccw_register_hcalls(); + + /* allocate RAM */ + memory_region_init_ram(ram, "s390.ram", my_ram_size); + vmstate_register_ram_global(ram); + memory_region_add_subregion(sysmem, 0, ram); + + /* allocate storage keys */ + storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE); + + /* init CPUs */ + s390_init_cpus(args->cpu_model, storage_keys); + + if (kvm_enabled()) { + kvm_s390_enable_css_support(s390_cpu_addr2state(0)); + } + /* + * Create virtual css and set it as default so that non mcss-e + * enabled guests only see virtio devices. + */ + ret = css_create_css_image(VIRTUAL_CSSID, true); + assert(ret == 0); + + /* Create VirtIO network adapters */ + s390_create_virtio_net(BUS(css_bus), "virtio-net-ccw"); +} + +static QEMUMachine ccw_machine = { + .name = "s390-ccw-virtio", + .alias = "s390-ccw", + .desc = "VirtIO-ccw based S390 machine", + .init = ccw_init, + .block_default_type = IF_VIRTIO, + .no_cdrom = 1, + .no_floppy = 1, + .no_serial = 1, + .no_parallel = 1, + .no_sdcard = 1, + .use_sclp = 1, + .max_cpus = 255, + DEFAULT_MACHINE_OPTIONS, +}; + +static void ccw_machine_init(void) +{ + qemu_register_machine(&ccw_machine); +} + +machine_init(ccw_machine_init) diff --git a/hw/s390x/s390-virtio-hcall.c b/hw/s390x/s390-virtio-hcall.c index d7938c0734..ee626493c6 100644 --- a/hw/s390x/s390-virtio-hcall.c +++ b/hw/s390x/s390-virtio-hcall.c @@ -10,7 +10,7 @@ */ #include "cpu.h" -#include "hw/s390-virtio.h" +#include "hw/s390x/s390-virtio.h" #define MAX_DIAG_SUBCODES 255 diff --git a/hw/s390-virtio.c b/hw/s390x/s390-virtio.c index 5edaabb7c4..e25c330320 100644 --- a/hw/s390-virtio.c +++ b/hw/s390x/s390-virtio.c @@ -21,22 +21,22 @@ * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ -#include "hw.h" +#include "hw/hw.h" #include "block/block.h" #include "sysemu/blockdev.h" #include "sysemu/sysemu.h" #include "net/net.h" -#include "boards.h" +#include "hw/boards.h" #include "monitor/monitor.h" -#include "loader.h" +#include "hw/loader.h" #include "hw/virtio.h" #include "hw/sysbus.h" #include "sysemu/kvm.h" #include "exec/address-spaces.h" -#include "hw/s390-virtio-bus.h" +#include "hw/s390x/s390-virtio-bus.h" #include "hw/s390x/sclp.h" -#include "hw/s390-virtio.h" +#include "hw/s390x/s390-virtio.h" //#define DEBUG_S390 @@ -86,6 +86,9 @@ static int s390_virtio_hcall_reset(const uint64_t *args) VirtIOS390Device *dev; dev = s390_virtio_bus_find_mem(s390_bus, mem); + if (dev == NULL) { + return -EINVAL; + } virtio_reset(dev->vdev); stb_phys(dev->dev_offs + VIRTIO_DEV_OFFS_STATUS, 0); s390_virtio_device_sync(dev); @@ -127,8 +130,10 @@ static void s390_virtio_register_hcalls(void) */ static unsigned s390_running_cpus; -void s390_add_running_cpu(CPUS390XState *env) +void s390_add_running_cpu(S390CPU *cpu) { + CPUS390XState *env = &cpu->env; + if (env->halted) { s390_running_cpus++; env->halted = 0; @@ -136,8 +141,10 @@ void s390_add_running_cpu(CPUS390XState *env) } } -unsigned s390_del_running_cpu(CPUS390XState *env) +unsigned s390_del_running_cpu(S390CPU *cpu) { + CPUS390XState *env = &cpu->env; + if (env->halted == 0) { assert(s390_running_cpus >= 1); s390_running_cpus--; @@ -147,13 +154,73 @@ unsigned s390_del_running_cpu(CPUS390XState *env) return s390_running_cpus; } +void s390_init_ipl_dev(const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename) +{ + DeviceState *dev; + + dev = qdev_create(NULL, "s390-ipl"); + if (kernel_filename) { + qdev_prop_set_string(dev, "kernel", kernel_filename); + } + if (initrd_filename) { + qdev_prop_set_string(dev, "initrd", initrd_filename); + } + qdev_prop_set_string(dev, "cmdline", kernel_cmdline); + qdev_init_nofail(dev); +} + +void s390_init_cpus(const char *cpu_model, uint8_t *storage_keys) +{ + int i; + + if (cpu_model == NULL) { + cpu_model = "host"; + } + + ipi_states = g_malloc(sizeof(S390CPU *) * smp_cpus); + + for (i = 0; i < smp_cpus; i++) { + S390CPU *cpu; + + cpu = cpu_s390x_init(cpu_model); + + ipi_states[i] = cpu; + cpu->env.halted = 1; + cpu->env.exception_index = EXCP_HLT; + cpu->env.storage_keys = storage_keys; + } +} + + +void s390_create_virtio_net(BusState *bus, const char *name) +{ + int i; + + for (i = 0; i < nb_nics; i++) { + NICInfo *nd = &nd_table[i]; + DeviceState *dev; + + if (!nd->model) { + nd->model = g_strdup("virtio"); + } + + if (strcmp(nd->model, "virtio")) { + fprintf(stderr, "S390 only supports VirtIO nics\n"); + exit(1); + } + + dev = qdev_create(bus, name); + qdev_set_nic_properties(dev, nd); + qdev_init_nofail(dev); + } +} + /* PC hardware initialisation */ static void s390_init(QEMUMachineInitArgs *args) { ram_addr_t my_ram_size = args->ram_size; - const char *cpu_model = args->cpu_model; - CPUS390XState *env = NULL; - DeviceState *dev; MemoryRegion *sysmem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); int shift = 0; @@ -161,7 +228,6 @@ static void s390_init(QEMUMachineInitArgs *args) void *virtio_region; hwaddr virtio_region_len; hwaddr virtio_region_start; - int i; /* s390x ram size detection needs a 16bit multiplier + an increment. So guests > 64GB can be specified in 2MB steps etc. */ @@ -176,15 +242,8 @@ static void s390_init(QEMUMachineInitArgs *args) /* get a BUS */ s390_bus = s390_virtio_bus_init(&my_ram_size); s390_sclp_init(); - dev = qdev_create(NULL, "s390-ipl"); - if (args->kernel_filename) { - qdev_prop_set_string(dev, "kernel", args->kernel_filename); - } - if (args->initrd_filename) { - qdev_prop_set_string(dev, "initrd", args->initrd_filename); - } - qdev_prop_set_string(dev, "cmdline", args->kernel_cmdline); - qdev_init_nofail(dev); + s390_init_ipl_dev(args->kernel_filename, args->kernel_cmdline, + args->initrd_filename); /* register hypercalls */ s390_virtio_register_hcalls(); @@ -207,46 +266,10 @@ static void s390_init(QEMUMachineInitArgs *args) storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE); /* init CPUs */ - if (cpu_model == NULL) { - cpu_model = "host"; - } - - ipi_states = g_malloc(sizeof(S390CPU *) * smp_cpus); - - for (i = 0; i < smp_cpus; i++) { - S390CPU *cpu; - CPUS390XState *tmp_env; - - cpu = cpu_s390x_init(cpu_model); - tmp_env = &cpu->env; - if (!env) { - env = tmp_env; - } - ipi_states[i] = cpu; - tmp_env->halted = 1; - tmp_env->exception_index = EXCP_HLT; - tmp_env->storage_keys = storage_keys; - } - + s390_init_cpus(args->cpu_model, storage_keys); /* Create VirtIO network adapters */ - for(i = 0; i < nb_nics; i++) { - NICInfo *nd = &nd_table[i]; - DeviceState *dev; - - if (!nd->model) { - nd->model = g_strdup("virtio"); - } - - if (strcmp(nd->model, "virtio")) { - fprintf(stderr, "S390 only supports VirtIO nics\n"); - exit(1); - } - - dev = qdev_create((BusState *)s390_bus, "virtio-net-s390"); - qdev_set_nic_properties(dev, nd); - qdev_init_nofail(dev); - } + s390_create_virtio_net((BusState *)s390_bus, "virtio-net-s390"); } static QEMUMachine s390_machine = { diff --git a/hw/s390-virtio.h b/hw/s390x/s390-virtio.h index 25bb610fd8..a6c4c19895 100644 --- a/hw/s390-virtio.h +++ b/hw/s390x/s390-virtio.h @@ -15,8 +15,14 @@ #define KVM_S390_VIRTIO_NOTIFY 0 #define KVM_S390_VIRTIO_RESET 1 #define KVM_S390_VIRTIO_SET_STATUS 2 +#define KVM_S390_VIRTIO_CCW_NOTIFY 3 typedef int (*s390_virtio_fn)(const uint64_t *args); void s390_register_virtio_hypercall(uint64_t code, s390_virtio_fn fn); +void s390_init_cpus(const char *cpu_model, uint8_t *storage_keys); +void s390_init_ipl_dev(const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename); +void s390_create_virtio_net(BusState *bus, const char *name); #endif diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c new file mode 100644 index 0000000000..231f81e48c --- /dev/null +++ b/hw/s390x/virtio-ccw.c @@ -0,0 +1,960 @@ +/* + * virtio ccw target implementation + * + * Copyright 2012 IBM Corp. + * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#include "hw/hw.h" +#include "block/block.h" +#include "sysemu/blockdev.h" +#include "sysemu/sysemu.h" +#include "net/net.h" +#include "monitor/monitor.h" +#include "hw/virtio.h" +#include "hw/virtio-serial.h" +#include "hw/virtio-net.h" +#include "hw/sysbus.h" +#include "qemu/bitops.h" +#include "hw/virtio-bus.h" + +#include "ioinst.h" +#include "css.h" +#include "virtio-ccw.h" +#include "trace.h" + +static int virtual_css_bus_reset(BusState *qbus) +{ + /* This should actually be modelled via the generic css */ + css_reset(); + + /* we dont traverse ourself, return 0 */ + return 0; +} + + +static void virtual_css_bus_class_init(ObjectClass *klass, void *data) +{ + BusClass *k = BUS_CLASS(klass); + + k->reset = virtual_css_bus_reset; +} + +static const TypeInfo virtual_css_bus_info = { + .name = TYPE_VIRTUAL_CSS_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(VirtualCssBus), + .class_init = virtual_css_bus_class_init, +}; + +static const VirtIOBindings virtio_ccw_bindings; + +VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch) +{ + VirtIODevice *vdev = NULL; + + if (sch->driver_data) { + vdev = ((VirtioCcwDevice *)sch->driver_data)->vdev; + } + return vdev; +} + +VirtualCssBus *virtual_css_bus_init(void) +{ + VirtualCssBus *cbus; + BusState *bus; + DeviceState *dev; + + /* Create bridge device */ + dev = qdev_create(NULL, "virtual-css-bridge"); + qdev_init_nofail(dev); + + /* Create bus on bridge device */ + bus = qbus_create(TYPE_VIRTUAL_CSS_BUS, dev, "virtual-css"); + cbus = VIRTUAL_CSS_BUS(bus); + + /* Enable hotplugging */ + bus->allow_hotplug = 1; + + return cbus; +} + +/* Communication blocks used by several channel commands. */ +typedef struct VqInfoBlock { + uint64_t queue; + uint32_t align; + uint16_t index; + uint16_t num; +} QEMU_PACKED VqInfoBlock; + +typedef struct VqConfigBlock { + uint16_t index; + uint16_t num_max; +} QEMU_PACKED VqConfigBlock; + +typedef struct VirtioFeatDesc { + uint32_t features; + uint8_t index; +} QEMU_PACKED VirtioFeatDesc; + +/* Specify where the virtqueues for the subchannel are in guest memory. */ +static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align, + uint16_t index, uint16_t num) +{ + VirtioCcwDevice *dev = sch->driver_data; + + if (index > VIRTIO_PCI_QUEUE_MAX) { + return -EINVAL; + } + + /* Current code in virtio.c relies on 4K alignment. */ + if (addr && (align != 4096)) { + return -EINVAL; + } + + if (!dev) { + return -EINVAL; + } + + virtio_queue_set_addr(dev->vdev, index, addr); + if (!addr) { + virtio_queue_set_vector(dev->vdev, index, 0); + } else { + /* Fail if we don't have a big enough queue. */ + /* TODO: Add interface to handle vring.num changing */ + if (virtio_queue_get_num(dev->vdev, index) > num) { + return -EINVAL; + } + virtio_queue_set_vector(dev->vdev, index, index); + } + /* tell notify handler in case of config change */ + dev->vdev->config_vector = VIRTIO_PCI_QUEUE_MAX; + return 0; +} + +static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) +{ + int ret; + VqInfoBlock info; + uint8_t status; + VirtioFeatDesc features; + void *config; + hwaddr indicators; + VqConfigBlock vq_config; + VirtioCcwDevice *dev = sch->driver_data; + bool check_len; + int len; + hwaddr hw_len; + + if (!dev) { + return -EINVAL; + } + + trace_virtio_ccw_interpret_ccw(sch->cssid, sch->ssid, sch->schid, + ccw.cmd_code); + check_len = !((ccw.flags & CCW_FLAG_SLI) && !(ccw.flags & CCW_FLAG_DC)); + + /* Look at the command. */ + switch (ccw.cmd_code) { + case CCW_CMD_SET_VQ: + if (check_len) { + if (ccw.count != sizeof(info)) { + ret = -EINVAL; + break; + } + } else if (ccw.count < sizeof(info)) { + /* Can't execute command. */ + ret = -EINVAL; + break; + } + if (!ccw.cda) { + ret = -EFAULT; + } else { + info.queue = ldq_phys(ccw.cda); + info.align = ldl_phys(ccw.cda + sizeof(info.queue)); + info.index = lduw_phys(ccw.cda + sizeof(info.queue) + + sizeof(info.align)); + info.num = lduw_phys(ccw.cda + sizeof(info.queue) + + sizeof(info.align) + + sizeof(info.index)); + ret = virtio_ccw_set_vqs(sch, info.queue, info.align, info.index, + info.num); + sch->curr_status.scsw.count = 0; + } + break; + case CCW_CMD_VDEV_RESET: + virtio_reset(dev->vdev); + ret = 0; + break; + case CCW_CMD_READ_FEAT: + if (check_len) { + if (ccw.count != sizeof(features)) { + ret = -EINVAL; + break; + } + } else if (ccw.count < sizeof(features)) { + /* Can't execute command. */ + ret = -EINVAL; + break; + } + if (!ccw.cda) { + ret = -EFAULT; + } else { + features.index = ldub_phys(ccw.cda + sizeof(features.features)); + if (features.index < ARRAY_SIZE(dev->host_features)) { + features.features = dev->host_features[features.index]; + } else { + /* Return zeroes if the guest supports more feature bits. */ + features.features = 0; + } + stl_le_phys(ccw.cda, features.features); + sch->curr_status.scsw.count = ccw.count - sizeof(features); + ret = 0; + } + break; + case CCW_CMD_WRITE_FEAT: + if (check_len) { + if (ccw.count != sizeof(features)) { + ret = -EINVAL; + break; + } + } else if (ccw.count < sizeof(features)) { + /* Can't execute command. */ + ret = -EINVAL; + break; + } + if (!ccw.cda) { + ret = -EFAULT; + } else { + features.index = ldub_phys(ccw.cda + sizeof(features.features)); + features.features = ldl_le_phys(ccw.cda); + if (features.index < ARRAY_SIZE(dev->host_features)) { + if (dev->vdev->set_features) { + dev->vdev->set_features(dev->vdev, features.features); + } + dev->vdev->guest_features = features.features; + } else { + /* + * If the guest supports more feature bits, assert that it + * passes us zeroes for those we don't support. + */ + if (features.features) { + fprintf(stderr, "Guest bug: features[%i]=%x (expected 0)\n", + features.index, features.features); + /* XXX: do a unit check here? */ + } + } + sch->curr_status.scsw.count = ccw.count - sizeof(features); + ret = 0; + } + break; + case CCW_CMD_READ_CONF: + if (check_len) { + if (ccw.count > dev->vdev->config_len) { + ret = -EINVAL; + break; + } + } + len = MIN(ccw.count, dev->vdev->config_len); + if (!ccw.cda) { + ret = -EFAULT; + } else { + dev->vdev->get_config(dev->vdev, dev->vdev->config); + /* XXX config space endianness */ + cpu_physical_memory_write(ccw.cda, dev->vdev->config, len); + sch->curr_status.scsw.count = ccw.count - len; + ret = 0; + } + break; + case CCW_CMD_WRITE_CONF: + if (check_len) { + if (ccw.count > dev->vdev->config_len) { + ret = -EINVAL; + break; + } + } + len = MIN(ccw.count, dev->vdev->config_len); + hw_len = len; + if (!ccw.cda) { + ret = -EFAULT; + } else { + config = cpu_physical_memory_map(ccw.cda, &hw_len, 0); + if (!config) { + ret = -EFAULT; + } else { + len = hw_len; + /* XXX config space endianness */ + memcpy(dev->vdev->config, config, len); + cpu_physical_memory_unmap(config, hw_len, 0, hw_len); + if (dev->vdev->set_config) { + dev->vdev->set_config(dev->vdev, dev->vdev->config); + } + sch->curr_status.scsw.count = ccw.count - len; + ret = 0; + } + } + break; + case CCW_CMD_WRITE_STATUS: + if (check_len) { + if (ccw.count != sizeof(status)) { + ret = -EINVAL; + break; + } + } else if (ccw.count < sizeof(status)) { + /* Can't execute command. */ + ret = -EINVAL; + break; + } + if (!ccw.cda) { + ret = -EFAULT; + } else { + status = ldub_phys(ccw.cda); + virtio_set_status(dev->vdev, status); + if (dev->vdev->status == 0) { + virtio_reset(dev->vdev); + } + sch->curr_status.scsw.count = ccw.count - sizeof(status); + ret = 0; + } + break; + case CCW_CMD_SET_IND: + if (check_len) { + if (ccw.count != sizeof(indicators)) { + ret = -EINVAL; + break; + } + } else if (ccw.count < sizeof(indicators)) { + /* Can't execute command. */ + ret = -EINVAL; + break; + } + indicators = ldq_phys(ccw.cda); + if (!indicators) { + ret = -EFAULT; + } else { + dev->indicators = indicators; + sch->curr_status.scsw.count = ccw.count - sizeof(indicators); + ret = 0; + } + break; + case CCW_CMD_SET_CONF_IND: + if (check_len) { + if (ccw.count != sizeof(indicators)) { + ret = -EINVAL; + break; + } + } else if (ccw.count < sizeof(indicators)) { + /* Can't execute command. */ + ret = -EINVAL; + break; + } + indicators = ldq_phys(ccw.cda); + if (!indicators) { + ret = -EFAULT; + } else { + dev->indicators2 = indicators; + sch->curr_status.scsw.count = ccw.count - sizeof(indicators); + ret = 0; + } + break; + case CCW_CMD_READ_VQ_CONF: + if (check_len) { + if (ccw.count != sizeof(vq_config)) { + ret = -EINVAL; + break; + } + } else if (ccw.count < sizeof(vq_config)) { + /* Can't execute command. */ + ret = -EINVAL; + break; + } + if (!ccw.cda) { + ret = -EFAULT; + } else { + vq_config.index = lduw_phys(ccw.cda); + vq_config.num_max = virtio_queue_get_num(dev->vdev, + vq_config.index); + stw_phys(ccw.cda + sizeof(vq_config.index), vq_config.num_max); + sch->curr_status.scsw.count = ccw.count - sizeof(vq_config); + ret = 0; + } + break; + default: + ret = -ENOSYS; + break; + } + return ret; +} + +static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) +{ + unsigned int cssid = 0; + unsigned int ssid = 0; + unsigned int schid; + unsigned int devno; + bool have_devno = false; + bool found = false; + SubchDev *sch; + int ret; + int num; + DeviceState *parent = DEVICE(dev); + + sch = g_malloc0(sizeof(SubchDev)); + + sch->driver_data = dev; + dev->sch = sch; + + dev->vdev = vdev; + dev->indicators = 0; + + /* Initialize subchannel structure. */ + sch->channel_prog = 0x0; + sch->last_cmd_valid = false; + sch->orb = NULL; + /* + * Use a device number if provided. Otherwise, fall back to subchannel + * number. + */ + if (dev->bus_id) { + num = sscanf(dev->bus_id, "%x.%x.%04x", &cssid, &ssid, &devno); + if (num == 3) { + if ((cssid > MAX_CSSID) || (ssid > MAX_SSID)) { + ret = -EINVAL; + error_report("Invalid cssid or ssid: cssid %x, ssid %x", + cssid, ssid); + goto out_err; + } + /* Enforce use of virtual cssid. */ + if (cssid != VIRTUAL_CSSID) { + ret = -EINVAL; + error_report("cssid %x not valid for virtio devices", cssid); + goto out_err; + } + if (css_devno_used(cssid, ssid, devno)) { + ret = -EEXIST; + error_report("Device %x.%x.%04x already exists", cssid, ssid, + devno); + goto out_err; + } + sch->cssid = cssid; + sch->ssid = ssid; + sch->devno = devno; + have_devno = true; + } else { + ret = -EINVAL; + error_report("Malformed devno parameter '%s'", dev->bus_id); + goto out_err; + } + } + + /* Find the next free id. */ + if (have_devno) { + for (schid = 0; schid <= MAX_SCHID; schid++) { + if (!css_find_subch(1, cssid, ssid, schid)) { + sch->schid = schid; + css_subch_assign(cssid, ssid, schid, devno, sch); + found = true; + break; + } + } + if (!found) { + ret = -ENODEV; + error_report("No free subchannel found for %x.%x.%04x", cssid, ssid, + devno); + goto out_err; + } + trace_virtio_ccw_new_device(cssid, ssid, schid, devno, + "user-configured"); + } else { + cssid = VIRTUAL_CSSID; + for (ssid = 0; ssid <= MAX_SSID; ssid++) { + for (schid = 0; schid <= MAX_SCHID; schid++) { + if (!css_find_subch(1, cssid, ssid, schid)) { + sch->cssid = cssid; + sch->ssid = ssid; + sch->schid = schid; + devno = schid; + /* + * If the devno is already taken, look further in this + * subchannel set. + */ + while (css_devno_used(cssid, ssid, devno)) { + if (devno == MAX_SCHID) { + devno = 0; + } else if (devno == schid - 1) { + ret = -ENODEV; + error_report("No free devno found"); + goto out_err; + } else { + devno++; + } + } + sch->devno = devno; + css_subch_assign(cssid, ssid, schid, devno, sch); + found = true; + break; + } + } + if (found) { + break; + } + } + if (!found) { + ret = -ENODEV; + error_report("Virtual channel subsystem is full!"); + goto out_err; + } + trace_virtio_ccw_new_device(cssid, ssid, schid, devno, + "auto-configured"); + } + + /* Build initial schib. */ + css_sch_build_virtual_schib(sch, 0, VIRTIO_CCW_CHPID_TYPE); + + sch->ccw_cb = virtio_ccw_cb; + + /* Build senseid data. */ + memset(&sch->id, 0, sizeof(SenseId)); + sch->id.reserved = 0xff; + sch->id.cu_type = VIRTIO_CCW_CU_TYPE; + sch->id.cu_model = dev->vdev->device_id; + + virtio_bind_device(vdev, &virtio_ccw_bindings, DEVICE(dev)); + /* Only the first 32 feature bits are used. */ + dev->host_features[0] = vdev->get_features(vdev, dev->host_features[0]); + dev->host_features[0] |= 0x1 << VIRTIO_F_NOTIFY_ON_EMPTY; + dev->host_features[0] |= 0x1 << VIRTIO_F_BAD_FEATURE; + + css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, + parent->hotplugged, 1); + return 0; + +out_err: + dev->sch = NULL; + g_free(sch); + return ret; +} + +static int virtio_ccw_exit(VirtioCcwDevice *dev) +{ + SubchDev *sch = dev->sch; + + if (sch) { + css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); + g_free(sch); + } + dev->indicators = 0; + return 0; +} + +static int virtio_ccw_net_init(VirtioCcwDevice *dev) +{ + VirtIODevice *vdev; + + vdev = virtio_net_init((DeviceState *)dev, &dev->nic, &dev->net); + if (!vdev) { + return -1; + } + + return virtio_ccw_device_init(dev, vdev); +} + +static int virtio_ccw_net_exit(VirtioCcwDevice *dev) +{ + virtio_net_exit(dev->vdev); + return virtio_ccw_exit(dev); +} + +static int virtio_ccw_blk_init(VirtioCcwDevice *dev) +{ + VirtIODevice *vdev; + + vdev = virtio_blk_init((DeviceState *)dev, &dev->blk); + if (!vdev) { + return -1; + } + + return virtio_ccw_device_init(dev, vdev); +} + +static int virtio_ccw_blk_exit(VirtioCcwDevice *dev) +{ + virtio_blk_exit(dev->vdev); + blockdev_mark_auto_del(dev->blk.conf.bs); + return virtio_ccw_exit(dev); +} + +static int virtio_ccw_serial_init(VirtioCcwDevice *dev) +{ + VirtIODevice *vdev; + + vdev = virtio_serial_init((DeviceState *)dev, &dev->serial); + if (!vdev) { + return -1; + } + + return virtio_ccw_device_init(dev, vdev); +} + +static int virtio_ccw_serial_exit(VirtioCcwDevice *dev) +{ + virtio_serial_exit(dev->vdev); + return virtio_ccw_exit(dev); +} + +static int virtio_ccw_balloon_init(VirtioCcwDevice *dev) +{ + VirtIODevice *vdev; + + vdev = virtio_balloon_init((DeviceState *)dev); + if (!vdev) { + return -1; + } + + return virtio_ccw_device_init(dev, vdev); +} + +static int virtio_ccw_balloon_exit(VirtioCcwDevice *dev) +{ + virtio_balloon_exit(dev->vdev); + return virtio_ccw_exit(dev); +} + +static int virtio_ccw_scsi_init(VirtioCcwDevice *dev) +{ + VirtIODevice *vdev; + + vdev = virtio_scsi_init((DeviceState *)dev, &dev->scsi); + if (!vdev) { + return -1; + } + + return virtio_ccw_device_init(dev, vdev); +} + +static int virtio_ccw_scsi_exit(VirtioCcwDevice *dev) +{ + virtio_scsi_exit(dev->vdev); + return virtio_ccw_exit(dev); +} + +/* DeviceState to VirtioCcwDevice. Note: used on datapath, + * be careful and test performance if you change this. + */ +static inline VirtioCcwDevice *to_virtio_ccw_dev_fast(DeviceState *d) +{ + return container_of(d, VirtioCcwDevice, parent_obj); +} + +static void virtio_ccw_notify(DeviceState *d, uint16_t vector) +{ + VirtioCcwDevice *dev = to_virtio_ccw_dev_fast(d); + SubchDev *sch = dev->sch; + uint64_t indicators; + + if (vector >= 128) { + return; + } + + if (vector < VIRTIO_PCI_QUEUE_MAX) { + indicators = ldq_phys(dev->indicators); + indicators |= 1ULL << vector; + stq_phys(dev->indicators, indicators); + } else { + vector = 0; + indicators = ldq_phys(dev->indicators2); + indicators |= 1ULL << vector; + stq_phys(dev->indicators2, indicators); + } + + css_conditional_io_interrupt(sch); + +} + +static unsigned virtio_ccw_get_features(DeviceState *d) +{ + VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); + + /* Only the first 32 feature bits are used. */ + return dev->host_features[0]; +} + +static void virtio_ccw_reset(DeviceState *d) +{ + VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); + + virtio_reset(dev->vdev); + css_reset_sch(dev->sch); +} + +/**************** Virtio-ccw Bus Device Descriptions *******************/ + +static const VirtIOBindings virtio_ccw_bindings = { + .notify = virtio_ccw_notify, + .get_features = virtio_ccw_get_features, +}; + +static Property virtio_ccw_net_properties[] = { + DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), + DEFINE_VIRTIO_NET_FEATURES(VirtioCcwDevice, host_features[0]), + DEFINE_NIC_PROPERTIES(VirtioCcwDevice, nic), + DEFINE_PROP_UINT32("x-txtimer", VirtioCcwDevice, + net.txtimer, TX_TIMER_INTERVAL), + DEFINE_PROP_INT32("x-txburst", VirtioCcwDevice, + net.txburst, TX_BURST), + DEFINE_PROP_STRING("tx", VirtioCcwDevice, net.tx), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_ccw_net_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); + + k->init = virtio_ccw_net_init; + k->exit = virtio_ccw_net_exit; + dc->reset = virtio_ccw_reset; + dc->props = virtio_ccw_net_properties; +} + +static const TypeInfo virtio_ccw_net = { + .name = "virtio-net-ccw", + .parent = TYPE_VIRTIO_CCW_DEVICE, + .instance_size = sizeof(VirtioCcwDevice), + .class_init = virtio_ccw_net_class_init, +}; + +static Property virtio_ccw_blk_properties[] = { + DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), + DEFINE_BLOCK_PROPERTIES(VirtioCcwDevice, blk.conf), + DEFINE_PROP_STRING("serial", VirtioCcwDevice, blk.serial), +#ifdef __linux__ + DEFINE_PROP_BIT("scsi", VirtioCcwDevice, blk.scsi, 0, true), +#endif + DEFINE_VIRTIO_BLK_FEATURES(VirtioCcwDevice, host_features[0]), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_ccw_blk_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); + + k->init = virtio_ccw_blk_init; + k->exit = virtio_ccw_blk_exit; + dc->reset = virtio_ccw_reset; + dc->props = virtio_ccw_blk_properties; +} + +static const TypeInfo virtio_ccw_blk = { + .name = "virtio-blk-ccw", + .parent = TYPE_VIRTIO_CCW_DEVICE, + .instance_size = sizeof(VirtioCcwDevice), + .class_init = virtio_ccw_blk_class_init, +}; + +static Property virtio_ccw_serial_properties[] = { + DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), + DEFINE_PROP_UINT32("max_ports", VirtioCcwDevice, + serial.max_virtserial_ports, 31), + DEFINE_VIRTIO_COMMON_FEATURES(VirtioCcwDevice, host_features[0]), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_ccw_serial_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); + + k->init = virtio_ccw_serial_init; + k->exit = virtio_ccw_serial_exit; + dc->reset = virtio_ccw_reset; + dc->props = virtio_ccw_serial_properties; +} + +static const TypeInfo virtio_ccw_serial = { + .name = "virtio-serial-ccw", + .parent = TYPE_VIRTIO_CCW_DEVICE, + .instance_size = sizeof(VirtioCcwDevice), + .class_init = virtio_ccw_serial_class_init, +}; + +static Property virtio_ccw_balloon_properties[] = { + DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), + DEFINE_VIRTIO_COMMON_FEATURES(VirtioCcwDevice, host_features[0]), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_ccw_balloon_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); + + k->init = virtio_ccw_balloon_init; + k->exit = virtio_ccw_balloon_exit; + dc->reset = virtio_ccw_reset; + dc->props = virtio_ccw_balloon_properties; +} + +static const TypeInfo virtio_ccw_balloon = { + .name = "virtio-balloon-ccw", + .parent = TYPE_VIRTIO_CCW_DEVICE, + .instance_size = sizeof(VirtioCcwDevice), + .class_init = virtio_ccw_balloon_class_init, +}; + +static Property virtio_ccw_scsi_properties[] = { + DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), + DEFINE_VIRTIO_SCSI_PROPERTIES(VirtioCcwDevice, host_features[0], scsi), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_ccw_scsi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); + + k->init = virtio_ccw_scsi_init; + k->exit = virtio_ccw_scsi_exit; + dc->reset = virtio_ccw_reset; + dc->props = virtio_ccw_scsi_properties; +} + +static const TypeInfo virtio_ccw_scsi = { + .name = "virtio-scsi-ccw", + .parent = TYPE_VIRTIO_CCW_DEVICE, + .instance_size = sizeof(VirtioCcwDevice), + .class_init = virtio_ccw_scsi_class_init, +}; + +static int virtio_ccw_busdev_init(DeviceState *dev) +{ + VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev; + VirtIOCCWDeviceClass *_info = VIRTIO_CCW_DEVICE_GET_CLASS(dev); + + virtio_ccw_bus_new(&_dev->bus, _dev); + + return _info->init(_dev); +} + +static int virtio_ccw_busdev_exit(DeviceState *dev) +{ + VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev; + VirtIOCCWDeviceClass *_info = VIRTIO_CCW_DEVICE_GET_CLASS(dev); + + return _info->exit(_dev); +} + +static int virtio_ccw_busdev_unplug(DeviceState *dev) +{ + VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev; + SubchDev *sch = _dev->sch; + + /* + * We should arrive here only for device_del, since we don't support + * direct hot(un)plug of channels, but only through virtio. + */ + assert(sch != NULL); + /* Subchannel is now disabled and no longer valid. */ + sch->curr_status.pmcw.flags &= ~(PMCW_FLAGS_MASK_ENA | + PMCW_FLAGS_MASK_DNV); + + css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, 1, 0); + + object_unparent(OBJECT(dev)); + qdev_free(dev); + return 0; +} + +static void virtio_ccw_device_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->init = virtio_ccw_busdev_init; + dc->exit = virtio_ccw_busdev_exit; + dc->unplug = virtio_ccw_busdev_unplug; + dc->bus_type = TYPE_VIRTUAL_CSS_BUS; + +} + +static const TypeInfo virtio_ccw_device_info = { + .name = TYPE_VIRTIO_CCW_DEVICE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(VirtioCcwDevice), + .class_init = virtio_ccw_device_class_init, + .class_size = sizeof(VirtIOCCWDeviceClass), + .abstract = true, +}; + +/***************** Virtual-css Bus Bridge Device ********************/ +/* Only required to have the virtio bus as child in the system bus */ + +static int virtual_css_bridge_init(SysBusDevice *dev) +{ + /* nothing */ + return 0; +} + +static void virtual_css_bridge_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = virtual_css_bridge_init; + dc->no_user = 1; +} + +static const TypeInfo virtual_css_bridge_info = { + .name = "virtual-css-bridge", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SysBusDevice), + .class_init = virtual_css_bridge_class_init, +}; + +/* virtio-ccw-bus */ + +void virtio_ccw_bus_new(VirtioBusState *bus, VirtioCcwDevice *dev) +{ + DeviceState *qdev = DEVICE(dev); + BusState *qbus; + + qbus_create_inplace((BusState *)bus, TYPE_VIRTIO_CCW_BUS, qdev, NULL); + qbus = BUS(bus); + qbus->allow_hotplug = 0; +} + +static void virtio_ccw_bus_class_init(ObjectClass *klass, void *data) +{ + VirtioBusClass *k = VIRTIO_BUS_CLASS(klass); + BusClass *bus_class = BUS_CLASS(klass); + + bus_class->max_dev = 1; + k->notify = virtio_ccw_notify; + k->get_features = virtio_ccw_get_features; +} + +static const TypeInfo virtio_ccw_bus_info = { + .name = TYPE_VIRTIO_CCW_BUS, + .parent = TYPE_VIRTIO_BUS, + .instance_size = sizeof(VirtioCcwBusState), + .class_init = virtio_ccw_bus_class_init, +}; + +static void virtio_ccw_register(void) +{ + type_register_static(&virtio_ccw_bus_info); + type_register_static(&virtual_css_bus_info); + type_register_static(&virtio_ccw_device_info); + type_register_static(&virtio_ccw_serial); + type_register_static(&virtio_ccw_blk); + type_register_static(&virtio_ccw_net); + type_register_static(&virtio_ccw_balloon); + type_register_static(&virtio_ccw_scsi); + type_register_static(&virtual_css_bridge_info); +} + +type_init(virtio_ccw_register) diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h new file mode 100644 index 0000000000..48474b31ab --- /dev/null +++ b/hw/s390x/virtio-ccw.h @@ -0,0 +1,98 @@ +/* + * virtio ccw target definitions + * + * Copyright 2012 IBM Corp. + * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#ifndef HW_S390X_VIRTIO_CCW_H +#define HW_S390X_VIRTIO_CCW_H + +#include <hw/virtio-blk.h> +#include <hw/virtio-net.h> +#include <hw/virtio-serial.h> +#include <hw/virtio-scsi.h> +#include <hw/virtio-bus.h> + +#define VIRTUAL_CSSID 0xfe + +#define VIRTIO_CCW_CU_TYPE 0x3832 +#define VIRTIO_CCW_CHPID_TYPE 0x32 + +#define CCW_CMD_SET_VQ 0x13 +#define CCW_CMD_VDEV_RESET 0x33 +#define CCW_CMD_READ_FEAT 0x12 +#define CCW_CMD_WRITE_FEAT 0x11 +#define CCW_CMD_READ_CONF 0x22 +#define CCW_CMD_WRITE_CONF 0x21 +#define CCW_CMD_WRITE_STATUS 0x31 +#define CCW_CMD_SET_IND 0x43 +#define CCW_CMD_SET_CONF_IND 0x53 +#define CCW_CMD_READ_VQ_CONF 0x32 + +#define TYPE_VIRTIO_CCW_DEVICE "virtio-ccw-device" +#define VIRTIO_CCW_DEVICE(obj) \ + OBJECT_CHECK(VirtioCcwDevice, (obj), TYPE_VIRTIO_CCW_DEVICE) +#define VIRTIO_CCW_DEVICE_CLASS(klass) \ + OBJECT_CLASS_CHECK(VirtIOCCWDeviceClass, (klass), TYPE_VIRTIO_CCW_DEVICE) +#define VIRTIO_CCW_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(VirtIOCCWDeviceClass, (obj), TYPE_VIRTIO_CCW_DEVICE) + +typedef struct VirtioBusState VirtioCcwBusState; +typedef struct VirtioBusClass VirtioCcwBusClass; + +#define TYPE_VIRTIO_CCW_BUS "virtio-ccw-bus" +#define VIRTIO_CCW_BUS(obj) \ + OBJECT_CHECK(VirtioCcwBus, (obj), TYPE_VIRTIO_CCW_BUS) +#define VIRTIO_CCW_BUS_GET_CLASS(obj) \ + OBJECT_CHECK(VirtioCcwBusState, (obj), TYPE_VIRTIO_CCW_BUS) +#define VIRTIO_CCW_BUS_CLASS(klass) \ + OBJECT_CLASS_CHECK(VirtioCcwBusClass, klass, TYPE_VIRTIO_CCW_BUS) + +typedef struct VirtioCcwDevice VirtioCcwDevice; + +void virtio_ccw_bus_new(VirtioBusState *bus, VirtioCcwDevice *dev); + +typedef struct VirtIOCCWDeviceClass { + DeviceClass parent_class; + int (*init)(VirtioCcwDevice *dev); + int (*exit)(VirtioCcwDevice *dev); +} VirtIOCCWDeviceClass; + +/* Change here if we want to support more feature bits. */ +#define VIRTIO_CCW_FEATURE_SIZE 1 + +struct VirtioCcwDevice { + DeviceState parent_obj; + SubchDev *sch; + VirtIODevice *vdev; + char *bus_id; + VirtIOBlkConf blk; + NICConf nic; + uint32_t host_features[VIRTIO_CCW_FEATURE_SIZE]; + virtio_serial_conf serial; + virtio_net_conf net; + VirtIOSCSIConf scsi; + VirtioBusState bus; + /* Guest provided values: */ + hwaddr indicators; + hwaddr indicators2; +}; + +/* virtual css bus type */ +typedef struct VirtualCssBus { + BusState parent_obj; +} VirtualCssBus; + +#define TYPE_VIRTUAL_CSS_BUS "virtual-css-bus" +#define VIRTUAL_CSS_BUS(obj) \ + OBJECT_CHECK(VirtualCssBus, (obj), TYPE_VIRTUAL_CSS_BUS) + +VirtualCssBus *virtual_css_bus_init(void); +void virtio_ccw_device_update_status(SubchDev *sch); +VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch); +#endif diff --git a/hw/smc91c111.c b/hw/smc91c111.c index 36cb4ed74f..67fd074d85 100644 --- a/hw/smc91c111.c +++ b/hw/smc91c111.c @@ -237,7 +237,7 @@ static void smc91c111_do_tx(smc91c111_state *s) smc91c111_release_packet(s, packetnum); else if (s->tx_fifo_done_len < NUM_PACKETS) s->tx_fifo_done[s->tx_fifo_done_len++] = packetnum; - qemu_send_packet(&s->nic->nc, p, len); + qemu_send_packet(qemu_get_queue(s->nic), p, len); } s->tx_fifo_len = 0; smc91c111_update(s); @@ -442,6 +442,7 @@ static void smc91c111_writeb(void *opaque, hwaddr offset, return; case 12: /* Early receive. */ s->ercv = value & 0x1f; + return; case 13: /* Ignore. */ return; @@ -630,7 +631,7 @@ static uint32_t smc91c111_readl(void *opaque, hwaddr offset) static int smc91c111_can_receive(NetClientState *nc) { - smc91c111_state *s = DO_UPCAST(NICState, nc, nc)->opaque; + smc91c111_state *s = qemu_get_nic_opaque(nc); if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST)) return 1; @@ -641,7 +642,7 @@ static int smc91c111_can_receive(NetClientState *nc) static ssize_t smc91c111_receive(NetClientState *nc, const uint8_t *buf, size_t size) { - smc91c111_state *s = DO_UPCAST(NICState, nc, nc)->opaque; + smc91c111_state *s = qemu_get_nic_opaque(nc); int status; int packetsize; uint32_t crc; @@ -730,7 +731,7 @@ static const MemoryRegionOps smc91c111_mem_ops = { static void smc91c111_cleanup(NetClientState *nc) { - smc91c111_state *s = DO_UPCAST(NICState, nc, nc)->opaque; + smc91c111_state *s = qemu_get_nic_opaque(nc); s->nic = NULL; } @@ -753,7 +754,7 @@ static int smc91c111_init1(SysBusDevice *dev) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_smc91c111_info, &s->conf, object_get_typename(OBJECT(dev)), dev->qdev.id, s); - qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); /* ??? Save/restore. */ return 0; } diff --git a/hw/spapr.c b/hw/spapr.c index d80b792b37..e88a27aa71 100644 --- a/hw/spapr.c +++ b/hw/spapr.c @@ -77,12 +77,6 @@ #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 SPAPR_PCI_MSI_WIN_ADDR (0x10000000000ULL + 0x90000000) - #define PHANDLE_XICP 0x00001111 #define HTAB_SIZE(spapr) (1ULL << ((spapr)->htab_shift)) @@ -857,12 +851,7 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) /* Set up PCI */ spapr_pci_rtas_init(); - spapr_create_phb(spapr, "pci", SPAPR_PCI_BUID, - SPAPR_PCI_MEM_WIN_ADDR, - SPAPR_PCI_MEM_WIN_SIZE, - SPAPR_PCI_IO_WIN_ADDR, - SPAPR_PCI_MSI_WIN_ADDR); - phb = PCI_HOST_BRIDGE(QLIST_FIRST(&spapr->phbs)); + phb = spapr_create_phb(spapr, 0, "pci"); for (i = 0; i < nb_nics; i++) { NICInfo *nd = &nd_table[i]; diff --git a/hw/spapr_llan.c b/hw/spapr_llan.c index db34b485aa..6ef29362f5 100644 --- a/hw/spapr_llan.c +++ b/hw/spapr_llan.c @@ -85,7 +85,7 @@ typedef struct VIOsPAPRVLANDevice { static int spapr_vlan_can_receive(NetClientState *nc) { - VIOsPAPRVLANDevice *dev = DO_UPCAST(NICState, nc, nc)->opaque; + VIOsPAPRVLANDevice *dev = qemu_get_nic_opaque(nc); return (dev->isopen && dev->rx_bufs > 0); } @@ -93,7 +93,7 @@ static int spapr_vlan_can_receive(NetClientState *nc) static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf, size_t size) { - VIOsPAPRDevice *sdev = DO_UPCAST(NICState, nc, nc)->opaque; + VIOsPAPRDevice *sdev = qemu_get_nic_opaque(nc); VIOsPAPRVLANDevice *dev = (VIOsPAPRVLANDevice *)sdev; vlan_bd_t rxq_bd = vio_ldq(sdev, dev->buf_list + VLAN_RXQ_BD_OFF); vlan_bd_t bd; @@ -199,7 +199,7 @@ static int spapr_vlan_init(VIOsPAPRDevice *sdev) dev->nic = qemu_new_nic(&net_spapr_vlan_info, &dev->nicconf, object_get_typename(OBJECT(sdev)), sdev->qdev.id, dev); - qemu_format_nic_info_str(&dev->nic->nc, dev->nicconf.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(dev->nic), dev->nicconf.macaddr.a); return 0; } @@ -462,7 +462,7 @@ static target_ulong h_send_logical_lan(PowerPCCPU *cpu, sPAPREnvironment *spapr, p += VLAN_BD_LEN(bufs[i]); } - qemu_send_packet(&dev->nic->nc, lbuf, total_len); + qemu_send_packet(qemu_get_queue(dev->nic), lbuf, total_len); return H_SUCCESS; } diff --git a/hw/spapr_pci.c b/hw/spapr_pci.c index bbcc9fc968..4eacbcfd58 100644 --- a/hw/spapr_pci.c +++ b/hw/spapr_pci.c @@ -435,7 +435,7 @@ static void pci_spapr_set_irq(void *opaque, int irq_num, int level) */ sPAPRPHBState *phb = opaque; - trace_spapr_pci_lsi_set(phb->busname, irq_num, phb->lsi_table[irq_num].irq); + trace_spapr_pci_lsi_set(phb->dtbusname, irq_num, phb->lsi_table[irq_num].irq); qemu_set_irq(spapr_phb_lsi_qirq(phb, irq_num), level); } @@ -522,7 +522,63 @@ static int spapr_phb_init(SysBusDevice *s) int i; PCIBus *bus; + if (sphb->index != -1) { + hwaddr windows_base; + + if ((sphb->buid != -1) || (sphb->dma_liobn != -1) + || (sphb->mem_win_addr != -1) + || (sphb->io_win_addr != -1) + || (sphb->msi_win_addr != -1)) { + fprintf(stderr, "Either \"index\" or other parameters must" + " be specified for PAPR PHB, not both\n"); + return -1; + } + + sphb->buid = SPAPR_PCI_BASE_BUID + sphb->index; + sphb->dma_liobn = SPAPR_PCI_BASE_LIOBN + sphb->index; + + windows_base = SPAPR_PCI_WINDOW_BASE + + sphb->index * SPAPR_PCI_WINDOW_SPACING; + sphb->mem_win_addr = windows_base + SPAPR_PCI_MMIO_WIN_OFF; + sphb->io_win_addr = windows_base + SPAPR_PCI_IO_WIN_OFF; + sphb->msi_win_addr = windows_base + SPAPR_PCI_MSI_WIN_OFF; + } + + if (sphb->buid == -1) { + fprintf(stderr, "BUID not specified for PHB\n"); + return -1; + } + + if (sphb->dma_liobn == -1) { + fprintf(stderr, "LIOBN not specified for PHB\n"); + return -1; + } + + if (sphb->mem_win_addr == -1) { + fprintf(stderr, "Memory window address not specified for PHB\n"); + return -1; + } + + if (sphb->io_win_addr == -1) { + fprintf(stderr, "IO window address not specified for PHB\n"); + return -1; + } + + if (sphb->msi_win_addr == -1) { + fprintf(stderr, "MSI window address not specified for PHB\n"); + return -1; + } + + if (find_phb(spapr, sphb->buid)) { + fprintf(stderr, "PCI host bridges must have unique BUIDs\n"); + return -1; + } + sphb->dtbusname = g_strdup_printf("pci@%" PRIx64, sphb->buid); + if (!sphb->busname) { + sphb->busname = sphb->dtbusname; + } + namebuf = alloca(strlen(sphb->dtbusname) + 32); /* Initialize memory regions */ @@ -565,17 +621,19 @@ static int spapr_phb_init(SysBusDevice *s) &sphb->msiwindow); } - bus = pci_register_bus(DEVICE(s), - sphb->busname ? sphb->busname : sphb->dtbusname, + bus = pci_register_bus(DEVICE(s), sphb->busname, pci_spapr_set_irq, pci_spapr_map_irq, sphb, &sphb->memspace, &sphb->iospace, PCI_DEVFN(0, 0), PCI_NUM_PINS); phb->bus = bus; - sphb->dma_liobn = SPAPR_PCI_BASE_LIOBN | (pci_find_domain(bus) << 16); sphb->dma_window_start = 0; sphb->dma_window_size = 0x40000000; sphb->dma = spapr_tce_new_dma_context(sphb->dma_liobn, sphb->dma_window_size); + if (!sphb->dma) { + fprintf(stderr, "Unable to create TCE table for %s\n", sphb->dtbusname); + return -1; + } pci_setup_iommu(bus, spapr_pci_dma_context_fn, sphb); QLIST_INSERT_HEAD(&spapr->phbs, sphb, list); @@ -605,13 +663,17 @@ static void spapr_phb_reset(DeviceState *qdev) } static Property spapr_phb_properties[] = { - DEFINE_PROP_HEX64("buid", sPAPRPHBState, buid, 0), DEFINE_PROP_STRING("busname", sPAPRPHBState, busname), - DEFINE_PROP_HEX64("mem_win_addr", sPAPRPHBState, mem_win_addr, 0), - DEFINE_PROP_HEX64("mem_win_size", sPAPRPHBState, mem_win_size, 0x20000000), - DEFINE_PROP_HEX64("io_win_addr", sPAPRPHBState, io_win_addr, 0), - DEFINE_PROP_HEX64("io_win_size", sPAPRPHBState, io_win_size, 0x10000), - DEFINE_PROP_HEX64("msi_win_addr", sPAPRPHBState, msi_win_addr, 0), + DEFINE_PROP_INT32("index", sPAPRPHBState, index, -1), + DEFINE_PROP_HEX64("buid", sPAPRPHBState, buid, -1), + DEFINE_PROP_HEX32("liobn", sPAPRPHBState, dma_liobn, -1), + DEFINE_PROP_HEX64("mem_win_addr", sPAPRPHBState, mem_win_addr, -1), + DEFINE_PROP_HEX64("mem_win_size", sPAPRPHBState, mem_win_size, + SPAPR_PCI_MMIO_WIN_SIZE), + DEFINE_PROP_HEX64("io_win_addr", sPAPRPHBState, io_win_addr, -1), + DEFINE_PROP_HEX64("io_win_size", sPAPRPHBState, io_win_size, + SPAPR_PCI_IO_WIN_SIZE), + DEFINE_PROP_HEX64("msi_win_addr", sPAPRPHBState, msi_win_addr, -1), DEFINE_PROP_END_OF_LIST(), }; @@ -632,25 +694,17 @@ static const TypeInfo spapr_phb_info = { .class_init = spapr_phb_class_init, }; -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, uint64_t msi_win_addr) +PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index, + const char *busname) { DeviceState *dev; dev = qdev_create(NULL, TYPE_SPAPR_PCI_HOST_BRIDGE); - - if (busname) { - qdev_prop_set_string(dev, "busname", g_strdup(busname)); - } - qdev_prop_set_uint64(dev, "buid", buid); - qdev_prop_set_uint64(dev, "mem_win_addr", mem_win_addr); - qdev_prop_set_uint64(dev, "mem_win_size", mem_win_size); - qdev_prop_set_uint64(dev, "io_win_addr", io_win_addr); - qdev_prop_set_uint64(dev, "msi_win_addr", msi_win_addr); - + qdev_prop_set_uint32(dev, "index", index); + qdev_prop_set_string(dev, "busname", busname); qdev_init_nofail(dev); + + return PCI_HOST_BRIDGE(dev); } /* Macros to operate with address in OF binding to PCI */ diff --git a/hw/spapr_pci.h b/hw/spapr_pci.h index 7b26ba1561..8bb3c62c3d 100644 --- a/hw/spapr_pci.h +++ b/hw/spapr_pci.h @@ -37,6 +37,7 @@ typedef struct sPAPRPHBState { PCIHostState parent_obj; + int32_t index; uint64_t buid; char *busname; char *dtbusname; @@ -64,18 +65,25 @@ typedef struct sPAPRPHBState { QLIST_ENTRY(sPAPRPHBState) list; } sPAPRPHBState; +#define SPAPR_PCI_BASE_BUID 0x800000020000000ULL + +#define SPAPR_PCI_WINDOW_BASE 0x10000000000ULL +#define SPAPR_PCI_WINDOW_SPACING 0x1000000000ULL +#define SPAPR_PCI_MMIO_WIN_OFF 0xA0000000 +#define SPAPR_PCI_MMIO_WIN_SIZE 0x20000000 +#define SPAPR_PCI_IO_WIN_OFF 0x80000000 +#define SPAPR_PCI_IO_WIN_SIZE 0x10000 +#define SPAPR_PCI_MSI_WIN_OFF 0x90000000 + +#define SPAPR_PCI_MEM_WIN_BUS_OFFSET 0x80000000ULL + static inline qemu_irq spapr_phb_lsi_qirq(struct sPAPRPHBState *phb, int pin) { return xics_get_qirq(spapr->icp, phb->lsi_table[pin].irq); } -#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, uint64_t msi_win_addr); +PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index, + const char *busname); int spapr_populate_pci_dt(sPAPRPHBState *phb, uint32_t xics_phandle, diff --git a/hw/spapr_vio.c b/hw/spapr_vio.c index 2054219c95..34c9ca6b65 100644 --- a/hw/spapr_vio.c +++ b/hw/spapr_vio.c @@ -492,7 +492,7 @@ VIOsPAPRBus *spapr_vio_bus_init(void) qbus = qbus_create(TYPE_SPAPR_VIO_BUS, dev, "spapr-vio"); bus = DO_UPCAST(VIOsPAPRBus, bus, qbus); - bus->next_reg = 0x1000; + bus->next_reg = 0x71000000; /* hcall-vio */ spapr_register_hypercall(H_VIO_SIGNAL, h_vio_signal); diff --git a/hw/stellaris_enet.c b/hw/stellaris_enet.c index 5e9053fa26..6c701fb67b 100644 --- a/hw/stellaris_enet.c +++ b/hw/stellaris_enet.c @@ -80,7 +80,7 @@ static void stellaris_enet_update(stellaris_enet_state *s) /* TODO: Implement MAC address filtering. */ static ssize_t stellaris_enet_receive(NetClientState *nc, const uint8_t *buf, size_t size) { - stellaris_enet_state *s = DO_UPCAST(NICState, nc, nc)->opaque; + stellaris_enet_state *s = qemu_get_nic_opaque(nc); int n; uint8_t *p; uint32_t crc; @@ -122,7 +122,7 @@ static ssize_t stellaris_enet_receive(NetClientState *nc, const uint8_t *buf, si static int stellaris_enet_can_receive(NetClientState *nc) { - stellaris_enet_state *s = DO_UPCAST(NICState, nc, nc)->opaque; + stellaris_enet_state *s = qemu_get_nic_opaque(nc); if ((s->rctl & SE_RCTL_RXEN) == 0) return 1; @@ -259,7 +259,8 @@ static void stellaris_enet_write(void *opaque, hwaddr offset, memset(&s->tx_fifo[s->tx_frame_len], 0, 60 - s->tx_frame_len); s->tx_fifo_len = 60; } - qemu_send_packet(&s->nic->nc, s->tx_fifo, s->tx_frame_len); + qemu_send_packet(qemu_get_queue(s->nic), s->tx_fifo, + s->tx_frame_len); s->tx_frame_len = -1; s->ris |= SE_INT_TXEMP; stellaris_enet_update(s); @@ -383,7 +384,7 @@ static int stellaris_enet_load(QEMUFile *f, void *opaque, int version_id) static void stellaris_enet_cleanup(NetClientState *nc) { - stellaris_enet_state *s = DO_UPCAST(NICState, nc, nc)->opaque; + stellaris_enet_state *s = qemu_get_nic_opaque(nc); unregister_savevm(&s->busdev.qdev, "stellaris_enet", s); @@ -412,7 +413,7 @@ static int stellaris_enet_init(SysBusDevice *dev) s->nic = qemu_new_nic(&net_stellaris_enet_info, &s->conf, object_get_typename(OBJECT(dev)), dev->qdev.id, s); - qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); stellaris_enet_reset(s); register_savevm(&s->busdev.qdev, "stellaris_enet", -1, 1, diff --git a/hw/sun4m.c b/hw/sun4m.c index 035a011768..9903f443cb 100644 --- a/hw/sun4m.c +++ b/hw/sun4m.c @@ -1021,6 +1021,7 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, ram_addr_t RAM_size, hwdef->ecc_version); fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2); + fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1); fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, hwdef->machine_id); @@ -1665,6 +1666,7 @@ static void sun4d_hw_init(const struct sun4d_hwdef *hwdef, ram_addr_t RAM_size, "Sun4d"); fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2); + fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1); fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, hwdef->machine_id); @@ -1865,6 +1867,7 @@ static void sun4c_hw_init(const struct sun4c_hwdef *hwdef, ram_addr_t RAM_size, "Sun4c"); fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2); + fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1); fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, hwdef->machine_id); diff --git a/hw/sun4u.c b/hw/sun4u.c index b891b84c9c..9fbda29ac4 100644 --- a/hw/sun4u.c +++ b/hw/sun4u.c @@ -878,6 +878,7 @@ static void sun4uv_init(MemoryRegion *address_space_mem, (uint8_t *)&nd_table[0].macaddr); fw_cfg = fw_cfg_init(BIOS_CFG_IOPORT, BIOS_CFG_IOPORT + 1, 0, 0); + fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1); fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, hwdef->machine_id); diff --git a/hw/unin_pci.c b/hw/unin_pci.c index 46757924b6..f1c3c20f37 100644 --- a/hw/unin_pci.c +++ b/hw/unin_pci.c @@ -22,7 +22,7 @@ * THE SOFTWARE. */ #include "hw.h" -#include "ppc_mac.h" +#include "ppc/mac.h" #include "pci/pci.h" #include "pci/pci_host.h" diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c index 9dede4c68d..a01a5e793a 100644 --- a/hw/usb/dev-network.c +++ b/hw/usb/dev-network.c @@ -1012,7 +1012,7 @@ static int rndis_keepalive_response(USBNetState *s, static void usb_net_reset_in_buf(USBNetState *s) { s->in_ptr = s->in_len = 0; - qemu_flush_queued_packets(&s->nic->nc); + qemu_flush_queued_packets(qemu_get_queue(s->nic)); } static int rndis_parse(USBNetState *s, uint8_t *data, int length) @@ -1196,7 +1196,7 @@ static void usb_net_handle_dataout(USBNetState *s, USBPacket *p) if (!is_rndis(s)) { if (p->iov.size < 64) { - qemu_send_packet(&s->nic->nc, s->out_buf, s->out_ptr); + qemu_send_packet(qemu_get_queue(s->nic), s->out_buf, s->out_ptr); s->out_ptr = 0; } return; @@ -1209,7 +1209,7 @@ static void usb_net_handle_dataout(USBNetState *s, USBPacket *p) uint32_t offs = 8 + le32_to_cpu(msg->DataOffset); uint32_t size = le32_to_cpu(msg->DataLength); if (offs + size <= len) - qemu_send_packet(&s->nic->nc, s->out_buf + offs, size); + qemu_send_packet(qemu_get_queue(s->nic), s->out_buf + offs, size); } s->out_ptr -= len; memmove(s->out_buf, &s->out_buf[len], s->out_ptr); @@ -1261,7 +1261,7 @@ static void usb_net_handle_data(USBDevice *dev, USBPacket *p) static ssize_t usbnet_receive(NetClientState *nc, const uint8_t *buf, size_t size) { - USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque; + USBNetState *s = qemu_get_nic_opaque(nc); uint8_t *in_buf = s->in_buf; size_t total_size = size; @@ -1308,7 +1308,7 @@ static ssize_t usbnet_receive(NetClientState *nc, const uint8_t *buf, size_t siz static int usbnet_can_receive(NetClientState *nc) { - USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque; + USBNetState *s = qemu_get_nic_opaque(nc); if (is_rndis(s) && s->rndis_state != RNDIS_DATA_INITIALIZED) { return 1; @@ -1319,7 +1319,7 @@ static int usbnet_can_receive(NetClientState *nc) static void usbnet_cleanup(NetClientState *nc) { - USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque; + USBNetState *s = qemu_get_nic_opaque(nc); s->nic = NULL; } @@ -1330,7 +1330,7 @@ static void usb_net_handle_destroy(USBDevice *dev) /* TODO: remove the nd_table[] entry */ rndis_clear_responsequeue(s); - qemu_del_net_client(&s->nic->nc); + qemu_del_nic(s->nic); } static NetClientInfo net_usbnet_info = { @@ -1361,7 +1361,7 @@ static int usb_net_initfn(USBDevice *dev) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_usbnet_info, &s->conf, object_get_typename(OBJECT(s)), s->dev.qdev.id, s); - qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); snprintf(s->usbstring_mac, sizeof(s->usbstring_mac), "%02x%02x%02x%02x%02x%02x", 0x40, diff --git a/hw/vexpress.c b/hw/vexpress.c index 7f0897c773..741b044f1d 100644 --- a/hw/vexpress.c +++ b/hw/vexpress.c @@ -271,7 +271,7 @@ static void a15_daughterboard_init(const VEDBoardInfo *daughterboard, cpu_model = "cortex-a15"; } - *proc_id = 0x14000217; + *proc_id = 0x14000237; for (n = 0; n < smp_cpus; n++) { ARMCPU *cpu; diff --git a/hw/vfio_pci.c b/hw/vfio_pci.c index c51ae6761b..66537b7eb5 100644 --- a/hw/vfio_pci.c +++ b/hw/vfio_pci.c @@ -1899,6 +1899,9 @@ static int vfio_get_device(VFIOGroup *group, const char *name, VFIODevice *vdev) (unsigned long)reg_info.flags); vdev->config_size = reg_info.size; + if (vdev->config_size == PCI_CONFIG_SPACE_SIZE) { + vdev->pdev.cap_present &= ~QEMU_PCI_CAP_EXPRESS; + } vdev->config_offset = reg_info.offset; error: @@ -2121,6 +2124,7 @@ static void vfio_pci_dev_class_init(ObjectClass *klass, void *data) pdc->exit = vfio_exitfn; pdc->config_read = vfio_pci_read_config; pdc->config_write = vfio_pci_write_config; + pdc->is_express = 1; /* We might be */ } static const TypeInfo vfio_pci_dev_info = { diff --git a/hw/vhost.c b/hw/vhost.c index cee8aad4a1..8d41fdb53f 100644 --- a/hw/vhost.c +++ b/hw/vhost.c @@ -269,11 +269,8 @@ static inline void vhost_dev_log_resize(struct vhost_dev* dev, uint64_t size) vhost_log_chunk_t *log; uint64_t log_base; int r, i; - if (size) { - log = g_malloc0(size * sizeof *log); - } else { - log = NULL; - } + + log = g_malloc0(size * sizeof *log); log_base = (uint64_t)(unsigned long)log; r = ioctl(dev->control, VHOST_SET_LOG_BASE, &log_base); assert(r >= 0); @@ -619,14 +616,17 @@ static int vhost_virtqueue_start(struct vhost_dev *dev, { hwaddr s, l, a; int r; + int vhost_vq_index = idx - dev->vq_index; struct vhost_vring_file file = { - .index = idx, + .index = vhost_vq_index }; struct vhost_vring_state state = { - .index = idx, + .index = vhost_vq_index }; struct VirtQueue *vvq = virtio_get_queue(vdev, idx); + assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs); + vq->num = state.num = virtio_queue_get_num(vdev, idx); r = ioctl(dev->control, VHOST_SET_VRING_NUM, &state); if (r) { @@ -669,11 +669,12 @@ static int vhost_virtqueue_start(struct vhost_dev *dev, goto fail_alloc_ring; } - r = vhost_virtqueue_set_addr(dev, vq, idx, dev->log_enabled); + r = vhost_virtqueue_set_addr(dev, vq, vhost_vq_index, dev->log_enabled); if (r < 0) { r = -errno; goto fail_alloc; } + file.fd = event_notifier_get_fd(virtio_queue_get_host_notifier(vvq)); r = ioctl(dev->control, VHOST_SET_VRING_KICK, &file); if (r) { @@ -709,9 +710,10 @@ static void vhost_virtqueue_stop(struct vhost_dev *dev, unsigned idx) { struct vhost_vring_state state = { - .index = idx, + .index = idx - dev->vq_index }; int r; + assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs); r = ioctl(dev->control, VHOST_GET_VRING_BASE, &state); if (r < 0) { fprintf(stderr, "vhost VQ %d ring restore failed: %d\n", idx, r); @@ -867,7 +869,9 @@ int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) } for (i = 0; i < hdev->nvqs; ++i) { - r = vdev->binding->set_host_notifier(vdev->binding_opaque, i, true); + r = vdev->binding->set_host_notifier(vdev->binding_opaque, + hdev->vq_index + i, + true); if (r < 0) { fprintf(stderr, "vhost VQ %d notifier binding failed: %d\n", i, -r); goto fail_vq; @@ -877,7 +881,9 @@ int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) return 0; fail_vq: while (--i >= 0) { - r = vdev->binding->set_host_notifier(vdev->binding_opaque, i, false); + r = vdev->binding->set_host_notifier(vdev->binding_opaque, + hdev->vq_index + i, + false); if (r < 0) { fprintf(stderr, "vhost VQ %d notifier cleanup error: %d\n", i, -r); fflush(stderr); @@ -898,7 +904,9 @@ void vhost_dev_disable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) int i, r; for (i = 0; i < hdev->nvqs; ++i) { - r = vdev->binding->set_host_notifier(vdev->binding_opaque, i, false); + r = vdev->binding->set_host_notifier(vdev->binding_opaque, + hdev->vq_index + i, + false); if (r < 0) { fprintf(stderr, "vhost VQ %d notifier cleanup failed: %d\n", i, -r); fflush(stderr); @@ -912,8 +920,9 @@ void vhost_dev_disable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) */ bool vhost_virtqueue_pending(struct vhost_dev *hdev, int n) { - struct vhost_virtqueue *vq = hdev->vqs + n; + struct vhost_virtqueue *vq = hdev->vqs + n - hdev->vq_index; assert(hdev->started); + assert(n >= hdev->vq_index && n < hdev->vq_index + hdev->nvqs); return event_notifier_test_and_clear(&vq->masked_notifier); } @@ -922,15 +931,16 @@ void vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, int n, bool mask) { struct VirtQueue *vvq = virtio_get_queue(vdev, n); - int r; + int r, index = n - hdev->vq_index; assert(hdev->started); + assert(n >= hdev->vq_index && n < hdev->vq_index + hdev->nvqs); struct vhost_vring_file file = { - .index = n, + .index = index }; if (mask) { - file.fd = event_notifier_get_fd(&hdev->vqs[n].masked_notifier); + file.fd = event_notifier_get_fd(&hdev->vqs[index].masked_notifier); } else { file.fd = event_notifier_get_fd(virtio_queue_get_guest_notifier(vvq)); } @@ -945,20 +955,6 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) hdev->started = true; - if (!vdev->binding->set_guest_notifiers) { - fprintf(stderr, "binding does not support guest notifiers\n"); - r = -ENOSYS; - goto fail; - } - - r = vdev->binding->set_guest_notifiers(vdev->binding_opaque, - hdev->nvqs, - true); - if (r < 0) { - fprintf(stderr, "Error binding guest notifier: %d\n", -r); - goto fail_notifiers; - } - r = vhost_dev_set_features(hdev, hdev->log_enabled); if (r < 0) { goto fail_features; @@ -970,9 +966,9 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) } for (i = 0; i < hdev->nvqs; ++i) { r = vhost_virtqueue_start(hdev, - vdev, - hdev->vqs + i, - i); + vdev, + hdev->vqs + i, + hdev->vq_index + i); if (r < 0) { goto fail_vq; } @@ -995,15 +991,13 @@ fail_log: fail_vq: while (--i >= 0) { vhost_virtqueue_stop(hdev, - vdev, - hdev->vqs + i, - i); + vdev, + hdev->vqs + i, + hdev->vq_index + i); } + i = hdev->nvqs; fail_mem: fail_features: - vdev->binding->set_guest_notifiers(vdev->binding_opaque, hdev->nvqs, false); -fail_notifiers: -fail: hdev->started = false; return r; @@ -1012,29 +1006,22 @@ fail: /* Host notifiers must be enabled at this point. */ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev) { - int i, r; + int i; for (i = 0; i < hdev->nvqs; ++i) { vhost_virtqueue_stop(hdev, - vdev, - hdev->vqs + i, - i); + vdev, + hdev->vqs + i, + hdev->vq_index + i); } for (i = 0; i < hdev->n_mem_sections; ++i) { vhost_sync_dirty_bitmap(hdev, &hdev->mem_sections[i], 0, (hwaddr)~0x0ull); } - r = vdev->binding->set_guest_notifiers(vdev->binding_opaque, - hdev->nvqs, - false); - if (r < 0) { - fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", r); - fflush(stderr); - } - assert (r >= 0); hdev->started = false; g_free(hdev->log); hdev->log = NULL; hdev->log_size = 0; } + diff --git a/hw/vhost.h b/hw/vhost.h index 44c61a5877..f062d48807 100644 --- a/hw/vhost.h +++ b/hw/vhost.h @@ -35,6 +35,8 @@ struct vhost_dev { MemoryRegionSection *mem_sections; struct vhost_virtqueue *vqs; int nvqs; + /* the first virtuque which would be used by this vhost dev */ + int vq_index; unsigned long long features; unsigned long long acked_features; unsigned long long backend_features; diff --git a/hw/vhost_net.c b/hw/vhost_net.c index d3a04caef6..8693ac27f6 100644 --- a/hw/vhost_net.c +++ b/hw/vhost_net.c @@ -140,12 +140,21 @@ bool vhost_net_query(VHostNetState *net, VirtIODevice *dev) return vhost_dev_query(&net->dev, dev); } -int vhost_net_start(struct vhost_net *net, - VirtIODevice *dev) +static int vhost_net_start_one(struct vhost_net *net, + VirtIODevice *dev, + int vq_index) { struct vhost_vring_file file = { }; int r; + if (net->dev.started) { + return 0; + } + + net->dev.nvqs = 2; + net->dev.vqs = net->vqs; + net->dev.vq_index = vq_index; + r = vhost_dev_enable_notifiers(&net->dev, dev); if (r < 0) { goto fail_notifiers; @@ -181,11 +190,15 @@ fail_notifiers: return r; } -void vhost_net_stop(struct vhost_net *net, - VirtIODevice *dev) +static void vhost_net_stop_one(struct vhost_net *net, + VirtIODevice *dev) { struct vhost_vring_file file = { .fd = -1 }; + if (!net->dev.started) { + return; + } + for (file.index = 0; file.index < net->dev.nvqs; ++file.index) { int r = ioctl(net->dev.control, VHOST_NET_SET_BACKEND, &file); assert(r >= 0); @@ -195,6 +208,61 @@ void vhost_net_stop(struct vhost_net *net, vhost_dev_disable_notifiers(&net->dev, dev); } +int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, + int total_queues) +{ + int r, i = 0; + + if (!dev->binding->set_guest_notifiers) { + error_report("binding does not support guest notifiers\n"); + r = -ENOSYS; + goto err; + } + + for (i = 0; i < total_queues; i++) { + r = vhost_net_start_one(tap_get_vhost_net(ncs[i].peer), dev, i * 2); + + if (r < 0) { + goto err; + } + } + + r = dev->binding->set_guest_notifiers(dev->binding_opaque, + total_queues * 2, + true); + if (r < 0) { + error_report("Error binding guest notifier: %d\n", -r); + goto err; + } + + return 0; + +err: + while (--i >= 0) { + vhost_net_stop_one(tap_get_vhost_net(ncs[i].peer), dev); + } + return r; +} + +void vhost_net_stop(VirtIODevice *dev, NetClientState *ncs, + int total_queues) +{ + int i, r; + + r = dev->binding->set_guest_notifiers(dev->binding_opaque, + total_queues * 2, + false); + if (r < 0) { + fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", r); + fflush(stderr); + } + assert(r >= 0); + + for (i = 0; i < total_queues; i++) { + vhost_net_stop_one(tap_get_vhost_net(ncs[i].peer), dev); + } +} + void vhost_net_cleanup(struct vhost_net *net) { vhost_dev_cleanup(&net->dev); @@ -224,13 +292,15 @@ bool vhost_net_query(VHostNetState *net, VirtIODevice *dev) return false; } -int vhost_net_start(struct vhost_net *net, - VirtIODevice *dev) +int vhost_net_start(VirtIODevice *dev, + NetClientState *ncs, + int total_queues) { return -ENOSYS; } -void vhost_net_stop(struct vhost_net *net, - VirtIODevice *dev) +void vhost_net_stop(VirtIODevice *dev, + NetClientState *ncs, + int total_queues) { } diff --git a/hw/vhost_net.h b/hw/vhost_net.h index 88912b85fd..2d936bb5f5 100644 --- a/hw/vhost_net.h +++ b/hw/vhost_net.h @@ -9,8 +9,8 @@ typedef struct vhost_net VHostNetState; VHostNetState *vhost_net_init(NetClientState *backend, int devfd, bool force); bool vhost_net_query(VHostNetState *net, VirtIODevice *dev); -int vhost_net_start(VHostNetState *net, VirtIODevice *dev); -void vhost_net_stop(VHostNetState *net, VirtIODevice *dev); +int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, int total_queues); +void vhost_net_stop(VirtIODevice *dev, NetClientState *ncs, int total_queues); void vhost_net_cleanup(VHostNetState *net); diff --git a/hw/virtio-balloon.c b/hw/virtio-balloon.c index 3040bc63ab..c0a790264c 100644 --- a/hw/virtio-balloon.c +++ b/hw/virtio-balloon.c @@ -14,6 +14,7 @@ */ #include "qemu/iov.h" +#include "qemu/timer.h" #include "qemu-common.h" #include "virtio.h" #include "pc.h" @@ -22,6 +23,7 @@ #include "virtio-balloon.h" #include "sysemu/kvm.h" #include "exec/address-spaces.h" +#include "qapi/visitor.h" #if defined(__linux__) #include <sys/mman.h> @@ -36,6 +38,9 @@ typedef struct VirtIOBalloon uint64_t stats[VIRTIO_BALLOON_S_NR]; VirtQueueElement stats_vq_elem; size_t stats_vq_offset; + QEMUTimer *stats_timer; + int64_t stats_last_update; + int64_t stats_poll_interval; DeviceState *qdev; } VirtIOBalloon; @@ -53,6 +58,16 @@ static void balloon_page(void *addr, int deflate) #endif } +static const char *balloon_stat_names[] = { + [VIRTIO_BALLOON_S_SWAP_IN] = "stat-swap-in", + [VIRTIO_BALLOON_S_SWAP_OUT] = "stat-swap-out", + [VIRTIO_BALLOON_S_MAJFLT] = "stat-major-faults", + [VIRTIO_BALLOON_S_MINFLT] = "stat-minor-faults", + [VIRTIO_BALLOON_S_MEMFREE] = "stat-free-memory", + [VIRTIO_BALLOON_S_MEMTOT] = "stat-total-memory", + [VIRTIO_BALLOON_S_NR] = NULL +}; + /* * reset_stats - Mark all items in the stats array as unset * @@ -67,6 +82,118 @@ static inline void reset_stats(VirtIOBalloon *dev) for (i = 0; i < VIRTIO_BALLOON_S_NR; dev->stats[i++] = -1); } +static bool balloon_stats_supported(const VirtIOBalloon *s) +{ + return s->vdev.guest_features & (1 << VIRTIO_BALLOON_F_STATS_VQ); +} + +static bool balloon_stats_enabled(const VirtIOBalloon *s) +{ + return s->stats_poll_interval > 0; +} + +static void balloon_stats_destroy_timer(VirtIOBalloon *s) +{ + if (balloon_stats_enabled(s)) { + qemu_del_timer(s->stats_timer); + qemu_free_timer(s->stats_timer); + s->stats_timer = NULL; + s->stats_poll_interval = 0; + } +} + +static void balloon_stats_change_timer(VirtIOBalloon *s, int secs) +{ + qemu_mod_timer(s->stats_timer, qemu_get_clock_ms(vm_clock) + secs * 1000); +} + +static void balloon_stats_poll_cb(void *opaque) +{ + VirtIOBalloon *s = opaque; + + if (!balloon_stats_supported(s)) { + /* re-schedule */ + balloon_stats_change_timer(s, s->stats_poll_interval); + return; + } + + virtqueue_push(s->svq, &s->stats_vq_elem, s->stats_vq_offset); + virtio_notify(&s->vdev, s->svq); +} + +static void balloon_stats_get_all(Object *obj, struct Visitor *v, + void *opaque, const char *name, Error **errp) +{ + VirtIOBalloon *s = opaque; + int i; + + if (!s->stats_last_update) { + error_setg(errp, "guest hasn't updated any stats yet"); + return; + } + + visit_start_struct(v, NULL, "guest-stats", name, 0, errp); + visit_type_int(v, &s->stats_last_update, "last-update", errp); + + visit_start_struct(v, NULL, NULL, "stats", 0, errp); + for (i = 0; i < VIRTIO_BALLOON_S_NR; i++) { + visit_type_int64(v, (int64_t *) &s->stats[i], balloon_stat_names[i], + errp); + } + visit_end_struct(v, errp); + + visit_end_struct(v, errp); +} + +static void balloon_stats_get_poll_interval(Object *obj, struct Visitor *v, + void *opaque, const char *name, + Error **errp) +{ + VirtIOBalloon *s = opaque; + visit_type_int(v, &s->stats_poll_interval, name, errp); +} + +static void balloon_stats_set_poll_interval(Object *obj, struct Visitor *v, + void *opaque, const char *name, + Error **errp) +{ + VirtIOBalloon *s = opaque; + int64_t value; + + visit_type_int(v, &value, name, errp); + if (error_is_set(errp)) { + return; + } + + if (value < 0) { + error_setg(errp, "timer value must be greater than zero"); + return; + } + + if (value == s->stats_poll_interval) { + return; + } + + if (value == 0) { + /* timer=0 disables the timer */ + balloon_stats_destroy_timer(s); + return; + } + + if (balloon_stats_enabled(s)) { + /* timer interval change */ + s->stats_poll_interval = value; + balloon_stats_change_timer(s, value); + return; + } + + /* create a new timer */ + g_assert(s->stats_timer == NULL); + s->stats_timer = qemu_new_timer_ms(vm_clock, balloon_stats_poll_cb, s); + s->stats_poll_interval = value; + balloon_stats_change_timer(s, 0); +} + static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq) { VirtIOBalloon *s = to_virtio_balloon(vdev); @@ -107,9 +234,10 @@ static void virtio_balloon_receive_stats(VirtIODevice *vdev, VirtQueue *vq) VirtQueueElement *elem = &s->stats_vq_elem; VirtIOBalloonStat stat; size_t offset = 0; + qemu_timeval tv; if (!virtqueue_pop(vq, elem)) { - return; + goto out; } /* Initialize the stats to get rid of any stale values. This is only @@ -128,6 +256,18 @@ static void virtio_balloon_receive_stats(VirtIODevice *vdev, VirtQueue *vq) s->stats[tag] = val; } s->stats_vq_offset = offset; + + if (qemu_gettimeofday(&tv) < 0) { + fprintf(stderr, "warning: %s: failed to get time of day\n", __func__); + goto out; + } + + s->stats_last_update = tv.tv_sec; + +out: + if (balloon_stats_enabled(s)) { + balloon_stats_change_timer(s, s->stats_poll_interval); + } } static void virtio_balloon_get_config(VirtIODevice *vdev, uint8_t *config_data) @@ -164,28 +304,6 @@ static uint32_t virtio_balloon_get_features(VirtIODevice *vdev, uint32_t f) static void virtio_balloon_stat(void *opaque, BalloonInfo *info) { VirtIOBalloon *dev = opaque; - -#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->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); - info->actual = ram_size - ((uint64_t) dev->actual << VIRTIO_BALLOON_PFN_SHIFT); } @@ -255,12 +373,18 @@ VirtIODevice *virtio_balloon_init(DeviceState *dev) s->dvq = virtio_add_queue(&s->vdev, 128, virtio_balloon_handle_output); s->svq = virtio_add_queue(&s->vdev, 128, virtio_balloon_receive_stats); - reset_stats(s); - s->qdev = dev; register_savevm(dev, "virtio-balloon", -1, 1, virtio_balloon_save, virtio_balloon_load, s); + object_property_add(OBJECT(dev), "guest-stats", "guest statistics", + balloon_stats_get_all, NULL, NULL, s, NULL); + + object_property_add(OBJECT(dev), "guest-stats-polling-interval", "int", + balloon_stats_get_poll_interval, + balloon_stats_set_poll_interval, + NULL, s, NULL); + return &s->vdev; } @@ -268,6 +392,7 @@ void virtio_balloon_exit(VirtIODevice *vdev) { VirtIOBalloon *s = DO_UPCAST(VirtIOBalloon, vdev, vdev); + balloon_stats_destroy_timer(s); qemu_remove_balloon_handler(s); unregister_savevm(s->qdev, "virtio-balloon", s); virtio_cleanup(vdev); diff --git a/hw/virtio-net.c b/hw/virtio-net.c index 3bb01b1037..e37358a40c 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -26,28 +26,33 @@ #define MAC_TABLE_ENTRIES 64 #define MAX_VLAN (1 << 12) /* Per 802.1Q definition */ +typedef struct VirtIONetQueue { + VirtQueue *rx_vq; + VirtQueue *tx_vq; + QEMUTimer *tx_timer; + QEMUBH *tx_bh; + int tx_waiting; + struct { + VirtQueueElement elem; + ssize_t len; + } async_tx; + struct VirtIONet *n; +} VirtIONetQueue; + typedef struct VirtIONet { VirtIODevice vdev; uint8_t mac[ETH_ALEN]; uint16_t status; - VirtQueue *rx_vq; - VirtQueue *tx_vq; + VirtIONetQueue vqs[MAX_QUEUE_NUM]; VirtQueue *ctrl_vq; NICState *nic; - QEMUTimer *tx_timer; - QEMUBH *tx_bh; uint32_t tx_timeout; int32_t tx_burst; - int tx_waiting; uint32_t has_vnet_hdr; size_t host_hdr_len; size_t guest_hdr_len; uint8_t has_ufo; - struct { - VirtQueueElement elem; - ssize_t len; - } async_tx; int mergeable_rx_bufs; uint8_t promisc; uint8_t allmulti; @@ -65,8 +70,23 @@ typedef struct VirtIONet } mac_table; uint32_t *vlans; DeviceState *qdev; + int multiqueue; + uint16_t max_queues; + uint16_t curr_queues; } VirtIONet; +static VirtIONetQueue *virtio_net_get_subqueue(NetClientState *nc) +{ + VirtIONet *n = qemu_get_nic_opaque(nc); + + return &n->vqs[nc->queue_index]; +} + +static int vq2q(int queue_index) +{ + return queue_index / 2; +} + /* TODO * - we could suppress RX interrupt if we were so inclined. */ @@ -82,6 +102,7 @@ static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config) struct virtio_net_config netcfg; stw_p(&netcfg.status, n->status); + stw_p(&netcfg.max_virtqueue_pairs, n->max_queues); memcpy(netcfg.mac, n->mac, ETH_ALEN); memcpy(config, &netcfg, sizeof(netcfg)); } @@ -93,9 +114,10 @@ static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config) memcpy(&netcfg, config, sizeof(netcfg)); - if (memcmp(netcfg.mac, n->mac, ETH_ALEN)) { + if (!(n->vdev.guest_features >> VIRTIO_NET_F_CTRL_MAC_ADDR & 1) && + memcmp(netcfg.mac, n->mac, ETH_ALEN)) { memcpy(n->mac, netcfg.mac, ETH_ALEN); - qemu_format_nic_info_str(&n->nic->nc, n->mac); + qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac); } } @@ -107,34 +129,38 @@ static bool virtio_net_started(VirtIONet *n, uint8_t status) static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) { - if (!n->nic->nc.peer) { + NetClientState *nc = qemu_get_queue(n->nic); + int queues = n->multiqueue ? n->max_queues : 1; + + if (!nc->peer) { return; } - if (n->nic->nc.peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { + if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { return; } - if (!tap_get_vhost_net(n->nic->nc.peer)) { + if (!tap_get_vhost_net(nc->peer)) { return; } + if (!!n->vhost_started == virtio_net_started(n, status) && - !n->nic->nc.peer->link_down) { + !nc->peer->link_down) { return; } if (!n->vhost_started) { int r; - if (!vhost_net_query(tap_get_vhost_net(n->nic->nc.peer), &n->vdev)) { + if (!vhost_net_query(tap_get_vhost_net(nc->peer), &n->vdev)) { return; } n->vhost_started = 1; - r = vhost_net_start(tap_get_vhost_net(n->nic->nc.peer), &n->vdev); + r = vhost_net_start(&n->vdev, n->nic->ncs, queues); if (r < 0) { error_report("unable to start vhost net: %d: " "falling back on userspace virtio", -r); n->vhost_started = 0; } } else { - vhost_net_stop(tap_get_vhost_net(n->nic->nc.peer), &n->vdev); + vhost_net_stop(&n->vdev, n->nic->ncs, queues); n->vhost_started = 0; } } @@ -142,32 +168,45 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status) { VirtIONet *n = to_virtio_net(vdev); + VirtIONetQueue *q; + int i; + uint8_t queue_status; virtio_net_vhost_status(n, status); - if (!n->tx_waiting) { - return; - } + for (i = 0; i < n->max_queues; i++) { + q = &n->vqs[i]; - if (virtio_net_started(n, status) && !n->vhost_started) { - if (n->tx_timer) { - qemu_mod_timer(n->tx_timer, - qemu_get_clock_ns(vm_clock) + n->tx_timeout); + if ((!n->multiqueue && i != 0) || i >= n->curr_queues) { + queue_status = 0; } else { - qemu_bh_schedule(n->tx_bh); + queue_status = status; } - } else { - if (n->tx_timer) { - qemu_del_timer(n->tx_timer); + + if (!q->tx_waiting) { + continue; + } + + if (virtio_net_started(n, queue_status) && !n->vhost_started) { + if (q->tx_timer) { + qemu_mod_timer(q->tx_timer, + qemu_get_clock_ns(vm_clock) + n->tx_timeout); + } else { + qemu_bh_schedule(q->tx_bh); + } } else { - qemu_bh_cancel(n->tx_bh); + if (q->tx_timer) { + qemu_del_timer(q->tx_timer); + } else { + qemu_bh_cancel(q->tx_bh); + } } } } static void virtio_net_set_link_status(NetClientState *nc) { - VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; + VirtIONet *n = qemu_get_nic_opaque(nc); uint16_t old_status = n->status; if (nc->link_down) @@ -192,6 +231,8 @@ static void virtio_net_reset(VirtIODevice *vdev) n->nomulti = 0; n->nouni = 0; n->nobcast = 0; + /* multiqueue is disabled by default */ + n->curr_queues = 1; /* Flush any MAC and VLAN filter table state */ n->mac_table.in_use = 0; @@ -199,18 +240,22 @@ static void virtio_net_reset(VirtIODevice *vdev) n->mac_table.multi_overflow = 0; n->mac_table.uni_overflow = 0; memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN); + memcpy(&n->mac[0], &n->nic->conf->macaddr, sizeof(n->mac)); memset(n->vlans, 0, MAX_VLAN >> 3); } static void peer_test_vnet_hdr(VirtIONet *n) { - if (!n->nic->nc.peer) + NetClientState *nc = qemu_get_queue(n->nic); + if (!nc->peer) { return; + } - if (n->nic->nc.peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) + if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { return; + } - n->has_vnet_hdr = tap_has_vnet_hdr(n->nic->nc.peer); + n->has_vnet_hdr = tap_has_vnet_hdr(nc->peer); } static int peer_has_vnet_hdr(VirtIONet *n) @@ -223,28 +268,81 @@ static int peer_has_ufo(VirtIONet *n) if (!peer_has_vnet_hdr(n)) return 0; - n->has_ufo = tap_has_ufo(n->nic->nc.peer); + n->has_ufo = tap_has_ufo(qemu_get_queue(n->nic)->peer); return n->has_ufo; } static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs) { + int i; + NetClientState *nc; + n->mergeable_rx_bufs = mergeable_rx_bufs; n->guest_hdr_len = n->mergeable_rx_bufs ? sizeof(struct virtio_net_hdr_mrg_rxbuf) : sizeof(struct virtio_net_hdr); - if (peer_has_vnet_hdr(n) && - tap_has_vnet_hdr_len(n->nic->nc.peer, n->guest_hdr_len)) { - tap_set_vnet_hdr_len(n->nic->nc.peer, n->guest_hdr_len); - n->host_hdr_len = n->guest_hdr_len; + for (i = 0; i < n->max_queues; i++) { + nc = qemu_get_subqueue(n->nic, i); + + if (peer_has_vnet_hdr(n) && + tap_has_vnet_hdr_len(nc->peer, n->guest_hdr_len)) { + tap_set_vnet_hdr_len(nc->peer, n->guest_hdr_len); + n->host_hdr_len = n->guest_hdr_len; + } + } +} + +static int peer_attach(VirtIONet *n, int index) +{ + NetClientState *nc = qemu_get_subqueue(n->nic, index); + + if (!nc->peer) { + return 0; + } + + if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { + return 0; + } + + return tap_enable(nc->peer); +} + +static int peer_detach(VirtIONet *n, int index) +{ + NetClientState *nc = qemu_get_subqueue(n->nic, index); + + if (!nc->peer) { + return 0; + } + + if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { + return 0; + } + + return tap_disable(nc->peer); +} + +static void virtio_net_set_queues(VirtIONet *n) +{ + int i; + + for (i = 0; i < n->max_queues; i++) { + if (i < n->curr_queues) { + assert(!peer_attach(n, i)); + } else { + assert(!peer_detach(n, i)); + } } } +static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue, int ctrl); + static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features) { VirtIONet *n = to_virtio_net(vdev); + NetClientState *nc = qemu_get_queue(n->nic); features |= (1 << VIRTIO_NET_F_MAC); @@ -265,14 +363,13 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features) features &= ~(0x1 << VIRTIO_NET_F_HOST_UFO); } - if (!n->nic->nc.peer || - n->nic->nc.peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { + if (!nc->peer || nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { return features; } - if (!tap_get_vhost_net(n->nic->nc.peer)) { + if (!tap_get_vhost_net(nc->peer)) { return features; } - return vhost_net_get_features(tap_get_vhost_net(n->nic->nc.peer), features); + return vhost_net_get_features(tap_get_vhost_net(nc->peer), features); } static uint32_t virtio_net_bad_features(VirtIODevice *vdev) @@ -293,66 +390,84 @@ static uint32_t virtio_net_bad_features(VirtIODevice *vdev) static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features) { VirtIONet *n = to_virtio_net(vdev); + int i; + + virtio_net_set_multiqueue(n, !!(features & (1 << VIRTIO_NET_F_MQ)), + !!(features & (1 << VIRTIO_NET_F_CTRL_VQ))); virtio_net_set_mrg_rx_bufs(n, !!(features & (1 << VIRTIO_NET_F_MRG_RXBUF))); if (n->has_vnet_hdr) { - tap_set_offload(n->nic->nc.peer, + tap_set_offload(qemu_get_subqueue(n->nic, 0)->peer, (features >> VIRTIO_NET_F_GUEST_CSUM) & 1, (features >> VIRTIO_NET_F_GUEST_TSO4) & 1, (features >> VIRTIO_NET_F_GUEST_TSO6) & 1, (features >> VIRTIO_NET_F_GUEST_ECN) & 1, (features >> VIRTIO_NET_F_GUEST_UFO) & 1); } - if (!n->nic->nc.peer || - n->nic->nc.peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { - return; - } - if (!tap_get_vhost_net(n->nic->nc.peer)) { - return; + + for (i = 0; i < n->max_queues; i++) { + NetClientState *nc = qemu_get_subqueue(n->nic, i); + + if (!nc->peer || nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { + continue; + } + if (!tap_get_vhost_net(nc->peer)) { + continue; + } + vhost_net_ack_features(tap_get_vhost_net(nc->peer), features); } - vhost_net_ack_features(tap_get_vhost_net(n->nic->nc.peer), features); } static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd, - VirtQueueElement *elem) + struct iovec *iov, unsigned int iov_cnt) { uint8_t on; + size_t s; - if (elem->out_num != 2 || elem->out_sg[1].iov_len != sizeof(on)) { - error_report("virtio-net ctrl invalid rx mode command"); - exit(1); + s = iov_to_buf(iov, iov_cnt, 0, &on, sizeof(on)); + if (s != sizeof(on)) { + return VIRTIO_NET_ERR; } - on = ldub_p(elem->out_sg[1].iov_base); - - if (cmd == VIRTIO_NET_CTRL_RX_MODE_PROMISC) + if (cmd == VIRTIO_NET_CTRL_RX_PROMISC) { n->promisc = on; - else if (cmd == VIRTIO_NET_CTRL_RX_MODE_ALLMULTI) + } else if (cmd == VIRTIO_NET_CTRL_RX_ALLMULTI) { n->allmulti = on; - else if (cmd == VIRTIO_NET_CTRL_RX_MODE_ALLUNI) + } else if (cmd == VIRTIO_NET_CTRL_RX_ALLUNI) { n->alluni = on; - else if (cmd == VIRTIO_NET_CTRL_RX_MODE_NOMULTI) + } else if (cmd == VIRTIO_NET_CTRL_RX_NOMULTI) { n->nomulti = on; - else if (cmd == VIRTIO_NET_CTRL_RX_MODE_NOUNI) + } else if (cmd == VIRTIO_NET_CTRL_RX_NOUNI) { n->nouni = on; - else if (cmd == VIRTIO_NET_CTRL_RX_MODE_NOBCAST) + } else if (cmd == VIRTIO_NET_CTRL_RX_NOBCAST) { n->nobcast = on; - else + } else { return VIRTIO_NET_ERR; + } return VIRTIO_NET_OK; } static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd, - VirtQueueElement *elem) + struct iovec *iov, unsigned int iov_cnt) { struct virtio_net_ctrl_mac mac_data; + size_t s; + + if (cmd == VIRTIO_NET_CTRL_MAC_ADDR_SET) { + if (iov_size(iov, iov_cnt) != sizeof(n->mac)) { + return VIRTIO_NET_ERR; + } + s = iov_to_buf(iov, iov_cnt, 0, &n->mac, sizeof(n->mac)); + assert(s == sizeof(n->mac)); + qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac); + return VIRTIO_NET_OK; + } - if (cmd != VIRTIO_NET_CTRL_MAC_TABLE_SET || elem->out_num != 3 || - elem->out_sg[1].iov_len < sizeof(mac_data) || - elem->out_sg[2].iov_len < sizeof(mac_data)) + if (cmd != VIRTIO_NET_CTRL_MAC_TABLE_SET) { return VIRTIO_NET_ERR; + } n->mac_table.in_use = 0; n->mac_table.first_multi = 0; @@ -360,54 +475,72 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd, n->mac_table.multi_overflow = 0; memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN); - mac_data.entries = ldl_p(elem->out_sg[1].iov_base); + s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries, + sizeof(mac_data.entries)); + mac_data.entries = ldl_p(&mac_data.entries); + if (s != sizeof(mac_data.entries)) { + return VIRTIO_NET_ERR; + } + iov_discard_front(&iov, &iov_cnt, s); - if (sizeof(mac_data.entries) + - (mac_data.entries * ETH_ALEN) > elem->out_sg[1].iov_len) + if (mac_data.entries * ETH_ALEN > iov_size(iov, iov_cnt)) { return VIRTIO_NET_ERR; + } if (mac_data.entries <= MAC_TABLE_ENTRIES) { - memcpy(n->mac_table.macs, elem->out_sg[1].iov_base + sizeof(mac_data), - mac_data.entries * ETH_ALEN); + s = iov_to_buf(iov, iov_cnt, 0, n->mac_table.macs, + mac_data.entries * ETH_ALEN); + if (s != mac_data.entries * ETH_ALEN) { + return VIRTIO_NET_ERR; + } n->mac_table.in_use += mac_data.entries; } else { n->mac_table.uni_overflow = 1; } + iov_discard_front(&iov, &iov_cnt, mac_data.entries * ETH_ALEN); + n->mac_table.first_multi = n->mac_table.in_use; - mac_data.entries = ldl_p(elem->out_sg[2].iov_base); + s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries, + sizeof(mac_data.entries)); + mac_data.entries = ldl_p(&mac_data.entries); + if (s != sizeof(mac_data.entries)) { + return VIRTIO_NET_ERR; + } + + iov_discard_front(&iov, &iov_cnt, s); - if (sizeof(mac_data.entries) + - (mac_data.entries * ETH_ALEN) > elem->out_sg[2].iov_len) + if (mac_data.entries * ETH_ALEN != iov_size(iov, iov_cnt)) { return VIRTIO_NET_ERR; + } - if (mac_data.entries) { - if (n->mac_table.in_use + mac_data.entries <= MAC_TABLE_ENTRIES) { - memcpy(n->mac_table.macs + (n->mac_table.in_use * ETH_ALEN), - elem->out_sg[2].iov_base + sizeof(mac_data), - mac_data.entries * ETH_ALEN); - n->mac_table.in_use += mac_data.entries; - } else { - n->mac_table.multi_overflow = 1; + if (n->mac_table.in_use + mac_data.entries <= MAC_TABLE_ENTRIES) { + s = iov_to_buf(iov, iov_cnt, 0, n->mac_table.macs, + mac_data.entries * ETH_ALEN); + if (s != mac_data.entries * ETH_ALEN) { + return VIRTIO_NET_ERR; } + n->mac_table.in_use += mac_data.entries; + } else { + n->mac_table.multi_overflow = 1; } return VIRTIO_NET_OK; } static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd, - VirtQueueElement *elem) + struct iovec *iov, unsigned int iov_cnt) { uint16_t vid; + size_t s; - if (elem->out_num != 2 || elem->out_sg[1].iov_len != sizeof(vid)) { - error_report("virtio-net ctrl invalid vlan command"); + s = iov_to_buf(iov, iov_cnt, 0, &vid, sizeof(vid)); + vid = lduw_p(&vid); + if (s != sizeof(vid)) { return VIRTIO_NET_ERR; } - vid = lduw_p(elem->out_sg[1].iov_base); - if (vid >= MAX_VLAN) return VIRTIO_NET_ERR; @@ -421,36 +554,73 @@ static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd, return VIRTIO_NET_OK; } +static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd, + VirtQueueElement *elem) +{ + struct virtio_net_ctrl_mq s; + + if (elem->out_num != 2 || + elem->out_sg[1].iov_len != sizeof(struct virtio_net_ctrl_mq)) { + error_report("virtio-net ctrl invalid steering command"); + return VIRTIO_NET_ERR; + } + + if (cmd != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) { + return VIRTIO_NET_ERR; + } + + memcpy(&s, elem->out_sg[1].iov_base, sizeof(struct virtio_net_ctrl_mq)); + + if (s.virtqueue_pairs < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN || + s.virtqueue_pairs > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX || + s.virtqueue_pairs > n->max_queues || + !n->multiqueue) { + return VIRTIO_NET_ERR; + } + + n->curr_queues = s.virtqueue_pairs; + /* stop the backend before changing the number of queues to avoid handling a + * disabled queue */ + virtio_net_set_status(&n->vdev, n->vdev.status); + virtio_net_set_queues(n); + + return VIRTIO_NET_OK; +} static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) { VirtIONet *n = to_virtio_net(vdev); struct virtio_net_ctrl_hdr ctrl; virtio_net_ctrl_ack status = VIRTIO_NET_ERR; VirtQueueElement elem; + size_t s; + struct iovec *iov; + unsigned int iov_cnt; while (virtqueue_pop(vq, &elem)) { - if ((elem.in_num < 1) || (elem.out_num < 1)) { + if (iov_size(elem.in_sg, elem.in_num) < sizeof(status) || + iov_size(elem.out_sg, elem.out_num) < sizeof(ctrl)) { error_report("virtio-net ctrl missing headers"); exit(1); } - if (elem.out_sg[0].iov_len < sizeof(ctrl) || - elem.in_sg[elem.in_num - 1].iov_len < sizeof(status)) { - error_report("virtio-net ctrl header not in correct element"); - exit(1); + iov = elem.out_sg; + iov_cnt = elem.out_num; + s = iov_to_buf(iov, iov_cnt, 0, &ctrl, sizeof(ctrl)); + iov_discard_front(&iov, &iov_cnt, sizeof(ctrl)); + if (s != sizeof(ctrl)) { + status = VIRTIO_NET_ERR; + } else if (ctrl.class == VIRTIO_NET_CTRL_RX) { + status = virtio_net_handle_rx_mode(n, ctrl.cmd, iov, iov_cnt); + } else if (ctrl.class == VIRTIO_NET_CTRL_MAC) { + status = virtio_net_handle_mac(n, ctrl.cmd, iov, iov_cnt); + } else if (ctrl.class == VIRTIO_NET_CTRL_VLAN) { + status = virtio_net_handle_vlan_table(n, ctrl.cmd, iov, iov_cnt); + } else if (ctrl.class == VIRTIO_NET_CTRL_MQ) { + status = virtio_net_handle_mq(n, ctrl.cmd, &elem); } - ctrl.class = ldub_p(elem.out_sg[0].iov_base); - ctrl.cmd = ldub_p(elem.out_sg[0].iov_base + sizeof(ctrl.class)); - - if (ctrl.class == VIRTIO_NET_CTRL_RX_MODE) - status = virtio_net_handle_rx_mode(n, ctrl.cmd, &elem); - else if (ctrl.class == VIRTIO_NET_CTRL_MAC) - status = virtio_net_handle_mac(n, ctrl.cmd, &elem); - else if (ctrl.class == VIRTIO_NET_CTRL_VLAN) - status = virtio_net_handle_vlan_table(n, ctrl.cmd, &elem); - - stb_p(elem.in_sg[elem.in_num - 1].iov_base, status); + s = iov_from_buf(elem.in_sg, elem.in_num, 0, &status, sizeof(status)); + assert(s == sizeof(status)); virtqueue_push(vq, &elem, sizeof(status)); virtio_notify(vdev, vq); @@ -462,42 +632,52 @@ static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq) { VirtIONet *n = to_virtio_net(vdev); + int queue_index = vq2q(virtio_get_queue_index(vq)); - qemu_flush_queued_packets(&n->nic->nc); + qemu_flush_queued_packets(qemu_get_subqueue(n->nic, queue_index)); } static int virtio_net_can_receive(NetClientState *nc) { - VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; + VirtIONet *n = qemu_get_nic_opaque(nc); + VirtIONetQueue *q = virtio_net_get_subqueue(nc); + if (!n->vdev.vm_running) { return 0; } - if (!virtio_queue_ready(n->rx_vq) || - !(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) + if (nc->queue_index >= n->curr_queues) { + return 0; + } + + if (!virtio_queue_ready(q->rx_vq) || + !(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) { return 0; + } return 1; } -static int virtio_net_has_buffers(VirtIONet *n, int bufsize) +static int virtio_net_has_buffers(VirtIONetQueue *q, int bufsize) { - if (virtio_queue_empty(n->rx_vq) || + VirtIONet *n = q->n; + if (virtio_queue_empty(q->rx_vq) || (n->mergeable_rx_bufs && - !virtqueue_avail_bytes(n->rx_vq, bufsize, 0))) { - virtio_queue_set_notification(n->rx_vq, 1); + !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) { + virtio_queue_set_notification(q->rx_vq, 1); /* To avoid a race condition where the guest has made some buffers * available after the above check but before notification was * enabled, check for available buffers again. */ - if (virtio_queue_empty(n->rx_vq) || + if (virtio_queue_empty(q->rx_vq) || (n->mergeable_rx_bufs && - !virtqueue_avail_bytes(n->rx_vq, bufsize, 0))) + !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) { return 0; + } } - virtio_queue_set_notification(n->rx_vq, 0); + virtio_queue_set_notification(q->rx_vq, 0); return 1; } @@ -599,18 +779,21 @@ static int receive_filter(VirtIONet *n, const uint8_t *buf, int size) static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t size) { - VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; + VirtIONet *n = qemu_get_nic_opaque(nc); + VirtIONetQueue *q = virtio_net_get_subqueue(nc); struct iovec mhdr_sg[VIRTQUEUE_MAX_SIZE]; struct virtio_net_hdr_mrg_rxbuf mhdr; unsigned mhdr_cnt = 0; size_t offset, i, guest_offset; - if (!virtio_net_can_receive(&n->nic->nc)) + if (!virtio_net_can_receive(nc)) { return -1; + } /* hdr_len refers to the header we supply to the guest */ - if (!virtio_net_has_buffers(n, size + n->guest_hdr_len - n->host_hdr_len)) + if (!virtio_net_has_buffers(q, size + n->guest_hdr_len - n->host_hdr_len)) { return 0; + } if (!receive_filter(n, buf, size)) return size; @@ -624,7 +807,7 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t total = 0; - if (virtqueue_pop(n->rx_vq, &elem) == 0) { + if (virtqueue_pop(q->rx_vq, &elem) == 0) { if (i == 0) return -1; error_report("virtio-net unexpected empty queue: " @@ -677,7 +860,7 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t } /* signal other side */ - virtqueue_fill(n->rx_vq, &elem, total, i++); + virtqueue_fill(q->rx_vq, &elem, total, i++); } if (mhdr_cnt) { @@ -687,44 +870,47 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t &mhdr.num_buffers, sizeof mhdr.num_buffers); } - virtqueue_flush(n->rx_vq, i); - virtio_notify(&n->vdev, n->rx_vq); + virtqueue_flush(q->rx_vq, i); + virtio_notify(&n->vdev, q->rx_vq); return size; } -static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq); +static int32_t virtio_net_flush_tx(VirtIONetQueue *q); static void virtio_net_tx_complete(NetClientState *nc, ssize_t len) { - VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; + VirtIONet *n = qemu_get_nic_opaque(nc); + VirtIONetQueue *q = virtio_net_get_subqueue(nc); - virtqueue_push(n->tx_vq, &n->async_tx.elem, 0); - virtio_notify(&n->vdev, n->tx_vq); + virtqueue_push(q->tx_vq, &q->async_tx.elem, 0); + virtio_notify(&n->vdev, q->tx_vq); - n->async_tx.elem.out_num = n->async_tx.len = 0; + q->async_tx.elem.out_num = q->async_tx.len = 0; - virtio_queue_set_notification(n->tx_vq, 1); - virtio_net_flush_tx(n, n->tx_vq); + virtio_queue_set_notification(q->tx_vq, 1); + virtio_net_flush_tx(q); } /* TX */ -static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq) +static int32_t virtio_net_flush_tx(VirtIONetQueue *q) { + VirtIONet *n = q->n; VirtQueueElement elem; int32_t num_packets = 0; + int queue_index = vq2q(virtio_get_queue_index(q->tx_vq)); if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) { return num_packets; } assert(n->vdev.vm_running); - if (n->async_tx.elem.out_num) { - virtio_queue_set_notification(n->tx_vq, 0); + if (q->async_tx.elem.out_num) { + virtio_queue_set_notification(q->tx_vq, 0); return num_packets; } - while (virtqueue_pop(vq, &elem)) { + while (virtqueue_pop(q->tx_vq, &elem)) { ssize_t ret, len; unsigned int out_num = elem.out_num; struct iovec *out_sg = &elem.out_sg[0]; @@ -754,19 +940,19 @@ static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq) len = n->guest_hdr_len; - ret = qemu_sendv_packet_async(&n->nic->nc, out_sg, out_num, - virtio_net_tx_complete); + ret = qemu_sendv_packet_async(qemu_get_subqueue(n->nic, queue_index), + out_sg, out_num, virtio_net_tx_complete); if (ret == 0) { - virtio_queue_set_notification(n->tx_vq, 0); - n->async_tx.elem = elem; - n->async_tx.len = len; + virtio_queue_set_notification(q->tx_vq, 0); + q->async_tx.elem = elem; + q->async_tx.len = len; return -EBUSY; } len += ret; - virtqueue_push(vq, &elem, 0); - virtio_notify(&n->vdev, vq); + virtqueue_push(q->tx_vq, &elem, 0); + virtio_notify(&n->vdev, q->tx_vq); if (++num_packets >= n->tx_burst) { break; @@ -778,22 +964,23 @@ static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq) static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq) { VirtIONet *n = to_virtio_net(vdev); + VirtIONetQueue *q = &n->vqs[vq2q(virtio_get_queue_index(vq))]; /* This happens when device was stopped but VCPU wasn't. */ if (!n->vdev.vm_running) { - n->tx_waiting = 1; + q->tx_waiting = 1; return; } - if (n->tx_waiting) { + if (q->tx_waiting) { virtio_queue_set_notification(vq, 1); - qemu_del_timer(n->tx_timer); - n->tx_waiting = 0; - virtio_net_flush_tx(n, vq); + qemu_del_timer(q->tx_timer); + q->tx_waiting = 0; + virtio_net_flush_tx(q); } else { - qemu_mod_timer(n->tx_timer, + qemu_mod_timer(q->tx_timer, qemu_get_clock_ns(vm_clock) + n->tx_timeout); - n->tx_waiting = 1; + q->tx_waiting = 1; virtio_queue_set_notification(vq, 0); } } @@ -801,48 +988,51 @@ static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq) static void virtio_net_handle_tx_bh(VirtIODevice *vdev, VirtQueue *vq) { VirtIONet *n = to_virtio_net(vdev); + VirtIONetQueue *q = &n->vqs[vq2q(virtio_get_queue_index(vq))]; - if (unlikely(n->tx_waiting)) { + if (unlikely(q->tx_waiting)) { return; } - n->tx_waiting = 1; + q->tx_waiting = 1; /* This happens when device was stopped but VCPU wasn't. */ if (!n->vdev.vm_running) { return; } virtio_queue_set_notification(vq, 0); - qemu_bh_schedule(n->tx_bh); + qemu_bh_schedule(q->tx_bh); } static void virtio_net_tx_timer(void *opaque) { - VirtIONet *n = opaque; + VirtIONetQueue *q = opaque; + VirtIONet *n = q->n; assert(n->vdev.vm_running); - n->tx_waiting = 0; + q->tx_waiting = 0; /* Just in case the driver is not ready on more */ if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) return; - virtio_queue_set_notification(n->tx_vq, 1); - virtio_net_flush_tx(n, n->tx_vq); + virtio_queue_set_notification(q->tx_vq, 1); + virtio_net_flush_tx(q); } static void virtio_net_tx_bh(void *opaque) { - VirtIONet *n = opaque; + VirtIONetQueue *q = opaque; + VirtIONet *n = q->n; int32_t ret; assert(n->vdev.vm_running); - n->tx_waiting = 0; + q->tx_waiting = 0; /* Just in case the driver is not ready on more */ if (unlikely(!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK))) return; - ret = virtio_net_flush_tx(n, n->tx_vq); + ret = virtio_net_flush_tx(q); if (ret == -EBUSY) { return; /* Notification re-enable handled by tx_complete */ } @@ -850,24 +1040,61 @@ static void virtio_net_tx_bh(void *opaque) /* If we flush a full burst of packets, assume there are * more coming and immediately reschedule */ if (ret >= n->tx_burst) { - qemu_bh_schedule(n->tx_bh); - n->tx_waiting = 1; + qemu_bh_schedule(q->tx_bh); + q->tx_waiting = 1; return; } /* If less than a full burst, re-enable notification and flush * anything that may have come in while we weren't looking. If * we find something, assume the guest is still active and reschedule */ - virtio_queue_set_notification(n->tx_vq, 1); - if (virtio_net_flush_tx(n, n->tx_vq) > 0) { - virtio_queue_set_notification(n->tx_vq, 0); - qemu_bh_schedule(n->tx_bh); - n->tx_waiting = 1; + virtio_queue_set_notification(q->tx_vq, 1); + if (virtio_net_flush_tx(q) > 0) { + virtio_queue_set_notification(q->tx_vq, 0); + qemu_bh_schedule(q->tx_bh); + q->tx_waiting = 1; } } +static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue, int ctrl) +{ + VirtIODevice *vdev = &n->vdev; + int i, max = multiqueue ? n->max_queues : 1; + + n->multiqueue = multiqueue; + + for (i = 2; i <= n->max_queues * 2 + 1; i++) { + virtio_del_queue(vdev, i); + } + + for (i = 1; i < max; i++) { + n->vqs[i].rx_vq = virtio_add_queue(vdev, 256, virtio_net_handle_rx); + if (n->vqs[i].tx_timer) { + n->vqs[i].tx_vq = + virtio_add_queue(vdev, 256, virtio_net_handle_tx_timer); + n->vqs[i].tx_timer = qemu_new_timer_ns(vm_clock, + virtio_net_tx_timer, + &n->vqs[i]); + } else { + n->vqs[i].tx_vq = + virtio_add_queue(vdev, 256, virtio_net_handle_tx_bh); + n->vqs[i].tx_bh = qemu_bh_new(virtio_net_tx_bh, &n->vqs[i]); + } + + n->vqs[i].tx_waiting = 0; + n->vqs[i].n = n; + } + + if (ctrl) { + n->ctrl_vq = virtio_add_queue(vdev, 64, virtio_net_handle_ctrl); + } + + virtio_net_set_queues(n); +} + static void virtio_net_save(QEMUFile *f, void *opaque) { + int i; VirtIONet *n = opaque; /* At this point, backend must be stopped, otherwise @@ -876,7 +1103,7 @@ static void virtio_net_save(QEMUFile *f, void *opaque) virtio_save(&n->vdev, f); qemu_put_buffer(f, n->mac, ETH_ALEN); - qemu_put_be32(f, n->tx_waiting); + qemu_put_be32(f, n->vqs[0].tx_waiting); qemu_put_be32(f, n->mergeable_rx_bufs); qemu_put_be16(f, n->status); qemu_put_byte(f, n->promisc); @@ -892,13 +1119,19 @@ static void virtio_net_save(QEMUFile *f, void *opaque) qemu_put_byte(f, n->nouni); qemu_put_byte(f, n->nobcast); qemu_put_byte(f, n->has_ufo); + if (n->max_queues > 1) { + qemu_put_be16(f, n->max_queues); + qemu_put_be16(f, n->curr_queues); + for (i = 1; i < n->curr_queues; i++) { + qemu_put_be32(f, n->vqs[i].tx_waiting); + } + } } static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) { VirtIONet *n = opaque; - int i; - int ret; + int ret, i, link_down; if (version_id < 2 || version_id > VIRTIO_NET_VM_VERSION) return -EINVAL; @@ -909,7 +1142,7 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) } qemu_get_buffer(f, n->mac, ETH_ALEN); - n->tx_waiting = qemu_get_be32(f); + n->vqs[0].tx_waiting = qemu_get_be32(f); virtio_net_set_mrg_rx_bufs(n, qemu_get_be32(f)); @@ -951,7 +1184,7 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) } if (n->has_vnet_hdr) { - tap_set_offload(n->nic->nc.peer, + tap_set_offload(qemu_get_queue(n->nic)->peer, (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_CSUM) & 1, (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_TSO4) & 1, (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_TSO6) & 1, @@ -979,6 +1212,20 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) } } + if (n->max_queues > 1) { + if (n->max_queues != qemu_get_be16(f)) { + error_report("virtio-net: different max_queues "); + return -1; + } + + n->curr_queues = qemu_get_be16(f); + for (i = 1; i < n->curr_queues; i++) { + n->vqs[i].tx_waiting = qemu_get_be32(f); + } + } + + virtio_net_set_queues(n); + /* Find the first multicast entry in the saved MAC filter */ for (i = 0; i < n->mac_table.in_use; i++) { if (n->mac_table.macs[i * ETH_ALEN] & 1) { @@ -989,14 +1236,17 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) /* nc.link_down can't be migrated, so infer link_down according * to link status bit in n->status */ - n->nic->nc.link_down = (n->status & VIRTIO_NET_S_LINK_UP) == 0; + link_down = (n->status & VIRTIO_NET_S_LINK_UP) == 0; + for (i = 0; i < n->max_queues; i++) { + qemu_get_subqueue(n->nic, i)->link_down = link_down; + } return 0; } static void virtio_net_cleanup(NetClientState *nc) { - VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; + VirtIONet *n = qemu_get_nic_opaque(nc); n->nic = NULL; } @@ -1013,16 +1263,18 @@ static NetClientInfo net_virtio_info = { static bool virtio_net_guest_notifier_pending(VirtIODevice *vdev, int idx) { VirtIONet *n = to_virtio_net(vdev); + NetClientState *nc = qemu_get_subqueue(n->nic, vq2q(idx)); assert(n->vhost_started); - return vhost_net_virtqueue_pending(tap_get_vhost_net(n->nic->nc.peer), idx); + return vhost_net_virtqueue_pending(tap_get_vhost_net(nc->peer), idx); } static void virtio_net_guest_notifier_mask(VirtIODevice *vdev, int idx, bool mask) { VirtIONet *n = to_virtio_net(vdev); + NetClientState *nc = qemu_get_subqueue(n->nic, vq2q(idx)); assert(n->vhost_started); - vhost_net_virtqueue_mask(tap_get_vhost_net(n->nic->nc.peer), + vhost_net_virtqueue_mask(tap_get_vhost_net(nc->peer), vdev, idx, mask); } @@ -1030,6 +1282,7 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, virtio_net_conf *net) { VirtIONet *n; + int i; n = (VirtIONet *)virtio_common_init("virtio-net", VIRTIO_ID_NET, sizeof(struct virtio_net_config), @@ -1044,7 +1297,11 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, n->vdev.set_status = virtio_net_set_status; n->vdev.guest_notifier_mask = virtio_net_guest_notifier_mask; n->vdev.guest_notifier_pending = virtio_net_guest_notifier_pending; - n->rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx); + n->vqs[0].rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx); + n->max_queues = conf->queues; + n->curr_queues = 1; + n->vqs[0].n = n; + n->tx_timeout = net->txtimer; if (net->tx && strcmp(net->tx, "timer") && strcmp(net->tx, "bh")) { error_report("virtio-net: " @@ -1054,12 +1311,14 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, } if (net->tx && !strcmp(net->tx, "timer")) { - n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx_timer); - n->tx_timer = qemu_new_timer_ns(vm_clock, virtio_net_tx_timer, n); - n->tx_timeout = net->txtimer; + n->vqs[0].tx_vq = virtio_add_queue(&n->vdev, 256, + virtio_net_handle_tx_timer); + n->vqs[0].tx_timer = qemu_new_timer_ns(vm_clock, virtio_net_tx_timer, + &n->vqs[0]); } else { - n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx_bh); - n->tx_bh = qemu_bh_new(virtio_net_tx_bh, n); + n->vqs[0].tx_vq = virtio_add_queue(&n->vdev, 256, + virtio_net_handle_tx_bh); + n->vqs[0].tx_bh = qemu_bh_new(virtio_net_tx_bh, &n->vqs[0]); } n->ctrl_vq = virtio_add_queue(&n->vdev, 64, virtio_net_handle_ctrl); qemu_macaddr_default_if_unset(&conf->macaddr); @@ -1069,15 +1328,17 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, n->nic = qemu_new_nic(&net_virtio_info, conf, object_get_typename(OBJECT(dev)), dev->id, n); peer_test_vnet_hdr(n); if (peer_has_vnet_hdr(n)) { - tap_using_vnet_hdr(n->nic->nc.peer, 1); + for (i = 0; i < n->max_queues; i++) { + tap_using_vnet_hdr(qemu_get_subqueue(n->nic, i)->peer, true); + } n->host_hdr_len = sizeof(struct virtio_net_hdr); } else { n->host_hdr_len = 0; } - qemu_format_nic_info_str(&n->nic->nc, conf->macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(n->nic), conf->macaddr.a); - n->tx_waiting = 0; + n->vqs[0].tx_waiting = 0; n->tx_burst = net->txburst; virtio_net_set_mrg_rx_bufs(n, 0); n->promisc = 1; /* for compatibility */ @@ -1098,24 +1359,30 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, void virtio_net_exit(VirtIODevice *vdev) { VirtIONet *n = DO_UPCAST(VirtIONet, vdev, vdev); + int i; /* This will stop vhost backend if appropriate. */ virtio_net_set_status(vdev, 0); - qemu_purge_queued_packets(&n->nic->nc); - unregister_savevm(n->qdev, "virtio-net", n); g_free(n->mac_table.macs); g_free(n->vlans); - if (n->tx_timer) { - qemu_del_timer(n->tx_timer); - qemu_free_timer(n->tx_timer); - } else { - qemu_bh_delete(n->tx_bh); + for (i = 0; i < n->max_queues; i++) { + VirtIONetQueue *q = &n->vqs[i]; + NetClientState *nc = qemu_get_subqueue(n->nic, i); + + qemu_purge_queued_packets(nc); + + if (q->tx_timer) { + qemu_del_timer(q->tx_timer); + qemu_free_timer(q->tx_timer); + } else { + qemu_bh_delete(q->tx_bh); + } } - qemu_del_net_client(&n->nic->nc); + qemu_del_nic(n->nic); virtio_cleanup(&n->vdev); } diff --git a/hw/virtio-net.h b/hw/virtio-net.h index d46fb9840f..f5fea6e9bc 100644 --- a/hw/virtio-net.h +++ b/hw/virtio-net.h @@ -43,6 +43,10 @@ #define VIRTIO_NET_F_CTRL_RX 18 /* Control channel RX mode support */ #define VIRTIO_NET_F_CTRL_VLAN 19 /* Control channel VLAN filtering */ #define VIRTIO_NET_F_CTRL_RX_EXTRA 20 /* Extra RX mode control support */ +#define VIRTIO_NET_F_MQ 22 /* Device supports Receive Flow + * Steering */ + +#define VIRTIO_NET_F_CTRL_MAC_ADDR 23 /* Set MAC address */ #define VIRTIO_NET_S_LINK_UP 1 /* Link is up */ @@ -71,6 +75,8 @@ struct virtio_net_config uint8_t mac[ETH_ALEN]; /* See VIRTIO_NET_F_STATUS and VIRTIO_NET_S_* above */ uint16_t status; + /* Max virtqueue pairs supported by the device */ + uint16_t max_virtqueue_pairs; } QEMU_PACKED; /* @@ -97,16 +103,16 @@ typedef uint8_t virtio_net_ctrl_ack; * 0 and 1 are supported with the VIRTIO_NET_F_CTRL_RX feature. * Commands 2-5 are added with VIRTIO_NET_F_CTRL_RX_EXTRA. */ -#define VIRTIO_NET_CTRL_RX_MODE 0 - #define VIRTIO_NET_CTRL_RX_MODE_PROMISC 0 - #define VIRTIO_NET_CTRL_RX_MODE_ALLMULTI 1 - #define VIRTIO_NET_CTRL_RX_MODE_ALLUNI 2 - #define VIRTIO_NET_CTRL_RX_MODE_NOMULTI 3 - #define VIRTIO_NET_CTRL_RX_MODE_NOUNI 4 - #define VIRTIO_NET_CTRL_RX_MODE_NOBCAST 5 +#define VIRTIO_NET_CTRL_RX 0 + #define VIRTIO_NET_CTRL_RX_PROMISC 0 + #define VIRTIO_NET_CTRL_RX_ALLMULTI 1 + #define VIRTIO_NET_CTRL_RX_ALLUNI 2 + #define VIRTIO_NET_CTRL_RX_NOMULTI 3 + #define VIRTIO_NET_CTRL_RX_NOUNI 4 + #define VIRTIO_NET_CTRL_RX_NOBCAST 5 /* - * Control the MAC filter table. + * Control the MAC * * The MAC filter table is managed by the hypervisor, the guest should * assume the size is infinite. Filtering should be considered @@ -119,6 +125,10 @@ typedef uint8_t virtio_net_ctrl_ack; * first sg list contains unicast addresses, the second is for multicast. * This functionality is present if the VIRTIO_NET_F_CTRL_RX feature * is available. + * + * The ADDR_SET command requests one out scatterlist, it contains a + * 6 bytes MAC address. This functionality is present if the + * VIRTIO_NET_F_CTRL_MAC_ADDR feature is available. */ struct virtio_net_ctrl_mac { uint32_t entries; @@ -126,6 +136,7 @@ struct virtio_net_ctrl_mac { }; #define VIRTIO_NET_CTRL_MAC 1 #define VIRTIO_NET_CTRL_MAC_TABLE_SET 0 + #define VIRTIO_NET_CTRL_MAC_ADDR_SET 1 /* * Control VLAN filtering @@ -140,6 +151,26 @@ struct virtio_net_ctrl_mac { #define VIRTIO_NET_CTRL_VLAN_ADD 0 #define VIRTIO_NET_CTRL_VLAN_DEL 1 +/* + * Control Multiqueue + * + * The command VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET + * enables multiqueue, specifying the number of the transmit and + * receive queues that will be used. After the command is consumed and acked by + * the device, the device will not steer new packets on receive virtqueues + * other than specified nor read from transmit virtqueues other than specified. + * Accordingly, driver should not transmit new packets on virtqueues other than + * specified. + */ +struct virtio_net_ctrl_mq { + uint16_t virtqueue_pairs; +}; + +#define VIRTIO_NET_CTRL_MQ 4 + #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET 0 + #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN 1 + #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX 0x8000 + #define DEFINE_VIRTIO_NET_FEATURES(_state, _field) \ DEFINE_VIRTIO_COMMON_FEATURES(_state, _field), \ DEFINE_PROP_BIT("csum", _state, _field, VIRTIO_NET_F_CSUM, true), \ @@ -158,5 +189,8 @@ struct virtio_net_ctrl_mac { DEFINE_PROP_BIT("ctrl_vq", _state, _field, VIRTIO_NET_F_CTRL_VQ, true), \ DEFINE_PROP_BIT("ctrl_rx", _state, _field, VIRTIO_NET_F_CTRL_RX, true), \ DEFINE_PROP_BIT("ctrl_vlan", _state, _field, VIRTIO_NET_F_CTRL_VLAN, true), \ - DEFINE_PROP_BIT("ctrl_rx_extra", _state, _field, VIRTIO_NET_F_CTRL_RX_EXTRA, true) + DEFINE_PROP_BIT("ctrl_rx_extra", _state, _field, VIRTIO_NET_F_CTRL_RX_EXTRA, true), \ + DEFINE_PROP_BIT("ctrl_mac_addr", _state, _field, VIRTIO_NET_F_CTRL_MAC_ADDR, true), \ + DEFINE_PROP_BIT("mq", _state, _field, VIRTIO_NET_F_MQ, true) + #endif diff --git a/hw/virtio.c b/hw/virtio.c index ca170c319e..e259348518 100644 --- a/hw/virtio.c +++ b/hw/virtio.c @@ -73,6 +73,8 @@ struct VirtQueue /* Notification enabled? */ bool notification; + uint16_t queue_index; + int inuse; uint16_t vector; @@ -701,6 +703,15 @@ VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size, return &vdev->vq[i]; } +void virtio_del_queue(VirtIODevice *vdev, int n) +{ + if (n < 0 || n >= VIRTIO_PCI_QUEUE_MAX) { + abort(); + } + + vdev->vq[n].vring.num = 0; +} + void virtio_irq(VirtQueue *vq) { trace_virtio_irq(vq); @@ -922,6 +933,7 @@ void virtio_init(VirtIODevice *vdev, const char *name, for (i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) { vdev->vq[i].vector = VIRTIO_NO_VECTOR; vdev->vq[i].vdev = vdev; + vdev->vq[i].queue_index = i; } vdev->name = name; @@ -1009,6 +1021,11 @@ VirtQueue *virtio_get_queue(VirtIODevice *vdev, int n) return vdev->vq + n; } +uint16_t virtio_get_queue_index(VirtQueue *vq) +{ + return vq->queue_index; +} + static void virtio_queue_guest_notifier_read(EventNotifier *n) { VirtQueue *vq = container_of(n, VirtQueue, guest_notifier); diff --git a/hw/virtio.h b/hw/virtio.h index 9cc7b85671..a29a54d4f3 100644 --- a/hw/virtio.h +++ b/hw/virtio.h @@ -181,6 +181,8 @@ VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size, void (*handle_output)(VirtIODevice *, VirtQueue *)); +void virtio_del_queue(VirtIODevice *vdev, int n); + void virtqueue_push(VirtQueue *vq, const VirtQueueElement *elem, unsigned int len); void virtqueue_flush(VirtQueue *vq, unsigned int count); @@ -278,6 +280,7 @@ hwaddr virtio_queue_get_ring_size(VirtIODevice *vdev, int n); uint16_t virtio_queue_get_last_avail_idx(VirtIODevice *vdev, int n); void virtio_queue_set_last_avail_idx(VirtIODevice *vdev, int n, uint16_t idx); VirtQueue *virtio_get_queue(VirtIODevice *vdev, int n); +uint16_t virtio_get_queue_index(VirtQueue *vq); int virtio_queue_get_id(VirtQueue *vq); EventNotifier *virtio_queue_get_guest_notifier(VirtQueue *vq); void virtio_queue_set_guest_notifier_fd_handler(VirtQueue *vq, bool assign, diff --git a/hw/vmware_vga.c b/hw/vmware_vga.c index 62771bb7b5..cd15ee40a8 100644 --- a/hw/vmware_vga.c +++ b/hw/vmware_vga.c @@ -296,6 +296,15 @@ static inline void vmsvga_update_rect(struct vmsvga_state_s *s, uint8_t *src; uint8_t *dst; + if (x < 0) { + fprintf(stderr, "%s: update x was < 0 (%d)\n", __func__, x); + w += x; + x = 0; + } + if (w < 0) { + fprintf(stderr, "%s: update w was < 0 (%d)\n", __func__, w); + w = 0; + } if (x + w > ds_get_width(s->vga.ds)) { fprintf(stderr, "%s: update width too large x: %d, w: %d\n", __func__, x, w); @@ -303,6 +312,15 @@ static inline void vmsvga_update_rect(struct vmsvga_state_s *s, w = ds_get_width(s->vga.ds) - x; } + if (y < 0) { + fprintf(stderr, "%s: update y was < 0 (%d)\n", __func__, y); + h += y; + y = 0; + } + if (h < 0) { + fprintf(stderr, "%s: update h was < 0 (%d)\n", __func__, h); + h = 0; + } if (y + h > ds_get_height(s->vga.ds)) { fprintf(stderr, "%s: update height too large y: %d, h: %d\n", __func__, y, h); diff --git a/hw/wm8750.c b/hw/wm8750.c index bb85064c9b..d3ea5ba8f5 100644 --- a/hw/wm8750.c +++ b/hw/wm8750.c @@ -632,7 +632,7 @@ static void wm8750_fini(I2CSlave *i2c) void wm8750_data_req_set(DeviceState *dev, void (*data_req)(void *, int, int), void *opaque) { - WM8750State *s = FROM_I2C_SLAVE(WM8750State, I2C_SLAVE_FROM_QDEV(dev)); + WM8750State *s = FROM_I2C_SLAVE(WM8750State, I2C_SLAVE(dev)); s->data_req = data_req; s->opaque = opaque; } @@ -21,9 +21,9 @@ enum xen_mode { extern uint32_t xen_domid; extern enum xen_mode xen_mode; -extern int xen_allowed; +extern bool xen_allowed; -static inline int xen_enabled(void) +static inline bool xen_enabled(void) { #if defined(CONFIG_XEN_BACKEND) && !defined(CONFIG_NO_XEN) return xen_allowed; diff --git a/hw/xen_nic.c b/hw/xen_nic.c index dc12110dba..34961c287a 100644 --- a/hw/xen_nic.c +++ b/hw/xen_nic.c @@ -185,9 +185,11 @@ static void net_tx_packets(struct XenNetDev *netdev) } memcpy(tmpbuf, page + txreq.offset, txreq.size); net_checksum_calculate(tmpbuf, txreq.size); - qemu_send_packet(&netdev->nic->nc, tmpbuf, txreq.size); + qemu_send_packet(qemu_get_queue(netdev->nic), tmpbuf, + txreq.size); } else { - qemu_send_packet(&netdev->nic->nc, page + txreq.offset, txreq.size); + qemu_send_packet(qemu_get_queue(netdev->nic), + page + txreq.offset, txreq.size); } xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1); net_tx_response(netdev, &txreq, NETIF_RSP_OKAY); @@ -234,7 +236,7 @@ static void net_rx_response(struct XenNetDev *netdev, static int net_rx_ok(NetClientState *nc) { - struct XenNetDev *netdev = DO_UPCAST(NICState, nc, nc)->opaque; + struct XenNetDev *netdev = qemu_get_nic_opaque(nc); RING_IDX rc, rp; if (netdev->xendev.be_state != XenbusStateConnected) { @@ -255,7 +257,7 @@ static int net_rx_ok(NetClientState *nc) static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size) { - struct XenNetDev *netdev = DO_UPCAST(NICState, nc, nc)->opaque; + struct XenNetDev *netdev = qemu_get_nic_opaque(nc); netif_rx_request_t rxreq; RING_IDX rc, rp; void *page; @@ -324,12 +326,11 @@ static int net_init(struct XenDevice *xendev) return -1; } - netdev->conf.peer = NULL; - netdev->nic = qemu_new_nic(&net_xen_info, &netdev->conf, "xen", NULL, netdev); - snprintf(netdev->nic->nc.info_str, sizeof(netdev->nic->nc.info_str), + snprintf(qemu_get_queue(netdev->nic)->info_str, + sizeof(qemu_get_queue(netdev->nic)->info_str), "nic: xenbus vif macaddr=%s", netdev->mac); /* fill info */ @@ -405,7 +406,7 @@ static void net_disconnect(struct XenDevice *xendev) netdev->rxs = NULL; } if (netdev->nic) { - qemu_del_net_client(&netdev->nic->nc); + qemu_del_nic(netdev->nic); netdev->nic = NULL; } } @@ -414,7 +415,7 @@ static void net_event(struct XenDevice *xendev) { struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); net_tx_packets(netdev); - qemu_flush_queued_packets(&netdev->nic->nc); + qemu_flush_queued_packets(qemu_get_queue(netdev->nic)); } static int net_free(struct XenDevice *xendev) diff --git a/hw/xgmac.c b/hw/xgmac.c index 00dae7789c..50722988b9 100644 --- a/hw/xgmac.c +++ b/hw/xgmac.c @@ -235,7 +235,7 @@ static void xgmac_enet_send(struct XgmacState *s) frame_size += len; if (bd.ctl_stat & 0x20000000) { /* Last buffer in frame. */ - qemu_send_packet(&s->nic->nc, frame, len); + qemu_send_packet(qemu_get_queue(s->nic), frame, len); ptr = frame; frame_size = 0; s->regs[DMA_STATUS] |= DMA_STATUS_TI | DMA_STATUS_NIS; @@ -310,7 +310,7 @@ static const MemoryRegionOps enet_mem_ops = { static int eth_can_rx(NetClientState *nc) { - struct XgmacState *s = DO_UPCAST(NICState, nc, nc)->opaque; + struct XgmacState *s = qemu_get_nic_opaque(nc); /* RX enabled? */ return s->regs[DMA_CONTROL] & DMA_CONTROL_SR; @@ -318,7 +318,7 @@ static int eth_can_rx(NetClientState *nc) static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) { - struct XgmacState *s = DO_UPCAST(NICState, nc, nc)->opaque; + struct XgmacState *s = qemu_get_nic_opaque(nc); static const unsigned char sa_bcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; int unicast, broadcast, multicast; @@ -366,7 +366,7 @@ out: static void eth_cleanup(NetClientState *nc) { - struct XgmacState *s = DO_UPCAST(NICState, nc, nc)->opaque; + struct XgmacState *s = qemu_get_nic_opaque(nc); s->nic = NULL; } @@ -391,7 +391,7 @@ static int xgmac_enet_init(SysBusDevice *dev) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_xgmac_enet_info, &s->conf, object_get_typename(OBJECT(dev)), dev->qdev.id, s); - qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); s->regs[XGMAC_ADDR_HIGH(0)] = (s->conf.macaddr.a[5] << 8) | s->conf.macaddr.a[4]; diff --git a/hw/xilinx_axidma.c b/hw/xilinx_axidma.c index d0ee566a28..cc51584dfc 100644 --- a/hw/xilinx_axidma.c +++ b/hw/xilinx_axidma.c @@ -444,7 +444,7 @@ static void axidma_write(void *opaque, hwaddr addr, break; default: D(qemu_log("%s: ch=%d addr=" TARGET_FMT_plx " v=%x\n", - __func__, sid, addr * 4, value)); + __func__, sid, addr * 4, (unsigned)value)); s->regs[addr] = value; break; } diff --git a/hw/xilinx_axienet.c b/hw/xilinx_axienet.c index 51c2896e44..34e344ce2c 100644 --- a/hw/xilinx_axienet.c +++ b/hw/xilinx_axienet.c @@ -617,7 +617,7 @@ static const MemoryRegionOps enet_ops = { static int eth_can_rx(NetClientState *nc) { - struct XilinxAXIEnet *s = DO_UPCAST(NICState, nc, nc)->opaque; + struct XilinxAXIEnet *s = qemu_get_nic_opaque(nc); /* RX enabled? */ return !axienet_rx_resetting(s) && axienet_rx_enabled(s); @@ -640,7 +640,7 @@ static int enet_match_addr(const uint8_t *buf, uint32_t f0, uint32_t f1) static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) { - struct XilinxAXIEnet *s = DO_UPCAST(NICState, nc, nc)->opaque; + struct XilinxAXIEnet *s = qemu_get_nic_opaque(nc); static const unsigned char sa_bcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; static const unsigned char sa_ipmcast[3] = {0x01, 0x00, 0x52}; @@ -785,7 +785,7 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) static void eth_cleanup(NetClientState *nc) { /* FIXME. */ - struct XilinxAXIEnet *s = DO_UPCAST(NICState, nc, nc)->opaque; + struct XilinxAXIEnet *s = qemu_get_nic_opaque(nc); g_free(s->rxmem); g_free(s); } @@ -826,7 +826,7 @@ axienet_stream_push(StreamSlave *obj, uint8_t *buf, size_t size, uint32_t *hdr) buf[write_off + 1] = csum & 0xff; } - qemu_send_packet(&s->nic->nc, buf, size); + qemu_send_packet(qemu_get_queue(s->nic), buf, size); s->stats.tx_bytes += size; s->regs[R_IS] |= IS_TX_COMPLETE; @@ -853,7 +853,7 @@ static int xilinx_enet_init(SysBusDevice *dev) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_xilinx_enet_info, &s->conf, object_get_typename(OBJECT(dev)), dev->qdev.id, s); - qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); tdk_init(&s->TEMAC.phy); mdio_attach(&s->TEMAC.mdio_bus, &s->TEMAC.phy, s->c_phyaddr); diff --git a/hw/xilinx_ethlite.c b/hw/xilinx_ethlite.c index 2254851f0a..21c6f8c49c 100644 --- a/hw/xilinx_ethlite.c +++ b/hw/xilinx_ethlite.c @@ -89,7 +89,7 @@ eth_read(void *opaque, hwaddr addr, unsigned int size) case R_RX_CTRL1: case R_RX_CTRL0: r = s->regs[addr]; - D(qemu_log("%s %x=%x\n", __func__, addr * 4, r)); + D(qemu_log("%s " TARGET_FMT_plx "=%x\n", __func__, addr * 4, r)); break; default: @@ -115,9 +115,10 @@ eth_write(void *opaque, hwaddr addr, if (addr == R_TX_CTRL1) base = 0x800 / 4; - D(qemu_log("%s addr=%x val=%x\n", __func__, addr * 4, value)); + D(qemu_log("%s addr=" TARGET_FMT_plx " val=%x\n", + __func__, addr * 4, value)); if ((value & (CTRL_P | CTRL_S)) == CTRL_S) { - qemu_send_packet(&s->nic->nc, + qemu_send_packet(qemu_get_queue(s->nic), (void *) &s->regs[base], s->regs[base + R_TX_LEN0]); D(qemu_log("eth_tx %d\n", s->regs[base + R_TX_LEN0])); @@ -135,12 +136,16 @@ eth_write(void *opaque, hwaddr addr, break; /* Keep these native. */ + case R_RX_CTRL0: + case R_RX_CTRL1: + if (!(value & CTRL_S)) { + qemu_flush_queued_packets(qemu_get_queue(s->nic)); + } case R_TX_LEN0: case R_TX_LEN1: case R_TX_GIE0: - case R_RX_CTRL0: - case R_RX_CTRL1: - D(qemu_log("%s addr=%x val=%x\n", __func__, addr * 4, value)); + D(qemu_log("%s addr=" TARGET_FMT_plx " val=%x\n", + __func__, addr * 4, value)); s->regs[addr] = value; break; @@ -162,15 +167,15 @@ static const MemoryRegionOps eth_ops = { static int eth_can_rx(NetClientState *nc) { - struct xlx_ethlite *s = DO_UPCAST(NICState, nc, nc)->opaque; - int r; - r = !(s->regs[R_RX_CTRL0] & CTRL_S); - return r; + struct xlx_ethlite *s = qemu_get_nic_opaque(nc); + unsigned int rxbase = s->rxbuf * (0x800 / 4); + + return !(s->regs[rxbase + R_RX_CTRL0] & CTRL_S); } static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) { - struct xlx_ethlite *s = DO_UPCAST(NICState, nc, nc)->opaque; + struct xlx_ethlite *s = qemu_get_nic_opaque(nc); unsigned int rxbase = s->rxbuf * (0x800 / 4); /* DA filter. */ @@ -182,7 +187,7 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) return -1; } - D(qemu_log("%s %d rxbase=%x\n", __func__, size, rxbase)); + D(qemu_log("%s %zd rxbase=%x\n", __func__, size, rxbase)); memcpy(&s->regs[rxbase + R_RX_BUF0], buf, size); s->regs[rxbase + R_RX_CTRL0] |= CTRL_S; @@ -196,7 +201,7 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) static void eth_cleanup(NetClientState *nc) { - struct xlx_ethlite *s = DO_UPCAST(NICState, nc, nc)->opaque; + struct xlx_ethlite *s = qemu_get_nic_opaque(nc); s->nic = NULL; } @@ -223,7 +228,7 @@ static int xilinx_ethlite_init(SysBusDevice *dev) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_xilinx_ethlite_info, &s->conf, object_get_typename(OBJECT(dev)), dev->qdev.id, s); - qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); return 0; } diff --git a/include/block/block.h b/include/block/block.h index ffd193637d..5c3b911c1b 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -309,6 +309,10 @@ int bdrv_get_flags(BlockDriverState *bs); int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors); int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi); +void bdrv_round_to_clusters(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + int64_t *cluster_sector_num, + int *cluster_nb_sectors); const char *bdrv_get_encrypted_filename(BlockDriverState *bs); void bdrv_get_backing_filename(BlockDriverState *bs, @@ -351,13 +355,12 @@ void bdrv_set_buffer_alignment(BlockDriverState *bs, int align); void *qemu_blockalign(BlockDriverState *bs, size_t size); bool bdrv_qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov); -#define BDRV_SECTORS_PER_DIRTY_CHUNK 2048 - -void bdrv_set_dirty_tracking(BlockDriverState *bs, int enable); +struct HBitmapIter; +void bdrv_set_dirty_tracking(BlockDriverState *bs, int granularity); int bdrv_get_dirty(BlockDriverState *bs, int64_t sector); void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector, int nr_sectors); void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector, int nr_sectors); -int64_t bdrv_get_next_dirty(BlockDriverState *bs, int64_t sector); +void bdrv_dirty_iter_init(BlockDriverState *bs, struct HBitmapIter *hbi); int64_t bdrv_get_dirty_count(BlockDriverState *bs); void bdrv_enable_copy_on_read(BlockDriverState *bs); diff --git a/include/block/block_int.h b/include/block/block_int.h index f83ffb8a08..eaad53e426 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -32,6 +32,7 @@ #include "qapi-types.h" #include "qapi/qmp/qerror.h" #include "monitor/monitor.h" +#include "qemu/hbitmap.h" #define BLOCK_FLAG_ENCRYPT 1 #define BLOCK_FLAG_COMPAT6 4 @@ -55,6 +56,7 @@ #define BLOCK_OPT_SUBFMT "subformat" #define BLOCK_OPT_COMPAT_LEVEL "compat" #define BLOCK_OPT_LAZY_REFCOUNTS "lazy_refcounts" +#define BLOCK_OPT_ADAPTER_TYPE "adapter_type" typedef struct BdrvTrackedRequest BdrvTrackedRequest; @@ -275,8 +277,7 @@ struct BlockDriverState { bool iostatus_enabled; BlockDeviceIoStatus iostatus; char device_name[32]; - unsigned long *dirty_bitmap; - int64_t dirty_count; + HBitmap *dirty_bitmap; int in_use; /* users other than guest access, eg. block migration */ QTAILQ_ENTRY(BlockDriverState) list; @@ -344,6 +345,8 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base, * @bs: Block device to operate on. * @target: Block device to write to. * @speed: The maximum speed, in bytes per second, or 0 for unlimited. + * @granularity: The chosen granularity for the dirty bitmap. + * @buf_size: The amount of data that can be in flight at one time. * @mode: Whether to collapse all images in the chain to the target. * @on_source_error: The action to take upon error reading from the source. * @on_target_error: The action to take upon error writing to the target. @@ -357,8 +360,8 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base, * @bs will be switched to read from @target. */ void mirror_start(BlockDriverState *bs, BlockDriverState *target, - int64_t speed, MirrorSyncMode mode, - BlockdevOnError on_source_error, + int64_t speed, int64_t granularity, int64_t buf_size, + MirrorSyncMode mode, BlockdevOnError on_source_error, BlockdevOnError on_target_error, BlockDriverCompletionFunc *cb, void *opaque, Error **errp); diff --git a/include/net/net.h b/include/net/net.h index 4a92b6c3d2..43a045e052 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -9,24 +9,32 @@ #include "migration/vmstate.h" #include "qapi-types.h" +#define MAX_QUEUE_NUM 1024 + struct MACAddr { uint8_t a[6]; }; /* qdev nic properties */ +typedef struct NICPeers { + NetClientState *ncs[MAX_QUEUE_NUM]; +} NICPeers; + typedef struct NICConf { MACAddr macaddr; - NetClientState *peer; + NICPeers peers; int32_t bootindex; + int32_t queues; } NICConf; #define DEFINE_NIC_PROPERTIES(_state, _conf) \ DEFINE_PROP_MACADDR("mac", _state, _conf.macaddr), \ - DEFINE_PROP_VLAN("vlan", _state, _conf.peer), \ - DEFINE_PROP_NETDEV("netdev", _state, _conf.peer), \ + DEFINE_PROP_VLAN("vlan", _state, _conf.peers), \ + DEFINE_PROP_NETDEV("netdev", _state, _conf.peers), \ DEFINE_PROP_INT32("bootindex", _state, _conf.bootindex, -1) + /* Net clients */ typedef void (NetPoll)(NetClientState *, bool enable); @@ -35,6 +43,7 @@ typedef ssize_t (NetReceive)(NetClientState *, const uint8_t *, size_t); typedef ssize_t (NetReceiveIOV)(NetClientState *, const struct iovec *, int); typedef void (NetCleanup) (NetClientState *); typedef void (LinkStatusChanged)(NetClientState *); +typedef void (NetClientDestructor)(NetClientState *); typedef struct NetClientInfo { NetClientOptionsKind type; @@ -58,16 +67,20 @@ struct NetClientState { char *name; char info_str[256]; unsigned receive_disabled : 1; + NetClientDestructor *destructor; + unsigned int queue_index; }; typedef struct NICState { - NetClientState nc; + NetClientState ncs[MAX_QUEUE_NUM]; NICConf *conf; void *opaque; bool peer_deleted; } NICState; NetClientState *qemu_find_netdev(const char *id); +int qemu_find_net_clients_except(const char *id, NetClientState **ncs, + NetClientOptionsKind type, int max); NetClientState *qemu_new_net_client(NetClientInfo *info, NetClientState *peer, const char *model, @@ -77,6 +90,11 @@ NICState *qemu_new_nic(NetClientInfo *info, const char *model, const char *name, void *opaque); +void qemu_del_nic(NICState *nic); +NetClientState *qemu_get_subqueue(NICState *nic, int queue_index); +NetClientState *qemu_get_queue(NICState *nic); +NICState *qemu_get_nic(NetClientState *nc); +void *qemu_get_nic_opaque(NetClientState *nc); void qemu_del_net_client(NetClientState *nc); NetClientState *qemu_find_vlan_client_by_name(Monitor *mon, int vlan_id, const char *client_str); diff --git a/include/net/tap.h b/include/net/tap.h index bb7efb5439..a994f20447 100644 --- a/include/net/tap.h +++ b/include/net/tap.h @@ -29,12 +29,14 @@ #include "qemu-common.h" #include "qapi-types.h" -int tap_has_ufo(NetClientState *nc); +bool tap_has_ufo(NetClientState *nc); int tap_has_vnet_hdr(NetClientState *nc); int tap_has_vnet_hdr_len(NetClientState *nc, int len); -void tap_using_vnet_hdr(NetClientState *nc, int using_vnet_hdr); +void tap_using_vnet_hdr(NetClientState *nc, bool using_vnet_hdr); void tap_set_offload(NetClientState *nc, int csum, int tso4, int tso6, int ecn, int ufo); void tap_set_vnet_hdr_len(NetClientState *nc, int len); +int tap_enable(NetClientState *nc); +int tap_disable(NetClientState *nc); int tap_get_fd(NetClientState *nc); diff --git a/include/qemu-common.h b/include/qemu-common.h index ca464bb367..80016adae3 100644 --- a/include/qemu-common.h +++ b/include/qemu-common.h @@ -68,6 +68,9 @@ #if !defined(ECANCELED) #define ECANCELED 4097 #endif +#if !defined(EMEDIUMTYPE) +#define EMEDIUMTYPE 4098 +#endif #ifndef TIME_MAX #define TIME_MAX LONG_MAX #endif @@ -170,6 +173,10 @@ int qemu_fdatasync(int fd); int fcntl_setfl(int fd, int flag); int qemu_parse_fd(const char *param); +int parse_uint(const char *s, unsigned long long *value, char **endptr, + int base); +int parse_uint_full(const char *s, unsigned long long *value, int base); + /* * strtosz() suffixes used to specify the default treatment of an * argument passed to strtosz() without an explicit suffix. diff --git a/include/qemu/bitops.h b/include/qemu/bitops.h index 74e14e5724..8b88791862 100644 --- a/include/qemu/bitops.h +++ b/include/qemu/bitops.h @@ -13,6 +13,7 @@ #define BITOPS_H #include "qemu-common.h" +#include "host-utils.h" #define BITS_PER_BYTE CHAR_BIT #define BITS_PER_LONG (sizeof (unsigned long) * BITS_PER_BYTE) @@ -23,41 +24,29 @@ #define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) /** - * bitops_ffs - find first bit in word. + * bitops_ctzl - count trailing zeroes in word. * @word: The word to search * - * Undefined if no bit exists, so code should check against 0 first. + * Returns -1 if no bit exists. Note that compared to the C library + * routine ffsl, this one returns one less. */ -static unsigned long bitops_ffsl(unsigned long word) +static unsigned long bitops_ctzl(unsigned long word) { - int num = 0; +#if QEMU_GNUC_PREREQ(3, 4) + return __builtin_ffsl(word) - 1; +#else + if (!word) { + return -1; + } -#if LONG_MAX > 0x7FFFFFFF - if ((word & 0xffffffff) == 0) { - num += 32; - word >>= 32; - } + if (sizeof(long) == 4) { + return ctz32(word); + } else if (sizeof(long) == 8) { + return ctz64(word); + } else { + abort(); + } #endif - if ((word & 0xffff) == 0) { - num += 16; - word >>= 16; - } - if ((word & 0xff) == 0) { - num += 8; - word >>= 8; - } - if ((word & 0xf) == 0) { - num += 4; - word >>= 4; - } - if ((word & 0x3) == 0) { - num += 2; - word >>= 2; - } - if ((word & 0x1) == 0) { - num += 1; - } - return num; } /** @@ -99,14 +88,14 @@ static inline unsigned long bitops_flsl(unsigned long word) } /** - * ffz - find first zero in word. + * cto - count trailing ones in word. * @word: The word to search * - * Undefined if no zero exists, so code should check against ~0UL first. + * Returns -1 if all bit are set. */ -static inline unsigned long ffz(unsigned long word) +static inline unsigned long bitops_ctol(unsigned long word) { - return bitops_ffsl(~word); + return bitops_ctzl(~word); } /** diff --git a/include/qemu/bswap.h b/include/qemu/bswap.h index e6d4798142..d3af35d1d9 100644 --- a/include/qemu/bswap.h +++ b/include/qemu/bswap.h @@ -2,8 +2,8 @@ #define BSWAP_H #include "config-host.h" - #include <inttypes.h> +#include <limits.h> #include "fpu/softfloat.h" #ifdef CONFIG_MACHINE_BSWAP_H @@ -458,7 +458,15 @@ static inline void cpu_to_32wu(uint32_t *p, uint32_t v) static inline unsigned long leul_to_cpu(unsigned long v) { - return le_bswap(v, HOST_LONG_BITS); + /* In order to break an include loop between here and + qemu-common.h, don't rely on HOST_LONG_BITS. */ +#if ULONG_MAX == UINT32_MAX + return le_bswap(v, 32); +#elif ULONG_MAX == UINT64_MAX + return le_bswap(v, 64); +#else +# error Unknown sizeof long +#endif } #undef le_bswap diff --git a/include/qemu/hbitmap.h b/include/qemu/hbitmap.h new file mode 100644 index 0000000000..250de03b03 --- /dev/null +++ b/include/qemu/hbitmap.h @@ -0,0 +1,208 @@ +/* + * Hierarchical Bitmap Data Type + * + * Copyright Red Hat, Inc., 2012 + * + * Author: Paolo Bonzini <pbonzini@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#ifndef HBITMAP_H +#define HBITMAP_H 1 + +#include <limits.h> +#include <stdint.h> +#include <stdbool.h> +#include "bitops.h" + +typedef struct HBitmap HBitmap; +typedef struct HBitmapIter HBitmapIter; + +#define BITS_PER_LEVEL (BITS_PER_LONG == 32 ? 5 : 6) + +/* For 32-bit, the largest that fits in a 4 GiB address space. + * For 64-bit, the number of sectors in 1 PiB. Good luck, in + * either case... :) + */ +#define HBITMAP_LOG_MAX_SIZE (BITS_PER_LONG == 32 ? 34 : 41) + +/* We need to place a sentinel in level 0 to speed up iteration. Thus, + * we do this instead of HBITMAP_LOG_MAX_SIZE / BITS_PER_LEVEL. The + * difference is that it allocates an extra level when HBITMAP_LOG_MAX_SIZE + * is an exact multiple of BITS_PER_LEVEL. + */ +#define HBITMAP_LEVELS ((HBITMAP_LOG_MAX_SIZE / BITS_PER_LEVEL) + 1) + +struct HBitmapIter { + const HBitmap *hb; + + /* Copied from hb for access in the inline functions (hb is opaque). */ + int granularity; + + /* Entry offset into the last-level array of longs. */ + size_t pos; + + /* The currently-active path in the tree. Each item of cur[i] stores + * the bits (i.e. the subtrees) yet to be processed under that node. + */ + unsigned long cur[HBITMAP_LEVELS]; +}; + +/** + * hbitmap_alloc: + * @size: Number of bits in the bitmap. + * @granularity: Granularity of the bitmap. Aligned groups of 2^@granularity + * bits will be represented by a single bit. Each operation on a + * range of bits first rounds the bits to determine which group they land + * in, and then affect the entire set; iteration will only visit the first + * bit of each group. + * + * Allocate a new HBitmap. + */ +HBitmap *hbitmap_alloc(uint64_t size, int granularity); + +/** + * hbitmap_empty: + * @hb: HBitmap to operate on. + * + * Return whether the bitmap is empty. + */ +bool hbitmap_empty(const HBitmap *hb); + +/** + * hbitmap_granularity: + * @hb: HBitmap to operate on. + * + * Return the granularity of the HBitmap. + */ +int hbitmap_granularity(const HBitmap *hb); + +/** + * hbitmap_count: + * @hb: HBitmap to operate on. + * + * Return the number of bits set in the HBitmap. + */ +uint64_t hbitmap_count(const HBitmap *hb); + +/** + * hbitmap_set: + * @hb: HBitmap to operate on. + * @start: First bit to set (0-based). + * @count: Number of bits to set. + * + * Set a consecutive range of bits in an HBitmap. + */ +void hbitmap_set(HBitmap *hb, uint64_t start, uint64_t count); + +/** + * hbitmap_reset: + * @hb: HBitmap to operate on. + * @start: First bit to reset (0-based). + * @count: Number of bits to reset. + * + * Reset a consecutive range of bits in an HBitmap. + */ +void hbitmap_reset(HBitmap *hb, uint64_t start, uint64_t count); + +/** + * hbitmap_get: + * @hb: HBitmap to operate on. + * @item: Bit to query (0-based). + * + * Return whether the @item-th bit in an HBitmap is set. + */ +bool hbitmap_get(const HBitmap *hb, uint64_t item); + +/** + * hbitmap_free: + * @hb: HBitmap to operate on. + * + * Free an HBitmap and all of its associated memory. + */ +void hbitmap_free(HBitmap *hb); + +/** + * hbitmap_iter_init: + * @hbi: HBitmapIter to initialize. + * @hb: HBitmap to iterate on. + * @first: First bit to visit (0-based, must be strictly less than the + * size of the bitmap). + * + * Set up @hbi to iterate on the HBitmap @hb. hbitmap_iter_next will return + * the lowest-numbered bit that is set in @hb, starting at @first. + * + * Concurrent setting of bits is acceptable, and will at worst cause the + * iteration to miss some of those bits. Resetting bits before the current + * position of the iterator is also okay. However, concurrent resetting of + * bits can lead to unexpected behavior if the iterator has not yet reached + * those bits. + */ +void hbitmap_iter_init(HBitmapIter *hbi, const HBitmap *hb, uint64_t first); + +/* hbitmap_iter_skip_words: + * @hbi: HBitmapIter to operate on. + * + * Internal function used by hbitmap_iter_next and hbitmap_iter_next_word. + */ +unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi); + +/** + * hbitmap_iter_next: + * @hbi: HBitmapIter to operate on. + * + * Return the next bit that is set in @hbi's associated HBitmap, + * or -1 if all remaining bits are zero. + */ +static inline int64_t hbitmap_iter_next(HBitmapIter *hbi) +{ + unsigned long cur = hbi->cur[HBITMAP_LEVELS - 1]; + int64_t item; + + if (cur == 0) { + cur = hbitmap_iter_skip_words(hbi); + if (cur == 0) { + return -1; + } + } + + /* The next call will resume work from the next bit. */ + hbi->cur[HBITMAP_LEVELS - 1] = cur & (cur - 1); + item = ((uint64_t)hbi->pos << BITS_PER_LEVEL) + bitops_ctzl(cur); + + return item << hbi->granularity; +} + +/** + * hbitmap_iter_next_word: + * @hbi: HBitmapIter to operate on. + * @p_cur: Location where to store the next non-zero word. + * + * Return the index of the next nonzero word that is set in @hbi's + * associated HBitmap, and set *p_cur to the content of that word + * (bits before the index that was passed to hbitmap_iter_init are + * trimmed on the first call). Return -1, and set *p_cur to zero, + * if all remaining words are zero. + */ +static inline size_t hbitmap_iter_next_word(HBitmapIter *hbi, unsigned long *p_cur) +{ + unsigned long cur = hbi->cur[HBITMAP_LEVELS - 1]; + + if (cur == 0) { + cur = hbitmap_iter_skip_words(hbi); + if (cur == 0) { + *p_cur = 0; + return -1; + } + } + + /* The next call will resume work from the next word. */ + hbi->cur[HBITMAP_LEVELS - 1] = 0; + *p_cur = cur; + return hbi->pos; +} + + +#endif diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h index 2a32be4cc0..81c9a754ae 100644 --- a/include/qemu/host-utils.h +++ b/include/qemu/host-utils.h @@ -26,7 +26,6 @@ #define HOST_UTILS_H 1 #include "qemu/compiler.h" /* QEMU_GNUC_PREREQ */ -#include <string.h> /* ffsl */ #if defined(__x86_64__) #define __HAVE_FAST_MULU64__ @@ -238,29 +237,4 @@ static inline int ctpop64(uint64_t val) #endif } -/* glibc does not provide an inline version of ffsl, so always define - * ours. We need to give it a different name, however. - */ -#ifdef __GLIBC__ -#define ffsl qemu_ffsl -#endif -static inline int ffsl(long val) -{ - if (!val) { - return 0; - } - -#if QEMU_GNUC_PREREQ(3, 4) - return __builtin_ctzl(val) + 1; -#else - if (sizeof(long) == 4) { - return ctz32(val) + 1; - } else if (sizeof(long) == 8) { - return ctz64(val) + 1; - } else { - abort(); - } -#endif -} - #endif diff --git a/include/qom/cpu.h b/include/qom/cpu.h index 773caf9fa1..46f2247274 100644 --- a/include/qom/cpu.h +++ b/include/qom/cpu.h @@ -40,6 +40,8 @@ typedef struct CPUState CPUState; /** * CPUClass: + * @class_by_name: Callback to map -cpu command line model name to an + * instantiatable CPU type. * @reset: Callback to reset the #CPUState to its initial state. * * Represents a CPU family or model. @@ -49,6 +51,8 @@ typedef struct CPUClass { DeviceClass parent_class; /*< public >*/ + ObjectClass *(*class_by_name)(const char *cpu_model); + void (*reset)(CPUState *cpu); } CPUClass; @@ -89,10 +93,8 @@ struct CPUState { bool stop; bool stopped; -#if !defined(CONFIG_USER_ONLY) int kvm_fd; bool kvm_vcpu_dirty; -#endif struct KVMState *kvm_state; struct kvm_run *kvm_run; @@ -108,6 +110,17 @@ struct CPUState { void cpu_reset(CPUState *cpu); /** + * cpu_class_by_name: + * @typename: The CPU base type. + * @cpu_model: The model string without any parameters. + * + * Looks up a CPU #ObjectClass matching name @cpu_model. + * + * Returns: A #CPUClass or %NULL if not matching class is found. + */ +ObjectClass *cpu_class_by_name(const char *typename, const char *cpu_model); + +/** * qemu_cpu_has_work: * @cpu: The vCPU to check. * diff --git a/include/qom/object.h b/include/qom/object.h index 8e16ea8a44..cf094e7142 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -553,9 +553,9 @@ struct InterfaceClass * object_new: * @typename: The name of the type of the object to instantiate. * - * This function will initialize a new object using heap allocated memory. This - * function should be paired with object_delete() to free the resources - * associated with the object. + * This function will initialize a new object using heap allocated memory. + * The returned object has a reference count of 1, and will be freed when + * the last reference is dropped. * * Returns: The newly allocated and instantiated object. */ @@ -565,30 +565,22 @@ Object *object_new(const char *typename); * object_new_with_type: * @type: The type of the object to instantiate. * - * This function will initialize a new object using heap allocated memory. This - * function should be paired with object_delete() to free the resources - * associated with the object. + * This function will initialize a new object using heap allocated memory. + * The returned object has a reference count of 1, and will be freed when + * the last reference is dropped. * * Returns: The newly allocated and instantiated object. */ Object *object_new_with_type(Type type); /** - * object_delete: - * @obj: The object to free. - * - * Finalize an object and then free the memory associated with it. This should - * be paired with object_new() to free the resources associated with an object. - */ -void object_delete(Object *obj); - -/** * object_initialize_with_type: * @obj: A pointer to the memory to be used for the object. * @type: The type of the object to instantiate. * * This function will initialize an object. The memory for the object should - * have already been allocated. + * have already been allocated. The returned object has a reference count of 1, + * and will be finalized when the last reference is dropped. */ void object_initialize_with_type(void *data, Type type); @@ -598,7 +590,8 @@ void object_initialize_with_type(void *data, Type type); * @typename: The name of the type of the object to instantiate. * * This function will initialize an object. The memory for the object should - * have already been allocated. + * have already been allocated. The returned object has a reference count of 1, + * and will be finalized when the last reference is dropped. */ void object_initialize(void *obj, const char *typename); @@ -691,6 +684,14 @@ ObjectClass *object_class_get_parent(ObjectClass *klass); const char *object_class_get_name(ObjectClass *klass); /** + * object_class_is_abstract: + * @klass: The class to obtain the abstractness for. + * + * Returns: %true if @klass is abstract, %false otherwise. + */ +bool object_class_is_abstract(ObjectClass *klass); + +/** * object_class_by_name: * @typename: The QOM typename to obtain the class for. * @@ -1033,6 +1034,11 @@ void object_property_add_child(Object *obj, const char *name, * between objects. * * Links form the graph in the object model. + * + * Ownership of the pointer that @child points to is transferred to the + * link property. The reference count for <code>*@child</code> is + * managed by the property from after the function returns till the + * property is deleted with object_property_del(). */ void object_property_add_link(Object *obj, const char *name, const char *type, Object **child, diff --git a/include/sysemu/cpus.h b/include/sysemu/cpus.h index 81bd81773f..f7f6854259 100644 --- a/include/sysemu/cpus.h +++ b/include/sysemu/cpus.h @@ -13,9 +13,16 @@ void cpu_synchronize_all_post_init(void); void qtest_clock_warp(int64_t dest); +#ifndef CONFIG_USER_ONLY /* vl.c */ extern int smp_cores; extern int smp_threads; +#else +/* *-user doesn't have configurable SMP topology */ +#define smp_cores 1 +#define smp_threads 1 +#endif + void set_numa_modes(void); void set_cpu_log(const char *optarg); void set_cpu_log_filename(const char *optarg); diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 6bdd51373e..f2d97b580d 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -36,9 +36,10 @@ #define KVM_FEATURE_ASYNC_PF 0 #define KVM_FEATURE_STEAL_TIME 0 #define KVM_FEATURE_PV_EOI 0 +#define KVM_FEATURE_CLOCKSOURCE_STABLE_BIT 0 #endif -extern int kvm_allowed; +extern bool kvm_allowed; extern bool kvm_kernel_irqchip; extern bool kvm_async_interrupts_allowed; extern bool kvm_irqfds_allowed; @@ -158,7 +159,7 @@ int kvm_update_guest_debug(CPUArchState *env, unsigned long reinject_trap); int kvm_set_signal_mask(CPUArchState *env, const sigset_t *sigset); #endif -int kvm_on_sigbus_vcpu(CPUArchState *env, int code, void *addr); +int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr); int kvm_on_sigbus(int code, void *addr); /* internal API */ @@ -195,6 +196,9 @@ int kvm_arch_init(KVMState *s); int kvm_arch_init_vcpu(CPUState *cpu); +/* Returns VCPU ID to be used on KVM_CREATE_VCPU ioctl() */ +unsigned long kvm_arch_vcpu_id(CPUState *cpu); + void kvm_arch_reset_vcpu(CPUState *cpu); int kvm_arch_on_sigbus_vcpu(CPUState *cpu, int code, void *addr); diff --git a/include/sysemu/os-win32.h b/include/sysemu/os-win32.h index d0e9234d24..bf9edeb9ab 100644 --- a/include/sysemu/os-win32.h +++ b/include/sysemu/os-win32.h @@ -73,6 +73,8 @@ struct tm *gmtime_r(const time_t *timep, struct tm *result); #undef localtime_r struct tm *localtime_r(const time_t *timep, struct tm *result); +char *strtok_r(char *str, const char *delim, char **saveptr); + static inline void os_setup_signal_handling(void) {} static inline void os_daemonize(void) {} static inline void os_setup_post(void) {} diff --git a/include/sysemu/qtest.h b/include/sysemu/qtest.h index 723a4f9536..9a0c6b31c8 100644 --- a/include/sysemu/qtest.h +++ b/include/sysemu/qtest.h @@ -17,7 +17,7 @@ #include "qemu-common.h" #if !defined(CONFIG_USER_ONLY) -extern int qtest_allowed; +extern bool qtest_allowed; extern const char *qtest_chrdev; extern const char *qtest_log; diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 337ce7df0c..1d9599e5f4 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -122,7 +122,7 @@ extern int semihosting_enabled; extern int old_param; extern int boot_menu; extern uint8_t *boot_splash_filedata; -extern int boot_splash_filedata_size; +extern size_t boot_splash_filedata_size; extern uint8_t qemu_extra_params_fw[2]; extern QEMUClock *rtc_clock; diff --git a/include/ui/qemu-pixman.h b/include/ui/qemu-pixman.h index 016fd87726..b032f529aa 100644 --- a/include/ui/qemu-pixman.h +++ b/include/ui/qemu-pixman.h @@ -15,7 +15,7 @@ #pragma GCC diagnostic error "-Wredundant-decls" #endif -#include "console.h" +#include "qemu/typedefs.h" /* * pixman image formats are defined to be native endian, diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h index 8b192e9613..46f9530fe3 100644 --- a/include/ui/spice-display.h +++ b/include/ui/spice-display.h @@ -21,6 +21,7 @@ #include "qemu/thread.h" #include "ui/qemu-pixman.h" +#include "ui/console.h" #include "sysemu/sysemu.h" #define NUM_MEMSLOTS 8 @@ -222,7 +222,7 @@ int kvm_init_vcpu(CPUState *cpu) DPRINTF("kvm_init_vcpu\n"); - ret = kvm_vm_ioctl(s, KVM_CREATE_VCPU, cpu->cpu_index); + ret = kvm_vm_ioctl(s, KVM_CREATE_VCPU, (void *)kvm_arch_vcpu_id(cpu)); if (ret < 0) { DPRINTF("kvm_create_vcpu failed\n"); goto err; @@ -2026,9 +2026,8 @@ int kvm_set_ioeventfd_pio_word(int fd, uint16_t addr, uint16_t val, bool assign) return 0; } -int kvm_on_sigbus_vcpu(CPUArchState *env, int code, void *addr) +int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr) { - CPUState *cpu = ENV_GET_CPU(env); return kvm_arch_on_sigbus_vcpu(cpu, code, addr); } diff --git a/kvm-stub.c b/kvm-stub.c index 47f8dca7d5..760aadc874 100644 --- a/kvm-stub.c +++ b/kvm-stub.c @@ -112,7 +112,7 @@ int kvm_set_ioeventfd_mmio(int fd, uint32_t adr, uint32_t val, bool assign, uint return -ENOSYS; } -int kvm_on_sigbus_vcpu(CPUArchState *env, int code, void *addr) +int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr) { return 1; } diff --git a/libcacard/vcard_emul_nss.c b/libcacard/vcard_emul_nss.c index 5f565e0b4a..df79476db8 100644 --- a/libcacard/vcard_emul_nss.c +++ b/libcacard/vcard_emul_nss.c @@ -454,7 +454,7 @@ vreader_emul_new(PK11SlotInfo *slot, VCardEmulType type, const char *params) new_reader_emul->slot = PK11_ReferenceSlot(slot); new_reader_emul->default_type = type; - new_reader_emul->type_params = strdup(params); + new_reader_emul->type_params = g_strdup(params); new_reader_emul->present = PR_FALSE; new_reader_emul->series = 0; new_reader_emul->saved_vcard = NULL; @@ -997,7 +997,7 @@ vcard_emul_init(const VCardEmulOptions *options) /* We should control this with options. For now we mirror out any * removable hardware slot */ default_card_type = options->hw_card_type; - default_type_params = strdup(options->hw_type_params); + default_type_params = g_strdup(options->hw_type_params); SECMOD_GetReadLock(module_lock); for (mlp = module_list; mlp; mlp = mlp->next) { diff --git a/libcacard/vreader.c b/libcacard/vreader.c index 313349b656..f3efc270a2 100644 --- a/libcacard/vreader.c +++ b/libcacard/vreader.c @@ -49,7 +49,7 @@ vreader_new(const char *name, VReaderEmul *private, reader = (VReader *)g_malloc(sizeof(VReader)); qemu_mutex_init(&reader->lock); reader->reference_count = 1; - reader->name = name ? strdup(name) : NULL; + reader->name = g_strdup(name); reader->card = NULL; reader->id = (vreader_id_t)-1; reader->reader_private = private; diff --git a/libcacard/vscclient.c b/libcacard/vscclient.c index 2fce52bed5..9b744f249c 100644 --- a/libcacard/vscclient.c +++ b/libcacard/vscclient.c @@ -503,8 +503,8 @@ main( command_line_options = vcard_emul_options(emul_args); } - qemu_host = strdup(argv[argc - 2]); - qemu_port = strdup(argv[argc - 1]); + qemu_host = g_strdup(argv[argc - 2]); + qemu_port = g_strdup(argv[argc - 1]); sock = connect_to_qemu(qemu_host, qemu_port); if (sock == -1) { fprintf(stderr, "error opening socket, exiting.\n"); diff --git a/linux-user/main.c b/linux-user/main.c index 0181bc2112..3df8aa2cc5 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -3540,7 +3540,7 @@ int main(int argc, char **argv, char **envp) fprintf(stderr, "Unable to find CPU definition\n"); exit(1); } -#if defined(TARGET_I386) || defined(TARGET_SPARC) || defined(TARGET_PPC) +#if defined(TARGET_SPARC) || defined(TARGET_PPC) cpu_reset(ENV_GET_CPU(env)); #endif diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 31a220af81..b10e9572a9 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -306,12 +306,12 @@ static inline int access_ok(int type, abi_ulong addr, abi_ulong size) ((hptr), (x)), 0) #define __get_user_e(x, hptr, e) \ - ((x) = \ + ((x) = (typeof(*hptr))( \ __builtin_choose_expr(sizeof(*(hptr)) == 1, ldub_p, \ __builtin_choose_expr(sizeof(*(hptr)) == 2, lduw_##e##_p, \ __builtin_choose_expr(sizeof(*(hptr)) == 4, ldl_##e##_p, \ __builtin_choose_expr(sizeof(*(hptr)) == 8, ldq_##e##_p, abort)))) \ - (hptr), 0) + (hptr)), 0) #ifdef TARGET_WORDS_BIGENDIAN # define __put_user(x, hptr) __put_user_e(x, hptr, be) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 08538fc35c..9e31ea7200 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -5219,7 +5219,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, NULL, NULL, 0); } thread_env = NULL; - object_delete(OBJECT(ENV_GET_CPU(cpu_env))); + object_unref(OBJECT(ENV_GET_CPU(cpu_env))); g_free(ts); pthread_exit(NULL); } @@ -855,7 +855,7 @@ static uint64_t memory_region_dispatch_read1(MemoryRegion *mr, } if (!mr->ops->read) { - return mr->ops->old_mmio.read[bitops_ffsl(size)](mr->opaque, addr); + return mr->ops->old_mmio.read[bitops_ctzl(size)](mr->opaque, addr); } /* FIXME: support unaligned access */ @@ -908,7 +908,7 @@ static void memory_region_dispatch_write(MemoryRegion *mr, adjust_endianness(mr, &data, size); if (!mr->ops->write) { - mr->ops->old_mmio.write[bitops_ffsl(size)](mr->opaque, addr, data); + mr->ops->old_mmio.write[bitops_ctzl(size)](mr->opaque, addr, data); return; } @@ -182,17 +182,18 @@ static char *assign_name(NetClientState *nc1, const char *model) return g_strdup(buf); } -NetClientState *qemu_new_net_client(NetClientInfo *info, - NetClientState *peer, - const char *model, - const char *name) +static void qemu_net_client_destructor(NetClientState *nc) { - NetClientState *nc; - - assert(info->size >= sizeof(NetClientState)); - - nc = g_malloc0(info->size); + g_free(nc); +} +static void qemu_net_client_setup(NetClientState *nc, + NetClientInfo *info, + NetClientState *peer, + const char *model, + const char *name, + NetClientDestructor *destructor) +{ nc->info = info; nc->model = g_strdup(model); if (name) { @@ -209,6 +210,21 @@ NetClientState *qemu_new_net_client(NetClientInfo *info, QTAILQ_INSERT_TAIL(&net_clients, nc, next); nc->send_queue = qemu_new_net_queue(nc); + nc->destructor = destructor; +} + +NetClientState *qemu_new_net_client(NetClientInfo *info, + NetClientState *peer, + const char *model, + const char *name) +{ + NetClientState *nc; + + assert(info->size >= sizeof(NetClientState)); + + nc = g_malloc0(info->size); + qemu_net_client_setup(nc, info, peer, model, name, + qemu_net_client_destructor); return nc; } @@ -220,27 +236,58 @@ NICState *qemu_new_nic(NetClientInfo *info, void *opaque) { NetClientState *nc; + NetClientState **peers = conf->peers.ncs; NICState *nic; + int i; assert(info->type == NET_CLIENT_OPTIONS_KIND_NIC); assert(info->size >= sizeof(NICState)); - nc = qemu_new_net_client(info, conf->peer, model, name); + nc = qemu_new_net_client(info, peers[0], model, name); + nc->queue_index = 0; - nic = DO_UPCAST(NICState, nc, nc); + nic = qemu_get_nic(nc); nic->conf = conf; nic->opaque = opaque; + for (i = 1; i < conf->queues; i++) { + qemu_net_client_setup(&nic->ncs[i], info, peers[i], model, nc->name, + NULL); + nic->ncs[i].queue_index = i; + } + return nic; } +NetClientState *qemu_get_subqueue(NICState *nic, int queue_index) +{ + return &nic->ncs[queue_index]; +} + +NetClientState *qemu_get_queue(NICState *nic) +{ + return qemu_get_subqueue(nic, 0); +} + +NICState *qemu_get_nic(NetClientState *nc) +{ + NetClientState *nc0 = nc - nc->queue_index; + + return DO_UPCAST(NICState, ncs[0], nc0); +} + +void *qemu_get_nic_opaque(NetClientState *nc) +{ + NICState *nic = qemu_get_nic(nc); + + return nic->opaque; +} + static void qemu_cleanup_net_client(NetClientState *nc) { QTAILQ_REMOVE(&net_clients, nc, next); - if (nc->info->cleanup) { - nc->info->cleanup(nc); - } + nc->info->cleanup(nc); } static void qemu_free_net_client(NetClientState *nc) @@ -253,37 +300,72 @@ static void qemu_free_net_client(NetClientState *nc) } g_free(nc->name); g_free(nc->model); - g_free(nc); + if (nc->destructor) { + nc->destructor(nc); + } } void qemu_del_net_client(NetClientState *nc) { + NetClientState *ncs[MAX_QUEUE_NUM]; + int queues, i; + + /* If the NetClientState belongs to a multiqueue backend, we will change all + * other NetClientStates also. + */ + queues = qemu_find_net_clients_except(nc->name, ncs, + NET_CLIENT_OPTIONS_KIND_NIC, + MAX_QUEUE_NUM); + assert(queues != 0); + /* If there is a peer NIC, delete and cleanup client, but do not free. */ if (nc->peer && nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_NIC) { - NICState *nic = DO_UPCAST(NICState, nc, nc->peer); + NICState *nic = qemu_get_nic(nc->peer); if (nic->peer_deleted) { return; } nic->peer_deleted = true; - /* Let NIC know peer is gone. */ - nc->peer->link_down = true; + + for (i = 0; i < queues; i++) { + ncs[i]->peer->link_down = true; + } + if (nc->peer->info->link_status_changed) { nc->peer->info->link_status_changed(nc->peer); } - qemu_cleanup_net_client(nc); + + for (i = 0; i < queues; i++) { + qemu_cleanup_net_client(ncs[i]); + } + return; } + assert(nc->info->type != NET_CLIENT_OPTIONS_KIND_NIC); + + for (i = 0; i < queues; i++) { + qemu_cleanup_net_client(ncs[i]); + qemu_free_net_client(ncs[i]); + } +} + +void qemu_del_nic(NICState *nic) +{ + int i, queues = nic->conf->queues; + /* If this is a peer NIC and peer has already been deleted, free it now. */ - if (nc->peer && nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) { - NICState *nic = DO_UPCAST(NICState, nc, nc); - if (nic->peer_deleted) { - qemu_free_net_client(nc->peer); + if (nic->peer_deleted) { + for (i = 0; i < queues; i++) { + qemu_free_net_client(qemu_get_subqueue(nic, i)->peer); } } - qemu_cleanup_net_client(nc); - qemu_free_net_client(nc); + for (i = queues - 1; i >= 0; i--) { + NetClientState *nc = qemu_get_subqueue(nic, i); + + qemu_cleanup_net_client(nc); + qemu_free_net_client(nc); + } } void qemu_foreach_nic(qemu_nic_foreach func, void *opaque) @@ -292,7 +374,9 @@ void qemu_foreach_nic(qemu_nic_foreach func, void *opaque) QTAILQ_FOREACH(nc, &net_clients, next) { if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) { - func(DO_UPCAST(NICState, nc, nc), opaque); + if (nc->queue_index == 0) { + func(qemu_get_nic(nc), opaque); + } } } } @@ -482,6 +566,27 @@ NetClientState *qemu_find_netdev(const char *id) return NULL; } +int qemu_find_net_clients_except(const char *id, NetClientState **ncs, + NetClientOptionsKind type, int max) +{ + NetClientState *nc; + int ret = 0; + + QTAILQ_FOREACH(nc, &net_clients, next) { + if (nc->info->type == type) { + continue; + } + if (!strcmp(nc->name, id)) { + if (ret < max) { + ncs[ret] = nc; + } + ret++; + } + } + + return ret; +} + static int nic_get_free_idx(void) { int index; @@ -566,9 +671,7 @@ static int net_init_nic(const NetClientOptions *opts, const char *name, assert(peer); nd->netdev = peer; } - if (name) { - nd->name = g_strdup(name); - } + nd->name = g_strdup(name); if (nic->has_model) { nd->model = g_strdup(nic->model); } @@ -848,8 +951,10 @@ void qmp_netdev_del(const char *id, Error **errp) void print_net_client(Monitor *mon, NetClientState *nc) { - monitor_printf(mon, "%s: type=%s,%s\n", nc->name, - NetClientOptionsKind_lookup[nc->info->type], nc->info_str); + monitor_printf(mon, "%s: index=%d,type=%s,%s\n", nc->name, + nc->queue_index, + NetClientOptionsKind_lookup[nc->info->type], + nc->info_str); } void do_info_network(Monitor *mon, const QDict *qdict) @@ -880,20 +985,23 @@ void do_info_network(Monitor *mon, const QDict *qdict) void qmp_set_link(const char *name, bool up, Error **errp) { - NetClientState *nc = NULL; + NetClientState *ncs[MAX_QUEUE_NUM]; + NetClientState *nc; + int queues, i; - QTAILQ_FOREACH(nc, &net_clients, next) { - if (!strcmp(nc->name, name)) { - goto done; - } - } -done: - if (!nc) { + queues = qemu_find_net_clients_except(name, ncs, + NET_CLIENT_OPTIONS_KIND_MAX, + MAX_QUEUE_NUM); + + if (queues == 0) { error_set(errp, QERR_DEVICE_NOT_FOUND, name); return; } + nc = ncs[0]; - nc->link_down = !up; + for (i = 0; i < queues; i++) { + ncs[i]->link_down = !up; + } if (nc->info->link_status_changed) { nc->info->link_status_changed(nc); @@ -913,10 +1021,18 @@ done: void net_cleanup(void) { - NetClientState *nc, *next_vc; + NetClientState *nc; - QTAILQ_FOREACH_SAFE(nc, &net_clients, next, next_vc) { - qemu_del_net_client(nc); + /* We may del multiple entries during qemu_del_net_client(), + * so QTAILQ_FOREACH_SAFE() is also not safe here. + */ + while (!QTAILQ_EMPTY(&net_clients)) { + nc = QTAILQ_FIRST(&net_clients); + if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) { + qemu_del_nic(qemu_get_nic(nc)); + } else { + qemu_del_net_client(nc); + } } } diff --git a/net/tap-aix.c b/net/tap-aix.c index aff6c527e9..804d16448d 100644 --- a/net/tap-aix.c +++ b/net/tap-aix.c @@ -25,7 +25,8 @@ #include "tap_int.h" #include <stdio.h> -int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required) +int tap_open(char *ifname, int ifname_size, int *vnet_hdr, + int vnet_hdr_required, int mq_required) { fprintf(stderr, "no tap on AIX\n"); return -1; @@ -59,3 +60,19 @@ void tap_fd_set_offload(int fd, int csum, int tso4, int tso6, int ecn, int ufo) { } + +int tap_fd_enable(int fd) +{ + return -1; +} + +int tap_fd_disable(int fd) +{ + return -1; +} + +int tap_fd_get_ifname(int fd, char *ifname) +{ + return -1; +} + diff --git a/net/tap-bsd.c b/net/tap-bsd.c index 01c705b4c0..bcdb2682b5 100644 --- a/net/tap-bsd.c +++ b/net/tap-bsd.c @@ -33,7 +33,8 @@ #include <net/if_tap.h> #endif -int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required) +int tap_open(char *ifname, int ifname_size, int *vnet_hdr, + int vnet_hdr_required, int mq_required) { int fd; #ifdef TAPGIFNAME @@ -145,3 +146,18 @@ void tap_fd_set_offload(int fd, int csum, int tso4, int tso6, int ecn, int ufo) { } + +int tap_fd_enable(int fd) +{ + return -1; +} + +int tap_fd_disable(int fd) +{ + return -1; +} + +int tap_fd_get_ifname(int fd, char *ifname) +{ + return -1; +} diff --git a/net/tap-haiku.c b/net/tap-haiku.c index 08cc034cee..e5ce436d24 100644 --- a/net/tap-haiku.c +++ b/net/tap-haiku.c @@ -25,7 +25,8 @@ #include "tap_int.h" #include <stdio.h> -int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required) +int tap_open(char *ifname, int ifname_size, int *vnet_hdr, + int vnet_hdr_required, int mq_required) { fprintf(stderr, "no tap on Haiku\n"); return -1; @@ -59,3 +60,18 @@ void tap_fd_set_offload(int fd, int csum, int tso4, int tso6, int ecn, int ufo) { } + +int tap_fd_enable(int fd) +{ + return -1; +} + +int tap_fd_disable(int fd) +{ + return -1; +} + +int tap_fd_get_ifname(int fd, char *ifname) +{ + return -1; +} diff --git a/net/tap-linux.c b/net/tap-linux.c index 059f5f34ab..a9531892a6 100644 --- a/net/tap-linux.c +++ b/net/tap-linux.c @@ -36,7 +36,8 @@ #define PATH_NET_TUN "/dev/net/tun" -int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required) +int tap_open(char *ifname, int ifname_size, int *vnet_hdr, + int vnet_hdr_required, int mq_required) { struct ifreq ifr; int fd, ret; @@ -76,6 +77,20 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required ioctl(fd, TUNSETVNETHDRSZ, &len); } + if (mq_required) { + unsigned int features; + + if ((ioctl(fd, TUNGETFEATURES, &features) != 0) || + !(features & IFF_MULTI_QUEUE)) { + error_report("multiqueue required, but no kernel " + "support for IFF_MULTI_QUEUE available"); + close(fd); + return -1; + } else { + ifr.ifr_flags |= IFF_MULTI_QUEUE; + } + } + if (ifname[0] != '\0') pstrcpy(ifr.ifr_name, IFNAMSIZ, ifname); else @@ -164,7 +179,7 @@ int tap_probe_vnet_hdr_len(int fd, int len) if (ioctl(fd, TUNSETVNETHDRSZ, &orig) == -1) { fprintf(stderr, "TUNGETVNETHDRSZ ioctl() failed: %s. Exiting.\n", strerror(errno)); - assert(0); + abort(); return -errno; } return 1; @@ -175,7 +190,7 @@ void tap_fd_set_vnet_hdr_len(int fd, int len) if (ioctl(fd, TUNSETVNETHDRSZ, &len) == -1) { fprintf(stderr, "TUNSETVNETHDRSZ ioctl() failed: %s. Exiting.\n", strerror(errno)); - assert(0); + abort(); } } @@ -209,3 +224,53 @@ void tap_fd_set_offload(int fd, int csum, int tso4, } } } + +/* Enable a specific queue of tap. */ +int tap_fd_enable(int fd) +{ + struct ifreq ifr; + int ret; + + memset(&ifr, 0, sizeof(ifr)); + + ifr.ifr_flags = IFF_ATTACH_QUEUE; + ret = ioctl(fd, TUNSETQUEUE, (void *) &ifr); + + if (ret != 0) { + error_report("could not enable queue"); + } + + return ret; +} + +/* Disable a specific queue of tap/ */ +int tap_fd_disable(int fd) +{ + struct ifreq ifr; + int ret; + + memset(&ifr, 0, sizeof(ifr)); + + ifr.ifr_flags = IFF_DETACH_QUEUE; + ret = ioctl(fd, TUNSETQUEUE, (void *) &ifr); + + if (ret != 0) { + error_report("could not disable queue"); + } + + return ret; +} + +int tap_fd_get_ifname(int fd, char *ifname) +{ + struct ifreq ifr; + + if (ioctl(fd, TUNGETIFF, &ifr) != 0) { + error_report("TUNGETIFF ioctl() failed: %s", + strerror(errno)); + return -1; + } + + pstrcpy(ifname, sizeof(ifr.ifr_name), ifr.ifr_name); + return 0; +} diff --git a/net/tap-linux.h b/net/tap-linux.h index cb2a6d480a..65087e1419 100644 --- a/net/tap-linux.h +++ b/net/tap-linux.h @@ -29,6 +29,7 @@ #define TUNSETSNDBUF _IOW('T', 212, int) #define TUNGETVNETHDRSZ _IOR('T', 215, int) #define TUNSETVNETHDRSZ _IOW('T', 216, int) +#define TUNSETQUEUE _IOW('T', 217, int) #endif @@ -36,6 +37,9 @@ #define IFF_TAP 0x0002 #define IFF_NO_PI 0x1000 #define IFF_VNET_HDR 0x4000 +#define IFF_MULTI_QUEUE 0x0100 +#define IFF_ATTACH_QUEUE 0x0200 +#define IFF_DETACH_QUEUE 0x0400 /* Features for GSO (TUNSETOFFLOAD). */ #define TUN_F_CSUM 0x01 /* You can hand me unchecksummed packets. */ diff --git a/net/tap-solaris.c b/net/tap-solaris.c index 486a7ea838..9c7278f1bf 100644 --- a/net/tap-solaris.c +++ b/net/tap-solaris.c @@ -173,7 +173,8 @@ static int tap_alloc(char *dev, size_t dev_size) return tap_fd; } -int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required) +int tap_open(char *ifname, int ifname_size, int *vnet_hdr, + int vnet_hdr_required, int mq_required) { char dev[10]=""; int fd; @@ -225,3 +226,18 @@ void tap_fd_set_offload(int fd, int csum, int tso4, int tso6, int ecn, int ufo) { } + +int tap_fd_enable(int fd) +{ + return -1; +} + +int tap_fd_disable(int fd) +{ + return -1; +} + +int tap_fd_get_ifname(int fd, char *ifname) +{ + return -1; +} diff --git a/net/tap-win32.c b/net/tap-win32.c index 265369c3c5..91e9e844a0 100644 --- a/net/tap-win32.c +++ b/net/tap-win32.c @@ -722,9 +722,9 @@ int net_init_tap(const NetClientOptions *opts, const char *name, return 0; } -int tap_has_ufo(NetClientState *nc) +bool tap_has_ufo(NetClientState *nc) { - return 0; + return false; } int tap_has_vnet_hdr(NetClientState *nc) @@ -741,7 +741,7 @@ void tap_fd_set_vnet_hdr_len(int fd, int len) { } -void tap_using_vnet_hdr(NetClientState *nc, int using_vnet_hdr) +void tap_using_vnet_hdr(NetClientState *nc, bool using_vnet_hdr) { } @@ -762,5 +762,15 @@ int tap_has_vnet_hdr_len(NetClientState *nc, int len) void tap_set_vnet_hdr_len(NetClientState *nc, int len) { - assert(0); + abort(); +} + +int tap_enable(NetClientState *nc) +{ + abort(); +} + +int tap_disable(NetClientState *nc) +{ + abort(); } @@ -55,10 +55,11 @@ typedef struct TAPState { char down_script[1024]; char down_script_arg[128]; uint8_t buf[TAP_BUFSIZE]; - unsigned int read_poll : 1; - unsigned int write_poll : 1; - unsigned int using_vnet_hdr : 1; - unsigned int has_ufo: 1; + bool read_poll; + bool write_poll; + bool using_vnet_hdr; + bool has_ufo; + bool enabled; VHostNetState *vhost_net; unsigned host_vnet_hdr_len; } TAPState; @@ -72,21 +73,21 @@ static void tap_writable(void *opaque); static void tap_update_fd_handler(TAPState *s) { qemu_set_fd_handler2(s->fd, - s->read_poll ? tap_can_send : NULL, - s->read_poll ? tap_send : NULL, - s->write_poll ? tap_writable : NULL, + s->read_poll && s->enabled ? tap_can_send : NULL, + s->read_poll && s->enabled ? tap_send : NULL, + s->write_poll && s->enabled ? tap_writable : NULL, s); } -static void tap_read_poll(TAPState *s, int enable) +static void tap_read_poll(TAPState *s, bool enable) { - s->read_poll = !!enable; + s->read_poll = enable; tap_update_fd_handler(s); } -static void tap_write_poll(TAPState *s, int enable) +static void tap_write_poll(TAPState *s, bool enable) { - s->write_poll = !!enable; + s->write_poll = enable; tap_update_fd_handler(s); } @@ -94,7 +95,7 @@ static void tap_writable(void *opaque) { TAPState *s = opaque; - tap_write_poll(s, 0); + tap_write_poll(s, false); qemu_flush_queued_packets(&s->nc); } @@ -108,7 +109,7 @@ static ssize_t tap_write_packet(TAPState *s, const struct iovec *iov, int iovcnt } while (len == -1 && errno == EINTR); if (len == -1 && errno == EAGAIN) { - tap_write_poll(s, 1); + tap_write_poll(s, true); return 0; } @@ -186,7 +187,7 @@ ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen) static void tap_send_completed(NetClientState *nc, ssize_t len) { TAPState *s = DO_UPCAST(TAPState, nc, nc); - tap_read_poll(s, 1); + tap_read_poll(s, true); } static void tap_send(void *opaque) @@ -209,12 +210,12 @@ static void tap_send(void *opaque) size = qemu_send_packet_async(&s->nc, buf, size, tap_send_completed); if (size == 0) { - tap_read_poll(s, 0); + tap_read_poll(s, false); } } while (size > 0 && qemu_can_send_packet(&s->nc)); } -int tap_has_ufo(NetClientState *nc) +bool tap_has_ufo(NetClientState *nc) { TAPState *s = DO_UPCAST(TAPState, nc, nc); @@ -253,12 +254,10 @@ void tap_set_vnet_hdr_len(NetClientState *nc, int len) s->host_vnet_hdr_len = len; } -void tap_using_vnet_hdr(NetClientState *nc, int using_vnet_hdr) +void tap_using_vnet_hdr(NetClientState *nc, bool using_vnet_hdr) { TAPState *s = DO_UPCAST(TAPState, nc, nc); - using_vnet_hdr = using_vnet_hdr != 0; - assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP); assert(!!s->host_vnet_hdr_len == using_vnet_hdr); @@ -290,8 +289,8 @@ static void tap_cleanup(NetClientState *nc) if (s->down_script[0]) launch_script(s->down_script, s->down_script_arg, s->fd); - tap_read_poll(s, 0); - tap_write_poll(s, 0); + tap_read_poll(s, false); + tap_write_poll(s, false); close(s->fd); s->fd = -1; } @@ -337,8 +336,9 @@ static TAPState *net_tap_fd_init(NetClientState *peer, s->fd = fd; s->host_vnet_hdr_len = vnet_hdr ? sizeof(struct virtio_net_hdr) : 0; - s->using_vnet_hdr = 0; + s->using_vnet_hdr = false; s->has_ufo = tap_probe_has_ufo(s->fd); + s->enabled = true; tap_set_offload(&s->nc, 0, 0, 0, 0, 0); /* * Make sure host header length is set correctly in tap: @@ -347,7 +347,7 @@ static TAPState *net_tap_fd_init(NetClientState *peer, if (tap_probe_vnet_hdr_len(s->fd, s->host_vnet_hdr_len)) { tap_fd_set_vnet_hdr_len(s->fd, s->host_vnet_hdr_len); } - tap_read_poll(s, 1); + tap_read_poll(s, true); s->vhost_net = NULL; return s; } @@ -558,17 +558,10 @@ int net_init_bridge(const NetClientOptions *opts, const char *name, static int net_tap_init(const NetdevTapOptions *tap, int *vnet_hdr, const char *setup_script, char *ifname, - size_t ifname_sz) + size_t ifname_sz, int mq_required) { int fd, vnet_hdr_required; - if (tap->has_ifname) { - pstrcpy(ifname, ifname_sz, tap->ifname); - } else { - assert(ifname_sz > 0); - ifname[0] = '\0'; - } - if (tap->has_vnet_hdr) { *vnet_hdr = tap->vnet_hdr; vnet_hdr_required = *vnet_hdr; @@ -577,7 +570,8 @@ static int net_tap_init(const NetdevTapOptions *tap, int *vnet_hdr, vnet_hdr_required = 0; } - TFR(fd = tap_open(ifname, ifname_sz, vnet_hdr, vnet_hdr_required)); + TFR(fd = tap_open(ifname, ifname_sz, vnet_hdr, vnet_hdr_required, + mq_required)); if (fd < 0) { return -1; } @@ -593,27 +587,118 @@ static int net_tap_init(const NetdevTapOptions *tap, int *vnet_hdr, return fd; } +#define MAX_TAP_QUEUES 1024 + +static int net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, + const char *model, const char *name, + const char *ifname, const char *script, + const char *downscript, const char *vhostfdname, + int vnet_hdr, int fd) +{ + TAPState *s; + + s = net_tap_fd_init(peer, model, name, fd, vnet_hdr); + if (!s) { + close(fd); + return -1; + } + + if (tap_set_sndbuf(s->fd, tap) < 0) { + return -1; + } + + if (tap->has_fd || tap->has_fds) { + snprintf(s->nc.info_str, sizeof(s->nc.info_str), "fd=%d", fd); + } else if (tap->has_helper) { + snprintf(s->nc.info_str, sizeof(s->nc.info_str), "helper=%s", + tap->helper); + } else { + snprintf(s->nc.info_str, sizeof(s->nc.info_str), + "ifname=%s,script=%s,downscript=%s", ifname, script, + downscript); + + if (strcmp(downscript, "no") != 0) { + snprintf(s->down_script, sizeof(s->down_script), "%s", downscript); + snprintf(s->down_script_arg, sizeof(s->down_script_arg), + "%s", ifname); + } + } + + if (tap->has_vhost ? tap->vhost : + vhostfdname || (tap->has_vhostforce && tap->vhostforce)) { + int vhostfd; + + if (tap->has_vhostfd) { + vhostfd = monitor_handle_fd_param(cur_mon, vhostfdname); + if (vhostfd == -1) { + return -1; + } + } else { + vhostfd = -1; + } + + s->vhost_net = vhost_net_init(&s->nc, vhostfd, + tap->has_vhostforce && tap->vhostforce); + if (!s->vhost_net) { + error_report("vhost-net requested but could not be initialized"); + return -1; + } + } else if (tap->has_vhostfd || tap->has_vhostfds) { + error_report("vhostfd= is not valid without vhost"); + return -1; + } + + return 0; +} + +static int get_fds(char *str, char *fds[], int max) +{ + char *ptr = str, *this; + size_t len = strlen(str); + int i = 0; + + while (i < max && ptr < str + len) { + this = strchr(ptr, ':'); + + if (this == NULL) { + fds[i] = g_strdup(ptr); + } else { + fds[i] = g_strndup(ptr, this - ptr); + } + + i++; + if (this == NULL) { + break; + } else { + ptr = this + 1; + } + } + + return i; +} + int net_init_tap(const NetClientOptions *opts, const char *name, NetClientState *peer) { const NetdevTapOptions *tap; - - int fd, vnet_hdr = 0; - const char *model; - TAPState *s; - + int fd, vnet_hdr = 0, i = 0, queues; /* for the no-fd, no-helper case */ const char *script = NULL; /* suppress wrong "uninit'd use" gcc warning */ + const char *downscript = NULL; + const char *vhostfdname; char ifname[128]; assert(opts->kind == NET_CLIENT_OPTIONS_KIND_TAP); tap = opts->tap; + queues = tap->has_queues ? tap->queues : 1; + vhostfdname = tap->has_vhostfd ? tap->vhostfd : NULL; if (tap->has_fd) { if (tap->has_ifname || tap->has_script || tap->has_downscript || - tap->has_vnet_hdr || tap->has_helper) { + tap->has_vnet_hdr || tap->has_helper || tap->has_queues || + tap->has_fds) { error_report("ifname=, script=, downscript=, vnet_hdr=, " - "and helper= are invalid with fd="); + "helper=, queues=, and fds= are invalid with fd="); return -1; } @@ -626,13 +711,61 @@ int net_init_tap(const NetClientOptions *opts, const char *name, vnet_hdr = tap_probe_vnet_hdr(fd); - model = "tap"; + if (net_init_tap_one(tap, peer, "tap", name, NULL, + script, downscript, + vhostfdname, vnet_hdr, fd)) { + return -1; + } + } else if (tap->has_fds) { + char *fds[MAX_TAP_QUEUES]; + char *vhost_fds[MAX_TAP_QUEUES]; + int nfds, nvhosts; + + if (tap->has_ifname || tap->has_script || tap->has_downscript || + tap->has_vnet_hdr || tap->has_helper || tap->has_queues || + tap->has_fd) { + error_report("ifname=, script=, downscript=, vnet_hdr=, " + "helper=, queues=, and fd= are invalid with fds="); + return -1; + } + nfds = get_fds(tap->fds, fds, MAX_TAP_QUEUES); + if (tap->has_vhostfds) { + nvhosts = get_fds(tap->vhostfds, vhost_fds, MAX_TAP_QUEUES); + if (nfds != nvhosts) { + error_report("The number of fds passed does not match the " + "number of vhostfds passed"); + return -1; + } + } + + for (i = 0; i < nfds; i++) { + fd = monitor_handle_fd_param(cur_mon, fds[i]); + if (fd == -1) { + return -1; + } + + fcntl(fd, F_SETFL, O_NONBLOCK); + + if (i == 0) { + vnet_hdr = tap_probe_vnet_hdr(fd); + } else if (vnet_hdr != tap_probe_vnet_hdr(fd)) { + error_report("vnet_hdr not consistent across given tap fds"); + return -1; + } + + if (net_init_tap_one(tap, peer, "tap", name, ifname, + script, downscript, + tap->has_vhostfds ? vhost_fds[i] : NULL, + vnet_hdr, fd)) { + return -1; + } + } } else if (tap->has_helper) { if (tap->has_ifname || tap->has_script || tap->has_downscript || - tap->has_vnet_hdr) { + tap->has_vnet_hdr || tap->has_queues || tap->has_fds) { error_report("ifname=, script=, downscript=, and vnet_hdr= " - "are invalid with helper="); + "queues=, and fds= are invalid with helper="); return -1; } @@ -642,74 +775,45 @@ int net_init_tap(const NetClientOptions *opts, const char *name, } fcntl(fd, F_SETFL, O_NONBLOCK); - vnet_hdr = tap_probe_vnet_hdr(fd); - model = "bridge"; - - } else { - script = tap->has_script ? tap->script : DEFAULT_NETWORK_SCRIPT; - fd = net_tap_init(tap, &vnet_hdr, script, ifname, sizeof ifname); - if (fd == -1) { + if (net_init_tap_one(tap, peer, "bridge", name, ifname, + script, downscript, vhostfdname, + vnet_hdr, fd)) { return -1; } - - model = "tap"; - } - - s = net_tap_fd_init(peer, model, name, fd, vnet_hdr); - if (!s) { - close(fd); - return -1; - } - - if (tap_set_sndbuf(s->fd, tap) < 0) { - return -1; - } - - if (tap->has_fd) { - snprintf(s->nc.info_str, sizeof(s->nc.info_str), "fd=%d", fd); - } else if (tap->has_helper) { - snprintf(s->nc.info_str, sizeof(s->nc.info_str), "helper=%s", - tap->helper); } else { - const char *downscript; - + script = tap->has_script ? tap->script : DEFAULT_NETWORK_SCRIPT; downscript = tap->has_downscript ? tap->downscript : - DEFAULT_NETWORK_DOWN_SCRIPT; + DEFAULT_NETWORK_DOWN_SCRIPT; - snprintf(s->nc.info_str, sizeof(s->nc.info_str), - "ifname=%s,script=%s,downscript=%s", ifname, script, - downscript); - - if (strcmp(downscript, "no") != 0) { - snprintf(s->down_script, sizeof(s->down_script), "%s", downscript); - snprintf(s->down_script_arg, sizeof(s->down_script_arg), "%s", ifname); + if (tap->has_ifname) { + pstrcpy(ifname, sizeof ifname, tap->ifname); + } else { + ifname[0] = '\0'; } - } - - if (tap->has_vhost ? tap->vhost : - tap->has_vhostfd || (tap->has_vhostforce && tap->vhostforce)) { - int vhostfd; - if (tap->has_vhostfd) { - vhostfd = monitor_handle_fd_param(cur_mon, tap->vhostfd); - if (vhostfd == -1) { + for (i = 0; i < queues; i++) { + fd = net_tap_init(tap, &vnet_hdr, i >= 1 ? "no" : script, + ifname, sizeof ifname, queues > 1); + if (fd == -1) { return -1; } - } else { - vhostfd = -1; - } - s->vhost_net = vhost_net_init(&s->nc, vhostfd, - tap->has_vhostforce && tap->vhostforce); - if (!s->vhost_net) { - error_report("vhost-net requested but could not be initialized"); - return -1; + if (queues > 1 && i == 0 && !tap->has_ifname) { + if (tap_fd_get_ifname(fd, ifname)) { + error_report("Fail to get ifname"); + return -1; + } + } + + if (net_init_tap_one(tap, peer, "tap", name, ifname, + i >= 1 ? "no" : script, + i >= 1 ? "no" : downscript, + vhostfdname, vnet_hdr, fd)) { + return -1; + } } - } else if (tap->has_vhostfd) { - error_report("vhostfd= is not valid without vhost"); - return -1; } return 0; @@ -721,3 +825,38 @@ VHostNetState *tap_get_vhost_net(NetClientState *nc) assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP); return s->vhost_net; } + +int tap_enable(NetClientState *nc) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + int ret; + + if (s->enabled) { + return 0; + } else { + ret = tap_fd_enable(s->fd); + if (ret == 0) { + s->enabled = true; + tap_update_fd_handler(s); + } + return ret; + } +} + +int tap_disable(NetClientState *nc) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + int ret; + + if (s->enabled == 0) { + return 0; + } else { + ret = tap_fd_disable(s->fd); + if (ret == 0) { + qemu_purge_queued_packets(nc); + s->enabled = false; + tap_update_fd_handler(s); + } + return ret; + } +} diff --git a/net/tap_int.h b/net/tap_int.h index 1dffe12a45..86bb224bc8 100644 --- a/net/tap_int.h +++ b/net/tap_int.h @@ -32,7 +32,8 @@ #define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup" #define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/qemu-ifdown" -int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required); +int tap_open(char *ifname, int ifname_size, int *vnet_hdr, + int vnet_hdr_required, int mq_required); ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen); @@ -42,5 +43,8 @@ int tap_probe_vnet_hdr_len(int fd, int len); int tap_probe_has_ufo(int fd); void tap_fd_set_offload(int fd, int csum, int tso4, int tso6, int ecn, int ufo); void tap_fd_set_vnet_hdr_len(int fd, int len); +int tap_fd_enable(int fd); +int tap_fd_disable(int fd); +int tap_fd_get_ifname(int fd, char *ifname); #endif /* QEMU_TAP_H */ diff --git a/pc-bios/bios.bin b/pc-bios/bios.bin Binary files differindex 924bee30d5..ab5dd9db6a 100644 --- a/pc-bios/bios.bin +++ b/pc-bios/bios.bin diff --git a/qapi-schema.json b/qapi-schema.json index 6d7252b9e8..736f881b75 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -325,6 +325,73 @@ { 'command': 'query-chardev', 'returns': ['ChardevInfo'] } ## +# @DataFormat: +# +# An enumeration of data format. +# +# @utf8: Data is a UTF-8 string (RFC 3629) +# +# @base64: Data is Base64 encoded binary (RFC 3548) +# +# Since: 1.4 +## +{ 'enum': 'DataFormat' + 'data': [ 'utf8', 'base64' ] } + +## +# @ringbuf-write: +# +# Write to a ring buffer character device. +# +# @device: the ring buffer character device name +# +# @data: data to write +# +# @format: #optional data encoding (default 'utf8'). +# - base64: data must be base64 encoded text. Its binary +# decoding gets written. +# Bug: invalid base64 is currently not rejected. +# Whitespace *is* invalid. +# - utf8: data's UTF-8 encoding is written +# - data itself is always Unicode regardless of format, like +# any other string. +# +# Returns: Nothing on success +# +# Since: 1.4 +## +{ 'command': 'ringbuf-write', + 'data': {'device': 'str', 'data': 'str', + '*format': 'DataFormat'} } + +## +# @ringbuf-read: +# +# Read from a ring buffer character device. +# +# @device: the ring buffer character device name +# +# @size: how many bytes to read at most +# +# @format: #optional data encoding (default 'utf8'). +# - base64: the data read is returned in base64 encoding. +# - utf8: the data read is interpreted as UTF-8. +# Bug: can screw up when the buffer contains invalid UTF-8 +# sequences, NUL characters, after the ring buffer lost +# data, and when reading stops because the size limit is +# reached. +# - The return value is always Unicode regardless of format, +# like any other string. +# +# Returns: data read from the device +# +# Since: 1.4 +## +{ 'command': 'ringbuf-read', + 'data': {'device': 'str', 'size': 'int', '*format': 'DataFormat'}, + 'returns': 'str' } + +## # @CommandInfo: # # Information about a QMP command @@ -667,10 +734,12 @@ # # @count: number of dirty bytes according to the dirty bitmap # +# @granularity: granularity of the dirty bitmap in bytes (since 1.4) +# # Since: 1.3 ## { 'type': 'BlockDirtyInfo', - 'data': {'count': 'int'} } + 'data': {'count': 'int', 'granularity': 'int'} } ## # @BlockInfo: @@ -977,28 +1046,10 @@ # # @actual: the number of bytes the balloon currently contains # -# @mem_swapped_in: #optional number of pages swapped in within the guest -# -# @mem_swapped_out: #optional number of pages swapped out within the guest -# -# @major_page_faults: #optional number of major page faults within the guest -# -# @minor_page_faults: #optional number of minor page faults within the guest -# -# @free_mem: #optional amount of memory (in bytes) free in the guest -# -# @total_mem: #optional amount of memory (in bytes) visible to the guest -# # Since: 0.14.0 # -# Notes: all current versions of QEMU do not fill out optional information in -# this structure. ## -{ 'type': 'BalloonInfo', - 'data': {'actual': 'int', '*mem_swapped_in': 'int', - '*mem_swapped_out': 'int', '*major_page_faults': 'int', - '*minor_page_faults': 'int', '*free_mem': 'int', - '*total_mem': 'int'} } +{ 'type': 'BalloonInfo', 'data': {'actual': 'int' } } ## # @query-balloon: @@ -1634,6 +1685,14 @@ # (all the disk, only the sectors allocated in the topmost image, or # only new I/O). # +# @granularity: #optional granularity of the dirty bitmap, default is 64K +# if the image format doesn't have clusters, 4K if the clusters +# are smaller than that, else the cluster size. Must be a +# power of 2 between 512 and 64M (since 1.4). +# +# @buf-size: #optional maximum amount of data in flight from source to +# target (since 1.4). +# # @on-source-error: #optional the action to take on an error on the source, # default 'report'. 'stop' and 'enospc' can only be used # if the block device supports io-status (see BlockInfo). @@ -1650,7 +1709,8 @@ { 'command': 'drive-mirror', 'data': { 'device': 'str', 'target': 'str', '*format': 'str', 'sync': 'MirrorSyncMode', '*mode': 'NewImageMode', - '*speed': 'int', '*on-source-error': 'BlockdevOnError', + '*speed': 'int', '*granularity': 'uint32', + '*buf-size': 'int', '*on-source-error': 'BlockdevOnError', '*on-target-error': 'BlockdevOnError' } } ## @@ -2466,6 +2526,7 @@ 'data': { '*ifname': 'str', '*fd': 'str', + '*fds': 'str', '*script': 'str', '*downscript': 'str', '*helper': 'str', @@ -2473,7 +2534,9 @@ '*vnet_hdr': 'bool', '*vhost': 'bool', '*vhostfd': 'str', - '*vhostforce': 'bool' } } + '*vhostfds': 'str', + '*vhostforce': 'bool', + '*queues': 'uint32'} } ## # @NetdevSocketOptions diff --git a/qapi/qmp-registry.c b/qapi/qmp-registry.c index 70cdbca470..28bbbe849e 100644 --- a/qapi/qmp-registry.c +++ b/qapi/qmp-registry.c @@ -92,7 +92,7 @@ char **qmp_get_command_list(void) list_head = list = g_malloc0(count * sizeof(char *)); QTAILQ_FOREACH(cmd, &qmp_commands, node) { - *list = strdup(cmd->name); + *list = g_strdup(cmd->name); list++; } diff --git a/qemu-char.c b/qemu-char.c index 9ba0573c6a..a3ba02127f 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2643,6 +2643,191 @@ size_t qemu_chr_mem_osize(const CharDriverState *chr) return d->outbuf_size; } +/*********************************************************/ +/* Ring buffer chardev */ + +typedef struct { + size_t size; + size_t prod; + size_t cons; + uint8_t *cbuf; +} RingBufCharDriver; + +static size_t ringbuf_count(const CharDriverState *chr) +{ + const RingBufCharDriver *d = chr->opaque; + + return d->prod - d->cons; +} + +static int ringbuf_chr_write(CharDriverState *chr, const uint8_t *buf, int len) +{ + RingBufCharDriver *d = chr->opaque; + int i; + + if (!buf || (len < 0)) { + return -1; + } + + for (i = 0; i < len; i++ ) { + d->cbuf[d->prod++ & (d->size - 1)] = buf[i]; + if (d->prod - d->cons > d->size) { + d->cons = d->prod - d->size; + } + } + + return 0; +} + +static int ringbuf_chr_read(CharDriverState *chr, uint8_t *buf, int len) +{ + RingBufCharDriver *d = chr->opaque; + int i; + + for (i = 0; i < len && d->cons != d->prod; i++) { + buf[i] = d->cbuf[d->cons++ & (d->size - 1)]; + } + + return i; +} + +static void ringbuf_chr_close(struct CharDriverState *chr) +{ + RingBufCharDriver *d = chr->opaque; + + g_free(d->cbuf); + g_free(d); + chr->opaque = NULL; +} + +static CharDriverState *qemu_chr_open_ringbuf(QemuOpts *opts) +{ + CharDriverState *chr; + RingBufCharDriver *d; + + chr = g_malloc0(sizeof(CharDriverState)); + d = g_malloc(sizeof(*d)); + + d->size = qemu_opt_get_size(opts, "size", 0); + if (d->size == 0) { + d->size = 65536; + } + + /* The size must be power of 2 */ + if (d->size & (d->size - 1)) { + error_report("size of ringbuf device must be power of two"); + goto fail; + } + + d->prod = 0; + d->cons = 0; + d->cbuf = g_malloc0(d->size); + + chr->opaque = d; + chr->chr_write = ringbuf_chr_write; + chr->chr_close = ringbuf_chr_close; + + return chr; + +fail: + g_free(d); + g_free(chr); + return NULL; +} + +static bool chr_is_ringbuf(const CharDriverState *chr) +{ + return chr->chr_write == ringbuf_chr_write; +} + +void qmp_ringbuf_write(const char *device, const char *data, + bool has_format, enum DataFormat format, + Error **errp) +{ + CharDriverState *chr; + const uint8_t *write_data; + int ret; + size_t write_count; + + chr = qemu_chr_find(device); + if (!chr) { + error_setg(errp, "Device '%s' not found", device); + return; + } + + if (!chr_is_ringbuf(chr)) { + error_setg(errp,"%s is not a ringbuf device", device); + return; + } + + if (has_format && (format == DATA_FORMAT_BASE64)) { + write_data = g_base64_decode(data, &write_count); + } else { + write_data = (uint8_t *)data; + write_count = strlen(data); + } + + ret = ringbuf_chr_write(chr, write_data, write_count); + + if (write_data != (uint8_t *)data) { + g_free((void *)write_data); + } + + if (ret < 0) { + error_setg(errp, "Failed to write to device %s", device); + return; + } +} + +char *qmp_ringbuf_read(const char *device, int64_t size, + bool has_format, enum DataFormat format, + Error **errp) +{ + CharDriverState *chr; + uint8_t *read_data; + size_t count; + char *data; + + chr = qemu_chr_find(device); + if (!chr) { + error_setg(errp, "Device '%s' not found", device); + return NULL; + } + + if (!chr_is_ringbuf(chr)) { + error_setg(errp,"%s is not a ringbuf device", device); + return NULL; + } + + if (size <= 0) { + error_setg(errp, "size must be greater than zero"); + return NULL; + } + + count = ringbuf_count(chr); + size = size > count ? count : size; + read_data = g_malloc(size + 1); + + ringbuf_chr_read(chr, read_data, size); + + if (has_format && (format == DATA_FORMAT_BASE64)) { + data = g_base64_encode(read_data, size); + g_free(read_data); + } else { + /* + * FIXME should read only complete, valid UTF-8 characters up + * to @size bytes. Invalid sequences should be replaced by a + * suitable replacement character. Except when (and only + * when) ring buffer lost characters since last read, initial + * continuation characters should be dropped. + */ + read_data[size] = 0; + data = (char *)read_data; + } + + return data; +} + QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) { char host[65], port[33], width[8], height[8]; @@ -2796,6 +2981,7 @@ static const struct { { .name = "udp", .open = qemu_chr_open_udp }, { .name = "msmouse", .open = qemu_chr_open_msmouse }, { .name = "vc", .open = text_console_init }, + { .name = "memory", .open = qemu_chr_open_ringbuf }, #ifdef _WIN32 { .name = "file", .open = qemu_chr_open_win_file_out }, { .name = "pipe", .open = qemu_chr_open_win_pipe }, @@ -3055,6 +3241,9 @@ QemuOptsList qemu_chardev_opts = { },{ .name = "debug", .type = QEMU_OPT_NUMBER, + },{ + .name = "size", + .type = QEMU_OPT_SIZE, }, { /* end of list */ } }, @@ -3129,11 +3318,11 @@ static CharDriverState *qmp_chardev_open_file(ChardevFile *file, Error **errp) static CharDriverState *qmp_chardev_open_port(ChardevPort *port, Error **errp) { - int flags, fd; - switch (port->type) { #ifdef HAVE_CHARDEV_TTY case CHARDEV_PORT_KIND_SERIAL: + { + int flags, fd; flags = O_RDWR; fd = qmp_chardev_open_file_source(port->device, flags, errp); if (error_is_set(errp)) { @@ -3141,15 +3330,19 @@ static CharDriverState *qmp_chardev_open_port(ChardevPort *port, Error **errp) } socket_set_nonblock(fd); return qemu_chr_open_tty_fd(fd); + } #endif #ifdef HAVE_CHARDEV_PARPORT case CHARDEV_PORT_KIND_PARALLEL: + { + int flags, fd; flags = O_RDWR; fd = qmp_chardev_open_file_source(port->device, flags, errp); if (error_is_set(errp)) { return NULL; } return qemu_chr_open_pp_fd(fd); + } #endif default: error_setg(errp, "unknown chardev port (%d)", port->type); diff --git a/qemu-log.c b/qemu-log.c index b655b305ea..30c9ab01bd 100644 --- a/qemu-log.c +++ b/qemu-log.c @@ -21,10 +21,12 @@ #include "qemu/log.h" #ifdef WIN32 -static const char *logfilename = "qemu.log"; +#define DEFAULT_LOGFILENAME "qemu.log" #else -static const char *logfilename = "/tmp/qemu.log"; +#define DEFAULT_LOGFILENAME "/tmp/qemu.log" #endif + +static char *logfilename; FILE *qemu_logfile; int qemu_loglevel; static int log_append = 0; @@ -54,11 +56,13 @@ void qemu_log_mask(int mask, const char *fmt, ...) /* enable or disable low levels log */ void qemu_set_log(int log_flags, bool use_own_buffers) { + const char *fname = logfilename ?: DEFAULT_LOGFILENAME; + qemu_loglevel = log_flags; if (qemu_loglevel && !qemu_logfile) { - qemu_logfile = fopen(logfilename, log_append ? "a" : "w"); + qemu_logfile = fopen(fname, log_append ? "a" : "w"); if (!qemu_logfile) { - perror(logfilename); + perror(fname); _exit(1); } /* must avoid mmap() usage of glibc by setting a buffer "by hand" */ @@ -84,7 +88,8 @@ void qemu_set_log(int log_flags, bool use_own_buffers) void cpu_set_log_filename(const char *filename) { - logfilename = strdup(filename); + g_free(logfilename); + logfilename = g_strdup(filename); if (qemu_logfile) { fclose(qemu_logfile); qemu_logfile = NULL; diff --git a/qemu-options.hx b/qemu-options.hx index 4e2b4994a2..046bdc0f63 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1736,6 +1736,7 @@ DEF("chardev", HAS_ARG, QEMU_OPTION_chardev, "-chardev msmouse,id=id[,mux=on|off]\n" "-chardev vc,id=id[[,width=width][,height=height]][[,cols=cols][,rows=rows]]\n" " [,mux=on|off]\n" + "-chardev ringbuf,id=id[,size=size]\n" "-chardev file,id=id,path=path[,mux=on|off]\n" "-chardev pipe,id=id,path=path[,mux=on|off]\n" #ifdef _WIN32 @@ -1777,6 +1778,7 @@ Backend is one of: @option{udp}, @option{msmouse}, @option{vc}, +@option{ringbuf}, @option{file}, @option{pipe}, @option{console}, @@ -1885,6 +1887,11 @@ the console, in pixels. @option{cols} and @option{rows} specify that the console be sized to fit a text console with the given dimensions. +@item -chardev ringbuf ,id=@var{id} [,size=@var{size}] + +Create a ring buffer with fixed size @option{size}. +@var{size} must be a power of two, and defaults to @code{64K}). + @item -chardev file ,id=@var{id} ,path=@var{path} Log all traffic received from the guest to a file. diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 0ad73f3430..7a0202eb2a 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -611,13 +611,14 @@ int64_t qmp_guest_fsfreeze_thaw(Error **err) static void guest_fsfreeze_cleanup(void) { - int64_t ret; Error *err = NULL; if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) { - ret = qmp_guest_fsfreeze_thaw(&err); - if (ret < 0 || err) { - slog("failed to clean up frozen filesystems"); + qmp_guest_fsfreeze_thaw(&err); + if (err) { + slog("failed to clean up frozen filesystems: %s", + error_get_pretty(err)); + error_free(err); } } } @@ -934,9 +935,11 @@ GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) error_setg_errno(errp, errno, "failed to get MAC address of %s", ifa->ifa_name); + close(sock); goto error; } + close(sock); mac_addr = (unsigned char *) &ifr.ifr_hwaddr.sa_data; info->value->hardware_address = @@ -946,20 +949,19 @@ GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) (int) mac_addr[4], (int) mac_addr[5]); info->value->has_hardware_address = true; - close(sock); } if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET) { /* interface with IPv4 address */ - address_item = g_malloc0(sizeof(*address_item)); - address_item->value = g_malloc0(sizeof(*address_item->value)); p = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; if (!inet_ntop(AF_INET, p, addr4, sizeof(addr4))) { error_setg_errno(errp, errno, "inet_ntop failed"); goto error; } + address_item = g_malloc0(sizeof(*address_item)); + address_item->value = g_malloc0(sizeof(*address_item->value)); address_item->value->ip_address = g_strdup(addr4); address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV4; @@ -972,14 +974,14 @@ GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) } else if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET6) { /* interface with IPv6 address */ - address_item = g_malloc0(sizeof(*address_item)); - address_item->value = g_malloc0(sizeof(*address_item->value)); p = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; if (!inet_ntop(AF_INET6, p, addr6, sizeof(addr6))) { error_setg_errno(errp, errno, "inet_ntop failed"); goto error; } + address_item = g_malloc0(sizeof(*address_item)); + address_item->value = g_malloc0(sizeof(*address_item->value)); address_item->value->ip_address = g_strdup(addr6); address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV6; diff --git a/qga/commands.c b/qga/commands.c index 7ffb35e4af..528b082fa8 100644 --- a/qga/commands.c +++ b/qga/commands.c @@ -61,7 +61,7 @@ struct GuestAgentInfo *qmp_guest_info(Error **err) while (*cmd_list) { cmd_info = g_malloc0(sizeof(GuestAgentCommandInfo)); - cmd_info->name = strdup(*cmd_list); + cmd_info->name = g_strdup(*cmd_list); cmd_info->enabled = qmp_command_is_enabled(cmd_info->name); cmd_info_list = g_malloc0(sizeof(GuestAgentCommandInfoList)); diff --git a/qmp-commands.hx b/qmp-commands.hx index cbf12804be..799adea1b7 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -466,6 +466,73 @@ Note: inject-nmi fails when the guest doesn't support injecting. EQMP { + .name = "ringbuf-write", + .args_type = "device:s,data:s,format:s?", + .mhandler.cmd_new = qmp_marshal_input_ringbuf_write, + }, + +SQMP +ringbuf-write +------------- + +Write to a ring buffer character device. + +Arguments: + +- "device": ring buffer character device name (json-string) +- "data": data to write (json-string) +- "format": data format (json-string, optional) + - Possible values: "utf8" (default), "base64" + Bug: invalid base64 is currently not rejected. + Whitespace *is* invalid. + +Example: + +-> { "execute": "ringbuf-write", + "arguments": { "device": "foo", + "data": "abcdefgh", + "format": "utf8" } } +<- { "return": {} } + +EQMP + + { + .name = "ringbuf-read", + .args_type = "device:s,size:i,format:s?", + .mhandler.cmd_new = qmp_marshal_input_ringbuf_read, + }, + +SQMP +ringbuf-read +------------- + +Read from a ring buffer character device. + +Arguments: + +- "device": ring buffer character device name (json-string) +- "size": how many bytes to read at most (json-int) + - Number of data bytes, not number of characters in encoded data +- "format": data format (json-string, optional) + - Possible values: "utf8" (default), "base64" + - Naturally, format "utf8" works only when the ring buffer + contains valid UTF-8 text. Invalid UTF-8 sequences get + replaced. Bug: replacement doesn't work. Bug: can screw + up on encountering NUL characters, after the ring buffer + lost data, and when reading stops because the size limit + is reached. + +Example: + +-> { "execute": "ringbuf-read", + "arguments": { "device": "foo", + "size": 1000, + "format": "utf8" } } +<- {"return": "abcdefgh"} + +EQMP + + { .name = "xen-save-devices-state", .args_type = "filename:F", .mhandler.cmd_new = qmp_marshal_input_xen_save_devices_state, @@ -938,7 +1005,8 @@ EQMP { .name = "drive-mirror", .args_type = "sync:s,device:B,target:s,speed:i?,mode:s?,format:s?," - "on-source-error:s?,on-target-error:s?", + "on-source-error:s?,on-target-error:s?," + "granularity:i?,buf-size:i?", .mhandler.cmd_new = qmp_marshal_input_drive_mirror, }, @@ -962,6 +1030,9 @@ Arguments: file/device (NewImageMode, optional, default 'absolute-paths') - "speed": maximum speed of the streaming job, in bytes per second (json-int) +- "granularity": granularity of the dirty bitmap, in bytes (json-int, optional) +- "buf_size": maximum amount of data in flight from source to target, in bytes + (json-int, default 10M) - "sync": what parts of the disk image should be copied to the destination; possibilities include "full" for all the disk, "top" for only the sectors allocated in the topmost image, or "none" to only replicate new I/O @@ -971,6 +1042,10 @@ Arguments: - "on-target-error": the action to take on an error on the target (BlockdevOnError, default 'report') +The default value of the granularity is the image cluster size clamped +between 4096 and 65536, if the image format defines one. If the format +does not define a cluster size, the default value of the granularity +is 65536. Example: @@ -1585,7 +1660,7 @@ Each json-object contain the following: - Possible values: "unknown" - "removable": true if the device is removable, false otherwise (json-bool) - "locked": true if the device is locked, false otherwise (json-bool) -- "tray-open": only present if removable, true if the device has a tray, +- "tray_open": only present if removable, true if the device has a tray, and it is open (json-bool) - "inserted": only present if the device is inserted, it is a json-object containing the following: @@ -2527,10 +2602,8 @@ Arguments: Example: -> { "execute": "query-migrate-capabilities" } -<- { "return": { - "capabilities" : [ { "capability" : "xbzrle", "state" : false } ] - } - } +<- { "return": [ { "state": false, "capability": "xbzrle" } ] } + EQMP { @@ -2549,13 +2622,6 @@ Make an asynchronous request for balloon info. When the request completes a json-object will be returned containing the following data: - "actual": current balloon value in bytes (json-int) -- "mem_swapped_in": Amount of memory swapped in bytes (json-int, optional) -- "mem_swapped_out": Amount of memory swapped out in bytes (json-int, optional) -- "major_page_faults": Number of major faults (json-int, optional) -- "minor_page_faults": Number of minor faults (json-int, optional) -- "free_mem": Total amount of free and unused memory in - bytes (json-int, optional) -- "total_mem": Total amount of available memory in bytes (json-int, optional) Example: @@ -2563,12 +2629,6 @@ Example: <- { "return":{ "actual":1073741824, - "mem_swapped_in":0, - "mem_swapped_out":0, - "major_page_faults":142, - "minor_page_faults":239245, - "free_mem":1014185984, - "total_mem":1044668416 } } diff --git a/qom/Makefile.objs b/qom/Makefile.objs index 1899a4ce42..6a93ac7398 100644 --- a/qom/Makefile.objs +++ b/qom/Makefile.objs @@ -1,2 +1,2 @@ -universal-obj-y = object.o container.o qom-qobject.o -universal-obj-y += cpu.o +common-obj-y = object.o container.o qom-qobject.o +common-obj-y += cpu.o @@ -34,11 +34,24 @@ static void cpu_common_reset(CPUState *cpu) { } +ObjectClass *cpu_class_by_name(const char *typename, const char *cpu_model) +{ + CPUClass *cc = CPU_CLASS(object_class_by_name(typename)); + + return cc->class_by_name(cpu_model); +} + +static ObjectClass *cpu_common_class_by_name(const char *cpu_model) +{ + return NULL; +} + static void cpu_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); CPUClass *k = CPU_CLASS(klass); + k->class_by_name = cpu_common_class_by_name; k->reset = cpu_common_reset; dc->no_user = 1; } diff --git a/qom/object.c b/qom/object.c index 03e6f24d28..563e45b0cc 100644 --- a/qom/object.c +++ b/qom/object.c @@ -361,12 +361,14 @@ static void object_property_del_child(Object *obj, Object *child, Error **errp) void object_unparent(Object *obj) { + object_ref(obj); if (obj->parent) { object_property_del_child(obj->parent, obj, NULL); } if (obj->class->unparent) { (obj->class->unparent)(obj); } + object_unref(obj); } static void object_deinit(Object *obj, TypeImpl *type) @@ -415,13 +417,6 @@ Object *object_new(const char *typename) return object_new_with_type(ti); } -void object_delete(Object *obj) -{ - object_unparent(obj); - g_assert(obj->ref == 1); - object_unref(obj); -} - Object *object_dynamic_cast(Object *obj, const char *typename) { if (obj && object_class_dynamic_cast(object_get_class(obj), typename)) { @@ -501,6 +496,11 @@ ObjectClass *object_get_class(Object *obj) return obj->class; } +bool object_class_is_abstract(ObjectClass *klass) +{ + return klass->type->abstract; +} + const char *object_class_get_name(ObjectClass *klass) { return klass->type->name; @@ -24,7 +24,7 @@ const char *qtest_chrdev; const char *qtest_log; -int qtest_allowed = 0; +bool qtest_allowed; static DeviceState *irq_intercept_dev; static FILE *qtest_log_fp; diff --git a/readline.c b/readline.c index a0c9638e4d..d6e04d4796 100644 --- a/readline.c +++ b/readline.c @@ -247,14 +247,14 @@ static void readline_hist_add(ReadLineState *rs, const char *cmdline) } if (idx == READLINE_MAX_CMDS) { /* Need to get one free slot */ - free(rs->history[0]); + g_free(rs->history[0]); memmove(rs->history, &rs->history[1], (READLINE_MAX_CMDS - 1) * sizeof(char *)); rs->history[READLINE_MAX_CMDS - 1] = NULL; idx = READLINE_MAX_CMDS - 1; } if (new_entry == NULL) - new_entry = strdup(cmdline); + new_entry = g_strdup(cmdline); rs->history[idx] = new_entry; rs->hist_entry = -1; } @@ -82,12 +82,16 @@ TRACETOOL=$(PYTHON) $(SRC_PATH)/scripts/tracetool.py # Generate timestamp files for .h include files -%.h: %.h-timestamp - @test -f $@ || cp $< $@ +config-%.h: config-%.h-timestamp + @cmp $< $@ >/dev/null 2>&1 || cp $< $@ -%.h-timestamp: %.mak - $(call quiet-command, sh $(SRC_PATH)/scripts/create_config < $< > $@, " GEN $(TARGET_DIR)$*.h") - @cmp $@ $*.h >/dev/null 2>&1 || cp $@ $*.h +config-%.h-timestamp: config-%.mak + $(call quiet-command, sh $(SRC_PATH)/scripts/create_config < $< > $@, " GEN $(TARGET_DIR)config-$*.h") + +.PHONY: clean-timestamp +clean-timestamp: + rm -f *.timestamp +clean: clean-timestamp # will delete the target of a rule if commands exit with a nonzero exit status .DELETE_ON_ERROR: @@ -81,7 +81,7 @@ static void qemu_announce_self_iter(NICState *nic, void *opaque) len = announce_self_create(buf, nic->conf->macaddr.a); - qemu_send_packet_raw(&nic->nc, buf, len); + qemu_send_packet_raw(qemu_get_queue(nic), buf, len); } @@ -2388,162 +2388,3 @@ void vmstate_register_ram_global(MemoryRegion *mr) { vmstate_register_ram(mr, NULL); } - -/* - page = zrun nzrun - | zrun nzrun page - - zrun = length - - nzrun = length byte... - - length = uleb128 encoded integer - */ -int xbzrle_encode_buffer(uint8_t *old_buf, uint8_t *new_buf, int slen, - uint8_t *dst, int dlen) -{ - uint32_t zrun_len = 0, nzrun_len = 0; - int d = 0, i = 0; - long res, xor; - uint8_t *nzrun_start = NULL; - - g_assert(!(((uintptr_t)old_buf | (uintptr_t)new_buf | slen) % - sizeof(long))); - - while (i < slen) { - /* overflow */ - if (d + 2 > dlen) { - return -1; - } - - /* not aligned to sizeof(long) */ - res = (slen - i) % sizeof(long); - while (res && old_buf[i] == new_buf[i]) { - zrun_len++; - i++; - res--; - } - - /* word at a time for speed */ - if (!res) { - while (i < slen && - (*(long *)(old_buf + i)) == (*(long *)(new_buf + i))) { - i += sizeof(long); - zrun_len += sizeof(long); - } - - /* go over the rest */ - while (i < slen && old_buf[i] == new_buf[i]) { - zrun_len++; - i++; - } - } - - /* buffer unchanged */ - if (zrun_len == slen) { - return 0; - } - - /* skip last zero run */ - if (i == slen) { - return d; - } - - d += uleb128_encode_small(dst + d, zrun_len); - - zrun_len = 0; - nzrun_start = new_buf + i; - - /* overflow */ - if (d + 2 > dlen) { - return -1; - } - /* not aligned to sizeof(long) */ - res = (slen - i) % sizeof(long); - while (res && old_buf[i] != new_buf[i]) { - i++; - nzrun_len++; - res--; - } - - /* word at a time for speed, use of 32-bit long okay */ - if (!res) { - /* truncation to 32-bit long okay */ - long mask = (long)0x0101010101010101ULL; - while (i < slen) { - xor = *(long *)(old_buf + i) ^ *(long *)(new_buf + i); - if ((xor - mask) & ~xor & (mask << 7)) { - /* found the end of an nzrun within the current long */ - while (old_buf[i] != new_buf[i]) { - nzrun_len++; - i++; - } - break; - } else { - i += sizeof(long); - nzrun_len += sizeof(long); - } - } - } - - d += uleb128_encode_small(dst + d, nzrun_len); - /* overflow */ - if (d + nzrun_len > dlen) { - return -1; - } - memcpy(dst + d, nzrun_start, nzrun_len); - d += nzrun_len; - nzrun_len = 0; - } - - return d; -} - -int xbzrle_decode_buffer(uint8_t *src, int slen, uint8_t *dst, int dlen) -{ - int i = 0, d = 0; - int ret; - uint32_t count = 0; - - while (i < slen) { - - /* zrun */ - if ((slen - i) < 2) { - return -1; - } - - ret = uleb128_decode_small(src + i, &count); - if (ret < 0 || (i && !count)) { - return -1; - } - i += ret; - d += count; - - /* overflow */ - if (d > dlen) { - return -1; - } - - /* nzrun */ - if ((slen - i) < 2) { - return -1; - } - - ret = uleb128_decode_small(src + i, &count); - if (ret < 0 || !count) { - return -1; - } - i += ret; - - /* overflow */ - if (d + count > dlen || i + count > slen) { - return -1; - } - - memcpy(dst + d, src + i, count); - d += count; - i += count; - } - - return d; -} diff --git a/scripts/kvm/vmxcap b/scripts/kvm/vmxcap index cbe6440ba3..0b23f7795a 100755 --- a/scripts/kvm/vmxcap +++ b/scripts/kvm/vmxcap @@ -147,6 +147,7 @@ controls = [ 5: 'Enable VPID', 6: 'WBINVD exiting', 7: 'Unrestricted guest', + 9: 'Virtual interrupt delivery', 10: 'PAUSE-loop exiting', 11: 'RDRAND exiting', 12: 'Enable INVPCID', diff --git a/scripts/make_device_config.sh b/scripts/make_device_config.sh index 5d14885dfc..0778fe2a42 100644 --- a/scripts/make_device_config.sh +++ b/scripts/make_device_config.sh @@ -25,4 +25,4 @@ done process_includes $src > $dest cat $src $all_includes | grep -v '^include' > $dest -echo "$1: $all_includes" > $dep +echo "`basename $1`: $all_includes" > $dep diff --git a/slirp/slirp.c b/slirp/slirp.c index e93b578832..0e6e232789 100644 --- a/slirp/slirp.c +++ b/slirp/slirp.c @@ -225,12 +225,8 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork, pstrcpy(slirp->client_hostname, sizeof(slirp->client_hostname), vhostname); } - if (tftp_path) { - slirp->tftp_prefix = g_strdup(tftp_path); - } - if (bootfile) { - slirp->bootp_filename = g_strdup(bootfile); - } + slirp->tftp_prefix = g_strdup(tftp_path); + slirp->bootp_filename = g_strdup(bootfile); slirp->vdhcp_startaddr = vdhcp_start; slirp->vnameserver_addr = vnameserver; diff --git a/target-alpha/cpu.c b/target-alpha/cpu.c index 40e980933f..0ad69f0859 100644 --- a/target-alpha/cpu.c +++ b/target-alpha/cpu.c @@ -96,14 +96,15 @@ static ObjectClass *alpha_cpu_class_by_name(const char *cpu_model) } oc = object_class_by_name(cpu_model); - if (oc != NULL) { + if (oc != NULL && object_class_dynamic_cast(oc, TYPE_ALPHA_CPU) != NULL && + !object_class_is_abstract(oc)) { return oc; } for (i = 0; i < ARRAY_SIZE(alpha_cpu_aliases); i++) { if (strcmp(cpu_model, alpha_cpu_aliases[i].alias) == 0) { oc = object_class_by_name(alpha_cpu_aliases[i].typename); - assert(oc != NULL); + assert(oc != NULL && !object_class_is_abstract(oc)); return oc; } } @@ -111,6 +112,9 @@ static ObjectClass *alpha_cpu_class_by_name(const char *cpu_model) typename = g_strdup_printf("%s-" TYPE_ALPHA_CPU, cpu_model); oc = object_class_by_name(typename); g_free(typename); + if (oc != NULL && object_class_is_abstract(oc)) { + oc = NULL; + } return oc; } @@ -244,6 +248,13 @@ static void alpha_cpu_initfn(Object *obj) env->fen = 1; } +static void alpha_cpu_class_init(ObjectClass *oc, void *data) +{ + CPUClass *cc = CPU_CLASS(oc); + + cc->class_by_name = alpha_cpu_class_by_name; +} + static const TypeInfo alpha_cpu_type_info = { .name = TYPE_ALPHA_CPU, .parent = TYPE_CPU, @@ -251,6 +262,7 @@ static const TypeInfo alpha_cpu_type_info = { .instance_init = alpha_cpu_initfn, .abstract = true, .class_size = sizeof(AlphaCPUClass), + .class_init = alpha_cpu_class_init, }; static void alpha_cpu_register_types(void) diff --git a/target-arm/cpu.c b/target-arm/cpu.c index 07588a13b2..1c6a628df4 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -201,6 +201,25 @@ void arm_cpu_realize(ARMCPU *cpu) /* CPU models */ +static ObjectClass *arm_cpu_class_by_name(const char *cpu_model) +{ + ObjectClass *oc; + char *typename; + + if (!cpu_model) { + return NULL; + } + + typename = g_strdup_printf("%s-" TYPE_ARM_CPU, cpu_model); + oc = object_class_by_name(typename); + g_free(typename); + if (!oc || !object_class_dynamic_cast(oc, TYPE_ARM_CPU) || + object_class_is_abstract(oc)) { + return NULL; + } + return oc; +} + static void arm926_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -766,19 +785,22 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data) acc->parent_reset = cc->reset; cc->reset = arm_cpu_reset; + + cc->class_by_name = arm_cpu_class_by_name; } static void cpu_register(const ARMCPUInfo *info) { TypeInfo type_info = { - .name = info->name, .parent = TYPE_ARM_CPU, .instance_size = sizeof(ARMCPU), .instance_init = info->initfn, .class_size = sizeof(ARMCPUClass), }; + type_info.name = g_strdup_printf("%s-" TYPE_ARM_CPU, info->name); type_register(&type_info); + g_free((void *)type_info.name); } static const TypeInfo arm_cpu_type_info = { diff --git a/target-arm/helper.c b/target-arm/helper.c index 37c34a11c4..eb7b2910c3 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -1262,12 +1262,14 @@ ARMCPU *cpu_arm_init(const char *cpu_model) { ARMCPU *cpu; CPUARMState *env; + ObjectClass *oc; static int inited = 0; - if (!object_class_by_name(cpu_model)) { + oc = cpu_class_by_name(TYPE_ARM_CPU, cpu_model); + if (!oc) { return NULL; } - cpu = ARM_CPU(object_new(cpu_model)); + cpu = ARM_CPU(object_new(object_class_get_name(oc))); env = &cpu->env; env->cpu_model_str = cpu_model; arm_cpu_realize(cpu); @@ -1301,9 +1303,9 @@ static gint arm_cpu_list_compare(gconstpointer a, gconstpointer b) name_a = object_class_get_name(class_a); name_b = object_class_get_name(class_b); - if (strcmp(name_a, "any") == 0) { + if (strcmp(name_a, "any-" TYPE_ARM_CPU) == 0) { return 1; - } else if (strcmp(name_b, "any") == 0) { + } else if (strcmp(name_b, "any-" TYPE_ARM_CPU) == 0) { return -1; } else { return strcmp(name_a, name_b); @@ -1314,9 +1316,14 @@ static void arm_cpu_list_entry(gpointer data, gpointer user_data) { ObjectClass *oc = data; CPUListState *s = user_data; + const char *typename; + char *name; + typename = object_class_get_name(oc); + name = g_strndup(typename, strlen(typename) - strlen("-" TYPE_ARM_CPU)); (*s->cpu_fprintf)(s->file, " %s\n", - object_class_get_name(oc)); + name); + g_free(name); } void arm_cpu_list(FILE *f, fprintf_function cpu_fprintf) diff --git a/target-arm/translate.c b/target-arm/translate.c index 724e00f7cf..a8893f767f 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -2737,7 +2737,6 @@ static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn) } } else { /* arm->vfp */ - tmp = load_reg(s, rd); if (insn & (1 << 21)) { rn >>= 1; /* system register */ @@ -2748,6 +2747,7 @@ static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn) /* Writes are ignored. */ break; case ARM_VFP_FPSCR: + tmp = load_reg(s, rd); gen_helper_vfp_set_fpscr(cpu_env, tmp); tcg_temp_free_i32(tmp); gen_lookup_tb(s); @@ -2757,18 +2757,21 @@ static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn) return 1; /* TODO: VFP subarchitecture support. * For now, keep the EN bit only */ + tmp = load_reg(s, rd); tcg_gen_andi_i32(tmp, tmp, 1 << 30); store_cpu_field(tmp, vfp.xregs[rn]); gen_lookup_tb(s); break; case ARM_VFP_FPINST: case ARM_VFP_FPINST2: + tmp = load_reg(s, rd); store_cpu_field(tmp, vfp.xregs[rn]); break; default: return 1; } } else { + tmp = load_reg(s, rd); gen_vfp_msr(tmp); gen_mov_vreg_F0(0, rn); } diff --git a/target-cris/op_helper.c b/target-cris/op_helper.c index 0f6a1eeb0a..b580513848 100644 --- a/target-cris/op_helper.c +++ b/target-cris/op_helper.c @@ -60,7 +60,7 @@ void tlb_fill(CPUCRISState *env, target_ulong addr, int is_write, int mmu_idx, int ret; D_LOG("%s pc=%x tpc=%x ra=%p\n", __func__, - env->pc, env->debug1, (void *)retaddr); + env->pc, env->pregs[PR_EDA], (void *)retaddr); ret = cpu_cris_handle_mmu_fault(env, addr, is_write, mmu_idx); if (unlikely(ret)) { if (retaddr) { diff --git a/target-i386/cpu.c b/target-i386/cpu.c index 376d4c8737..5c108e17ab 100644 --- a/target-i386/cpu.c +++ b/target-i386/cpu.c @@ -23,6 +23,8 @@ #include "cpu.h" #include "sysemu/kvm.h" +#include "sysemu/cpus.h" +#include "topology.h" #include "qemu/option.h" #include "qemu/config-file.h" @@ -45,6 +47,18 @@ #include "hw/apic_internal.h" #endif +static void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, + uint32_t vendor2, uint32_t vendor3) +{ + int i; + for (i = 0; i < 4; i++) { + dst[i] = vendor1 >> (8 * i); + dst[i + 4] = vendor2 >> (8 * i); + dst[i + 8] = vendor3 >> (8 * i); + } + dst[CPUID_VENDOR_SZ] = '\0'; +} + /* feature flags taken from "Intel Processor Identification and the CPUID * Instruction" and AMD's "CPUID Specification". In cases of disagreement * between feature naming conventions, aliases may be added. @@ -206,22 +220,17 @@ typedef struct model_features_t { int check_cpuid = 0; int enforce_cpuid = 0; -#if defined(CONFIG_KVM) static uint32_t kvm_default_features = (1 << KVM_FEATURE_CLOCKSOURCE) | (1 << KVM_FEATURE_NOP_IO_DELAY) | (1 << KVM_FEATURE_CLOCKSOURCE2) | (1 << KVM_FEATURE_ASYNC_PF) | (1 << KVM_FEATURE_STEAL_TIME) | + (1 << KVM_FEATURE_PV_EOI) | (1 << KVM_FEATURE_CLOCKSOURCE_STABLE_BIT); -static const uint32_t kvm_pv_eoi_features = (0x1 << KVM_FEATURE_PV_EOI); -#else -static uint32_t kvm_default_features = 0; -static const uint32_t kvm_pv_eoi_features = 0; -#endif -void enable_kvm_pv_eoi(void) +void disable_kvm_pv_eoi(void) { - kvm_default_features |= kvm_pv_eoi_features; + kvm_default_features &= ~(1UL << KVM_FEATURE_PV_EOI); } void host_cpuid(uint32_t function, uint32_t count, @@ -338,19 +347,17 @@ static void add_flagname_to_bitmaps(const char *flagname, } typedef struct x86_def_t { - struct x86_def_t *next; const char *name; uint32_t level; - uint32_t vendor1, vendor2, vendor3; + /* vendor is zero-terminated, 12 character ASCII string */ + char vendor[CPUID_VENDOR_SZ + 1]; int family; int model; int stepping; - int tsc_khz; uint32_t features, ext_features, ext2_features, ext3_features; uint32_t kvm_features, svm_features; uint32_t xlevel; char model_id[48]; - int vendor_override; /* Store the results of Centaur's CPUID instructions */ uint32_t ext4_features; uint32_t xlevel2; @@ -396,19 +403,13 @@ typedef struct x86_def_t { #define TCG_SVM_FEATURES 0 #define TCG_7_0_EBX_FEATURES (CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_SMAP) -/* maintains list of cpu model definitions - */ -static x86_def_t *x86_defs = {NULL}; - -/* built-in cpu model definitions (deprecated) +/* built-in CPU model definitions */ static x86_def_t builtin_x86_defs[] = { { .name = "qemu64", .level = 4, - .vendor1 = CPUID_VENDOR_AMD_1, - .vendor2 = CPUID_VENDOR_AMD_2, - .vendor3 = CPUID_VENDOR_AMD_3, + .vendor = CPUID_VENDOR_AMD, .family = 6, .model = 2, .stepping = 3, @@ -425,9 +426,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "phenom", .level = 5, - .vendor1 = CPUID_VENDOR_AMD_1, - .vendor2 = CPUID_VENDOR_AMD_2, - .vendor3 = CPUID_VENDOR_AMD_3, + .vendor = CPUID_VENDOR_AMD, .family = 16, .model = 2, .stepping = 3, @@ -453,9 +452,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "core2duo", .level = 10, - .vendor1 = CPUID_VENDOR_INTEL_1, - .vendor2 = CPUID_VENDOR_INTEL_2, - .vendor3 = CPUID_VENDOR_INTEL_3, + .vendor = CPUID_VENDOR_INTEL, .family = 6, .model = 15, .stepping = 11, @@ -474,9 +471,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "kvm64", .level = 5, - .vendor1 = CPUID_VENDOR_INTEL_1, - .vendor2 = CPUID_VENDOR_INTEL_2, - .vendor3 = CPUID_VENDOR_INTEL_3, + .vendor = CPUID_VENDOR_INTEL, .family = 15, .model = 6, .stepping = 1, @@ -500,9 +495,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "qemu32", .level = 4, - .vendor1 = CPUID_VENDOR_INTEL_1, - .vendor2 = CPUID_VENDOR_INTEL_2, - .vendor3 = CPUID_VENDOR_INTEL_3, + .vendor = CPUID_VENDOR_INTEL, .family = 6, .model = 3, .stepping = 3, @@ -513,9 +506,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "kvm32", .level = 5, - .vendor1 = CPUID_VENDOR_INTEL_1, - .vendor2 = CPUID_VENDOR_INTEL_2, - .vendor3 = CPUID_VENDOR_INTEL_3, + .vendor = CPUID_VENDOR_INTEL, .family = 15, .model = 6, .stepping = 1, @@ -530,9 +521,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "coreduo", .level = 10, - .vendor1 = CPUID_VENDOR_INTEL_1, - .vendor2 = CPUID_VENDOR_INTEL_2, - .vendor3 = CPUID_VENDOR_INTEL_3, + .vendor = CPUID_VENDOR_INTEL, .family = 6, .model = 14, .stepping = 8, @@ -548,9 +537,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "486", .level = 1, - .vendor1 = CPUID_VENDOR_INTEL_1, - .vendor2 = CPUID_VENDOR_INTEL_2, - .vendor3 = CPUID_VENDOR_INTEL_3, + .vendor = CPUID_VENDOR_INTEL, .family = 4, .model = 0, .stepping = 0, @@ -560,9 +547,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "pentium", .level = 1, - .vendor1 = CPUID_VENDOR_INTEL_1, - .vendor2 = CPUID_VENDOR_INTEL_2, - .vendor3 = CPUID_VENDOR_INTEL_3, + .vendor = CPUID_VENDOR_INTEL, .family = 5, .model = 4, .stepping = 3, @@ -572,9 +557,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "pentium2", .level = 2, - .vendor1 = CPUID_VENDOR_INTEL_1, - .vendor2 = CPUID_VENDOR_INTEL_2, - .vendor3 = CPUID_VENDOR_INTEL_3, + .vendor = CPUID_VENDOR_INTEL, .family = 6, .model = 5, .stepping = 2, @@ -584,9 +567,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "pentium3", .level = 2, - .vendor1 = CPUID_VENDOR_INTEL_1, - .vendor2 = CPUID_VENDOR_INTEL_2, - .vendor3 = CPUID_VENDOR_INTEL_3, + .vendor = CPUID_VENDOR_INTEL, .family = 6, .model = 7, .stepping = 3, @@ -596,9 +577,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "athlon", .level = 2, - .vendor1 = CPUID_VENDOR_AMD_1, - .vendor2 = CPUID_VENDOR_AMD_2, - .vendor3 = CPUID_VENDOR_AMD_3, + .vendor = CPUID_VENDOR_AMD, .family = 6, .model = 2, .stepping = 3, @@ -612,9 +591,7 @@ static x86_def_t builtin_x86_defs[] = { .name = "n270", /* original is on level 10 */ .level = 5, - .vendor1 = CPUID_VENDOR_INTEL_1, - .vendor2 = CPUID_VENDOR_INTEL_2, - .vendor3 = CPUID_VENDOR_INTEL_3, + .vendor = CPUID_VENDOR_INTEL, .family = 6, .model = 28, .stepping = 2, @@ -633,9 +610,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "Conroe", .level = 2, - .vendor1 = CPUID_VENDOR_INTEL_1, - .vendor2 = CPUID_VENDOR_INTEL_2, - .vendor3 = CPUID_VENDOR_INTEL_3, + .vendor = CPUID_VENDOR_INTEL, .family = 6, .model = 2, .stepping = 3, @@ -653,9 +628,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "Penryn", .level = 2, - .vendor1 = CPUID_VENDOR_INTEL_1, - .vendor2 = CPUID_VENDOR_INTEL_2, - .vendor3 = CPUID_VENDOR_INTEL_3, + .vendor = CPUID_VENDOR_INTEL, .family = 6, .model = 2, .stepping = 3, @@ -674,9 +647,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "Nehalem", .level = 2, - .vendor1 = CPUID_VENDOR_INTEL_1, - .vendor2 = CPUID_VENDOR_INTEL_2, - .vendor3 = CPUID_VENDOR_INTEL_3, + .vendor = CPUID_VENDOR_INTEL, .family = 6, .model = 2, .stepping = 3, @@ -695,9 +666,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "Westmere", .level = 11, - .vendor1 = CPUID_VENDOR_INTEL_1, - .vendor2 = CPUID_VENDOR_INTEL_2, - .vendor3 = CPUID_VENDOR_INTEL_3, + .vendor = CPUID_VENDOR_INTEL, .family = 6, .model = 44, .stepping = 1, @@ -717,9 +686,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "SandyBridge", .level = 0xd, - .vendor1 = CPUID_VENDOR_INTEL_1, - .vendor2 = CPUID_VENDOR_INTEL_2, - .vendor3 = CPUID_VENDOR_INTEL_3, + .vendor = CPUID_VENDOR_INTEL, .family = 6, .model = 42, .stepping = 1, @@ -742,9 +709,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "Haswell", .level = 0xd, - .vendor1 = CPUID_VENDOR_INTEL_1, - .vendor2 = CPUID_VENDOR_INTEL_2, - .vendor3 = CPUID_VENDOR_INTEL_3, + .vendor = CPUID_VENDOR_INTEL, .family = 6, .model = 60, .stepping = 1, @@ -772,9 +737,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "Opteron_G1", .level = 5, - .vendor1 = CPUID_VENDOR_AMD_1, - .vendor2 = CPUID_VENDOR_AMD_2, - .vendor3 = CPUID_VENDOR_AMD_3, + .vendor = CPUID_VENDOR_AMD, .family = 15, .model = 6, .stepping = 1, @@ -796,9 +759,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "Opteron_G2", .level = 5, - .vendor1 = CPUID_VENDOR_AMD_1, - .vendor2 = CPUID_VENDOR_AMD_2, - .vendor3 = CPUID_VENDOR_AMD_3, + .vendor = CPUID_VENDOR_AMD, .family = 15, .model = 6, .stepping = 1, @@ -822,9 +783,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "Opteron_G3", .level = 5, - .vendor1 = CPUID_VENDOR_AMD_1, - .vendor2 = CPUID_VENDOR_AMD_2, - .vendor3 = CPUID_VENDOR_AMD_3, + .vendor = CPUID_VENDOR_AMD, .family = 15, .model = 6, .stepping = 1, @@ -850,9 +809,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "Opteron_G4", .level = 0xd, - .vendor1 = CPUID_VENDOR_AMD_1, - .vendor2 = CPUID_VENDOR_AMD_2, - .vendor3 = CPUID_VENDOR_AMD_3, + .vendor = CPUID_VENDOR_AMD, .family = 21, .model = 1, .stepping = 2, @@ -882,9 +839,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "Opteron_G5", .level = 0xd, - .vendor1 = CPUID_VENDOR_AMD_1, - .vendor2 = CPUID_VENDOR_AMD_2, - .vendor3 = CPUID_VENDOR_AMD_3, + .vendor = CPUID_VENDOR_AMD, .family = 21, .model = 2, .stepping = 0, @@ -945,9 +900,7 @@ static void kvm_cpu_fill_host(x86_def_t *x86_cpu_def) x86_cpu_def->name = "host"; host_cpuid(0x0, 0, &eax, &ebx, &ecx, &edx); - x86_cpu_def->vendor1 = ebx; - x86_cpu_def->vendor2 = edx; - x86_cpu_def->vendor3 = ecx; + x86_cpu_vendor_words2str(x86_cpu_def->vendor, ebx, edx, ecx); host_cpuid(0x1, 0, &eax, &ebx, &ecx, &edx); x86_cpu_def->family = ((eax >> 8) & 0x0F) + ((eax >> 20) & 0xFF); @@ -972,12 +925,9 @@ static void kvm_cpu_fill_host(x86_def_t *x86_cpu_def) kvm_arch_get_supported_cpuid(s, 0x80000001, 0, R_ECX); cpu_x86_fill_model_id(x86_cpu_def->model_id); - x86_cpu_def->vendor_override = 0; /* Call Centaur's CPUID instruction. */ - if (x86_cpu_def->vendor1 == CPUID_VENDOR_VIA_1 && - x86_cpu_def->vendor2 == CPUID_VENDOR_VIA_2 && - x86_cpu_def->vendor3 == CPUID_VENDOR_VIA_3) { + if (!strcmp(x86_cpu_def->vendor, CPUID_VENDOR_VIA)) { host_cpuid(0xC0000000, 0, &eax, &ebx, &ecx, &edx); eax = kvm_arch_get_supported_cpuid(s, 0xC0000000, 0, R_EAX); if (eax >= 0xC0000001) { @@ -1213,15 +1163,10 @@ static char *x86_cpuid_get_vendor(Object *obj, Error **errp) X86CPU *cpu = X86_CPU(obj); CPUX86State *env = &cpu->env; char *value; - int i; value = (char *)g_malloc(CPUID_VENDOR_SZ + 1); - for (i = 0; i < 4; i++) { - value[i ] = env->cpuid_vendor1 >> (8 * i); - value[i + 4] = env->cpuid_vendor2 >> (8 * i); - value[i + 8] = env->cpuid_vendor3 >> (8 * i); - } - value[CPUID_VENDOR_SZ] = '\0'; + x86_cpu_vendor_words2str(value, env->cpuid_vendor1, env->cpuid_vendor2, + env->cpuid_vendor3); return value; } @@ -1246,7 +1191,6 @@ static void x86_cpuid_set_vendor(Object *obj, const char *value, env->cpuid_vendor2 |= ((uint8_t)value[i + 4]) << (8 * i); env->cpuid_vendor3 |= ((uint8_t)value[i + 8]) << (8 * i); } - env->cpuid_vendor_override = 1; } static char *x86_cpuid_get_model_id(Object *obj, Error **errp) @@ -1320,34 +1264,50 @@ static void x86_cpuid_set_tsc_freq(Object *obj, Visitor *v, void *opaque, static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *name) { x86_def_t *def; + int i; - for (def = x86_defs; def; def = def->next) { - if (name && !strcmp(name, def->name)) { - break; - } + if (name == NULL) { + return -1; } - if (kvm_enabled() && name && strcmp(name, "host") == 0) { + if (kvm_enabled() && strcmp(name, "host") == 0) { kvm_cpu_fill_host(x86_cpu_def); - } else if (!def) { - return -1; - } else { - memcpy(x86_cpu_def, def, sizeof(*def)); + return 0; } - return 0; + for (i = 0; i < ARRAY_SIZE(builtin_x86_defs); i++) { + def = &builtin_x86_defs[i]; + if (strcmp(name, def->name) == 0) { + memcpy(x86_cpu_def, def, sizeof(*def)); + /* sysenter isn't supported in compatibility mode on AMD, + * syscall isn't supported in compatibility mode on Intel. + * Normally we advertise the actual CPU vendor, but you can + * override this using the 'vendor' property if you want to use + * KVM's sysenter/syscall emulation in compatibility mode and + * when doing cross vendor migration + */ + if (kvm_enabled()) { + uint32_t ebx = 0, ecx = 0, edx = 0; + host_cpuid(0, 0, NULL, &ebx, &ecx, &edx); + x86_cpu_vendor_words2str(x86_cpu_def->vendor, ebx, edx, ecx); + } + return 0; + } + } + + return -1; } /* Parse "+feature,-feature,feature=foo" CPU feature string */ -static int cpu_x86_parse_featurestr(x86_def_t *x86_cpu_def, char *features) +static void cpu_x86_parse_featurestr(X86CPU *cpu, char *features, Error **errp) { - unsigned int i; char *featurestr; /* Single 'key=value" string being parsed */ /* Features to be added */ FeatureWordArray plus_features = { 0 }; /* Features to be removed */ FeatureWordArray minus_features = { 0 }; uint32_t numvalue; + CPUX86State *env = &cpu->env; featurestr = features ? strtok(features, ",") : NULL; @@ -1360,87 +1320,57 @@ static int cpu_x86_parse_featurestr(x86_def_t *x86_cpu_def, char *features) } else if ((val = strchr(featurestr, '='))) { *val = 0; val++; if (!strcmp(featurestr, "family")) { - char *err; - numvalue = strtoul(val, &err, 0); - if (!*val || *err || numvalue > 0xff + 0xf) { - fprintf(stderr, "bad numerical value %s\n", val); - goto error; - } - x86_cpu_def->family = numvalue; + object_property_parse(OBJECT(cpu), val, featurestr, errp); } else if (!strcmp(featurestr, "model")) { - char *err; - numvalue = strtoul(val, &err, 0); - if (!*val || *err || numvalue > 0xff) { - fprintf(stderr, "bad numerical value %s\n", val); - goto error; - } - x86_cpu_def->model = numvalue; + object_property_parse(OBJECT(cpu), val, featurestr, errp); } else if (!strcmp(featurestr, "stepping")) { - char *err; - numvalue = strtoul(val, &err, 0); - if (!*val || *err || numvalue > 0xf) { - fprintf(stderr, "bad numerical value %s\n", val); - goto error; - } - x86_cpu_def->stepping = numvalue ; + object_property_parse(OBJECT(cpu), val, featurestr, errp); } else if (!strcmp(featurestr, "level")) { - char *err; - numvalue = strtoul(val, &err, 0); - if (!*val || *err) { - fprintf(stderr, "bad numerical value %s\n", val); - goto error; - } - x86_cpu_def->level = numvalue; + object_property_parse(OBJECT(cpu), val, featurestr, errp); } else if (!strcmp(featurestr, "xlevel")) { char *err; + char num[32]; + numvalue = strtoul(val, &err, 0); if (!*val || *err) { - fprintf(stderr, "bad numerical value %s\n", val); - goto error; + error_setg(errp, "bad numerical value %s\n", val); + goto out; } if (numvalue < 0x80000000) { + fprintf(stderr, "xlevel value shall always be >= 0x80000000" + ", fixup will be removed in future versions\n"); numvalue += 0x80000000; } - x86_cpu_def->xlevel = numvalue; + snprintf(num, sizeof(num), "%" PRIu32, numvalue); + object_property_parse(OBJECT(cpu), num, featurestr, errp); } else if (!strcmp(featurestr, "vendor")) { - if (strlen(val) != 12) { - fprintf(stderr, "vendor string must be 12 chars long\n"); - goto error; - } - x86_cpu_def->vendor1 = 0; - x86_cpu_def->vendor2 = 0; - x86_cpu_def->vendor3 = 0; - for(i = 0; i < 4; i++) { - x86_cpu_def->vendor1 |= ((uint8_t)val[i ]) << (8 * i); - x86_cpu_def->vendor2 |= ((uint8_t)val[i + 4]) << (8 * i); - x86_cpu_def->vendor3 |= ((uint8_t)val[i + 8]) << (8 * i); - } - x86_cpu_def->vendor_override = 1; + object_property_parse(OBJECT(cpu), val, featurestr, errp); } else if (!strcmp(featurestr, "model_id")) { - pstrcpy(x86_cpu_def->model_id, sizeof(x86_cpu_def->model_id), - val); + object_property_parse(OBJECT(cpu), val, "model-id", errp); } else if (!strcmp(featurestr, "tsc_freq")) { int64_t tsc_freq; char *err; + char num[32]; tsc_freq = strtosz_suffix_unit(val, &err, STRTOSZ_DEFSUFFIX_B, 1000); if (tsc_freq < 0 || *err) { - fprintf(stderr, "bad numerical value %s\n", val); - goto error; + error_setg(errp, "bad numerical value %s\n", val); + goto out; } - x86_cpu_def->tsc_khz = tsc_freq / 1000; + snprintf(num, sizeof(num), "%" PRId64, tsc_freq); + object_property_parse(OBJECT(cpu), num, "tsc-frequency", errp); } else if (!strcmp(featurestr, "hv_spinlocks")) { char *err; numvalue = strtoul(val, &err, 0); if (!*val || *err) { - fprintf(stderr, "bad numerical value %s\n", val); - goto error; + error_setg(errp, "bad numerical value %s\n", val); + goto out; } hyperv_set_spinlock_retries(numvalue); } else { - fprintf(stderr, "unrecognized feature %s\n", featurestr); - goto error; + error_setg(errp, "unrecognized feature %s\n", featurestr); + goto out; } } else if (!strcmp(featurestr, "check")) { check_cpuid = 1; @@ -1451,31 +1381,34 @@ static int cpu_x86_parse_featurestr(x86_def_t *x86_cpu_def, char *features) } else if (!strcmp(featurestr, "hv_vapic")) { hyperv_enable_vapic_recommended(true); } else { - fprintf(stderr, "feature string `%s' not in format (+feature|-feature|feature=xyz)\n", featurestr); - goto error; + error_setg(errp, "feature string `%s' not in format (+feature|" + "-feature|feature=xyz)\n", featurestr); + goto out; + } + if (error_is_set(errp)) { + goto out; } featurestr = strtok(NULL, ","); } - x86_cpu_def->features |= plus_features[FEAT_1_EDX]; - x86_cpu_def->ext_features |= plus_features[FEAT_1_ECX]; - x86_cpu_def->ext2_features |= plus_features[FEAT_8000_0001_EDX]; - x86_cpu_def->ext3_features |= plus_features[FEAT_8000_0001_ECX]; - x86_cpu_def->ext4_features |= plus_features[FEAT_C000_0001_EDX]; - x86_cpu_def->kvm_features |= plus_features[FEAT_KVM]; - x86_cpu_def->svm_features |= plus_features[FEAT_SVM]; - x86_cpu_def->cpuid_7_0_ebx_features |= plus_features[FEAT_7_0_EBX]; - x86_cpu_def->features &= ~minus_features[FEAT_1_EDX]; - x86_cpu_def->ext_features &= ~minus_features[FEAT_1_ECX]; - x86_cpu_def->ext2_features &= ~minus_features[FEAT_8000_0001_EDX]; - x86_cpu_def->ext3_features &= ~minus_features[FEAT_8000_0001_ECX]; - x86_cpu_def->ext4_features &= ~minus_features[FEAT_C000_0001_EDX]; - x86_cpu_def->kvm_features &= ~minus_features[FEAT_KVM]; - x86_cpu_def->svm_features &= ~minus_features[FEAT_SVM]; - x86_cpu_def->cpuid_7_0_ebx_features &= ~minus_features[FEAT_7_0_EBX]; - return 0; + env->cpuid_features |= plus_features[FEAT_1_EDX]; + env->cpuid_ext_features |= plus_features[FEAT_1_ECX]; + env->cpuid_ext2_features |= plus_features[FEAT_8000_0001_EDX]; + env->cpuid_ext3_features |= plus_features[FEAT_8000_0001_ECX]; + env->cpuid_ext4_features |= plus_features[FEAT_C000_0001_EDX]; + env->cpuid_kvm_features |= plus_features[FEAT_KVM]; + env->cpuid_svm_features |= plus_features[FEAT_SVM]; + env->cpuid_7_0_ebx_features |= plus_features[FEAT_7_0_EBX]; + env->cpuid_features &= ~minus_features[FEAT_1_EDX]; + env->cpuid_ext_features &= ~minus_features[FEAT_1_ECX]; + env->cpuid_ext2_features &= ~minus_features[FEAT_8000_0001_EDX]; + env->cpuid_ext3_features &= ~minus_features[FEAT_8000_0001_ECX]; + env->cpuid_ext4_features &= ~minus_features[FEAT_C000_0001_EDX]; + env->cpuid_kvm_features &= ~minus_features[FEAT_KVM]; + env->cpuid_svm_features &= ~minus_features[FEAT_SVM]; + env->cpuid_7_0_ebx_features &= ~minus_features[FEAT_7_0_EBX]; -error: - return -1; +out: + return; } /* generate a composite string into buf of all cpuid names in featureset @@ -1513,8 +1446,10 @@ void x86_cpu_list(FILE *f, fprintf_function cpu_fprintf) { x86_def_t *def; char buf[256]; + int i; - for (def = x86_defs; def; def = def->next) { + for (i = 0; i < ARRAY_SIZE(builtin_x86_defs); i++) { + def = &builtin_x86_defs[i]; snprintf(buf, sizeof(buf), "%s", def->name); (*cpu_fprintf)(f, "x86 %16s %-48s\n", buf, def->model_id); } @@ -1536,11 +1471,13 @@ CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp) { CpuDefinitionInfoList *cpu_list = NULL; x86_def_t *def; + int i; - for (def = x86_defs; def; def = def->next) { + for (i = 0; i < ARRAY_SIZE(builtin_x86_defs); i++) { CpuDefinitionInfoList *entry; CpuDefinitionInfo *info; + def = &builtin_x86_defs[i]; info = g_malloc0(sizeof(*info)); info->name = g_strdup(def->name); @@ -1602,18 +1539,12 @@ int cpu_x86_register(X86CPU *cpu, const char *cpu_model) goto out; } - def->kvm_features |= kvm_default_features; + if (kvm_enabled()) { + def->kvm_features |= kvm_default_features; + } def->ext_features |= CPUID_EXT_HYPERVISOR; - if (cpu_x86_parse_featurestr(def, features) < 0) { - error_setg(&error, "Invalid cpu_model string format: %s", cpu_model); - goto out; - } - assert(def->vendor1); - env->cpuid_vendor1 = def->vendor1; - env->cpuid_vendor2 = def->vendor2; - env->cpuid_vendor3 = def->vendor3; - env->cpuid_vendor_override = def->vendor_override; + object_property_set_str(OBJECT(cpu), def->vendor, "vendor", &error); object_property_set_int(OBJECT(cpu), def->level, "level", &error); object_property_set_int(OBJECT(cpu), def->family, "family", &error); object_property_set_int(OBJECT(cpu), def->model, "model", &error); @@ -1628,11 +1559,13 @@ int cpu_x86_register(X86CPU *cpu, const char *cpu_model) env->cpuid_ext4_features = def->ext4_features; env->cpuid_7_0_ebx_features = def->cpuid_7_0_ebx_features; env->cpuid_xlevel2 = def->xlevel2; - object_property_set_int(OBJECT(cpu), (int64_t)def->tsc_khz * 1000, - "tsc-frequency", &error); object_property_set_str(OBJECT(cpu), def->model_id, "model-id", &error); + if (error) { + goto out; + } + cpu_x86_parse_featurestr(cpu, features, &error); out: g_strfreev(model_pieces); if (error) { @@ -1661,7 +1594,6 @@ void x86_cpudef_setup(void) for (i = 0; i < ARRAY_SIZE(builtin_x86_defs); ++i) { x86_def_t *def = &builtin_x86_defs[i]; - def->next = x86_defs; /* Look for specific "cpudef" models that */ /* have the QEMU version in .model_id */ @@ -1674,8 +1606,6 @@ void x86_cpudef_setup(void) break; } } - - x86_defs = def; } } @@ -1685,16 +1615,6 @@ static void get_cpuid_vendor(CPUX86State *env, uint32_t *ebx, *ebx = env->cpuid_vendor1; *edx = env->cpuid_vendor2; *ecx = env->cpuid_vendor3; - - /* sysenter isn't supported on compatibility mode on AMD, syscall - * isn't supported in compatibility mode on Intel. - * Normally we advertise the actual cpu vendor, but you can override - * this if you want to use KVM's sysenter/syscall emulation - * in compatibility mode and when doing cross vendor migration - */ - if (kvm_enabled() && ! env->cpuid_vendor_override) { - host_cpuid(0, 0, NULL, ebx, ecx, edx); - } } void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, @@ -2197,6 +2117,39 @@ void x86_cpu_realize(Object *obj, Error **errp) cpu_reset(CPU(cpu)); } +/* Enables contiguous-apic-ID mode, for compatibility */ +static bool compat_apic_id_mode; + +void enable_compat_apic_id_mode(void) +{ + compat_apic_id_mode = true; +} + +/* Calculates initial APIC ID for a specific CPU index + * + * Currently we need to be able to calculate the APIC ID from the CPU index + * alone (without requiring a CPU object), as the QEMU<->Seabios interfaces have + * no concept of "CPU index", and the NUMA tables on fw_cfg need the APIC ID of + * all CPUs up to max_cpus. + */ +uint32_t x86_cpu_apic_id_from_index(unsigned int cpu_index) +{ + uint32_t correct_id; + static bool warned; + + correct_id = x86_apicid_from_cpu_idx(smp_cores, smp_threads, cpu_index); + if (compat_apic_id_mode) { + if (cpu_index != correct_id && !warned) { + error_report("APIC IDs set in compatibility mode, " + "CPU topology won't match the configuration"); + warned = true; + } + return cpu_index; + } else { + return correct_id; + } +} + static void x86_cpu_initfn(Object *obj) { CPUState *cs = CPU(obj); @@ -2231,7 +2184,7 @@ static void x86_cpu_initfn(Object *obj) x86_cpuid_get_tsc_freq, x86_cpuid_set_tsc_freq, NULL, NULL, NULL); - env->cpuid_apic_id = cs->cpu_index; + env->cpuid_apic_id = x86_cpu_apic_id_from_index(cs->cpu_index); /* init various static tables used in TCG mode */ if (tcg_enabled() && !inited) { diff --git a/target-i386/cpu.h b/target-i386/cpu.h index 4e091cdec3..9e6e1a652f 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -537,14 +537,14 @@ typedef uint32_t FeatureWordArray[FEATURE_WORDS]; #define CPUID_VENDOR_INTEL_1 0x756e6547 /* "Genu" */ #define CPUID_VENDOR_INTEL_2 0x49656e69 /* "ineI" */ #define CPUID_VENDOR_INTEL_3 0x6c65746e /* "ntel" */ +#define CPUID_VENDOR_INTEL "GenuineIntel" #define CPUID_VENDOR_AMD_1 0x68747541 /* "Auth" */ #define CPUID_VENDOR_AMD_2 0x69746e65 /* "enti" */ #define CPUID_VENDOR_AMD_3 0x444d4163 /* "cAMD" */ +#define CPUID_VENDOR_AMD "AuthenticAMD" -#define CPUID_VENDOR_VIA_1 0x746e6543 /* "Cent" */ -#define CPUID_VENDOR_VIA_2 0x48727561 /* "aurH" */ -#define CPUID_VENDOR_VIA_3 0x736c7561 /* "auls" */ +#define CPUID_VENDOR_VIA "CentaurHauls" #define CPUID_MWAIT_IBE (1 << 1) /* Interrupts can exit capability */ #define CPUID_MWAIT_EMX (1 << 0) /* enumeration supported */ @@ -835,7 +835,6 @@ typedef struct CPUX86State { uint32_t cpuid_ext2_features; uint32_t cpuid_ext3_features; uint32_t cpuid_apic_id; - int cpuid_vendor_override; /* Store the results of Centaur's CPUID instructions */ uint32_t cpuid_xlevel2; uint32_t cpuid_ext4_features; @@ -1012,7 +1011,7 @@ void host_cpuid(uint32_t function, uint32_t count, int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr, int is_write, int mmu_idx); #define cpu_handle_mmu_fault cpu_x86_handle_mmu_fault -void cpu_x86_set_a20(CPUX86State *env, int a20_state); +void x86_cpu_set_a20(X86CPU *cpu, int a20_state); static inline bool hw_local_breakpoint_enabled(unsigned long dr7, int index) { @@ -1250,9 +1249,12 @@ void do_smm_enter(CPUX86State *env1); void cpu_report_tpr_access(CPUX86State *env, TPRAccess access); -void enable_kvm_pv_eoi(void); +void disable_kvm_pv_eoi(void); /* Return name of 32-bit register, from a R_* constant */ const char *get_register_name_32(unsigned int reg); +uint32_t x86_cpu_apic_id_from_index(unsigned int cpu_index); +void enable_compat_apic_id_mode(void); + #endif /* CPU_I386_H */ diff --git a/target-i386/helper.c b/target-i386/helper.c index 547c25ee9d..d1cb4e2445 100644 --- a/target-i386/helper.c +++ b/target-i386/helper.c @@ -366,8 +366,10 @@ void cpu_dump_state(CPUX86State *env, FILE *f, fprintf_function cpu_fprintf, /* x86 mmu */ /* XXX: add PGE support */ -void cpu_x86_set_a20(CPUX86State *env, int a20_state) +void x86_cpu_set_a20(X86CPU *cpu, int a20_state) { + CPUX86State *env = &cpu->env; + a20_state = (a20_state != 0); if (a20_state != ((env->a20_mask >> 20) & 1)) { #if defined(DEBUG_MMU) @@ -1276,14 +1278,14 @@ X86CPU *cpu_x86_init(const char *cpu_model) env->cpu_model_str = cpu_model; if (cpu_x86_register(cpu, cpu_model) < 0) { - object_delete(OBJECT(cpu)); + object_unref(OBJECT(cpu)); return NULL; } x86_cpu_realize(OBJECT(cpu), &error); if (error) { error_free(error); - object_delete(OBJECT(cpu)); + object_unref(OBJECT(cpu)); return NULL; } return cpu; diff --git a/target-i386/kvm.c b/target-i386/kvm.c index 3acff40c47..9ebf1816d9 100644 --- a/target-i386/kvm.c +++ b/target-i386/kvm.c @@ -411,11 +411,19 @@ static void cpu_update_state(void *opaque, int running, RunState state) } } +unsigned long kvm_arch_vcpu_id(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + return cpu->env.cpuid_apic_id; +} + +#define KVM_MAX_CPUID_ENTRIES 100 + int kvm_arch_init_vcpu(CPUState *cs) { struct { struct kvm_cpuid2 cpuid; - struct kvm_cpuid_entry2 entries[100]; + struct kvm_cpuid_entry2 entries[KVM_MAX_CPUID_ENTRIES]; } QEMU_PACKED cpuid_data; X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; @@ -502,6 +510,10 @@ int kvm_arch_init_vcpu(CPUState *cs) cpu_x86_cpuid(env, 0, 0, &limit, &unused, &unused, &unused); for (i = 0; i <= limit; i++) { + if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { + fprintf(stderr, "unsupported level value: 0x%x\n", limit); + abort(); + } c = &cpuid_data.entries[cpuid_i++]; switch (i) { @@ -516,6 +528,11 @@ int kvm_arch_init_vcpu(CPUState *cs) times = c->eax & 0xff; for (j = 1; j < times; ++j) { + if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { + fprintf(stderr, "cpuid_data is full, no space for " + "cpuid(eax:2):eax & 0xf = 0x%x\n", times); + abort(); + } c = &cpuid_data.entries[cpuid_i++]; c->function = i; c->flags = KVM_CPUID_FLAG_STATEFUL_FUNC; @@ -544,6 +561,11 @@ int kvm_arch_init_vcpu(CPUState *cs) if (i == 0xd && c->eax == 0) { continue; } + if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { + fprintf(stderr, "cpuid_data is full, no space for " + "cpuid(eax:0x%x,ecx:0x%x)\n", i, j); + abort(); + } c = &cpuid_data.entries[cpuid_i++]; } break; @@ -557,6 +579,10 @@ int kvm_arch_init_vcpu(CPUState *cs) cpu_x86_cpuid(env, 0x80000000, 0, &limit, &unused, &unused, &unused); for (i = 0x80000000; i <= limit; i++) { + if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { + fprintf(stderr, "unsupported xlevel value: 0x%x\n", limit); + abort(); + } c = &cpuid_data.entries[cpuid_i++]; c->function = i; @@ -569,6 +595,10 @@ int kvm_arch_init_vcpu(CPUState *cs) cpu_x86_cpuid(env, 0xC0000000, 0, &limit, &unused, &unused, &unused); for (i = 0xC0000000; i <= limit; i++) { + if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { + fprintf(stderr, "unsupported xlevel2 value: 0x%x\n", limit); + abort(); + } c = &cpuid_data.entries[cpuid_i++]; c->function = i; diff --git a/target-i386/topology.h b/target-i386/topology.h new file mode 100644 index 0000000000..24ed525453 --- /dev/null +++ b/target-i386/topology.h @@ -0,0 +1,136 @@ +/* + * x86 CPU topology data structures and functions + * + * Copyright (c) 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef TARGET_I386_TOPOLOGY_H +#define TARGET_I386_TOPOLOGY_H + +/* This file implements the APIC-ID-based CPU topology enumeration logic, + * documented at the following document: + * Intel® 64 Architecture Processor Topology Enumeration + * http://software.intel.com/en-us/articles/intel-64-architecture-processor-topology-enumeration/ + * + * This code should be compatible with AMD's "Extended Method" described at: + * AMD CPUID Specification (Publication #25481) + * Section 3: Multiple Core Calcuation + * as long as: + * nr_threads is set to 1; + * OFFSET_IDX is assumed to be 0; + * CPUID Fn8000_0008_ECX[ApicIdCoreIdSize[3:0]] is set to apicid_core_width(). + */ + +#include <stdint.h> +#include <string.h> + +#include "qemu/bitops.h" + +/* APIC IDs can be 32-bit, but beware: APIC IDs > 255 require x2APIC support + */ +typedef uint32_t apic_id_t; + +/* Return the bit width needed for 'count' IDs + */ +static unsigned apicid_bitwidth_for_count(unsigned count) +{ + g_assert(count >= 1); + if (count == 1) { + return 0; + } + return bitops_flsl(count - 1) + 1; +} + +/* Bit width of the SMT_ID (thread ID) field on the APIC ID + */ +static inline unsigned apicid_smt_width(unsigned nr_cores, unsigned nr_threads) +{ + return apicid_bitwidth_for_count(nr_threads); +} + +/* Bit width of the Core_ID field + */ +static inline unsigned apicid_core_width(unsigned nr_cores, unsigned nr_threads) +{ + return apicid_bitwidth_for_count(nr_cores); +} + +/* Bit offset of the Core_ID field + */ +static inline unsigned apicid_core_offset(unsigned nr_cores, + unsigned nr_threads) +{ + return apicid_smt_width(nr_cores, nr_threads); +} + +/* Bit offset of the Pkg_ID (socket ID) field + */ +static inline unsigned apicid_pkg_offset(unsigned nr_cores, unsigned nr_threads) +{ + return apicid_core_offset(nr_cores, nr_threads) + + apicid_core_width(nr_cores, nr_threads); +} + +/* Make APIC ID for the CPU based on Pkg_ID, Core_ID, SMT_ID + * + * The caller must make sure core_id < nr_cores and smt_id < nr_threads. + */ +static inline apic_id_t apicid_from_topo_ids(unsigned nr_cores, + unsigned nr_threads, + unsigned pkg_id, + unsigned core_id, + unsigned smt_id) +{ + return (pkg_id << apicid_pkg_offset(nr_cores, nr_threads)) | + (core_id << apicid_core_offset(nr_cores, nr_threads)) | + smt_id; +} + +/* Calculate thread/core/package IDs for a specific topology, + * based on (contiguous) CPU index + */ +static inline void x86_topo_ids_from_idx(unsigned nr_cores, + unsigned nr_threads, + unsigned cpu_index, + unsigned *pkg_id, + unsigned *core_id, + unsigned *smt_id) +{ + unsigned core_index = cpu_index / nr_threads; + *smt_id = cpu_index % nr_threads; + *core_id = core_index % nr_cores; + *pkg_id = core_index / nr_cores; +} + +/* Make APIC ID for the CPU 'cpu_index' + * + * 'cpu_index' is a sequential, contiguous ID for the CPU. + */ +static inline apic_id_t x86_apicid_from_cpu_idx(unsigned nr_cores, + unsigned nr_threads, + unsigned cpu_index) +{ + unsigned pkg_id, core_id, smt_id; + x86_topo_ids_from_idx(nr_cores, nr_threads, cpu_index, + &pkg_id, &core_id, &smt_id); + return apicid_from_topo_ids(nr_cores, nr_threads, pkg_id, core_id, smt_id); +} + +#endif /* TARGET_I386_TOPOLOGY_H */ diff --git a/target-m68k/Makefile.objs b/target-m68k/Makefile.objs index 7eccfab0e4..2e2b85044d 100644 --- a/target-m68k/Makefile.objs +++ b/target-m68k/Makefile.objs @@ -1,3 +1,2 @@ obj-y += m68k-semi.o obj-y += translate.o op_helper.o helper.o cpu.o -obj-$(CONFIG_SOFTMMU) += machine.o diff --git a/target-m68k/cpu.c b/target-m68k/cpu.c index ce89674a08..c71f715174 100644 --- a/target-m68k/cpu.c +++ b/target-m68k/cpu.c @@ -20,6 +20,7 @@ #include "cpu.h" #include "qemu-common.h" +#include "migration/vmstate.h" static void m68k_set_feature(CPUM68KState *env, int feature) @@ -55,6 +56,25 @@ static void m68k_cpu_reset(CPUState *s) /* CPU models */ +static ObjectClass *m68k_cpu_class_by_name(const char *cpu_model) +{ + ObjectClass *oc; + char *typename; + + if (cpu_model == NULL) { + return NULL; + } + + typename = g_strdup_printf("%s-" TYPE_M68K_CPU, cpu_model); + oc = object_class_by_name(typename); + g_free(typename); + if (oc != NULL && (object_class_dynamic_cast(oc, TYPE_M68K_CPU) == NULL || + object_class_is_abstract(oc))) { + return NULL; + } + return oc; +} + static void m5206_cpu_initfn(Object *obj) { M68kCPU *cpu = M68K_CPU(obj); @@ -127,24 +147,34 @@ static void m68k_cpu_initfn(Object *obj) cpu_exec_init(env); } +static const VMStateDescription vmstate_m68k_cpu = { + .name = "cpu", + .unmigratable = 1, +}; + static void m68k_cpu_class_init(ObjectClass *c, void *data) { M68kCPUClass *mcc = M68K_CPU_CLASS(c); CPUClass *cc = CPU_CLASS(c); + DeviceClass *dc = DEVICE_CLASS(c); mcc->parent_reset = cc->reset; cc->reset = m68k_cpu_reset; + + cc->class_by_name = m68k_cpu_class_by_name; + dc->vmsd = &vmstate_m68k_cpu; } static void register_cpu_type(const M68kCPUInfo *info) { TypeInfo type_info = { - .name = info->name, .parent = TYPE_M68K_CPU, .instance_init = info->instance_init, }; - type_register_static(&type_info); + type_info.name = g_strdup_printf("%s-" TYPE_M68K_CPU, info->name); + type_register(&type_info); + g_free((void *)type_info.name); } static const TypeInfo m68k_cpu_type_info = { diff --git a/target-m68k/helper.c b/target-m68k/helper.c index 097fc789d4..5ddcd707fd 100644 --- a/target-m68k/helper.c +++ b/target-m68k/helper.c @@ -34,9 +34,9 @@ static gint m68k_cpu_list_compare(gconstpointer a, gconstpointer b) name_a = object_class_get_name(class_a); name_b = object_class_get_name(class_b); - if (strcmp(name_a, "any") == 0) { + if (strcmp(name_a, "any-" TYPE_M68K_CPU) == 0) { return 1; - } else if (strcmp(name_b, "any") == 0) { + } else if (strcmp(name_b, "any-" TYPE_M68K_CPU) == 0) { return -1; } else { return strcasecmp(name_a, name_b); @@ -47,9 +47,14 @@ static void m68k_cpu_list_entry(gpointer data, gpointer user_data) { ObjectClass *c = data; CPUListState *s = user_data; + const char *typename; + char *name; + typename = object_class_get_name(c); + name = g_strndup(typename, strlen(typename) - strlen("-" TYPE_M68K_CPU)); (*s->cpu_fprintf)(s->file, "%s\n", - object_class_get_name(c)); + name); + g_free(name); } void m68k_cpu_list(FILE *f, fprintf_function cpu_fprintf) @@ -97,12 +102,14 @@ CPUM68KState *cpu_m68k_init(const char *cpu_model) { M68kCPU *cpu; CPUM68KState *env; + ObjectClass *oc; static int inited; - if (object_class_by_name(cpu_model) == NULL) { + oc = cpu_class_by_name(TYPE_M68K_CPU, cpu_model); + if (oc == NULL) { return NULL; } - cpu = M68K_CPU(object_new(cpu_model)); + cpu = M68K_CPU(object_new(object_class_get_name(oc))); env = &cpu->env; if (!inited) { diff --git a/target-m68k/machine.c b/target-m68k/machine.c deleted file mode 100644 index e69de29bb2..0000000000 --- a/target-m68k/machine.c +++ /dev/null diff --git a/target-m68k/translate.c b/target-m68k/translate.c index e763195f86..3f1478cc20 100644 --- a/target-m68k/translate.c +++ b/target-m68k/translate.c @@ -574,7 +574,7 @@ static inline TCGv gen_ea_once(CPUM68KState *env, DisasContext *s, return gen_ldst(s, opsize, tmp, val, what); } -/* Generate code to load/store a value ito/from an EA. If VAL > 0 this is +/* Generate code to load/store a value from/into an EA. If VAL > 0 this is a write otherwise it is a read (0 == sign extend, -1 == zero extend). ADDRP is non-null for readwrite operands. */ static TCGv gen_ea(CPUM68KState *env, DisasContext *s, uint16_t insn, diff --git a/target-microblaze/Makefile.objs b/target-microblaze/Makefile.objs index afb87bcc80..985330eac5 100644 --- a/target-microblaze/Makefile.objs +++ b/target-microblaze/Makefile.objs @@ -1,2 +1,2 @@ obj-y += translate.o op_helper.o helper.o cpu.o -obj-$(CONFIG_SOFTMMU) += mmu.o machine.o +obj-$(CONFIG_SOFTMMU) += mmu.o diff --git a/target-microblaze/cpu.c b/target-microblaze/cpu.c index 0f858fd869..39230fddcc 100644 --- a/target-microblaze/cpu.c +++ b/target-microblaze/cpu.c @@ -22,6 +22,7 @@ #include "cpu.h" #include "qemu-common.h" +#include "migration/vmstate.h" /* CPUClass::reset() */ @@ -94,13 +95,21 @@ static void mb_cpu_initfn(Object *obj) set_float_rounding_mode(float_round_nearest_even, &env->fp_status); } +static const VMStateDescription vmstate_mb_cpu = { + .name = "cpu", + .unmigratable = 1, +}; + static void mb_cpu_class_init(ObjectClass *oc, void *data) { + DeviceClass *dc = DEVICE_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); MicroBlazeCPUClass *mcc = MICROBLAZE_CPU_CLASS(oc); mcc->parent_reset = cc->reset; cc->reset = mb_cpu_reset; + + dc->vmsd = &vmstate_mb_cpu; } static const TypeInfo mb_cpu_type_info = { diff --git a/target-microblaze/cpu.h b/target-microblaze/cpu.h index 5621068d82..41480e71e1 100644 --- a/target-microblaze/cpu.h +++ b/target-microblaze/cpu.h @@ -307,8 +307,6 @@ static inline CPUMBState *cpu_init(const char *cpu_model) #define cpu_gen_code cpu_mb_gen_code #define cpu_signal_handler cpu_mb_signal_handler -#define CPU_SAVE_VERSION 1 - /* MMU modes definitions */ #define MMU_MODE0_SUFFIX _nommu #define MMU_MODE1_SUFFIX _kernel diff --git a/target-microblaze/machine.c b/target-microblaze/machine.c deleted file mode 100644 index 1be1c351b2..0000000000 --- a/target-microblaze/machine.c +++ /dev/null @@ -1,11 +0,0 @@ -#include "hw/hw.h" -#include "hw/boards.h" - -void cpu_save(QEMUFile *f, void *opaque) -{ -} - -int cpu_load(QEMUFile *f, void *opaque, int version_id) -{ - return 0; -} diff --git a/target-mips/dsp_helper.c b/target-mips/dsp_helper.c index 4870e3dbbc..96cb0447e2 100644 --- a/target-mips/dsp_helper.c +++ b/target-mips/dsp_helper.c @@ -20,6 +20,28 @@ #include "cpu.h" #include "helper.h" +/* As the byte ordering doesn't matter, i.e. all columns are treated + identically, these unions can be used directly. */ +typedef union { + uint8_t ub[4]; + int8_t sb[4]; + uint16_t uh[2]; + int16_t sh[2]; + uint32_t uw[1]; + int32_t sw[1]; +} DSP32Value; + +typedef union { + uint8_t ub[8]; + int8_t sb[8]; + uint16_t uh[4]; + int16_t sh[4]; + uint32_t uw[2]; + int32_t sw[2]; + uint64_t ul[1]; + int64_t sl[1]; +} DSP64Value; + /*** MIPS DSP internal functions begin ***/ #define MIPSDSP_ABS(x) (((x) >= 0) ? x : -x) #define MIPSDSP_OVERFLOW(a, b, c, d) (!(!((a ^ b ^ -1) & (a ^ c) & d))) @@ -1056,7 +1078,6 @@ static inline int32_t mipsdsp_cmpu_lt(uint32_t a, uint32_t b) b = num & MIPSDSP_LO; \ } while (0) -#define MIPSDSP_RETURN32(a) ((target_long)(int32_t)a) #define MIPSDSP_RETURN32_8(a, b, c, d) ((target_long)(int32_t) \ (((uint32_t)a << 24) | \ (((uint32_t)b << 16) | \ @@ -1089,287 +1110,169 @@ static inline int32_t mipsdsp_cmpu_lt(uint32_t a, uint32_t b) #endif /** DSP Arithmetic Sub-class insns **/ -#define ARITH_PH(name, func) \ -target_ulong helper_##name##_ph(target_ulong rs, target_ulong rt) \ -{ \ - uint16_t rsh, rsl, rth, rtl, temph, templ; \ - \ - MIPSDSP_SPLIT32_16(rs, rsh, rsl); \ - MIPSDSP_SPLIT32_16(rt, rth, rtl); \ - \ - temph = mipsdsp_##func(rsh, rth); \ - templ = mipsdsp_##func(rsl, rtl); \ - \ - return MIPSDSP_RETURN32_16(temph, templ); \ -} - -#define ARITH_PH_ENV(name, func) \ -target_ulong helper_##name##_ph(target_ulong rs, target_ulong rt, \ - CPUMIPSState *env) \ -{ \ - uint16_t rsh, rsl, rth, rtl, temph, templ; \ - \ - MIPSDSP_SPLIT32_16(rs, rsh, rsl); \ - MIPSDSP_SPLIT32_16(rt, rth, rtl); \ - \ - temph = mipsdsp_##func(rsh, rth, env); \ - templ = mipsdsp_##func(rsl, rtl, env); \ - \ - return MIPSDSP_RETURN32_16(temph, templ); \ -} - - -ARITH_PH_ENV(addq, add_i16); -ARITH_PH_ENV(addq_s, sat_add_i16); -ARITH_PH_ENV(addu, add_u16); -ARITH_PH_ENV(addu_s, sat_add_u16); - -ARITH_PH(addqh, rshift1_add_q16); -ARITH_PH(addqh_r, rrshift1_add_q16); - -ARITH_PH_ENV(subq, sub_i16); -ARITH_PH_ENV(subq_s, sat16_sub); -ARITH_PH_ENV(subu, sub_u16_u16); -ARITH_PH_ENV(subu_s, satu16_sub_u16_u16); - -ARITH_PH(subqh, rshift1_sub_q16); -ARITH_PH(subqh_r, rrshift1_sub_q16); - -#undef ARITH_PH -#undef ARITH_PH_ENV - -#ifdef TARGET_MIPS64 -#define ARITH_QH_ENV(name, func) \ -target_ulong helper_##name##_qh(target_ulong rs, target_ulong rt, \ - CPUMIPSState *env) \ -{ \ - uint16_t rs3, rs2, rs1, rs0; \ - uint16_t rt3, rt2, rt1, rt0; \ - uint16_t tempD, tempC, tempB, tempA; \ - \ - MIPSDSP_SPLIT64_16(rs, rs3, rs2, rs1, rs0); \ - MIPSDSP_SPLIT64_16(rt, rt3, rt2, rt1, rt0); \ - \ - tempD = mipsdsp_##func(rs3, rt3, env); \ - tempC = mipsdsp_##func(rs2, rt2, env); \ - tempB = mipsdsp_##func(rs1, rt1, env); \ - tempA = mipsdsp_##func(rs0, rt0, env); \ - \ - return MIPSDSP_RETURN64_16(tempD, tempC, tempB, tempA); \ -} - -ARITH_QH_ENV(addq, add_i16); -ARITH_QH_ENV(addq_s, sat_add_i16); -ARITH_QH_ENV(addu, add_u16); -ARITH_QH_ENV(addu_s, sat_add_u16); - -ARITH_QH_ENV(subq, sub_i16); -ARITH_QH_ENV(subq_s, sat16_sub); -ARITH_QH_ENV(subu, sub_u16_u16); -ARITH_QH_ENV(subu_s, satu16_sub_u16_u16); - -#undef ARITH_QH_ENV - -#endif - -#define ARITH_W(name, func) \ -target_ulong helper_##name##_w(target_ulong rs, target_ulong rt) \ -{ \ - uint32_t rd; \ - rd = mipsdsp_##func(rs, rt); \ - return MIPSDSP_RETURN32(rd); \ -} - -#define ARITH_W_ENV(name, func) \ -target_ulong helper_##name##_w(target_ulong rs, target_ulong rt, \ - CPUMIPSState *env) \ -{ \ - uint32_t rd; \ - rd = mipsdsp_##func(rs, rt, env); \ - return MIPSDSP_RETURN32(rd); \ -} - -ARITH_W_ENV(addq_s, sat_add_i32); - -ARITH_W(addqh, rshift1_add_q32); -ARITH_W(addqh_r, rrshift1_add_q32); - -ARITH_W_ENV(subq_s, sat32_sub); - -ARITH_W(subqh, rshift1_sub_q32); -ARITH_W(subqh_r, rrshift1_sub_q32); - -#undef ARITH_W -#undef ARITH_W_ENV - -target_ulong helper_absq_s_w(target_ulong rt, CPUMIPSState *env) -{ - uint32_t rd; - - rd = mipsdsp_sat_abs32(rt, env); - - return (target_ulong)rd; -} - +#define MIPSDSP32_UNOP_ENV(name, func, element) \ +target_ulong helper_##name(target_ulong rt, CPUMIPSState *env) \ +{ \ + DSP32Value dt; \ + unsigned int i, n; \ + \ + n = sizeof(DSP32Value) / sizeof(dt.element[0]); \ + dt.sw[0] = rt; \ + \ + for (i = 0; i < n; i++) { \ + dt.element[i] = mipsdsp_##func(dt.element[i], env); \ + } \ + \ + return (target_long)dt.sw[0]; \ +} +MIPSDSP32_UNOP_ENV(absq_s_ph, sat_abs16, sh) +MIPSDSP32_UNOP_ENV(absq_s_qb, sat_abs8, sb) +MIPSDSP32_UNOP_ENV(absq_s_w, sat_abs32, sw) +#undef MIPSDSP32_UNOP_ENV #if defined(TARGET_MIPS64) - -#define ARITH_PW_ENV(name, func) \ -target_ulong helper_##name##_pw(target_ulong rs, target_ulong rt, \ - CPUMIPSState *env) \ -{ \ - uint32_t rs1, rs0; \ - uint32_t rt1, rt0; \ - uint32_t tempB, tempA; \ - \ - MIPSDSP_SPLIT64_32(rs, rs1, rs0); \ - MIPSDSP_SPLIT64_32(rt, rt1, rt0); \ - \ - tempB = mipsdsp_##func(rs1, rt1, env); \ - tempA = mipsdsp_##func(rs0, rt0, env); \ - \ - return MIPSDSP_RETURN64_32(tempB, tempA); \ -} - -ARITH_PW_ENV(addq, add_i32); -ARITH_PW_ENV(addq_s, sat_add_i32); -ARITH_PW_ENV(subq, sub32); -ARITH_PW_ENV(subq_s, sat32_sub); - -#undef ARITH_PW_ENV - +#define MIPSDSP64_UNOP_ENV(name, func, element) \ +target_ulong helper_##name(target_ulong rt, CPUMIPSState *env) \ +{ \ + DSP64Value dt; \ + unsigned int i, n; \ + \ + n = sizeof(DSP64Value) / sizeof(dt.element[0]); \ + dt.sl[0] = rt; \ + \ + for (i = 0; i < n; i++) { \ + dt.element[i] = mipsdsp_##func(dt.element[i], env); \ + } \ + \ + return dt.sl[0]; \ +} +MIPSDSP64_UNOP_ENV(absq_s_ob, sat_abs8, sb) +MIPSDSP64_UNOP_ENV(absq_s_qh, sat_abs16, sh) +MIPSDSP64_UNOP_ENV(absq_s_pw, sat_abs32, sw) +#undef MIPSDSP64_UNOP_ENV #endif -#define ARITH_QB(name, func) \ -target_ulong helper_##name##_qb(target_ulong rs, target_ulong rt) \ -{ \ - uint8_t rs0, rs1, rs2, rs3; \ - uint8_t rt0, rt1, rt2, rt3; \ - uint8_t temp0, temp1, temp2, temp3; \ - \ - MIPSDSP_SPLIT32_8(rs, rs3, rs2, rs1, rs0); \ - MIPSDSP_SPLIT32_8(rt, rt3, rt2, rt1, rt0); \ - \ - temp0 = mipsdsp_##func(rs0, rt0); \ - temp1 = mipsdsp_##func(rs1, rt1); \ - temp2 = mipsdsp_##func(rs2, rt2); \ - temp3 = mipsdsp_##func(rs3, rt3); \ - \ - return MIPSDSP_RETURN32_8(temp3, temp2, temp1, temp0); \ -} +#define MIPSDSP32_BINOP(name, func, element) \ +target_ulong helper_##name(target_ulong rs, target_ulong rt) \ +{ \ + DSP32Value ds, dt; \ + unsigned int i, n; \ + \ + n = sizeof(DSP32Value) / sizeof(ds.element[0]); \ + ds.sw[0] = rs; \ + dt.sw[0] = rt; \ + \ + for (i = 0; i < n; i++) { \ + ds.element[i] = mipsdsp_##func(ds.element[i], dt.element[i]); \ + } \ + \ + return (target_long)ds.sw[0]; \ +} +MIPSDSP32_BINOP(addqh_ph, rshift1_add_q16, sh); +MIPSDSP32_BINOP(addqh_r_ph, rrshift1_add_q16, sh); +MIPSDSP32_BINOP(addqh_r_w, rrshift1_add_q32, sw); +MIPSDSP32_BINOP(addqh_w, rshift1_add_q32, sw); +MIPSDSP32_BINOP(adduh_qb, rshift1_add_u8, ub); +MIPSDSP32_BINOP(adduh_r_qb, rrshift1_add_u8, ub); +MIPSDSP32_BINOP(subqh_ph, rshift1_sub_q16, sh); +MIPSDSP32_BINOP(subqh_r_ph, rrshift1_sub_q16, sh); +MIPSDSP32_BINOP(subqh_r_w, rrshift1_sub_q32, sw); +MIPSDSP32_BINOP(subqh_w, rshift1_sub_q32, sw); +#undef MIPSDSP32_BINOP + +#define MIPSDSP32_BINOP_ENV(name, func, element) \ +target_ulong helper_##name(target_ulong rs, target_ulong rt, \ + CPUMIPSState *env) \ +{ \ + DSP32Value ds, dt; \ + unsigned int i, n; \ + \ + n = sizeof(DSP32Value) / sizeof(ds.element[0]); \ + ds.sw[0] = rs; \ + dt.sw[0] = rt; \ + \ + for (i = 0 ; i < n ; i++) { \ + ds.element[i] = mipsdsp_##func(ds.element[i], dt.element[i], env); \ + } \ + \ + return (target_long)ds.sw[0]; \ +} +MIPSDSP32_BINOP_ENV(addq_ph, add_i16, sh) +MIPSDSP32_BINOP_ENV(addq_s_ph, sat_add_i16, sh) +MIPSDSP32_BINOP_ENV(addq_s_w, sat_add_i32, sw); +MIPSDSP32_BINOP_ENV(addu_ph, add_u16, sh) +MIPSDSP32_BINOP_ENV(addu_qb, add_u8, ub); +MIPSDSP32_BINOP_ENV(addu_s_ph, sat_add_u16, sh) +MIPSDSP32_BINOP_ENV(addu_s_qb, sat_add_u8, ub); +MIPSDSP32_BINOP_ENV(subq_ph, sub_i16, sh); +MIPSDSP32_BINOP_ENV(subq_s_ph, sat16_sub, sh); +MIPSDSP32_BINOP_ENV(subq_s_w, sat32_sub, sw); +MIPSDSP32_BINOP_ENV(subu_ph, sub_u16_u16, sh); +MIPSDSP32_BINOP_ENV(subu_qb, sub_u8, ub); +MIPSDSP32_BINOP_ENV(subu_s_ph, satu16_sub_u16_u16, sh); +MIPSDSP32_BINOP_ENV(subu_s_qb, satu8_sub, ub); +#undef MIPSDSP32_BINOP_ENV -#define ARITH_QB_ENV(name, func) \ -target_ulong helper_##name##_qb(target_ulong rs, target_ulong rt, \ - CPUMIPSState *env) \ -{ \ - uint8_t rs0, rs1, rs2, rs3; \ - uint8_t rt0, rt1, rt2, rt3; \ - uint8_t temp0, temp1, temp2, temp3; \ - \ - MIPSDSP_SPLIT32_8(rs, rs3, rs2, rs1, rs0); \ - MIPSDSP_SPLIT32_8(rt, rt3, rt2, rt1, rt0); \ - \ - temp0 = mipsdsp_##func(rs0, rt0, env); \ - temp1 = mipsdsp_##func(rs1, rt1, env); \ - temp2 = mipsdsp_##func(rs2, rt2, env); \ - temp3 = mipsdsp_##func(rs3, rt3, env); \ - \ - return MIPSDSP_RETURN32_8(temp3, temp2, temp1, temp0); \ -} - -ARITH_QB(adduh, rshift1_add_u8); -ARITH_QB(adduh_r, rrshift1_add_u8); - -ARITH_QB_ENV(addu, add_u8); -ARITH_QB_ENV(addu_s, sat_add_u8); - -#undef ADDU_QB -#undef ADDU_QB_ENV - -#if defined(TARGET_MIPS64) -#define ARITH_OB(name, func) \ -target_ulong helper_##name##_ob(target_ulong rs, target_ulong rt) \ -{ \ - int i; \ - uint8_t rs_t[8], rt_t[8]; \ - uint8_t temp[8]; \ - uint64_t result; \ - \ - result = 0; \ - \ - for (i = 0; i < 8; i++) { \ - rs_t[i] = (rs >> (8 * i)) & MIPSDSP_Q0; \ - rt_t[i] = (rt >> (8 * i)) & MIPSDSP_Q0; \ - temp[i] = mipsdsp_##func(rs_t[i], rt_t[i]); \ - result |= (uint64_t)temp[i] << (8 * i); \ - } \ - \ - return result; \ -} - -#define ARITH_OB_ENV(name, func) \ -target_ulong helper_##name##_ob(target_ulong rs, target_ulong rt, \ - CPUMIPSState *env) \ -{ \ - int i; \ - uint8_t rs_t[8], rt_t[8]; \ - uint8_t temp[8]; \ - uint64_t result; \ - \ - result = 0; \ - \ - for (i = 0; i < 8; i++) { \ - rs_t[i] = (rs >> (8 * i)) & MIPSDSP_Q0; \ - rt_t[i] = (rt >> (8 * i)) & MIPSDSP_Q0; \ - temp[i] = mipsdsp_##func(rs_t[i], rt_t[i], env); \ - result |= (uint64_t)temp[i] << (8 * i); \ - } \ - \ - return result; \ -} - -ARITH_OB_ENV(addu, add_u8); -ARITH_OB_ENV(addu_s, sat_add_u8); - -ARITH_OB(adduh, rshift1_add_u8); -ARITH_OB(adduh_r, rrshift1_add_u8); - -ARITH_OB_ENV(subu, sub_u8); -ARITH_OB_ENV(subu_s, satu8_sub); - -ARITH_OB(subuh, rshift1_sub_u8); -ARITH_OB(subuh_r, rrshift1_sub_u8); - -#undef ARITH_OB -#undef ARITH_OB_ENV +#ifdef TARGET_MIPS64 +#define MIPSDSP64_BINOP(name, func, element) \ +target_ulong helper_##name(target_ulong rs, target_ulong rt) \ +{ \ + DSP64Value ds, dt; \ + unsigned int i, n; \ + \ + n = sizeof(DSP64Value) / sizeof(ds.element[0]); \ + ds.sl[0] = rs; \ + dt.sl[0] = rt; \ + \ + for (i = 0 ; i < n ; i++) { \ + ds.element[i] = mipsdsp_##func(ds.element[i], dt.element[i]); \ + } \ + \ + return ds.sl[0]; \ +} +MIPSDSP64_BINOP(adduh_ob, rshift1_add_u8, ub); +MIPSDSP64_BINOP(adduh_r_ob, rrshift1_add_u8, ub); +MIPSDSP64_BINOP(subuh_ob, rshift1_sub_u8, ub); +MIPSDSP64_BINOP(subuh_r_ob, rrshift1_sub_u8, ub); +#undef MIPSDSP64_BINOP + +#define MIPSDSP64_BINOP_ENV(name, func, element) \ +target_ulong helper_##name(target_ulong rs, target_ulong rt, \ + CPUMIPSState *env) \ +{ \ + DSP64Value ds, dt; \ + unsigned int i, n; \ + \ + n = sizeof(DSP64Value) / sizeof(ds.element[0]); \ + ds.sl[0] = rs; \ + dt.sl[0] = rt; \ + \ + for (i = 0 ; i < n ; i++) { \ + ds.element[i] = mipsdsp_##func(ds.element[i], dt.element[i], env); \ + } \ + \ + return ds.sl[0]; \ +} +MIPSDSP64_BINOP_ENV(addq_pw, add_i32, sw); +MIPSDSP64_BINOP_ENV(addq_qh, add_i16, sh); +MIPSDSP64_BINOP_ENV(addq_s_pw, sat_add_i32, sw); +MIPSDSP64_BINOP_ENV(addq_s_qh, sat_add_i16, sh); +MIPSDSP64_BINOP_ENV(addu_ob, add_u8, uh); +MIPSDSP64_BINOP_ENV(addu_qh, add_u16, uh); +MIPSDSP64_BINOP_ENV(addu_s_ob, sat_add_u8, uh); +MIPSDSP64_BINOP_ENV(addu_s_qh, sat_add_u16, uh); +MIPSDSP64_BINOP_ENV(subq_pw, sub32, sw); +MIPSDSP64_BINOP_ENV(subq_qh, sub_i16, sh); +MIPSDSP64_BINOP_ENV(subq_s_pw, sat32_sub, sw); +MIPSDSP64_BINOP_ENV(subq_s_qh, sat16_sub, sh); +MIPSDSP64_BINOP_ENV(subu_ob, sub_u8, uh); +MIPSDSP64_BINOP_ENV(subu_qh, sub_u16_u16, uh); +MIPSDSP64_BINOP_ENV(subu_s_ob, satu8_sub, uh); +MIPSDSP64_BINOP_ENV(subu_s_qh, satu16_sub_u16_u16, uh); +#undef MIPSDSP64_BINOP_ENV #endif -#define SUBU_QB(name, func) \ -target_ulong helper_##name##_qb(target_ulong rs, \ - target_ulong rt, \ - CPUMIPSState *env) \ -{ \ - uint8_t rs3, rs2, rs1, rs0; \ - uint8_t rt3, rt2, rt1, rt0; \ - uint8_t tempD, tempC, tempB, tempA; \ - \ - MIPSDSP_SPLIT32_8(rs, rs3, rs2, rs1, rs0); \ - MIPSDSP_SPLIT32_8(rt, rt3, rt2, rt1, rt0); \ - \ - tempD = mipsdsp_##func(rs3, rt3, env); \ - tempC = mipsdsp_##func(rs2, rt2, env); \ - tempB = mipsdsp_##func(rs1, rt1, env); \ - tempA = mipsdsp_##func(rs0, rt0, env); \ - \ - return MIPSDSP_RETURN32_8(tempD, tempC, tempB, tempA); \ -} - -SUBU_QB(subu, sub_u8); -SUBU_QB(subu_s, satu8_sub); - -#undef SUBU_QB - #define SUBUH_QB(name, var) \ target_ulong helper_##name##_qb(target_ulong rs, target_ulong rt) \ { \ @@ -1449,103 +1352,29 @@ target_ulong helper_modsub(target_ulong rs, target_ulong rt) target_ulong helper_raddu_w_qb(target_ulong rs) { - uint8_t rs3, rs2, rs1, rs0; - uint16_t temp; + target_ulong ret = 0; + DSP32Value ds; + unsigned int i; - MIPSDSP_SPLIT32_8(rs, rs3, rs2, rs1, rs0); - - temp = (uint16_t)rs3 + (uint16_t)rs2 + (uint16_t)rs1 + (uint16_t)rs0; - - return (target_ulong)temp; -} - -#if defined(TARGET_MIPS64) -target_ulong helper_raddu_l_ob(target_ulong rs) -{ - int i; - uint16_t rs_t[8]; - uint64_t temp; - - temp = 0; - - for (i = 0; i < 8; i++) { - rs_t[i] = (rs >> (8 * i)) & MIPSDSP_Q0; - temp += (uint64_t)rs_t[i]; + ds.uw[0] = rs; + for (i = 0; i < 4; i++) { + ret += ds.ub[i]; } - - return temp; -} -#endif - -target_ulong helper_absq_s_qb(target_ulong rt, CPUMIPSState *env) -{ - uint8_t tempD, tempC, tempB, tempA; - - MIPSDSP_SPLIT32_8(rt, tempD, tempC, tempB, tempA); - - tempD = mipsdsp_sat_abs8(tempD, env); - tempC = mipsdsp_sat_abs8(tempC, env); - tempB = mipsdsp_sat_abs8(tempB, env); - tempA = mipsdsp_sat_abs8(tempA, env); - - return MIPSDSP_RETURN32_8(tempD, tempC, tempB, tempA); -} - -target_ulong helper_absq_s_ph(target_ulong rt, CPUMIPSState *env) -{ - uint16_t tempB, tempA; - - MIPSDSP_SPLIT32_16(rt, tempB, tempA); - - tempB = mipsdsp_sat_abs16 (tempB, env); - tempA = mipsdsp_sat_abs16 (tempA, env); - - return MIPSDSP_RETURN32_16(tempB, tempA); + return ret; } #if defined(TARGET_MIPS64) -target_ulong helper_absq_s_ob(target_ulong rt, CPUMIPSState *env) +target_ulong helper_raddu_l_ob(target_ulong rs) { - int i; - int8_t temp[8]; - uint64_t result; + target_ulong ret = 0; + DSP64Value ds; + unsigned int i; + ds.ul[0] = rs; for (i = 0; i < 8; i++) { - temp[i] = (rt >> (8 * i)) & MIPSDSP_Q0; - temp[i] = mipsdsp_sat_abs8(temp[i], env); + ret += ds.ub[i]; } - - for (i = 0; i < 8; i++) { - result = (uint64_t)(uint8_t)temp[i] << (8 * i); - } - - return result; -} - -target_ulong helper_absq_s_qh(target_ulong rt, CPUMIPSState *env) -{ - int16_t tempD, tempC, tempB, tempA; - - MIPSDSP_SPLIT64_16(rt, tempD, tempC, tempB, tempA); - - tempD = mipsdsp_sat_abs16(tempD, env); - tempC = mipsdsp_sat_abs16(tempC, env); - tempB = mipsdsp_sat_abs16(tempB, env); - tempA = mipsdsp_sat_abs16(tempA, env); - - return MIPSDSP_RETURN64_16(tempD, tempC, tempB, tempA); -} - -target_ulong helper_absq_s_pw(target_ulong rt, CPUMIPSState *env) -{ - int32_t tempB, tempA; - - MIPSDSP_SPLIT64_32(rt, tempB, tempA); - - tempB = mipsdsp_sat_abs32(tempB, env); - tempA = mipsdsp_sat_abs32(tempA, env); - - return MIPSDSP_RETURN64_32(tempB, tempA); + return ret; } #endif @@ -3282,73 +3111,6 @@ PICK_INSN(pick_pw, 2, MIPSDSP_LLO, 32, 0); #endif #undef PICK_INSN -#define APPEND_INSN(name, ret_32) \ -target_ulong helper_##name(target_ulong rt, target_ulong rs, uint32_t sa) \ -{ \ - target_ulong temp; \ - \ - if (ret_32) { \ - temp = ((rt & MIPSDSP_LLO) << sa) | \ - ((rs & MIPSDSP_LLO) & ((0x01 << sa) - 1)); \ - temp = (target_long)(int32_t)(temp & MIPSDSP_LLO); \ - } else { \ - temp = (rt << sa) | (rs & ((0x01 << sa) - 1)); \ - } \ - \ - return temp; \ -} - -APPEND_INSN(append, 1); -#ifdef TARGET_MIPS64 -APPEND_INSN(dappend, 0); -#endif -#undef APPEND_INSN - -#define PREPEND_INSN(name, or_val, ret_32) \ -target_ulong helper_##name(target_ulong rs, target_ulong rt, \ - uint32_t sa) \ -{ \ - sa |= or_val; \ - \ - if (1) { \ - return (target_long)(int32_t)(uint32_t) \ - (((rs & MIPSDSP_LLO) << (32 - sa)) | \ - ((rt & MIPSDSP_LLO) >> sa)); \ - } else { \ - return (rs << (64 - sa)) | (rt >> sa); \ - } \ -} - -PREPEND_INSN(prepend, 0, 1); -#ifdef TARGET_MIPS64 -PREPEND_INSN(prependw, 0, 0); -PREPEND_INSN(prependd, 0x20, 0); -#endif -#undef PREPEND_INSN - -#define BALIGN_INSN(name, filter, ret32) \ -target_ulong helper_##name(target_ulong rs, target_ulong rt, uint32_t bp) \ -{ \ - bp = bp & 0x03; \ - \ - if ((bp & 1) == 0) { \ - return rt; \ - } else { \ - if (ret32) { \ - return (target_long)(int32_t)((rt << (8 * bp)) | \ - (rs >> (8 * (4 - bp)))); \ - } else { \ - return (rt << (8 * bp)) | (rs >> (8 * (8 - bp))); \ - } \ - } \ -} - -BALIGN_INSN(balign, 0x03, 1); -#if defined(TARGET_MIPS64) -BALIGN_INSN(dbalign, 0x07, 0); -#endif -#undef BALIGN_INSN - target_ulong helper_packrl_ph(target_ulong rs, target_ulong rt) { uint32_t rsl, rth; @@ -4005,7 +3767,6 @@ target_ulong helper_rddsp(target_ulong masknum, CPUMIPSState *env) #undef MIPSDSP_SPLIT32_8 #undef MIPSDSP_SPLIT32_16 -#undef MIPSDSP_RETURN32 #undef MIPSDSP_RETURN32_8 #undef MIPSDSP_RETURN32_16 diff --git a/target-mips/helper.h b/target-mips/helper.h index 9ea60ec1bb..cd48738ff9 100644 --- a/target-mips/helper.h +++ b/target-mips/helper.h @@ -654,19 +654,6 @@ DEF_HELPER_FLAGS_3(pick_ob, 0, tl, tl, tl, env) DEF_HELPER_FLAGS_3(pick_qh, 0, tl, tl, tl, env) DEF_HELPER_FLAGS_3(pick_pw, 0, tl, tl, tl, env) #endif -DEF_HELPER_FLAGS_3(append, TCG_CALL_NO_RWG_SE, tl, tl, tl, i32) -#if defined(TARGET_MIPS64) -DEF_HELPER_FLAGS_3(dappend, TCG_CALL_NO_RWG_SE, tl, tl, tl, i32) -#endif -DEF_HELPER_FLAGS_3(prepend, TCG_CALL_NO_RWG_SE, tl, tl, tl, i32) -#if defined(TARGET_MIPS64) -DEF_HELPER_FLAGS_3(prependd, TCG_CALL_NO_RWG_SE, tl, tl, tl, i32) -DEF_HELPER_FLAGS_3(prependw, TCG_CALL_NO_RWG_SE, tl, tl, tl, i32) -#endif -DEF_HELPER_FLAGS_3(balign, TCG_CALL_NO_RWG_SE, tl, tl, tl, i32) -#if defined(TARGET_MIPS64) -DEF_HELPER_FLAGS_3(dbalign, TCG_CALL_NO_RWG_SE, tl, tl, tl, i32) -#endif DEF_HELPER_FLAGS_2(packrl_ph, TCG_CALL_NO_RWG_SE, tl, tl, tl) #if defined(TARGET_MIPS64) DEF_HELPER_FLAGS_2(packrl_pw, TCG_CALL_NO_RWG_SE, tl, tl, tl) diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c index 1bca4a159e..526f84f136 100644 --- a/target-mips/op_helper.c +++ b/target-mips/op_helper.c @@ -2878,14 +2878,26 @@ FLOAT_BINOP(mul) FLOAT_BINOP(div) #undef FLOAT_BINOP +#define UNFUSED_FMA(prefix, a, b, c, flags) \ +{ \ + a = prefix##_mul(a, b, &env->active_fpu.fp_status); \ + if ((flags) & float_muladd_negate_c) { \ + a = prefix##_sub(a, c, &env->active_fpu.fp_status); \ + } else { \ + a = prefix##_add(a, c, &env->active_fpu.fp_status); \ + } \ + if ((flags) & float_muladd_negate_result) { \ + a = prefix##_chs(a); \ + } \ +} + /* FMA based operations */ #define FLOAT_FMA(name, type) \ uint64_t helper_float_ ## name ## _d(CPUMIPSState *env, \ uint64_t fdt0, uint64_t fdt1, \ uint64_t fdt2) \ { \ - fdt0 = float64_muladd(fdt0, fdt1, fdt2, type, \ - &env->active_fpu.fp_status); \ + UNFUSED_FMA(float64, fdt0, fdt1, fdt2, type); \ update_fcr31(env, GETPC()); \ return fdt0; \ } \ @@ -2894,8 +2906,7 @@ uint32_t helper_float_ ## name ## _s(CPUMIPSState *env, \ uint32_t fst0, uint32_t fst1, \ uint32_t fst2) \ { \ - fst0 = float32_muladd(fst0, fst1, fst2, type, \ - &env->active_fpu.fp_status); \ + UNFUSED_FMA(float32, fst0, fst1, fst2, type); \ update_fcr31(env, GETPC()); \ return fst0; \ } \ @@ -2911,10 +2922,8 @@ uint64_t helper_float_ ## name ## _ps(CPUMIPSState *env, \ uint32_t fst2 = fdt2 & 0XFFFFFFFF; \ uint32_t fsth2 = fdt2 >> 32; \ \ - fst0 = float32_muladd(fst0, fst1, fst2, type, \ - &env->active_fpu.fp_status); \ - fsth0 = float32_muladd(fsth0, fsth1, fsth2, type, \ - &env->active_fpu.fp_status); \ + UNFUSED_FMA(float32, fst0, fst1, fst2, type); \ + UNFUSED_FMA(float32, fsth0, fsth1, fsth2, type); \ update_fcr31(env, GETPC()); \ return ((uint64_t)fsth0 << 32) | fst0; \ } diff --git a/target-mips/translate.c b/target-mips/translate.c index 206ba83401..3b77b53b93 100644 --- a/target-mips/translate.c +++ b/target-mips/translate.c @@ -336,7 +336,7 @@ enum { /* DSP Bit/Manipulation Sub-class */ OPC_INSV_DSP = 0x0C | OPC_SPECIAL3, OPC_DINSV_DSP = 0x0D | OPC_SPECIAL3, - /* MIPS DSP Compare-Pick Sub-class */ + /* MIPS DSP Append Sub-class */ OPC_APPEND_DSP = 0x31 | OPC_SPECIAL3, OPC_DAPPEND_DSP = 0x35 | OPC_SPECIAL3, /* MIPS DSP Accumulator and DSPControl Access Sub-class */ @@ -543,7 +543,7 @@ enum { #define MASK_APPEND(op) (MASK_SPECIAL3(op) | (op & (0x1F << 6))) enum { - /* MIPS DSP Compare-Pick Sub-class */ + /* MIPS DSP Append Sub-class */ OPC_APPEND = (0x00 << 6) | OPC_APPEND_DSP, OPC_PREPEND = (0x01 << 6) | OPC_APPEND_DSP, OPC_BALIGN = (0x10 << 6) | OPC_APPEND_DSP, @@ -667,7 +667,7 @@ enum { #define MASK_DAPPEND(op) (MASK_SPECIAL3(op) | (op & (0x1F << 6))) enum { - /* DSP Compare-Pick Sub-class */ + /* DSP Append Sub-class */ OPC_DAPPEND = (0x00 << 6) | OPC_DAPPEND_DSP, OPC_PREPENDD = (0x03 << 6) | OPC_DAPPEND_DSP, OPC_PREPENDW = (0x01 << 6) | OPC_DAPPEND_DSP, @@ -1066,6 +1066,7 @@ typedef struct DisasContext { target_ulong pc, saved_pc; uint32_t opcode; int singlestep_enabled; + int insn_flags; /* Routine used to access memory */ int mem_idx; uint32_t hflags, saved_hflags; @@ -1393,23 +1394,32 @@ static inline void check_cp1_registers(DisasContext *ctx, int regs) static inline void check_dsp(DisasContext *ctx) { if (unlikely(!(ctx->hflags & MIPS_HFLAG_DSP))) { - generate_exception(ctx, EXCP_DSPDIS); + if (ctx->insn_flags & ASE_DSP) { + generate_exception(ctx, EXCP_DSPDIS); + } else { + generate_exception(ctx, EXCP_RI); + } } } static inline void check_dspr2(DisasContext *ctx) { if (unlikely(!(ctx->hflags & MIPS_HFLAG_DSPR2))) { - generate_exception(ctx, EXCP_DSPDIS); + if (ctx->insn_flags & ASE_DSP) { + generate_exception(ctx, EXCP_DSPDIS); + } else { + generate_exception(ctx, EXCP_RI); + } } } /* This code generates a "reserved instruction" exception if the CPU does not support the instruction set corresponding to flags. */ -static inline void check_insn(CPUMIPSState *env, DisasContext *ctx, int flags) +static inline void check_insn(DisasContext *ctx, int flags) { - if (unlikely(!(env->insn_flags & flags))) + if (unlikely(!(ctx->insn_flags & flags))) { generate_exception(ctx, EXCP_RI); + } } /* This code generates a "reserved instruction" exception if 64-bit @@ -1576,13 +1586,13 @@ static target_ulong pc_relative_pc (DisasContext *ctx) } /* Load */ -static void gen_ld (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, - int rt, int base, int16_t offset) +static void gen_ld(DisasContext *ctx, uint32_t opc, + int rt, int base, int16_t offset) { const char *opn = "ld"; TCGv t0, t1, t2; - if (rt == 0 && env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)) { + if (rt == 0 && ctx->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)) { /* Loongson CPU uses a load to zero register for prefetch. We emulate it as a NOP. On other CPU we must perform the actual memory access. */ @@ -1735,6 +1745,7 @@ static void gen_ld (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, tcg_temp_free(t2); tcg_gen_or_tl(t0, t0, t1); tcg_temp_free(t1); + tcg_gen_ext32s_tl(t0, t0); gen_store_gpr(t0, rt); opn = "lwr"; break; @@ -1921,8 +1932,8 @@ static void gen_cop1_ldst(CPUMIPSState *env, DisasContext *ctx, } /* Arithmetic with immediate operand */ -static void gen_arith_imm (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, - int rt, int rs, int16_t imm) +static void gen_arith_imm(DisasContext *ctx, uint32_t opc, + int rt, int rs, int16_t imm) { target_ulong uimm = (target_long)imm; /* Sign extend to 32/64 bits */ const char *opn = "imm arith"; @@ -2009,7 +2020,7 @@ static void gen_arith_imm (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, } /* Logic with immediate operand */ -static void gen_logic_imm(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, +static void gen_logic_imm(DisasContext *ctx, uint32_t opc, int rt, int rs, int16_t imm) { target_ulong uimm; @@ -2057,7 +2068,7 @@ static void gen_logic_imm(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, } /* Set on less than with immediate operand */ -static void gen_slt_imm(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, +static void gen_slt_imm(DisasContext *ctx, uint32_t opc, int rt, int rs, int16_t imm) { target_ulong uimm = (target_long)imm; /* Sign extend to 32/64 bits */ @@ -2087,7 +2098,7 @@ static void gen_slt_imm(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, } /* Shifts with immediate operand */ -static void gen_shift_imm(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, +static void gen_shift_imm(DisasContext *ctx, uint32_t opc, int rt, int rs, int16_t imm) { target_ulong uimm = ((uint16_t)imm) & 0x1f; @@ -2179,8 +2190,8 @@ static void gen_shift_imm(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, } /* Arithmetic */ -static void gen_arith (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, - int rd, int rs, int rt) +static void gen_arith(DisasContext *ctx, uint32_t opc, + int rd, int rs, int rt) { const char *opn = "arith"; @@ -2359,7 +2370,7 @@ static void gen_arith (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, } /* Conditional move */ -static void gen_cond_move(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, +static void gen_cond_move(DisasContext *ctx, uint32_t opc, int rd, int rs, int rt) { const char *opn = "cond move"; @@ -2395,7 +2406,7 @@ static void gen_cond_move(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, } /* Logic */ -static void gen_logic(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, +static void gen_logic(DisasContext *ctx, uint32_t opc, int rd, int rs, int rt) { const char *opn = "logic"; @@ -2457,7 +2468,7 @@ static void gen_logic(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, } /* Set on lower than */ -static void gen_slt(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, +static void gen_slt(DisasContext *ctx, uint32_t opc, int rd, int rs, int rt) { const char *opn = "slt"; @@ -2490,8 +2501,8 @@ static void gen_slt(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, } /* Shifts */ -static void gen_shift (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, - int rd, int rs, int rt) +static void gen_shift(DisasContext *ctx, uint32_t opc, + int rd, int rs, int rt) { const char *opn = "shifts"; TCGv t0, t1; @@ -4097,12 +4108,12 @@ static inline void gen_mtc0_store64 (TCGv arg, target_ulong off) tcg_gen_st_tl(arg, cpu_env, off); } -static void gen_mfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, int sel) +static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) { const char *rn = "invalid"; if (sel != 0) - check_insn(env, ctx, ISA_MIPS32); + check_insn(ctx, ISA_MIPS32); switch (reg) { case 0: @@ -4112,17 +4123,17 @@ static void gen_mfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i rn = "Index"; break; case 1: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mfc0_mvpcontrol(arg, cpu_env); rn = "MVPControl"; break; case 2: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mfc0_mvpconf0(arg, cpu_env); rn = "MVPConf0"; break; case 3: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mfc0_mvpconf1(arg, cpu_env); rn = "MVPConf1"; break; @@ -4137,37 +4148,37 @@ static void gen_mfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i rn = "Random"; break; case 1: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEControl)); rn = "VPEControl"; break; case 2: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEConf0)); rn = "VPEConf0"; break; case 3: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEConf1)); rn = "VPEConf1"; break; case 4: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_mfc0_load64(arg, offsetof(CPUMIPSState, CP0_YQMask)); rn = "YQMask"; break; case 5: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_mfc0_load64(arg, offsetof(CPUMIPSState, CP0_VPESchedule)); rn = "VPESchedule"; break; case 6: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_mfc0_load64(arg, offsetof(CPUMIPSState, CP0_VPEScheFBack)); rn = "VPEScheFBack"; break; case 7: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEOpt)); rn = "VPEOpt"; break; @@ -4183,37 +4194,37 @@ static void gen_mfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i rn = "EntryLo0"; break; case 1: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mfc0_tcstatus(arg, cpu_env); rn = "TCStatus"; break; case 2: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mfc0_tcbind(arg, cpu_env); rn = "TCBind"; break; case 3: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mfc0_tcrestart(arg, cpu_env); rn = "TCRestart"; break; case 4: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mfc0_tchalt(arg, cpu_env); rn = "TCHalt"; break; case 5: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mfc0_tccontext(arg, cpu_env); rn = "TCContext"; break; case 6: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mfc0_tcschedule(arg, cpu_env); rn = "TCSchedule"; break; case 7: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mfc0_tcschefback(arg, cpu_env); rn = "TCScheFBack"; break; @@ -4254,7 +4265,7 @@ static void gen_mfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i rn = "PageMask"; break; case 1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_PageGrain)); rn = "PageGrain"; break; @@ -4269,27 +4280,27 @@ static void gen_mfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i rn = "Wired"; break; case 1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf0)); rn = "SRSConf0"; break; case 2: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf1)); rn = "SRSConf1"; break; case 3: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf2)); rn = "SRSConf2"; break; case 4: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf3)); rn = "SRSConf3"; break; case 5: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf4)); rn = "SRSConf4"; break; @@ -4300,7 +4311,7 @@ static void gen_mfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i case 7: switch (sel) { case 0: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_HWREna)); rn = "HWREna"; break; @@ -4368,17 +4379,17 @@ static void gen_mfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i rn = "Status"; break; case 1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_IntCtl)); rn = "IntCtl"; break; case 2: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSCtl)); rn = "SRSCtl"; break; case 3: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSMap)); rn = "SRSMap"; break; @@ -4414,7 +4425,7 @@ static void gen_mfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i rn = "PRid"; break; case 1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_EBase)); rn = "EBase"; break; @@ -4488,7 +4499,7 @@ static void gen_mfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i switch (sel) { case 0: #if defined(TARGET_MIPS64) - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_XContext)); tcg_gen_ext32s_tl(arg, arg); rn = "XContext"; @@ -4677,12 +4688,12 @@ die: generate_exception(ctx, EXCP_RI); } -static void gen_mtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, int sel) +static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) { const char *rn = "invalid"; if (sel != 0) - check_insn(env, ctx, ISA_MIPS32); + check_insn(ctx, ISA_MIPS32); if (use_icount) gen_io_start(); @@ -4695,17 +4706,17 @@ static void gen_mtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i rn = "Index"; break; case 1: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_mvpcontrol(cpu_env, arg); rn = "MVPControl"; break; case 2: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); /* ignored */ rn = "MVPConf0"; break; case 3: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); /* ignored */ rn = "MVPConf1"; break; @@ -4720,37 +4731,37 @@ static void gen_mtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i rn = "Random"; break; case 1: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_vpecontrol(cpu_env, arg); rn = "VPEControl"; break; case 2: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_vpeconf0(cpu_env, arg); rn = "VPEConf0"; break; case 3: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_vpeconf1(cpu_env, arg); rn = "VPEConf1"; break; case 4: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_yqmask(cpu_env, arg); rn = "YQMask"; break; case 5: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_mtc0_store64(arg, offsetof(CPUMIPSState, CP0_VPESchedule)); rn = "VPESchedule"; break; case 6: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_mtc0_store64(arg, offsetof(CPUMIPSState, CP0_VPEScheFBack)); rn = "VPEScheFBack"; break; case 7: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_vpeopt(cpu_env, arg); rn = "VPEOpt"; break; @@ -4765,37 +4776,37 @@ static void gen_mtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i rn = "EntryLo0"; break; case 1: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_tcstatus(cpu_env, arg); rn = "TCStatus"; break; case 2: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_tcbind(cpu_env, arg); rn = "TCBind"; break; case 3: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_tcrestart(cpu_env, arg); rn = "TCRestart"; break; case 4: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_tchalt(cpu_env, arg); rn = "TCHalt"; break; case 5: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_tccontext(cpu_env, arg); rn = "TCContext"; break; case 6: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_tcschedule(cpu_env, arg); rn = "TCSchedule"; break; case 7: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_tcschefback(cpu_env, arg); rn = "TCScheFBack"; break; @@ -4834,7 +4845,7 @@ static void gen_mtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i rn = "PageMask"; break; case 1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_pagegrain(cpu_env, arg); rn = "PageGrain"; break; @@ -4849,27 +4860,27 @@ static void gen_mtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i rn = "Wired"; break; case 1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsconf0(cpu_env, arg); rn = "SRSConf0"; break; case 2: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsconf1(cpu_env, arg); rn = "SRSConf1"; break; case 3: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsconf2(cpu_env, arg); rn = "SRSConf2"; break; case 4: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsconf3(cpu_env, arg); rn = "SRSConf3"; break; case 5: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsconf4(cpu_env, arg); rn = "SRSConf4"; break; @@ -4880,7 +4891,7 @@ static void gen_mtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i case 7: switch (sel) { case 0: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_hwrena(cpu_env, arg); rn = "HWREna"; break; @@ -4935,21 +4946,21 @@ static void gen_mtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i rn = "Status"; break; case 1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_intctl(cpu_env, arg); /* Stop translation as we may have switched the execution mode */ ctx->bstate = BS_STOP; rn = "IntCtl"; break; case 2: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsctl(cpu_env, arg); /* Stop translation as we may have switched the execution mode */ ctx->bstate = BS_STOP; rn = "SRSCtl"; break; case 3: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mtc0_store32(arg, offsetof(CPUMIPSState, CP0_SRSMap)); /* Stop translation as we may have switched the execution mode */ ctx->bstate = BS_STOP; @@ -4987,7 +4998,7 @@ static void gen_mtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i rn = "PRid"; break; case 1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_ebase(cpu_env, arg); rn = "EBase"; break; @@ -5066,7 +5077,7 @@ static void gen_mtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i switch (sel) { case 0: #if defined(TARGET_MIPS64) - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); gen_helper_mtc0_xcontext(cpu_env, arg); rn = "XContext"; break; @@ -5274,12 +5285,12 @@ die: } #if defined(TARGET_MIPS64) -static void gen_dmfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, int sel) +static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) { const char *rn = "invalid"; if (sel != 0) - check_insn(env, ctx, ISA_MIPS64); + check_insn(ctx, ISA_MIPS64); switch (reg) { case 0: @@ -5289,17 +5300,17 @@ static void gen_dmfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, rn = "Index"; break; case 1: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mfc0_mvpcontrol(arg, cpu_env); rn = "MVPControl"; break; case 2: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mfc0_mvpconf0(arg, cpu_env); rn = "MVPConf0"; break; case 3: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mfc0_mvpconf1(arg, cpu_env); rn = "MVPConf1"; break; @@ -5314,37 +5325,37 @@ static void gen_dmfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, rn = "Random"; break; case 1: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEControl)); rn = "VPEControl"; break; case 2: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEConf0)); rn = "VPEConf0"; break; case 3: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEConf1)); rn = "VPEConf1"; break; case 4: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_YQMask)); rn = "YQMask"; break; case 5: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_VPESchedule)); rn = "VPESchedule"; break; case 6: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_VPEScheFBack)); rn = "VPEScheFBack"; break; case 7: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEOpt)); rn = "VPEOpt"; break; @@ -5359,37 +5370,37 @@ static void gen_dmfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, rn = "EntryLo0"; break; case 1: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mfc0_tcstatus(arg, cpu_env); rn = "TCStatus"; break; case 2: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mfc0_tcbind(arg, cpu_env); rn = "TCBind"; break; case 3: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_dmfc0_tcrestart(arg, cpu_env); rn = "TCRestart"; break; case 4: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_dmfc0_tchalt(arg, cpu_env); rn = "TCHalt"; break; case 5: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_dmfc0_tccontext(arg, cpu_env); rn = "TCContext"; break; case 6: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_dmfc0_tcschedule(arg, cpu_env); rn = "TCSchedule"; break; case 7: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_dmfc0_tcschefback(arg, cpu_env); rn = "TCScheFBack"; break; @@ -5428,7 +5439,7 @@ static void gen_dmfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, rn = "PageMask"; break; case 1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_PageGrain)); rn = "PageGrain"; break; @@ -5443,27 +5454,27 @@ static void gen_dmfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, rn = "Wired"; break; case 1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf0)); rn = "SRSConf0"; break; case 2: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf1)); rn = "SRSConf1"; break; case 3: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf2)); rn = "SRSConf2"; break; case 4: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf3)); rn = "SRSConf3"; break; case 5: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf4)); rn = "SRSConf4"; break; @@ -5474,7 +5485,7 @@ static void gen_dmfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, case 7: switch (sel) { case 0: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_HWREna)); rn = "HWREna"; break; @@ -5540,17 +5551,17 @@ static void gen_dmfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, rn = "Status"; break; case 1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_IntCtl)); rn = "IntCtl"; break; case 2: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSCtl)); rn = "SRSCtl"; break; case 3: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSMap)); rn = "SRSMap"; break; @@ -5585,7 +5596,7 @@ static void gen_dmfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, rn = "PRid"; break; case 1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_EBase)); rn = "EBase"; break; @@ -5657,7 +5668,7 @@ static void gen_dmfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, case 20: switch (sel) { case 0: - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_XContext)); rn = "XContext"; break; @@ -5843,12 +5854,12 @@ die: generate_exception(ctx, EXCP_RI); } -static void gen_dmtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, int sel) +static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) { const char *rn = "invalid"; if (sel != 0) - check_insn(env, ctx, ISA_MIPS64); + check_insn(ctx, ISA_MIPS64); if (use_icount) gen_io_start(); @@ -5861,17 +5872,17 @@ static void gen_dmtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, rn = "Index"; break; case 1: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_mvpcontrol(cpu_env, arg); rn = "MVPControl"; break; case 2: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); /* ignored */ rn = "MVPConf0"; break; case 3: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); /* ignored */ rn = "MVPConf1"; break; @@ -5886,37 +5897,37 @@ static void gen_dmtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, rn = "Random"; break; case 1: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_vpecontrol(cpu_env, arg); rn = "VPEControl"; break; case 2: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_vpeconf0(cpu_env, arg); rn = "VPEConf0"; break; case 3: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_vpeconf1(cpu_env, arg); rn = "VPEConf1"; break; case 4: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_yqmask(cpu_env, arg); rn = "YQMask"; break; case 5: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_VPESchedule)); rn = "VPESchedule"; break; case 6: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_VPEScheFBack)); rn = "VPEScheFBack"; break; case 7: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_vpeopt(cpu_env, arg); rn = "VPEOpt"; break; @@ -5931,37 +5942,37 @@ static void gen_dmtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, rn = "EntryLo0"; break; case 1: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_tcstatus(cpu_env, arg); rn = "TCStatus"; break; case 2: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_tcbind(cpu_env, arg); rn = "TCBind"; break; case 3: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_tcrestart(cpu_env, arg); rn = "TCRestart"; break; case 4: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_tchalt(cpu_env, arg); rn = "TCHalt"; break; case 5: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_tccontext(cpu_env, arg); rn = "TCContext"; break; case 6: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_tcschedule(cpu_env, arg); rn = "TCSchedule"; break; case 7: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_tcschefback(cpu_env, arg); rn = "TCScheFBack"; break; @@ -6000,7 +6011,7 @@ static void gen_dmtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, rn = "PageMask"; break; case 1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_pagegrain(cpu_env, arg); rn = "PageGrain"; break; @@ -6015,27 +6026,27 @@ static void gen_dmtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, rn = "Wired"; break; case 1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsconf0(cpu_env, arg); rn = "SRSConf0"; break; case 2: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsconf1(cpu_env, arg); rn = "SRSConf1"; break; case 3: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsconf2(cpu_env, arg); rn = "SRSConf2"; break; case 4: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsconf3(cpu_env, arg); rn = "SRSConf3"; break; case 5: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsconf4(cpu_env, arg); rn = "SRSConf4"; break; @@ -6046,7 +6057,7 @@ static void gen_dmtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, case 7: switch (sel) { case 0: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_hwrena(cpu_env, arg); rn = "HWREna"; break; @@ -6105,21 +6116,21 @@ static void gen_dmtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, rn = "Status"; break; case 1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_intctl(cpu_env, arg); /* Stop translation as we may have switched the execution mode */ ctx->bstate = BS_STOP; rn = "IntCtl"; break; case 2: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsctl(cpu_env, arg); /* Stop translation as we may have switched the execution mode */ ctx->bstate = BS_STOP; rn = "SRSCtl"; break; case 3: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mtc0_store32(arg, offsetof(CPUMIPSState, CP0_SRSMap)); /* Stop translation as we may have switched the execution mode */ ctx->bstate = BS_STOP; @@ -6167,7 +6178,7 @@ static void gen_dmtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, rn = "PRid"; break; case 1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_ebase(cpu_env, arg); rn = "EBase"; break; @@ -6236,7 +6247,7 @@ static void gen_dmtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, case 20: switch (sel) { case 0: - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); gen_helper_mtc0_xcontext(cpu_env, arg); rn = "XContext"; break; @@ -6493,7 +6504,7 @@ static void gen_mftr(CPUMIPSState *env, DisasContext *ctx, int rt, int rd, gen_helper_mftc0_tcschefback(t0, cpu_env); break; default: - gen_mfc0(env, ctx, t0, rt, sel); + gen_mfc0(ctx, t0, rt, sel); break; } break; @@ -6503,7 +6514,7 @@ static void gen_mftr(CPUMIPSState *env, DisasContext *ctx, int rt, int rd, gen_helper_mftc0_entryhi(t0, cpu_env); break; default: - gen_mfc0(env, ctx, t0, rt, sel); + gen_mfc0(ctx, t0, rt, sel); break; } case 12: @@ -6512,7 +6523,7 @@ static void gen_mftr(CPUMIPSState *env, DisasContext *ctx, int rt, int rd, gen_helper_mftc0_status(t0, cpu_env); break; default: - gen_mfc0(env, ctx, t0, rt, sel); + gen_mfc0(ctx, t0, rt, sel); break; } case 13: @@ -6561,12 +6572,12 @@ static void gen_mftr(CPUMIPSState *env, DisasContext *ctx, int rt, int rd, gen_helper_mftc0_debug(t0, cpu_env); break; default: - gen_mfc0(env, ctx, t0, rt, sel); + gen_mfc0(ctx, t0, rt, sel); break; } break; default: - gen_mfc0(env, ctx, t0, rt, sel); + gen_mfc0(ctx, t0, rt, sel); } } else switch (sel) { /* GPR registers. */ @@ -6711,7 +6722,7 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, gen_helper_mttc0_tcschefback(cpu_env, t0); break; default: - gen_mtc0(env, ctx, t0, rd, sel); + gen_mtc0(ctx, t0, rd, sel); break; } break; @@ -6721,7 +6732,7 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, gen_helper_mttc0_entryhi(cpu_env, t0); break; default: - gen_mtc0(env, ctx, t0, rd, sel); + gen_mtc0(ctx, t0, rd, sel); break; } case 12: @@ -6730,7 +6741,7 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, gen_helper_mttc0_status(cpu_env, t0); break; default: - gen_mtc0(env, ctx, t0, rd, sel); + gen_mtc0(ctx, t0, rd, sel); break; } case 13: @@ -6759,12 +6770,12 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, gen_helper_mttc0_debug(cpu_env, t0); break; default: - gen_mtc0(env, ctx, t0, rd, sel); + gen_mtc0(ctx, t0, rd, sel); break; } break; default: - gen_mtc0(env, ctx, t0, rd, sel); + gen_mtc0(ctx, t0, rd, sel); } } else switch (sel) { /* GPR registers. */ @@ -6866,7 +6877,7 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, /* Treat as NOP. */ return; } - gen_mfc0(env, ctx, cpu_gpr[rt], rd, ctx->opcode & 0x7); + gen_mfc0(ctx, cpu_gpr[rt], rd, ctx->opcode & 0x7); opn = "mfc0"; break; case OPC_MTC0: @@ -6874,35 +6885,35 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, TCGv t0 = tcg_temp_new(); gen_load_gpr(t0, rt); - gen_mtc0(env, ctx, t0, rd, ctx->opcode & 0x7); + gen_mtc0(ctx, t0, rd, ctx->opcode & 0x7); tcg_temp_free(t0); } opn = "mtc0"; break; #if defined(TARGET_MIPS64) case OPC_DMFC0: - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); if (rt == 0) { /* Treat as NOP. */ return; } - gen_dmfc0(env, ctx, cpu_gpr[rt], rd, ctx->opcode & 0x7); + gen_dmfc0(ctx, cpu_gpr[rt], rd, ctx->opcode & 0x7); opn = "dmfc0"; break; case OPC_DMTC0: - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); { TCGv t0 = tcg_temp_new(); gen_load_gpr(t0, rt); - gen_dmtc0(env, ctx, t0, rd, ctx->opcode & 0x7); + gen_dmtc0(ctx, t0, rd, ctx->opcode & 0x7); tcg_temp_free(t0); } opn = "dmtc0"; break; #endif case OPC_MFTR: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); if (rd == 0) { /* Treat as NOP. */ return; @@ -6912,7 +6923,7 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, opn = "mftr"; break; case OPC_MTTR: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_mttr(env, ctx, rd, rt, (ctx->opcode >> 5) & 1, ctx->opcode & 0x7, (ctx->opcode >> 4) & 1); opn = "mttr"; @@ -6943,13 +6954,13 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, break; case OPC_ERET: opn = "eret"; - check_insn(env, ctx, ISA_MIPS2); + check_insn(ctx, ISA_MIPS2); gen_helper_eret(cpu_env); ctx->bstate = BS_EXCP; break; case OPC_DERET: opn = "deret"; - check_insn(env, ctx, ISA_MIPS32); + check_insn(ctx, ISA_MIPS32); if (!(ctx->hflags & MIPS_HFLAG_DM)) { MIPS_INVAL(opn); generate_exception(ctx, EXCP_RI); @@ -6960,7 +6971,7 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, break; case OPC_WAIT: opn = "wait"; - check_insn(env, ctx, ISA_MIPS3 | ISA_MIPS32); + check_insn(ctx, ISA_MIPS3 | ISA_MIPS32); /* If we get an exception, we want to restart at next instruction */ ctx->pc += 4; save_cpu_state(ctx, 1); @@ -6980,15 +6991,15 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, #endif /* !CONFIG_USER_ONLY */ /* CP1 Branches (before delay slot) */ -static void gen_compute_branch1 (CPUMIPSState *env, DisasContext *ctx, uint32_t op, - int32_t cc, int32_t offset) +static void gen_compute_branch1(DisasContext *ctx, uint32_t op, + int32_t cc, int32_t offset) { target_ulong btarget; const char *opn = "cp1 cond branch"; TCGv_i32 t0 = tcg_temp_new_i32(); if (cc != 0) - check_insn(env, ctx, ISA_MIPS4 | ISA_MIPS32); + check_insn(ctx, ISA_MIPS4 | ISA_MIPS32); btarget = ctx->pc + 4 + offset; @@ -9032,15 +9043,14 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, fregnames[fs], fregnames[ft]); } -static void -gen_rdhwr (CPUMIPSState *env, DisasContext *ctx, int rt, int rd) +static void gen_rdhwr(DisasContext *ctx, int rt, int rd) { TCGv t0; #if !defined(CONFIG_USER_ONLY) /* The Linux kernel will emulate rdhwr if it's not supported natively. Therefore only check the ISA in system mode. */ - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); #endif t0 = tcg_temp_new(); @@ -9082,8 +9092,7 @@ gen_rdhwr (CPUMIPSState *env, DisasContext *ctx, int rt, int rd) tcg_temp_free(t0); } -static void handle_delay_slot (CPUMIPSState *env, DisasContext *ctx, - int insn_bytes) +static void handle_delay_slot(DisasContext *ctx, int insn_bytes) { if (ctx->hflags & MIPS_HFLAG_BMASK) { int proc_hflags = ctx->hflags & MIPS_HFLAG_BMASK; @@ -9121,7 +9130,7 @@ static void handle_delay_slot (CPUMIPSState *env, DisasContext *ctx, case MIPS_HFLAG_BR: /* unconditional branch to register */ MIPS_DEBUG("branch to register"); - if (env->insn_flags & (ASE_MIPS16 | ASE_MICROMIPS)) { + if (ctx->insn_flags & (ASE_MIPS16 | ASE_MICROMIPS)) { TCGv t0 = tcg_temp_new(); TCGv_i32 t1 = tcg_temp_new_i32(); @@ -9438,7 +9447,7 @@ static void gen_mips16_restore (DisasContext *ctx, #define DECR_AND_LOAD(reg) do { \ tcg_gen_subi_tl(t0, t0, 4); \ - tcg_gen_qemu_ld32u(t1, t0, ctx->mem_idx); \ + tcg_gen_qemu_ld32s(t1, t0, ctx->mem_idx); \ gen_store_gpr(t1, reg); \ } while (0) @@ -9548,7 +9557,7 @@ static void gen_addiupc (DisasContext *ctx, int rx, int imm, } #if defined(TARGET_MIPS64) -static void decode_i64_mips16 (CPUMIPSState *env, DisasContext *ctx, +static void decode_i64_mips16 (DisasContext *ctx, int ry, int funct, int16_t offset, int extended) { @@ -9556,7 +9565,7 @@ static void decode_i64_mips16 (CPUMIPSState *env, DisasContext *ctx, case I64_LDSP: check_mips_64(ctx); offset = extended ? offset : offset << 3; - gen_ld(env, ctx, OPC_LD, ry, 29, offset); + gen_ld(ctx, OPC_LD, ry, 29, offset); break; case I64_SDSP: check_mips_64(ctx); @@ -9571,20 +9580,20 @@ static void decode_i64_mips16 (CPUMIPSState *env, DisasContext *ctx, case I64_DADJSP: check_mips_64(ctx); offset = extended ? offset : ((int8_t)ctx->opcode) << 3; - gen_arith_imm(env, ctx, OPC_DADDIU, 29, 29, offset); + gen_arith_imm(ctx, OPC_DADDIU, 29, 29, offset); break; case I64_LDPC: if (extended && (ctx->hflags & MIPS_HFLAG_BMASK)) { generate_exception(ctx, EXCP_RI); } else { offset = extended ? offset : offset << 3; - gen_ld(env, ctx, OPC_LDPC, ry, 0, offset); + gen_ld(ctx, OPC_LDPC, ry, 0, offset); } break; case I64_DADDIU5: check_mips_64(ctx); offset = extended ? offset : ((int8_t)(offset << 3)) >> 3; - gen_arith_imm(env, ctx, OPC_DADDIU, ry, ry, offset); + gen_arith_imm(ctx, OPC_DADDIU, ry, ry, offset); break; case I64_DADDIUPC: check_mips_64(ctx); @@ -9594,7 +9603,7 @@ static void decode_i64_mips16 (CPUMIPSState *env, DisasContext *ctx, case I64_DADDIUSP: check_mips_64(ctx); offset = extended ? offset : offset << 2; - gen_arith_imm(env, ctx, OPC_DADDIU, ry, 29, offset); + gen_arith_imm(ctx, OPC_DADDIU, ry, 29, offset); break; } } @@ -9621,7 +9630,7 @@ static int decode_extended_mips16_opc (CPUMIPSState *env, DisasContext *ctx, counterparts. */ switch (op) { case M16_OPC_ADDIUSP: - gen_arith_imm(env, ctx, OPC_ADDIU, rx, 29, imm); + gen_arith_imm(ctx, OPC_ADDIU, rx, 29, imm); break; case M16_OPC_ADDIUPC: gen_addiupc(ctx, rx, imm, 0, 1); @@ -9641,28 +9650,28 @@ static int decode_extended_mips16_opc (CPUMIPSState *env, DisasContext *ctx, case M16_OPC_SHIFT: switch (ctx->opcode & 0x3) { case 0x0: - gen_shift_imm(env, ctx, OPC_SLL, rx, ry, sa); + gen_shift_imm(ctx, OPC_SLL, rx, ry, sa); break; case 0x1: #if defined(TARGET_MIPS64) check_mips_64(ctx); - gen_shift_imm(env, ctx, OPC_DSLL, rx, ry, sa); + gen_shift_imm(ctx, OPC_DSLL, rx, ry, sa); #else generate_exception(ctx, EXCP_RI); #endif break; case 0x2: - gen_shift_imm(env, ctx, OPC_SRL, rx, ry, sa); + gen_shift_imm(ctx, OPC_SRL, rx, ry, sa); break; case 0x3: - gen_shift_imm(env, ctx, OPC_SRA, rx, ry, sa); + gen_shift_imm(ctx, OPC_SRA, rx, ry, sa); break; } break; #if defined(TARGET_MIPS64) case M16_OPC_LD: check_mips_64(ctx); - gen_ld(env, ctx, OPC_LD, ry, rx, offset); + gen_ld(ctx, OPC_LD, ry, rx, offset); break; #endif case M16_OPC_RRIA: @@ -9673,22 +9682,22 @@ static int decode_extended_mips16_opc (CPUMIPSState *env, DisasContext *ctx, if ((ctx->opcode >> 4) & 0x1) { #if defined(TARGET_MIPS64) check_mips_64(ctx); - gen_arith_imm(env, ctx, OPC_DADDIU, ry, rx, imm); + gen_arith_imm(ctx, OPC_DADDIU, ry, rx, imm); #else generate_exception(ctx, EXCP_RI); #endif } else { - gen_arith_imm(env, ctx, OPC_ADDIU, ry, rx, imm); + gen_arith_imm(ctx, OPC_ADDIU, ry, rx, imm); } break; case M16_OPC_ADDIU8: - gen_arith_imm(env, ctx, OPC_ADDIU, rx, rx, imm); + gen_arith_imm(ctx, OPC_ADDIU, rx, rx, imm); break; case M16_OPC_SLTI: - gen_slt_imm(env, ctx, OPC_SLTI, 24, rx, imm); + gen_slt_imm(ctx, OPC_SLTI, 24, rx, imm); break; case M16_OPC_SLTIU: - gen_slt_imm(env, ctx, OPC_SLTIU, 24, rx, imm); + gen_slt_imm(ctx, OPC_SLTIU, 24, rx, imm); break; case M16_OPC_I8: switch (funct) { @@ -9702,7 +9711,7 @@ static int decode_extended_mips16_opc (CPUMIPSState *env, DisasContext *ctx, gen_st(ctx, OPC_SW, 31, 29, imm); break; case I8_ADJSP: - gen_arith_imm(env, ctx, OPC_ADDIU, 29, 29, imm); + gen_arith_imm(ctx, OPC_ADDIU, 29, 29, imm); break; case I8_SVRS: { @@ -9742,29 +9751,29 @@ static int decode_extended_mips16_opc (CPUMIPSState *env, DisasContext *ctx, break; #endif case M16_OPC_LB: - gen_ld(env, ctx, OPC_LB, ry, rx, offset); + gen_ld(ctx, OPC_LB, ry, rx, offset); break; case M16_OPC_LH: - gen_ld(env, ctx, OPC_LH, ry, rx, offset); + gen_ld(ctx, OPC_LH, ry, rx, offset); break; case M16_OPC_LWSP: - gen_ld(env, ctx, OPC_LW, rx, 29, offset); + gen_ld(ctx, OPC_LW, rx, 29, offset); break; case M16_OPC_LW: - gen_ld(env, ctx, OPC_LW, ry, rx, offset); + gen_ld(ctx, OPC_LW, ry, rx, offset); break; case M16_OPC_LBU: - gen_ld(env, ctx, OPC_LBU, ry, rx, offset); + gen_ld(ctx, OPC_LBU, ry, rx, offset); break; case M16_OPC_LHU: - gen_ld(env, ctx, OPC_LHU, ry, rx, offset); + gen_ld(ctx, OPC_LHU, ry, rx, offset); break; case M16_OPC_LWPC: - gen_ld(env, ctx, OPC_LWPC, rx, 0, offset); + gen_ld(ctx, OPC_LWPC, rx, 0, offset); break; #if defined(TARGET_MIPS64) case M16_OPC_LWU: - gen_ld(env, ctx, OPC_LWU, ry, rx, offset); + gen_ld(ctx, OPC_LWU, ry, rx, offset); break; #endif case M16_OPC_SB: @@ -9781,7 +9790,7 @@ static int decode_extended_mips16_opc (CPUMIPSState *env, DisasContext *ctx, break; #if defined(TARGET_MIPS64) case M16_OPC_I64: - decode_i64_mips16(env, ctx, ry, funct, offset, 1); + decode_i64_mips16(ctx, ry, funct, offset, 1); break; #endif default: @@ -9816,7 +9825,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx, { int16_t imm = ((uint8_t) ctx->opcode) << 2; - gen_arith_imm(env, ctx, OPC_ADDIU, rx, 29, imm); + gen_arith_imm(ctx, OPC_ADDIU, rx, 29, imm); } break; case M16_OPC_ADDIUPC: @@ -9849,28 +9858,28 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx, case M16_OPC_SHIFT: switch (ctx->opcode & 0x3) { case 0x0: - gen_shift_imm(env, ctx, OPC_SLL, rx, ry, sa); + gen_shift_imm(ctx, OPC_SLL, rx, ry, sa); break; case 0x1: #if defined(TARGET_MIPS64) check_mips_64(ctx); - gen_shift_imm(env, ctx, OPC_DSLL, rx, ry, sa); + gen_shift_imm(ctx, OPC_DSLL, rx, ry, sa); #else generate_exception(ctx, EXCP_RI); #endif break; case 0x2: - gen_shift_imm(env, ctx, OPC_SRL, rx, ry, sa); + gen_shift_imm(ctx, OPC_SRL, rx, ry, sa); break; case 0x3: - gen_shift_imm(env, ctx, OPC_SRA, rx, ry, sa); + gen_shift_imm(ctx, OPC_SRA, rx, ry, sa); break; } break; #if defined(TARGET_MIPS64) case M16_OPC_LD: check_mips_64(ctx); - gen_ld(env, ctx, OPC_LD, ry, rx, offset << 3); + gen_ld(ctx, OPC_LD, ry, rx, offset << 3); break; #endif case M16_OPC_RRIA: @@ -9880,12 +9889,12 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx, if ((ctx->opcode >> 4) & 1) { #if defined(TARGET_MIPS64) check_mips_64(ctx); - gen_arith_imm(env, ctx, OPC_DADDIU, ry, rx, imm); + gen_arith_imm(ctx, OPC_DADDIU, ry, rx, imm); #else generate_exception(ctx, EXCP_RI); #endif } else { - gen_arith_imm(env, ctx, OPC_ADDIU, ry, rx, imm); + gen_arith_imm(ctx, OPC_ADDIU, ry, rx, imm); } } break; @@ -9893,19 +9902,19 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx, { int16_t imm = (int8_t) ctx->opcode; - gen_arith_imm(env, ctx, OPC_ADDIU, rx, rx, imm); + gen_arith_imm(ctx, OPC_ADDIU, rx, rx, imm); } break; case M16_OPC_SLTI: { int16_t imm = (uint8_t) ctx->opcode; - gen_slt_imm(env, ctx, OPC_SLTI, 24, rx, imm); + gen_slt_imm(ctx, OPC_SLTI, 24, rx, imm); } break; case M16_OPC_SLTIU: { int16_t imm = (uint8_t) ctx->opcode; - gen_slt_imm(env, ctx, OPC_SLTIU, 24, rx, imm); + gen_slt_imm(ctx, OPC_SLTIU, 24, rx, imm); } break; case M16_OPC_I8: @@ -9926,7 +9935,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx, gen_st(ctx, OPC_SW, 31, 29, (ctx->opcode & 0xff) << 2); break; case I8_ADJSP: - gen_arith_imm(env, ctx, OPC_ADDIU, 29, 29, + gen_arith_imm(ctx, OPC_ADDIU, 29, 29, ((int8_t)ctx->opcode) << 3); break; case I8_SVRS: @@ -9957,12 +9966,12 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx, reg32 = (((ctx->opcode >> 3) & 0x3) << 3) | ((ctx->opcode >> 5) & 0x7); - gen_arith(env, ctx, OPC_ADDU, reg32, rz, 0); + gen_arith(ctx, OPC_ADDU, reg32, rz, 0); } break; case I8_MOVR32: reg32 = ctx->opcode & 0x1f; - gen_arith(env, ctx, OPC_ADDU, ry, reg32, 0); + gen_arith(ctx, OPC_ADDU, ry, reg32, 0); break; default: generate_exception(ctx, EXCP_RI); @@ -9974,13 +9983,13 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx, { int16_t imm = (uint8_t) ctx->opcode; - gen_arith_imm(env, ctx, OPC_ADDIU, rx, 0, imm); + gen_arith_imm(ctx, OPC_ADDIU, rx, 0, imm); } break; case M16_OPC_CMPI: { int16_t imm = (uint8_t) ctx->opcode; - gen_logic_imm(env, ctx, OPC_XORI, 24, rx, imm); + gen_logic_imm(ctx, OPC_XORI, 24, rx, imm); } break; #if defined(TARGET_MIPS64) @@ -9990,30 +9999,30 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx, break; #endif case M16_OPC_LB: - gen_ld(env, ctx, OPC_LB, ry, rx, offset); + gen_ld(ctx, OPC_LB, ry, rx, offset); break; case M16_OPC_LH: - gen_ld(env, ctx, OPC_LH, ry, rx, offset << 1); + gen_ld(ctx, OPC_LH, ry, rx, offset << 1); break; case M16_OPC_LWSP: - gen_ld(env, ctx, OPC_LW, rx, 29, ((uint8_t)ctx->opcode) << 2); + gen_ld(ctx, OPC_LW, rx, 29, ((uint8_t)ctx->opcode) << 2); break; case M16_OPC_LW: - gen_ld(env, ctx, OPC_LW, ry, rx, offset << 2); + gen_ld(ctx, OPC_LW, ry, rx, offset << 2); break; case M16_OPC_LBU: - gen_ld(env, ctx, OPC_LBU, ry, rx, offset); + gen_ld(ctx, OPC_LBU, ry, rx, offset); break; case M16_OPC_LHU: - gen_ld(env, ctx, OPC_LHU, ry, rx, offset << 1); + gen_ld(ctx, OPC_LHU, ry, rx, offset << 1); break; case M16_OPC_LWPC: - gen_ld(env, ctx, OPC_LWPC, rx, 0, ((uint8_t)ctx->opcode) << 2); + gen_ld(ctx, OPC_LWPC, rx, 0, ((uint8_t)ctx->opcode) << 2); break; #if defined (TARGET_MIPS64) case M16_OPC_LWU: check_mips_64(ctx); - gen_ld(env, ctx, OPC_LWU, ry, rx, offset << 2); + gen_ld(ctx, OPC_LWU, ry, rx, offset << 2); break; #endif case M16_OPC_SB: @@ -10055,7 +10064,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx, goto done; } - gen_arith(env, ctx, mips32_op, rz, rx, ry); + gen_arith(ctx, mips32_op, rz, rx, ry); done: ; } @@ -10084,7 +10093,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx, /* XXX: not clear which exception should be raised * when in debug mode... */ - check_insn(env, ctx, ISA_MIPS32); + check_insn(ctx, ISA_MIPS32); if (!(ctx->hflags & MIPS_HFLAG_DM)) { generate_exception(ctx, EXCP_DBp); } else { @@ -10092,46 +10101,46 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx, } break; case RR_SLT: - gen_slt(env, ctx, OPC_SLT, 24, rx, ry); + gen_slt(ctx, OPC_SLT, 24, rx, ry); break; case RR_SLTU: - gen_slt(env, ctx, OPC_SLTU, 24, rx, ry); + gen_slt(ctx, OPC_SLTU, 24, rx, ry); break; case RR_BREAK: generate_exception(ctx, EXCP_BREAK); break; case RR_SLLV: - gen_shift(env, ctx, OPC_SLLV, ry, rx, ry); + gen_shift(ctx, OPC_SLLV, ry, rx, ry); break; case RR_SRLV: - gen_shift(env, ctx, OPC_SRLV, ry, rx, ry); + gen_shift(ctx, OPC_SRLV, ry, rx, ry); break; case RR_SRAV: - gen_shift(env, ctx, OPC_SRAV, ry, rx, ry); + gen_shift(ctx, OPC_SRAV, ry, rx, ry); break; #if defined (TARGET_MIPS64) case RR_DSRL: check_mips_64(ctx); - gen_shift_imm(env, ctx, OPC_DSRL, ry, ry, sa); + gen_shift_imm(ctx, OPC_DSRL, ry, ry, sa); break; #endif case RR_CMP: - gen_logic(env, ctx, OPC_XOR, 24, rx, ry); + gen_logic(ctx, OPC_XOR, 24, rx, ry); break; case RR_NEG: - gen_arith(env, ctx, OPC_SUBU, rx, 0, ry); + gen_arith(ctx, OPC_SUBU, rx, 0, ry); break; case RR_AND: - gen_logic(env, ctx, OPC_AND, rx, rx, ry); + gen_logic(ctx, OPC_AND, rx, rx, ry); break; case RR_OR: - gen_logic(env, ctx, OPC_OR, rx, rx, ry); + gen_logic(ctx, OPC_OR, rx, rx, ry); break; case RR_XOR: - gen_logic(env, ctx, OPC_XOR, rx, rx, ry); + gen_logic(ctx, OPC_XOR, rx, rx, ry); break; case RR_NOT: - gen_logic(env, ctx, OPC_NOR, rx, ry, 0); + gen_logic(ctx, OPC_NOR, rx, ry, 0); break; case RR_MFHI: gen_HILO(ctx, OPC_MFHI, rx); @@ -10171,19 +10180,19 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx, #if defined (TARGET_MIPS64) case RR_DSRA: check_mips_64(ctx); - gen_shift_imm(env, ctx, OPC_DSRA, ry, ry, sa); + gen_shift_imm(ctx, OPC_DSRA, ry, ry, sa); break; case RR_DSLLV: check_mips_64(ctx); - gen_shift(env, ctx, OPC_DSLLV, ry, rx, ry); + gen_shift(ctx, OPC_DSLLV, ry, rx, ry); break; case RR_DSRLV: check_mips_64(ctx); - gen_shift(env, ctx, OPC_DSRLV, ry, rx, ry); + gen_shift(ctx, OPC_DSRLV, ry, rx, ry); break; case RR_DSRAV: check_mips_64(ctx); - gen_shift(env, ctx, OPC_DSRAV, ry, rx, ry); + gen_shift(ctx, OPC_DSRAV, ry, rx, ry); break; #endif case RR_MULT: @@ -10228,7 +10237,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx, #if defined(TARGET_MIPS64) case M16_OPC_I64: funct = (ctx->opcode >> 8) & 0x7; - decode_i64_mips16(env, ctx, ry, funct, offset, 0); + decode_i64_mips16(ctx, ry, funct, offset, 0); break; #endif default: @@ -10730,23 +10739,23 @@ static int mmreg2 (int r) /* Zero-extended immediate */ #define ZIMM(op, start, width) ((op >> start) & ((~0U) >> (32-width))) -static void gen_addiur1sp (CPUMIPSState *env, DisasContext *ctx) +static void gen_addiur1sp(DisasContext *ctx) { int rd = mmreg(uMIPS_RD(ctx->opcode)); - gen_arith_imm(env, ctx, OPC_ADDIU, rd, 29, ((ctx->opcode >> 1) & 0x3f) << 2); + gen_arith_imm(ctx, OPC_ADDIU, rd, 29, ((ctx->opcode >> 1) & 0x3f) << 2); } -static void gen_addiur2 (CPUMIPSState *env, DisasContext *ctx) +static void gen_addiur2(DisasContext *ctx) { static const int decoded_imm[] = { 1, 4, 8, 12, 16, 20, 24, -1 }; int rd = mmreg(uMIPS_RD(ctx->opcode)); int rs = mmreg(uMIPS_RS(ctx->opcode)); - gen_arith_imm(env, ctx, OPC_ADDIU, rd, rs, decoded_imm[ZIMM(ctx->opcode, 1, 3)]); + gen_arith_imm(ctx, OPC_ADDIU, rd, rs, decoded_imm[ZIMM(ctx->opcode, 1, 3)]); } -static void gen_addiusp (CPUMIPSState *env, DisasContext *ctx) +static void gen_addiusp(DisasContext *ctx) { int encoded = ZIMM(ctx->opcode, 1, 9); int decoded; @@ -10761,18 +10770,18 @@ static void gen_addiusp (CPUMIPSState *env, DisasContext *ctx) decoded = encoded - 768; } - gen_arith_imm(env, ctx, OPC_ADDIU, 29, 29, decoded << 2); + gen_arith_imm(ctx, OPC_ADDIU, 29, 29, decoded << 2); } -static void gen_addius5 (CPUMIPSState *env, DisasContext *ctx) +static void gen_addius5(DisasContext *ctx) { int imm = SIMM(ctx->opcode, 1, 4); int rd = (ctx->opcode >> 5) & 0x1f; - gen_arith_imm(env, ctx, OPC_ADDIU, rd, rd, imm); + gen_arith_imm(ctx, OPC_ADDIU, rd, rd, imm); } -static void gen_andi16 (CPUMIPSState *env, DisasContext *ctx) +static void gen_andi16(DisasContext *ctx) { static const int decoded_imm[] = { 128, 1, 2, 3, 4, 7, 8, 15, 16, 31, 32, 63, 64, 255, 32768, 65535 }; @@ -10780,7 +10789,7 @@ static void gen_andi16 (CPUMIPSState *env, DisasContext *ctx) int rs = mmreg(uMIPS_RS(ctx->opcode)); int encoded = ZIMM(ctx->opcode, 0, 4); - gen_logic_imm(env, ctx, OPC_ANDI, rd, rs, decoded_imm[encoded]); + gen_logic_imm(ctx, OPC_ANDI, rd, rs, decoded_imm[encoded]); } static void gen_ldst_multiple (DisasContext *ctx, uint32_t opc, int reglist, @@ -10831,7 +10840,7 @@ static void gen_ldst_multiple (DisasContext *ctx, uint32_t opc, int reglist, } -static void gen_pool16c_insn (CPUMIPSState *env, DisasContext *ctx, int *is_branch) +static void gen_pool16c_insn(DisasContext *ctx, int *is_branch) { int rd = mmreg((ctx->opcode >> 3) & 0x7); int rs = mmreg(ctx->opcode & 0x7); @@ -10842,25 +10851,25 @@ static void gen_pool16c_insn (CPUMIPSState *env, DisasContext *ctx, int *is_bran case NOT16 + 1: case NOT16 + 2: case NOT16 + 3: - gen_logic(env, ctx, OPC_NOR, rd, rs, 0); + gen_logic(ctx, OPC_NOR, rd, rs, 0); break; case XOR16 + 0: case XOR16 + 1: case XOR16 + 2: case XOR16 + 3: - gen_logic(env, ctx, OPC_XOR, rd, rd, rs); + gen_logic(ctx, OPC_XOR, rd, rd, rs); break; case AND16 + 0: case AND16 + 1: case AND16 + 2: case AND16 + 3: - gen_logic(env, ctx, OPC_AND, rd, rd, rs); + gen_logic(ctx, OPC_AND, rd, rd, rs); break; case OR16 + 0: case OR16 + 1: case OR16 + 2: case OR16 + 3: - gen_logic(env, ctx, OPC_OR, rd, rd, rs); + gen_logic(ctx, OPC_OR, rd, rd, rs); break; case LWM16 + 0: case LWM16 + 1: @@ -10935,7 +10944,7 @@ static void gen_pool16c_insn (CPUMIPSState *env, DisasContext *ctx, int *is_bran /* XXX: not clear which exception should be raised * when in debug mode... */ - check_insn(env, ctx, ISA_MIPS32); + check_insn(ctx, ISA_MIPS32); if (!(ctx->hflags & MIPS_HFLAG_DM)) { generate_exception(ctx, EXCP_DBp); } else { @@ -10948,7 +10957,7 @@ static void gen_pool16c_insn (CPUMIPSState *env, DisasContext *ctx, int *is_bran int imm = ZIMM(ctx->opcode, 0, 5); gen_compute_branch(ctx, OPC_JR, 2, 31, 0, 0); - gen_arith_imm(env, ctx, OPC_ADDIU, 29, 29, imm << 2); + gen_arith_imm(ctx, OPC_ADDIU, 29, 29, imm << 2); /* Let normal delay slot handling in our caller take us to the branch target. */ } @@ -11085,7 +11094,7 @@ static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs, /* Treat as NOP. */ break; } - gen_mfc0(env, ctx, cpu_gpr[rt], rs, (ctx->opcode >> 11) & 0x7); + gen_mfc0(ctx, cpu_gpr[rt], rs, (ctx->opcode >> 11) & 0x7); break; case MTC0: case MTC0 + 32: @@ -11094,7 +11103,7 @@ static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs, TCGv t0 = tcg_temp_new(); gen_load_gpr(t0, rt); - gen_mtc0(env, ctx, t0, rs, (ctx->opcode >> 11) & 0x7); + gen_mtc0(ctx, t0, rs, (ctx->opcode >> 11) & 0x7); tcg_temp_free(t0); } break; @@ -11113,11 +11122,11 @@ static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs, case CLZ: mips32_op = OPC_CLZ; do_cl: - check_insn(env, ctx, ISA_MIPS32); + check_insn(ctx, ISA_MIPS32); gen_cl(ctx, mips32_op, rt, rs); break; case RDHWR: - gen_rdhwr(env, ctx, rt, rs); + gen_rdhwr(ctx, rt, rs); break; case WSBH: gen_bshfl(ctx, OPC_WSBH, rs, rt); @@ -11146,7 +11155,7 @@ static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs, case MSUBU: mips32_op = OPC_MSUBU; do_muldiv: - check_insn(env, ctx, ISA_MIPS32); + check_insn(ctx, ISA_MIPS32); gen_muldiv(ctx, mips32_op, rs, rt); break; default: @@ -11187,12 +11196,12 @@ static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs, switch (minor) { case RDPGPR: check_cp0_enabled(ctx); - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_load_srsgpr(rt, rs); break; case WRPGPR: check_cp0_enabled(ctx); - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_store_srsgpr(rt, rs); break; default: @@ -11272,7 +11281,7 @@ static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs, ctx->bstate = BS_STOP; break; case SDBBP: - check_insn(env, ctx, ISA_MIPS32); + check_insn(ctx, ISA_MIPS32); if (!(ctx->hflags & MIPS_HFLAG_DM)) { generate_exception(ctx, EXCP_DBp); } else { @@ -11329,7 +11338,7 @@ enum { FMT_DWL_L = 2 }; -static void gen_pool32fxf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs) +static void gen_pool32fxf(DisasContext *ctx, int rt, int rs) { int extension = (ctx->opcode >> 6) & 0x3ff; uint32_t mips32_op; @@ -11614,7 +11623,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, case ROTR: mips32_op = OPC_ROTR; do_shifti: - gen_shift_imm(env, ctx, mips32_op, rt, rs, rd); + gen_shift_imm(ctx, mips32_op, rt, rs, rd); break; default: goto pool32a_invalid; @@ -11639,7 +11648,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, case MUL: mips32_op = OPC_MUL; do_arith: - gen_arith(env, ctx, mips32_op, rd, rs, rt); + gen_arith(ctx, mips32_op, rd, rs, rt); break; /* Shifts */ case SLLV: @@ -11654,7 +11663,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, case ROTRV: mips32_op = OPC_ROTRV; do_shift: - gen_shift(env, ctx, mips32_op, rd, rs, rt); + gen_shift(ctx, mips32_op, rd, rs, rt); break; /* Logical operations */ case AND: @@ -11669,7 +11678,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, case XOR32: mips32_op = OPC_XOR; do_logic: - gen_logic(env, ctx, mips32_op, rd, rs, rt); + gen_logic(ctx, mips32_op, rd, rs, rt); break; /* Set less than */ case SLT: @@ -11678,7 +11687,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, case SLTU: mips32_op = OPC_SLTU; do_slt: - gen_slt(env, ctx, mips32_op, rd, rs, rt); + gen_slt(ctx, mips32_op, rd, rs, rt); break; default: goto pool32a_invalid; @@ -11694,7 +11703,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, case MOVZ: mips32_op = OPC_MOVZ; do_cmov: - gen_cond_move(env, ctx, mips32_op, rd, rs, rt); + gen_cond_move(ctx, mips32_op, rd, rs, rt); break; case LWXS: gen_ldxs(ctx, rs, rt, rd); @@ -11839,7 +11848,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, } break; case POOL32FXF: - gen_pool32fxf(env, ctx, rt, rs); + gen_pool32fxf(ctx, rt, rs); break; case 0x00: /* PLL foo */ @@ -12107,7 +12116,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, target. */ break; case LUI: - gen_logic_imm(env, ctx, OPC_LUI, rs, -1, imm); + gen_logic_imm(ctx, OPC_LUI, rs, -1, imm); break; case SYNCI: break; @@ -12129,10 +12138,10 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, mips32_op = OPC_BC1TANY4; do_cp1mips3d: check_cop1x(ctx); - check_insn(env, ctx, ASE_MIPS3D); + check_insn(ctx, ASE_MIPS3D); /* Fall through */ do_cp1branch: - gen_compute_branch1(env, ctx, mips32_op, + gen_compute_branch1(ctx, mips32_op, (ctx->opcode >> 18) & 0x7, imm << 1); *is_branch = 1; break; @@ -12185,7 +12194,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, mips32_op = OPC_LL; goto do_ld_lr; do_ld_lr: - gen_ld(env, ctx, mips32_op, rt, rs, SIMM(ctx->opcode, 0, 12)); + gen_ld(ctx, mips32_op, rt, rs, SIMM(ctx->opcode, 0, 12)); break; do_st_lr: gen_st(ctx, mips32_op, rt, rs, SIMM(ctx->opcode, 0, 12)); @@ -12213,7 +12222,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, case ADDIU32: mips32_op = OPC_ADDIU; do_addi: - gen_arith_imm(env, ctx, mips32_op, rt, rs, imm); + gen_arith_imm(ctx, mips32_op, rt, rs, imm); break; /* Logical operations */ @@ -12226,7 +12235,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, case ANDI32: mips32_op = OPC_ANDI; do_logici: - gen_logic_imm(env, ctx, mips32_op, rt, rs, imm); + gen_logic_imm(ctx, mips32_op, rt, rs, imm); break; /* Set less than immediate */ @@ -12236,7 +12245,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, case SLTIU32: mips32_op = OPC_SLTIU; do_slti: - gen_slt_imm(env, ctx, mips32_op, rt, rs, imm); + gen_slt_imm(ctx, mips32_op, rt, rs, imm); break; case JALX32: offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 2; @@ -12323,7 +12332,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, mips32_op = OPC_SW; goto do_st; do_ld: - gen_ld(env, ctx, mips32_op, rt, rs, imm); + gen_ld(ctx, mips32_op, rt, rs, imm); break; do_st: gen_st(ctx, mips32_op, rt, rs, imm); @@ -12443,7 +12452,7 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx, int *is_b break; } - gen_arith(env, ctx, opc, rd, rs1, rs2); + gen_arith(ctx, opc, rd, rs1, rs2); } break; case POOL16B: @@ -12463,11 +12472,11 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx, int *is_b break; } - gen_shift_imm(env, ctx, opc, rd, rs, amount); + gen_shift_imm(ctx, opc, rd, rs, amount); } break; case POOL16C: - gen_pool16c_insn(env, ctx, is_branch); + gen_pool16c_insn(ctx, is_branch); break; case LWGP16: { @@ -12475,7 +12484,7 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx, int *is_b int rb = 28; /* GP */ int16_t offset = SIMM(ctx->opcode, 0, 7) << 2; - gen_ld(env, ctx, OPC_LW, rd, rb, offset); + gen_ld(ctx, OPC_LW, rd, rb, offset); } break; case POOL16F: @@ -12496,8 +12505,8 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx, int *is_b rs = rs_rt_enc[enc_rs]; rt = rs_rt_enc[enc_rt]; - gen_arith_imm(env, ctx, OPC_ADDIU, rd, rs, 0); - gen_arith_imm(env, ctx, OPC_ADDIU, re, rt, 0); + gen_arith_imm(ctx, OPC_ADDIU, rd, rs, 0); + gen_arith_imm(ctx, OPC_ADDIU, re, rt, 0); } break; case LBU16: @@ -12507,7 +12516,7 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx, int *is_b int16_t offset = ZIMM(ctx->opcode, 0, 4); offset = (offset == 0xf ? -1 : offset); - gen_ld(env, ctx, OPC_LBU, rd, rb, offset); + gen_ld(ctx, OPC_LBU, rd, rb, offset); } break; case LHU16: @@ -12516,7 +12525,7 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx, int *is_b int rb = mmreg(uMIPS_RS(ctx->opcode)); int16_t offset = ZIMM(ctx->opcode, 0, 4) << 1; - gen_ld(env, ctx, OPC_LHU, rd, rb, offset); + gen_ld(ctx, OPC_LHU, rd, rb, offset); } break; case LWSP16: @@ -12525,7 +12534,7 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx, int *is_b int rb = 29; /* SP */ int16_t offset = ZIMM(ctx->opcode, 0, 5) << 2; - gen_ld(env, ctx, OPC_LW, rd, rb, offset); + gen_ld(ctx, OPC_LW, rd, rb, offset); } break; case LW16: @@ -12534,7 +12543,7 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx, int *is_b int rb = mmreg(uMIPS_RS(ctx->opcode)); int16_t offset = ZIMM(ctx->opcode, 0, 4) << 2; - gen_ld(env, ctx, OPC_LW, rd, rb, offset); + gen_ld(ctx, OPC_LW, rd, rb, offset); } break; case SB16: @@ -12578,29 +12587,29 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx, int *is_b int rd = uMIPS_RD5(ctx->opcode); int rs = uMIPS_RS5(ctx->opcode); - gen_arith_imm(env, ctx, OPC_ADDIU, rd, rs, 0); + gen_arith_imm(ctx, OPC_ADDIU, rd, rs, 0); } break; case ANDI16: - gen_andi16(env, ctx); + gen_andi16(ctx); break; case POOL16D: switch (ctx->opcode & 0x1) { case ADDIUS5: - gen_addius5(env, ctx); + gen_addius5(ctx); break; case ADDIUSP: - gen_addiusp(env, ctx); + gen_addiusp(ctx); break; } break; case POOL16E: switch (ctx->opcode & 0x1) { case ADDIUR2: - gen_addiur2(env, ctx); + gen_addiur2(ctx); break; case ADDIUR1SP: - gen_addiur1sp(env, ctx); + gen_addiur1sp(ctx); break; } break; @@ -12651,17 +12660,12 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx, int *is_b #endif /* MIPSDSP functions. */ -static void gen_mipsdsp_ld(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, +static void gen_mipsdsp_ld(DisasContext *ctx, uint32_t opc, int rd, int base, int offset) { const char *opn = "ldx"; TCGv t0; - if (rd == 0) { - MIPS_DEBUG("NOP"); - return; - } - check_dsp(ctx); t0 = tcg_temp_new(); @@ -13717,8 +13721,7 @@ static void gen_mipsdsp_multiply(DisasContext *ctx, uint32_t op1, uint32_t op2, } -static void gen_mipsdsp_bitinsn(CPUMIPSState *env, DisasContext *ctx, - uint32_t op1, uint32_t op2, +static void gen_mipsdsp_bitinsn(DisasContext *ctx, uint32_t op1, uint32_t op2, int ret, int val) { const char *opn = "mipsdsp Bit/ Manipulation"; @@ -13866,7 +13869,6 @@ static void gen_mipsdsp_add_cmp_pick(DisasContext *ctx, int ret, int v1, int v2, int check_ret) { const char *opn = "mipsdsp add compare pick"; - TCGv_i32 t0; TCGv t1; TCGv v1_t; TCGv v2_t; @@ -13877,7 +13879,6 @@ static void gen_mipsdsp_add_cmp_pick(DisasContext *ctx, return; } - t0 = tcg_temp_new_i32(); t1 = tcg_temp_new(); v1_t = tcg_temp_new(); v2_t = tcg_temp_new(); @@ -13886,26 +13887,6 @@ static void gen_mipsdsp_add_cmp_pick(DisasContext *ctx, gen_load_gpr(v2_t, v2); switch (op1) { - case OPC_APPEND_DSP: - switch (op2) { - case OPC_APPEND: - tcg_gen_movi_i32(t0, v2); - gen_helper_append(cpu_gpr[ret], cpu_gpr[ret], v1_t, t0); - break; - case OPC_PREPEND: - tcg_gen_movi_i32(t0, v2); - gen_helper_prepend(cpu_gpr[ret], v1_t, cpu_gpr[ret], t0); - break; - case OPC_BALIGN: - tcg_gen_movi_i32(t0, v2); - gen_helper_balign(cpu_gpr[ret], v1_t, cpu_gpr[ret], t0); - break; - default: /* Invid */ - MIPS_INVAL("MASK APPEND"); - generate_exception(ctx, EXCP_RI); - break; - } - break; case OPC_CMPU_EQ_QB_DSP: switch (op2) { case OPC_CMPU_EQ_QB: @@ -14063,23 +14044,95 @@ static void gen_mipsdsp_add_cmp_pick(DisasContext *ctx, break; } break; +#endif + } + + tcg_temp_free(t1); + tcg_temp_free(v1_t); + tcg_temp_free(v2_t); + + (void)opn; /* avoid a compiler warning */ + MIPS_DEBUG("%s", opn); +} + +static void gen_mipsdsp_append(CPUMIPSState *env, DisasContext *ctx, + uint32_t op1, int rt, int rs, int sa) +{ + const char *opn = "mipsdsp append/dappend"; + TCGv t0; + + check_dspr2(ctx); + + if (rt == 0) { + /* Treat as NOP. */ + MIPS_DEBUG("NOP"); + return; + } + + t0 = tcg_temp_new(); + gen_load_gpr(t0, rs); + + switch (op1) { + case OPC_APPEND_DSP: + switch (MASK_APPEND(ctx->opcode)) { + case OPC_APPEND: + if (sa != 0) { + tcg_gen_deposit_tl(cpu_gpr[rt], t0, cpu_gpr[rt], sa, 32 - sa); + } + tcg_gen_ext32s_tl(cpu_gpr[rt], cpu_gpr[rt]); + break; + case OPC_PREPEND: + if (sa != 0) { + tcg_gen_ext32u_tl(cpu_gpr[rt], cpu_gpr[rt]); + tcg_gen_shri_tl(cpu_gpr[rt], cpu_gpr[rt], sa); + tcg_gen_shli_tl(t0, t0, 32 - sa); + tcg_gen_or_tl(cpu_gpr[rt], cpu_gpr[rt], t0); + } + tcg_gen_ext32s_tl(cpu_gpr[rt], cpu_gpr[rt]); + break; + case OPC_BALIGN: + sa &= 3; + if (sa != 0 && sa != 2) { + tcg_gen_shli_tl(cpu_gpr[rt], cpu_gpr[rt], 8 * sa); + tcg_gen_ext32u_tl(t0, t0); + tcg_gen_shri_tl(t0, t0, 8 * (4 - sa)); + tcg_gen_or_tl(cpu_gpr[rt], cpu_gpr[rt], t0); + } + tcg_gen_ext32s_tl(cpu_gpr[rt], cpu_gpr[rt]); + break; + default: /* Invalid */ + MIPS_INVAL("MASK APPEND"); + generate_exception(ctx, EXCP_RI); + break; + } + break; +#ifdef TARGET_MIPS64 case OPC_DAPPEND_DSP: - switch (op2) { + switch (MASK_DAPPEND(ctx->opcode)) { case OPC_DAPPEND: - tcg_gen_movi_i32(t0, v2); - gen_helper_dappend(cpu_gpr[ret], v1_t, cpu_gpr[ret], t0); + if (sa != 0) { + tcg_gen_deposit_tl(cpu_gpr[rt], t0, cpu_gpr[rt], sa, 64 - sa); + } break; case OPC_PREPENDD: - tcg_gen_movi_i32(t0, v2); - gen_helper_prependd(cpu_gpr[ret], v1_t, cpu_gpr[ret], t0); + tcg_gen_shri_tl(cpu_gpr[rt], cpu_gpr[rt], 0x20 | sa); + tcg_gen_shli_tl(t0, t0, 64 - (0x20 | sa)); + tcg_gen_or_tl(cpu_gpr[rt], t0, t0); break; case OPC_PREPENDW: - tcg_gen_movi_i32(t0, v2); - gen_helper_prependw(cpu_gpr[ret], v1_t, cpu_gpr[ret], t0); + if (sa != 0) { + tcg_gen_shri_tl(cpu_gpr[rt], cpu_gpr[rt], sa); + tcg_gen_shli_tl(t0, t0, 64 - sa); + tcg_gen_or_tl(cpu_gpr[rt], cpu_gpr[rt], t0); + } break; case OPC_DBALIGN: - tcg_gen_movi_i32(t0, v2); - gen_helper_dbalign(cpu_gpr[ret], v1_t, cpu_gpr[ret], t0); + sa &= 7; + if (sa != 0 && sa != 2 && sa != 4) { + tcg_gen_shli_tl(cpu_gpr[rt], cpu_gpr[rt], 8 * sa); + tcg_gen_shri_tl(t0, t0, 8 * (8 - sa)); + tcg_gen_or_tl(cpu_gpr[rt], cpu_gpr[rt], t0); + } break; default: /* Invalid */ MIPS_INVAL("MASK DAPPEND"); @@ -14089,12 +14142,7 @@ static void gen_mipsdsp_add_cmp_pick(DisasContext *ctx, break; #endif } - - tcg_temp_free_i32(t0); - tcg_temp_free(t1); - tcg_temp_free(v1_t); - tcg_temp_free(v2_t); - + tcg_temp_free(t0); (void)opn; /* avoid a compiler warning */ MIPS_DEBUG("%s", opn); } @@ -14372,18 +14420,18 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) switch (op1) { case OPC_SLL: /* Shift with immediate */ case OPC_SRA: - gen_shift_imm(env, ctx, op1, rd, rt, sa); + gen_shift_imm(ctx, op1, rd, rt, sa); break; case OPC_SRL: switch ((ctx->opcode >> 21) & 0x1f) { case 1: /* rotr is decoded as srl on non-R2 CPUs */ - if (env->insn_flags & ISA_MIPS32R2) { + if (ctx->insn_flags & ISA_MIPS32R2) { op1 = OPC_ROTR; } /* Fallthrough */ case 0: - gen_shift_imm(env, ctx, op1, rd, rt, sa); + gen_shift_imm(ctx, op1, rd, rt, sa); break; default: generate_exception(ctx, EXCP_RI); @@ -14392,27 +14440,27 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) break; case OPC_MOVN: /* Conditional move */ case OPC_MOVZ: - check_insn(env, ctx, ISA_MIPS4 | ISA_MIPS32 | + check_insn(ctx, ISA_MIPS4 | ISA_MIPS32 | INSN_LOONGSON2E | INSN_LOONGSON2F); - gen_cond_move(env, ctx, op1, rd, rs, rt); + gen_cond_move(ctx, op1, rd, rs, rt); break; case OPC_ADD ... OPC_SUBU: - gen_arith(env, ctx, op1, rd, rs, rt); + gen_arith(ctx, op1, rd, rs, rt); break; case OPC_SLLV: /* Shifts */ case OPC_SRAV: - gen_shift(env, ctx, op1, rd, rs, rt); + gen_shift(ctx, op1, rd, rs, rt); break; case OPC_SRLV: switch ((ctx->opcode >> 6) & 0x1f) { case 1: /* rotrv is decoded as srlv on non-R2 CPUs */ - if (env->insn_flags & ISA_MIPS32R2) { + if (ctx->insn_flags & ISA_MIPS32R2) { op1 = OPC_ROTRV; } /* Fallthrough */ case 0: - gen_shift(env, ctx, op1, rd, rs, rt); + gen_shift(ctx, op1, rd, rs, rt); break; default: generate_exception(ctx, EXCP_RI); @@ -14421,17 +14469,17 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) break; case OPC_SLT: /* Set on less than */ case OPC_SLTU: - gen_slt(env, ctx, op1, rd, rs, rt); + gen_slt(ctx, op1, rd, rs, rt); break; case OPC_AND: /* Logic*/ case OPC_OR: case OPC_NOR: case OPC_XOR: - gen_logic(env, ctx, op1, rd, rs, rt); + gen_logic(ctx, op1, rd, rs, rt); break; case OPC_MULT ... OPC_DIVU: if (sa) { - check_insn(env, ctx, INSN_VR54XX); + check_insn(ctx, INSN_VR54XX); op1 = MASK_MUL_VR54XX(ctx->opcode); gen_mul_vr54xx(ctx, op1, rd, rs, rt); } else @@ -14483,7 +14531,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) break; case OPC_MOVCI: - check_insn(env, ctx, ISA_MIPS4 | ISA_MIPS32); + check_insn(ctx, ISA_MIPS4 | ISA_MIPS32); if (env->CP0_Config1 & (1 << CP0C1_FP)) { check_cp1_enabled(ctx); gen_movci(ctx, rd, rs, (ctx->opcode >> 18) & 0x7, @@ -14499,22 +14547,22 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) case OPC_DSRA: case OPC_DSLL32: case OPC_DSRA32: - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); - gen_shift_imm(env, ctx, op1, rd, rt, sa); + gen_shift_imm(ctx, op1, rd, rt, sa); break; case OPC_DSRL: switch ((ctx->opcode >> 21) & 0x1f) { case 1: /* drotr is decoded as dsrl on non-R2 CPUs */ - if (env->insn_flags & ISA_MIPS32R2) { + if (ctx->insn_flags & ISA_MIPS32R2) { op1 = OPC_DROTR; } /* Fallthrough */ case 0: - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); - gen_shift_imm(env, ctx, op1, rd, rt, sa); + gen_shift_imm(ctx, op1, rd, rt, sa); break; default: generate_exception(ctx, EXCP_RI); @@ -14525,14 +14573,14 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) switch ((ctx->opcode >> 21) & 0x1f) { case 1: /* drotr32 is decoded as dsrl32 on non-R2 CPUs */ - if (env->insn_flags & ISA_MIPS32R2) { + if (ctx->insn_flags & ISA_MIPS32R2) { op1 = OPC_DROTR32; } /* Fallthrough */ case 0: - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); - gen_shift_imm(env, ctx, op1, rd, rt, sa); + gen_shift_imm(ctx, op1, rd, rt, sa); break; default: generate_exception(ctx, EXCP_RI); @@ -14540,28 +14588,28 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) } break; case OPC_DADD ... OPC_DSUBU: - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); - gen_arith(env, ctx, op1, rd, rs, rt); + gen_arith(ctx, op1, rd, rs, rt); break; case OPC_DSLLV: case OPC_DSRAV: - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); - gen_shift(env, ctx, op1, rd, rs, rt); + gen_shift(ctx, op1, rd, rs, rt); break; case OPC_DSRLV: switch ((ctx->opcode >> 6) & 0x1f) { case 1: /* drotrv is decoded as dsrlv on non-R2 CPUs */ - if (env->insn_flags & ISA_MIPS32R2) { + if (ctx->insn_flags & ISA_MIPS32R2) { op1 = OPC_DROTRV; } /* Fallthrough */ case 0: - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); - gen_shift(env, ctx, op1, rd, rs, rt); + gen_shift(ctx, op1, rd, rs, rt); break; default: generate_exception(ctx, EXCP_RI); @@ -14569,7 +14617,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) } break; case OPC_DMULT ... OPC_DDIVU: - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); gen_muldiv(ctx, op1, rs, rt); break; @@ -14585,22 +14633,22 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) switch (op1) { case OPC_MADD ... OPC_MADDU: /* Multiply and add/sub */ case OPC_MSUB ... OPC_MSUBU: - check_insn(env, ctx, ISA_MIPS32); + check_insn(ctx, ISA_MIPS32); gen_muldiv(ctx, op1, rs, rt); break; case OPC_MUL: - gen_arith(env, ctx, op1, rd, rs, rt); + gen_arith(ctx, op1, rd, rs, rt); break; case OPC_CLO: case OPC_CLZ: - check_insn(env, ctx, ISA_MIPS32); + check_insn(ctx, ISA_MIPS32); gen_cl(ctx, op1, rd, rs); break; case OPC_SDBBP: /* XXX: not clear which exception should be raised * when in debug mode... */ - check_insn(env, ctx, ISA_MIPS32); + check_insn(ctx, ISA_MIPS32); if (!(ctx->hflags & MIPS_HFLAG_DM)) { generate_exception(ctx, EXCP_DBp); } else { @@ -14614,13 +14662,13 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) case OPC_MULTU_G_2F: case OPC_MOD_G_2F: case OPC_MODU_G_2F: - check_insn(env, ctx, INSN_LOONGSON2F); + check_insn(ctx, INSN_LOONGSON2F); gen_loongson_integer(ctx, op1, rd, rs, rt); break; #if defined(TARGET_MIPS64) case OPC_DCLO: case OPC_DCLZ: - check_insn(env, ctx, ISA_MIPS64); + check_insn(ctx, ISA_MIPS64); check_mips_64(ctx); gen_cl(ctx, op1, rd, rs); break; @@ -14630,7 +14678,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) case OPC_DDIVU_G_2F: case OPC_DMOD_G_2F: case OPC_DMODU_G_2F: - check_insn(env, ctx, INSN_LOONGSON2F); + check_insn(ctx, INSN_LOONGSON2F); gen_loongson_integer(ctx, op1, rd, rs, rt); break; #endif @@ -14645,19 +14693,19 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) switch (op1) { case OPC_EXT: case OPC_INS: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_bitops(ctx, op1, rt, rs, sa, rd); break; case OPC_BSHFL: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); op2 = MASK_BSHFL(ctx->opcode); gen_bshfl(ctx, op2, rt, rd); break; case OPC_RDHWR: - gen_rdhwr(env, ctx, rt, rd); + gen_rdhwr(ctx, rt, rd); break; case OPC_FORK: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); { TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); @@ -14670,7 +14718,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) } break; case OPC_YIELD: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); { TCGv t0 = tcg_temp_new(); @@ -14686,7 +14734,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) case OPC_MULT_G_2E ... OPC_MULTU_G_2E: /* OPC_MULT_G_2E, OPC_ADDUH_QB_DSP, OPC_MUL_PH_DSP have * the same mask and op1. */ - if ((env->insn_flags & ASE_DSPR2) && (op1 == OPC_MULT_G_2E)) { + if ((ctx->insn_flags & ASE_DSPR2) && (op1 == OPC_MULT_G_2E)) { op2 = MASK_ADDUH_QB(ctx->opcode); switch (op2) { case OPC_ADDUH_QB: @@ -14714,7 +14762,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) generate_exception(ctx, EXCP_RI); break; } - } else if (env->insn_flags & INSN_LOONGSON2E) { + } else if (ctx->insn_flags & INSN_LOONGSON2E) { gen_loongson_integer(ctx, op1, rd, rs, rt); } else { generate_exception(ctx, EXCP_RI); @@ -14729,7 +14777,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) case OPC_LBUX: case OPC_LHX: case OPC_LWX: - gen_mipsdsp_ld(env, ctx, op2, rd, rs, rt); + gen_mipsdsp_ld(ctx, op2, rd, rs, rt); break; default: /* Invalid */ MIPS_INVAL("MASK LX"); @@ -14760,7 +14808,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) case OPC_REPLV_QB: case OPC_REPL_PH: case OPC_REPLV_PH: - gen_mipsdsp_bitinsn(env, ctx, op1, op2, rd, rt); + gen_mipsdsp_bitinsn(ctx, op1, op2, rd, rt); break; default: MIPS_INVAL("MASK ABSQ_S.PH"); @@ -14913,9 +14961,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) } break; case OPC_APPEND_DSP: - check_dspr2(ctx); - op2 = MASK_APPEND(ctx->opcode); - gen_mipsdsp_add_cmp_pick(ctx, op1, op2, rt, rs, rd, 1); + gen_mipsdsp_append(env, ctx, op1, rt, rs, rd); break; case OPC_EXTR_W_DSP: op2 = MASK_EXTR_W(ctx->opcode); @@ -14952,12 +14998,12 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) #if defined(TARGET_MIPS64) case OPC_DEXTM ... OPC_DEXT: case OPC_DINSM ... OPC_DINS: - check_insn(env, ctx, ISA_MIPS64R2); + check_insn(ctx, ISA_MIPS64R2); check_mips_64(ctx); gen_bitops(ctx, op1, rt, rs, sa, rd); break; case OPC_DBSHFL: - check_insn(env, ctx, ISA_MIPS64R2); + check_insn(ctx, ISA_MIPS64R2); check_mips_64(ctx); op2 = MASK_DBSHFL(ctx->opcode); gen_bshfl(ctx, op2, rt, rd); @@ -14965,7 +15011,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) case OPC_DDIV_G_2E ... OPC_DDIVU_G_2E: case OPC_DMULT_G_2E ... OPC_DMULTU_G_2E: case OPC_DMOD_G_2E ... OPC_DMODU_G_2E: - check_insn(env, ctx, INSN_LOONGSON2E); + check_insn(ctx, INSN_LOONGSON2E); gen_loongson_integer(ctx, op1, rd, rs, rt); break; case OPC_ABSQ_S_QH_DSP: @@ -14996,7 +15042,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) case OPC_REPLV_OB: case OPC_REPLV_PW: case OPC_REPLV_QH: - gen_mipsdsp_bitinsn(env, ctx, op1, op2, rd, rt); + gen_mipsdsp_bitinsn(ctx, op1, op2, rd, rt); break; default: /* Invalid */ MIPS_INVAL("MASK ABSQ_S.QH"); @@ -15089,9 +15135,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) } break; case OPC_DAPPEND_DSP: - check_dspr2(ctx); - op2 = MASK_DAPPEND(ctx->opcode); - gen_mipsdsp_add_cmp_pick(ctx, op1, op2, rt, rs, rd, 1); + gen_mipsdsp_append(env, ctx, op1, rt, rs, rd); break; case OPC_DEXTR_W_DSP: op2 = MASK_DEXTR_W(ctx->opcode); @@ -15217,7 +15261,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) gen_trap(ctx, op1, rs, -1, imm); break; case OPC_SYNCI: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); /* Treat as NOP. */ break; case OPC_BPOSGE32: /* MIPS DSP branch */ @@ -15263,27 +15307,27 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) op2 = MASK_MFMC0(ctx->opcode); switch (op2) { case OPC_DMT: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_dmt(t0); gen_store_gpr(t0, rt); break; case OPC_EMT: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_emt(t0); gen_store_gpr(t0, rt); break; case OPC_DVPE: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_dvpe(t0, cpu_env); gen_store_gpr(t0, rt); break; case OPC_EVPE: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_evpe(t0, cpu_env); gen_store_gpr(t0, rt); break; case OPC_DI: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); save_cpu_state(ctx, 1); gen_helper_di(t0, cpu_env); gen_store_gpr(t0, rt); @@ -15291,7 +15335,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) ctx->bstate = BS_STOP; break; case OPC_EI: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); save_cpu_state(ctx, 1); gen_helper_ei(t0, cpu_env); gen_store_gpr(t0, rt); @@ -15308,11 +15352,11 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) #endif /* !CONFIG_USER_ONLY */ break; case OPC_RDPGPR: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_load_srsgpr(rt, rd); break; case OPC_WRPGPR: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_store_srsgpr(rt, rd); break; default: @@ -15323,17 +15367,17 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) break; case OPC_ADDI: /* Arithmetic with immediate opcode */ case OPC_ADDIU: - gen_arith_imm(env, ctx, op, rt, rs, imm); + gen_arith_imm(ctx, op, rt, rs, imm); break; case OPC_SLTI: /* Set on less than with immediate opcode */ case OPC_SLTIU: - gen_slt_imm(env, ctx, op, rt, rs, imm); + gen_slt_imm(ctx, op, rt, rs, imm); break; case OPC_ANDI: /* Arithmetic with immediate opcode */ case OPC_LUI: case OPC_ORI: case OPC_XORI: - gen_logic_imm(env, ctx, op, rt, rs, imm); + gen_logic_imm(ctx, op, rt, rs, imm); break; case OPC_J ... OPC_JAL: /* Jump */ offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 2; @@ -15347,7 +15391,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) break; case OPC_LB ... OPC_LWR: /* Load and stores */ case OPC_LL: - gen_ld(env, ctx, op, rt, rs, imm); + gen_ld(ctx, op, rt, rs, imm); break; case OPC_SB ... OPC_SW: case OPC_SWR: @@ -15358,11 +15402,11 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) break; case OPC_CACHE: check_cp0_enabled(ctx); - check_insn(env, ctx, ISA_MIPS3 | ISA_MIPS32); + check_insn(ctx, ISA_MIPS3 | ISA_MIPS32); /* Treat as NOP. */ break; case OPC_PREF: - check_insn(env, ctx, ISA_MIPS4 | ISA_MIPS32); + check_insn(ctx, ISA_MIPS4 | ISA_MIPS32); /* Treat as NOP. */ break; @@ -15381,7 +15425,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) switch (op1) { case OPC_MFHC1: case OPC_MTHC1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); case OPC_MFC1: case OPC_CFC1: case OPC_MTC1: @@ -15391,17 +15435,17 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) #if defined(TARGET_MIPS64) case OPC_DMFC1: case OPC_DMTC1: - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); gen_cp1(ctx, op1, rt, rd); break; #endif case OPC_BC1ANY2: case OPC_BC1ANY4: check_cop1x(ctx); - check_insn(env, ctx, ASE_MIPS3D); + check_insn(ctx, ASE_MIPS3D); /* fall through */ case OPC_BC1: - gen_compute_branch1(env, ctx, MASK_BC1(ctx->opcode), + gen_compute_branch1(ctx, MASK_BC1(ctx->opcode), (rt >> 2) & 0x7, imm << 2); *is_branch = 1; break; @@ -15432,7 +15476,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) generate_exception_err(ctx, EXCP_CpU, 2); break; case OPC_CP2: - check_insn(env, ctx, INSN_LOONGSON2F); + check_insn(ctx, INSN_LOONGSON2F); /* Note that these instructions use different fields. */ gen_loongson_multimedia(ctx, sa, rd, rt); break; @@ -15484,36 +15528,36 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) case OPC_LDL ... OPC_LDR: case OPC_LLD: case OPC_LD: - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); - gen_ld(env, ctx, op, rt, rs, imm); + gen_ld(ctx, op, rt, rs, imm); break; case OPC_SDL ... OPC_SDR: case OPC_SD: - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); gen_st(ctx, op, rt, rs, imm); break; case OPC_SCD: - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); gen_st_cond(ctx, op, rt, rs, imm); break; case OPC_DADDI: case OPC_DADDIU: - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); - gen_arith_imm(env, ctx, op, rt, rs, imm); + gen_arith_imm(ctx, op, rt, rs, imm); break; #endif case OPC_JALX: - check_insn(env, ctx, ASE_MIPS16 | ASE_MICROMIPS); + check_insn(ctx, ASE_MIPS16 | ASE_MICROMIPS); offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 2; gen_compute_branch(ctx, op, 4, rs, rt, offset); *is_branch = 1; break; case OPC_MDMX: - check_insn(env, ctx, ASE_MDMX); + check_insn(ctx, ASE_MDMX); /* MDMX: Not implemented. */ default: /* Invalid */ MIPS_INVAL("major opcode"); @@ -15544,6 +15588,7 @@ gen_intermediate_code_internal (CPUMIPSState *env, TranslationBlock *tb, ctx.pc = pc_start; ctx.saved_pc = -1; ctx.singlestep_enabled = env->singlestep_enabled; + ctx.insn_flags = env->insn_flags; ctx.tb = tb; ctx.bstate = BS_NONE; /* Restore delay slot state from the tb context. */ @@ -15596,10 +15641,10 @@ gen_intermediate_code_internal (CPUMIPSState *env, TranslationBlock *tb, ctx.opcode = cpu_ldl_code(env, ctx.pc); insn_bytes = 4; decode_opc(env, &ctx, &is_branch); - } else if (env->insn_flags & ASE_MICROMIPS) { + } else if (ctx.insn_flags & ASE_MICROMIPS) { ctx.opcode = cpu_lduw_code(env, ctx.pc); insn_bytes = decode_micromips_opc(env, &ctx, &is_branch); - } else if (env->insn_flags & ASE_MIPS16) { + } else if (ctx.insn_flags & ASE_MIPS16) { ctx.opcode = cpu_lduw_code(env, ctx.pc); insn_bytes = decode_mips16_opc(env, &ctx, &is_branch); } else { @@ -15608,7 +15653,7 @@ gen_intermediate_code_internal (CPUMIPSState *env, TranslationBlock *tb, break; } if (!is_branch) { - handle_delay_slot(env, &ctx, insn_bytes); + handle_delay_slot(&ctx, insn_bytes); } ctx.pc += insn_bytes; @@ -15933,10 +15978,8 @@ void cpu_state_reset(CPUMIPSState *env) if (env->CP0_Config1 & (1 << CP0C1_FP)) { env->CP0_Status |= (1 << CP0St_CU1); } - if (env->cpu_model->insn_flags & ASE_DSPR2) { - env->hflags |= MIPS_HFLAG_DSP | MIPS_HFLAG_DSPR2; - } else if (env->cpu_model->insn_flags & ASE_DSP) { - env->hflags |= MIPS_HFLAG_DSP; + if (env->CP0_Config3 & (1 << CP0C3_DSPP)) { + env->CP0_Status |= (1 << CP0St_MX); } #else if (env->hflags & MIPS_HFLAG_BMASK) { diff --git a/target-openrisc/cpu.c b/target-openrisc/cpu.c index 56544d8ab5..a7a8de8a37 100644 --- a/target-openrisc/cpu.c +++ b/target-openrisc/cpu.c @@ -88,6 +88,23 @@ static void openrisc_cpu_initfn(Object *obj) } /* CPU models */ + +static ObjectClass *openrisc_cpu_class_by_name(const char *cpu_model) +{ + ObjectClass *oc; + + if (cpu_model == NULL) { + return NULL; + } + + oc = object_class_by_name(cpu_model); + if (oc != NULL && (!object_class_dynamic_cast(oc, TYPE_OPENRISC_CPU) || + object_class_is_abstract(oc))) { + return NULL; + } + return oc; +} + static void or1200_initfn(Object *obj) { OpenRISCCPU *cpu = OPENRISC_CPU(obj); @@ -120,19 +137,22 @@ static void openrisc_cpu_class_init(ObjectClass *oc, void *data) occ->parent_reset = cc->reset; cc->reset = openrisc_cpu_reset; + + cc->class_by_name = openrisc_cpu_class_by_name; } static void cpu_register(const OpenRISCCPUInfo *info) { TypeInfo type_info = { - .name = info->name, .parent = TYPE_OPENRISC_CPU, .instance_size = sizeof(OpenRISCCPU), .instance_init = info->initfn, .class_size = sizeof(OpenRISCCPUClass), }; - type_register_static(&type_info); + type_info.name = g_strdup_printf("%s-" TYPE_OPENRISC_CPU, info->name); + type_register(&type_info); + g_free((void *)type_info.name); } static const TypeInfo openrisc_cpu_type_info = { @@ -140,7 +160,7 @@ static const TypeInfo openrisc_cpu_type_info = { .parent = TYPE_CPU, .instance_size = sizeof(OpenRISCCPU), .instance_init = openrisc_cpu_initfn, - .abstract = false, + .abstract = true, .class_size = sizeof(OpenRISCCPUClass), .class_init = openrisc_cpu_class_init, }; @@ -158,11 +178,13 @@ static void openrisc_cpu_register_types(void) OpenRISCCPU *cpu_openrisc_init(const char *cpu_model) { OpenRISCCPU *cpu; + ObjectClass *oc; - if (!object_class_by_name(cpu_model)) { + oc = openrisc_cpu_class_by_name(cpu_model); + if (oc == NULL) { return NULL; } - cpu = OPENRISC_CPU(object_new(cpu_model)); + cpu = OPENRISC_CPU(object_new(object_class_get_name(oc))); cpu->env.cpu_model_str = cpu_model; openrisc_cpu_realize(OBJECT(cpu), NULL); @@ -170,11 +192,6 @@ OpenRISCCPU *cpu_openrisc_init(const char *cpu_model) return cpu; } -typedef struct OpenRISCCPUList { - fprintf_function cpu_fprintf; - FILE *file; -} OpenRISCCPUList; - /* Sort alphabetically by type name, except for "any". */ static gint openrisc_cpu_list_compare(gconstpointer a, gconstpointer b) { @@ -184,9 +201,9 @@ static gint openrisc_cpu_list_compare(gconstpointer a, gconstpointer b) name_a = object_class_get_name(class_a); name_b = object_class_get_name(class_b); - if (strcmp(name_a, "any") == 0) { + if (strcmp(name_a, "any-" TYPE_OPENRISC_CPU) == 0) { return 1; - } else if (strcmp(name_b, "any") == 0) { + } else if (strcmp(name_b, "any-" TYPE_OPENRISC_CPU) == 0) { return -1; } else { return strcmp(name_a, name_b); @@ -196,15 +213,21 @@ static gint openrisc_cpu_list_compare(gconstpointer a, gconstpointer b) static void openrisc_cpu_list_entry(gpointer data, gpointer user_data) { ObjectClass *oc = data; - OpenRISCCPUList *s = user_data; + CPUListState *s = user_data; + const char *typename; + char *name; + typename = object_class_get_name(oc); + name = g_strndup(typename, + strlen(typename) - strlen("-" TYPE_OPENRISC_CPU)); (*s->cpu_fprintf)(s->file, " %s\n", - object_class_get_name(oc)); + name); + g_free(name); } void cpu_openrisc_list(FILE *f, fprintf_function cpu_fprintf) { - OpenRISCCPUList s = { + CPUListState s = { .file = f, .cpu_fprintf = cpu_fprintf, }; diff --git a/target-openrisc/exception_helper.c b/target-openrisc/exception_helper.c index dab4148151..0c53b7755b 100644 --- a/target-openrisc/exception_helper.c +++ b/target-openrisc/exception_helper.c @@ -23,7 +23,7 @@ void HELPER(exception)(CPUOpenRISCState *env, uint32_t excp) { - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); raise_exception(cpu, excp); } diff --git a/target-openrisc/fpu_helper.c b/target-openrisc/fpu_helper.c index b184d5ef73..4615a366d1 100644 --- a/target-openrisc/fpu_helper.c +++ b/target-openrisc/fpu_helper.c @@ -68,7 +68,7 @@ static inline void update_fpcsr(OpenRISCCPU *cpu) uint64_t HELPER(itofd)(CPUOpenRISCState *env, uint64_t val) { uint64_t itofd; - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); set_float_exception_flags(0, &cpu->env.fp_status); itofd = int32_to_float64(val, &cpu->env.fp_status); @@ -80,7 +80,7 @@ uint64_t HELPER(itofd)(CPUOpenRISCState *env, uint64_t val) uint32_t HELPER(itofs)(CPUOpenRISCState *env, uint32_t val) { uint32_t itofs; - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); set_float_exception_flags(0, &cpu->env.fp_status); itofs = int32_to_float32(val, &cpu->env.fp_status); @@ -92,7 +92,7 @@ uint32_t HELPER(itofs)(CPUOpenRISCState *env, uint32_t val) uint64_t HELPER(ftoid)(CPUOpenRISCState *env, uint64_t val) { uint64_t ftoid; - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); set_float_exception_flags(0, &cpu->env.fp_status); ftoid = float32_to_int64(val, &cpu->env.fp_status); @@ -104,7 +104,7 @@ uint64_t HELPER(ftoid)(CPUOpenRISCState *env, uint64_t val) uint32_t HELPER(ftois)(CPUOpenRISCState *env, uint32_t val) { uint32_t ftois; - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); set_float_exception_flags(0, &cpu->env.fp_status); ftois = float32_to_int32(val, &cpu->env.fp_status); @@ -120,7 +120,7 @@ uint64_t helper_float_ ## name ## _d(CPUOpenRISCState *env, \ uint64_t fdt0, uint64_t fdt1) \ { \ uint64_t result; \ - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); \ + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ set_float_exception_flags(0, &cpu->env.fp_status); \ result = float64_ ## name(fdt0, fdt1, &cpu->env.fp_status); \ update_fpcsr(cpu); \ @@ -131,7 +131,7 @@ uint32_t helper_float_ ## name ## _s(CPUOpenRISCState *env, \ uint32_t fdt0, uint32_t fdt1) \ { \ uint32_t result; \ - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); \ + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ set_float_exception_flags(0, &cpu->env.fp_status); \ result = float32_ ## name(fdt0, fdt1, &cpu->env.fp_status); \ update_fpcsr(cpu); \ @@ -152,7 +152,7 @@ uint64_t helper_float_ ## name1 ## name2 ## _d(CPUOpenRISCState *env, \ { \ uint64_t result, temp, hi, lo; \ uint32_t val1, val2; \ - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); \ + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ hi = env->fpmaddhi; \ lo = env->fpmaddlo; \ set_float_exception_flags(0, &cpu->env.fp_status); \ @@ -174,7 +174,7 @@ uint32_t helper_float_ ## name1 ## name2 ## _s(CPUOpenRISCState *env, \ { \ uint64_t result, temp, hi, lo; \ uint32_t val1, val2; \ - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); \ + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ hi = cpu->env.fpmaddhi; \ lo = cpu->env.fpmaddlo; \ set_float_exception_flags(0, &cpu->env.fp_status); \ @@ -198,7 +198,7 @@ uint64_t helper_float_ ## name ## _d(CPUOpenRISCState *env, \ uint64_t fdt0, uint64_t fdt1) \ { \ int res; \ - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); \ + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ set_float_exception_flags(0, &cpu->env.fp_status); \ res = float64_ ## name(fdt0, fdt1, &cpu->env.fp_status); \ update_fpcsr(cpu); \ @@ -209,7 +209,7 @@ uint32_t helper_float_ ## name ## _s(CPUOpenRISCState *env, \ uint32_t fdt0, uint32_t fdt1)\ { \ int res; \ - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); \ + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ set_float_exception_flags(0, &cpu->env.fp_status); \ res = float32_ ## name(fdt0, fdt1, &cpu->env.fp_status); \ update_fpcsr(cpu); \ @@ -227,7 +227,7 @@ uint64_t helper_float_ ## name ## _d(CPUOpenRISCState *env, \ uint64_t fdt0, uint64_t fdt1) \ { \ int res; \ - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); \ + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ set_float_exception_flags(0, &cpu->env.fp_status); \ res = !float64_eq_quiet(fdt0, fdt1, &cpu->env.fp_status); \ update_fpcsr(cpu); \ @@ -238,7 +238,7 @@ uint32_t helper_float_ ## name ## _s(CPUOpenRISCState *env, \ uint32_t fdt0, uint32_t fdt1) \ { \ int res; \ - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); \ + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ set_float_exception_flags(0, &cpu->env.fp_status); \ res = !float32_eq_quiet(fdt0, fdt1, &cpu->env.fp_status); \ update_fpcsr(cpu); \ @@ -253,7 +253,7 @@ uint64_t helper_float_ ## name ## _d(CPUOpenRISCState *env, \ uint64_t fdt0, uint64_t fdt1) \ { \ int res; \ - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); \ + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ set_float_exception_flags(0, &cpu->env.fp_status); \ res = !float64_le(fdt0, fdt1, &cpu->env.fp_status); \ update_fpcsr(cpu); \ @@ -264,7 +264,7 @@ uint32_t helper_float_ ## name ## _s(CPUOpenRISCState *env, \ uint32_t fdt0, uint32_t fdt1) \ { \ int res; \ - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); \ + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ set_float_exception_flags(0, &cpu->env.fp_status); \ res = !float32_le(fdt0, fdt1, &cpu->env.fp_status); \ update_fpcsr(cpu); \ @@ -278,7 +278,7 @@ uint64_t helper_float_ ## name ## _d(CPUOpenRISCState *env, \ uint64_t fdt0, uint64_t fdt1) \ { \ int res; \ - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); \ + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ set_float_exception_flags(0, &cpu->env.fp_status); \ res = !float64_lt(fdt0, fdt1, &cpu->env.fp_status); \ update_fpcsr(cpu); \ @@ -289,7 +289,7 @@ uint32_t helper_float_ ## name ## _s(CPUOpenRISCState *env, \ uint32_t fdt0, uint32_t fdt1) \ { \ int res; \ - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); \ + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ set_float_exception_flags(0, &cpu->env.fp_status); \ res = !float32_lt(fdt0, fdt1, &cpu->env.fp_status); \ update_fpcsr(cpu); \ diff --git a/target-openrisc/int_helper.c b/target-openrisc/int_helper.c index 20f9837e6a..16cb5abfcd 100644 --- a/target-openrisc/int_helper.c +++ b/target-openrisc/int_helper.c @@ -48,7 +48,7 @@ uint32_t HELPER(mul32)(CPUOpenRISCState *env, uint64_t result; uint32_t high, cy; - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); result = (uint64_t)ra * rb; /* regisiers in or32 is 32bit, so 32 is NOT a magic number. diff --git a/target-openrisc/interrupt_helper.c b/target-openrisc/interrupt_helper.c index 79f5afed44..a176441b01 100644 --- a/target-openrisc/interrupt_helper.c +++ b/target-openrisc/interrupt_helper.c @@ -23,7 +23,7 @@ void HELPER(rfe)(CPUOpenRISCState *env) { - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); #ifndef CONFIG_USER_ONLY int need_flush_tlb = (cpu->env.sr & (SR_SM | SR_IME | SR_DME)) ^ (cpu->env.esr & (SR_SM | SR_IME | SR_DME)); diff --git a/target-openrisc/mmu.c b/target-openrisc/mmu.c index 836465259a..d354e1f8b2 100644 --- a/target-openrisc/mmu.c +++ b/target-openrisc/mmu.c @@ -187,7 +187,7 @@ int cpu_openrisc_handle_mmu_fault(CPUOpenRISCState *env, int ret = 0; hwaddr physical = 0; int prot = 0; - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); ret = cpu_openrisc_get_phys_addr(cpu, &physical, &prot, address, rw); @@ -209,7 +209,7 @@ int cpu_openrisc_handle_mmu_fault(CPUOpenRISCState *env, target_ulong address, int rw, int mmu_idx) { int ret = 0; - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); cpu_openrisc_raise_mmu_exception(cpu, address, rw, ret); ret = 1; @@ -224,7 +224,7 @@ hwaddr cpu_get_phys_page_debug(CPUOpenRISCState *env, { hwaddr phys_addr; int prot; - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); if (cpu_openrisc_get_phys_addr(cpu, &phys_addr, &prot, addr, 0)) { return -1; diff --git a/target-openrisc/sys_helper.c b/target-openrisc/sys_helper.c index f160dc397c..3c5f45ab75 100644 --- a/target-openrisc/sys_helper.c +++ b/target-openrisc/sys_helper.c @@ -30,7 +30,7 @@ void HELPER(mtspr)(CPUOpenRISCState *env, int spr = (ra | offset); int idx; - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); switch (spr) { case TO_SPR(0, 0): /* VR */ @@ -177,7 +177,7 @@ target_ulong HELPER(mfspr)(CPUOpenRISCState *env, int spr = (ra | offset); int idx; - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); switch (spr) { case TO_SPR(0, 0): /* VR */ diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 953146eeba..8c081dbec5 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -1857,10 +1857,8 @@ enum { PPC_CACHE = 0x0000000200000000ULL, /* icbi instruction */ PPC_CACHE_ICBI = 0x0000000400000000ULL, - /* dcbz instruction with fixed cache line size */ + /* dcbz instruction */ PPC_CACHE_DCBZ = 0x0000000800000000ULL, - /* dcbz instruction with tunable cache line size */ - PPC_CACHE_DCBZT = 0x0000001000000000ULL, /* dcba instruction */ PPC_CACHE_DCBA = 0x0000002000000000ULL, /* Freescale cache locking instructions */ @@ -1928,7 +1926,7 @@ enum { | PPC_MEM_TLBIE | PPC_MEM_TLBSYNC \ | PPC_MEM_SYNC | PPC_MEM_EIEIO \ | PPC_CACHE | PPC_CACHE_ICBI \ - | PPC_CACHE_DCBZ | PPC_CACHE_DCBZT \ + | PPC_CACHE_DCBZ \ | PPC_CACHE_DCBA | PPC_CACHE_LOCK \ | PPC_EXTERN | PPC_SEGMENT | PPC_6xx_TLB \ | PPC_74xx_TLB | PPC_40x_TLB | PPC_SEGMENT_64B \ diff --git a/target-ppc/helper.h b/target-ppc/helper.h index 83139d5225..18e039452f 100644 --- a/target-ppc/helper.h +++ b/target-ppc/helper.h @@ -25,8 +25,7 @@ DEF_HELPER_3(stmw, void, env, tl, i32) DEF_HELPER_4(lsw, void, env, tl, i32, i32) DEF_HELPER_5(lswx, void, env, tl, i32, i32, i32) DEF_HELPER_4(stsw, void, env, tl, i32, i32) -DEF_HELPER_2(dcbz, void, env, tl) -DEF_HELPER_2(dcbz_970, void, env, tl) +DEF_HELPER_3(dcbz, void, env, tl, i32) DEF_HELPER_2(icbi, void, env, tl) DEF_HELPER_5(lscbx, tl, env, tl, i32, i32, i32) diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c index 2f4f06818a..2c64c634f1 100644 --- a/target-ppc/kvm.c +++ b/target-ppc/kvm.c @@ -384,6 +384,11 @@ static inline void kvm_fixup_page_sizes(PowerPCCPU *cpu) #endif /* !defined (TARGET_PPC64) */ +unsigned long kvm_arch_vcpu_id(CPUState *cpu) +{ + return cpu->cpu_index; +} + int kvm_arch_init_vcpu(CPUState *cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); diff --git a/target-ppc/mem_helper.c b/target-ppc/mem_helper.c index 902b1cd823..ba383c8f11 100644 --- a/target-ppc/mem_helper.c +++ b/target-ppc/mem_helper.c @@ -136,18 +136,21 @@ static void do_dcbz(CPUPPCState *env, target_ulong addr, int dcache_line_size) } } -void helper_dcbz(CPUPPCState *env, target_ulong addr) +void helper_dcbz(CPUPPCState *env, target_ulong addr, uint32_t is_dcbzl) { - do_dcbz(env, addr, env->dcache_line_size); -} + int dcbz_size = env->dcache_line_size; -void helper_dcbz_970(CPUPPCState *env, target_ulong addr) -{ - if (((env->spr[SPR_970_HID5] >> 7) & 0x3) == 1) { - do_dcbz(env, addr, 32); - } else { - do_dcbz(env, addr, env->dcache_line_size); +#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) + if (!is_dcbzl && + (env->excp_model == POWERPC_EXCP_970) && + ((env->spr[SPR_970_HID5] >> 7) & 0x3) == 1) { + dcbz_size = 32; } +#endif + + /* XXX add e500mc support */ + + do_dcbz(env, addr, dcbz_size); } void helper_icbi(CPUPPCState *env, target_ulong addr) diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c index 0aee7a9063..1cc1c1649a 100644 --- a/target-ppc/mmu_helper.c +++ b/target-ppc/mmu_helper.c @@ -587,7 +587,7 @@ static inline int find_pte2(CPUPPCState *env, mmu_ctx_t *ctx, int is_64b, int h, } r = pte64_check(ctx, pte0, pte1, h, rw, type); - LOG_MMU("Load pte from " TARGET_FMT_lx " => " TARGET_FMT_lx " " + LOG_MMU("Load pte from %016" HWADDR_PRIx " => " TARGET_FMT_lx " " TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n", pteg_off + (i * 16), pte0, pte1, (int)(pte0 & 1), h, (int)((pte0 >> 1) & 1), ctx->ptem); @@ -602,7 +602,7 @@ static inline int find_pte2(CPUPPCState *env, mmu_ctx_t *ctx, int is_64b, int h, pte1 = ldl_phys(env->htab_base + pteg_off + (i * 8) + 4); } r = pte32_check(ctx, pte0, pte1, h, rw, type); - LOG_MMU("Load pte from " TARGET_FMT_lx " => " TARGET_FMT_lx " " + LOG_MMU("Load pte from %08" HWADDR_PRIx " => " TARGET_FMT_lx " " TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n", pteg_off + (i * 8), pte0, pte1, (int)(pte0 >> 31), h, (int)((pte0 >> 6) & 1), ctx->ptem); @@ -633,7 +633,7 @@ static inline int find_pte2(CPUPPCState *env, mmu_ctx_t *ctx, int is_64b, int h, } if (good != -1) { done: - LOG_MMU("found PTE at addr " TARGET_FMT_lx " prot=%01x ret=%d\n", + LOG_MMU("found PTE at addr %08" HWADDR_PRIx " prot=%01x ret=%d\n", ctx->raddr, ctx->prot, ret); /* Update page flags */ pte1 = ctx->raddr; @@ -2260,8 +2260,9 @@ void helper_store_601_batu(CPUPPCState *env, uint32_t nr, target_ulong value) void helper_store_601_batl(CPUPPCState *env, uint32_t nr, target_ulong value) { +#if !defined(FLUSH_ALL_TLBS) target_ulong mask; -#if defined(FLUSH_ALL_TLBS) +#else int do_inval; #endif diff --git a/target-ppc/translate.c b/target-ppc/translate.c index 798b7acfc9..2ac5794add 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -4118,29 +4118,21 @@ static void gen_dcbtst(DisasContext *ctx) /* dcbz */ static void gen_dcbz(DisasContext *ctx) { - TCGv t0; - gen_set_access_type(ctx, ACCESS_CACHE); - /* NIP cannot be restored if the memory exception comes from an helper */ - gen_update_nip(ctx, ctx->nip - 4); - t0 = tcg_temp_new(); - gen_addr_reg_index(ctx, t0); - gen_helper_dcbz(cpu_env, t0); - tcg_temp_free(t0); -} + TCGv tcgv_addr; + TCGv_i32 tcgv_is_dcbzl; + int is_dcbzl = ctx->opcode & 0x00200000 ? 1 : 0; -static void gen_dcbz_970(DisasContext *ctx) -{ - TCGv t0; gen_set_access_type(ctx, ACCESS_CACHE); /* NIP cannot be restored if the memory exception comes from an helper */ gen_update_nip(ctx, ctx->nip - 4); - t0 = tcg_temp_new(); - gen_addr_reg_index(ctx, t0); - if (ctx->opcode & 0x00200000) - gen_helper_dcbz(cpu_env, t0); - else - gen_helper_dcbz_970(cpu_env, t0); - tcg_temp_free(t0); + tcgv_addr = tcg_temp_new(); + tcgv_is_dcbzl = tcg_const_i32(is_dcbzl); + + gen_addr_reg_index(ctx, tcgv_addr); + gen_helper_dcbz(cpu_env, tcgv_addr, tcgv_is_dcbzl); + + tcg_temp_free(tcgv_addr); + tcg_temp_free_i32(tcgv_is_dcbzl); } /* dst / dstt */ @@ -8648,8 +8640,7 @@ GEN_HANDLER(dcbi, 0x1F, 0x16, 0x0E, 0x03E00001, PPC_CACHE), GEN_HANDLER(dcbst, 0x1F, 0x16, 0x01, 0x03E00001, PPC_CACHE), GEN_HANDLER(dcbt, 0x1F, 0x16, 0x08, 0x02000001, PPC_CACHE), GEN_HANDLER(dcbtst, 0x1F, 0x16, 0x07, 0x02000001, PPC_CACHE), -GEN_HANDLER(dcbz, 0x1F, 0x16, 0x1F, 0x03E00001, PPC_CACHE_DCBZ), -GEN_HANDLER2(dcbz_970, "dcbz", 0x1F, 0x16, 0x1F, 0x03C00001, PPC_CACHE_DCBZT), +GEN_HANDLER(dcbz, 0x1F, 0x16, 0x1F, 0x03C00001, PPC_CACHE_DCBZ), GEN_HANDLER(dst, 0x1F, 0x16, 0x0A, 0x01800001, PPC_ALTIVEC), GEN_HANDLER(dstst, 0x1F, 0x16, 0x0B, 0x02000001, PPC_ALTIVEC), GEN_HANDLER(dss, 0x1F, 0x16, 0x19, 0x019FF801, PPC_ALTIVEC), @@ -9698,7 +9689,7 @@ static inline void gen_intermediate_code_internal(CPUPPCState *env, } LOG_DISAS("translate opcode %08x (%02x %02x %02x) (%s)\n", ctx.opcode, opc1(ctx.opcode), opc2(ctx.opcode), - opc3(ctx.opcode), little_endian ? "little" : "big"); + opc3(ctx.opcode), ctx.le_mode ? "little" : "big"); if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { tcg_gen_debug_insn_start(ctx.nip); } diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 2d78529273..f0388508bc 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -28,6 +28,7 @@ #include <sysemu/kvm.h> #include "kvm_ppc.h" #include "sysemu/arch_init.h" +#include "sysemu/cpus.h" //#define PPC_DUMP_CPU //#define PPC_DEBUG_SPR @@ -6297,7 +6298,7 @@ static void init_proc_7457 (CPUPPCState *env) PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \ PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZT | \ + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \ PPC_MEM_SYNC | PPC_MEM_EIEIO | \ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ PPC_64B | PPC_ALTIVEC | \ @@ -6393,7 +6394,7 @@ static void init_proc_970 (CPUPPCState *env) PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \ PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZT | \ + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \ PPC_MEM_SYNC | PPC_MEM_EIEIO | \ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ PPC_64B | PPC_ALTIVEC | \ @@ -6495,7 +6496,7 @@ static void init_proc_970FX (CPUPPCState *env) PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \ PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZT | \ + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \ PPC_MEM_SYNC | PPC_MEM_EIEIO | \ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ PPC_64B | PPC_ALTIVEC | \ @@ -6585,7 +6586,7 @@ static void init_proc_970GX (CPUPPCState *env) PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \ PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZT | \ + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \ PPC_MEM_SYNC | PPC_MEM_EIEIO | \ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ PPC_64B | PPC_ALTIVEC | \ @@ -6676,7 +6677,7 @@ static void init_proc_970MP (CPUPPCState *env) PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \ PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZT | \ + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \ PPC_MEM_SYNC | PPC_MEM_EIEIO | \ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ PPC_64B | PPC_ALTIVEC | \ @@ -10036,6 +10037,17 @@ static void ppc_cpu_realize(Object *obj, Error **errp) PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); ppc_def_t *def = pcc->info; Error *local_err = NULL; +#if !defined(CONFIG_USER_ONLY) + int max_smt = kvm_enabled() ? kvmppc_smt_threads() : 1; +#endif + +#if !defined(CONFIG_USER_ONLY) + if (smp_threads > max_smt) { + fprintf(stderr, "Cannot support more than %d threads on PPC with %s\n", + max_smt, kvm_enabled() ? "KVM" : "TCG"); + exit(1); + } +#endif if (kvm_enabled()) { if (kvmppc_fixup_cpu(cpu) != 0) { @@ -10346,7 +10358,7 @@ PowerPCCPU *cpu_ppc_init(const char *cpu_model) if (err != NULL) { fprintf(stderr, "%s\n", error_get_pretty(err)); error_free(err); - object_delete(OBJECT(cpu)); + object_unref(OBJECT(cpu)); return NULL; } @@ -10566,6 +10578,8 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) pcc->parent_reset = cc->reset; cc->reset = ppc_cpu_reset; + + cc->class_by_name = ppc_cpu_class_by_name; } static const TypeInfo ppc_cpu_type_info = { diff --git a/target-s390x/Makefile.objs b/target-s390x/Makefile.objs index e728abf759..4e634173a4 100644 --- a/target-s390x/Makefile.objs +++ b/target-s390x/Makefile.objs @@ -1,4 +1,4 @@ obj-y += translate.o helper.o cpu.o interrupt.o obj-y += int_helper.o fpu_helper.o cc_helper.o mem_helper.o misc_helper.o -obj-$(CONFIG_SOFTMMU) += machine.o +obj-$(CONFIG_SOFTMMU) += ioinst.o obj-$(CONFIG_KVM) += kvm.o diff --git a/target-s390x/cpu.c b/target-s390x/cpu.c index 0b68db8305..d765e7b984 100644 --- a/target-s390x/cpu.c +++ b/target-s390x/cpu.c @@ -26,8 +26,8 @@ #include "cpu.h" #include "qemu-common.h" #include "qemu/timer.h" -#ifndef CONFIG_USER_ONLY #include "hw/hw.h" +#ifndef CONFIG_USER_ONLY #include "sysemu/arch_init.h" #endif @@ -70,7 +70,7 @@ static void s390_cpu_reset(CPUState *s) log_cpu_state(env, 0); } - s390_del_running_cpu(env); + s390_del_running_cpu(cpu); scc->parent_reset(s); @@ -135,13 +135,21 @@ static void s390_cpu_finalize(Object *obj) #endif } +static const VMStateDescription vmstate_s390_cpu = { + .name = "cpu", + .unmigratable = 1, +}; + static void s390_cpu_class_init(ObjectClass *oc, void *data) { S390CPUClass *scc = S390_CPU_CLASS(oc); CPUClass *cc = CPU_CLASS(scc); + DeviceClass *dc = DEVICE_CLASS(oc); scc->parent_reset = cc->reset; cc->reset = s390_cpu_reset; + + dc->vmsd = &vmstate_s390_cpu; } static const TypeInfo s390_cpu_type_info = { diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h index 1f2d94218a..01e59b99f0 100644 --- a/target-s390x/cpu.h +++ b/target-s390x/cpu.h @@ -50,6 +50,11 @@ #define MMU_USER_IDX 1 #define MAX_EXT_QUEUE 16 +#define MAX_IO_QUEUE 16 +#define MAX_MCHK_QUEUE 16 + +#define PSW_MCHK_MASK 0x0004000000000000 +#define PSW_IO_MASK 0x0200000000000000 typedef struct PSW { uint64_t mask; @@ -62,6 +67,17 @@ typedef struct ExtQueue { uint32_t param64; } ExtQueue; +typedef struct IOIntQueue { + uint16_t id; + uint16_t nr; + uint32_t parm; + uint32_t word; +} IOIntQueue; + +typedef struct MchkQueue { + uint16_t type; +} MchkQueue; + typedef struct CPUS390XState { uint64_t regs[16]; /* GP registers */ CPU_DoubleU fregs[16]; /* FP registers */ @@ -93,9 +109,17 @@ typedef struct CPUS390XState { uint64_t cregs[16]; /* control registers */ ExtQueue ext_queue[MAX_EXT_QUEUE]; - int pending_int; + IOIntQueue io_queue[MAX_IO_QUEUE][8]; + MchkQueue mchk_queue[MAX_MCHK_QUEUE]; + int pending_int; int ext_index; + int io_index[8]; + int mchk_index; + + uint64_t ckc; + uint64_t cputm; + uint32_t todpr; CPU_COMMON @@ -123,6 +147,9 @@ static inline void cpu_clone_regs(CPUS390XState *env, target_ulong newsp) } #endif +/* distinguish between 24 bit and 31 bit addressing */ +#define HIGH_ORDER_BIT 0x80000000 + /* Interrupt Codes */ /* Program Interrupts */ #define PGM_OPERATION 0x0001 @@ -300,8 +327,27 @@ int cpu_s390x_handle_mmu_fault (CPUS390XState *env, target_ulong address, int rw int mmu_idx); #define cpu_handle_mmu_fault cpu_s390x_handle_mmu_fault +#include "ioinst.h" #ifndef CONFIG_USER_ONLY +void *s390_cpu_physical_memory_map(CPUS390XState *env, hwaddr addr, hwaddr *len, + int is_write); +void s390_cpu_physical_memory_unmap(CPUS390XState *env, void *addr, hwaddr len, + int is_write); +static inline hwaddr decode_basedisp_s(CPUS390XState *env, uint32_t ipb) +{ + hwaddr addr = 0; + uint8_t reg; + + reg = ipb >> 28; + if (reg > 0) { + addr = env->regs[reg]; + } + addr += (ipb >> 16) & 0xfff; + + return addr; +} + void s390x_tod_timer(void *opaque); void s390x_cpu_timer(void *opaque); @@ -329,8 +375,8 @@ static inline void kvm_s390_interrupt_internal(S390CPU *cpu, int type, } #endif S390CPU *s390_cpu_addr2state(uint16_t cpu_addr); -void s390_add_running_cpu(CPUS390XState *env); -unsigned s390_del_running_cpu(CPUS390XState *env); +void s390_add_running_cpu(S390CPU *cpu); +unsigned s390_del_running_cpu(S390CPU *cpu); /* service interrupts are floating therefore we must not pass an cpustate */ void s390_sclp_extint(uint32_t parm); @@ -339,11 +385,11 @@ void s390_sclp_extint(uint32_t parm); extern const hwaddr virtio_size; #else -static inline void s390_add_running_cpu(CPUS390XState *env) +static inline void s390_add_running_cpu(S390CPU *cpu) { } -static inline unsigned s390_del_running_cpu(CPUS390XState *env) +static inline unsigned s390_del_running_cpu(S390CPU *cpu) { return 0; } @@ -351,6 +397,114 @@ static inline unsigned s390_del_running_cpu(CPUS390XState *env) void cpu_lock(void); void cpu_unlock(void); +typedef struct SubchDev SubchDev; + +#ifndef CONFIG_USER_ONLY +SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid, + uint16_t schid); +bool css_subch_visible(SubchDev *sch); +void css_conditional_io_interrupt(SubchDev *sch); +int css_do_stsch(SubchDev *sch, SCHIB *schib); +bool css_schid_final(uint8_t cssid, uint8_t ssid, uint16_t schid); +int css_do_msch(SubchDev *sch, SCHIB *schib); +int css_do_xsch(SubchDev *sch); +int css_do_csch(SubchDev *sch); +int css_do_hsch(SubchDev *sch); +int css_do_ssch(SubchDev *sch, ORB *orb); +int css_do_tsch(SubchDev *sch, IRB *irb); +int css_do_stcrw(CRW *crw); +int css_do_tpi(IOIntCode *int_code, int lowcore); +int css_collect_chp_desc(int m, uint8_t cssid, uint8_t f_chpid, uint8_t l_chpid, + int rfmt, void *buf); +void css_do_schm(uint8_t mbk, int update, int dct, uint64_t mbo); +int css_enable_mcsse(void); +int css_enable_mss(void); +int css_do_rsch(SubchDev *sch); +int css_do_rchp(uint8_t cssid, uint8_t chpid); +bool css_present(uint8_t cssid); +#else +static inline SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid, + uint16_t schid) +{ + return NULL; +} +static inline bool css_subch_visible(SubchDev *sch) +{ + return false; +} +static inline void css_conditional_io_interrupt(SubchDev *sch) +{ +} +static inline int css_do_stsch(SubchDev *sch, SCHIB *schib) +{ + return -ENODEV; +} +static inline bool css_schid_final(uint8_t cssid, uint8_t ssid, uint16_t schid) +{ + return true; +} +static inline int css_do_msch(SubchDev *sch, SCHIB *schib) +{ + return -ENODEV; +} +static inline int css_do_xsch(SubchDev *sch) +{ + return -ENODEV; +} +static inline int css_do_csch(SubchDev *sch) +{ + return -ENODEV; +} +static inline int css_do_hsch(SubchDev *sch) +{ + return -ENODEV; +} +static inline int css_do_ssch(SubchDev *sch, ORB *orb) +{ + return -ENODEV; +} +static inline int css_do_tsch(SubchDev *sch, IRB *irb) +{ + return -ENODEV; +} +static inline int css_do_stcrw(CRW *crw) +{ + return 1; +} +static inline int css_do_tpi(IOIntCode *int_code, int lowcore) +{ + return 0; +} +static inline int css_collect_chp_desc(int m, uint8_t cssid, uint8_t f_chpid, + int rfmt, uint8_t l_chpid, void *buf) +{ + return 0; +} +static inline void css_do_schm(uint8_t mbk, int update, int dct, uint64_t mbo) +{ +} +static inline int css_enable_mss(void) +{ + return -EINVAL; +} +static inline int css_enable_mcsse(void) +{ + return -EINVAL; +} +static inline int css_do_rsch(SubchDev *sch) +{ + return -ENODEV; +} +static inline int css_do_rchp(uint8_t cssid, uint8_t chpid) +{ + return -ENODEV; +} +static inline bool css_present(uint8_t cssid) +{ + return false; +} +#endif + static inline void cpu_set_tls(CPUS390XState *env, target_ulong newtls) { env->aregs[0] = newtls >> 32; @@ -370,10 +524,14 @@ void s390_cpu_list(FILE *f, fprintf_function cpu_fprintf); #define EXCP_EXT 1 /* external interrupt */ #define EXCP_SVC 2 /* supervisor call (syscall) */ #define EXCP_PGM 3 /* program interruption */ +#define EXCP_IO 7 /* I/O interrupt */ +#define EXCP_MCHK 8 /* machine check */ #define INTERRUPT_EXT (1 << 0) #define INTERRUPT_TOD (1 << 1) #define INTERRUPT_CPUTIMER (1 << 2) +#define INTERRUPT_IO (1 << 3) +#define INTERRUPT_MCHK (1 << 4) /* Program Status Word. */ #define S390_PSWM_REGNUM 0 @@ -817,9 +975,11 @@ static inline uint64_t time2tod(uint64_t ns) { return (ns << 9) / 125; } -static inline void cpu_inject_ext(CPUS390XState *env, uint32_t code, uint32_t param, +static inline void cpu_inject_ext(S390CPU *cpu, uint32_t code, uint32_t param, uint64_t param64) { + CPUS390XState *env = &cpu->env; + if (env->ext_index == MAX_EXT_QUEUE - 1) { /* ugh - can't queue anymore. Let's drop. */ return; @@ -836,6 +996,48 @@ static inline void cpu_inject_ext(CPUS390XState *env, uint32_t code, uint32_t pa cpu_interrupt(env, CPU_INTERRUPT_HARD); } +static inline void cpu_inject_io(S390CPU *cpu, uint16_t subchannel_id, + uint16_t subchannel_number, + uint32_t io_int_parm, uint32_t io_int_word) +{ + CPUS390XState *env = &cpu->env; + int isc = ffs(io_int_word << 2) - 1; + + if (env->io_index[isc] == MAX_IO_QUEUE - 1) { + /* ugh - can't queue anymore. Let's drop. */ + return; + } + + env->io_index[isc]++; + assert(env->io_index[isc] < MAX_IO_QUEUE); + + env->io_queue[env->io_index[isc]][isc].id = subchannel_id; + env->io_queue[env->io_index[isc]][isc].nr = subchannel_number; + env->io_queue[env->io_index[isc]][isc].parm = io_int_parm; + env->io_queue[env->io_index[isc]][isc].word = io_int_word; + + env->pending_int |= INTERRUPT_IO; + cpu_interrupt(env, CPU_INTERRUPT_HARD); +} + +static inline void cpu_inject_crw_mchk(S390CPU *cpu) +{ + CPUS390XState *env = &cpu->env; + + if (env->mchk_index == MAX_MCHK_QUEUE - 1) { + /* ugh - can't queue anymore. Let's drop. */ + return; + } + + env->mchk_index++; + assert(env->mchk_index < MAX_MCHK_QUEUE); + + env->mchk_queue[env->mchk_index].type = 1; + + env->pending_int |= INTERRUPT_MCHK; + cpu_interrupt(env, CPU_INTERRUPT_HARD); +} + static inline bool cpu_has_work(CPUState *cpu) { CPUS390XState *env = &S390_CPU(cpu)->env; @@ -859,4 +1061,52 @@ void program_interrupt(CPUS390XState *env, uint32_t code, int ilen); void QEMU_NORETURN runtime_exception(CPUS390XState *env, int excp, uintptr_t retaddr); +#include <sysemu/kvm.h> + +#ifdef CONFIG_KVM +void kvm_s390_io_interrupt(S390CPU *cpu, uint16_t subchannel_id, + uint16_t subchannel_nr, uint32_t io_int_parm, + uint32_t io_int_word); +void kvm_s390_crw_mchk(S390CPU *cpu); +void kvm_s390_enable_css_support(S390CPU *cpu); +#else +static inline void kvm_s390_io_interrupt(S390CPU *cpu, + uint16_t subchannel_id, + uint16_t subchannel_nr, + uint32_t io_int_parm, + uint32_t io_int_word) +{ +} +static inline void kvm_s390_crw_mchk(S390CPU *cpu) +{ +} +static inline void kvm_s390_enable_css_support(S390CPU *cpu) +{ +} +#endif + +static inline void s390_io_interrupt(S390CPU *cpu, + uint16_t subchannel_id, + uint16_t subchannel_nr, + uint32_t io_int_parm, + uint32_t io_int_word) +{ + if (kvm_enabled()) { + kvm_s390_io_interrupt(cpu, subchannel_id, subchannel_nr, io_int_parm, + io_int_word); + } else { + cpu_inject_io(cpu, subchannel_id, subchannel_nr, io_int_parm, + io_int_word); + } +} + +static inline void s390_crw_mchk(S390CPU *cpu) +{ + if (kvm_enabled()) { + kvm_s390_crw_mchk(cpu); + } else { + cpu_inject_crw_mchk(cpu); + } +} + #endif diff --git a/target-s390x/helper.c b/target-s390x/helper.c index 9a132e6d2c..043feb2739 100644 --- a/target-s390x/helper.c +++ b/target-s390x/helper.c @@ -387,7 +387,7 @@ int cpu_s390x_handle_mmu_fault(CPUS390XState *env, target_ulong orig_vaddr, int prot; DPRINTF("%s: address 0x%" PRIx64 " rw %d mmu_idx %d\n", - __func__, _vaddr, rw, mmu_idx); + __func__, orig_vaddr, rw, mmu_idx); orig_vaddr &= TARGET_PAGE_MASK; vaddr = orig_vaddr; @@ -404,8 +404,8 @@ int cpu_s390x_handle_mmu_fault(CPUS390XState *env, target_ulong orig_vaddr, /* check out of RAM access */ if (raddr > (ram_size + virtio_size)) { - DPRINTF("%s: aaddr %" PRIx64 " > ram_size %" PRIx64 "\n", __func__, - (uint64_t)aaddr, (uint64_t)ram_size); + DPRINTF("%s: raddr %" PRIx64 " > ram_size %" PRIx64 "\n", __func__, + (uint64_t)raddr, (uint64_t)ram_size); trigger_pgm_exception(env, PGM_ADDRESSING, ILEN_LATER); return 1; } @@ -441,8 +441,9 @@ hwaddr cpu_get_phys_page_debug(CPUS390XState *env, void load_psw(CPUS390XState *env, uint64_t mask, uint64_t addr) { if (mask & PSW_MASK_WAIT) { + S390CPU *cpu = s390_env_get_cpu(env); if (!(mask & (PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK))) { - if (s390_del_running_cpu(env) == 0) { + if (s390_del_running_cpu(cpu) == 0) { #ifndef CONFIG_USER_ONLY qemu_system_shutdown_request(); #endif @@ -471,13 +472,56 @@ static uint64_t get_psw_mask(CPUS390XState *env) return r; } +static LowCore *cpu_map_lowcore(CPUS390XState *env) +{ + LowCore *lowcore; + hwaddr len = sizeof(LowCore); + + lowcore = cpu_physical_memory_map(env->psa, &len, 1); + + if (len < sizeof(LowCore)) { + cpu_abort(env, "Could not map lowcore\n"); + } + + return lowcore; +} + +static void cpu_unmap_lowcore(LowCore *lowcore) +{ + cpu_physical_memory_unmap(lowcore, sizeof(LowCore), 1, sizeof(LowCore)); +} + +void *s390_cpu_physical_memory_map(CPUS390XState *env, hwaddr addr, hwaddr *len, + int is_write) +{ + hwaddr start = addr; + + /* Mind the prefix area. */ + if (addr < 8192) { + /* Map the lowcore. */ + start += env->psa; + *len = MIN(*len, 8192 - addr); + } else if ((addr >= env->psa) && (addr < env->psa + 8192)) { + /* Map the 0 page. */ + start -= env->psa; + *len = MIN(*len, 8192 - start); + } + + return cpu_physical_memory_map(start, len, is_write); +} + +void s390_cpu_physical_memory_unmap(CPUS390XState *env, void *addr, hwaddr len, + int is_write) +{ + cpu_physical_memory_unmap(addr, len, is_write, len); +} + static void do_svc_interrupt(CPUS390XState *env) { uint64_t mask, addr; LowCore *lowcore; - hwaddr len = TARGET_PAGE_SIZE; - lowcore = cpu_physical_memory_map(env->psa, &len, 1); + lowcore = cpu_map_lowcore(env); lowcore->svc_code = cpu_to_be16(env->int_svc_code); lowcore->svc_ilen = cpu_to_be16(env->int_svc_ilen); @@ -486,7 +530,7 @@ static void do_svc_interrupt(CPUS390XState *env) mask = be64_to_cpu(lowcore->svc_new_psw.mask); addr = be64_to_cpu(lowcore->svc_new_psw.addr); - cpu_physical_memory_unmap(lowcore, len, 1, len); + cpu_unmap_lowcore(lowcore); load_psw(env, mask, addr); } @@ -495,7 +539,6 @@ static void do_program_interrupt(CPUS390XState *env) { uint64_t mask, addr; LowCore *lowcore; - hwaddr len = TARGET_PAGE_SIZE; int ilen = env->int_pgm_ilen; switch (ilen) { @@ -513,7 +556,7 @@ static void do_program_interrupt(CPUS390XState *env) qemu_log_mask(CPU_LOG_INT, "%s: code=0x%x ilen=%d\n", __func__, env->int_pgm_code, ilen); - lowcore = cpu_physical_memory_map(env->psa, &len, 1); + lowcore = cpu_map_lowcore(env); lowcore->pgm_ilen = cpu_to_be16(ilen); lowcore->pgm_code = cpu_to_be16(env->int_pgm_code); @@ -522,7 +565,7 @@ static void do_program_interrupt(CPUS390XState *env) mask = be64_to_cpu(lowcore->program_new_psw.mask); addr = be64_to_cpu(lowcore->program_new_psw.addr); - cpu_physical_memory_unmap(lowcore, len, 1, len); + cpu_unmap_lowcore(lowcore); DPRINTF("%s: %x %x %" PRIx64 " %" PRIx64 "\n", __func__, env->int_pgm_code, ilen, env->psw.mask, @@ -537,7 +580,6 @@ static void do_ext_interrupt(CPUS390XState *env) { uint64_t mask, addr; LowCore *lowcore; - hwaddr len = TARGET_PAGE_SIZE; ExtQueue *q; if (!(env->psw.mask & PSW_MASK_EXT)) { @@ -549,7 +591,7 @@ static void do_ext_interrupt(CPUS390XState *env) } q = &env->ext_queue[env->ext_index]; - lowcore = cpu_physical_memory_map(env->psa, &len, 1); + lowcore = cpu_map_lowcore(env); lowcore->ext_int_code = cpu_to_be16(q->code); lowcore->ext_params = cpu_to_be32(q->param); @@ -560,7 +602,7 @@ static void do_ext_interrupt(CPUS390XState *env) mask = be64_to_cpu(lowcore->external_new_psw.mask); addr = be64_to_cpu(lowcore->external_new_psw.addr); - cpu_physical_memory_unmap(lowcore, len, 1, len); + cpu_unmap_lowcore(lowcore); env->ext_index--; if (env->ext_index == -1) { @@ -573,12 +615,142 @@ static void do_ext_interrupt(CPUS390XState *env) load_psw(env, mask, addr); } +static void do_io_interrupt(CPUS390XState *env) +{ + uint64_t mask = 0, addr = 0; + LowCore *lowcore; + IOIntQueue *q; + uint8_t isc; + int disable = 1; + int found = 0; + + if (!(env->psw.mask & PSW_MASK_IO)) { + cpu_abort(env, "I/O int w/o I/O mask\n"); + } + + for (isc = 0; isc < ARRAY_SIZE(env->io_index); isc++) { + if (env->io_index[isc] < 0) { + continue; + } + if (env->io_index[isc] > MAX_IO_QUEUE) { + cpu_abort(env, "I/O queue overrun for isc %d: %d\n", + isc, env->io_index[isc]); + } + + q = &env->io_queue[env->io_index[isc]][isc]; + if (!(env->cregs[6] & q->word)) { + disable = 0; + continue; + } + found = 1; + lowcore = cpu_map_lowcore(env); + + lowcore->subchannel_id = cpu_to_be16(q->id); + lowcore->subchannel_nr = cpu_to_be16(q->nr); + lowcore->io_int_parm = cpu_to_be32(q->parm); + lowcore->io_int_word = cpu_to_be32(q->word); + lowcore->io_old_psw.mask = cpu_to_be64(get_psw_mask(env)); + lowcore->io_old_psw.addr = cpu_to_be64(env->psw.addr); + mask = be64_to_cpu(lowcore->io_new_psw.mask); + addr = be64_to_cpu(lowcore->io_new_psw.addr); + + cpu_unmap_lowcore(lowcore); + + env->io_index[isc]--; + if (env->io_index[isc] >= 0) { + disable = 0; + } + break; + } + + if (disable) { + env->pending_int &= ~INTERRUPT_IO; + } + + if (found) { + DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__, + env->psw.mask, env->psw.addr); + load_psw(env, mask, addr); + } +} + +static void do_mchk_interrupt(CPUS390XState *env) +{ + uint64_t mask, addr; + LowCore *lowcore; + MchkQueue *q; + int i; + + if (!(env->psw.mask & PSW_MASK_MCHECK)) { + cpu_abort(env, "Machine check w/o mchk mask\n"); + } + + if (env->mchk_index < 0 || env->mchk_index > MAX_MCHK_QUEUE) { + cpu_abort(env, "Mchk queue overrun: %d\n", env->mchk_index); + } + + q = &env->mchk_queue[env->mchk_index]; + + if (q->type != 1) { + /* Don't know how to handle this... */ + cpu_abort(env, "Unknown machine check type %d\n", q->type); + } + if (!(env->cregs[14] & (1 << 28))) { + /* CRW machine checks disabled */ + return; + } + + lowcore = cpu_map_lowcore(env); + + for (i = 0; i < 16; i++) { + lowcore->floating_pt_save_area[i] = cpu_to_be64(env->fregs[i].ll); + lowcore->gpregs_save_area[i] = cpu_to_be64(env->regs[i]); + lowcore->access_regs_save_area[i] = cpu_to_be32(env->aregs[i]); + lowcore->cregs_save_area[i] = cpu_to_be64(env->cregs[i]); + } + lowcore->prefixreg_save_area = cpu_to_be32(env->psa); + lowcore->fpt_creg_save_area = cpu_to_be32(env->fpc); + lowcore->tod_progreg_save_area = cpu_to_be32(env->todpr); + lowcore->cpu_timer_save_area[0] = cpu_to_be32(env->cputm >> 32); + lowcore->cpu_timer_save_area[1] = cpu_to_be32((uint32_t)env->cputm); + lowcore->clock_comp_save_area[0] = cpu_to_be32(env->ckc >> 32); + lowcore->clock_comp_save_area[1] = cpu_to_be32((uint32_t)env->ckc); + + lowcore->mcck_interruption_code[0] = cpu_to_be32(0x00400f1d); + lowcore->mcck_interruption_code[1] = cpu_to_be32(0x40330000); + lowcore->mcck_old_psw.mask = cpu_to_be64(get_psw_mask(env)); + lowcore->mcck_old_psw.addr = cpu_to_be64(env->psw.addr); + mask = be64_to_cpu(lowcore->mcck_new_psw.mask); + addr = be64_to_cpu(lowcore->mcck_new_psw.addr); + + cpu_unmap_lowcore(lowcore); + + env->mchk_index--; + if (env->mchk_index == -1) { + env->pending_int &= ~INTERRUPT_MCHK; + } + + DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__, + env->psw.mask, env->psw.addr); + + load_psw(env, mask, addr); +} + void do_interrupt(CPUS390XState *env) { + S390CPU *cpu = s390_env_get_cpu(env); + qemu_log_mask(CPU_LOG_INT, "%s: %d at pc=%" PRIx64 "\n", __func__, env->exception_index, env->psw.addr); - s390_add_running_cpu(env); + s390_add_running_cpu(cpu); + /* handle machine checks */ + if ((env->psw.mask & PSW_MASK_MCHECK) && + (env->exception_index == -1)) { + if (env->pending_int & INTERRUPT_MCHK) { + env->exception_index = EXCP_MCHK; + } + } /* handle external interrupts */ if ((env->psw.mask & PSW_MASK_EXT) && env->exception_index == -1) { @@ -586,17 +758,24 @@ void do_interrupt(CPUS390XState *env) /* code is already in env */ env->exception_index = EXCP_EXT; } else if (env->pending_int & INTERRUPT_TOD) { - cpu_inject_ext(env, 0x1004, 0, 0); + cpu_inject_ext(cpu, 0x1004, 0, 0); env->exception_index = EXCP_EXT; env->pending_int &= ~INTERRUPT_EXT; env->pending_int &= ~INTERRUPT_TOD; } else if (env->pending_int & INTERRUPT_CPUTIMER) { - cpu_inject_ext(env, 0x1005, 0, 0); + cpu_inject_ext(cpu, 0x1005, 0, 0); env->exception_index = EXCP_EXT; env->pending_int &= ~INTERRUPT_EXT; env->pending_int &= ~INTERRUPT_TOD; } } + /* handle I/O interrupts */ + if ((env->psw.mask & PSW_MASK_IO) && + (env->exception_index == -1)) { + if (env->pending_int & INTERRUPT_IO) { + env->exception_index = EXCP_IO; + } + } switch (env->exception_index) { case EXCP_PGM: @@ -608,6 +787,12 @@ void do_interrupt(CPUS390XState *env) case EXCP_EXT: do_ext_interrupt(env); break; + case EXCP_IO: + do_io_interrupt(env); + break; + case EXCP_MCHK: + do_mchk_interrupt(env); + break; } env->exception_index = -1; diff --git a/target-s390x/interrupt.c b/target-s390x/interrupt.c index e51519dbd7..6d6580de3a 100644 --- a/target-s390x/interrupt.c +++ b/target-s390x/interrupt.c @@ -24,7 +24,7 @@ void s390_sclp_extint(uint32_t parm) #endif } else { env->psw.addr += 4; - cpu_inject_ext(env, EXT_SERVICE, parm, 0); + cpu_inject_ext(dummy_cpu, EXT_SERVICE, parm, 0); } } #endif diff --git a/target-s390x/ioinst.c b/target-s390x/ioinst.c new file mode 100644 index 0000000000..e3531f365e --- /dev/null +++ b/target-s390x/ioinst.c @@ -0,0 +1,761 @@ +/* + * I/O instructions for S/390 + * + * Copyright 2012 IBM Corp. + * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#include <sys/types.h> + +#include "cpu.h" +#include "ioinst.h" +#include "trace.h" + +int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid, + int *schid) +{ + if (!IOINST_SCHID_ONE(value)) { + return -EINVAL; + } + if (!IOINST_SCHID_M(value)) { + if (IOINST_SCHID_CSSID(value)) { + return -EINVAL; + } + *cssid = 0; + *m = 0; + } else { + *cssid = IOINST_SCHID_CSSID(value); + *m = 1; + } + *ssid = IOINST_SCHID_SSID(value); + *schid = IOINST_SCHID_NR(value); + return 0; +} + +int ioinst_handle_xsch(CPUS390XState *env, uint64_t reg1) +{ + int cssid, ssid, schid, m; + SubchDev *sch; + int ret = -ENODEV; + int cc; + + if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { + program_interrupt(env, PGM_OPERAND, 2); + return -EIO; + } + trace_ioinst_sch_id("xsch", cssid, ssid, schid); + sch = css_find_subch(m, cssid, ssid, schid); + if (sch && css_subch_visible(sch)) { + ret = css_do_xsch(sch); + } + switch (ret) { + case -ENODEV: + cc = 3; + break; + case -EBUSY: + cc = 2; + break; + case 0: + cc = 0; + break; + default: + cc = 1; + break; + } + + return cc; +} + +int ioinst_handle_csch(CPUS390XState *env, uint64_t reg1) +{ + int cssid, ssid, schid, m; + SubchDev *sch; + int ret = -ENODEV; + int cc; + + if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { + program_interrupt(env, PGM_OPERAND, 2); + return -EIO; + } + trace_ioinst_sch_id("csch", cssid, ssid, schid); + sch = css_find_subch(m, cssid, ssid, schid); + if (sch && css_subch_visible(sch)) { + ret = css_do_csch(sch); + } + if (ret == -ENODEV) { + cc = 3; + } else { + cc = 0; + } + return cc; +} + +int ioinst_handle_hsch(CPUS390XState *env, uint64_t reg1) +{ + int cssid, ssid, schid, m; + SubchDev *sch; + int ret = -ENODEV; + int cc; + + if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { + program_interrupt(env, PGM_OPERAND, 2); + return -EIO; + } + trace_ioinst_sch_id("hsch", cssid, ssid, schid); + sch = css_find_subch(m, cssid, ssid, schid); + if (sch && css_subch_visible(sch)) { + ret = css_do_hsch(sch); + } + switch (ret) { + case -ENODEV: + cc = 3; + break; + case -EBUSY: + cc = 2; + break; + case 0: + cc = 0; + break; + default: + cc = 1; + break; + } + + return cc; +} + +static int ioinst_schib_valid(SCHIB *schib) +{ + if ((schib->pmcw.flags & PMCW_FLAGS_MASK_INVALID) || + (schib->pmcw.chars & PMCW_CHARS_MASK_INVALID)) { + return 0; + } + /* Disallow extended measurements for now. */ + if (schib->pmcw.chars & PMCW_CHARS_MASK_XMWME) { + return 0; + } + return 1; +} + +int ioinst_handle_msch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) +{ + int cssid, ssid, schid, m; + SubchDev *sch; + SCHIB *schib; + uint64_t addr; + int ret = -ENODEV; + int cc; + hwaddr len = sizeof(*schib); + + if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { + program_interrupt(env, PGM_OPERAND, 2); + return -EIO; + } + trace_ioinst_sch_id("msch", cssid, ssid, schid); + addr = decode_basedisp_s(env, ipb); + schib = s390_cpu_physical_memory_map(env, addr, &len, 0); + if (!schib || len != sizeof(*schib)) { + program_interrupt(env, PGM_SPECIFICATION, 2); + cc = -EIO; + goto out; + } + if (!ioinst_schib_valid(schib)) { + program_interrupt(env, PGM_OPERAND, 2); + cc = -EIO; + goto out; + } + sch = css_find_subch(m, cssid, ssid, schid); + if (sch && css_subch_visible(sch)) { + ret = css_do_msch(sch, schib); + } + switch (ret) { + case -ENODEV: + cc = 3; + break; + case -EBUSY: + cc = 2; + break; + case 0: + cc = 0; + break; + default: + cc = 1; + break; + } +out: + s390_cpu_physical_memory_unmap(env, schib, len, 0); + return cc; +} + +static void copy_orb_from_guest(ORB *dest, const ORB *src) +{ + dest->intparm = be32_to_cpu(src->intparm); + dest->ctrl0 = be16_to_cpu(src->ctrl0); + dest->lpm = src->lpm; + dest->ctrl1 = src->ctrl1; + dest->cpa = be32_to_cpu(src->cpa); +} + +static int ioinst_orb_valid(ORB *orb) +{ + if ((orb->ctrl0 & ORB_CTRL0_MASK_INVALID) || + (orb->ctrl1 & ORB_CTRL1_MASK_INVALID)) { + return 0; + } + if ((orb->cpa & HIGH_ORDER_BIT) != 0) { + return 0; + } + return 1; +} + +int ioinst_handle_ssch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) +{ + int cssid, ssid, schid, m; + SubchDev *sch; + ORB *orig_orb, orb; + uint64_t addr; + int ret = -ENODEV; + int cc; + hwaddr len = sizeof(*orig_orb); + + if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { + program_interrupt(env, PGM_OPERAND, 2); + return -EIO; + } + trace_ioinst_sch_id("ssch", cssid, ssid, schid); + addr = decode_basedisp_s(env, ipb); + orig_orb = s390_cpu_physical_memory_map(env, addr, &len, 0); + if (!orig_orb || len != sizeof(*orig_orb)) { + program_interrupt(env, PGM_SPECIFICATION, 2); + cc = -EIO; + goto out; + } + copy_orb_from_guest(&orb, orig_orb); + if (!ioinst_orb_valid(&orb)) { + program_interrupt(env, PGM_OPERAND, 2); + cc = -EIO; + goto out; + } + sch = css_find_subch(m, cssid, ssid, schid); + if (sch && css_subch_visible(sch)) { + ret = css_do_ssch(sch, &orb); + } + switch (ret) { + case -ENODEV: + cc = 3; + break; + case -EBUSY: + cc = 2; + break; + case 0: + cc = 0; + break; + default: + cc = 1; + break; + } + +out: + s390_cpu_physical_memory_unmap(env, orig_orb, len, 0); + return cc; +} + +int ioinst_handle_stcrw(CPUS390XState *env, uint32_t ipb) +{ + CRW *crw; + uint64_t addr; + int cc; + hwaddr len = sizeof(*crw); + + addr = decode_basedisp_s(env, ipb); + crw = s390_cpu_physical_memory_map(env, addr, &len, 1); + if (!crw || len != sizeof(*crw)) { + program_interrupt(env, PGM_SPECIFICATION, 2); + cc = -EIO; + goto out; + } + cc = css_do_stcrw(crw); + /* 0 - crw stored, 1 - zeroes stored */ +out: + s390_cpu_physical_memory_unmap(env, crw, len, 1); + return cc; +} + +int ioinst_handle_stsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) +{ + int cssid, ssid, schid, m; + SubchDev *sch; + uint64_t addr; + int cc; + SCHIB *schib; + hwaddr len = sizeof(*schib); + + if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { + program_interrupt(env, PGM_OPERAND, 2); + return -EIO; + } + trace_ioinst_sch_id("stsch", cssid, ssid, schid); + addr = decode_basedisp_s(env, ipb); + schib = s390_cpu_physical_memory_map(env, addr, &len, 1); + if (!schib || len != sizeof(*schib)) { + program_interrupt(env, PGM_SPECIFICATION, 2); + cc = -EIO; + goto out; + } + sch = css_find_subch(m, cssid, ssid, schid); + if (sch) { + if (css_subch_visible(sch)) { + css_do_stsch(sch, schib); + cc = 0; + } else { + /* Indicate no more subchannels in this css/ss */ + cc = 3; + } + } else { + if (css_schid_final(cssid, ssid, schid)) { + cc = 3; /* No more subchannels in this css/ss */ + } else { + /* Store an empty schib. */ + memset(schib, 0, sizeof(*schib)); + cc = 0; + } + } +out: + s390_cpu_physical_memory_unmap(env, schib, len, 1); + return cc; +} + +int ioinst_handle_tsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) +{ + int cssid, ssid, schid, m; + SubchDev *sch; + IRB *irb; + uint64_t addr; + int ret = -ENODEV; + int cc; + hwaddr len = sizeof(*irb); + + if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { + program_interrupt(env, PGM_OPERAND, 2); + return -EIO; + } + trace_ioinst_sch_id("tsch", cssid, ssid, schid); + addr = decode_basedisp_s(env, ipb); + irb = s390_cpu_physical_memory_map(env, addr, &len, 1); + if (!irb || len != sizeof(*irb)) { + program_interrupt(env, PGM_SPECIFICATION, 2); + cc = -EIO; + goto out; + } + sch = css_find_subch(m, cssid, ssid, schid); + if (sch && css_subch_visible(sch)) { + ret = css_do_tsch(sch, irb); + /* 0 - status pending, 1 - not status pending */ + cc = ret; + } else { + cc = 3; + } +out: + s390_cpu_physical_memory_unmap(env, irb, sizeof(*irb), 1); + return cc; +} + +typedef struct ChscReq { + uint16_t len; + uint16_t command; + uint32_t param0; + uint32_t param1; + uint32_t param2; +} QEMU_PACKED ChscReq; + +typedef struct ChscResp { + uint16_t len; + uint16_t code; + uint32_t param; + char data[0]; +} QEMU_PACKED ChscResp; + +#define CHSC_MIN_RESP_LEN 0x0008 + +#define CHSC_SCPD 0x0002 +#define CHSC_SCSC 0x0010 +#define CHSC_SDA 0x0031 + +#define CHSC_SCPD_0_M 0x20000000 +#define CHSC_SCPD_0_C 0x10000000 +#define CHSC_SCPD_0_FMT 0x0f000000 +#define CHSC_SCPD_0_CSSID 0x00ff0000 +#define CHSC_SCPD_0_RFMT 0x00000f00 +#define CHSC_SCPD_0_RES 0xc000f000 +#define CHSC_SCPD_1_RES 0xffffff00 +#define CHSC_SCPD_01_CHPID 0x000000ff +static void ioinst_handle_chsc_scpd(ChscReq *req, ChscResp *res) +{ + uint16_t len = be16_to_cpu(req->len); + uint32_t param0 = be32_to_cpu(req->param0); + uint32_t param1 = be32_to_cpu(req->param1); + uint16_t resp_code; + int rfmt; + uint16_t cssid; + uint8_t f_chpid, l_chpid; + int desc_size; + int m; + + rfmt = (param0 & CHSC_SCPD_0_RFMT) >> 8; + if ((rfmt == 0) || (rfmt == 1)) { + rfmt = !!(param0 & CHSC_SCPD_0_C); + } + if ((len != 0x0010) || (param0 & CHSC_SCPD_0_RES) || + (param1 & CHSC_SCPD_1_RES) || req->param2) { + resp_code = 0x0003; + goto out_err; + } + if (param0 & CHSC_SCPD_0_FMT) { + resp_code = 0x0007; + goto out_err; + } + cssid = (param0 & CHSC_SCPD_0_CSSID) >> 16; + m = param0 & CHSC_SCPD_0_M; + if (cssid != 0) { + if (!m || !css_present(cssid)) { + resp_code = 0x0008; + goto out_err; + } + } + f_chpid = param0 & CHSC_SCPD_01_CHPID; + l_chpid = param1 & CHSC_SCPD_01_CHPID; + if (l_chpid < f_chpid) { + resp_code = 0x0003; + goto out_err; + } + /* css_collect_chp_desc() is endian-aware */ + desc_size = css_collect_chp_desc(m, cssid, f_chpid, l_chpid, rfmt, + &res->data); + res->code = cpu_to_be16(0x0001); + res->len = cpu_to_be16(8 + desc_size); + res->param = cpu_to_be32(rfmt); + return; + + out_err: + res->code = cpu_to_be16(resp_code); + res->len = cpu_to_be16(CHSC_MIN_RESP_LEN); + res->param = cpu_to_be32(rfmt); +} + +#define CHSC_SCSC_0_M 0x20000000 +#define CHSC_SCSC_0_FMT 0x000f0000 +#define CHSC_SCSC_0_CSSID 0x0000ff00 +#define CHSC_SCSC_0_RES 0xdff000ff +static void ioinst_handle_chsc_scsc(ChscReq *req, ChscResp *res) +{ + uint16_t len = be16_to_cpu(req->len); + uint32_t param0 = be32_to_cpu(req->param0); + uint8_t cssid; + uint16_t resp_code; + uint32_t general_chars[510]; + uint32_t chsc_chars[508]; + + if (len != 0x0010) { + resp_code = 0x0003; + goto out_err; + } + + if (param0 & CHSC_SCSC_0_FMT) { + resp_code = 0x0007; + goto out_err; + } + cssid = (param0 & CHSC_SCSC_0_CSSID) >> 8; + if (cssid != 0) { + if (!(param0 & CHSC_SCSC_0_M) || !css_present(cssid)) { + resp_code = 0x0008; + goto out_err; + } + } + if ((param0 & CHSC_SCSC_0_RES) || req->param1 || req->param2) { + resp_code = 0x0003; + goto out_err; + } + res->code = cpu_to_be16(0x0001); + res->len = cpu_to_be16(4080); + res->param = 0; + + memset(general_chars, 0, sizeof(general_chars)); + memset(chsc_chars, 0, sizeof(chsc_chars)); + + general_chars[0] = cpu_to_be32(0x03000000); + general_chars[1] = cpu_to_be32(0x00059000); + + chsc_chars[0] = cpu_to_be32(0x40000000); + chsc_chars[3] = cpu_to_be32(0x00040000); + + memcpy(res->data, general_chars, sizeof(general_chars)); + memcpy(res->data + sizeof(general_chars), chsc_chars, sizeof(chsc_chars)); + return; + + out_err: + res->code = cpu_to_be16(resp_code); + res->len = cpu_to_be16(CHSC_MIN_RESP_LEN); + res->param = 0; +} + +#define CHSC_SDA_0_FMT 0x0f000000 +#define CHSC_SDA_0_OC 0x0000ffff +#define CHSC_SDA_0_RES 0xf0ff0000 +#define CHSC_SDA_OC_MCSSE 0x0 +#define CHSC_SDA_OC_MSS 0x2 +static void ioinst_handle_chsc_sda(ChscReq *req, ChscResp *res) +{ + uint16_t resp_code = 0x0001; + uint16_t len = be16_to_cpu(req->len); + uint32_t param0 = be32_to_cpu(req->param0); + uint16_t oc; + int ret; + + if ((len != 0x0400) || (param0 & CHSC_SDA_0_RES)) { + resp_code = 0x0003; + goto out; + } + + if (param0 & CHSC_SDA_0_FMT) { + resp_code = 0x0007; + goto out; + } + + oc = param0 & CHSC_SDA_0_OC; + switch (oc) { + case CHSC_SDA_OC_MCSSE: + ret = css_enable_mcsse(); + if (ret == -EINVAL) { + resp_code = 0x0101; + goto out; + } + break; + case CHSC_SDA_OC_MSS: + ret = css_enable_mss(); + if (ret == -EINVAL) { + resp_code = 0x0101; + goto out; + } + break; + default: + resp_code = 0x0003; + goto out; + } + +out: + res->code = cpu_to_be16(resp_code); + res->len = cpu_to_be16(CHSC_MIN_RESP_LEN); + res->param = 0; +} + +static void ioinst_handle_chsc_unimplemented(ChscResp *res) +{ + res->len = cpu_to_be16(CHSC_MIN_RESP_LEN); + res->code = cpu_to_be16(0x0004); + res->param = 0; +} + +int ioinst_handle_chsc(CPUS390XState *env, uint32_t ipb) +{ + ChscReq *req; + ChscResp *res; + uint64_t addr; + int reg; + uint16_t len; + uint16_t command; + hwaddr map_size = TARGET_PAGE_SIZE; + int ret = 0; + + trace_ioinst("chsc"); + reg = (ipb >> 20) & 0x00f; + addr = env->regs[reg]; + /* Page boundary? */ + if (addr & 0xfff) { + program_interrupt(env, PGM_SPECIFICATION, 2); + return -EIO; + } + req = s390_cpu_physical_memory_map(env, addr, &map_size, 1); + if (!req || map_size != TARGET_PAGE_SIZE) { + program_interrupt(env, PGM_SPECIFICATION, 2); + ret = -EIO; + goto out; + } + len = be16_to_cpu(req->len); + /* Length field valid? */ + if ((len < 16) || (len > 4088) || (len & 7)) { + program_interrupt(env, PGM_OPERAND, 2); + ret = -EIO; + goto out; + } + memset((char *)req + len, 0, TARGET_PAGE_SIZE - len); + res = (void *)((char *)req + len); + command = be16_to_cpu(req->command); + trace_ioinst_chsc_cmd(command, len); + switch (command) { + case CHSC_SCSC: + ioinst_handle_chsc_scsc(req, res); + break; + case CHSC_SCPD: + ioinst_handle_chsc_scpd(req, res); + break; + case CHSC_SDA: + ioinst_handle_chsc_sda(req, res); + break; + default: + ioinst_handle_chsc_unimplemented(res); + break; + } + +out: + s390_cpu_physical_memory_unmap(env, req, map_size, 1); + return ret; +} + +int ioinst_handle_tpi(CPUS390XState *env, uint32_t ipb) +{ + uint64_t addr; + int lowcore; + IOIntCode *int_code; + hwaddr len, orig_len; + int ret; + + trace_ioinst("tpi"); + addr = decode_basedisp_s(env, ipb); + lowcore = addr ? 0 : 1; + len = lowcore ? 8 /* two words */ : 12 /* three words */; + orig_len = len; + int_code = s390_cpu_physical_memory_map(env, addr, &len, 1); + if (!int_code || (len != orig_len)) { + program_interrupt(env, PGM_SPECIFICATION, 2); + ret = -EIO; + goto out; + } + ret = css_do_tpi(int_code, lowcore); +out: + s390_cpu_physical_memory_unmap(env, int_code, len, 1); + return ret; +} + +#define SCHM_REG1_RES(_reg) (_reg & 0x000000000ffffffc) +#define SCHM_REG1_MBK(_reg) ((_reg & 0x00000000f0000000) >> 28) +#define SCHM_REG1_UPD(_reg) ((_reg & 0x0000000000000002) >> 1) +#define SCHM_REG1_DCT(_reg) (_reg & 0x0000000000000001) + +int ioinst_handle_schm(CPUS390XState *env, uint64_t reg1, uint64_t reg2, + uint32_t ipb) +{ + uint8_t mbk; + int update; + int dct; + + trace_ioinst("schm"); + + if (SCHM_REG1_RES(reg1)) { + program_interrupt(env, PGM_OPERAND, 2); + return -EIO; + } + + mbk = SCHM_REG1_MBK(reg1); + update = SCHM_REG1_UPD(reg1); + dct = SCHM_REG1_DCT(reg1); + + if (update && (reg2 & 0x0000000000000fff)) { + program_interrupt(env, PGM_OPERAND, 2); + return -EIO; + } + + css_do_schm(mbk, update, dct, update ? reg2 : 0); + + return 0; +} + +int ioinst_handle_rsch(CPUS390XState *env, uint64_t reg1) +{ + int cssid, ssid, schid, m; + SubchDev *sch; + int ret = -ENODEV; + int cc; + + if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { + program_interrupt(env, PGM_OPERAND, 2); + return -EIO; + } + trace_ioinst_sch_id("rsch", cssid, ssid, schid); + sch = css_find_subch(m, cssid, ssid, schid); + if (sch && css_subch_visible(sch)) { + ret = css_do_rsch(sch); + } + switch (ret) { + case -ENODEV: + cc = 3; + break; + case -EINVAL: + cc = 2; + break; + case 0: + cc = 0; + break; + default: + cc = 1; + break; + } + + return cc; + +} + +#define RCHP_REG1_RES(_reg) (_reg & 0x00000000ff00ff00) +#define RCHP_REG1_CSSID(_reg) ((_reg & 0x0000000000ff0000) >> 16) +#define RCHP_REG1_CHPID(_reg) (_reg & 0x00000000000000ff) +int ioinst_handle_rchp(CPUS390XState *env, uint64_t reg1) +{ + int cc; + uint8_t cssid; + uint8_t chpid; + int ret; + + if (RCHP_REG1_RES(reg1)) { + program_interrupt(env, PGM_OPERAND, 2); + return -EIO; + } + + cssid = RCHP_REG1_CSSID(reg1); + chpid = RCHP_REG1_CHPID(reg1); + + trace_ioinst_chp_id("rchp", cssid, chpid); + + ret = css_do_rchp(cssid, chpid); + + switch (ret) { + case -ENODEV: + cc = 3; + break; + case -EBUSY: + cc = 2; + break; + case 0: + cc = 0; + break; + default: + /* Invalid channel subsystem. */ + program_interrupt(env, PGM_OPERAND, 2); + return -EIO; + } + + return cc; +} + +#define SAL_REG1_INVALID(_reg) (_reg & 0x0000000080000000) +int ioinst_handle_sal(CPUS390XState *env, uint64_t reg1) +{ + /* We do not provide address limit checking, so let's suppress it. */ + if (SAL_REG1_INVALID(reg1) || reg1 & 0x000000000000ffff) { + program_interrupt(env, PGM_OPERAND, 2); + return -EIO; + } + return 0; +} diff --git a/target-s390x/ioinst.h b/target-s390x/ioinst.h new file mode 100644 index 0000000000..d5a43f4a71 --- /dev/null +++ b/target-s390x/ioinst.h @@ -0,0 +1,230 @@ +/* + * S/390 channel I/O instructions + * + * Copyright 2012 IBM Corp. + * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. +*/ + +#ifndef IOINST_S390X_H +#define IOINST_S390X_H +/* + * Channel I/O related definitions, as defined in the Principles + * Of Operation (and taken from the Linux implementation). + */ + +/* subchannel status word (command mode only) */ +typedef struct SCSW { + uint16_t flags; + uint16_t ctrl; + uint32_t cpa; + uint8_t dstat; + uint8_t cstat; + uint16_t count; +} QEMU_PACKED SCSW; + +#define SCSW_FLAGS_MASK_KEY 0xf000 +#define SCSW_FLAGS_MASK_SCTL 0x0800 +#define SCSW_FLAGS_MASK_ESWF 0x0400 +#define SCSW_FLAGS_MASK_CC 0x0300 +#define SCSW_FLAGS_MASK_FMT 0x0080 +#define SCSW_FLAGS_MASK_PFCH 0x0040 +#define SCSW_FLAGS_MASK_ISIC 0x0020 +#define SCSW_FLAGS_MASK_ALCC 0x0010 +#define SCSW_FLAGS_MASK_SSI 0x0008 +#define SCSW_FLAGS_MASK_ZCC 0x0004 +#define SCSW_FLAGS_MASK_ECTL 0x0002 +#define SCSW_FLAGS_MASK_PNO 0x0001 + +#define SCSW_CTRL_MASK_FCTL 0x7000 +#define SCSW_CTRL_MASK_ACTL 0x0fe0 +#define SCSW_CTRL_MASK_STCTL 0x001f + +#define SCSW_FCTL_CLEAR_FUNC 0x1000 +#define SCSW_FCTL_HALT_FUNC 0x2000 +#define SCSW_FCTL_START_FUNC 0x4000 + +#define SCSW_ACTL_SUSP 0x0020 +#define SCSW_ACTL_DEVICE_ACTIVE 0x0040 +#define SCSW_ACTL_SUBCH_ACTIVE 0x0080 +#define SCSW_ACTL_CLEAR_PEND 0x0100 +#define SCSW_ACTL_HALT_PEND 0x0200 +#define SCSW_ACTL_START_PEND 0x0400 +#define SCSW_ACTL_RESUME_PEND 0x0800 + +#define SCSW_STCTL_STATUS_PEND 0x0001 +#define SCSW_STCTL_SECONDARY 0x0002 +#define SCSW_STCTL_PRIMARY 0x0004 +#define SCSW_STCTL_INTERMEDIATE 0x0008 +#define SCSW_STCTL_ALERT 0x0010 + +#define SCSW_DSTAT_ATTENTION 0x80 +#define SCSW_DSTAT_STAT_MOD 0x40 +#define SCSW_DSTAT_CU_END 0x20 +#define SCSW_DSTAT_BUSY 0x10 +#define SCSW_DSTAT_CHANNEL_END 0x08 +#define SCSW_DSTAT_DEVICE_END 0x04 +#define SCSW_DSTAT_UNIT_CHECK 0x02 +#define SCSW_DSTAT_UNIT_EXCEP 0x01 + +#define SCSW_CSTAT_PCI 0x80 +#define SCSW_CSTAT_INCORR_LEN 0x40 +#define SCSW_CSTAT_PROG_CHECK 0x20 +#define SCSW_CSTAT_PROT_CHECK 0x10 +#define SCSW_CSTAT_DATA_CHECK 0x08 +#define SCSW_CSTAT_CHN_CTRL_CHK 0x04 +#define SCSW_CSTAT_INTF_CTRL_CHK 0x02 +#define SCSW_CSTAT_CHAIN_CHECK 0x01 + +/* path management control word */ +typedef struct PMCW { + uint32_t intparm; + uint16_t flags; + uint16_t devno; + uint8_t lpm; + uint8_t pnom; + uint8_t lpum; + uint8_t pim; + uint16_t mbi; + uint8_t pom; + uint8_t pam; + uint8_t chpid[8]; + uint32_t chars; +} QEMU_PACKED PMCW; + +#define PMCW_FLAGS_MASK_QF 0x8000 +#define PMCW_FLAGS_MASK_W 0x4000 +#define PMCW_FLAGS_MASK_ISC 0x3800 +#define PMCW_FLAGS_MASK_ENA 0x0080 +#define PMCW_FLAGS_MASK_LM 0x0060 +#define PMCW_FLAGS_MASK_MME 0x0018 +#define PMCW_FLAGS_MASK_MP 0x0004 +#define PMCW_FLAGS_MASK_TF 0x0002 +#define PMCW_FLAGS_MASK_DNV 0x0001 +#define PMCW_FLAGS_MASK_INVALID 0x0700 + +#define PMCW_CHARS_MASK_ST 0x00e00000 +#define PMCW_CHARS_MASK_MBFC 0x00000004 +#define PMCW_CHARS_MASK_XMWME 0x00000002 +#define PMCW_CHARS_MASK_CSENSE 0x00000001 +#define PMCW_CHARS_MASK_INVALID 0xff1ffff8 + +/* subchannel information block */ +typedef struct SCHIB { + PMCW pmcw; + SCSW scsw; + uint64_t mba; + uint8_t mda[4]; +} QEMU_PACKED SCHIB; + +/* interruption response block */ +typedef struct IRB { + SCSW scsw; + uint32_t esw[5]; + uint32_t ecw[8]; + uint32_t emw[8]; +} QEMU_PACKED IRB; + +/* operation request block */ +typedef struct ORB { + uint32_t intparm; + uint16_t ctrl0; + uint8_t lpm; + uint8_t ctrl1; + uint32_t cpa; +} QEMU_PACKED ORB; + +#define ORB_CTRL0_MASK_KEY 0xf000 +#define ORB_CTRL0_MASK_SPND 0x0800 +#define ORB_CTRL0_MASK_STR 0x0400 +#define ORB_CTRL0_MASK_MOD 0x0200 +#define ORB_CTRL0_MASK_SYNC 0x0100 +#define ORB_CTRL0_MASK_FMT 0x0080 +#define ORB_CTRL0_MASK_PFCH 0x0040 +#define ORB_CTRL0_MASK_ISIC 0x0020 +#define ORB_CTRL0_MASK_ALCC 0x0010 +#define ORB_CTRL0_MASK_SSIC 0x0008 +#define ORB_CTRL0_MASK_C64 0x0002 +#define ORB_CTRL0_MASK_I2K 0x0001 +#define ORB_CTRL0_MASK_INVALID 0x0004 + +#define ORB_CTRL1_MASK_ILS 0x80 +#define ORB_CTRL1_MASK_MIDAW 0x40 +#define ORB_CTRL1_MASK_ORBX 0x01 +#define ORB_CTRL1_MASK_INVALID 0x3e + +/* channel command word (type 1) */ +typedef struct CCW1 { + uint8_t cmd_code; + uint8_t flags; + uint16_t count; + uint32_t cda; +} QEMU_PACKED CCW1; + +#define CCW_FLAG_DC 0x80 +#define CCW_FLAG_CC 0x40 +#define CCW_FLAG_SLI 0x20 +#define CCW_FLAG_SKIP 0x10 +#define CCW_FLAG_PCI 0x08 +#define CCW_FLAG_IDA 0x04 +#define CCW_FLAG_SUSPEND 0x02 + +#define CCW_CMD_NOOP 0x03 +#define CCW_CMD_BASIC_SENSE 0x04 +#define CCW_CMD_TIC 0x08 +#define CCW_CMD_SENSE_ID 0xe4 + +typedef struct CRW { + uint16_t flags; + uint16_t rsid; +} QEMU_PACKED CRW; + +#define CRW_FLAGS_MASK_S 0x4000 +#define CRW_FLAGS_MASK_R 0x2000 +#define CRW_FLAGS_MASK_C 0x1000 +#define CRW_FLAGS_MASK_RSC 0x0f00 +#define CRW_FLAGS_MASK_A 0x0080 +#define CRW_FLAGS_MASK_ERC 0x003f + +#define CRW_ERC_INIT 0x02 +#define CRW_ERC_IPI 0x04 + +#define CRW_RSC_SUBCH 0x3 +#define CRW_RSC_CHP 0x4 + +/* I/O interruption code */ +typedef struct IOIntCode { + uint32_t subsys_id; + uint32_t intparm; + uint32_t interrupt_id; +} QEMU_PACKED IOIntCode; + +/* schid disintegration */ +#define IOINST_SCHID_ONE(_schid) ((_schid & 0x00010000) >> 16) +#define IOINST_SCHID_M(_schid) ((_schid & 0x00080000) >> 19) +#define IOINST_SCHID_CSSID(_schid) ((_schid & 0xff000000) >> 24) +#define IOINST_SCHID_SSID(_schid) ((_schid & 0x00060000) >> 17) +#define IOINST_SCHID_NR(_schid) (_schid & 0x0000ffff) + +int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid, + int *schid); +int ioinst_handle_xsch(CPUS390XState *env, uint64_t reg1); +int ioinst_handle_csch(CPUS390XState *env, uint64_t reg1); +int ioinst_handle_hsch(CPUS390XState *env, uint64_t reg1); +int ioinst_handle_msch(CPUS390XState *env, uint64_t reg1, uint32_t ipb); +int ioinst_handle_ssch(CPUS390XState *env, uint64_t reg1, uint32_t ipb); +int ioinst_handle_stcrw(CPUS390XState *env, uint32_t ipb); +int ioinst_handle_stsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb); +int ioinst_handle_tsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb); +int ioinst_handle_chsc(CPUS390XState *env, uint32_t ipb); +int ioinst_handle_tpi(CPUS390XState *env, uint32_t ipb); +int ioinst_handle_schm(CPUS390XState *env, uint64_t reg1, uint64_t reg2, + uint32_t ipb); +int ioinst_handle_rsch(CPUS390XState *env, uint64_t reg1); +int ioinst_handle_rchp(CPUS390XState *env, uint64_t reg1); +int ioinst_handle_sal(CPUS390XState *env, uint64_t reg1); + +#endif diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c index add6a58f9c..3929771182 100644 --- a/target-s390x/kvm.c +++ b/target-s390x/kvm.c @@ -47,9 +47,29 @@ #define IPA0_DIAG 0x8300 #define IPA0_SIGP 0xae00 -#define IPA0_PRIV 0xb200 +#define IPA0_B2 0xb200 +#define IPA0_B9 0xb900 +#define IPA0_EB 0xeb00 #define PRIV_SCLP_CALL 0x20 +#define PRIV_CSCH 0x30 +#define PRIV_HSCH 0x31 +#define PRIV_MSCH 0x32 +#define PRIV_SSCH 0x33 +#define PRIV_STSCH 0x34 +#define PRIV_TSCH 0x35 +#define PRIV_TPI 0x36 +#define PRIV_SAL 0x37 +#define PRIV_RSCH 0x38 +#define PRIV_STCRW 0x39 +#define PRIV_STCPS 0x3a +#define PRIV_RCHP 0x3b +#define PRIV_SCHM 0x3c +#define PRIV_CHSC 0x5f +#define PRIV_SIGA 0x74 +#define PRIV_XSCH 0x76 +#define PRIV_SQBS 0x8a +#define PRIV_EQBS 0x9c #define DIAG_KVM_HYPERCALL 0x500 #define DIAG_KVM_BREAKPOINT 0x501 @@ -76,6 +96,11 @@ int kvm_arch_init(KVMState *s) return 0; } +unsigned long kvm_arch_vcpu_id(CPUState *cpu) +{ + return cpu->cpu_index; +} + int kvm_arch_init_vcpu(CPUState *cpu) { int ret = 0; @@ -375,10 +400,123 @@ static int kvm_sclp_service_call(S390CPU *cpu, struct kvm_run *run, return 0; } -static int handle_priv(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1) +static int kvm_handle_css_inst(S390CPU *cpu, struct kvm_run *run, + uint8_t ipa0, uint8_t ipa1, uint8_t ipb) +{ + int r = 0; + int no_cc = 0; + CPUS390XState *env = &cpu->env; + + if (ipa0 != 0xb2) { + /* Not handled for now. */ + return -1; + } + cpu_synchronize_state(env); + switch (ipa1) { + case PRIV_XSCH: + r = ioinst_handle_xsch(env, env->regs[1]); + break; + case PRIV_CSCH: + r = ioinst_handle_csch(env, env->regs[1]); + break; + case PRIV_HSCH: + r = ioinst_handle_hsch(env, env->regs[1]); + break; + case PRIV_MSCH: + r = ioinst_handle_msch(env, env->regs[1], run->s390_sieic.ipb); + break; + case PRIV_SSCH: + r = ioinst_handle_ssch(env, env->regs[1], run->s390_sieic.ipb); + break; + case PRIV_STCRW: + r = ioinst_handle_stcrw(env, run->s390_sieic.ipb); + break; + case PRIV_STSCH: + r = ioinst_handle_stsch(env, env->regs[1], run->s390_sieic.ipb); + break; + case PRIV_TSCH: + /* We should only get tsch via KVM_EXIT_S390_TSCH. */ + fprintf(stderr, "Spurious tsch intercept\n"); + break; + case PRIV_CHSC: + r = ioinst_handle_chsc(env, run->s390_sieic.ipb); + break; + case PRIV_TPI: + /* This should have been handled by kvm already. */ + fprintf(stderr, "Spurious tpi intercept\n"); + break; + case PRIV_SCHM: + no_cc = 1; + r = ioinst_handle_schm(env, env->regs[1], env->regs[2], + run->s390_sieic.ipb); + break; + case PRIV_RSCH: + r = ioinst_handle_rsch(env, env->regs[1]); + break; + case PRIV_RCHP: + r = ioinst_handle_rchp(env, env->regs[1]); + break; + case PRIV_STCPS: + /* We do not provide this instruction, it is suppressed. */ + no_cc = 1; + r = 0; + break; + case PRIV_SAL: + no_cc = 1; + r = ioinst_handle_sal(env, env->regs[1]); + break; + default: + r = -1; + break; + } + + if (r >= 0) { + if (!no_cc) { + setcc(cpu, r); + } + r = 0; + } else if (r < -1) { + r = 0; + } + return r; +} + +static int is_ioinst(uint8_t ipa0, uint8_t ipa1, uint8_t ipb) +{ + int ret = 0; + uint16_t ipa = (ipa0 << 8) | ipa1; + + switch (ipa) { + case IPA0_B2 | PRIV_CSCH: + case IPA0_B2 | PRIV_HSCH: + case IPA0_B2 | PRIV_MSCH: + case IPA0_B2 | PRIV_SSCH: + case IPA0_B2 | PRIV_STSCH: + case IPA0_B2 | PRIV_TPI: + case IPA0_B2 | PRIV_SAL: + case IPA0_B2 | PRIV_RSCH: + case IPA0_B2 | PRIV_STCRW: + case IPA0_B2 | PRIV_STCPS: + case IPA0_B2 | PRIV_RCHP: + case IPA0_B2 | PRIV_SCHM: + case IPA0_B2 | PRIV_CHSC: + case IPA0_B2 | PRIV_SIGA: + case IPA0_B2 | PRIV_XSCH: + case IPA0_B9 | PRIV_EQBS: + case IPA0_EB | PRIV_SQBS: + ret = 1; + break; + } + + return ret; +} + +static int handle_priv(S390CPU *cpu, struct kvm_run *run, + uint8_t ipa0, uint8_t ipa1) { int r = 0; uint16_t ipbh0 = (run->s390_sieic.ipb & 0xffff0000) >> 16; + uint8_t ipb = run->s390_sieic.ipb & 0xff; dprintf("KVM: PRIV: %d\n", ipa1); switch (ipa1) { @@ -386,8 +524,16 @@ static int handle_priv(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1) r = kvm_sclp_service_call(cpu, run, ipbh0); break; default: - dprintf("KVM: unknown PRIV: 0x%x\n", ipa1); - r = -1; + if (is_ioinst(ipa0, ipa1, ipb)) { + r = kvm_handle_css_inst(cpu, run, ipa0, ipa1, ipb); + if (r == -1) { + setcc(cpu, 3); + r = 0; + } + } else { + dprintf("KVM: unknown PRIV: 0x%x\n", ipa1); + r = -1; + } break; } @@ -424,12 +570,10 @@ static int handle_diag(CPUS390XState *env, struct kvm_run *run, int ipb_code) static int s390_cpu_restart(S390CPU *cpu) { - CPUS390XState *env = &cpu->env; - kvm_s390_interrupt(cpu, KVM_S390_RESTART, 0); - s390_add_running_cpu(env); + s390_add_running_cpu(cpu); qemu_cpu_kick(CPU(cpu)); - dprintf("DONE: SIGP cpu restart: %p\n", env); + dprintf("DONE: SIGP cpu restart: %p\n", &cpu->env); return 0; } @@ -445,7 +589,7 @@ static int s390_cpu_initial_reset(S390CPU *cpu) CPUS390XState *env = &cpu->env; int i; - s390_del_running_cpu(env); + s390_del_running_cpu(cpu); if (kvm_vcpu_ioctl(CPU(cpu), KVM_S390_INITIAL_RESET, NULL) < 0) { perror("cannot init reset vcpu"); } @@ -528,15 +672,17 @@ static int handle_instruction(S390CPU *cpu, struct kvm_run *run) dprintf("handle_instruction 0x%x 0x%x\n", run->s390_sieic.ipa, run->s390_sieic.ipb); switch (ipa0) { - case IPA0_PRIV: - r = handle_priv(cpu, run, ipa1); - break; - case IPA0_DIAG: - r = handle_diag(env, run, ipb_code); - break; - case IPA0_SIGP: - r = handle_sigp(cpu, run, ipa1); - break; + case IPA0_B2: + case IPA0_B9: + case IPA0_EB: + r = handle_priv(cpu, run, ipa0 >> 8, ipa1); + break; + case IPA0_DIAG: + r = handle_diag(env, run, ipb_code); + break; + case IPA0_SIGP: + r = handle_sigp(cpu, run, ipa1); + break; } if (r < 0) { @@ -553,7 +699,6 @@ static bool is_special_wait_psw(CPUState *cs) static int handle_intercept(S390CPU *cpu) { - CPUS390XState *env = &cpu->env; CPUState *cs = CPU(cpu); struct kvm_run *run = cs->kvm_run; int icpt_code = run->s390_sieic.icptcode; @@ -566,14 +711,14 @@ static int handle_intercept(S390CPU *cpu) r = handle_instruction(cpu, run); break; case ICPT_WAITPSW: - if (s390_del_running_cpu(env) == 0 && + if (s390_del_running_cpu(cpu) == 0 && is_special_wait_psw(cs)) { qemu_system_shutdown_request(); } r = EXCP_HALTED; break; case ICPT_CPU_STOP: - if (s390_del_running_cpu(env) == 0) { + if (s390_del_running_cpu(cpu) == 0) { qemu_system_shutdown_request(); } r = EXCP_HALTED; @@ -595,6 +740,43 @@ static int handle_intercept(S390CPU *cpu) return r; } +static int handle_tsch(S390CPU *cpu) +{ + CPUS390XState *env = &cpu->env; + CPUState *cs = CPU(cpu); + struct kvm_run *run = cs->kvm_run; + int ret; + + cpu_synchronize_state(env); + ret = ioinst_handle_tsch(env, env->regs[1], run->s390_tsch.ipb); + if (ret >= 0) { + /* Success; set condition code. */ + setcc(cpu, ret); + ret = 0; + } else if (ret < -1) { + /* + * Failure. + * If an I/O interrupt had been dequeued, we have to reinject it. + */ + if (run->s390_tsch.dequeued) { + uint16_t subchannel_id = run->s390_tsch.subchannel_id; + uint16_t subchannel_nr = run->s390_tsch.subchannel_nr; + uint32_t io_int_parm = run->s390_tsch.io_int_parm; + uint32_t io_int_word = run->s390_tsch.io_int_word; + uint32_t type = ((subchannel_id & 0xff00) << 24) | + ((subchannel_id & 0x00060) << 22) | (subchannel_nr << 16); + + kvm_s390_interrupt_internal(cpu, type, + ((uint32_t)subchannel_id << 16) + | subchannel_nr, + ((uint64_t)io_int_parm << 32) + | io_int_word, 1); + } + ret = 0; + } + return ret; +} + int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) { S390CPU *cpu = S390_CPU(cs); @@ -607,6 +789,9 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) case KVM_EXIT_S390_RESET: qemu_system_reset_request(); break; + case KVM_EXIT_S390_TSCH: + ret = handle_tsch(cpu); + break; default: fprintf(stderr, "Unknown KVM exit: %d\n", run->exit_reason); break; @@ -632,3 +817,33 @@ int kvm_arch_on_sigbus(int code, void *addr) { return 1; } + +void kvm_s390_io_interrupt(S390CPU *cpu, uint16_t subchannel_id, + uint16_t subchannel_nr, uint32_t io_int_parm, + uint32_t io_int_word) +{ + uint32_t type; + + type = ((subchannel_id & 0xff00) << 24) | + ((subchannel_id & 0x00060) << 22) | (subchannel_nr << 16); + kvm_s390_interrupt_internal(cpu, type, + ((uint32_t)subchannel_id << 16) | subchannel_nr, + ((uint64_t)io_int_parm << 32) | io_int_word, 1); +} + +void kvm_s390_crw_mchk(S390CPU *cpu) +{ + kvm_s390_interrupt_internal(cpu, KVM_S390_MCHK, 1 << 28, + 0x00400f1d40330000, 1); +} + +void kvm_s390_enable_css_support(S390CPU *cpu) +{ + struct kvm_enable_cap cap = {}; + int r; + + /* Activate host kernel channel subsystem support. */ + cap.cap = KVM_CAP_S390_CSS_SUPPORT; + r = kvm_vcpu_ioctl(CPU(cpu), KVM_ENABLE_CAP, &cap); + assert(r == 0); +} diff --git a/target-s390x/machine.c b/target-s390x/machine.c deleted file mode 100644 index 3e79be6d56..0000000000 --- a/target-s390x/machine.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * QEMU S390x machine definitions - * - * Copyright (c) 2009 Alexander Graf <agraf@suse.de> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "hw/hw.h" -#include "hw/boards.h" - -void cpu_save(QEMUFile *f, void *opaque) -{ -} - -int cpu_load(QEMUFile *f, void *opaque, int version_id) -{ - return 0; -} diff --git a/target-sh4/Makefile.objs b/target-sh4/Makefile.objs index ca20f21443..cb448a840f 100644 --- a/target-sh4/Makefile.objs +++ b/target-sh4/Makefile.objs @@ -1,2 +1 @@ obj-y += translate.o op_helper.o helper.o cpu.o -obj-$(CONFIG_SOFTMMU) += machine.o diff --git a/target-sh4/cpu.c b/target-sh4/cpu.c index e4858a03ed..d2831226b9 100644 --- a/target-sh4/cpu.c +++ b/target-sh4/cpu.c @@ -21,6 +21,7 @@ #include "cpu.h" #include "qemu-common.h" +#include "migration/vmstate.h" /* CPUClass::reset() */ @@ -63,13 +64,21 @@ static void superh_cpu_initfn(Object *obj) env->movcal_backup_tail = &(env->movcal_backup); } +static const VMStateDescription vmstate_sh_cpu = { + .name = "cpu", + .unmigratable = 1, +}; + static void superh_cpu_class_init(ObjectClass *oc, void *data) { + DeviceClass *dc = DEVICE_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); SuperHCPUClass *scc = SUPERH_CPU_CLASS(oc); scc->parent_reset = cc->reset; cc->reset = superh_cpu_reset; + + dc->vmsd = &vmstate_sh_cpu; } static const TypeInfo superh_cpu_type_info = { diff --git a/target-sh4/machine.c b/target-sh4/machine.c deleted file mode 100644 index e69de29bb2..0000000000 --- a/target-sh4/machine.c +++ /dev/null diff --git a/target-sparc/cpu.c b/target-sparc/cpu.c index f404aa8b5f..4bc1afc755 100644 --- a/target-sparc/cpu.c +++ b/target-sparc/cpu.c @@ -119,7 +119,7 @@ SPARCCPU *cpu_sparc_init(const char *cpu_model) } if (cpu_sparc_register(env, cpu_model) < 0) { - object_delete(OBJECT(cpu)); + object_unref(OBJECT(cpu)); return NULL; } qemu_init_vcpu(env); diff --git a/target-unicore32/Makefile.objs b/target-unicore32/Makefile.objs index 8e143da937..6b41b1e9ef 100644 --- a/target-unicore32/Makefile.objs +++ b/target-unicore32/Makefile.objs @@ -1,4 +1,4 @@ obj-y += translate.o op_helper.o helper.o cpu.o obj-y += ucf64_helper.o -obj-$(CONFIG_SOFTMMU) += machine.o softmmu.o +obj-$(CONFIG_SOFTMMU) += softmmu.o diff --git a/target-unicore32/cpu.c b/target-unicore32/cpu.c index 884c101010..4e4177fc57 100644 --- a/target-unicore32/cpu.c +++ b/target-unicore32/cpu.c @@ -14,6 +14,7 @@ #include "cpu.h" #include "qemu-common.h" +#include "migration/vmstate.h" static inline void set_feature(CPUUniCore32State *env, int feature) { @@ -22,6 +23,25 @@ static inline void set_feature(CPUUniCore32State *env, int feature) /* CPU models */ +static ObjectClass *uc32_cpu_class_by_name(const char *cpu_model) +{ + ObjectClass *oc; + char *typename; + + if (cpu_model == NULL) { + return NULL; + } + + typename = g_strdup_printf("%s-" TYPE_UNICORE32_CPU, cpu_model); + oc = object_class_by_name(typename); + g_free(typename); + if (oc != NULL && (!object_class_dynamic_cast(oc, TYPE_UNICORE32_CPU) || + object_class_is_abstract(oc))) { + oc = NULL; + } + return oc; +} + typedef struct UniCore32CPUInfo { const char *name; void (*instance_init)(Object *obj); @@ -67,7 +87,6 @@ static void uc32_cpu_initfn(Object *obj) CPUUniCore32State *env = &cpu->env; cpu_exec_init(env); - env->cpu_model_str = object_get_typename(obj); #ifdef CONFIG_USER_ONLY env->uncached_asr = ASR_MODE_USER; @@ -80,15 +99,30 @@ static void uc32_cpu_initfn(Object *obj) tlb_flush(env, 1); } +static const VMStateDescription vmstate_uc32_cpu = { + .name = "cpu", + .unmigratable = 1, +}; + +static void uc32_cpu_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + CPUClass *cc = CPU_CLASS(oc); + + cc->class_by_name = uc32_cpu_class_by_name; + dc->vmsd = &vmstate_uc32_cpu; +} + static void uc32_register_cpu_type(const UniCore32CPUInfo *info) { TypeInfo type_info = { - .name = info->name, .parent = TYPE_UNICORE32_CPU, .instance_init = info->instance_init, }; - type_register_static(&type_info); + type_info.name = g_strdup_printf("%s-" TYPE_UNICORE32_CPU, info->name); + type_register(&type_info); + g_free((void *)type_info.name); } static const TypeInfo uc32_cpu_type_info = { @@ -98,6 +132,7 @@ static const TypeInfo uc32_cpu_type_info = { .instance_init = uc32_cpu_initfn, .abstract = true, .class_size = sizeof(UniCore32CPUClass), + .class_init = uc32_cpu_class_init, }; static void uc32_cpu_register_types(void) diff --git a/target-unicore32/cpu.h b/target-unicore32/cpu.h index 509ce7c69d..ae9a9d623d 100644 --- a/target-unicore32/cpu.h +++ b/target-unicore32/cpu.h @@ -133,8 +133,6 @@ int uc32_cpu_signal_handler(int host_signum, void *pinfo, void *puc); int uc32_cpu_handle_mmu_fault(CPUUniCore32State *env, target_ulong address, int rw, int mmu_idx); -#define CPU_SAVE_VERSION 2 - /* MMU modes definitions */ #define MMU_MODE0_SUFFIX _kernel #define MMU_MODE1_SUFFIX _user diff --git a/target-unicore32/helper.c b/target-unicore32/helper.c index 5359538ea5..3a92232de5 100644 --- a/target-unicore32/helper.c +++ b/target-unicore32/helper.c @@ -29,13 +29,16 @@ CPUUniCore32State *uc32_cpu_init(const char *cpu_model) { UniCore32CPU *cpu; CPUUniCore32State *env; + ObjectClass *oc; static int inited = 1; - if (object_class_by_name(cpu_model) == NULL) { + oc = cpu_class_by_name(TYPE_UNICORE32_CPU, cpu_model); + if (oc == NULL) { return NULL; } - cpu = UNICORE32_CPU(object_new(cpu_model)); + cpu = UNICORE32_CPU(object_new(object_class_get_name(oc))); env = &cpu->env; + env->cpu_model_str = cpu_model; if (inited) { inited = 0; diff --git a/target-unicore32/machine.c b/target-unicore32/machine.c deleted file mode 100644 index 60b2ec1771..0000000000 --- a/target-unicore32/machine.c +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Generic machine functions for UniCore32 ISA - * - * Copyright (C) 2010-2012 Guan Xuetao - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation, or any later version. - * See the COPYING file in the top-level directory. - */ -#include "hw/hw.h" - -void cpu_save(QEMUFile *f, void *opaque) -{ - hw_error("%s not supported yet.\n", __func__); -} - -int cpu_load(QEMUFile *f, void *opaque, int version_id) -{ - hw_error("%s not supported yet.\n", __func__); - - return 0; -} diff --git a/target-xtensa/Makefile.objs b/target-xtensa/Makefile.objs index b30e5a8466..644b7f99bb 100644 --- a/target-xtensa/Makefile.objs +++ b/target-xtensa/Makefile.objs @@ -3,4 +3,3 @@ obj-y += core-dc232b.o obj-y += core-dc233c.o obj-y += core-fsf.o obj-y += translate.o op_helper.o helper.o cpu.o -obj-$(CONFIG_SOFTMMU) += machine.o diff --git a/target-xtensa/cpu.c b/target-xtensa/cpu.c index 035b07c1c5..ebc7e9979b 100644 --- a/target-xtensa/cpu.c +++ b/target-xtensa/cpu.c @@ -30,6 +30,7 @@ #include "cpu.h" #include "qemu-common.h" +#include "migration/vmstate.h" /* CPUClass::reset() */ @@ -64,13 +65,21 @@ static void xtensa_cpu_initfn(Object *obj) cpu_exec_init(env); } +static const VMStateDescription vmstate_xtensa_cpu = { + .name = "cpu", + .unmigratable = 1, +}; + static void xtensa_cpu_class_init(ObjectClass *oc, void *data) { + DeviceClass *dc = DEVICE_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); XtensaCPUClass *xcc = XTENSA_CPU_CLASS(cc); xcc->parent_reset = cc->reset; cc->reset = xtensa_cpu_reset; + + dc->vmsd = &vmstate_xtensa_cpu; } static const TypeInfo xtensa_cpu_type_info = { diff --git a/target-xtensa/machine.c b/target-xtensa/machine.c deleted file mode 100644 index ddeffb2da4..0000000000 --- a/target-xtensa/machine.c +++ /dev/null @@ -1,38 +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 "hw/hw.h" -#include "hw/boards.h" - -void cpu_save(QEMUFile *f, void *opaque) -{ -} - -int cpu_load(QEMUFile *f, void *opaque, int version_id) -{ - return 0; -} diff --git a/tests/.gitignore b/tests/.gitignore index f9041f3d32..38c94ef1da 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -10,4 +10,5 @@ test-qmp-commands.h test-qmp-commands test-qmp-input-strict test-qmp-marshal.c +test-x86-cpuid *-test diff --git a/tests/Makefile b/tests/Makefile index d86e95a400..a2d62b8596 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,17 +1,17 @@ export SRC_PATH check-unit-y = tests/check-qdict$(EXESUF) -gcov-files-check-qdict-y = qdict.c +gcov-files-check-qdict-y = qobject/qdict.c check-unit-y += tests/check-qfloat$(EXESUF) -gcov-files-check-qfloat-y = qfloat.c +gcov-files-check-qfloat-y = qobject/qfloat.c check-unit-y += tests/check-qint$(EXESUF) -gcov-files-check-qint-y = qint.c +gcov-files-check-qint-y = qobject/qint.c check-unit-y += tests/check-qstring$(EXESUF) -gcov-files-check-qstring-y = qstring.c +gcov-files-check-qstring-y = qobject/qstring.c check-unit-y += tests/check-qlist$(EXESUF) -gcov-files-check-qlist-y = qlist.c +gcov-files-check-qlist-y = qobject/qlist.c check-unit-y += tests/check-qjson$(EXESUF) -gcov-files-check-qjson-y = qjson.c +gcov-files-check-qjson-y = qobject/qjson.c check-unit-y += tests/test-qmp-output-visitor$(EXESUF) gcov-files-test-qmp-output-visitor-y = qapi/qmp-output-visitor.c check-unit-y += tests/test-qmp-input-visitor$(EXESUF) @@ -39,12 +39,21 @@ endif endif check-unit-y += tests/test-visitor-serialization$(EXESUF) check-unit-y += tests/test-iov$(EXESUF) -gcov-files-test-iov-y = iov.c +gcov-files-test-iov-y = util/iov.c check-unit-y += tests/test-aio$(EXESUF) gcov-files-test-aio-$(CONFIG_WIN32) = aio-win32.c gcov-files-test-aio-$(CONFIG_POSIX) = aio-posix.c check-unit-y += tests/test-thread-pool$(EXESUF) gcov-files-test-thread-pool-y = thread-pool.c +gcov-files-test-hbitmap-y = util/hbitmap.c +check-unit-y += tests/test-hbitmap$(EXESUF) +check-unit-y += tests/test-x86-cpuid$(EXESUF) +# all code tested by test-x86-cpuid is inside topology.h +gcov-files-test-x86-cpuid-y = +check-unit-y += tests/test-xbzrle$(EXESUF) +gcov-files-test-xbzrle-y = xbzrle.c +check-unit-y += tests/test-cutils$(EXESUF) +gcov-files-test-cutils-y += util/cutils.c check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh @@ -57,11 +66,13 @@ gcov-files-i386-y += hw/hd-geometry.c check-qtest-i386-y += tests/rtc-test$(EXESUF) check-qtest-x86_64-y = $(check-qtest-i386-y) gcov-files-i386-y += i386-softmmu/hw/mc146818rtc.c -check-qtest-sparc-y = tests/m48t59-test$(EXESUF) -check-qtest-sparc64-y = tests/m48t59-test$(EXESUF) +gcov-files-x86_64-y = $(subst i386-softmmu/,x86_64-softmmu/,$(gcov-files-i386-y)) +#check-qtest-sparc-y = tests/m48t59-test$(EXESUF) +#check-qtest-sparc64-y = tests/m48t59-test$(EXESUF) gcov-files-sparc-y += hw/m48t59.c +gcov-files-sparc64-y += hw/m48t59.c check-qtest-arm-y = tests/tmp105-test$(EXESUF) -qcov-files-arm-y += hw/tmp105.c +gcov-files-arm-y += hw/tmp105.c GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h tests/test-qmp-commands.h @@ -70,12 +81,15 @@ test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \ tests/test-coroutine.o tests/test-string-output-visitor.o \ tests/test-string-input-visitor.o tests/test-qmp-output-visitor.o \ tests/test-qmp-input-visitor.o tests/test-qmp-input-strict.o \ - tests/test-qmp-commands.o tests/test-visitor-serialization.o + tests/test-qmp-commands.o tests/test-visitor-serialization.o \ + tests/test-x86-cpuid.o test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o $(test-obj-y): QEMU_INCLUDES += -Itests +tests/test-x86-cpuid.o: QEMU_INCLUDES += -I$(SRC_PATH)/target-i386 + tests/check-qint$(EXESUF): tests/check-qint.o libqemuutil.a tests/check-qstring$(EXESUF): tests/check-qstring.o libqemuutil.a tests/check-qdict$(EXESUF): tests/check-qdict.o libqemuutil.a @@ -86,6 +100,10 @@ tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(block-obj-y) libqemuutil tests/test-aio$(EXESUF): tests/test-aio.o $(block-obj-y) libqemuutil.a libqemustub.a tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(block-obj-y) libqemuutil.a libqemustub.a tests/test-iov$(EXESUF): tests/test-iov.o libqemuutil.a +tests/test-hbitmap$(EXESUF): tests/test-hbitmap.o libqemuutil.a libqemustub.a +tests/test-x86-cpuid$(EXESUF): tests/test-x86-cpuid.o +tests/test-xbzrle$(EXESUF): tests/test-xbzrle.o xbzrle.o page_cache.o libqemuutil.a +tests/test-cutils$(EXESUF): tests/test-cutils.o util/cutils.o tests/test-qapi-types.c tests/test-qapi-types.h :\ $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py diff --git a/tests/libqtest.c b/tests/libqtest.c index 913fa0535c..762dec4ac0 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -39,7 +39,8 @@ struct QTestState int qmp_fd; bool irq_level[MAX_IRQ]; GString *rx; - gchar *pid_file; + gchar *pid_file; /* QEMU PID file */ + int child_pid; /* Child process created to execute QEMU */ char *socket_path, *qmp_socket_path; }; @@ -144,6 +145,7 @@ QTestState *qtest_init(const char *extra_args) s->rx = g_string_new(""); s->pid_file = pid_file; + s->child_pid = pid; for (i = 0; i < MAX_IRQ; i++) { s->irq_level[i] = false; } @@ -165,8 +167,9 @@ void qtest_quit(QTestState *s) pid_t pid = qtest_qemu_pid(s); if (pid != -1) { + /* kill QEMU, but wait for the child created by us to run system() */ kill(pid, SIGTERM); - waitpid(pid, &status, 0); + waitpid(s->child_pid, &status, 0); } unlink(s->pid_file); diff --git a/tests/m48t59-test.c b/tests/m48t59-test.c index d79f55472d..77d69b330d 100644 --- a/tests/m48t59-test.c +++ b/tests/m48t59-test.c @@ -142,7 +142,9 @@ static void cmos_get_date_time(struct tm *date) date->tm_mday = mday; date->tm_mon = mon - 1; date->tm_year = base_year + year - 1900; +#ifndef __sun__ date->tm_gmtoff = 0; +#endif ts = mktime(date); } diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 index c6eb851871..720eeff921 100755 --- a/tests/qemu-iotests/041 +++ b/tests/qemu-iotests/041 @@ -207,6 +207,37 @@ class TestSingleDrive(ImageMirroringTestCase): self.assertTrue(self.compare_images(test_img, target_img), 'target image does not match source after mirroring') + def test_small_buffer(self): + self.assert_no_active_mirrors() + + # A small buffer is rounded up automatically + result = self.vm.qmp('drive-mirror', device='drive0', sync='full', + buf_size=4096, target=target_img) + self.assert_qmp(result, 'return', {}) + + self.complete_and_wait() + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/file', target_img) + self.vm.shutdown() + self.assertTrue(self.compare_images(test_img, target_img), + 'target image does not match source after mirroring') + + def test_small_buffer2(self): + self.assert_no_active_mirrors() + + qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,size=%d' + % (TestSingleDrive.image_len, TestSingleDrive.image_len), target_img) + result = self.vm.qmp('drive-mirror', device='drive0', sync='full', + buf_size=65536, mode='existing', target=target_img) + self.assert_qmp(result, 'return', {}) + + self.complete_and_wait() + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/file', target_img) + self.vm.shutdown() + self.assertTrue(self.compare_images(test_img, target_img), + 'target image does not match source after mirroring') + def test_large_cluster(self): self.assert_no_active_mirrors() @@ -292,6 +323,75 @@ class TestMirrorNoBacking(ImageMirroringTestCase): self.assertTrue(self.compare_images(test_img, target_img), 'target image does not match source after mirroring') + def test_large_cluster(self): + self.assert_no_active_mirrors() + + # qemu-img create fails if the image is not there + qemu_img('create', '-f', iotests.imgfmt, '-o', 'size=%d' + %(TestMirrorNoBacking.image_len), target_backing_img) + qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s' + % (TestMirrorNoBacking.image_len, target_backing_img), target_img) + os.remove(target_backing_img) + + result = self.vm.qmp('drive-mirror', device='drive0', sync='full', + mode='existing', target=target_img) + self.assert_qmp(result, 'return', {}) + + self.complete_and_wait() + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/file', target_img) + self.vm.shutdown() + self.assertTrue(self.compare_images(test_img, target_img), + 'target image does not match source after mirroring') + +class TestMirrorResized(ImageMirroringTestCase): + backing_len = 1 * 1024 * 1024 # MB + image_len = 2 * 1024 * 1024 # MB + + def setUp(self): + self.create_image(backing_img, TestMirrorResized.backing_len) + qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) + qemu_img('resize', test_img, '2M') + self.vm = iotests.VM().add_drive(test_img) + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + os.remove(test_img) + os.remove(backing_img) + try: + os.remove(target_img) + except OSError: + pass + + def test_complete_top(self): + self.assert_no_active_mirrors() + + result = self.vm.qmp('drive-mirror', device='drive0', sync='top', + target=target_img) + self.assert_qmp(result, 'return', {}) + + self.complete_and_wait() + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/file', target_img) + self.vm.shutdown() + self.assertTrue(self.compare_images(test_img, target_img), + 'target image does not match source after mirroring') + + def test_complete_full(self): + self.assert_no_active_mirrors() + + result = self.vm.qmp('drive-mirror', device='drive0', sync='full', + target=target_img) + self.assert_qmp(result, 'return', {}) + + self.complete_and_wait() + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/file', target_img) + self.vm.shutdown() + self.assertTrue(self.compare_images(test_img, target_img), + 'target image does not match source after mirroring') + class TestReadErrors(ImageMirroringTestCase): image_len = 2 * 1024 * 1024 # MB @@ -330,6 +430,9 @@ new_state = "1" '-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw' % (self.blkdebug_file, backing_img), test_img) + # Write something for tests that use sync='top' + qemu_io('-c', 'write %d 512' % (self.MIRROR_GRANULARITY + 65536), + test_img) self.vm = iotests.VM().add_drive(test_img) self.vm.launch() @@ -383,6 +486,32 @@ new_state = "1" self.complete_and_wait() self.vm.shutdown() + def test_large_cluster(self): + self.assert_no_active_mirrors() + + # Test COW into the target image. The first half of the + # cluster at MIRROR_GRANULARITY has to be copied from + # backing_img, even though sync='top'. + qemu_img('create', '-f', iotests.imgfmt, '-ocluster_size=131072,backing_file=%s' %(backing_img), target_img) + result = self.vm.qmp('drive-mirror', device='drive0', sync='top', + on_source_error='ignore', + mode='existing', target=target_img) + self.assert_qmp(result, 'return', {}) + + event = self.vm.get_qmp_event(wait=True) + self.assertEquals(event['event'], 'BLOCK_JOB_ERROR') + self.assert_qmp(event, 'data/device', 'drive0') + self.assert_qmp(event, 'data/operation', 'read') + result = self.vm.qmp('query-block-jobs') + self.assert_qmp(result, 'return[0]/paused', False) + self.complete_and_wait() + self.vm.shutdown() + + # Detach blkdebug to compare images successfully + qemu_img('rebase', '-f', iotests.imgfmt, '-u', '-b', backing_img, test_img) + self.assertTrue(self.compare_images(test_img, target_img), + 'target image does not match source after mirroring') + def test_stop_read(self): self.assert_no_active_mirrors() diff --git a/tests/qemu-iotests/041.out b/tests/qemu-iotests/041.out index 71009c239f..42314e9c00 100644 --- a/tests/qemu-iotests/041.out +++ b/tests/qemu-iotests/041.out @@ -1,5 +1,5 @@ -.................. +........................ ---------------------------------------------------------------------- -Ran 18 tests +Ran 24 tests OK diff --git a/tests/qemu-iotests/047 b/tests/qemu-iotests/047 new file mode 100755 index 0000000000..0cf36b434f --- /dev/null +++ b/tests/qemu-iotests/047 @@ -0,0 +1,75 @@ +#!/bin/bash +# +# Regression test for commit b7ab0fea (which was a corruption fix, +# despite the commit message claiming otherwise) +# +# Copyright (C) 2013 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# creator +owner=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 +_supported_proto generic +_supported_os Linux + +size=128M + +_make_test_img $size + +function qemu_io_cmds() +{ +cat <<EOF +write -P 0x66 0 320k +write 320k 128k +write -P 0x55 1M 128k +write -P 0x88 448k 128k +discard 320k 128k +aio_flush + +write -P 0x77 0 480k +aio_flush + +read -P 0x77 0 480k +read -P 0x88 480k 96k +read -P 0x55 1M 128k +EOF +} + +qemu_io_cmds | $QEMU_IO $TEST_IMG | _filter_qemu_io +_check_test_img + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/047.out b/tests/qemu-iotests/047.out new file mode 100644 index 0000000000..81b2ff73b8 --- /dev/null +++ b/tests/qemu-iotests/047.out @@ -0,0 +1,22 @@ +QA output created by 047 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +qemu-io> wrote 327680/327680 bytes at offset 0 +320 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 131072/131072 bytes at offset 327680 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 131072/131072 bytes at offset 1048576 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 131072/131072 bytes at offset 458752 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> discard 131072/131072 bytes at offset 327680 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> qemu-io> qemu-io> wrote 491520/491520 bytes at offset 0 +480 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> qemu-io> qemu-io> read 491520/491520 bytes at offset 0 +480 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> read 98304/98304 bytes at offset 491520 +96 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> read 131072/131072 bytes at offset 1048576 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> No errors were found on the image. +*** done diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index a0307de06b..1bbd2bf929 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -53,3 +53,4 @@ 044 rw auto 045 rw auto 046 rw auto aio +047 rw auto diff --git a/tests/rtc-test.c b/tests/rtc-test.c index e7123cafbc..203c0fc363 100644 --- a/tests/rtc-test.c +++ b/tests/rtc-test.c @@ -115,7 +115,9 @@ static void cmos_get_date_time(struct tm *date) date->tm_mday = mday; date->tm_mon = mon - 1; date->tm_year = base_year + year - 1900; +#ifndef __sun__ date->tm_gmtoff = 0; +#endif ts = mktime(date); } diff --git a/tests/tcg/mips/mips32-dsp/mthlip.c b/tests/tcg/mips/mips32-dsp/mthlip.c index 9549aae36a..85f94d8450 100644 --- a/tests/tcg/mips/mips32-dsp/mthlip.c +++ b/tests/tcg/mips/mips32-dsp/mthlip.c @@ -30,7 +30,7 @@ int main() assert(ach == resulth); assert(acl == resultl); - dsp = 0x3f; + dsp = 0x1f; ach = 0x05; acl = 0xB4CB; rs = 0x00FFBBAA; diff --git a/tests/test-cutils.c b/tests/test-cutils.c new file mode 100644 index 0000000000..2a4556d3aa --- /dev/null +++ b/tests/test-cutils.c @@ -0,0 +1,251 @@ +/* + * cutils.c unit-tests + * + * Copyright (C) 2013 Red Hat Inc. + * + * Authors: + * Eduardo Habkost <ehabkost@redhat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <glib.h> +#include <errno.h> +#include <string.h> + +#include "qemu-common.h" + + +static void test_parse_uint_null(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + int r; + + r = parse_uint(NULL, &i, &endptr, 0); + + g_assert_cmpint(r, ==, -EINVAL); + g_assert_cmpint(i, ==, 0); + g_assert(endptr == NULL); +} + +static void test_parse_uint_empty(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + const char *str = ""; + int r; + + r = parse_uint(str, &i, &endptr, 0); + + g_assert_cmpint(r, ==, -EINVAL); + g_assert_cmpint(i, ==, 0); + g_assert(endptr == str); +} + +static void test_parse_uint_whitespace(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + const char *str = " \t "; + int r; + + r = parse_uint(str, &i, &endptr, 0); + + g_assert_cmpint(r, ==, -EINVAL); + g_assert_cmpint(i, ==, 0); + g_assert(endptr == str); +} + + +static void test_parse_uint_invalid(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + const char *str = " \t xxx"; + int r; + + r = parse_uint(str, &i, &endptr, 0); + + g_assert_cmpint(r, ==, -EINVAL); + g_assert_cmpint(i, ==, 0); + g_assert(endptr == str); +} + + +static void test_parse_uint_trailing(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + const char *str = "123xxx"; + int r; + + r = parse_uint(str, &i, &endptr, 0); + + g_assert_cmpint(r, ==, 0); + g_assert_cmpint(i, ==, 123); + g_assert(endptr == str + 3); +} + +static void test_parse_uint_correct(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + const char *str = "123"; + int r; + + r = parse_uint(str, &i, &endptr, 0); + + g_assert_cmpint(r, ==, 0); + g_assert_cmpint(i, ==, 123); + g_assert(endptr == str + strlen(str)); +} + +static void test_parse_uint_octal(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + const char *str = "0123"; + int r; + + r = parse_uint(str, &i, &endptr, 0); + + g_assert_cmpint(r, ==, 0); + g_assert_cmpint(i, ==, 0123); + g_assert(endptr == str + strlen(str)); +} + +static void test_parse_uint_decimal(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + const char *str = "0123"; + int r; + + r = parse_uint(str, &i, &endptr, 10); + + g_assert_cmpint(r, ==, 0); + g_assert_cmpint(i, ==, 123); + g_assert(endptr == str + strlen(str)); +} + + +static void test_parse_uint_llong_max(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + char *str = g_strdup_printf("%llu", (unsigned long long)LLONG_MAX + 1); + int r; + + r = parse_uint(str, &i, &endptr, 0); + + g_assert_cmpint(r, ==, 0); + g_assert_cmpint(i, ==, (unsigned long long)LLONG_MAX + 1); + g_assert(endptr == str + strlen(str)); + + g_free(str); +} + +static void test_parse_uint_overflow(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + const char *str = "99999999999999999999999999999999999999"; + int r; + + r = parse_uint(str, &i, &endptr, 0); + + g_assert_cmpint(r, ==, -ERANGE); + g_assert_cmpint(i, ==, ULLONG_MAX); + g_assert(endptr == str + strlen(str)); +} + +static void test_parse_uint_negative(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + const char *str = " \t -321"; + int r; + + r = parse_uint(str, &i, &endptr, 0); + + g_assert_cmpint(r, ==, -ERANGE); + g_assert_cmpint(i, ==, 0); + g_assert(endptr == str + strlen(str)); +} + + +static void test_parse_uint_full_trailing(void) +{ + unsigned long long i = 999; + const char *str = "123xxx"; + int r; + + r = parse_uint_full(str, &i, 0); + + g_assert_cmpint(r, ==, -EINVAL); + g_assert_cmpint(i, ==, 0); +} + +static void test_parse_uint_full_correct(void) +{ + unsigned long long i = 999; + const char *str = "123"; + int r; + + r = parse_uint_full(str, &i, 0); + + g_assert_cmpint(r, ==, 0); + g_assert_cmpint(i, ==, 123); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/cutils/parse_uint/null", test_parse_uint_null); + g_test_add_func("/cutils/parse_uint/empty", test_parse_uint_empty); + g_test_add_func("/cutils/parse_uint/whitespace", + test_parse_uint_whitespace); + g_test_add_func("/cutils/parse_uint/invalid", test_parse_uint_invalid); + g_test_add_func("/cutils/parse_uint/trailing", test_parse_uint_trailing); + g_test_add_func("/cutils/parse_uint/correct", test_parse_uint_correct); + g_test_add_func("/cutils/parse_uint/octal", test_parse_uint_octal); + g_test_add_func("/cutils/parse_uint/decimal", test_parse_uint_decimal); + g_test_add_func("/cutils/parse_uint/llong_max", test_parse_uint_llong_max); + g_test_add_func("/cutils/parse_uint/overflow", test_parse_uint_overflow); + g_test_add_func("/cutils/parse_uint/negative", test_parse_uint_negative); + g_test_add_func("/cutils/parse_uint_full/trailing", + test_parse_uint_full_trailing); + g_test_add_func("/cutils/parse_uint_full/correct", + test_parse_uint_full_correct); + + return g_test_run(); +} diff --git a/tests/test-hbitmap.c b/tests/test-hbitmap.c new file mode 100644 index 0000000000..8c902f2055 --- /dev/null +++ b/tests/test-hbitmap.c @@ -0,0 +1,401 @@ +/* + * Hierarchical bitmap unit-tests. + * + * Copyright (C) 2012 Red Hat Inc. + * + * Author: Paolo Bonzini <pbonzini@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include <glib.h> +#include <stdarg.h> +#include "qemu/hbitmap.h" + +#define LOG_BITS_PER_LONG (BITS_PER_LONG == 32 ? 5 : 6) + +#define L1 BITS_PER_LONG +#define L2 (BITS_PER_LONG * L1) +#define L3 (BITS_PER_LONG * L2) + +typedef struct TestHBitmapData { + HBitmap *hb; + unsigned long *bits; + size_t size; + int granularity; +} TestHBitmapData; + + +/* Check that the HBitmap and the shadow bitmap contain the same data, + * ignoring the same "first" bits. + */ +static void hbitmap_test_check(TestHBitmapData *data, + uint64_t first) +{ + uint64_t count = 0; + size_t pos; + int bit; + HBitmapIter hbi; + int64_t i, next; + + hbitmap_iter_init(&hbi, data->hb, first); + + i = first; + for (;;) { + next = hbitmap_iter_next(&hbi); + if (next < 0) { + next = data->size; + } + + while (i < next) { + pos = i >> LOG_BITS_PER_LONG; + bit = i & (BITS_PER_LONG - 1); + i++; + g_assert_cmpint(data->bits[pos] & (1UL << bit), ==, 0); + } + + if (next == data->size) { + break; + } + + pos = i >> LOG_BITS_PER_LONG; + bit = i & (BITS_PER_LONG - 1); + i++; + count++; + g_assert_cmpint(data->bits[pos] & (1UL << bit), !=, 0); + } + + if (first == 0) { + g_assert_cmpint(count << data->granularity, ==, hbitmap_count(data->hb)); + } +} + +/* This is provided instead of a test setup function so that the sizes + are kept in the test functions (and not in main()) */ +static void hbitmap_test_init(TestHBitmapData *data, + uint64_t size, int granularity) +{ + size_t n; + data->hb = hbitmap_alloc(size, granularity); + + n = (size + BITS_PER_LONG - 1) / BITS_PER_LONG; + if (n == 0) { + n = 1; + } + data->bits = g_new0(unsigned long, n); + data->size = size; + data->granularity = granularity; + if (size) { + hbitmap_test_check(data, 0); + } +} + +static void hbitmap_test_teardown(TestHBitmapData *data, + const void *unused) +{ + if (data->hb) { + hbitmap_free(data->hb); + data->hb = NULL; + } + if (data->bits) { + g_free(data->bits); + data->bits = NULL; + } +} + +/* Set a range in the HBitmap and in the shadow "simple" bitmap. + * The two bitmaps are then tested against each other. + */ +static void hbitmap_test_set(TestHBitmapData *data, + uint64_t first, uint64_t count) +{ + hbitmap_set(data->hb, first, count); + while (count-- != 0) { + size_t pos = first >> LOG_BITS_PER_LONG; + int bit = first & (BITS_PER_LONG - 1); + first++; + + data->bits[pos] |= 1UL << bit; + } + + if (data->granularity == 0) { + hbitmap_test_check(data, 0); + } +} + +/* Reset a range in the HBitmap and in the shadow "simple" bitmap. + */ +static void hbitmap_test_reset(TestHBitmapData *data, + uint64_t first, uint64_t count) +{ + hbitmap_reset(data->hb, first, count); + while (count-- != 0) { + size_t pos = first >> LOG_BITS_PER_LONG; + int bit = first & (BITS_PER_LONG - 1); + first++; + + data->bits[pos] &= ~(1UL << bit); + } + + if (data->granularity == 0) { + hbitmap_test_check(data, 0); + } +} + +static void hbitmap_test_check_get(TestHBitmapData *data) +{ + uint64_t count = 0; + uint64_t i; + + for (i = 0; i < data->size; i++) { + size_t pos = i >> LOG_BITS_PER_LONG; + int bit = i & (BITS_PER_LONG - 1); + unsigned long val = data->bits[pos] & (1UL << bit); + count += hbitmap_get(data->hb, i); + g_assert_cmpint(hbitmap_get(data->hb, i), ==, val != 0); + } + g_assert_cmpint(count, ==, hbitmap_count(data->hb)); +} + +static void test_hbitmap_zero(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, 0, 0); +} + +static void test_hbitmap_unaligned(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L3 + 23, 0); + hbitmap_test_set(data, 0, 1); + hbitmap_test_set(data, L3 + 22, 1); +} + +static void test_hbitmap_iter_empty(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L1, 0); +} + +static void test_hbitmap_iter_partial(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L3, 0); + hbitmap_test_set(data, 0, L3); + hbitmap_test_check(data, 1); + hbitmap_test_check(data, L1 - 1); + hbitmap_test_check(data, L1); + hbitmap_test_check(data, L1 * 2 - 1); + hbitmap_test_check(data, L2 - 1); + hbitmap_test_check(data, L2); + hbitmap_test_check(data, L2 + 1); + hbitmap_test_check(data, L2 + L1); + hbitmap_test_check(data, L2 + L1 * 2 - 1); + hbitmap_test_check(data, L2 * 2 - 1); + hbitmap_test_check(data, L2 * 2); + hbitmap_test_check(data, L2 * 2 + 1); + hbitmap_test_check(data, L2 * 2 + L1); + hbitmap_test_check(data, L2 * 2 + L1 * 2 - 1); + hbitmap_test_check(data, L3 / 2); +} + +static void test_hbitmap_set_all(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L3, 0); + hbitmap_test_set(data, 0, L3); +} + +static void test_hbitmap_get_all(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L3, 0); + hbitmap_test_set(data, 0, L3); + hbitmap_test_check_get(data); +} + +static void test_hbitmap_get_some(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, 2 * L2, 0); + hbitmap_test_set(data, 10, 1); + hbitmap_test_check_get(data); + hbitmap_test_set(data, L1 - 1, 1); + hbitmap_test_check_get(data); + hbitmap_test_set(data, L1, 1); + hbitmap_test_check_get(data); + hbitmap_test_set(data, L2 - 1, 1); + hbitmap_test_check_get(data); + hbitmap_test_set(data, L2, 1); + hbitmap_test_check_get(data); +} + +static void test_hbitmap_set_one(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, 2 * L2, 0); + hbitmap_test_set(data, 10, 1); + hbitmap_test_set(data, L1 - 1, 1); + hbitmap_test_set(data, L1, 1); + hbitmap_test_set(data, L2 - 1, 1); + hbitmap_test_set(data, L2, 1); +} + +static void test_hbitmap_set_two_elem(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, 2 * L2, 0); + hbitmap_test_set(data, L1 - 1, 2); + hbitmap_test_set(data, L1 * 2 - 1, 4); + hbitmap_test_set(data, L1 * 4, L1 + 1); + hbitmap_test_set(data, L1 * 8 - 1, L1 + 1); + hbitmap_test_set(data, L2 - 1, 2); + hbitmap_test_set(data, L2 + L1 - 1, 8); + hbitmap_test_set(data, L2 + L1 * 4, L1 + 1); + hbitmap_test_set(data, L2 + L1 * 8 - 1, L1 + 1); +} + +static void test_hbitmap_set(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L3 * 2, 0); + hbitmap_test_set(data, L1 - 1, L1 + 2); + hbitmap_test_set(data, L1 * 3 - 1, L1 + 2); + hbitmap_test_set(data, L1 * 5, L1 * 2 + 1); + hbitmap_test_set(data, L1 * 8 - 1, L1 * 2 + 1); + hbitmap_test_set(data, L2 - 1, L1 + 2); + hbitmap_test_set(data, L2 + L1 * 2 - 1, L1 + 2); + hbitmap_test_set(data, L2 + L1 * 4, L1 * 2 + 1); + hbitmap_test_set(data, L2 + L1 * 7 - 1, L1 * 2 + 1); + hbitmap_test_set(data, L2 * 2 - 1, L3 * 2 - L2 * 2); +} + +static void test_hbitmap_set_twice(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L1 * 3, 0); + hbitmap_test_set(data, 0, L1 * 3); + hbitmap_test_set(data, L1, 1); +} + +static void test_hbitmap_set_overlap(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L3 * 2, 0); + hbitmap_test_set(data, L1 - 1, L1 + 2); + hbitmap_test_set(data, L1 * 2 - 1, L1 * 2 + 2); + hbitmap_test_set(data, 0, L1 * 3); + hbitmap_test_set(data, L1 * 8 - 1, L2); + hbitmap_test_set(data, L2, L1); + hbitmap_test_set(data, L2 - L1 - 1, L1 * 8 + 2); + hbitmap_test_set(data, L2, L3 - L2 + 1); + hbitmap_test_set(data, L3 - L1, L1 * 3); + hbitmap_test_set(data, L3 - 1, 3); + hbitmap_test_set(data, L3 - 1, L2); +} + +static void test_hbitmap_reset_empty(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L3, 0); + hbitmap_test_reset(data, 0, L3); +} + +static void test_hbitmap_reset(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L3 * 2, 0); + hbitmap_test_set(data, L1 - 1, L1 + 2); + hbitmap_test_reset(data, L1 * 2 - 1, L1 * 2 + 2); + hbitmap_test_set(data, 0, L1 * 3); + hbitmap_test_reset(data, L1 * 8 - 1, L2); + hbitmap_test_set(data, L2, L1); + hbitmap_test_reset(data, L2 - L1 - 1, L1 * 8 + 2); + hbitmap_test_set(data, L2, L3 - L2 + 1); + hbitmap_test_reset(data, L3 - L1, L1 * 3); + hbitmap_test_set(data, L3 - 1, 3); + hbitmap_test_reset(data, L3 - 1, L2); + hbitmap_test_set(data, 0, L3 * 2); + hbitmap_test_reset(data, 0, L1); + hbitmap_test_reset(data, 0, L2); + hbitmap_test_reset(data, L3, L3); + hbitmap_test_set(data, L3 / 2, L3); +} + +static void test_hbitmap_granularity(TestHBitmapData *data, + const void *unused) +{ + /* Note that hbitmap_test_check has to be invoked manually in this test. */ + hbitmap_test_init(data, L1, 1); + hbitmap_test_set(data, 0, 1); + g_assert_cmpint(hbitmap_count(data->hb), ==, 2); + hbitmap_test_check(data, 0); + hbitmap_test_set(data, 2, 1); + g_assert_cmpint(hbitmap_count(data->hb), ==, 4); + hbitmap_test_check(data, 0); + hbitmap_test_set(data, 0, 3); + g_assert_cmpint(hbitmap_count(data->hb), ==, 4); + hbitmap_test_reset(data, 0, 1); + g_assert_cmpint(hbitmap_count(data->hb), ==, 2); +} + +static void test_hbitmap_iter_granularity(TestHBitmapData *data, + const void *unused) +{ + HBitmapIter hbi; + + /* Note that hbitmap_test_check has to be invoked manually in this test. */ + hbitmap_test_init(data, 131072 << 7, 7); + hbitmap_iter_init(&hbi, data->hb, 0); + g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); + + hbitmap_test_set(data, ((L2 + L1 + 1) << 7) + 8, 8); + hbitmap_iter_init(&hbi, data->hb, 0); + g_assert_cmpint(hbitmap_iter_next(&hbi), ==, (L2 + L1 + 1) << 7); + g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); + + hbitmap_iter_init(&hbi, data->hb, (L2 + L1 + 2) << 7); + g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); + + hbitmap_test_set(data, (131072 << 7) - 8, 8); + hbitmap_iter_init(&hbi, data->hb, 0); + g_assert_cmpint(hbitmap_iter_next(&hbi), ==, (L2 + L1 + 1) << 7); + g_assert_cmpint(hbitmap_iter_next(&hbi), ==, 131071 << 7); + g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); + + hbitmap_iter_init(&hbi, data->hb, (L2 + L1 + 2) << 7); + g_assert_cmpint(hbitmap_iter_next(&hbi), ==, 131071 << 7); + g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); +} + +static void hbitmap_test_add(const char *testpath, + void (*test_func)(TestHBitmapData *data, const void *user_data)) +{ + g_test_add(testpath, TestHBitmapData, NULL, NULL, test_func, + hbitmap_test_teardown); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + hbitmap_test_add("/hbitmap/size/0", test_hbitmap_zero); + hbitmap_test_add("/hbitmap/size/unaligned", test_hbitmap_unaligned); + hbitmap_test_add("/hbitmap/iter/empty", test_hbitmap_iter_empty); + hbitmap_test_add("/hbitmap/iter/partial", test_hbitmap_iter_partial); + hbitmap_test_add("/hbitmap/iter/granularity", test_hbitmap_iter_granularity); + hbitmap_test_add("/hbitmap/get/all", test_hbitmap_get_all); + hbitmap_test_add("/hbitmap/get/some", test_hbitmap_get_some); + hbitmap_test_add("/hbitmap/set/all", test_hbitmap_set_all); + hbitmap_test_add("/hbitmap/set/one", test_hbitmap_set_one); + hbitmap_test_add("/hbitmap/set/two-elem", test_hbitmap_set_two_elem); + hbitmap_test_add("/hbitmap/set/general", test_hbitmap_set); + hbitmap_test_add("/hbitmap/set/twice", test_hbitmap_set_twice); + hbitmap_test_add("/hbitmap/set/overlap", test_hbitmap_set_overlap); + hbitmap_test_add("/hbitmap/reset/empty", test_hbitmap_reset_empty); + hbitmap_test_add("/hbitmap/reset/general", test_hbitmap_reset); + hbitmap_test_add("/hbitmap/granularity", test_hbitmap_granularity); + g_test_run(); + + return 0; +} diff --git a/tests/test-string-input-visitor.c b/tests/test-string-input-visitor.c index 899feda579..f6b0093554 100644 --- a/tests/test-string-input-visitor.c +++ b/tests/test-string-input-visitor.c @@ -165,6 +165,53 @@ static void test_visitor_in_enum(TestInputVisitorData *data, data->siv = NULL; } +/* Try to crash the visitors */ +static void test_visitor_in_fuzz(TestInputVisitorData *data, + const void *unused) +{ + int64_t ires; + bool bres; + double nres; + char *sres; + EnumOne eres; + Error *errp = NULL; + Visitor *v; + unsigned int i; + char buf[10000]; + + for (i = 0; i < 100; i++) { + unsigned int j; + + j = g_test_rand_int_range(0, sizeof(buf) - 1); + + buf[j] = '\0'; + + if (j != 0) { + for (j--; j != 0; j--) { + buf[j - 1] = (char)g_test_rand_int_range(0, 256); + } + } + + v = visitor_input_test_init(data, buf); + visit_type_int(v, &ires, NULL, &errp); + + v = visitor_input_test_init(data, buf); + visit_type_bool(v, &bres, NULL, &errp); + visitor_input_teardown(data, NULL); + + v = visitor_input_test_init(data, buf); + visit_type_number(v, &nres, NULL, &errp); + + v = visitor_input_test_init(data, buf); + visit_type_str(v, &sres, NULL, &errp); + g_free(sres); + + v = visitor_input_test_init(data, buf); + visit_type_EnumOne(v, &eres, NULL, &errp); + visitor_input_teardown(data, NULL); + } +} + static void input_visitor_test_add(const char *testpath, TestInputVisitorData *data, void (*test_func)(TestInputVisitorData *data, const void *user_data)) @@ -189,6 +236,8 @@ int main(int argc, char **argv) &in_visitor_data, test_visitor_in_string); input_visitor_test_add("/string-visitor/input/enum", &in_visitor_data, test_visitor_in_enum); + input_visitor_test_add("/string-visitor/input/fuzz", + &in_visitor_data, test_visitor_in_fuzz); g_test_run(); diff --git a/tests/test-x86-cpuid.c b/tests/test-x86-cpuid.c new file mode 100644 index 0000000000..8d9f96a113 --- /dev/null +++ b/tests/test-x86-cpuid.c @@ -0,0 +1,110 @@ +/* + * Test code for x86 CPUID and Topology functions + * + * Copyright (c) 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <glib.h> + +#include "topology.h" + +static void test_topo_bits(void) +{ + /* simple tests for 1 thread per core, 1 core per socket */ + g_assert_cmpuint(apicid_smt_width(1, 1), ==, 0); + g_assert_cmpuint(apicid_core_width(1, 1), ==, 0); + + g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 1, 0), ==, 0); + g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 1, 1), ==, 1); + g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 1, 2), ==, 2); + g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 1, 3), ==, 3); + + + /* Test field width calculation for multiple values + */ + g_assert_cmpuint(apicid_smt_width(1, 2), ==, 1); + g_assert_cmpuint(apicid_smt_width(1, 3), ==, 2); + g_assert_cmpuint(apicid_smt_width(1, 4), ==, 2); + + g_assert_cmpuint(apicid_smt_width(1, 14), ==, 4); + g_assert_cmpuint(apicid_smt_width(1, 15), ==, 4); + g_assert_cmpuint(apicid_smt_width(1, 16), ==, 4); + g_assert_cmpuint(apicid_smt_width(1, 17), ==, 5); + + + g_assert_cmpuint(apicid_core_width(30, 2), ==, 5); + g_assert_cmpuint(apicid_core_width(31, 2), ==, 5); + g_assert_cmpuint(apicid_core_width(32, 2), ==, 5); + g_assert_cmpuint(apicid_core_width(33, 2), ==, 6); + + + /* build a weird topology and see if IDs are calculated correctly + */ + + /* This will use 2 bits for thread ID and 3 bits for core ID + */ + g_assert_cmpuint(apicid_smt_width(6, 3), ==, 2); + g_assert_cmpuint(apicid_core_width(6, 3), ==, 3); + g_assert_cmpuint(apicid_pkg_offset(6, 3), ==, 5); + + g_assert_cmpuint(x86_apicid_from_cpu_idx(6, 3, 0), ==, 0); + g_assert_cmpuint(x86_apicid_from_cpu_idx(6, 3, 1), ==, 1); + g_assert_cmpuint(x86_apicid_from_cpu_idx(6, 3, 2), ==, 2); + + g_assert_cmpuint(x86_apicid_from_cpu_idx(6, 3, 1 * 3 + 0), ==, + (1 << 2) | 0); + g_assert_cmpuint(x86_apicid_from_cpu_idx(6, 3, 1 * 3 + 1), ==, + (1 << 2) | 1); + g_assert_cmpuint(x86_apicid_from_cpu_idx(6, 3, 1 * 3 + 2), ==, + (1 << 2) | 2); + + g_assert_cmpuint(x86_apicid_from_cpu_idx(6, 3, 2 * 3 + 0), ==, + (2 << 2) | 0); + g_assert_cmpuint(x86_apicid_from_cpu_idx(6, 3, 2 * 3 + 1), ==, + (2 << 2) | 1); + g_assert_cmpuint(x86_apicid_from_cpu_idx(6, 3, 2 * 3 + 2), ==, + (2 << 2) | 2); + + g_assert_cmpuint(x86_apicid_from_cpu_idx(6, 3, 5 * 3 + 0), ==, + (5 << 2) | 0); + g_assert_cmpuint(x86_apicid_from_cpu_idx(6, 3, 5 * 3 + 1), ==, + (5 << 2) | 1); + g_assert_cmpuint(x86_apicid_from_cpu_idx(6, 3, 5 * 3 + 2), ==, + (5 << 2) | 2); + + g_assert_cmpuint(x86_apicid_from_cpu_idx(6, 3, 1 * 6 * 3 + 0 * 3 + 0), ==, + (1 << 5)); + g_assert_cmpuint(x86_apicid_from_cpu_idx(6, 3, 1 * 6 * 3 + 1 * 3 + 1), ==, + (1 << 5) | (1 << 2) | 1); + g_assert_cmpuint(x86_apicid_from_cpu_idx(6, 3, 3 * 6 * 3 + 5 * 3 + 2), ==, + (3 << 5) | (5 << 2) | 2); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/cpuid/topology/basic", test_topo_bits); + + g_test_run(); + + return 0; +} diff --git a/tests/test-xbzrle.c b/tests/test-xbzrle.c new file mode 100644 index 0000000000..db93b0a3d2 --- /dev/null +++ b/tests/test-xbzrle.c @@ -0,0 +1,196 @@ +/* + * Xor Based Zero Run Length Encoding unit tests. + * + * Copyright 2013 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Orit Wasserman <owasserm@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include <string.h> +#include <sys/time.h> +#include <assert.h> +#include "qemu-common.h" +#include "include/migration/migration.h" + +#define PAGE_SIZE 4096 + +static void test_uleb(void) +{ + uint32_t i, val; + uint8_t buf[2]; + int encode_ret, decode_ret; + + for (i = 0; i <= 0x3fff; i++) { + encode_ret = uleb128_encode_small(&buf[0], i); + decode_ret = uleb128_decode_small(&buf[0], &val); + g_assert(encode_ret == decode_ret); + g_assert(i == val); + } + + /* decode invalid value */ + buf[0] = 0x80; + buf[1] = 0x80; + + decode_ret = uleb128_decode_small(&buf[0], &val); + g_assert(decode_ret == -1); + g_assert(val == 0); +} + +static void test_encode_decode_zero(void) +{ + uint8_t *buffer = g_malloc0(PAGE_SIZE); + uint8_t *compressed = g_malloc0(PAGE_SIZE); + int i = 0; + int dlen = 0; + int diff_len = g_test_rand_int_range(0, PAGE_SIZE - 1006); + + for (i = diff_len; i > 0; i--) { + buffer[1000 + i] = i; + } + + buffer[1000 + diff_len + 3] = 103; + buffer[1000 + diff_len + 5] = 105; + + /* encode zero page */ + dlen = xbzrle_encode_buffer(buffer, buffer, PAGE_SIZE, compressed, + PAGE_SIZE); + g_assert(dlen == 0); + + g_free(buffer); + g_free(compressed); +} + +static void test_encode_decode_unchanged(void) +{ + uint8_t *compressed = g_malloc0(PAGE_SIZE); + uint8_t *test = g_malloc0(PAGE_SIZE); + int i = 0; + int dlen = 0; + int diff_len = g_test_rand_int_range(0, PAGE_SIZE - 1006); + + for (i = diff_len; i > 0; i--) { + test[1000 + i] = i + 4; + } + + test[1000 + diff_len + 3] = 107; + test[1000 + diff_len + 5] = 109; + + /* test unchanged buffer */ + dlen = xbzrle_encode_buffer(test, test, PAGE_SIZE, compressed, + PAGE_SIZE); + g_assert(dlen == 0); + + g_free(test); + g_free(compressed); +} + +static void test_encode_decode_1_byte(void) +{ + uint8_t *buffer = g_malloc0(PAGE_SIZE); + uint8_t *test = g_malloc0(PAGE_SIZE); + uint8_t *compressed = g_malloc(PAGE_SIZE); + int dlen = 0, rc = 0; + uint8_t buf[2]; + + test[PAGE_SIZE - 1] = 1; + + dlen = xbzrle_encode_buffer(buffer, test, PAGE_SIZE, compressed, + PAGE_SIZE); + g_assert(dlen == (uleb128_encode_small(&buf[0], 4095) + 2)); + + rc = xbzrle_decode_buffer(compressed, dlen, buffer, PAGE_SIZE); + g_assert(rc == PAGE_SIZE); + g_assert(memcmp(test, buffer, PAGE_SIZE) == 0); + + g_free(buffer); + g_free(compressed); + g_free(test); +} + +static void test_encode_decode_overflow(void) +{ + uint8_t *compressed = g_malloc0(PAGE_SIZE); + uint8_t *test = g_malloc0(PAGE_SIZE); + uint8_t *buffer = g_malloc0(PAGE_SIZE); + int i = 0, rc = 0; + + for (i = 0; i < PAGE_SIZE / 2 - 1; i++) { + test[i * 2] = 1; + } + + /* encode overflow */ + rc = xbzrle_encode_buffer(buffer, test, PAGE_SIZE, compressed, + PAGE_SIZE); + g_assert(rc == -1); + + g_free(buffer); + g_free(compressed); + g_free(test); +} + +static void encode_decode_range(void) +{ + uint8_t *buffer = g_malloc0(PAGE_SIZE); + uint8_t *compressed = g_malloc(PAGE_SIZE); + uint8_t *test = g_malloc0(PAGE_SIZE); + int i = 0, rc = 0; + int dlen = 0; + + int diff_len = g_test_rand_int_range(0, PAGE_SIZE - 1006); + + for (i = diff_len; i > 0; i--) { + buffer[1000 + i] = i; + test[1000 + i] = i + 4; + } + + buffer[1000 + diff_len + 3] = 103; + test[1000 + diff_len + 3] = 107; + + buffer[1000 + diff_len + 5] = 105; + test[1000 + diff_len + 5] = 109; + + /* test encode/decode */ + dlen = xbzrle_encode_buffer(test, buffer, PAGE_SIZE, compressed, + PAGE_SIZE); + + rc = xbzrle_decode_buffer(compressed, dlen, test, PAGE_SIZE); + g_assert(rc < PAGE_SIZE); + g_assert(memcmp(test, buffer, PAGE_SIZE) == 0); + + g_free(buffer); + g_free(compressed); + g_free(test); +} + +static void test_encode_decode(void) +{ + int i; + + for (i = 0; i < 10000; i++) { + encode_decode_range(); + } +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + g_test_rand_int(); + g_test_add_func("/xbzrle/uleb", test_uleb); + g_test_add_func("/xbzrle/encode_decode_zero", test_encode_decode_zero); + g_test_add_func("/xbzrle/encode_decode_unchanged", + test_encode_decode_unchanged); + g_test_add_func("/xbzrle/encode_decode_1_byte", test_encode_decode_1_byte); + g_test_add_func("/xbzrle/encode_decode_overflow", + test_encode_decode_overflow); + g_test_add_func("/xbzrle/encode_decode", test_encode_decode); + + return g_test_run(); +} diff --git a/trace-events b/trace-events index 09091e6d17..1011f27676 100644 --- a/trace-events +++ b/trace-events @@ -79,10 +79,17 @@ commit_start(void *bs, void *base, void *top, void *s, void *co, void *opaque) " # block/mirror.c mirror_start(void *bs, void *s, void *co, void *opaque) "bs %p s %p co %p opaque %p" +mirror_restart_iter(void *s, int64_t cnt) "s %p dirty count %"PRId64 mirror_before_flush(void *s) "s %p" mirror_before_drain(void *s, int64_t cnt) "s %p dirty count %"PRId64 mirror_before_sleep(void *s, int64_t cnt, int synced) "s %p dirty count %"PRId64" synced %d" mirror_one_iteration(void *s, int64_t sector_num, int nb_sectors) "s %p sector_num %"PRId64" nb_sectors %d" +mirror_cow(void *s, int64_t sector_num) "s %p sector_num %"PRId64 +mirror_iteration_done(void *s, int64_t sector_num, int nb_sectors, int ret) "s %p sector_num %"PRId64" nb_sectors %d ret %d" +mirror_yield(void *s, int64_t cnt, int buf_free_count, int in_flight) "s %p dirty count %"PRId64" free buffers %d in_flight %d" +mirror_yield_in_flight(void *s, int64_t sector_num, int in_flight) "s %p sector_num %"PRId64" in_flight %d" +mirror_yield_buf_busy(void *s, int nb_chunks, int in_flight) "s %p requested chunks %d in_flight %d" +mirror_break_buf_busy(void *s, int nb_chunks, int in_flight) "s %p requested chunks %d in_flight %d" # blockdev.c qmp_block_job_cancel(void *job) "job %p" @@ -1060,3 +1067,26 @@ xics_set_irq_lsi(int srcno, int nr) "set_irq_lsi: srcno %d [irq %#x]" xics_ics_write_xive(int nr, int srcno, int server, uint8_t priority) "ics_write_xive: irq %#x [src %d] server %#x prio %#x" xics_ics_reject(int nr, int srcno) "reject irq %#x [src %d]" xics_ics_eoi(int nr) "ics_eoi: irq %#x" + +# hbitmap.c +hbitmap_iter_skip_words(const void *hb, void *hbi, uint64_t pos, unsigned long cur) "hb %p hbi %p pos %"PRId64" cur 0x%lx" +hbitmap_reset(void *hb, uint64_t start, uint64_t count, uint64_t sbit, uint64_t ebit) "hb %p items %"PRIu64",%"PRIu64" bits %"PRIu64"..%"PRIu64 +hbitmap_set(void *hb, uint64_t start, uint64_t count, uint64_t sbit, uint64_t ebit) "hb %p items %"PRIu64",%"PRIu64" bits %"PRIu64"..%"PRIu64 + +# target-s390x/ioinst.c +ioinst(const char *insn) "IOINST: %s" +ioinst_sch_id(const char *insn, int cssid, int ssid, int schid) "IOINST: %s (%x.%x.%04x)" +ioinst_chp_id(const char *insn, int cssid, int chpid) "IOINST: %s (%x.%02x)" +ioinst_chsc_cmd(uint16_t cmd, uint16_t len) "IOINST: chsc command %04x, len %04x" + +# hw/s390x/css.c +css_enable_facility(const char *facility) "CSS: enable %s" +css_crw(uint8_t rsc, uint8_t erc, uint16_t rsid, const char *chained) "CSS: queueing crw: rsc=%x, erc=%x, rsid=%x %s" +css_chpid_add(uint8_t cssid, uint8_t chpid, uint8_t type) "CSS: add chpid %x.%02x (type %02x)" +css_new_image(uint8_t cssid, const char *default_cssid) "CSS: add css image %02x %s" +css_assign_subch(const char *do_assign, uint8_t cssid, uint8_t ssid, uint16_t schid, uint16_t devno) "CSS: %s %x.%x.%04x (devno %04x)" +css_io_interrupt(int cssid, int ssid, int schid, uint32_t intparm, uint8_t isc, const char *conditional) "CSS: I/O interrupt on sch %x.%x.%04x (intparm %08x, isc %x) %s" + +# hw/s390x/virtio-ccw.c +virtio_ccw_interpret_ccw(int cssid, int ssid, int schid, int cmd_code) "VIRTIO-CCW: %x.%x.%04x: interpret command %x" +virtio_ccw_new_device(int cssid, int ssid, int schid, int devno, const char *devno_mode) "VIRTIO-CCW: add subchannel %x.%x.%04x, devno %04x (%s)" diff --git a/trace/Makefile.objs b/trace/Makefile.objs index 27fe26b5c2..dde9d5784e 100644 --- a/trace/Makefile.objs +++ b/trace/Makefile.objs @@ -4,24 +4,24 @@ # Auto-generated header for tracing routines $(obj)/generated-tracers.h: $(obj)/generated-tracers.h-timestamp + @cmp -s $< $@ || cp $< $@ $(obj)/generated-tracers.h-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak $(call quiet-command,$(TRACETOOL) \ --format=h \ --backend=$(TRACE_BACKEND) \ < $< > $@," GEN $(patsubst %-timestamp,%,$@)") - @cmp -s $@ $(patsubst %-timestamp,%,$@) || cp $@ $(patsubst %-timestamp,%,$@) ###################################################################### # Auto-generated tracing routines (non-DTrace) ifneq ($(TRACE_BACKEND),dtrace) $(obj)/generated-tracers.c: $(obj)/generated-tracers.c-timestamp + @cmp -s $< $@ || cp $< $@ $(obj)/generated-tracers.c-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak $(call quiet-command,$(TRACETOOL) \ --format=c \ --backend=$(TRACE_BACKEND) \ < $< > $@," GEN $(patsubst %-timestamp,%,$@)") - @cmp -s $@ $(patsubst %-timestamp,%,$@) || cp $@ $(patsubst %-timestamp,%,$@) $(obj)/generated-tracers.o: $(obj)/generated-tracers.c $(obj)/generated-tracers.h endif diff --git a/trace/simple.c b/trace/simple.c index ce17d64bd7..74701e3272 100644 --- a/trace/simple.c +++ b/trace/simple.c @@ -53,7 +53,7 @@ enum { uint8_t trace_buf[TRACE_BUF_LEN]; static unsigned int trace_idx; static unsigned int writeout_idx; -static uint64_t dropped_events; +static int dropped_events; static FILE *trace_fp; static char *trace_file_name; @@ -63,7 +63,7 @@ typedef struct { uint64_t timestamp_ns; uint32_t length; /* in bytes */ uint32_t reserved; /* unused */ - uint8_t arguments[]; + uint64_t arguments[]; } TraceRecord; typedef struct { @@ -160,25 +160,22 @@ static gpointer writeout_thread(gpointer opaque) uint8_t bytes[sizeof(TraceRecord) + sizeof(uint64_t)]; } dropped; unsigned int idx = 0; - uint64_t dropped_count; + int dropped_count; size_t unused __attribute__ ((unused)); for (;;) { wait_for_trace_records_available(); - if (dropped_events) { + if (g_atomic_int_get(&dropped_events)) { dropped.rec.event = DROPPED_EVENT_ID, dropped.rec.timestamp_ns = get_clock(); - dropped.rec.length = sizeof(TraceRecord) + sizeof(dropped_events), + dropped.rec.length = sizeof(TraceRecord) + sizeof(uint64_t), dropped.rec.reserved = 0; - while (1) { - dropped_count = dropped_events; - if (g_atomic_int_compare_and_exchange((gint *)&dropped_events, - dropped_count, 0)) { - break; - } - } - memcpy(dropped.rec.arguments, &dropped_count, sizeof(uint64_t)); + do { + dropped_count = g_atomic_int_get(&dropped_events); + } while (!g_atomic_int_compare_and_exchange(&dropped_events, + dropped_count, 0)); + dropped.rec.arguments[0] = dropped_count; unused = fwrite(&dropped.rec, dropped.rec.length, 1, trace_fp); } @@ -213,22 +210,17 @@ int trace_record_start(TraceBufferRecord *rec, TraceEventID event, size_t datasi uint32_t rec_len = sizeof(TraceRecord) + datasize; uint64_t timestamp_ns = get_clock(); - while (1) { - old_idx = trace_idx; + do { + old_idx = g_atomic_int_get(&trace_idx); smp_rmb(); new_idx = old_idx + rec_len; if (new_idx - writeout_idx > TRACE_BUF_LEN) { /* Trace Buffer Full, Event dropped ! */ - g_atomic_int_inc((gint *)&dropped_events); + g_atomic_int_inc(&dropped_events); return -ENOSPC; } - - if (g_atomic_int_compare_and_exchange((gint *)&trace_idx, - old_idx, new_idx)) { - break; - } - } + } while (!g_atomic_int_compare_and_exchange(&trace_idx, old_idx, new_idx)); idx = old_idx % TRACE_BUF_LEN; @@ -275,7 +267,8 @@ void trace_record_finish(TraceBufferRecord *rec) record.event |= TRACE_RECORD_VALID; write_to_buffer(rec->tbuf_idx, &record, sizeof(TraceRecord)); - if ((trace_idx - writeout_idx) > TRACE_BUF_FLUSH_THRESHOLD) { + if ((g_atomic_int_get(&trace_idx) - writeout_idx) + > TRACE_BUF_FLUSH_THRESHOLD) { flush_trace_file(false); } } diff --git a/ui/cocoa.m b/ui/cocoa.m index 3bf1c6e890..ca42413b34 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -265,6 +265,7 @@ static int cocoa_keycode_to_qemu(int keycode) BOOL isTabletEnabled; } - (void) resizeContentToWidth:(int)w height:(int)h displayState:(DisplayState *)ds; +- (void) updateDataOffset:(DisplayState *)ds; - (void) grabMouse; - (void) ungrabMouse; - (void) toggleFullScreen:(id)sender; @@ -429,6 +430,20 @@ QemuCocoaView *cocoaView; [self setFrame:NSMakeRect(cx, cy, cw, ch)]; } +- (void) updateDataOffset:(DisplayState *)ds +{ + COCOA_DEBUG("QemuCocoaView: UpdateDataOffset\n"); + + // update screenBuffer + if (dataProviderRef) { + CGDataProviderRelease(dataProviderRef); + } + + size_t size = ds_get_width(ds) * 4 * ds_get_height(ds); + dataProviderRef = CGDataProviderCreateWithData(NULL, ds_get_data(ds), + size, NULL); +} + - (void) toggleFullScreen:(id)sender { COCOA_DEBUG("QemuCocoaView: toggleFullScreen\n"); @@ -813,9 +828,9 @@ QemuCocoaView *cocoaView; [sheet close]; - asprintf(&argv[0], "%s", bin); - asprintf(&argv[1], "-hda"); - asprintf(&argv[2], "%s", img); + argv[0] = g_strdup_printf("%s", bin); + argv[1] = g_strdup_printf("-hda"); + argv[2] = g_strdup_printf("%s", img); printf("Using argc %d argv %s -hda %s\n", 3, bin, img); @@ -1004,6 +1019,11 @@ static void cocoa_refresh(DisplayState *ds) vga_hw_update(); } +static void cocoa_setdata(DisplayState *ds) +{ + [cocoaView updateDataOffset:ds]; +} + static void cocoa_cleanup(void) { COCOA_DEBUG("qemu_cocoa: cocoa_cleanup\n"); @@ -1020,6 +1040,7 @@ void cocoa_display_init(DisplayState *ds, int full_screen) dcl->dpy_gfx_update = cocoa_update; dcl->dpy_gfx_resize = cocoa_resize; dcl->dpy_refresh = cocoa_refresh; + dcl->dpy_gfx_setdata = cocoa_setdata; register_displaychangelistener(ds, dcl); diff --git a/ui/qemu-pixman.c b/ui/qemu-pixman.c index 609335ab11..6dcbe90546 100644 --- a/ui/qemu-pixman.c +++ b/ui/qemu-pixman.c @@ -3,7 +3,8 @@ * See the COPYING file in the top-level directory. */ -#include "ui/qemu-pixman.h" +#include "qemu-common.h" +#include "ui/console.h" int qemu_pixman_get_type(int rshift, int gshift, int bshift) { diff --git a/ui/spice-core.c b/ui/spice-core.c index 3f2c5650cd..bcc4199e7a 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -848,8 +848,8 @@ static int qemu_spice_set_ticket(bool fail_if_conn, bool disconnect_if_conn) int qemu_spice_set_passwd(const char *passwd, bool fail_if_conn, bool disconnect_if_conn) { - free(auth_passwd); - auth_passwd = strdup(passwd); + g_free(auth_passwd); + auth_passwd = g_strdup(passwd); return qemu_spice_set_ticket(fail_if_conn, disconnect_if_conn); } diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c index 9ccdc1971c..3e3020916c 100644 --- a/ui/vnc-ws.c +++ b/ui/vnc-ws.c @@ -120,10 +120,11 @@ static char *vncws_extract_handshake_entry(const char *handshake, static void vncws_send_handshake_response(VncState *vs, const char* key) { char combined_key[WS_CLIENT_KEY_LEN + WS_GUID_LEN + 1]; - char hash[SHA1_DIGEST_LEN]; - size_t hash_size = SHA1_DIGEST_LEN; + unsigned char hash[SHA1_DIGEST_LEN]; + size_t hash_size = sizeof(hash); char *accept = NULL, *response = NULL; gnutls_datum_t in; + int ret; g_strlcpy(combined_key, key, WS_CLIENT_KEY_LEN + 1); g_strlcat(combined_key, WS_GUID, WS_CLIENT_KEY_LEN + WS_GUID_LEN + 1); @@ -131,9 +132,9 @@ static void vncws_send_handshake_response(VncState *vs, const char* key) /* hash and encode it */ in.data = (void *)combined_key; in.size = WS_CLIENT_KEY_LEN + WS_GUID_LEN; - if (gnutls_fingerprint(GNUTLS_DIG_SHA1, &in, hash, &hash_size) - == GNUTLS_E_SUCCESS) { - accept = g_base64_encode((guchar *)hash, SHA1_DIGEST_LEN); + ret = gnutls_fingerprint(GNUTLS_DIG_SHA1, &in, hash, &hash_size); + if (ret == GNUTLS_E_SUCCESS && hash_size <= SHA1_DIGEST_LEN) { + accept = g_base64_encode(hash, hash_size); } if (accept == NULL) { VNC_DEBUG("Hashing Websocket combined key failed\n"); diff --git a/ui/vnc_keysym.h b/ui/vnc_keysym.h index df33cfe53c..6250bec692 100644 --- a/ui/vnc_keysym.h +++ b/ui/vnc_keysym.h @@ -215,10 +215,14 @@ static const name2keysym_t name2keysym[]={ { "Zabovedot", 0x1af}, { "zacute", 0x1bc}, { "Zacute", 0x1ac}, +{ "Odoubleacute", 0x1d5}, +{ "Udoubleacute", 0x1db}, { "cacute", 0x1e6}, { "Cacute", 0x1c6}, { "nacute", 0x1f1}, { "Nacute", 0x1d1}, +{ "odoubleacute", 0x1f5}, +{ "udoubleacute", 0x1fb}, /* modifiers */ {"ISO_Level3_Shift", 0xfe03}, /* XK_ISO_Level3_Shift */ diff --git a/util/Makefile.objs b/util/Makefile.objs index 5baeb53af6..495a178557 100644 --- a/util/Makefile.objs +++ b/util/Makefile.objs @@ -2,7 +2,7 @@ util-obj-y = osdep.o cutils.o qemu-timer-common.o util-obj-$(CONFIG_WIN32) += oslib-win32.o qemu-thread-win32.o event_notifier-win32.o util-obj-$(CONFIG_POSIX) += oslib-posix.o qemu-thread-posix.o event_notifier-posix.o util-obj-y += envlist.o path.o host-utils.o cache-utils.o module.o -util-obj-y += bitmap.o bitops.o +util-obj-y += bitmap.o bitops.o hbitmap.o util-obj-y += acl.o util-obj-y += error.o qemu-error.o util-obj-$(CONFIG_POSIX) += compatfd.o diff --git a/util/bitops.c b/util/bitops.c index 4c3a836a01..7b853cf944 100644 --- a/util/bitops.c +++ b/util/bitops.c @@ -60,7 +60,7 @@ found_first: return result + size; /* Nope. */ } found_middle: - return result + bitops_ffsl(tmp); + return result + bitops_ctzl(tmp); } /* @@ -109,7 +109,7 @@ found_first: return result + size; /* Nope. */ } found_middle: - return result + ffz(tmp); + return result + bitops_ctol(tmp); } unsigned long find_last_bit(const unsigned long *addr, unsigned long size) diff --git a/util/cutils.c b/util/cutils.c index 80bb1dcbf7..1439da4f99 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -270,6 +270,105 @@ int64_t strtosz(const char *nptr, char **end) return strtosz_suffix(nptr, end, STRTOSZ_DEFSUFFIX_MB); } +/** + * parse_uint: + * + * @s: String to parse + * @value: Destination for parsed integer value + * @endptr: Destination for pointer to first character not consumed + * @base: integer base, between 2 and 36 inclusive, or 0 + * + * Parse unsigned integer + * + * Parsed syntax is like strtoull()'s: arbitrary whitespace, a single optional + * '+' or '-', an optional "0x" if @base is 0 or 16, one or more digits. + * + * If @s is null, or @base is invalid, or @s doesn't start with an + * integer in the syntax above, set *@value to 0, *@endptr to @s, and + * return -EINVAL. + * + * Set *@endptr to point right beyond the parsed integer (even if the integer + * overflows or is negative, all digits will be parsed and *@endptr will + * point right beyond them). + * + * If the integer is negative, set *@value to 0, and return -ERANGE. + * + * If the integer overflows unsigned long long, set *@value to + * ULLONG_MAX, and return -ERANGE. + * + * Else, set *@value to the parsed integer, and return 0. + */ +int parse_uint(const char *s, unsigned long long *value, char **endptr, + int base) +{ + int r = 0; + char *endp = (char *)s; + unsigned long long val = 0; + + if (!s) { + r = -EINVAL; + goto out; + } + + errno = 0; + val = strtoull(s, &endp, base); + if (errno) { + r = -errno; + goto out; + } + + if (endp == s) { + r = -EINVAL; + goto out; + } + + /* make sure we reject negative numbers: */ + while (isspace((unsigned char)*s)) { + s++; + } + if (*s == '-') { + val = 0; + r = -ERANGE; + goto out; + } + +out: + *value = val; + *endptr = endp; + return r; +} + +/** + * parse_uint_full: + * + * @s: String to parse + * @value: Destination for parsed integer value + * @base: integer base, between 2 and 36 inclusive, or 0 + * + * Parse unsigned integer from entire string + * + * Have the same behavior of parse_uint(), but with an additional check + * for additional data after the parsed number. If extra characters are present + * after the parsed number, the function will return -EINVAL, and *@v will + * be set to 0. + */ +int parse_uint_full(const char *s, unsigned long long *value, int base) +{ + char *endp; + int r; + + r = parse_uint(s, value, &endp, base); + if (r < 0) { + return r; + } + if (*endp) { + *value = 0; + return -EINVAL; + } + + return 0; +} + int qemu_parse_fd(const char *param) { int fd; diff --git a/util/envlist.c b/util/envlist.c index ff99fc44e9..ebc06cf0f3 100644 --- a/util/envlist.c +++ b/util/envlist.c @@ -1,9 +1,4 @@ -#include <assert.h> -#include <errno.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - +#include "qemu-common.h" #include "qemu/queue.h" #include "qemu/envlist.h" diff --git a/util/hbitmap.c b/util/hbitmap.c new file mode 100644 index 0000000000..a0df5d3591 --- /dev/null +++ b/util/hbitmap.c @@ -0,0 +1,401 @@ +/* + * Hierarchical Bitmap Data Type + * + * Copyright Red Hat, Inc., 2012 + * + * Author: Paolo Bonzini <pbonzini@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#include <string.h> +#include <glib.h> +#include <assert.h> +#include "qemu/osdep.h" +#include "qemu/hbitmap.h" +#include "qemu/host-utils.h" +#include "trace.h" + +/* HBitmaps provides an array of bits. The bits are stored as usual in an + * array of unsigned longs, but HBitmap is also optimized to provide fast + * iteration over set bits; going from one bit to the next is O(logB n) + * worst case, with B = sizeof(long) * CHAR_BIT: the result is low enough + * that the number of levels is in fact fixed. + * + * In order to do this, it stacks multiple bitmaps with progressively coarser + * granularity; in all levels except the last, bit N is set iff the N-th + * unsigned long is nonzero in the immediately next level. When iteration + * completes on the last level it can examine the 2nd-last level to quickly + * skip entire words, and even do so recursively to skip blocks of 64 words or + * powers thereof (32 on 32-bit machines). + * + * Given an index in the bitmap, it can be split in group of bits like + * this (for the 64-bit case): + * + * bits 0-57 => word in the last bitmap | bits 58-63 => bit in the word + * bits 0-51 => word in the 2nd-last bitmap | bits 52-57 => bit in the word + * bits 0-45 => word in the 3rd-last bitmap | bits 46-51 => bit in the word + * + * So it is easy to move up simply by shifting the index right by + * log2(BITS_PER_LONG) bits. To move down, you shift the index left + * similarly, and add the word index within the group. Iteration uses + * ffs (find first set bit) to find the next word to examine; this + * operation can be done in constant time in most current architectures. + * + * Setting or clearing a range of m bits on all levels, the work to perform + * is O(m + m/W + m/W^2 + ...), which is O(m) like on a regular bitmap. + * + * When iterating on a bitmap, each bit (on any level) is only visited + * once. Hence, The total cost of visiting a bitmap with m bits in it is + * the number of bits that are set in all bitmaps. Unless the bitmap is + * extremely sparse, this is also O(m + m/W + m/W^2 + ...), so the amortized + * cost of advancing from one bit to the next is usually constant (worst case + * O(logB n) as in the non-amortized complexity). + */ + +struct HBitmap { + /* Number of total bits in the bottom level. */ + uint64_t size; + + /* Number of set bits in the bottom level. */ + uint64_t count; + + /* A scaling factor. Given a granularity of G, each bit in the bitmap will + * will actually represent a group of 2^G elements. Each operation on a + * range of bits first rounds the bits to determine which group they land + * in, and then affect the entire page; iteration will only visit the first + * bit of each group. Here is an example of operations in a size-16, + * granularity-1 HBitmap: + * + * initial state 00000000 + * set(start=0, count=9) 11111000 (iter: 0, 2, 4, 6, 8) + * reset(start=1, count=3) 00111000 (iter: 4, 6, 8) + * set(start=9, count=2) 00111100 (iter: 4, 6, 8, 10) + * reset(start=5, count=5) 00000000 + * + * From an implementation point of view, when setting or resetting bits, + * the bitmap will scale bit numbers right by this amount of bits. When + * iterating, the bitmap will scale bit numbers left by this amount of + * bits. + */ + int granularity; + + /* A number of progressively less coarse bitmaps (i.e. level 0 is the + * coarsest). Each bit in level N represents a word in level N+1 that + * has a set bit, except the last level where each bit represents the + * actual bitmap. + * + * Note that all bitmaps have the same number of levels. Even a 1-bit + * bitmap will still allocate HBITMAP_LEVELS arrays. + */ + unsigned long *levels[HBITMAP_LEVELS]; +}; + +static inline int popcountl(unsigned long l) +{ + return BITS_PER_LONG == 32 ? ctpop32(l) : ctpop64(l); +} + +/* Advance hbi to the next nonzero word and return it. hbi->pos + * is updated. Returns zero if we reach the end of the bitmap. + */ +unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi) +{ + size_t pos = hbi->pos; + const HBitmap *hb = hbi->hb; + unsigned i = HBITMAP_LEVELS - 1; + + unsigned long cur; + do { + cur = hbi->cur[--i]; + pos >>= BITS_PER_LEVEL; + } while (cur == 0); + + /* Check for end of iteration. We always use fewer than BITS_PER_LONG + * bits in the level 0 bitmap; thus we can repurpose the most significant + * bit as a sentinel. The sentinel is set in hbitmap_alloc and ensures + * that the above loop ends even without an explicit check on i. + */ + + if (i == 0 && cur == (1UL << (BITS_PER_LONG - 1))) { + return 0; + } + for (; i < HBITMAP_LEVELS - 1; i++) { + /* Shift back pos to the left, matching the right shifts above. + * The index of this word's least significant set bit provides + * the low-order bits. + */ + pos = (pos << BITS_PER_LEVEL) + bitops_ctzl(cur); + hbi->cur[i] = cur & (cur - 1); + + /* Set up next level for iteration. */ + cur = hb->levels[i + 1][pos]; + } + + hbi->pos = pos; + trace_hbitmap_iter_skip_words(hbi->hb, hbi, pos, cur); + + assert(cur); + return cur; +} + +void hbitmap_iter_init(HBitmapIter *hbi, const HBitmap *hb, uint64_t first) +{ + unsigned i, bit; + uint64_t pos; + + hbi->hb = hb; + pos = first >> hb->granularity; + assert(pos < hb->size); + hbi->pos = pos >> BITS_PER_LEVEL; + hbi->granularity = hb->granularity; + + for (i = HBITMAP_LEVELS; i-- > 0; ) { + bit = pos & (BITS_PER_LONG - 1); + pos >>= BITS_PER_LEVEL; + + /* Drop bits representing items before first. */ + hbi->cur[i] = hb->levels[i][pos] & ~((1UL << bit) - 1); + + /* We have already added level i+1, so the lowest set bit has + * been processed. Clear it. + */ + if (i != HBITMAP_LEVELS - 1) { + hbi->cur[i] &= ~(1UL << bit); + } + } +} + +bool hbitmap_empty(const HBitmap *hb) +{ + return hb->count == 0; +} + +int hbitmap_granularity(const HBitmap *hb) +{ + return hb->granularity; +} + +uint64_t hbitmap_count(const HBitmap *hb) +{ + return hb->count << hb->granularity; +} + +/* Count the number of set bits between start and end, not accounting for + * the granularity. Also an example of how to use hbitmap_iter_next_word. + */ +static uint64_t hb_count_between(HBitmap *hb, uint64_t start, uint64_t last) +{ + HBitmapIter hbi; + uint64_t count = 0; + uint64_t end = last + 1; + unsigned long cur; + size_t pos; + + hbitmap_iter_init(&hbi, hb, start << hb->granularity); + for (;;) { + pos = hbitmap_iter_next_word(&hbi, &cur); + if (pos >= (end >> BITS_PER_LEVEL)) { + break; + } + count += popcountl(cur); + } + + if (pos == (end >> BITS_PER_LEVEL)) { + /* Drop bits representing the END-th and subsequent items. */ + int bit = end & (BITS_PER_LONG - 1); + cur &= (1UL << bit) - 1; + count += popcountl(cur); + } + + return count; +} + +/* Setting starts at the last layer and propagates up if an element + * changes from zero to non-zero. + */ +static inline bool hb_set_elem(unsigned long *elem, uint64_t start, uint64_t last) +{ + unsigned long mask; + bool changed; + + assert((last >> BITS_PER_LEVEL) == (start >> BITS_PER_LEVEL)); + assert(start <= last); + + mask = 2UL << (last & (BITS_PER_LONG - 1)); + mask -= 1UL << (start & (BITS_PER_LONG - 1)); + changed = (*elem == 0); + *elem |= mask; + return changed; +} + +/* The recursive workhorse (the depth is limited to HBITMAP_LEVELS)... */ +static void hb_set_between(HBitmap *hb, int level, uint64_t start, uint64_t last) +{ + size_t pos = start >> BITS_PER_LEVEL; + size_t lastpos = last >> BITS_PER_LEVEL; + bool changed = false; + size_t i; + + i = pos; + if (i < lastpos) { + uint64_t next = (start | (BITS_PER_LONG - 1)) + 1; + changed |= hb_set_elem(&hb->levels[level][i], start, next - 1); + for (;;) { + start = next; + next += BITS_PER_LONG; + if (++i == lastpos) { + break; + } + changed |= (hb->levels[level][i] == 0); + hb->levels[level][i] = ~0UL; + } + } + changed |= hb_set_elem(&hb->levels[level][i], start, last); + + /* If there was any change in this layer, we may have to update + * the one above. + */ + if (level > 0 && changed) { + hb_set_between(hb, level - 1, pos, lastpos); + } +} + +void hbitmap_set(HBitmap *hb, uint64_t start, uint64_t count) +{ + /* Compute range in the last layer. */ + uint64_t last = start + count - 1; + + trace_hbitmap_set(hb, start, count, + start >> hb->granularity, last >> hb->granularity); + + start >>= hb->granularity; + last >>= hb->granularity; + count = last - start + 1; + + hb->count += count - hb_count_between(hb, start, last); + hb_set_between(hb, HBITMAP_LEVELS - 1, start, last); +} + +/* Resetting works the other way round: propagate up if the new + * value is zero. + */ +static inline bool hb_reset_elem(unsigned long *elem, uint64_t start, uint64_t last) +{ + unsigned long mask; + bool blanked; + + assert((last >> BITS_PER_LEVEL) == (start >> BITS_PER_LEVEL)); + assert(start <= last); + + mask = 2UL << (last & (BITS_PER_LONG - 1)); + mask -= 1UL << (start & (BITS_PER_LONG - 1)); + blanked = *elem != 0 && ((*elem & ~mask) == 0); + *elem &= ~mask; + return blanked; +} + +/* The recursive workhorse (the depth is limited to HBITMAP_LEVELS)... */ +static void hb_reset_between(HBitmap *hb, int level, uint64_t start, uint64_t last) +{ + size_t pos = start >> BITS_PER_LEVEL; + size_t lastpos = last >> BITS_PER_LEVEL; + bool changed = false; + size_t i; + + i = pos; + if (i < lastpos) { + uint64_t next = (start | (BITS_PER_LONG - 1)) + 1; + + /* Here we need a more complex test than when setting bits. Even if + * something was changed, we must not blank bits in the upper level + * unless the lower-level word became entirely zero. So, remove pos + * from the upper-level range if bits remain set. + */ + if (hb_reset_elem(&hb->levels[level][i], start, next - 1)) { + changed = true; + } else { + pos++; + } + + for (;;) { + start = next; + next += BITS_PER_LONG; + if (++i == lastpos) { + break; + } + changed |= (hb->levels[level][i] != 0); + hb->levels[level][i] = 0UL; + } + } + + /* Same as above, this time for lastpos. */ + if (hb_reset_elem(&hb->levels[level][i], start, last)) { + changed = true; + } else { + lastpos--; + } + + if (level > 0 && changed) { + hb_reset_between(hb, level - 1, pos, lastpos); + } +} + +void hbitmap_reset(HBitmap *hb, uint64_t start, uint64_t count) +{ + /* Compute range in the last layer. */ + uint64_t last = start + count - 1; + + trace_hbitmap_reset(hb, start, count, + start >> hb->granularity, last >> hb->granularity); + + start >>= hb->granularity; + last >>= hb->granularity; + + hb->count -= hb_count_between(hb, start, last); + hb_reset_between(hb, HBITMAP_LEVELS - 1, start, last); +} + +bool hbitmap_get(const HBitmap *hb, uint64_t item) +{ + /* Compute position and bit in the last layer. */ + uint64_t pos = item >> hb->granularity; + unsigned long bit = 1UL << (pos & (BITS_PER_LONG - 1)); + + return (hb->levels[HBITMAP_LEVELS - 1][pos >> BITS_PER_LEVEL] & bit) != 0; +} + +void hbitmap_free(HBitmap *hb) +{ + unsigned i; + for (i = HBITMAP_LEVELS; i-- > 0; ) { + g_free(hb->levels[i]); + } + g_free(hb); +} + +HBitmap *hbitmap_alloc(uint64_t size, int granularity) +{ + HBitmap *hb = g_malloc0(sizeof (struct HBitmap)); + unsigned i; + + assert(granularity >= 0 && granularity < 64); + size = (size + (1ULL << granularity) - 1) >> granularity; + assert(size <= ((uint64_t)1 << HBITMAP_LOG_MAX_SIZE)); + + hb->size = size; + hb->granularity = granularity; + for (i = HBITMAP_LEVELS; i-- > 0; ) { + size = MAX((size + BITS_PER_LONG - 1) >> BITS_PER_LEVEL, 1); + hb->levels[i] = g_malloc0(size * sizeof(unsigned long)); + } + + /* We necessarily have free bits in level 0 due to the definition + * of HBITMAP_LEVELS, so use one for a sentinel. This speeds up + * hbitmap_iter_skip_words. + */ + assert(size == 1); + hb->levels[0][0] |= 1UL << (BITS_PER_LONG - 1); + return hb; +} diff --git a/util/iov.c b/util/iov.c index c0f5c56618..fbe675d373 100644 --- a/util/iov.c +++ b/util/iov.c @@ -304,6 +304,10 @@ void qemu_iovec_concat_iov(QEMUIOVector *dst, { int i; size_t done; + + if (!sbytes) { + return; + } assert(dst->nalloc != -1); for (i = 0, done = 0; done < sbytes && i < src_cnt; i++) { if (soffset < src_iov[i].iov_len) { diff --git a/util/oslib-posix.c b/util/oslib-posix.c index 4f5ec6788b..b4152fb33c 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -105,8 +105,6 @@ void *qemu_memalign(size_t alignment, size_t size) return ptr; } -/* conflicts with qemu_vmalloc in bsd-user/mmap.c */ -#if !defined(CONFIG_BSD_USER) /* alloc shared memory pages */ void *qemu_vmalloc(size_t size) { @@ -129,7 +127,6 @@ void *qemu_vmalloc(size_t size) trace_qemu_vmalloc(size, ptr); return ptr; } -#endif void qemu_vfree(void *ptr) { diff --git a/util/qemu-option.c b/util/qemu-option.c index f532b765a0..c12e7245ef 100644 --- a/util/qemu-option.c +++ b/util/qemu-option.c @@ -643,9 +643,7 @@ static void opt_set(QemuOpts *opts, const char *name, const char *value, QTAILQ_INSERT_TAIL(&opts->head, opt, next); } opt->desc = desc; - if (value) { - opt->str = g_strdup(value); - } + opt->str = g_strdup(value); qemu_opt_parse(opt, &local_err); if (error_is_set(&local_err)) { error_propagate(errp, local_err); @@ -792,9 +790,7 @@ QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id, } } opts = g_malloc0(sizeof(*opts)); - if (id) { - opts->id = g_strdup(id); - } + opts->id = g_strdup(id); opts->list = list; loc_save(&opts->loc); QTAILQ_INIT(&opts->head); @@ -176,6 +176,7 @@ int main(int argc, char **argv) #define DEFAULT_RAM_SIZE 128 #define MAX_VIRTIO_CONSOLES 1 +#define MAX_SCLP_CONSOLES 1 static const char *data_dir; const char *bios_name = NULL; @@ -203,6 +204,7 @@ int no_quit = 0; CharDriverState *serial_hds[MAX_SERIAL_PORTS]; CharDriverState *parallel_hds[MAX_PARALLEL_PORTS]; CharDriverState *virtcon_hds[MAX_VIRTIO_CONSOLES]; +CharDriverState *sclp_hds[MAX_SCLP_CONSOLES]; int win2k_install_hack = 0; int singlestep = 0; int smp_cpus = 1; @@ -231,7 +233,7 @@ unsigned int nb_prom_envs = 0; const char *prom_envs[MAX_PROM_ENVS]; int boot_menu; uint8_t *boot_splash_filedata; -int boot_splash_filedata_size; +size_t boot_splash_filedata_size; uint8_t qemu_extra_params_fw[2]; typedef struct FWBootEntry FWBootEntry; @@ -261,9 +263,9 @@ static NotifierList exit_notifiers = static NotifierList machine_init_done_notifiers = NOTIFIER_LIST_INITIALIZER(machine_init_done_notifiers); -static int tcg_allowed = 1; -int kvm_allowed = 0; -int xen_allowed = 0; +static bool tcg_allowed = true; +bool kvm_allowed; +bool xen_allowed; uint32_t xen_domid; enum xen_mode xen_mode = XEN_EMULATE; static int tcg_tb_size; @@ -271,6 +273,7 @@ static int tcg_tb_size; static int default_serial = 1; static int default_parallel = 1; static int default_virtcon = 1; +static int default_sclp = 1; static int default_monitor = 1; static int default_floppy = 1; static int default_cdrom = 1; @@ -1241,21 +1244,79 @@ char *get_boot_devices_list(size_t *size) return list; } -static void numa_add(const char *optarg) +static void numa_node_parse_cpus(int nodenr, const char *cpus) { - char option[128]; char *endptr; unsigned long long value, endvalue; - int nodenr; - value = endvalue = 0ULL; + /* Empty CPU range strings will be considered valid, they will simply + * not set any bit in the CPU bitmap. + */ + if (!*cpus) { + return; + } + + if (parse_uint(cpus, &value, &endptr, 10) < 0) { + goto error; + } + if (*endptr == '-') { + if (parse_uint_full(endptr + 1, &endvalue, 10) < 0) { + goto error; + } + } else if (*endptr == '\0') { + endvalue = value; + } else { + goto error; + } + + if (endvalue >= MAX_CPUMASK_BITS) { + endvalue = MAX_CPUMASK_BITS - 1; + fprintf(stderr, + "qemu: NUMA: A max of %d VCPUs are supported\n", + MAX_CPUMASK_BITS); + } + + if (endvalue < value) { + goto error; + } + + bitmap_set(node_cpumask[nodenr], value, endvalue-value+1); + return; + +error: + fprintf(stderr, "qemu: Invalid NUMA CPU range: %s\n", cpus); + exit(1); +} + +static void numa_add(const char *optarg) +{ + char option[128]; + char *endptr; + unsigned long long nodenr; - optarg = get_opt_name(option, 128, optarg, ',') + 1; + optarg = get_opt_name(option, 128, optarg, ','); + if (*optarg == ',') { + optarg++; + } if (!strcmp(option, "node")) { + + if (nb_numa_nodes >= MAX_NODES) { + fprintf(stderr, "qemu: too many NUMA nodes\n"); + exit(1); + } + if (get_param_value(option, 128, "nodeid", optarg) == 0) { nodenr = nb_numa_nodes; } else { - nodenr = strtoull(option, NULL, 10); + if (parse_uint_full(option, &nodenr, 10) < 0) { + fprintf(stderr, "qemu: Invalid NUMA nodeid: %s\n", option); + exit(1); + } + } + + if (nodenr >= MAX_NODES) { + fprintf(stderr, "qemu: invalid NUMA nodeid: %llu\n", nodenr); + exit(1); } if (get_param_value(option, 128, "mem", optarg) == 0) { @@ -1270,23 +1331,12 @@ static void numa_add(const char *optarg) node_mem[nodenr] = sval; } if (get_param_value(option, 128, "cpus", optarg) != 0) { - value = strtoull(option, &endptr, 10); - if (*endptr == '-') { - endvalue = strtoull(endptr+1, &endptr, 10); - } else { - endvalue = value; - } - - if (!(endvalue < MAX_CPUMASK_BITS)) { - endvalue = MAX_CPUMASK_BITS - 1; - fprintf(stderr, - "A max of %d CPUs are supported in a guest\n", - MAX_CPUMASK_BITS); - } - - bitmap_set(node_cpumask[nodenr], value, endvalue-value+1); + numa_node_parse_cpus(nodenr, option); } nb_numa_nodes++; + } else { + fprintf(stderr, "Invalid -numa option: %s\n", option); + exit(1); } } @@ -2233,6 +2283,7 @@ static int device_init_func(QemuOpts *opts, void *opaque) dev = qdev_device_add(opts); if (!dev) return -1; + object_unref(OBJECT(dev)); return 0; } @@ -2340,6 +2391,7 @@ struct device_config { DEV_VIRTCON, /* -virtioconsole */ DEV_DEBUGCON, /* -debugcon */ DEV_GDB, /* -gdb, -s */ + DEV_SCLP, /* s390 sclp */ } type; const char *cmdline; Location loc; @@ -2458,6 +2510,39 @@ static int virtcon_parse(const char *devname) return 0; } +static int sclp_parse(const char *devname) +{ + QemuOptsList *device = qemu_find_opts("device"); + static int index = 0; + char label[32]; + QemuOpts *dev_opts; + + if (strcmp(devname, "none") == 0) { + return 0; + } + if (index == MAX_SCLP_CONSOLES) { + fprintf(stderr, "qemu: too many sclp consoles\n"); + exit(1); + } + + assert(arch_type == QEMU_ARCH_S390X); + + dev_opts = qemu_opts_create(device, NULL, 0, NULL); + qemu_opt_set(dev_opts, "driver", "sclpconsole"); + + snprintf(label, sizeof(label), "sclpcon%d", index); + sclp_hds[index] = qemu_chr_new(label, devname, NULL); + if (!sclp_hds[index]) { + fprintf(stderr, "qemu: could not connect sclp console" + " to character backend '%s'\n", devname); + return -1; + } + qemu_opt_set(dev_opts, "chardev", label); + + index++; + return 0; +} + static int debugcon_parse(const char *devname) { QemuOpts *opts; @@ -2507,7 +2592,7 @@ static struct { const char *name; int (*available)(void); int (*init)(void); - int *allowed; + bool *allowed; } accel_list[] = { { "tcg", "tcg", tcg_available, tcg_init, &tcg_allowed }, { "xen", "Xen", xen_available, xen_init, &xen_allowed }, @@ -2520,8 +2605,8 @@ static int configure_accelerator(void) const char *p = NULL; char buf[10]; int i, ret; - bool accel_initialised = 0; - bool init_failed = 0; + bool accel_initialised = false; + bool init_failed = false; QemuOptsList *list = qemu_find_opts("machine"); if (!QTAILQ_EMPTY(&list->head)) { @@ -2540,21 +2625,21 @@ static int configure_accelerator(void) p = get_opt_name(buf, sizeof (buf), p, ':'); for (i = 0; i < ARRAY_SIZE(accel_list); i++) { if (strcmp(accel_list[i].opt_name, buf) == 0) { - *(accel_list[i].allowed) = 1; + if (!accel_list[i].available()) { + printf("%s not supported for this target\n", + accel_list[i].name); + continue; + } + *(accel_list[i].allowed) = true; ret = accel_list[i].init(); if (ret < 0) { - init_failed = 1; - if (!accel_list[i].available()) { - printf("%s not supported for this target\n", - accel_list[i].name); - } else { - fprintf(stderr, "failed to initialize %s: %s\n", - accel_list[i].name, - strerror(-ret)); - } - *(accel_list[i].allowed) = 0; + init_failed = true; + fprintf(stderr, "failed to initialize %s: %s\n", + accel_list[i].name, + strerror(-ret)); + *(accel_list[i].allowed) = false; } else { - accel_initialised = 1; + accel_initialised = true; } break; } @@ -2565,7 +2650,9 @@ static int configure_accelerator(void) } if (!accel_initialised) { - fprintf(stderr, "No accelerator found!\n"); + if (!init_failed) { + fprintf(stderr, "No accelerator found!\n"); + } exit(1); } @@ -2957,10 +3044,6 @@ int main(int argc, char **argv, char **envp) } break; case QEMU_OPTION_numa: - if (nb_numa_nodes >= MAX_NODES) { - fprintf(stderr, "qemu: too many NUMA nodes\n"); - exit(1); - } numa_add(optarg); break; case QEMU_OPTION_display: @@ -3615,6 +3698,7 @@ int main(int argc, char **argv, char **envp) default_serial = 0; default_parallel = 0; default_virtcon = 0; + default_sclp = 0; default_monitor = 0; default_net = 0; default_floppy = 0; @@ -3832,6 +3916,9 @@ int main(int argc, char **argv, char **envp) if (!machine->use_virtcon) { default_virtcon = 0; } + if (!machine->use_sclp) { + default_sclp = 0; + } if (machine->no_floppy) { default_floppy = 0; } @@ -3873,11 +3960,16 @@ int main(int argc, char **argv, char **envp) add_device_config(DEV_SERIAL, "mon:stdio"); } else if (default_virtcon && default_monitor) { add_device_config(DEV_VIRTCON, "mon:stdio"); + } else if (default_sclp && default_monitor) { + add_device_config(DEV_SCLP, "mon:stdio"); } else { if (default_serial) add_device_config(DEV_SERIAL, "stdio"); if (default_virtcon) add_device_config(DEV_VIRTCON, "stdio"); + if (default_sclp) { + add_device_config(DEV_SCLP, "stdio"); + } if (default_monitor) monitor_parse("stdio", "readline"); } @@ -3890,6 +3982,9 @@ int main(int argc, char **argv, char **envp) monitor_parse("vc:80Cx24C", "readline"); if (default_virtcon) add_device_config(DEV_VIRTCON, "vc:80Cx24C"); + if (default_sclp) { + add_device_config(DEV_SCLP, "vc:80Cx24C"); + } } socket_init(); @@ -4060,6 +4155,9 @@ int main(int argc, char **argv, char **envp) exit(1); if (foreach_device_config(DEV_VIRTCON, virtcon_parse) < 0) exit(1); + if (foreach_device_config(DEV_SCLP, sclp_parse) < 0) { + exit(1); + } if (foreach_device_config(DEV_DEBUGCON, debugcon_parse) < 0) exit(1); diff --git a/xbzrle.c b/xbzrle.c new file mode 100644 index 0000000000..fbcb35d0e3 --- /dev/null +++ b/xbzrle.c @@ -0,0 +1,173 @@ +/* + * Xor Based Zero Run Length Encoding + * + * Copyright 2013 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Orit Wasserman <owasserm@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ +#include "qemu-common.h" +#include "include/migration/migration.h" + +/* + page = zrun nzrun + | zrun nzrun page + + zrun = length + + nzrun = length byte... + + length = uleb128 encoded integer + */ +int xbzrle_encode_buffer(uint8_t *old_buf, uint8_t *new_buf, int slen, + uint8_t *dst, int dlen) +{ + uint32_t zrun_len = 0, nzrun_len = 0; + int d = 0, i = 0; + long res, xor; + uint8_t *nzrun_start = NULL; + + g_assert(!(((uintptr_t)old_buf | (uintptr_t)new_buf | slen) % + sizeof(long))); + + while (i < slen) { + /* overflow */ + if (d + 2 > dlen) { + return -1; + } + + /* not aligned to sizeof(long) */ + res = (slen - i) % sizeof(long); + while (res && old_buf[i] == new_buf[i]) { + zrun_len++; + i++; + res--; + } + + /* word at a time for speed */ + if (!res) { + while (i < slen && + (*(long *)(old_buf + i)) == (*(long *)(new_buf + i))) { + i += sizeof(long); + zrun_len += sizeof(long); + } + + /* go over the rest */ + while (i < slen && old_buf[i] == new_buf[i]) { + zrun_len++; + i++; + } + } + + /* buffer unchanged */ + if (zrun_len == slen) { + return 0; + } + + /* skip last zero run */ + if (i == slen) { + return d; + } + + d += uleb128_encode_small(dst + d, zrun_len); + + zrun_len = 0; + nzrun_start = new_buf + i; + + /* overflow */ + if (d + 2 > dlen) { + return -1; + } + /* not aligned to sizeof(long) */ + res = (slen - i) % sizeof(long); + while (res && old_buf[i] != new_buf[i]) { + i++; + nzrun_len++; + res--; + } + + /* word at a time for speed, use of 32-bit long okay */ + if (!res) { + /* truncation to 32-bit long okay */ + long mask = (long)0x0101010101010101ULL; + while (i < slen) { + xor = *(long *)(old_buf + i) ^ *(long *)(new_buf + i); + if ((xor - mask) & ~xor & (mask << 7)) { + /* found the end of an nzrun within the current long */ + while (old_buf[i] != new_buf[i]) { + nzrun_len++; + i++; + } + break; + } else { + i += sizeof(long); + nzrun_len += sizeof(long); + } + } + } + + d += uleb128_encode_small(dst + d, nzrun_len); + /* overflow */ + if (d + nzrun_len > dlen) { + return -1; + } + memcpy(dst + d, nzrun_start, nzrun_len); + d += nzrun_len; + nzrun_len = 0; + } + + return d; +} + +int xbzrle_decode_buffer(uint8_t *src, int slen, uint8_t *dst, int dlen) +{ + int i = 0, d = 0; + int ret; + uint32_t count = 0; + + while (i < slen) { + + /* zrun */ + if ((slen - i) < 2) { + return -1; + } + + ret = uleb128_decode_small(src + i, &count); + if (ret < 0 || (i && !count)) { + return -1; + } + i += ret; + d += count; + + /* overflow */ + if (d > dlen) { + return -1; + } + + /* nzrun */ + if ((slen - i) < 2) { + return -1; + } + + ret = uleb128_decode_small(src + i, &count); + if (ret < 0 || !count) { + return -1; + } + i += ret; + + /* overflow */ + if (d + count > dlen || i + count > slen) { + return -1; + } + + memcpy(dst + d, src + i, count); + d += count; + i += count; + } + + return d; +} |