diff options
337 files changed, 8707 insertions, 3378 deletions
@@ -30,7 +30,7 @@ CONFIG_ALL=y -include config-all-devices.mak -include config-all-disas.mak -config-host.mak: $(SRC_PATH)/configure +config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/pc-bios @echo $@ is out-of-date, running configure @# TODO: The next lines include code which supports a smooth @# transition from old configurations without config.status. @@ -416,6 +416,7 @@ pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \ pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \ efi-e1000.rom efi-eepro100.rom efi-ne2k_pci.rom \ efi-pcnet.rom efi-rtl8139.rom efi-virtio.rom \ +efi-e1000e.rom efi-vmxnet3.rom \ qemu-icon.bmp qemu_logo_no_text.svg \ bamboo.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \ multiboot.bin linuxboot.bin kvmvapic.bin \ diff --git a/backends/msmouse.c b/backends/msmouse.c index 8dea5a130f..aeb905562d 100644 --- a/backends/msmouse.c +++ b/backends/msmouse.c @@ -25,16 +25,51 @@ #include "qemu-common.h" #include "sysemu/char.h" #include "ui/console.h" +#include "ui/input.h" #define MSMOUSE_LO6(n) ((n) & 0x3f) #define MSMOUSE_HI2(n) (((n) & 0xc0) >> 6) -static void msmouse_event(void *opaque, - int dx, int dy, int dz, int buttons_state) +typedef struct { + CharDriverState *chr; + QemuInputHandlerState *hs; + int axis[INPUT_AXIS__MAX]; + bool btns[INPUT_BUTTON__MAX]; + bool btnc[INPUT_BUTTON__MAX]; + uint8_t outbuf[32]; + int outlen; +} MouseState; + +static void msmouse_chr_accept_input(CharDriverState *chr) { - CharDriverState *chr = (CharDriverState *)opaque; + MouseState *mouse = chr->opaque; + int len; + + len = qemu_chr_be_can_write(chr); + if (len > mouse->outlen) { + len = mouse->outlen; + } + if (!len) { + return; + } + + qemu_chr_be_write(chr, mouse->outbuf, len); + mouse->outlen -= len; + if (mouse->outlen) { + memmove(mouse->outbuf, mouse->outbuf + len, mouse->outlen); + } +} +static void msmouse_queue_event(MouseState *mouse) +{ unsigned char bytes[4] = { 0x40, 0x00, 0x00, 0x00 }; + int dx, dy, count = 3; + + dx = mouse->axis[INPUT_AXIS_X]; + mouse->axis[INPUT_AXIS_X] = 0; + + dy = mouse->axis[INPUT_AXIS_Y]; + mouse->axis[INPUT_AXIS_Y] = 0; /* Movement deltas */ bytes[0] |= (MSMOUSE_HI2(dy) << 2) | MSMOUSE_HI2(dx); @@ -42,14 +77,54 @@ static void msmouse_event(void *opaque, bytes[2] |= MSMOUSE_LO6(dy); /* Buttons */ - bytes[0] |= (buttons_state & 0x01 ? 0x20 : 0x00); - bytes[0] |= (buttons_state & 0x02 ? 0x10 : 0x00); - bytes[3] |= (buttons_state & 0x04 ? 0x20 : 0x00); - - /* We always send the packet of, so that we do not have to keep track - of previous state of the middle button. This can potentially confuse - some very old drivers for two button mice though. */ - qemu_chr_be_write(chr, bytes, 4); + bytes[0] |= (mouse->btns[INPUT_BUTTON_LEFT] ? 0x20 : 0x00); + bytes[0] |= (mouse->btns[INPUT_BUTTON_RIGHT] ? 0x10 : 0x00); + if (mouse->btns[INPUT_BUTTON_MIDDLE] || + mouse->btnc[INPUT_BUTTON_MIDDLE]) { + bytes[3] |= (mouse->btns[INPUT_BUTTON_MIDDLE] ? 0x20 : 0x00); + mouse->btnc[INPUT_BUTTON_MIDDLE] = false; + count = 4; + } + + if (mouse->outlen <= sizeof(mouse->outbuf) - count) { + memcpy(mouse->outbuf + mouse->outlen, bytes, count); + mouse->outlen += count; + } else { + /* queue full -> drop event */ + } +} + +static void msmouse_input_event(DeviceState *dev, QemuConsole *src, + InputEvent *evt) +{ + MouseState *mouse = (MouseState *)dev; + InputMoveEvent *move; + InputBtnEvent *btn; + + switch (evt->type) { + case INPUT_EVENT_KIND_REL: + move = evt->u.rel.data; + mouse->axis[move->axis] += move->value; + break; + + case INPUT_EVENT_KIND_BTN: + btn = evt->u.btn.data; + mouse->btns[btn->button] = btn->down; + mouse->btnc[btn->button] = true; + break; + + default: + /* keep gcc happy */ + break; + } +} + +static void msmouse_input_sync(DeviceState *dev) +{ + MouseState *mouse = (MouseState *)dev; + + msmouse_queue_event(mouse); + msmouse_chr_accept_input(mouse->chr); } static int msmouse_chr_write (struct CharDriverState *s, const uint8_t *buf, int len) @@ -60,26 +135,41 @@ static int msmouse_chr_write (struct CharDriverState *s, const uint8_t *buf, int static void msmouse_chr_close (struct CharDriverState *chr) { - g_free (chr); + MouseState *mouse = chr->opaque; + + qemu_input_handler_unregister(mouse->hs); + g_free(mouse); + g_free(chr); } +static QemuInputHandler msmouse_handler = { + .name = "QEMU Microsoft Mouse", + .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL, + .event = msmouse_input_event, + .sync = msmouse_input_sync, +}; + static CharDriverState *qemu_chr_open_msmouse(const char *id, ChardevBackend *backend, ChardevReturn *ret, Error **errp) { ChardevCommon *common = backend->u.msmouse.data; + MouseState *mouse; CharDriverState *chr; chr = qemu_chr_alloc(common, errp); - if (!chr) { - return NULL; - } chr->chr_write = msmouse_chr_write; chr->chr_close = msmouse_chr_close; + chr->chr_accept_input = msmouse_chr_accept_input; chr->explicit_be_open = true; - qemu_add_mouse_event_handler(msmouse_event, chr, 0, "QEMU Microsoft Mouse"); + mouse = g_new0(MouseState, 1); + mouse->hs = qemu_input_handler_register((DeviceState *)mouse, + &msmouse_handler); + + mouse->chr = chr; + chr->opaque = mouse; return chr; } @@ -536,9 +536,10 @@ BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size, return drv; } -static int find_image_format(BlockDriverState *bs, const char *filename, +static int find_image_format(BdrvChild *file, const char *filename, BlockDriver **pdrv, Error **errp) { + BlockDriverState *bs = file->bs; BlockDriver *drv; uint8_t buf[BLOCK_PROBE_BUF_SIZE]; int ret = 0; @@ -549,7 +550,7 @@ static int find_image_format(BlockDriverState *bs, const char *filename, return ret; } - ret = bdrv_pread(bs, 0, buf, sizeof(buf)); + ret = bdrv_pread(file, 0, buf, sizeof(buf)); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read image for determining its " "format"); @@ -937,7 +938,6 @@ static int bdrv_open_common(BlockDriverState *bs, BdrvChild *file, goto fail_opts; } - bs->request_alignment = drv->bdrv_co_preadv ? 1 : 512; bs->read_only = !(bs->open_flags & BDRV_O_RDWR); if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) { @@ -1017,7 +1017,7 @@ static int bdrv_open_common(BlockDriverState *bs, BdrvChild *file, assert(bdrv_opt_mem_align(bs) != 0); assert(bdrv_min_mem_align(bs) != 0); - assert(is_power_of_2(bs->request_alignment) || bdrv_is_sg(bs)); + assert(is_power_of_2(bs->bl.request_alignment)); qemu_opts_del(opts); return 0; @@ -1653,7 +1653,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, /* Image format probing */ bs->probed = !drv; if (!drv && file) { - ret = find_image_format(file->bs, filename, &drv, &local_err); + ret = find_image_format(file, filename, &drv, &local_err); if (ret < 0) { goto fail; } @@ -2184,9 +2184,9 @@ static void bdrv_close(BlockDriverState *bs) bs->backing_file[0] = '\0'; bs->backing_format[0] = '\0'; bs->total_sectors = 0; - bs->encrypted = 0; - bs->valid_key = 0; - bs->sg = 0; + bs->encrypted = false; + bs->valid_key = false; + bs->sg = false; QDECREF(bs->options); QDECREF(bs->explicit_options); bs->options = NULL; @@ -2323,116 +2323,6 @@ int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix) return bs->drv->bdrv_check(bs, res, fix); } -#define COMMIT_BUF_SECTORS 2048 - -/* commit COW file into the raw image */ -int bdrv_commit(BlockDriverState *bs) -{ - BlockDriver *drv = bs->drv; - int64_t sector, total_sectors, length, backing_length; - int n, ro, open_flags; - int ret = 0; - uint8_t *buf = NULL; - - if (!drv) - return -ENOMEDIUM; - - if (!bs->backing) { - return -ENOTSUP; - } - - if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT_SOURCE, NULL) || - bdrv_op_is_blocked(bs->backing->bs, BLOCK_OP_TYPE_COMMIT_TARGET, NULL)) { - return -EBUSY; - } - - ro = bs->backing->bs->read_only; - open_flags = bs->backing->bs->open_flags; - - if (ro) { - if (bdrv_reopen(bs->backing->bs, open_flags | BDRV_O_RDWR, NULL)) { - return -EACCES; - } - } - - length = bdrv_getlength(bs); - if (length < 0) { - ret = length; - goto ro_cleanup; - } - - backing_length = bdrv_getlength(bs->backing->bs); - if (backing_length < 0) { - ret = backing_length; - goto ro_cleanup; - } - - /* If our top snapshot is larger than the backing file image, - * grow the backing file image if possible. If not possible, - * we must return an error */ - if (length > backing_length) { - ret = bdrv_truncate(bs->backing->bs, length); - if (ret < 0) { - goto ro_cleanup; - } - } - - total_sectors = length >> BDRV_SECTOR_BITS; - - /* qemu_try_blockalign() for bs will choose an alignment that works for - * bs->backing->bs as well, so no need to compare the alignment manually. */ - buf = qemu_try_blockalign(bs, COMMIT_BUF_SECTORS * BDRV_SECTOR_SIZE); - if (buf == NULL) { - ret = -ENOMEM; - goto ro_cleanup; - } - - for (sector = 0; sector < total_sectors; sector += n) { - ret = bdrv_is_allocated(bs, sector, COMMIT_BUF_SECTORS, &n); - if (ret < 0) { - goto ro_cleanup; - } - if (ret) { - ret = bdrv_read(bs, sector, buf, n); - if (ret < 0) { - goto ro_cleanup; - } - - ret = bdrv_write(bs->backing->bs, sector, buf, n); - if (ret < 0) { - goto ro_cleanup; - } - } - } - - if (drv->bdrv_make_empty) { - ret = drv->bdrv_make_empty(bs); - if (ret < 0) { - goto ro_cleanup; - } - bdrv_flush(bs); - } - - /* - * Make sure all data we wrote to the backing device is actually - * stable on disk. - */ - if (bs->backing) { - bdrv_flush(bs->backing->bs); - } - - ret = 0; -ro_cleanup: - qemu_vfree(buf); - - if (ro) { - /* ignoring error return here */ - bdrv_reopen(bs->backing->bs, open_flags & ~BDRV_O_RDWR, NULL); - } - - return ret; -} - /* * Return values: * 0 - success @@ -2644,30 +2534,30 @@ void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr) *nb_sectors_ptr = nb_sectors < 0 ? 0 : nb_sectors; } -int bdrv_is_read_only(BlockDriverState *bs) +bool bdrv_is_read_only(BlockDriverState *bs) { return bs->read_only; } -int bdrv_is_sg(BlockDriverState *bs) +bool bdrv_is_sg(BlockDriverState *bs) { return bs->sg; } -int bdrv_is_encrypted(BlockDriverState *bs) +bool bdrv_is_encrypted(BlockDriverState *bs) { if (bs->backing && bs->backing->bs->encrypted) { - return 1; + return true; } return bs->encrypted; } -int bdrv_key_required(BlockDriverState *bs) +bool bdrv_key_required(BlockDriverState *bs) { BdrvChild *backing = bs->backing; if (backing && backing->bs->encrypted && !backing->bs->valid_key) { - return 1; + return true; } return (bs->encrypted && !bs->valid_key); } @@ -2689,10 +2579,10 @@ int bdrv_set_key(BlockDriverState *bs, const char *key) } ret = bs->drv->bdrv_set_key(bs, key); if (ret < 0) { - bs->valid_key = 0; + bs->valid_key = false; } else if (!bs->valid_key) { /* call the change callback now, we skipped it on open */ - bs->valid_key = 1; + bs->valid_key = true; bdrv_parent_cb_change_media(bs, true); } return ret; diff --git a/block/Makefile.objs b/block/Makefile.objs index 44a5416225..2593a2f8a6 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs @@ -9,7 +9,7 @@ block-obj-y += block-backend.o snapshot.o qapi.o block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o block-obj-$(CONFIG_POSIX) += raw-posix.o block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o -block-obj-y += null.o mirror.o io.o +block-obj-y += null.o mirror.o commit.o io.o block-obj-y += throttle-groups.o block-obj-y += nbd.o nbd-client.o sheepdog.o @@ -26,7 +26,6 @@ block-obj-y += write-threshold.o block-obj-y += crypto.o common-obj-y += stream.o -common-obj-y += commit.o common-obj-y += backup.o iscsi.o-cflags := $(LIBISCSI_CFLAGS) diff --git a/block/blkdebug.c b/block/blkdebug.c index 20d25bda67..bbaa33fdd8 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -37,6 +37,7 @@ typedef struct BDRVBlkdebugState { int state; int new_state; + int align; QLIST_HEAD(, BlkdebugRule) rules[BLKDBG__MAX]; QSIMPLEQ_HEAD(, BlkdebugRule) active_rules; @@ -382,10 +383,10 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, } /* Set request alignment */ - align = qemu_opt_get_size(opts, "align", bs->request_alignment); - if (align > 0 && align < INT_MAX && !(align & (align - 1))) { - bs->request_alignment = align; - } else { + align = qemu_opt_get_size(opts, "align", 0); + if (align < INT_MAX && is_power_of_2(align)) { + s->align = align; + } else if (align) { error_setg(errp, "Invalid alignment"); ret = -EINVAL; goto fail_unref; @@ -456,7 +457,7 @@ static BlockAIOCB *blkdebug_aio_readv(BlockDriverState *bs, return inject_error(bs, cb, opaque, rule); } - return bdrv_aio_readv(bs->file->bs, sector_num, qiov, nb_sectors, + return bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors, cb, opaque); } @@ -479,7 +480,7 @@ static BlockAIOCB *blkdebug_aio_writev(BlockDriverState *bs, return inject_error(bs, cb, opaque, rule); } - return bdrv_aio_writev(bs->file->bs, sector_num, qiov, nb_sectors, + return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque); } @@ -720,6 +721,15 @@ static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options) bs->full_open_options = opts; } +static void blkdebug_refresh_limits(BlockDriverState *bs, Error **errp) +{ + BDRVBlkdebugState *s = bs->opaque; + + if (s->align) { + bs->bl.request_alignment = s->align; + } +} + static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, Error **errp) { @@ -738,6 +748,7 @@ static BlockDriver bdrv_blkdebug = { .bdrv_getlength = blkdebug_getlength, .bdrv_truncate = blkdebug_truncate, .bdrv_refresh_filename = blkdebug_refresh_filename, + .bdrv_refresh_limits = blkdebug_refresh_limits, .bdrv_aio_readv = blkdebug_aio_readv, .bdrv_aio_writev = blkdebug_aio_writev, diff --git a/block/blkreplay.c b/block/blkreplay.c index 525c2d54e0..70650e4be1 100755 --- a/block/blkreplay.c +++ b/block/blkreplay.c @@ -81,22 +81,22 @@ static void block_request_create(uint64_t reqid, BlockDriverState *bs, replay_block_event(req->bh, reqid); } -static int coroutine_fn blkreplay_co_readv(BlockDriverState *bs, - int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) +static int coroutine_fn blkreplay_co_preadv(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) { uint64_t reqid = request_id++; - int ret = bdrv_co_readv(bs->file->bs, sector_num, nb_sectors, qiov); + int ret = bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); block_request_create(reqid, bs, qemu_coroutine_self()); qemu_coroutine_yield(); return ret; } -static int coroutine_fn blkreplay_co_writev(BlockDriverState *bs, - int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) +static int coroutine_fn blkreplay_co_pwritev(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) { uint64_t reqid = request_id++; - int ret = bdrv_co_writev(bs->file->bs, sector_num, nb_sectors, qiov); + int ret = bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); block_request_create(reqid, bs, qemu_coroutine_self()); qemu_coroutine_yield(); @@ -107,7 +107,7 @@ static int coroutine_fn blkreplay_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int count, BdrvRequestFlags flags) { uint64_t reqid = request_id++; - int ret = bdrv_co_pwrite_zeroes(bs->file->bs, offset, count, flags); + int ret = bdrv_co_pwrite_zeroes(bs->file, offset, count, flags); block_request_create(reqid, bs, qemu_coroutine_self()); qemu_coroutine_yield(); @@ -144,8 +144,8 @@ static BlockDriver bdrv_blkreplay = { .bdrv_close = blkreplay_close, .bdrv_getlength = blkreplay_getlength, - .bdrv_co_readv = blkreplay_co_readv, - .bdrv_co_writev = blkreplay_co_writev, + .bdrv_co_preadv = blkreplay_co_preadv, + .bdrv_co_pwritev = blkreplay_co_pwritev, .bdrv_co_pwrite_zeroes = blkreplay_co_pwrite_zeroes, .bdrv_co_discard = blkreplay_co_discard, diff --git a/block/blkverify.c b/block/blkverify.c index 4045396a3d..da62d75969 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -247,9 +247,9 @@ static BlockAIOCB *blkverify_aio_readv(BlockDriverState *bs, qemu_iovec_init(&acb->raw_qiov, acb->qiov->niov); qemu_iovec_clone(&acb->raw_qiov, qiov, acb->buf); - bdrv_aio_readv(s->test_file->bs, sector_num, qiov, nb_sectors, + bdrv_aio_readv(s->test_file, sector_num, qiov, nb_sectors, blkverify_aio_cb, acb); - bdrv_aio_readv(bs->file->bs, sector_num, &acb->raw_qiov, nb_sectors, + bdrv_aio_readv(bs->file, sector_num, &acb->raw_qiov, nb_sectors, blkverify_aio_cb, acb); return &acb->common; } @@ -262,9 +262,9 @@ static BlockAIOCB *blkverify_aio_writev(BlockDriverState *bs, BlkverifyAIOCB *acb = blkverify_aio_get(bs, true, sector_num, qiov, nb_sectors, cb, opaque); - bdrv_aio_writev(s->test_file->bs, sector_num, qiov, nb_sectors, + bdrv_aio_writev(s->test_file, sector_num, qiov, nb_sectors, blkverify_aio_cb, acb); - bdrv_aio_writev(bs->file->bs, sector_num, qiov, nb_sectors, + bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, blkverify_aio_cb, acb); return &acb->common; } diff --git a/block/block-backend.c b/block/block-backend.c index 34500e6080..a862f6577b 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -760,7 +760,7 @@ int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset, throttle_group_co_io_limits_intercept(blk, bytes, false); } - return bdrv_co_preadv(blk_bs(blk), offset, bytes, qiov, flags); + return bdrv_co_preadv(blk->root, offset, bytes, qiov, flags); } int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset, @@ -785,7 +785,7 @@ int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset, flags |= BDRV_REQ_FUA; } - return bdrv_co_pwritev(blk_bs(blk), offset, bytes, qiov, flags); + return bdrv_co_pwritev(blk->root, offset, bytes, qiov, flags); } typedef struct BlkRwCo { @@ -870,6 +870,11 @@ int blk_pwrite_zeroes(BlockBackend *blk, int64_t offset, flags | BDRV_REQ_ZERO_WRITE); } +int blk_make_zero(BlockBackend *blk, BdrvRequestFlags flags) +{ + return bdrv_make_zero(blk->root, flags); +} + static void error_callback_bh(void *opaque) { struct BlockBackendAIOCB *acb = opaque; @@ -1303,15 +1308,16 @@ int blk_get_flags(BlockBackend *blk) } } -int blk_get_max_transfer_length(BlockBackend *blk) +/* Returns the maximum transfer length, in bytes; guaranteed nonzero */ +uint32_t blk_get_max_transfer(BlockBackend *blk) { BlockDriverState *bs = blk_bs(blk); + uint32_t max = 0; if (bs) { - return bs->bl.max_transfer_length; - } else { - return 0; + max = bs->bl.max_transfer; } + return MIN_NON_ZERO(max, INT_MAX); } int blk_get_max_iov(BlockBackend *blk) diff --git a/block/bochs.c b/block/bochs.c index 6c8d0f3426..8c9652ebeb 100644 --- a/block/bochs.c +++ b/block/bochs.c @@ -104,10 +104,9 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, struct bochs_header bochs; int ret; - bs->read_only = 1; // no write support yet - bs->request_alignment = BDRV_SECTOR_SIZE; /* No sub-sector I/O supported */ + bs->read_only = true; /* no write support yet */ - ret = bdrv_pread(bs->file->bs, 0, &bochs, sizeof(bochs)); + ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)); if (ret < 0) { return ret; } @@ -141,7 +140,7 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, return -ENOMEM; } - ret = bdrv_pread(bs->file->bs, le32_to_cpu(bochs.header), s->catalog_bitmap, + ret = bdrv_pread(bs->file, le32_to_cpu(bochs.header), s->catalog_bitmap, s->catalog_size * 4); if (ret < 0) { goto fail; @@ -189,6 +188,11 @@ fail: return ret; } +static void bochs_refresh_limits(BlockDriverState *bs, Error **errp) +{ + bs->bl.request_alignment = BDRV_SECTOR_SIZE; /* No sub-sector I/O */ +} + static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num) { BDRVBochsState *s = bs->opaque; @@ -210,7 +214,7 @@ static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num) (s->extent_blocks + s->bitmap_blocks)); /* read in bitmap for current extent */ - ret = bdrv_pread(bs->file->bs, bitmap_offset + (extent_offset / 8), + ret = bdrv_pread(bs->file, bitmap_offset + (extent_offset / 8), &bitmap_entry, 1); if (ret < 0) { return ret; @@ -251,7 +255,7 @@ bochs_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, qemu_iovec_concat(&local_qiov, qiov, bytes_done, 512); if (block_offset > 0) { - ret = bdrv_co_preadv(bs->file->bs, block_offset, 512, + ret = bdrv_co_preadv(bs->file, block_offset, 512, &local_qiov, 0); if (ret < 0) { goto fail; @@ -283,6 +287,7 @@ static BlockDriver bdrv_bochs = { .instance_size = sizeof(BDRVBochsState), .bdrv_probe = bochs_probe, .bdrv_open = bochs_open, + .bdrv_refresh_limits = bochs_refresh_limits, .bdrv_co_preadv = bochs_co_preadv, .bdrv_close = bochs_close, }; diff --git a/block/cloop.c b/block/cloop.c index ea5a92b6d4..7b75f7ef7b 100644 --- a/block/cloop.c +++ b/block/cloop.c @@ -66,11 +66,10 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, uint32_t offsets_size, max_compressed_block_size = 1, i; int ret; - bs->read_only = 1; - bs->request_alignment = BDRV_SECTOR_SIZE; /* No sub-sector I/O supported */ + bs->read_only = true; /* read header */ - ret = bdrv_pread(bs->file->bs, 128, &s->block_size, 4); + ret = bdrv_pread(bs->file, 128, &s->block_size, 4); if (ret < 0) { return ret; } @@ -96,7 +95,7 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, return -EINVAL; } - ret = bdrv_pread(bs->file->bs, 128 + 4, &s->n_blocks, 4); + ret = bdrv_pread(bs->file, 128 + 4, &s->n_blocks, 4); if (ret < 0) { return ret; } @@ -127,7 +126,7 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, return -ENOMEM; } - ret = bdrv_pread(bs->file->bs, 128 + 4 + 4, s->offsets, offsets_size); + ret = bdrv_pread(bs->file, 128 + 4 + 4, s->offsets, offsets_size); if (ret < 0) { goto fail; } @@ -199,6 +198,11 @@ fail: return ret; } +static void cloop_refresh_limits(BlockDriverState *bs, Error **errp) +{ + bs->bl.request_alignment = BDRV_SECTOR_SIZE; /* No sub-sector I/O */ +} + static inline int cloop_read_block(BlockDriverState *bs, int block_num) { BDRVCloopState *s = bs->opaque; @@ -207,7 +211,7 @@ static inline int cloop_read_block(BlockDriverState *bs, int block_num) int ret; uint32_t bytes = s->offsets[block_num + 1] - s->offsets[block_num]; - ret = bdrv_pread(bs->file->bs, s->offsets[block_num], + ret = bdrv_pread(bs->file, s->offsets[block_num], s->compressed_block, bytes); if (ret != bytes) { return -1; @@ -280,6 +284,7 @@ static BlockDriver bdrv_cloop = { .instance_size = sizeof(BDRVCloopState), .bdrv_probe = cloop_probe, .bdrv_open = cloop_open, + .bdrv_refresh_limits = cloop_refresh_limits, .bdrv_co_preadv = cloop_co_preadv, .bdrv_close = cloop_close, }; diff --git a/block/commit.c b/block/commit.c index 444333ba65..379efb7c92 100644 --- a/block/commit.c +++ b/block/commit.c @@ -282,3 +282,124 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base, trace_commit_start(bs, base, top, s, s->common.co, opaque); qemu_coroutine_enter(s->common.co, s); } + + +#define COMMIT_BUF_SECTORS 2048 + +/* commit COW file into the raw image */ +int bdrv_commit(BlockDriverState *bs) +{ + BlockBackend *src, *backing; + BlockDriver *drv = bs->drv; + int64_t sector, total_sectors, length, backing_length; + int n, ro, open_flags; + int ret = 0; + uint8_t *buf = NULL; + + if (!drv) + return -ENOMEDIUM; + + if (!bs->backing) { + return -ENOTSUP; + } + + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT_SOURCE, NULL) || + bdrv_op_is_blocked(bs->backing->bs, BLOCK_OP_TYPE_COMMIT_TARGET, NULL)) { + return -EBUSY; + } + + ro = bs->backing->bs->read_only; + open_flags = bs->backing->bs->open_flags; + + if (ro) { + if (bdrv_reopen(bs->backing->bs, open_flags | BDRV_O_RDWR, NULL)) { + return -EACCES; + } + } + + src = blk_new(); + blk_insert_bs(src, bs); + + backing = blk_new(); + blk_insert_bs(backing, bs->backing->bs); + + length = blk_getlength(src); + if (length < 0) { + ret = length; + goto ro_cleanup; + } + + backing_length = blk_getlength(backing); + if (backing_length < 0) { + ret = backing_length; + goto ro_cleanup; + } + + /* If our top snapshot is larger than the backing file image, + * grow the backing file image if possible. If not possible, + * we must return an error */ + if (length > backing_length) { + ret = blk_truncate(backing, length); + if (ret < 0) { + goto ro_cleanup; + } + } + + total_sectors = length >> BDRV_SECTOR_BITS; + + /* blk_try_blockalign() for src will choose an alignment that works for + * backing as well, so no need to compare the alignment manually. */ + buf = blk_try_blockalign(src, COMMIT_BUF_SECTORS * BDRV_SECTOR_SIZE); + if (buf == NULL) { + ret = -ENOMEM; + goto ro_cleanup; + } + + for (sector = 0; sector < total_sectors; sector += n) { + ret = bdrv_is_allocated(bs, sector, COMMIT_BUF_SECTORS, &n); + if (ret < 0) { + goto ro_cleanup; + } + if (ret) { + ret = blk_pread(src, sector * BDRV_SECTOR_SIZE, buf, + n * BDRV_SECTOR_SIZE); + if (ret < 0) { + goto ro_cleanup; + } + + ret = blk_pwrite(backing, sector * BDRV_SECTOR_SIZE, buf, + n * BDRV_SECTOR_SIZE, 0); + if (ret < 0) { + goto ro_cleanup; + } + } + } + + if (drv->bdrv_make_empty) { + ret = drv->bdrv_make_empty(bs); + if (ret < 0) { + goto ro_cleanup; + } + blk_flush(src); + } + + /* + * Make sure all data we wrote to the backing device is actually + * stable on disk. + */ + blk_flush(backing); + + ret = 0; +ro_cleanup: + qemu_vfree(buf); + + blk_unref(src); + blk_unref(backing); + + if (ro) { + /* ignoring error return here */ + bdrv_reopen(bs->backing->bs, open_flags & ~BDRV_O_RDWR, NULL); + } + + return ret; +} diff --git a/block/crypto.c b/block/crypto.c index 758e14e032..7eaa0571b5 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -64,7 +64,7 @@ static ssize_t block_crypto_read_func(QCryptoBlock *block, BlockDriverState *bs = opaque; ssize_t ret; - ret = bdrv_pread(bs->file->bs, offset, buf, buflen); + ret = bdrv_pread(bs->file, offset, buf, buflen); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read encryption header"); return ret; @@ -193,17 +193,16 @@ block_crypto_open_opts_init(QCryptoBlockFormat format, QemuOpts *opts, Error **errp) { - OptsVisitor *ov; + Visitor *v; QCryptoBlockOpenOptions *ret = NULL; Error *local_err = NULL; ret = g_new0(QCryptoBlockOpenOptions, 1); ret->format = format; - ov = opts_visitor_new(opts); + v = opts_visitor_new(opts); - visit_start_struct(opts_get_visitor(ov), - NULL, NULL, 0, &local_err); + visit_start_struct(v, NULL, NULL, 0, &local_err); if (local_err) { goto out; } @@ -211,7 +210,7 @@ block_crypto_open_opts_init(QCryptoBlockFormat format, switch (format) { case Q_CRYPTO_BLOCK_FORMAT_LUKS: visit_type_QCryptoBlockOptionsLUKS_members( - opts_get_visitor(ov), &ret->u.luks, &local_err); + v, &ret->u.luks, &local_err); break; default: @@ -219,10 +218,10 @@ block_crypto_open_opts_init(QCryptoBlockFormat format, break; } if (!local_err) { - visit_check_struct(opts_get_visitor(ov), &local_err); + visit_check_struct(v, &local_err); } - visit_end_struct(opts_get_visitor(ov)); + visit_end_struct(v, NULL); out: if (local_err) { @@ -230,7 +229,7 @@ block_crypto_open_opts_init(QCryptoBlockFormat format, qapi_free_QCryptoBlockOpenOptions(ret); ret = NULL; } - opts_visitor_cleanup(ov); + visit_free(v); return ret; } @@ -240,17 +239,16 @@ block_crypto_create_opts_init(QCryptoBlockFormat format, QemuOpts *opts, Error **errp) { - OptsVisitor *ov; + Visitor *v; QCryptoBlockCreateOptions *ret = NULL; Error *local_err = NULL; ret = g_new0(QCryptoBlockCreateOptions, 1); ret->format = format; - ov = opts_visitor_new(opts); + v = opts_visitor_new(opts); - visit_start_struct(opts_get_visitor(ov), - NULL, NULL, 0, &local_err); + visit_start_struct(v, NULL, NULL, 0, &local_err); if (local_err) { goto out; } @@ -258,7 +256,7 @@ block_crypto_create_opts_init(QCryptoBlockFormat format, switch (format) { case Q_CRYPTO_BLOCK_FORMAT_LUKS: visit_type_QCryptoBlockCreateOptionsLUKS_members( - opts_get_visitor(ov), &ret->u.luks, &local_err); + v, &ret->u.luks, &local_err); break; default: @@ -266,10 +264,10 @@ block_crypto_create_opts_init(QCryptoBlockFormat format, break; } if (!local_err) { - visit_check_struct(opts_get_visitor(ov), &local_err); + visit_check_struct(v, &local_err); } - visit_end_struct(opts_get_visitor(ov)); + visit_end_struct(v, NULL); out: if (local_err) { @@ -277,7 +275,7 @@ block_crypto_create_opts_init(QCryptoBlockFormat format, qapi_free_QCryptoBlockCreateOptions(ret); ret = NULL; } - opts_visitor_cleanup(ov); + visit_free(v); return ret; } @@ -322,8 +320,8 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, goto cleanup; } - bs->encrypted = 1; - bs->valid_key = 1; + bs->encrypted = true; + bs->valid_key = true; ret = 0; cleanup: @@ -428,7 +426,7 @@ block_crypto_co_readv(BlockDriverState *bs, int64_t sector_num, qemu_iovec_reset(&hd_qiov); qemu_iovec_add(&hd_qiov, cipher_data, cur_nr_sectors * 512); - ret = bdrv_co_readv(bs->file->bs, + ret = bdrv_co_readv(bs->file, payload_offset + sector_num, cur_nr_sectors, &hd_qiov); if (ret < 0) { @@ -507,7 +505,7 @@ block_crypto_co_writev(BlockDriverState *bs, int64_t sector_num, qemu_iovec_reset(&hd_qiov); qemu_iovec_add(&hd_qiov, cipher_data, cur_nr_sectors * 512); - ret = bdrv_co_writev(bs->file->bs, + ret = bdrv_co_writev(bs->file, payload_offset + sector_num, cur_nr_sectors, &hd_qiov); if (ret < 0) { diff --git a/block/dmg.c b/block/dmg.c index 06eb5138f3..b0ed89baa7 100644 --- a/block/dmg.c +++ b/block/dmg.c @@ -86,7 +86,7 @@ static int read_uint64(BlockDriverState *bs, int64_t offset, uint64_t *result) uint64_t buffer; int ret; - ret = bdrv_pread(bs->file->bs, offset, &buffer, 8); + ret = bdrv_pread(bs->file, offset, &buffer, 8); if (ret < 0) { return ret; } @@ -100,7 +100,7 @@ static int read_uint32(BlockDriverState *bs, int64_t offset, uint32_t *result) uint32_t buffer; int ret; - ret = bdrv_pread(bs->file->bs, offset, &buffer, 4); + ret = bdrv_pread(bs->file, offset, &buffer, 4); if (ret < 0) { return ret; } @@ -153,8 +153,9 @@ static void update_max_chunk_size(BDRVDMGState *s, uint32_t chunk, } } -static int64_t dmg_find_koly_offset(BlockDriverState *file_bs, Error **errp) +static int64_t dmg_find_koly_offset(BdrvChild *file, Error **errp) { + BlockDriverState *file_bs = file->bs; int64_t length; int64_t offset = 0; uint8_t buffer[515]; @@ -178,7 +179,7 @@ static int64_t dmg_find_koly_offset(BlockDriverState *file_bs, Error **errp) offset = length - 511 - 512; } length = length < 515 ? length : 515; - ret = bdrv_pread(file_bs, offset, buffer, length); + ret = bdrv_pread(file, offset, buffer, length); if (ret < 0) { error_setg_errno(errp, -ret, "Failed while reading UDIF trailer"); return ret; @@ -355,7 +356,7 @@ static int dmg_read_resource_fork(BlockDriverState *bs, DmgHeaderState *ds, offset += 4; buffer = g_realloc(buffer, count); - ret = bdrv_pread(bs->file->bs, offset, buffer, count); + ret = bdrv_pread(bs->file, offset, buffer, count); if (ret < 0) { goto fail; } @@ -392,7 +393,7 @@ static int dmg_read_plist_xml(BlockDriverState *bs, DmgHeaderState *ds, buffer = g_malloc(info_length + 1); buffer[info_length] = '\0'; - ret = bdrv_pread(bs->file->bs, info_begin, buffer, info_length); + ret = bdrv_pread(bs->file, info_begin, buffer, info_length); if (ret != info_length) { ret = -EINVAL; goto fail; @@ -438,8 +439,7 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, int64_t offset; int ret; - bs->read_only = 1; - bs->request_alignment = BDRV_SECTOR_SIZE; /* No sub-sector I/O supported */ + bs->read_only = true; s->n_chunks = 0; s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL; @@ -449,7 +449,7 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, ds.max_sectors_per_chunk = 1; /* locate the UDIF trailer */ - offset = dmg_find_koly_offset(bs->file->bs, errp); + offset = dmg_find_koly_offset(bs->file, errp); if (offset < 0) { ret = offset; goto fail; @@ -547,6 +547,11 @@ fail: return ret; } +static void dmg_refresh_limits(BlockDriverState *bs, Error **errp) +{ + bs->bl.request_alignment = BDRV_SECTOR_SIZE; /* No sub-sector I/O */ +} + static inline int is_sector_in_chunk(BDRVDMGState* s, uint32_t chunk_num, uint64_t sector_num) { @@ -595,7 +600,7 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) case 0x80000005: { /* zlib compressed */ /* we need to buffer, because only the chunk as whole can be * inflated. */ - ret = bdrv_pread(bs->file->bs, s->offsets[chunk], + ret = bdrv_pread(bs->file, s->offsets[chunk], s->compressed_chunk, s->lengths[chunk]); if (ret != s->lengths[chunk]) { return -1; @@ -619,7 +624,7 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) case 0x80000006: /* bzip2 compressed */ /* we need to buffer, because only the chunk as whole can be * inflated. */ - ret = bdrv_pread(bs->file->bs, s->offsets[chunk], + ret = bdrv_pread(bs->file, s->offsets[chunk], s->compressed_chunk, s->lengths[chunk]); if (ret != s->lengths[chunk]) { return -1; @@ -644,7 +649,7 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) break; #endif /* CONFIG_BZIP2 */ case 1: /* copy */ - ret = bdrv_pread(bs->file->bs, s->offsets[chunk], + ret = bdrv_pread(bs->file, s->offsets[chunk], s->uncompressed_chunk, s->lengths[chunk]); if (ret != s->lengths[chunk]) { return -1; @@ -720,6 +725,7 @@ static BlockDriver bdrv_dmg = { .instance_size = sizeof(BDRVDMGState), .bdrv_probe = dmg_probe, .bdrv_open = dmg_open, + .bdrv_refresh_limits = dmg_refresh_limits, .bdrv_co_preadv = dmg_co_preadv, .bdrv_close = dmg_close, }; diff --git a/block/io.c b/block/io.c index 7cf3645ade..708690898f 100644 --- a/block/io.c +++ b/block/io.c @@ -33,7 +33,7 @@ #define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */ -static BlockAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs, +static BlockAIOCB *bdrv_co_aio_rw_vector(BdrvChild *child, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, @@ -67,6 +67,17 @@ static void bdrv_parent_drained_end(BlockDriverState *bs) } } +static void bdrv_merge_limits(BlockLimits *dst, const BlockLimits *src) +{ + dst->opt_transfer = MAX(dst->opt_transfer, src->opt_transfer); + dst->max_transfer = MIN_NON_ZERO(dst->max_transfer, src->max_transfer); + dst->opt_mem_alignment = MAX(dst->opt_mem_alignment, + src->opt_mem_alignment); + dst->min_mem_alignment = MAX(dst->min_mem_alignment, + src->min_mem_alignment); + dst->max_iov = MIN_NON_ZERO(dst->max_iov, src->max_iov); +} + void bdrv_refresh_limits(BlockDriverState *bs, Error **errp) { BlockDriver *drv = bs->drv; @@ -78,6 +89,9 @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp) return; } + /* Default alignment based on whether driver has byte interface */ + bs->bl.request_alignment = drv->bdrv_co_preadv ? 1 : 512; + /* Take some limits from the children as a default */ if (bs->file) { bdrv_refresh_limits(bs->file->bs, &local_err); @@ -85,11 +99,7 @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp) error_propagate(errp, local_err); return; } - bs->bl.opt_transfer_length = bs->file->bs->bl.opt_transfer_length; - bs->bl.max_transfer_length = bs->file->bs->bl.max_transfer_length; - bs->bl.min_mem_alignment = bs->file->bs->bl.min_mem_alignment; - bs->bl.opt_mem_alignment = bs->file->bs->bl.opt_mem_alignment; - bs->bl.max_iov = bs->file->bs->bl.max_iov; + bdrv_merge_limits(&bs->bl, &bs->file->bs->bl); } else { bs->bl.min_mem_alignment = 512; bs->bl.opt_mem_alignment = getpagesize(); @@ -104,21 +114,7 @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp) error_propagate(errp, local_err); return; } - bs->bl.opt_transfer_length = - MAX(bs->bl.opt_transfer_length, - bs->backing->bs->bl.opt_transfer_length); - bs->bl.max_transfer_length = - MIN_NON_ZERO(bs->bl.max_transfer_length, - bs->backing->bs->bl.max_transfer_length); - bs->bl.opt_mem_alignment = - MAX(bs->bl.opt_mem_alignment, - bs->backing->bs->bl.opt_mem_alignment); - bs->bl.min_mem_alignment = - MAX(bs->bl.min_mem_alignment, - bs->backing->bs->bl.min_mem_alignment); - bs->bl.max_iov = - MIN(bs->bl.max_iov, - bs->backing->bs->bl.max_iov); + bdrv_merge_limits(&bs->bl, &bs->backing->bs->bl); } /* Then let the driver override it */ @@ -463,7 +459,7 @@ static int bdrv_get_cluster_size(BlockDriverState *bs) ret = bdrv_get_info(bs, &bdi); if (ret < 0 || bdi.cluster_size == 0) { - return bs->request_alignment; + return bs->bl.request_alignment; } else { return bdi.cluster_size; } @@ -557,7 +553,7 @@ static int bdrv_check_request(BlockDriverState *bs, int64_t sector_num, } typedef struct RwCo { - BlockDriverState *bs; + BdrvChild *child; int64_t offset; QEMUIOVector *qiov; bool is_write; @@ -570,11 +566,11 @@ static void coroutine_fn bdrv_rw_co_entry(void *opaque) RwCo *rwco = opaque; if (!rwco->is_write) { - rwco->ret = bdrv_co_preadv(rwco->bs, rwco->offset, + rwco->ret = bdrv_co_preadv(rwco->child, rwco->offset, rwco->qiov->size, rwco->qiov, rwco->flags); } else { - rwco->ret = bdrv_co_pwritev(rwco->bs, rwco->offset, + rwco->ret = bdrv_co_pwritev(rwco->child, rwco->offset, rwco->qiov->size, rwco->qiov, rwco->flags); } @@ -583,13 +579,13 @@ static void coroutine_fn bdrv_rw_co_entry(void *opaque) /* * Process a vectored synchronous request using coroutines */ -static int bdrv_prwv_co(BlockDriverState *bs, int64_t offset, +static int bdrv_prwv_co(BdrvChild *child, int64_t offset, QEMUIOVector *qiov, bool is_write, BdrvRequestFlags flags) { Coroutine *co; RwCo rwco = { - .bs = bs, + .child = child, .offset = offset, .qiov = qiov, .is_write = is_write, @@ -601,7 +597,7 @@ static int bdrv_prwv_co(BlockDriverState *bs, int64_t offset, /* Fast-path if already in coroutine context */ bdrv_rw_co_entry(&rwco); } else { - AioContext *aio_context = bdrv_get_aio_context(bs); + AioContext *aio_context = bdrv_get_aio_context(child->bs); co = qemu_coroutine_create(bdrv_rw_co_entry); qemu_coroutine_enter(co, &rwco); @@ -615,7 +611,7 @@ static int bdrv_prwv_co(BlockDriverState *bs, int64_t offset, /* * Process a synchronous request using coroutines */ -static int bdrv_rw_co(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, +static int bdrv_rw_co(BdrvChild *child, int64_t sector_num, uint8_t *buf, int nb_sectors, bool is_write, BdrvRequestFlags flags) { QEMUIOVector qiov; @@ -629,15 +625,15 @@ static int bdrv_rw_co(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, } qemu_iovec_init_external(&qiov, &iov, 1); - return bdrv_prwv_co(bs, sector_num << BDRV_SECTOR_BITS, + return bdrv_prwv_co(child, sector_num << BDRV_SECTOR_BITS, &qiov, is_write, flags); } /* return < 0 if error. See bdrv_write() for the return codes */ -int bdrv_read(BlockDriverState *bs, int64_t sector_num, +int bdrv_read(BdrvChild *child, int64_t sector_num, uint8_t *buf, int nb_sectors) { - return bdrv_rw_co(bs, sector_num, buf, nb_sectors, false, 0); + return bdrv_rw_co(child, sector_num, buf, nb_sectors, false, 0); } /* Return < 0 if error. Important errors are: @@ -646,13 +642,13 @@ int bdrv_read(BlockDriverState *bs, int64_t sector_num, -EINVAL Invalid sector number or nb_sectors -EACCES Trying to write a read-only device */ -int bdrv_write(BlockDriverState *bs, int64_t sector_num, +int bdrv_write(BdrvChild *child, int64_t sector_num, const uint8_t *buf, int nb_sectors) { - return bdrv_rw_co(bs, sector_num, (uint8_t *)buf, nb_sectors, true, 0); + return bdrv_rw_co(child, sector_num, (uint8_t *)buf, nb_sectors, true, 0); } -int bdrv_pwrite_zeroes(BlockDriverState *bs, int64_t offset, +int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset, int count, BdrvRequestFlags flags) { QEMUIOVector qiov; @@ -662,7 +658,7 @@ int bdrv_pwrite_zeroes(BlockDriverState *bs, int64_t offset, }; qemu_iovec_init_external(&qiov, &iov, 1); - return bdrv_prwv_co(bs, offset, &qiov, true, + return bdrv_prwv_co(child, offset, &qiov, true, BDRV_REQ_ZERO_WRITE | flags); } @@ -675,9 +671,10 @@ int bdrv_pwrite_zeroes(BlockDriverState *bs, int64_t offset, * * Returns < 0 on error, 0 on success. For error codes see bdrv_write(). */ -int bdrv_make_zero(BlockDriverState *bs, BdrvRequestFlags flags) +int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags) { int64_t target_sectors, ret, nb_sectors, sector_num = 0; + BlockDriverState *bs = child->bs; BlockDriverState *file; int n; @@ -701,7 +698,7 @@ int bdrv_make_zero(BlockDriverState *bs, BdrvRequestFlags flags) sector_num += n; continue; } - ret = bdrv_pwrite_zeroes(bs, sector_num << BDRV_SECTOR_BITS, + ret = bdrv_pwrite_zeroes(child, sector_num << BDRV_SECTOR_BITS, n << BDRV_SECTOR_BITS, flags); if (ret < 0) { error_report("error writing zeroes at sector %" PRId64 ": %s", @@ -712,11 +709,11 @@ int bdrv_make_zero(BlockDriverState *bs, BdrvRequestFlags flags) } } -int bdrv_preadv(BlockDriverState *bs, int64_t offset, QEMUIOVector *qiov) +int bdrv_preadv(BdrvChild *child, int64_t offset, QEMUIOVector *qiov) { int ret; - ret = bdrv_prwv_co(bs, offset, qiov, false, 0); + ret = bdrv_prwv_co(child, offset, qiov, false, 0); if (ret < 0) { return ret; } @@ -724,7 +721,7 @@ int bdrv_preadv(BlockDriverState *bs, int64_t offset, QEMUIOVector *qiov) return qiov->size; } -int bdrv_pread(BlockDriverState *bs, int64_t offset, void *buf, int bytes) +int bdrv_pread(BdrvChild *child, int64_t offset, void *buf, int bytes) { QEMUIOVector qiov; struct iovec iov = { @@ -737,14 +734,14 @@ int bdrv_pread(BlockDriverState *bs, int64_t offset, void *buf, int bytes) } qemu_iovec_init_external(&qiov, &iov, 1); - return bdrv_preadv(bs, offset, &qiov); + return bdrv_preadv(child, offset, &qiov); } -int bdrv_pwritev(BlockDriverState *bs, int64_t offset, QEMUIOVector *qiov) +int bdrv_pwritev(BdrvChild *child, int64_t offset, QEMUIOVector *qiov) { int ret; - ret = bdrv_prwv_co(bs, offset, qiov, true, 0); + ret = bdrv_prwv_co(child, offset, qiov, true, 0); if (ret < 0) { return ret; } @@ -752,8 +749,7 @@ int bdrv_pwritev(BlockDriverState *bs, int64_t offset, QEMUIOVector *qiov) return qiov->size; } -int bdrv_pwrite(BlockDriverState *bs, int64_t offset, - const void *buf, int bytes) +int bdrv_pwrite(BdrvChild *child, int64_t offset, const void *buf, int bytes) { QEMUIOVector qiov; struct iovec iov = { @@ -766,7 +762,7 @@ int bdrv_pwrite(BlockDriverState *bs, int64_t offset, } qemu_iovec_init_external(&qiov, &iov, 1); - return bdrv_pwritev(bs, offset, &qiov); + return bdrv_pwritev(child, offset, &qiov); } /* @@ -775,17 +771,17 @@ int bdrv_pwrite(BlockDriverState *bs, int64_t offset, * * Returns 0 on success, -errno in error cases. */ -int bdrv_pwrite_sync(BlockDriverState *bs, int64_t offset, - const void *buf, int count) +int bdrv_pwrite_sync(BdrvChild *child, int64_t offset, + const void *buf, int count) { int ret; - ret = bdrv_pwrite(bs, offset, buf, count); + ret = bdrv_pwrite(child, offset, buf, count); if (ret < 0) { return ret; } - ret = bdrv_flush(bs); + ret = bdrv_flush(child->bs); if (ret < 0) { return ret; } @@ -945,6 +941,9 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BlockDriverState *bs, if (drv->bdrv_co_pwrite_zeroes && buffer_is_zero(bounce_buffer, iov.iov_len)) { + /* FIXME: Should we (perhaps conditionally) be setting + * BDRV_REQ_MAY_UNMAP, if it will allow for a sparser copy + * that still correctly reads as zero? */ ret = bdrv_co_do_pwrite_zeroes(bs, cluster_offset, cluster_bytes, 0); } else { /* This does not change the data on the disk, it is not necessary @@ -987,7 +986,12 @@ static int coroutine_fn bdrv_aligned_preadv(BlockDriverState *bs, assert((bytes & (align - 1)) == 0); assert(!qiov || bytes == qiov->size); assert((bs->open_flags & BDRV_O_NO_IO) == 0); - assert(!(flags & ~BDRV_REQ_MASK)); + + /* TODO: We would need a per-BDS .supported_read_flags and + * potential fallback support, if we ever implement any read flags + * to pass through to drivers. For now, there aren't any + * passthrough flags. */ + assert(!(flags & ~(BDRV_REQ_NO_SERIALISING | BDRV_REQ_COPY_ON_READ))); /* Handle Copy on Read and associated serialisation */ if (flags & BDRV_REQ_COPY_ON_READ) { @@ -1028,7 +1032,7 @@ static int coroutine_fn bdrv_aligned_preadv(BlockDriverState *bs, } max_bytes = ROUND_UP(MAX(0, total_bytes - offset), align); - if (bytes < max_bytes) { + if (bytes <= max_bytes) { ret = bdrv_driver_preadv(bs, offset, bytes, qiov, 0); } else if (max_bytes > 0) { QEMUIOVector local_qiov; @@ -1057,14 +1061,15 @@ out: /* * Handle a read request in coroutine context */ -int coroutine_fn bdrv_co_preadv(BlockDriverState *bs, +int coroutine_fn bdrv_co_preadv(BdrvChild *child, int64_t offset, unsigned int bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { + BlockDriverState *bs = child->bs; BlockDriver *drv = bs->drv; BdrvTrackedRequest req; - uint64_t align = bs->request_alignment; + uint64_t align = bs->bl.request_alignment; uint8_t *head_buf = NULL; uint8_t *tail_buf = NULL; QEMUIOVector local_qiov; @@ -1125,7 +1130,7 @@ int coroutine_fn bdrv_co_preadv(BlockDriverState *bs, return ret; } -static int coroutine_fn bdrv_co_do_readv(BlockDriverState *bs, +static int coroutine_fn bdrv_co_do_readv(BdrvChild *child, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, BdrvRequestFlags flags) { @@ -1133,19 +1138,20 @@ static int coroutine_fn bdrv_co_do_readv(BlockDriverState *bs, return -EINVAL; } - return bdrv_co_preadv(bs, sector_num << BDRV_SECTOR_BITS, + return bdrv_co_preadv(child, sector_num << BDRV_SECTOR_BITS, nb_sectors << BDRV_SECTOR_BITS, qiov, flags); } -int coroutine_fn bdrv_co_readv(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov) +int coroutine_fn bdrv_co_readv(BdrvChild *child, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov) { - trace_bdrv_co_readv(bs, sector_num, nb_sectors); + trace_bdrv_co_readv(child->bs, sector_num, nb_sectors); - return bdrv_co_do_readv(bs, sector_num, nb_sectors, qiov, 0); + return bdrv_co_do_readv(child, sector_num, nb_sectors, qiov, 0); } -#define MAX_WRITE_ZEROES_BOUNCE_BUFFER 32768 +/* Maximum buffer for write zeroes fallback, in bytes */ +#define MAX_WRITE_ZEROES_BOUNCE_BUFFER (32768 << BDRV_SECTOR_BITS) static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int count, BdrvRequestFlags flags) @@ -1159,8 +1165,8 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, int tail = 0; int max_write_zeroes = MIN_NON_ZERO(bs->bl.max_pwrite_zeroes, INT_MAX); - int alignment = MAX(bs->bl.pwrite_zeroes_alignment ?: 1, - bs->request_alignment); + int alignment = MAX(bs->bl.pwrite_zeroes_alignment, + bs->bl.request_alignment); assert(is_power_of_2(alignment)); head = offset & (alignment - 1); @@ -1203,7 +1209,7 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, if (ret == -ENOTSUP) { /* Fall back to bounce buffer if write zeroes is unsupported */ - int max_xfer_len = MIN_NON_ZERO(bs->bl.max_transfer_length, + int max_transfer = MIN_NON_ZERO(bs->bl.max_transfer, MAX_WRITE_ZEROES_BOUNCE_BUFFER); BdrvRequestFlags write_flags = flags & ~BDRV_REQ_ZERO_WRITE; @@ -1214,7 +1220,7 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, write_flags &= ~BDRV_REQ_FUA; need_flush = true; } - num = MIN(num, max_xfer_len << BDRV_SECTOR_BITS); + num = MIN(num, max_transfer); iov.iov_len = num; if (iov.iov_base == NULL) { iov.iov_base = qemu_try_blockalign(bs, num); @@ -1231,7 +1237,7 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, /* Keep bounce buffer around if it is big enough for all * all future requests. */ - if (num < max_xfer_len << BDRV_SECTOR_BITS) { + if (num < max_transfer) { qemu_vfree(iov.iov_base); iov.iov_base = NULL; } @@ -1254,7 +1260,7 @@ fail: */ static int coroutine_fn bdrv_aligned_pwritev(BlockDriverState *bs, BdrvTrackedRequest *req, int64_t offset, unsigned int bytes, - QEMUIOVector *qiov, int flags) + int64_t align, QEMUIOVector *qiov, int flags) { BlockDriver *drv = bs->drv; bool waited; @@ -1263,6 +1269,9 @@ static int coroutine_fn bdrv_aligned_pwritev(BlockDriverState *bs, int64_t start_sector = offset >> BDRV_SECTOR_BITS; int64_t end_sector = DIV_ROUND_UP(offset + bytes, BDRV_SECTOR_SIZE); + assert(is_power_of_2(align)); + assert((offset & (align - 1)) == 0); + assert((bytes & (align - 1)) == 0); assert(!qiov || bytes == qiov->size); assert((bs->open_flags & BDRV_O_NO_IO) == 0); assert(!(flags & ~BDRV_REQ_MASK)); @@ -1316,7 +1325,7 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BlockDriverState *bs, uint8_t *buf = NULL; QEMUIOVector local_qiov; struct iovec iov; - uint64_t align = bs->request_alignment; + uint64_t align = bs->bl.request_alignment; unsigned int head_padding_bytes, tail_padding_bytes; int ret = 0; @@ -1349,7 +1358,7 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BlockDriverState *bs, memset(buf + head_padding_bytes, 0, zero_bytes); ret = bdrv_aligned_pwritev(bs, req, offset & ~(align - 1), align, - &local_qiov, + align, &local_qiov, flags & ~BDRV_REQ_ZERO_WRITE); if (ret < 0) { goto fail; @@ -1362,7 +1371,7 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BlockDriverState *bs, if (bytes >= align) { /* Write the aligned part in the middle. */ uint64_t aligned_bytes = bytes & ~(align - 1); - ret = bdrv_aligned_pwritev(bs, req, offset, aligned_bytes, + ret = bdrv_aligned_pwritev(bs, req, offset, aligned_bytes, align, NULL, flags); if (ret < 0) { goto fail; @@ -1386,7 +1395,7 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BlockDriverState *bs, bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL); memset(buf, 0, bytes); - ret = bdrv_aligned_pwritev(bs, req, offset, align, + ret = bdrv_aligned_pwritev(bs, req, offset, align, align, &local_qiov, flags & ~BDRV_REQ_ZERO_WRITE); } fail: @@ -1398,12 +1407,13 @@ fail: /* * Handle a write request in coroutine context */ -int coroutine_fn bdrv_co_pwritev(BlockDriverState *bs, +int coroutine_fn bdrv_co_pwritev(BdrvChild *child, int64_t offset, unsigned int bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { + BlockDriverState *bs = child->bs; BdrvTrackedRequest req; - uint64_t align = bs->request_alignment; + uint64_t align = bs->bl.request_alignment; uint8_t *head_buf = NULL; uint8_t *tail_buf = NULL; QEMUIOVector local_qiov; @@ -1511,7 +1521,7 @@ int coroutine_fn bdrv_co_pwritev(BlockDriverState *bs, bytes = ROUND_UP(bytes, align); } - ret = bdrv_aligned_pwritev(bs, &req, offset, bytes, + ret = bdrv_aligned_pwritev(bs, &req, offset, bytes, align, use_local_qiov ? &local_qiov : qiov, flags); @@ -1527,7 +1537,7 @@ out: return ret; } -static int coroutine_fn bdrv_co_do_writev(BlockDriverState *bs, +static int coroutine_fn bdrv_co_do_writev(BdrvChild *child, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, BdrvRequestFlags flags) { @@ -1535,29 +1545,28 @@ static int coroutine_fn bdrv_co_do_writev(BlockDriverState *bs, return -EINVAL; } - return bdrv_co_pwritev(bs, sector_num << BDRV_SECTOR_BITS, + return bdrv_co_pwritev(child, sector_num << BDRV_SECTOR_BITS, nb_sectors << BDRV_SECTOR_BITS, qiov, flags); } -int coroutine_fn bdrv_co_writev(BlockDriverState *bs, int64_t sector_num, +int coroutine_fn bdrv_co_writev(BdrvChild *child, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) { - trace_bdrv_co_writev(bs, sector_num, nb_sectors); + trace_bdrv_co_writev(child->bs, sector_num, nb_sectors); - return bdrv_co_do_writev(bs, sector_num, nb_sectors, qiov, 0); + return bdrv_co_do_writev(child, sector_num, nb_sectors, qiov, 0); } -int coroutine_fn bdrv_co_pwrite_zeroes(BlockDriverState *bs, - int64_t offset, int count, - BdrvRequestFlags flags) +int coroutine_fn bdrv_co_pwrite_zeroes(BdrvChild *child, int64_t offset, + int count, BdrvRequestFlags flags) { - trace_bdrv_co_pwrite_zeroes(bs, offset, count, flags); + trace_bdrv_co_pwrite_zeroes(child->bs, offset, count, flags); - if (!(bs->open_flags & BDRV_O_UNMAP)) { + if (!(child->bs->open_flags & BDRV_O_UNMAP)) { flags &= ~BDRV_REQ_MAY_UNMAP; } - return bdrv_co_pwritev(bs, offset, count, NULL, + return bdrv_co_pwritev(child, offset, count, NULL, BDRV_REQ_ZERO_WRITE | flags); } @@ -1954,23 +1963,23 @@ int bdrv_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) /**************************************************************/ /* async I/Os */ -BlockAIOCB *bdrv_aio_readv(BlockDriverState *bs, int64_t sector_num, +BlockAIOCB *bdrv_aio_readv(BdrvChild *child, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, BlockCompletionFunc *cb, void *opaque) { - trace_bdrv_aio_readv(bs, sector_num, nb_sectors, opaque); + trace_bdrv_aio_readv(child->bs, sector_num, nb_sectors, opaque); - return bdrv_co_aio_rw_vector(bs, sector_num, qiov, nb_sectors, 0, + return bdrv_co_aio_rw_vector(child, sector_num, qiov, nb_sectors, 0, cb, opaque, false); } -BlockAIOCB *bdrv_aio_writev(BlockDriverState *bs, int64_t sector_num, +BlockAIOCB *bdrv_aio_writev(BdrvChild *child, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, BlockCompletionFunc *cb, void *opaque) { - trace_bdrv_aio_writev(bs, sector_num, nb_sectors, opaque); + trace_bdrv_aio_writev(child->bs, sector_num, nb_sectors, opaque); - return bdrv_co_aio_rw_vector(bs, sector_num, qiov, nb_sectors, 0, + return bdrv_co_aio_rw_vector(child, sector_num, qiov, nb_sectors, 0, cb, opaque, true); } @@ -2026,6 +2035,7 @@ typedef struct BlockRequest { typedef struct BlockAIOCBCoroutine { BlockAIOCB common; + BdrvChild *child; BlockRequest req; bool is_write; bool need_bh; @@ -2069,20 +2079,19 @@ static void bdrv_co_maybe_schedule_bh(BlockAIOCBCoroutine *acb) static void coroutine_fn bdrv_co_do_rw(void *opaque) { BlockAIOCBCoroutine *acb = opaque; - BlockDriverState *bs = acb->common.bs; if (!acb->is_write) { - acb->req.error = bdrv_co_do_readv(bs, acb->req.sector, + acb->req.error = bdrv_co_do_readv(acb->child, acb->req.sector, acb->req.nb_sectors, acb->req.qiov, acb->req.flags); } else { - acb->req.error = bdrv_co_do_writev(bs, acb->req.sector, + acb->req.error = bdrv_co_do_writev(acb->child, acb->req.sector, acb->req.nb_sectors, acb->req.qiov, acb->req.flags); } bdrv_co_complete(acb); } -static BlockAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs, +static BlockAIOCB *bdrv_co_aio_rw_vector(BdrvChild *child, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, @@ -2094,7 +2103,8 @@ static BlockAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs, Coroutine *co; BlockAIOCBCoroutine *acb; - acb = qemu_aio_get(&bdrv_em_co_aiocb_info, bs, cb, opaque); + acb = qemu_aio_get(&bdrv_em_co_aiocb_info, child->bs, cb, opaque); + acb->child = child; acb->need_bh = true; acb->req.error = -EINPROGRESS; acb->req.sector = sector_num; @@ -2200,9 +2210,15 @@ void qemu_aio_unref(void *p) /**************************************************************/ /* Coroutine block device emulation */ +typedef struct FlushCo { + BlockDriverState *bs; + int ret; +} FlushCo; + + static void coroutine_fn bdrv_flush_co_entry(void *opaque) { - RwCo *rwco = opaque; + FlushCo *rwco = opaque; rwco->ret = bdrv_co_flush(rwco->bs); } @@ -2286,25 +2302,25 @@ out: int bdrv_flush(BlockDriverState *bs) { Coroutine *co; - RwCo rwco = { + FlushCo flush_co = { .bs = bs, .ret = NOT_DONE, }; if (qemu_in_coroutine()) { /* Fast-path if already in coroutine context */ - bdrv_flush_co_entry(&rwco); + bdrv_flush_co_entry(&flush_co); } else { AioContext *aio_context = bdrv_get_aio_context(bs); co = qemu_coroutine_create(bdrv_flush_co_entry); - qemu_coroutine_enter(co, &rwco); - while (rwco.ret == NOT_DONE) { + qemu_coroutine_enter(co, &flush_co); + while (flush_co.ret == NOT_DONE) { aio_poll(aio_context, true); } } - return rwco.ret; + return flush_co.ret; } typedef struct DiscardCo { @@ -2355,19 +2371,21 @@ int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num, goto out; } - max_discard = MIN_NON_ZERO(bs->bl.max_discard, BDRV_REQUEST_MAX_SECTORS); + max_discard = MIN_NON_ZERO(bs->bl.max_pdiscard >> BDRV_SECTOR_BITS, + BDRV_REQUEST_MAX_SECTORS); while (nb_sectors > 0) { int ret; int num = nb_sectors; + int discard_alignment = bs->bl.pdiscard_alignment >> BDRV_SECTOR_BITS; /* align request */ - if (bs->bl.discard_alignment && - num >= bs->bl.discard_alignment && - sector_num % bs->bl.discard_alignment) { - if (num > bs->bl.discard_alignment) { - num = bs->bl.discard_alignment; + if (discard_alignment && + num >= discard_alignment && + sector_num % discard_alignment) { + if (num > discard_alignment) { + num = discard_alignment; } - num -= sector_num % bs->bl.discard_alignment; + num -= sector_num % discard_alignment; } /* limit request size */ diff --git a/block/iscsi.c b/block/iscsi.c index 9bb5ff6216..24f78a7755 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -473,9 +473,10 @@ iscsi_co_writev_flags(BlockDriverState *bs, int64_t sector_num, int nb_sectors, return -EINVAL; } - if (bs->bl.max_transfer_length && nb_sectors > bs->bl.max_transfer_length) { + if (bs->bl.max_transfer && + nb_sectors << BDRV_SECTOR_BITS > bs->bl.max_transfer) { error_report("iSCSI Error: Write of %d sectors exceeds max_xfer_len " - "of %d sectors", nb_sectors, bs->bl.max_transfer_length); + "of %" PRIu32 " bytes", nb_sectors, bs->bl.max_transfer); return -EINVAL; } @@ -650,9 +651,10 @@ static int coroutine_fn iscsi_co_readv(BlockDriverState *bs, return -EINVAL; } - if (bs->bl.max_transfer_length && nb_sectors > bs->bl.max_transfer_length) { + if (bs->bl.max_transfer && + nb_sectors << BDRV_SECTOR_BITS > bs->bl.max_transfer) { error_report("iSCSI Error: Read of %d sectors exceeds max_xfer_len " - "of %d sectors", nb_sectors, bs->bl.max_transfer_length); + "of %" PRIu32 " bytes", nb_sectors, bs->bl.max_transfer); return -EINVAL; } @@ -1589,14 +1591,13 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, goto out; } bs->total_sectors = sector_lun2qemu(iscsilun->num_blocks, iscsilun); - bs->request_alignment = iscsilun->block_size; /* We don't have any emulation for devices other than disks and CD-ROMs, so * this must be sg ioctl compatible. We force it to be sg, otherwise qemu * will try to read from the device to guess the image format. */ if (iscsilun->type != TYPE_DISK && iscsilun->type != TYPE_ROM) { - bs->sg = 1; + bs->sg = true; } task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1, @@ -1696,34 +1697,33 @@ static void iscsi_close(BlockDriverState *bs) memset(iscsilun, 0, sizeof(IscsiLun)); } -static int sector_limits_lun2qemu(int64_t sector, IscsiLun *iscsilun) -{ - return MIN(sector_lun2qemu(sector, iscsilun), INT_MAX / 2 + 1); -} - static void iscsi_refresh_limits(BlockDriverState *bs, Error **errp) { /* We don't actually refresh here, but just return data queried in * iscsi_open(): iscsi targets don't change their limits. */ IscsiLun *iscsilun = bs->opaque; - uint32_t max_xfer_len = iscsilun->use_16_for_rw ? 0xffffffff : 0xffff; + uint64_t max_xfer_len = iscsilun->use_16_for_rw ? 0xffffffff : 0xffff; + + bs->bl.request_alignment = iscsilun->block_size; if (iscsilun->bl.max_xfer_len) { max_xfer_len = MIN(max_xfer_len, iscsilun->bl.max_xfer_len); } - bs->bl.max_transfer_length = sector_limits_lun2qemu(max_xfer_len, iscsilun); + if (max_xfer_len * iscsilun->block_size < INT_MAX) { + bs->bl.max_transfer = max_xfer_len * iscsilun->block_size; + } if (iscsilun->lbp.lbpu) { - if (iscsilun->bl.max_unmap < 0xffffffff) { - bs->bl.max_discard = - sector_limits_lun2qemu(iscsilun->bl.max_unmap, iscsilun); + if (iscsilun->bl.max_unmap < 0xffffffff / iscsilun->block_size) { + bs->bl.max_pdiscard = + iscsilun->bl.max_unmap * iscsilun->block_size; } - bs->bl.discard_alignment = - sector_limits_lun2qemu(iscsilun->bl.opt_unmap_gran, iscsilun); + bs->bl.pdiscard_alignment = + iscsilun->bl.opt_unmap_gran * iscsilun->block_size; } else { - bs->bl.discard_alignment = iscsilun->block_size >> BDRV_SECTOR_BITS; + bs->bl.pdiscard_alignment = iscsilun->block_size; } if (iscsilun->bl.max_ws_len < 0xffffffff / iscsilun->block_size) { @@ -1736,8 +1736,11 @@ static void iscsi_refresh_limits(BlockDriverState *bs, Error **errp) } else { bs->bl.pwrite_zeroes_alignment = iscsilun->block_size; } - bs->bl.opt_transfer_length = - sector_limits_lun2qemu(iscsilun->bl.opt_xfer_len, iscsilun); + if (iscsilun->bl.opt_xfer_len && + iscsilun->bl.opt_xfer_len < INT_MAX / iscsilun->block_size) { + bs->bl.opt_transfer = pow2floor(iscsilun->bl.opt_xfer_len * + iscsilun->block_size); + } } /* Note that this will not re-establish a connection with an iSCSI target - it diff --git a/block/linux-aio.c b/block/linux-aio.c index e468960146..7df8651581 100644 --- a/block/linux-aio.c +++ b/block/linux-aio.c @@ -87,7 +87,7 @@ static void qemu_laio_process_completion(struct qemu_laiocb *laiocb) qemu_iovec_memset(laiocb->qiov, ret, 0, laiocb->qiov->size - ret); } else { - ret = -EINVAL; + ret = -ENOSPC; } } } diff --git a/block/nbd-client.c b/block/nbd-client.c index 4d13444409..420bce89f3 100644 --- a/block/nbd-client.c +++ b/block/nbd-client.c @@ -269,10 +269,6 @@ static int nbd_co_writev_1(BlockDriverState *bs, int64_t sector_num, return -reply.error; } -/* qemu-nbd has a limit of slightly less than 1M per request. Try to - * remain aligned to 4K. */ -#define NBD_MAX_SECTORS 2040 - int nbd_client_co_readv(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) { diff --git a/block/nbd.c b/block/nbd.c index 6015e8b537..08e5b67b2f 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -362,8 +362,8 @@ static int nbd_co_flush(BlockDriverState *bs) static void nbd_refresh_limits(BlockDriverState *bs, Error **errp) { - bs->bl.max_discard = UINT32_MAX >> BDRV_SECTOR_BITS; - bs->bl.max_transfer_length = UINT32_MAX >> BDRV_SECTOR_BITS; + bs->bl.max_pdiscard = NBD_MAX_BUFFER_SIZE; + bs->bl.max_transfer = NBD_MAX_BUFFER_SIZE; } static int nbd_co_discard(BlockDriverState *bs, int64_t sector_num, diff --git a/block/parallels.c b/block/parallels.c index d6a1a616b6..807a80169f 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -210,7 +210,7 @@ static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num, int ret; space += s->prealloc_size; if (s->prealloc_mode == PRL_PREALLOC_MODE_FALLOCATE) { - ret = bdrv_pwrite_zeroes(bs->file->bs, + ret = bdrv_pwrite_zeroes(bs->file, s->data_end << BDRV_SECTOR_BITS, space << BDRV_SECTOR_BITS, 0); } else { @@ -250,7 +250,7 @@ static coroutine_fn int parallels_co_flush_to_os(BlockDriverState *bs) if (off + to_write > s->header_size) { to_write = s->header_size - off; } - ret = bdrv_pwrite(bs->file->bs, off, (uint8_t *)s->header + off, + ret = bdrv_pwrite(bs->file, off, (uint8_t *)s->header + off, to_write); if (ret < 0) { qemu_co_mutex_unlock(&s->lock); @@ -311,7 +311,7 @@ static coroutine_fn int parallels_co_writev(BlockDriverState *bs, qemu_iovec_reset(&hd_qiov); qemu_iovec_concat(&hd_qiov, qiov, bytes_done, nbytes); - ret = bdrv_co_writev(bs->file->bs, position, n, &hd_qiov); + ret = bdrv_co_writev(bs->file, position, n, &hd_qiov); if (ret < 0) { break; } @@ -351,7 +351,7 @@ static coroutine_fn int parallels_co_readv(BlockDriverState *bs, qemu_iovec_reset(&hd_qiov); qemu_iovec_concat(&hd_qiov, qiov, bytes_done, nbytes); - ret = bdrv_co_readv(bs->file->bs, position, n, &hd_qiov); + ret = bdrv_co_readv(bs->file, position, n, &hd_qiov); if (ret < 0) { break; } @@ -432,7 +432,7 @@ static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res, } if (flush_bat) { - ret = bdrv_pwrite_sync(bs->file->bs, 0, s->header, s->header_size); + ret = bdrv_pwrite_sync(bs->file, 0, s->header, s->header_size); if (ret < 0) { res->check_errors++; return ret; @@ -563,7 +563,7 @@ static int parallels_update_header(BlockDriverState *bs) if (size > s->header_size) { size = s->header_size; } - return bdrv_pwrite_sync(bs->file->bs, 0, s->header, size); + return bdrv_pwrite_sync(bs->file, 0, s->header, size); } static int parallels_open(BlockDriverState *bs, QDict *options, int flags, @@ -576,7 +576,7 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, Error *local_err = NULL; char *buf; - ret = bdrv_pread(bs->file->bs, 0, &ph, sizeof(ph)); + ret = bdrv_pread(bs->file, 0, &ph, sizeof(ph)); if (ret < 0) { goto fail; } @@ -631,7 +631,7 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, s->header_size = size; } - ret = bdrv_pread(bs->file->bs, 0, s->header, s->header_size); + ret = bdrv_pread(bs->file, 0, s->header, s->header_size); if (ret < 0) { goto fail; } diff --git a/block/qapi.c b/block/qapi.c index 5594f74d17..6f947e3e66 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -690,16 +690,15 @@ static void dump_qdict(fprintf_function func_fprintf, void *f, int indentation, void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f, ImageInfoSpecific *info_spec) { - QmpOutputVisitor *ov = qmp_output_visitor_new(); QObject *obj, *data; + Visitor *v = qmp_output_visitor_new(&obj); - visit_type_ImageInfoSpecific(qmp_output_get_visitor(ov), NULL, &info_spec, - &error_abort); - obj = qmp_output_get_qobject(ov); + visit_type_ImageInfoSpecific(v, NULL, &info_spec, &error_abort); + visit_complete(v, &obj); assert(qobject_type(obj) == QTYPE_QDICT); data = qdict_get(qobject_to_qdict(obj), "data"); dump_qobject(func_fprintf, f, 1, data); - qmp_output_visitor_cleanup(ov); + visit_free(v); } void bdrv_image_info_dump(fprintf_function func_fprintf, void *f, diff --git a/block/qcow.c b/block/qcow.c index 312af52816..ac849bd47c 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -105,7 +105,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, int ret; QCowHeader header; - ret = bdrv_pread(bs->file->bs, 0, &header, sizeof(header)); + ret = bdrv_pread(bs->file, 0, &header, sizeof(header)); if (ret < 0) { goto fail; } @@ -174,7 +174,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - bs->encrypted = 1; + bs->encrypted = true; } s->cluster_bits = header.cluster_bits; s->cluster_size = 1 << s->cluster_bits; @@ -208,7 +208,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - ret = bdrv_pread(bs->file->bs, s->l1_table_offset, s->l1_table, + ret = bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, s->l1_size * sizeof(uint64_t)); if (ret < 0) { goto fail; @@ -239,7 +239,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, ret = -EINVAL; goto fail; } - ret = bdrv_pread(bs->file->bs, header.backing_file_offset, + ret = bdrv_pread(bs->file, header.backing_file_offset, bs->backing_file, len); if (ret < 0) { goto fail; @@ -390,7 +390,7 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, /* update the L1 entry */ s->l1_table[l1_index] = l2_offset; tmp = cpu_to_be64(l2_offset); - if (bdrv_pwrite_sync(bs->file->bs, + if (bdrv_pwrite_sync(bs->file, s->l1_table_offset + l1_index * sizeof(tmp), &tmp, sizeof(tmp)) < 0) return 0; @@ -420,11 +420,11 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, l2_table = s->l2_cache + (min_index << s->l2_bits); if (new_l2_table) { memset(l2_table, 0, s->l2_size * sizeof(uint64_t)); - if (bdrv_pwrite_sync(bs->file->bs, l2_offset, l2_table, + if (bdrv_pwrite_sync(bs->file, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) < 0) return 0; } else { - if (bdrv_pread(bs->file->bs, l2_offset, l2_table, + if (bdrv_pread(bs->file, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) != s->l2_size * sizeof(uint64_t)) return 0; @@ -450,7 +450,7 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, cluster_offset = (cluster_offset + s->cluster_size - 1) & ~(s->cluster_size - 1); /* write the cluster content */ - if (bdrv_pwrite(bs->file->bs, cluster_offset, s->cluster_cache, + if (bdrv_pwrite(bs->file, cluster_offset, s->cluster_cache, s->cluster_size) != s->cluster_size) return -1; @@ -480,7 +480,7 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, errno = EIO; return -1; } - if (bdrv_pwrite(bs->file->bs, + if (bdrv_pwrite(bs->file, cluster_offset + i * 512, s->cluster_data, 512) != 512) return -1; @@ -495,7 +495,7 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, /* update L2 table */ tmp = cpu_to_be64(cluster_offset); l2_table[l2_index] = tmp; - if (bdrv_pwrite_sync(bs->file->bs, l2_offset + l2_index * sizeof(tmp), + if (bdrv_pwrite_sync(bs->file, l2_offset + l2_index * sizeof(tmp), &tmp, sizeof(tmp)) < 0) return 0; } @@ -565,7 +565,7 @@ static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) if (s->cluster_cache_offset != coffset) { csize = cluster_offset >> (63 - s->cluster_bits); csize &= (s->cluster_size - 1); - ret = bdrv_pread(bs->file->bs, coffset, s->cluster_data, csize); + ret = bdrv_pread(bs->file, coffset, s->cluster_data, csize); if (ret != csize) return -1; if (decompress_buffer(s->cluster_cache, s->cluster_size, @@ -619,8 +619,7 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, hd_iov.iov_len = n * 512; qemu_iovec_init_external(&hd_qiov, &hd_iov, 1); qemu_co_mutex_unlock(&s->lock); - ret = bdrv_co_readv(bs->backing->bs, sector_num, - n, &hd_qiov); + ret = bdrv_co_readv(bs->backing, sector_num, n, &hd_qiov); qemu_co_mutex_lock(&s->lock); if (ret < 0) { goto fail; @@ -644,7 +643,7 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, hd_iov.iov_len = n * 512; qemu_iovec_init_external(&hd_qiov, &hd_iov, 1); qemu_co_mutex_unlock(&s->lock); - ret = bdrv_co_readv(bs->file->bs, + ret = bdrv_co_readv(bs->file, (cluster_offset >> 9) + index_in_cluster, n, &hd_qiov); qemu_co_mutex_lock(&s->lock); @@ -746,7 +745,7 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num, hd_iov.iov_len = n * 512; qemu_iovec_init_external(&hd_qiov, &hd_iov, 1); qemu_co_mutex_unlock(&s->lock); - ret = bdrv_co_writev(bs->file->bs, + ret = bdrv_co_writev(bs->file, (cluster_offset >> 9) + index_in_cluster, n, &hd_qiov); qemu_co_mutex_lock(&s->lock); @@ -900,7 +899,7 @@ static int qcow_make_empty(BlockDriverState *bs) int ret; memset(s->l1_table, 0, l1_length); - if (bdrv_pwrite_sync(bs->file->bs, s->l1_table_offset, s->l1_table, + if (bdrv_pwrite_sync(bs->file, s->l1_table_offset, s->l1_table, l1_length) < 0) return -1; ret = bdrv_truncate(bs->file->bs, s->l1_table_offset + l1_length); @@ -914,6 +913,49 @@ static int qcow_make_empty(BlockDriverState *bs) return 0; } +typedef struct QcowWriteCo { + BlockDriverState *bs; + int64_t sector_num; + const uint8_t *buf; + int nb_sectors; + int ret; +} QcowWriteCo; + +static void qcow_write_co_entry(void *opaque) +{ + QcowWriteCo *co = opaque; + QEMUIOVector qiov; + + struct iovec iov = (struct iovec) { + .iov_base = (uint8_t*) co->buf, + .iov_len = co->nb_sectors * BDRV_SECTOR_SIZE, + }; + qemu_iovec_init_external(&qiov, &iov, 1); + + co->ret = qcow_co_writev(co->bs, co->sector_num, co->nb_sectors, &qiov); +} + +/* Wrapper for non-coroutine contexts */ +static int qcow_write(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors) +{ + Coroutine *co; + AioContext *aio_context = bdrv_get_aio_context(bs); + QcowWriteCo data = { + .bs = bs, + .sector_num = sector_num, + .buf = buf, + .nb_sectors = nb_sectors, + .ret = -EINPROGRESS, + }; + co = qemu_coroutine_create(qcow_write_co_entry); + qemu_coroutine_enter(co, &data); + while (data.ret == -EINPROGRESS) { + aio_poll(aio_context, true); + } + return data.ret; +} + /* XXX: put compressed sectors first, then all the cluster aligned tables to avoid losing bytes in alignment */ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num, @@ -970,7 +1012,7 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num, if (ret != Z_STREAM_END || out_len >= s->cluster_size) { /* could not compress: write normal cluster */ - ret = bdrv_write(bs, sector_num, buf, s->cluster_sectors); + ret = qcow_write(bs, sector_num, buf, s->cluster_sectors); if (ret < 0) { goto fail; } @@ -983,7 +1025,7 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num, } cluster_offset &= s->cluster_offset_mask; - ret = bdrv_pwrite(bs->file->bs, cluster_offset, out_buf, out_len); + ret = bdrv_pwrite(bs->file, cluster_offset, out_buf, out_len); if (ret < 0) { goto fail; } diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c index 580631c3d8..6eaefeddc4 100644 --- a/block/qcow2-cache.c +++ b/block/qcow2-cache.c @@ -210,7 +210,7 @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE); } - ret = bdrv_pwrite(bs->file->bs, c->entries[i].offset, + ret = bdrv_pwrite(bs->file, c->entries[i].offset, qcow2_cache_get_table_addr(bs, c, i), s->cluster_size); if (ret < 0) { return ret; @@ -357,7 +357,7 @@ static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, BLKDBG_EVENT(bs->file, BLKDBG_L2_LOAD); } - ret = bdrv_pread(bs->file->bs, offset, + ret = bdrv_pread(bs->file, offset, qcow2_cache_get_table_addr(bs, c, i), s->cluster_size); if (ret < 0) { diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 0fb43566fb..6b92ce9429 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -108,7 +108,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_WRITE_TABLE); for(i = 0; i < s->l1_size; i++) new_l1_table[i] = cpu_to_be64(new_l1_table[i]); - ret = bdrv_pwrite_sync(bs->file->bs, new_l1_table_offset, + ret = bdrv_pwrite_sync(bs->file, new_l1_table_offset, new_l1_table, new_l1_size2); if (ret < 0) goto fail; @@ -117,9 +117,9 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, /* set new table */ BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_ACTIVATE_TABLE); - cpu_to_be32w((uint32_t*)data, new_l1_size); + stl_be_p(data, new_l1_size); stq_be_p(data + 4, new_l1_table_offset); - ret = bdrv_pwrite_sync(bs->file->bs, offsetof(QCowHeader, l1_size), + ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, l1_size), data, sizeof(data)); if (ret < 0) { goto fail; @@ -185,7 +185,7 @@ int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index) } BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE); - ret = bdrv_pwrite_sync(bs->file->bs, + ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset + 8 * l1_start_index, buf, sizeof(buf)); if (ret < 0) { @@ -446,7 +446,7 @@ static int coroutine_fn do_perform_cow(BlockDriverState *bs, } BLKDBG_EVENT(bs->file, BLKDBG_COW_WRITE); - ret = bdrv_co_pwritev(bs->file->bs, cluster_offset + offset_in_cluster, + ret = bdrv_co_pwritev(bs->file, cluster_offset + offset_in_cluster, bytes, &qiov, 0); if (ret < 0) { goto out; @@ -1408,7 +1408,7 @@ int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) sector_offset = coffset & 511; csize = nb_csectors * 512 - sector_offset; BLKDBG_EVENT(bs->file, BLKDBG_READ_COMPRESSED); - ret = bdrv_read(bs->file->bs, coffset >> 9, s->cluster_data, + ret = bdrv_read(bs->file, coffset >> 9, s->cluster_data, nb_csectors); if (ret < 0) { return ret; @@ -1677,7 +1677,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, (void **)&l2_table); } else { /* load inactive L2 tables from disk */ - ret = bdrv_read(bs->file->bs, l2_offset / BDRV_SECTOR_SIZE, + ret = bdrv_read(bs->file, l2_offset / BDRV_SECTOR_SIZE, (void *)l2_table, s->cluster_sectors); } if (ret < 0) { @@ -1752,7 +1752,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, goto fail; } - ret = bdrv_pwrite_zeroes(bs->file->bs, offset, s->cluster_size, 0); + ret = bdrv_pwrite_zeroes(bs->file, offset, s->cluster_size, 0); if (ret < 0) { if (!preallocated) { qcow2_free_clusters(bs, offset, s->cluster_size, @@ -1784,7 +1784,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, goto fail; } - ret = bdrv_write(bs->file->bs, l2_offset / BDRV_SECTOR_SIZE, + ret = bdrv_write(bs->file, l2_offset / BDRV_SECTOR_SIZE, (void *)l2_table, s->cluster_sectors); if (ret < 0) { goto fail; @@ -1859,7 +1859,7 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs, l1_table = g_realloc(l1_table, l1_sectors * BDRV_SECTOR_SIZE); - ret = bdrv_read(bs->file->bs, + ret = bdrv_read(bs->file, s->snapshots[i].l1_table_offset / BDRV_SECTOR_SIZE, (void *)l1_table, l1_sectors); if (ret < 0) { diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 3bef410839..49b6ce6bfd 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -104,7 +104,7 @@ int qcow2_refcount_init(BlockDriverState *bs) goto fail; } BLKDBG_EVENT(bs->file, BLKDBG_REFTABLE_LOAD); - ret = bdrv_pread(bs->file->bs, s->refcount_table_offset, + ret = bdrv_pread(bs->file, s->refcount_table_offset, s->refcount_table, refcount_table_size2); if (ret < 0) { goto fail; @@ -431,7 +431,7 @@ static int alloc_refcount_block(BlockDriverState *bs, if (refcount_table_index < s->refcount_table_size) { uint64_t data64 = cpu_to_be64(new_block); BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_HOOKUP); - ret = bdrv_pwrite_sync(bs->file->bs, + ret = bdrv_pwrite_sync(bs->file, s->refcount_table_offset + refcount_table_index * sizeof(uint64_t), &data64, sizeof(data64)); if (ret < 0) { @@ -533,7 +533,7 @@ static int alloc_refcount_block(BlockDriverState *bs, /* Write refcount blocks to disk */ BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS); - ret = bdrv_pwrite_sync(bs->file->bs, meta_offset, new_blocks, + ret = bdrv_pwrite_sync(bs->file, meta_offset, new_blocks, blocks_clusters * s->cluster_size); g_free(new_blocks); new_blocks = NULL; @@ -547,7 +547,7 @@ static int alloc_refcount_block(BlockDriverState *bs, } BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE); - ret = bdrv_pwrite_sync(bs->file->bs, table_offset, new_table, + ret = bdrv_pwrite_sync(bs->file, table_offset, new_table, table_size * sizeof(uint64_t)); if (ret < 0) { goto fail_table; @@ -562,10 +562,10 @@ static int alloc_refcount_block(BlockDriverState *bs, uint64_t d64; uint32_t d32; } data; - cpu_to_be64w(&data.d64, table_offset); - cpu_to_be32w(&data.d32, table_clusters); + data.d64 = cpu_to_be64(table_offset); + data.d32 = cpu_to_be32(table_clusters); BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE); - ret = bdrv_pwrite_sync(bs->file->bs, + ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, refcount_table_offset), &data, sizeof(data)); if (ret < 0) { @@ -1070,7 +1070,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, } l1_allocated = true; - ret = bdrv_pread(bs->file->bs, l1_table_offset, l1_table, l1_size2); + ret = bdrv_pread(bs->file, l1_table_offset, l1_table, l1_size2); if (ret < 0) { goto fail; } @@ -1223,7 +1223,7 @@ fail: cpu_to_be64s(&l1_table[i]); } - ret = bdrv_pwrite_sync(bs->file->bs, l1_table_offset, + ret = bdrv_pwrite_sync(bs->file, l1_table_offset, l1_table, l1_size2); for (i = 0; i < l1_size; i++) { @@ -1382,7 +1382,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, l2_size = s->l2_size * sizeof(uint64_t); l2_table = g_malloc(l2_size); - ret = bdrv_pread(bs->file->bs, l2_offset, l2_table, l2_size); + ret = bdrv_pread(bs->file, l2_offset, l2_table, l2_size); if (ret < 0) { fprintf(stderr, "ERROR: I/O error in check_refcounts_l2\n"); res->check_errors++; @@ -1514,7 +1514,7 @@ static int check_refcounts_l1(BlockDriverState *bs, res->check_errors++; goto fail; } - ret = bdrv_pread(bs->file->bs, l1_table_offset, l1_table, l1_size2); + ret = bdrv_pread(bs->file, l1_table_offset, l1_table, l1_size2); if (ret < 0) { fprintf(stderr, "ERROR: I/O error in check_refcounts_l1\n"); res->check_errors++; @@ -1612,7 +1612,7 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, } } - ret = bdrv_pread(bs->file->bs, l2_offset, l2_table, + ret = bdrv_pread(bs->file, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)); if (ret < 0) { fprintf(stderr, "ERROR: Could not read L2 table: %s\n", @@ -1664,7 +1664,7 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, goto fail; } - ret = bdrv_pwrite(bs->file->bs, l2_offset, l2_table, + ret = bdrv_pwrite(bs->file, l2_offset, l2_table, s->cluster_size); if (ret < 0) { fprintf(stderr, "ERROR: Could not write L2 table: %s\n", @@ -2098,7 +2098,7 @@ write_refblocks: on_disk_refblock = (void *)((char *) *refcount_table + refblock_index * s->cluster_size); - ret = bdrv_write(bs->file->bs, refblock_offset / BDRV_SECTOR_SIZE, + ret = bdrv_write(bs->file, refblock_offset / BDRV_SECTOR_SIZE, on_disk_refblock, s->cluster_sectors); if (ret < 0) { fprintf(stderr, "ERROR writing refblock: %s\n", strerror(-ret)); @@ -2147,7 +2147,7 @@ write_refblocks: } assert(reftable_size < INT_MAX / sizeof(uint64_t)); - ret = bdrv_pwrite(bs->file->bs, reftable_offset, on_disk_reftable, + ret = bdrv_pwrite(bs->file, reftable_offset, on_disk_reftable, reftable_size * sizeof(uint64_t)); if (ret < 0) { fprintf(stderr, "ERROR writing reftable: %s\n", strerror(-ret)); @@ -2155,12 +2155,11 @@ write_refblocks: } /* Enter new reftable into the image header */ - cpu_to_be64w(&reftable_offset_and_clusters.reftable_offset, - reftable_offset); - cpu_to_be32w(&reftable_offset_and_clusters.reftable_clusters, - size_to_clusters(s, reftable_size * sizeof(uint64_t))); - ret = bdrv_pwrite_sync(bs->file->bs, offsetof(QCowHeader, - refcount_table_offset), + reftable_offset_and_clusters.reftable_offset = cpu_to_be64(reftable_offset); + reftable_offset_and_clusters.reftable_clusters = + cpu_to_be32(size_to_clusters(s, reftable_size * sizeof(uint64_t))); + ret = bdrv_pwrite_sync(bs->file, + offsetof(QCowHeader, refcount_table_offset), &reftable_offset_and_clusters, sizeof(reftable_offset_and_clusters)); if (ret < 0) { @@ -2407,7 +2406,7 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, return -ENOMEM; } - ret = bdrv_pread(bs->file->bs, l1_ofs, l1, l1_sz2); + ret = bdrv_pread(bs->file, l1_ofs, l1, l1_sz2); if (ret < 0) { g_free(l1); return ret; @@ -2560,7 +2559,7 @@ static int flush_refblock(BlockDriverState *bs, uint64_t **reftable, return ret; } - ret = bdrv_pwrite(bs->file->bs, offset, refblock, s->cluster_size); + ret = bdrv_pwrite(bs->file, offset, refblock, s->cluster_size); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to write refblock"); return ret; @@ -2830,7 +2829,7 @@ int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order, cpu_to_be64s(&new_reftable[i]); } - ret = bdrv_pwrite(bs->file->bs, new_reftable_offset, new_reftable, + ret = bdrv_pwrite(bs->file, new_reftable_offset, new_reftable, new_reftable_size * sizeof(uint64_t)); for (i = 0; i < new_reftable_size; i++) { diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c index 242fb21d6e..032424322a 100644 --- a/block/qcow2-snapshot.c +++ b/block/qcow2-snapshot.c @@ -67,7 +67,7 @@ int qcow2_read_snapshots(BlockDriverState *bs) for(i = 0; i < s->nb_snapshots; i++) { /* Read statically sized part of the snapshot header */ offset = align_offset(offset, 8); - ret = bdrv_pread(bs->file->bs, offset, &h, sizeof(h)); + ret = bdrv_pread(bs->file, offset, &h, sizeof(h)); if (ret < 0) { goto fail; } @@ -86,7 +86,7 @@ int qcow2_read_snapshots(BlockDriverState *bs) name_size = be16_to_cpu(h.name_size); /* Read extra data */ - ret = bdrv_pread(bs->file->bs, offset, &extra, + ret = bdrv_pread(bs->file, offset, &extra, MIN(sizeof(extra), extra_data_size)); if (ret < 0) { goto fail; @@ -105,7 +105,7 @@ int qcow2_read_snapshots(BlockDriverState *bs) /* Read snapshot ID */ sn->id_str = g_malloc(id_str_size + 1); - ret = bdrv_pread(bs->file->bs, offset, sn->id_str, id_str_size); + ret = bdrv_pread(bs->file, offset, sn->id_str, id_str_size); if (ret < 0) { goto fail; } @@ -114,7 +114,7 @@ int qcow2_read_snapshots(BlockDriverState *bs) /* Read snapshot name */ sn->name = g_malloc(name_size + 1); - ret = bdrv_pread(bs->file->bs, offset, sn->name, name_size); + ret = bdrv_pread(bs->file, offset, sn->name, name_size); if (ret < 0) { goto fail; } @@ -217,25 +217,25 @@ static int qcow2_write_snapshots(BlockDriverState *bs) h.name_size = cpu_to_be16(name_size); offset = align_offset(offset, 8); - ret = bdrv_pwrite(bs->file->bs, offset, &h, sizeof(h)); + ret = bdrv_pwrite(bs->file, offset, &h, sizeof(h)); if (ret < 0) { goto fail; } offset += sizeof(h); - ret = bdrv_pwrite(bs->file->bs, offset, &extra, sizeof(extra)); + ret = bdrv_pwrite(bs->file, offset, &extra, sizeof(extra)); if (ret < 0) { goto fail; } offset += sizeof(extra); - ret = bdrv_pwrite(bs->file->bs, offset, sn->id_str, id_str_size); + ret = bdrv_pwrite(bs->file, offset, sn->id_str, id_str_size); if (ret < 0) { goto fail; } offset += id_str_size; - ret = bdrv_pwrite(bs->file->bs, offset, sn->name, name_size); + ret = bdrv_pwrite(bs->file, offset, sn->name, name_size); if (ret < 0) { goto fail; } @@ -257,7 +257,7 @@ static int qcow2_write_snapshots(BlockDriverState *bs) header_data.nb_snapshots = cpu_to_be32(s->nb_snapshots); header_data.snapshots_offset = cpu_to_be64(snapshots_offset); - ret = bdrv_pwrite_sync(bs->file->bs, offsetof(QCowHeader, nb_snapshots), + ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, nb_snapshots), &header_data, sizeof(header_data)); if (ret < 0) { goto fail; @@ -399,7 +399,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) goto fail; } - ret = bdrv_pwrite(bs->file->bs, sn->l1_table_offset, l1_table, + ret = bdrv_pwrite(bs->file, sn->l1_table_offset, l1_table, s->l1_size * sizeof(uint64_t)); if (ret < 0) { goto fail; @@ -512,7 +512,7 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) goto fail; } - ret = bdrv_pread(bs->file->bs, sn->l1_table_offset, + ret = bdrv_pread(bs->file, sn->l1_table_offset, sn_l1_table, sn_l1_bytes); if (ret < 0) { goto fail; @@ -530,7 +530,7 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) goto fail; } - ret = bdrv_pwrite_sync(bs->file->bs, s->l1_table_offset, sn_l1_table, + ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset, sn_l1_table, cur_l1_bytes); if (ret < 0) { goto fail; @@ -716,7 +716,7 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs, return -ENOMEM; } - ret = bdrv_pread(bs->file->bs, sn->l1_table_offset, + ret = bdrv_pread(bs->file, sn->l1_table_offset, new_l1_table, new_l1_bytes); if (ret < 0) { error_setg(errp, "Failed to read l1 table for snapshot"); diff --git a/block/qcow2.c b/block/qcow2.c index 23f666d4ae..a5ea19b0b6 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -107,7 +107,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, printf("attempting to read extended header in offset %lu\n", offset); #endif - ret = bdrv_pread(bs->file->bs, offset, &ext, sizeof(ext)); + ret = bdrv_pread(bs->file, offset, &ext, sizeof(ext)); if (ret < 0) { error_setg_errno(errp, -ret, "qcow2_read_extension: ERROR: " "pread fail from offset %" PRIu64, offset); @@ -135,7 +135,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, sizeof(bs->backing_format)); return 2; } - ret = bdrv_pread(bs->file->bs, offset, bs->backing_format, ext.len); + ret = bdrv_pread(bs->file, offset, bs->backing_format, ext.len); if (ret < 0) { error_setg_errno(errp, -ret, "ERROR: ext_backing_format: " "Could not read format name"); @@ -151,7 +151,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, case QCOW2_EXT_MAGIC_FEATURE_TABLE: if (p_feature_table != NULL) { void* feature_table = g_malloc0(ext.len + 2 * sizeof(Qcow2Feature)); - ret = bdrv_pread(bs->file->bs, offset , feature_table, ext.len); + ret = bdrv_pread(bs->file, offset , feature_table, ext.len); if (ret < 0) { error_setg_errno(errp, -ret, "ERROR: ext_feature_table: " "Could not read table"); @@ -172,7 +172,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, uext->len = ext.len; QLIST_INSERT_HEAD(&s->unknown_header_ext, uext, next); - ret = bdrv_pread(bs->file->bs, offset , uext->data, uext->len); + ret = bdrv_pread(bs->file, offset , uext->data, uext->len); if (ret < 0) { error_setg_errno(errp, -ret, "ERROR: unknown extension: " "Could not read data"); @@ -249,7 +249,7 @@ int qcow2_mark_dirty(BlockDriverState *bs) } val = cpu_to_be64(s->incompatible_features | QCOW2_INCOMPAT_DIRTY); - ret = bdrv_pwrite(bs->file->bs, offsetof(QCowHeader, incompatible_features), + ret = bdrv_pwrite(bs->file, offsetof(QCowHeader, incompatible_features), &val, sizeof(val)); if (ret < 0) { return ret; @@ -817,7 +817,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, uint64_t ext_end; uint64_t l1_vm_state_index; - ret = bdrv_pread(bs->file->bs, 0, &header, sizeof(header)); + ret = bdrv_pread(bs->file, 0, &header, sizeof(header)); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read qcow2 header"); goto fail; @@ -892,7 +892,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, if (header.header_length > sizeof(header)) { s->unknown_header_fields_size = header.header_length - sizeof(header); s->unknown_header_fields = g_malloc(s->unknown_header_fields_size); - ret = bdrv_pread(bs->file->bs, sizeof(header), s->unknown_header_fields, + ret = bdrv_pread(bs->file, sizeof(header), s->unknown_header_fields, s->unknown_header_fields_size); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read unknown qcow2 header " @@ -980,10 +980,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - bs->encrypted = 1; - - /* Encryption works on a sector granularity */ - bs->request_alignment = BDRV_SECTOR_SIZE; + bs->encrypted = true; } s->l2_bits = s->cluster_bits - 3; /* L2 is always one cluster */ @@ -1069,7 +1066,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, ret = -ENOMEM; goto fail; } - ret = bdrv_pread(bs->file->bs, s->l1_table_offset, s->l1_table, + ret = bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, s->l1_size * sizeof(uint64_t)); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read L1 table"); @@ -1125,7 +1122,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, ret = -EINVAL; goto fail; } - ret = bdrv_pread(bs->file->bs, header.backing_file_offset, + ret = bdrv_pread(bs->file, header.backing_file_offset, bs->backing_file, len); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read backing file name"); @@ -1202,6 +1199,10 @@ static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp) { BDRVQcow2State *s = bs->opaque; + if (bs->encrypted) { + /* Encryption works on a sector granularity */ + bs->bl.request_alignment = BDRV_SECTOR_SIZE; + } bs->bl.pwrite_zeroes_alignment = s->cluster_size; } @@ -1442,7 +1443,7 @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset, BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO); qemu_co_mutex_unlock(&s->lock); - ret = bdrv_co_preadv(bs->backing->bs, offset, n1, + ret = bdrv_co_preadv(bs->backing, offset, n1, &local_qiov, 0); qemu_co_mutex_lock(&s->lock); @@ -1505,7 +1506,7 @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset, BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); qemu_co_mutex_unlock(&s->lock); - ret = bdrv_co_preadv(bs->file->bs, + ret = bdrv_co_preadv(bs->file, cluster_offset + offset_in_cluster, cur_bytes, &hd_qiov, 0); qemu_co_mutex_lock(&s->lock); @@ -1636,7 +1637,7 @@ static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset, BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); trace_qcow2_writev_data(qemu_coroutine_self(), cluster_offset + offset_in_cluster); - ret = bdrv_co_pwritev(bs->file->bs, + ret = bdrv_co_pwritev(bs->file, cluster_offset + offset_in_cluster, cur_bytes, &hd_qiov, 0); qemu_co_mutex_lock(&s->lock); @@ -1975,7 +1976,7 @@ int qcow2_update_header(BlockDriverState *bs) } /* Write the new header */ - ret = bdrv_pwrite(bs->file->bs, 0, header, s->cluster_size); + ret = bdrv_pwrite(bs->file, 0, header, s->cluster_size); if (ret < 0) { goto fail; } @@ -2058,7 +2059,7 @@ static int preallocate(BlockDriverState *bs) */ if (host_offset != 0) { uint8_t data = 0; - ret = bdrv_pwrite(bs->file->bs, (host_offset + cur_bytes) - 1, + ret = bdrv_pwrite(bs->file, (host_offset + cur_bytes) - 1, &data, 1); if (ret < 0) { return ret; @@ -2522,7 +2523,7 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset) /* write updated header.size */ offset = cpu_to_be64(offset); - ret = bdrv_pwrite_sync(bs->file->bs, offsetof(QCowHeader, size), + ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, size), &offset, sizeof(uint64_t)); if (ret < 0) { return ret; @@ -2532,6 +2533,51 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset) return 0; } +typedef struct Qcow2WriteCo { + BlockDriverState *bs; + int64_t sector_num; + const uint8_t *buf; + int nb_sectors; + int ret; +} Qcow2WriteCo; + +static void qcow2_write_co_entry(void *opaque) +{ + Qcow2WriteCo *co = opaque; + QEMUIOVector qiov; + uint64_t offset = co->sector_num * BDRV_SECTOR_SIZE; + uint64_t bytes = co->nb_sectors * BDRV_SECTOR_SIZE; + + struct iovec iov = (struct iovec) { + .iov_base = (uint8_t*) co->buf, + .iov_len = bytes, + }; + qemu_iovec_init_external(&qiov, &iov, 1); + + co->ret = qcow2_co_pwritev(co->bs, offset, bytes, &qiov, 0); +} + +/* Wrapper for non-coroutine contexts */ +static int qcow2_write(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors) +{ + Coroutine *co; + AioContext *aio_context = bdrv_get_aio_context(bs); + Qcow2WriteCo data = { + .bs = bs, + .sector_num = sector_num, + .buf = buf, + .nb_sectors = nb_sectors, + .ret = -EINPROGRESS, + }; + co = qemu_coroutine_create(qcow2_write_co_entry); + qemu_coroutine_enter(co, &data); + while (data.ret == -EINPROGRESS) { + aio_poll(aio_context, true); + } + return data.ret; +} + /* XXX: put compressed sectors first, then all the cluster aligned tables to avoid losing bytes in alignment */ static int qcow2_write_compressed(BlockDriverState *bs, int64_t sector_num, @@ -2595,7 +2641,7 @@ static int qcow2_write_compressed(BlockDriverState *bs, int64_t sector_num, if (ret != Z_STREAM_END || out_len >= s->cluster_size) { /* could not compress: write normal cluster */ - ret = bdrv_write(bs, sector_num, buf, s->cluster_sectors); + ret = qcow2_write(bs, sector_num, buf, s->cluster_sectors); if (ret < 0) { goto fail; } @@ -2614,7 +2660,7 @@ static int qcow2_write_compressed(BlockDriverState *bs, int64_t sector_num, } BLKDBG_EVENT(bs->file, BLKDBG_WRITE_COMPRESSED); - ret = bdrv_pwrite(bs->file->bs, cluster_offset, out_buf, out_len); + ret = bdrv_pwrite(bs->file, cluster_offset, out_buf, out_len); if (ret < 0) { goto fail; } @@ -2663,7 +2709,7 @@ static int make_completely_empty(BlockDriverState *bs) /* After this call, neither the in-memory nor the on-disk refcount * information accurately describe the actual references */ - ret = bdrv_pwrite_zeroes(bs->file->bs, s->l1_table_offset, + ret = bdrv_pwrite_zeroes(bs->file, s->l1_table_offset, l1_clusters * s->cluster_size, 0); if (ret < 0) { goto fail_broken_refcounts; @@ -2677,7 +2723,7 @@ static int make_completely_empty(BlockDriverState *bs) * overwrite parts of the existing refcount and L1 table, which is not * an issue because the dirty flag is set, complete data loss is in fact * desired and partial data loss is consequently fine as well */ - ret = bdrv_pwrite_zeroes(bs->file->bs, s->cluster_size, + ret = bdrv_pwrite_zeroes(bs->file, s->cluster_size, (2 + l1_clusters) * s->cluster_size, 0); /* This call (even if it failed overall) may have overwritten on-disk * refcount structures; in that case, the in-memory refcount information @@ -2693,10 +2739,10 @@ static int make_completely_empty(BlockDriverState *bs) /* "Create" an empty reftable (one cluster) directly after the image * header and an empty L1 table three clusters after the image header; * the cluster between those two will be used as the first refblock */ - cpu_to_be64w(&l1_ofs_rt_ofs_cls.l1_offset, 3 * s->cluster_size); - cpu_to_be64w(&l1_ofs_rt_ofs_cls.reftable_offset, s->cluster_size); - cpu_to_be32w(&l1_ofs_rt_ofs_cls.reftable_clusters, 1); - ret = bdrv_pwrite_sync(bs->file->bs, offsetof(QCowHeader, l1_table_offset), + l1_ofs_rt_ofs_cls.l1_offset = cpu_to_be64(3 * s->cluster_size); + l1_ofs_rt_ofs_cls.reftable_offset = cpu_to_be64(s->cluster_size); + l1_ofs_rt_ofs_cls.reftable_clusters = cpu_to_be32(1); + ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, l1_table_offset), &l1_ofs_rt_ofs_cls, sizeof(l1_ofs_rt_ofs_cls)); if (ret < 0) { goto fail_broken_refcounts; @@ -2727,7 +2773,7 @@ static int make_completely_empty(BlockDriverState *bs) /* Enter the first refblock into the reftable */ rt_entry = cpu_to_be64(2 * s->cluster_size); - ret = bdrv_pwrite_sync(bs->file->bs, s->cluster_size, + ret = bdrv_pwrite_sync(bs->file, s->cluster_size, &rt_entry, sizeof(rt_entry)); if (ret < 0) { goto fail_broken_refcounts; diff --git a/block/qed-table.c b/block/qed-table.c index c841ad10fe..1a731dff51 100644 --- a/block/qed-table.c +++ b/block/qed-table.c @@ -65,7 +65,7 @@ static void qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table, read_table_cb->iov.iov_len = s->header.cluster_size * s->header.table_size, qemu_iovec_init_external(qiov, &read_table_cb->iov, 1); - bdrv_aio_readv(s->bs->file->bs, offset / BDRV_SECTOR_SIZE, qiov, + bdrv_aio_readv(s->bs->file, offset / BDRV_SECTOR_SIZE, qiov, qiov->size / BDRV_SECTOR_SIZE, qed_read_table_cb, read_table_cb); } @@ -154,7 +154,7 @@ static void qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table, /* Adjust for offset into table */ offset += start * sizeof(uint64_t); - bdrv_aio_writev(s->bs->file->bs, offset / BDRV_SECTOR_SIZE, + bdrv_aio_writev(s->bs->file, offset / BDRV_SECTOR_SIZE, &write_table_cb->qiov, write_table_cb->qiov.size / BDRV_SECTOR_SIZE, qed_write_table_cb, write_table_cb); diff --git a/block/qed.c b/block/qed.c index 12068061ac..f619d82c6e 100644 --- a/block/qed.c +++ b/block/qed.c @@ -86,7 +86,7 @@ int qed_write_header_sync(BDRVQEDState *s) int ret; qed_header_cpu_to_le(&s->header, &le); - ret = bdrv_pwrite(s->bs->file->bs, 0, &le, sizeof(le)); + ret = bdrv_pwrite(s->bs->file, 0, &le, sizeof(le)); if (ret != sizeof(le)) { return ret; } @@ -123,7 +123,7 @@ static void qed_write_header_read_cb(void *opaque, int ret) /* Update header */ qed_header_cpu_to_le(&s->header, (QEDHeader *)write_header_cb->buf); - bdrv_aio_writev(s->bs->file->bs, 0, &write_header_cb->qiov, + bdrv_aio_writev(s->bs->file, 0, &write_header_cb->qiov, write_header_cb->nsectors, qed_write_header_cb, write_header_cb); } @@ -155,7 +155,7 @@ static void qed_write_header(BDRVQEDState *s, BlockCompletionFunc cb, write_header_cb->iov.iov_len = len; qemu_iovec_init_external(&write_header_cb->qiov, &write_header_cb->iov, 1); - bdrv_aio_readv(s->bs->file->bs, 0, &write_header_cb->qiov, nsectors, + bdrv_aio_readv(s->bs->file, 0, &write_header_cb->qiov, nsectors, qed_write_header_read_cb, write_header_cb); } @@ -218,7 +218,7 @@ static bool qed_is_image_size_valid(uint64_t image_size, uint32_t cluster_size, * * The string is NUL-terminated. */ -static int qed_read_string(BlockDriverState *file, uint64_t offset, size_t n, +static int qed_read_string(BdrvChild *file, uint64_t offset, size_t n, char *buf, size_t buflen) { int ret; @@ -389,7 +389,7 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, s->bs = bs; QSIMPLEQ_INIT(&s->allocating_write_reqs); - ret = bdrv_pread(bs->file->bs, 0, &le_header, sizeof(le_header)); + ret = bdrv_pread(bs->file, 0, &le_header, sizeof(le_header)); if (ret < 0) { return ret; } @@ -446,7 +446,7 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, return -EINVAL; } - ret = qed_read_string(bs->file->bs, s->header.backing_filename_offset, + ret = qed_read_string(bs->file, s->header.backing_filename_offset, s->header.backing_filename_size, bs->backing_file, sizeof(bs->backing_file)); if (ret < 0) { @@ -800,7 +800,7 @@ static void qed_read_backing_file(BDRVQEDState *s, uint64_t pos, qemu_iovec_concat(*backing_qiov, qiov, 0, size); BLKDBG_EVENT(s->bs->file, BLKDBG_READ_BACKING_AIO); - bdrv_aio_readv(s->bs->backing->bs, pos / BDRV_SECTOR_SIZE, + bdrv_aio_readv(s->bs->backing, pos / BDRV_SECTOR_SIZE, *backing_qiov, size / BDRV_SECTOR_SIZE, cb, opaque); } @@ -837,7 +837,7 @@ static void qed_copy_from_backing_file_write(void *opaque, int ret) } BLKDBG_EVENT(s->bs->file, BLKDBG_COW_WRITE); - bdrv_aio_writev(s->bs->file->bs, copy_cb->offset / BDRV_SECTOR_SIZE, + bdrv_aio_writev(s->bs->file, copy_cb->offset / BDRV_SECTOR_SIZE, ©_cb->qiov, copy_cb->qiov.size / BDRV_SECTOR_SIZE, qed_copy_from_backing_file_cb, copy_cb); } @@ -1087,7 +1087,7 @@ static void qed_aio_write_main(void *opaque, int ret) } BLKDBG_EVENT(s->bs->file, BLKDBG_WRITE_AIO); - bdrv_aio_writev(s->bs->file->bs, offset / BDRV_SECTOR_SIZE, + bdrv_aio_writev(s->bs->file, offset / BDRV_SECTOR_SIZE, &acb->cur_qiov, acb->cur_qiov.size / BDRV_SECTOR_SIZE, next_fn, acb); } @@ -1319,7 +1319,7 @@ static void qed_aio_read_data(void *opaque, int ret, } BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); - bdrv_aio_readv(bs->file->bs, offset / BDRV_SECTOR_SIZE, + bdrv_aio_readv(bs->file, offset / BDRV_SECTOR_SIZE, &acb->cur_qiov, acb->cur_qiov.size / BDRV_SECTOR_SIZE, qed_aio_next_io, acb); return; @@ -1575,7 +1575,7 @@ static int bdrv_qed_change_backing_file(BlockDriverState *bs, } /* Write new header */ - ret = bdrv_pwrite_sync(bs->file->bs, 0, buffer, buffer_len); + ret = bdrv_pwrite_sync(bs->file, 0, buffer, buffer_len); g_free(buffer); if (ret == 0) { memcpy(&s->header, &new_header, sizeof(new_header)); diff --git a/block/quorum.c b/block/quorum.c index 331b726f97..9cf876fb34 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -383,7 +383,7 @@ static bool quorum_rewrite_bad_versions(BDRVQuorumState *s, QuorumAIOCB *acb, continue; } QLIST_FOREACH(item, &version->items, next) { - bdrv_aio_writev(s->children[item->index]->bs, acb->sector_num, + bdrv_aio_writev(s->children[item->index], acb->sector_num, acb->qiov, acb->nb_sectors, quorum_rewrite_aio_cb, acb); } @@ -660,7 +660,7 @@ static BlockAIOCB *read_quorum_children(QuorumAIOCB *acb) } for (i = 0; i < s->num_children; i++) { - acb->qcrs[i].aiocb = bdrv_aio_readv(s->children[i]->bs, acb->sector_num, + acb->qcrs[i].aiocb = bdrv_aio_readv(s->children[i], acb->sector_num, &acb->qcrs[i].qiov, acb->nb_sectors, quorum_aio_cb, &acb->qcrs[i]); } @@ -678,7 +678,7 @@ static BlockAIOCB *read_fifo_child(QuorumAIOCB *acb) qemu_iovec_clone(&acb->qcrs[acb->child_iter].qiov, acb->qiov, acb->qcrs[acb->child_iter].buf); acb->qcrs[acb->child_iter].aiocb = - bdrv_aio_readv(s->children[acb->child_iter]->bs, acb->sector_num, + bdrv_aio_readv(s->children[acb->child_iter], acb->sector_num, &acb->qcrs[acb->child_iter].qiov, acb->nb_sectors, quorum_aio_cb, &acb->qcrs[acb->child_iter]); @@ -719,7 +719,7 @@ static BlockAIOCB *quorum_aio_writev(BlockDriverState *bs, int i; for (i = 0; i < s->num_children; i++) { - acb->qcrs[i].aiocb = bdrv_aio_writev(s->children[i]->bs, sector_num, + acb->qcrs[i].aiocb = bdrv_aio_writev(s->children[i], sector_num, qiov, nb_sectors, &quorum_aio_cb, &acb->qcrs[i]); } diff --git a/block/raw-posix.c b/block/raw-posix.c index bef7a671a6..c979ac3fd1 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -302,22 +302,22 @@ static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp) /* For SCSI generic devices the alignment is not really used. With buffered I/O, we don't have any restrictions. */ if (bdrv_is_sg(bs) || !s->needs_alignment) { - bs->request_alignment = 1; + bs->bl.request_alignment = 1; s->buf_align = 1; return; } - bs->request_alignment = 0; + bs->bl.request_alignment = 0; s->buf_align = 0; /* Let's try to use the logical blocksize for the alignment. */ - if (probe_logical_blocksize(fd, &bs->request_alignment) < 0) { - bs->request_alignment = 0; + if (probe_logical_blocksize(fd, &bs->bl.request_alignment) < 0) { + bs->bl.request_alignment = 0; } #ifdef CONFIG_XFS if (s->is_xfs) { struct dioattr da; if (xfsctl(NULL, fd, XFS_IOC_DIOINFO, &da) >= 0) { - bs->request_alignment = da.d_miniosz; + bs->bl.request_alignment = da.d_miniosz; /* The kernel returns wrong information for d_mem */ /* s->buf_align = da.d_mem; */ } @@ -337,21 +337,21 @@ static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp) qemu_vfree(buf); } - if (!bs->request_alignment) { + if (!bs->bl.request_alignment) { size_t align; buf = qemu_memalign(s->buf_align, max_align); for (align = 512; align <= max_align; align <<= 1) { if (raw_is_io_aligned(fd, buf, align)) { - bs->request_alignment = align; + bs->bl.request_alignment = align; break; } } qemu_vfree(buf); } - if (!s->buf_align || !bs->request_alignment) { - error_setg(errp, "Could not find working O_DIRECT alignment. " - "Try cache.direct=off."); + if (!s->buf_align || !bs->bl.request_alignment) { + error_setg(errp, "Could not find working O_DIRECT alignment"); + error_append_hint(errp, "Try cache.direct=off\n"); } } @@ -745,8 +745,8 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp) if (!fstat(s->fd, &st)) { if (S_ISBLK(st.st_mode)) { int ret = hdev_get_max_transfer_length(s->fd); - if (ret >= 0) { - bs->bl.max_transfer_length = ret; + if (ret > 0 && ret <= BDRV_REQUEST_MAX_SECTORS) { + bs->bl.max_transfer = pow2floor(ret << BDRV_SECTOR_BITS); } } } diff --git a/block/raw-win32.c b/block/raw-win32.c index fd23891534..62edb1a6cc 100644 --- a/block/raw-win32.c +++ b/block/raw-win32.c @@ -222,7 +222,7 @@ static void raw_attach_aio_context(BlockDriverState *bs, } } -static void raw_probe_alignment(BlockDriverState *bs) +static void raw_probe_alignment(BlockDriverState *bs, Error **errp) { BDRVRawState *s = bs->opaque; DWORD sectorsPerCluster, freeClusters, totalClusters, count; @@ -230,14 +230,14 @@ static void raw_probe_alignment(BlockDriverState *bs) BOOL status; if (s->type == FTYPE_CD) { - bs->request_alignment = 2048; + bs->bl.request_alignment = 2048; return; } if (s->type == FTYPE_HARDDISK) { status = DeviceIoControl(s->hfile, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0, &dg, sizeof(dg), &count, NULL); if (status != 0) { - bs->request_alignment = dg.Geometry.BytesPerSector; + bs->bl.request_alignment = dg.Geometry.BytesPerSector; return; } /* try GetDiskFreeSpace too */ @@ -247,7 +247,7 @@ static void raw_probe_alignment(BlockDriverState *bs) GetDiskFreeSpace(s->drive_path, §orsPerCluster, &dg.Geometry.BytesPerSector, &freeClusters, &totalClusters); - bs->request_alignment = dg.Geometry.BytesPerSector; + bs->bl.request_alignment = dg.Geometry.BytesPerSector; } } @@ -365,7 +365,6 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, win32_aio_attach_aio_context(s->aio, bdrv_get_aio_context(bs)); } - raw_probe_alignment(bs); ret = 0; fail: qemu_opts_del(opts); @@ -550,6 +549,7 @@ BlockDriver bdrv_file = { .bdrv_needs_filename = true, .bdrv_parse_filename = raw_parse_filename, .bdrv_file_open = raw_open, + .bdrv_refresh_limits = raw_probe_alignment, .bdrv_close = raw_close, .bdrv_create = raw_create, .bdrv_has_zero_init = bdrv_has_zero_init_1, diff --git a/block/raw_bsd.c b/block/raw_bsd.c index 7f637914b7..5f9dd299a6 100644 --- a/block/raw_bsd.c +++ b/block/raw_bsd.c @@ -1,6 +1,6 @@ /* BlockDriver implementation for "raw" * - * Copyright (C) 2010, 2013, Red Hat, Inc. + * Copyright (C) 2010-2016 Red Hat, Inc. * Copyright (C) 2010, Blue Swirl <blauwirbel@gmail.com> * Copyright (C) 2009, Anthony Liguori <aliguori@us.ibm.com> * @@ -54,7 +54,7 @@ static int coroutine_fn raw_co_readv(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) { BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); - return bdrv_co_readv(bs->file->bs, sector_num, nb_sectors, qiov); + return bdrv_co_readv(bs->file, sector_num, nb_sectors, qiov); } static int coroutine_fn @@ -105,7 +105,7 @@ raw_co_writev_flags(BlockDriverState *bs, int64_t sector_num, int nb_sectors, } BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); - ret = bdrv_co_pwritev(bs->file->bs, sector_num * BDRV_SECTOR_SIZE, + ret = bdrv_co_pwritev(bs->file, sector_num * BDRV_SECTOR_SIZE, nb_sectors * BDRV_SECTOR_SIZE, qiov, flags); fail: @@ -131,7 +131,7 @@ static int coroutine_fn raw_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int count, BdrvRequestFlags flags) { - return bdrv_co_pwrite_zeroes(bs->file->bs, offset, count, flags); + return bdrv_co_pwrite_zeroes(bs->file, offset, count, flags); } static int coroutine_fn raw_co_discard(BlockDriverState *bs, @@ -150,11 +150,6 @@ static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) return bdrv_get_info(bs->file->bs, bdi); } -static void raw_refresh_limits(BlockDriverState *bs, Error **errp) -{ - bs->bl = bs->file->bs->bl; -} - static int raw_truncate(BlockDriverState *bs, int64_t offset) { return bdrv_truncate(bs->file->bs, offset); @@ -252,7 +247,6 @@ BlockDriver bdrv_raw = { .bdrv_getlength = &raw_getlength, .has_variable_length = true, .bdrv_get_info = &raw_get_info, - .bdrv_refresh_limits = &raw_refresh_limits, .bdrv_probe_blocksizes = &raw_probe_blocksizes, .bdrv_probe_geometry = &raw_probe_geometry, .bdrv_media_changed = &raw_media_changed, diff --git a/block/vdi.c b/block/vdi.c index 7d9ab9cc17..8a1cf97928 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -403,7 +403,7 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, logout("\n"); - ret = bdrv_read(bs->file->bs, 0, (uint8_t *)&header, 1); + ret = bdrv_read(bs->file, 0, (uint8_t *)&header, 1); if (ret < 0) { goto fail; } @@ -500,7 +500,7 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - ret = bdrv_read(bs->file->bs, s->bmap_sector, (uint8_t *)s->bmap, + ret = bdrv_read(bs->file, s->bmap_sector, (uint8_t *)s->bmap, bmap_size); if (ret < 0) { goto fail_free_bmap; @@ -597,7 +597,7 @@ vdi_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, qemu_iovec_reset(&local_qiov); qemu_iovec_concat(&local_qiov, qiov, bytes_done, n_bytes); - ret = bdrv_co_preadv(bs->file->bs, data_offset, n_bytes, + ret = bdrv_co_preadv(bs->file, data_offset, n_bytes, &local_qiov, 0); } logout("%u bytes read\n", n_bytes); @@ -670,7 +670,7 @@ vdi_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, * acquire the lock and thus the padded cluster is written before * the other coroutines can write to the affected area. */ qemu_co_mutex_lock(&s->write_lock); - ret = bdrv_pwrite(bs->file->bs, data_offset, block, s->block_size); + ret = bdrv_pwrite(bs->file, data_offset, block, s->block_size); qemu_co_mutex_unlock(&s->write_lock); } else { uint64_t data_offset = s->header.offset_data + @@ -690,7 +690,7 @@ vdi_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, qemu_iovec_reset(&local_qiov); qemu_iovec_concat(&local_qiov, qiov, bytes_done, n_bytes); - ret = bdrv_co_pwritev(bs->file->bs, data_offset, n_bytes, + ret = bdrv_co_pwritev(bs->file, data_offset, n_bytes, &local_qiov, 0); } @@ -719,7 +719,7 @@ vdi_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, assert(VDI_IS_ALLOCATED(bmap_first)); *header = s->header; vdi_header_to_le(header); - ret = bdrv_write(bs->file->bs, 0, block, 1); + ret = bdrv_write(bs->file, 0, block, 1); g_free(block); block = NULL; @@ -737,7 +737,7 @@ vdi_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, base = ((uint8_t *)&s->bmap[0]) + bmap_first * SECTOR_SIZE; logout("will write %u block map sectors starting from entry %u\n", n_sectors, bmap_first); - ret = bdrv_write(bs->file->bs, offset, base, n_sectors); + ret = bdrv_write(bs->file, offset, base, n_sectors); } return ret; diff --git a/block/vhdx-log.c b/block/vhdx-log.c index 8ab7d22d9a..02eb104310 100644 --- a/block/vhdx-log.c +++ b/block/vhdx-log.c @@ -84,7 +84,7 @@ static int vhdx_log_peek_hdr(BlockDriverState *bs, VHDXLogEntries *log, offset = log->offset + read; - ret = bdrv_pread(bs->file->bs, offset, hdr, sizeof(VHDXLogEntryHeader)); + ret = bdrv_pread(bs->file, offset, hdr, sizeof(VHDXLogEntryHeader)); if (ret < 0) { goto exit; } @@ -144,7 +144,7 @@ static int vhdx_log_read_sectors(BlockDriverState *bs, VHDXLogEntries *log, } offset = log->offset + read; - ret = bdrv_pread(bs->file->bs, offset, buffer, VHDX_LOG_SECTOR_SIZE); + ret = bdrv_pread(bs->file, offset, buffer, VHDX_LOG_SECTOR_SIZE); if (ret < 0) { goto exit; } @@ -194,7 +194,7 @@ static int vhdx_log_write_sectors(BlockDriverState *bs, VHDXLogEntries *log, /* full */ break; } - ret = bdrv_pwrite(bs->file->bs, offset, buffer_tmp, + ret = bdrv_pwrite(bs->file, offset, buffer_tmp, VHDX_LOG_SECTOR_SIZE); if (ret < 0) { goto exit; @@ -466,7 +466,7 @@ static int vhdx_log_flush_desc(BlockDriverState *bs, VHDXLogDescriptor *desc, /* count is only > 1 if we are writing zeroes */ for (i = 0; i < count; i++) { - ret = bdrv_pwrite_sync(bs->file->bs, file_offset, buffer, + ret = bdrv_pwrite_sync(bs->file, file_offset, buffer, VHDX_LOG_SECTOR_SIZE); if (ret < 0) { goto exit; @@ -945,7 +945,7 @@ static int vhdx_log_write(BlockDriverState *bs, BDRVVHDXState *s, if (i == 0 && leading_length) { /* partial sector at the front of the buffer */ - ret = bdrv_pread(bs->file->bs, file_offset, merged_sector, + ret = bdrv_pread(bs->file, file_offset, merged_sector, VHDX_LOG_SECTOR_SIZE); if (ret < 0) { goto exit; @@ -955,7 +955,7 @@ static int vhdx_log_write(BlockDriverState *bs, BDRVVHDXState *s, sector_write = merged_sector; } else if (i == sectors - 1 && trailing_length) { /* partial sector at the end of the buffer */ - ret = bdrv_pread(bs->file->bs, + ret = bdrv_pread(bs->file, file_offset, merged_sector + trailing_length, VHDX_LOG_SECTOR_SIZE - trailing_length); diff --git a/block/vhdx.c b/block/vhdx.c index f5605a2ff4..75ef2b1c2d 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -298,9 +298,10 @@ static int vhdx_probe(const uint8_t *buf, int buf_size, const char *filename) * and then update the header checksum. Header is converted to proper * endianness before being written to the specified file offset */ -static int vhdx_write_header(BlockDriverState *bs_file, VHDXHeader *hdr, +static int vhdx_write_header(BdrvChild *file, VHDXHeader *hdr, uint64_t offset, bool read) { + BlockDriverState *bs_file = file->bs; uint8_t *buffer = NULL; int ret; VHDXHeader *header_le; @@ -315,7 +316,7 @@ static int vhdx_write_header(BlockDriverState *bs_file, VHDXHeader *hdr, buffer = qemu_blockalign(bs_file, VHDX_HEADER_SIZE); if (read) { /* if true, we can't assume the extra reserved bytes are 0 */ - ret = bdrv_pread(bs_file, offset, buffer, VHDX_HEADER_SIZE); + ret = bdrv_pread(file, offset, buffer, VHDX_HEADER_SIZE); if (ret < 0) { goto exit; } @@ -329,7 +330,7 @@ static int vhdx_write_header(BlockDriverState *bs_file, VHDXHeader *hdr, vhdx_header_le_export(hdr, header_le); vhdx_update_checksum(buffer, VHDX_HEADER_SIZE, offsetof(VHDXHeader, checksum)); - ret = bdrv_pwrite_sync(bs_file, offset, header_le, sizeof(VHDXHeader)); + ret = bdrv_pwrite_sync(file, offset, header_le, sizeof(VHDXHeader)); exit: qemu_vfree(buffer); @@ -378,7 +379,7 @@ static int vhdx_update_header(BlockDriverState *bs, BDRVVHDXState *s, inactive_header->log_guid = *log_guid; } - ret = vhdx_write_header(bs->file->bs, inactive_header, header_offset, true); + ret = vhdx_write_header(bs->file, inactive_header, header_offset, true); if (ret < 0) { goto exit; } @@ -430,7 +431,7 @@ static void vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s, /* We have to read the whole VHDX_HEADER_SIZE instead of * sizeof(VHDXHeader), because the checksum is over the whole * region */ - ret = bdrv_pread(bs->file->bs, VHDX_HEADER1_OFFSET, buffer, + ret = bdrv_pread(bs->file, VHDX_HEADER1_OFFSET, buffer, VHDX_HEADER_SIZE); if (ret < 0) { goto fail; @@ -447,7 +448,7 @@ static void vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s, } } - ret = bdrv_pread(bs->file->bs, VHDX_HEADER2_OFFSET, buffer, + ret = bdrv_pread(bs->file, VHDX_HEADER2_OFFSET, buffer, VHDX_HEADER_SIZE); if (ret < 0) { goto fail; @@ -521,7 +522,7 @@ static int vhdx_open_region_tables(BlockDriverState *bs, BDRVVHDXState *s) * whole block */ buffer = qemu_blockalign(bs, VHDX_HEADER_BLOCK_SIZE); - ret = bdrv_pread(bs->file->bs, VHDX_REGION_TABLE_OFFSET, buffer, + ret = bdrv_pread(bs->file, VHDX_REGION_TABLE_OFFSET, buffer, VHDX_HEADER_BLOCK_SIZE); if (ret < 0) { goto fail; @@ -634,7 +635,7 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s) buffer = qemu_blockalign(bs, VHDX_METADATA_TABLE_MAX_SIZE); - ret = bdrv_pread(bs->file->bs, s->metadata_rt.file_offset, buffer, + ret = bdrv_pread(bs->file, s->metadata_rt.file_offset, buffer, VHDX_METADATA_TABLE_MAX_SIZE); if (ret < 0) { goto exit; @@ -737,7 +738,7 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s) goto exit; } - ret = bdrv_pread(bs->file->bs, + ret = bdrv_pread(bs->file, s->metadata_entries.file_parameters_entry.offset + s->metadata_rt.file_offset, &s->params, @@ -772,7 +773,7 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s) /* determine virtual disk size, logical sector size, * and phys sector size */ - ret = bdrv_pread(bs->file->bs, + ret = bdrv_pread(bs->file, s->metadata_entries.virtual_disk_size_entry.offset + s->metadata_rt.file_offset, &s->virtual_disk_size, @@ -780,7 +781,7 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s) if (ret < 0) { goto exit; } - ret = bdrv_pread(bs->file->bs, + ret = bdrv_pread(bs->file, s->metadata_entries.logical_sector_size_entry.offset + s->metadata_rt.file_offset, &s->logical_sector_size, @@ -788,7 +789,7 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s) if (ret < 0) { goto exit; } - ret = bdrv_pread(bs->file->bs, + ret = bdrv_pread(bs->file, s->metadata_entries.phys_sector_size_entry.offset + s->metadata_rt.file_offset, &s->physical_sector_size, @@ -905,7 +906,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags, QLIST_INIT(&s->regions); /* validate the file signature */ - ret = bdrv_pread(bs->file->bs, 0, &signature, sizeof(uint64_t)); + ret = bdrv_pread(bs->file, 0, &signature, sizeof(uint64_t)); if (ret < 0) { goto fail; } @@ -964,7 +965,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - ret = bdrv_pread(bs->file->bs, s->bat_offset, s->bat, s->bat_rt.length); + ret = bdrv_pread(bs->file, s->bat_offset, s->bat, s->bat_rt.length); if (ret < 0) { goto fail; } @@ -1117,7 +1118,7 @@ static coroutine_fn int vhdx_co_readv(BlockDriverState *bs, int64_t sector_num, break; case PAYLOAD_BLOCK_FULLY_PRESENT: qemu_co_mutex_unlock(&s->lock); - ret = bdrv_co_readv(bs->file->bs, + ret = bdrv_co_readv(bs->file, sinfo.file_offset >> BDRV_SECTOR_BITS, sinfo.sectors_avail, &hd_qiov); qemu_co_mutex_lock(&s->lock); @@ -1326,7 +1327,7 @@ static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num, } /* block exists, so we can just overwrite it */ qemu_co_mutex_unlock(&s->lock); - ret = bdrv_co_writev(bs->file->bs, + ret = bdrv_co_writev(bs->file, sinfo.file_offset >> BDRV_SECTOR_BITS, sectors_to_write, &hd_qiov); qemu_co_mutex_lock(&s->lock); @@ -1387,9 +1388,11 @@ exit: * There are 2 headers, and the highest sequence number will represent * the active header */ -static int vhdx_create_new_headers(BlockDriverState *bs, uint64_t image_size, +static int vhdx_create_new_headers(BlockBackend *blk, uint64_t image_size, uint32_t log_size) { + BlockDriverState *bs = blk_bs(blk); + BdrvChild *child; int ret = 0; VHDXHeader *hdr = NULL; @@ -1404,12 +1407,18 @@ static int vhdx_create_new_headers(BlockDriverState *bs, uint64_t image_size, vhdx_guid_generate(&hdr->file_write_guid); vhdx_guid_generate(&hdr->data_write_guid); - ret = vhdx_write_header(bs, hdr, VHDX_HEADER1_OFFSET, false); + /* XXX Ugly way to get blk->root, but that's a feature, not a bug. This + * hack makes it obvious that vhdx_write_header() bypasses the BlockBackend + * here, which it really shouldn't be doing. */ + child = QLIST_FIRST(&bs->parents); + assert(!QLIST_NEXT(child, next_parent)); + + ret = vhdx_write_header(child, hdr, VHDX_HEADER1_OFFSET, false); if (ret < 0) { goto exit; } hdr->sequence_number++; - ret = vhdx_write_header(bs, hdr, VHDX_HEADER2_OFFSET, false); + ret = vhdx_write_header(child, hdr, VHDX_HEADER2_OFFSET, false); if (ret < 0) { goto exit; } @@ -1442,7 +1451,7 @@ exit: * The first 64KB of the Metadata section is reserved for the metadata * header and entries; beyond that, the metadata items themselves reside. */ -static int vhdx_create_new_metadata(BlockDriverState *bs, +static int vhdx_create_new_metadata(BlockBackend *blk, uint64_t image_size, uint32_t block_size, uint32_t sector_size, @@ -1538,13 +1547,13 @@ static int vhdx_create_new_metadata(BlockDriverState *bs, VHDX_META_FLAGS_IS_VIRTUAL_DISK; vhdx_metadata_entry_le_export(&md_table_entry[4]); - ret = bdrv_pwrite(bs, metadata_offset, buffer, VHDX_HEADER_BLOCK_SIZE); + ret = blk_pwrite(blk, metadata_offset, buffer, VHDX_HEADER_BLOCK_SIZE, 0); if (ret < 0) { goto exit; } - ret = bdrv_pwrite(bs, metadata_offset + (64 * KiB), entry_buffer, - VHDX_METADATA_ENTRY_BUFFER_SIZE); + ret = blk_pwrite(blk, metadata_offset + (64 * KiB), entry_buffer, + VHDX_METADATA_ENTRY_BUFFER_SIZE, 0); if (ret < 0) { goto exit; } @@ -1564,7 +1573,7 @@ exit: * Fixed images: default state of the BAT is fully populated, with * file offsets and state PAYLOAD_BLOCK_FULLY_PRESENT. */ -static int vhdx_create_bat(BlockDriverState *bs, BDRVVHDXState *s, +static int vhdx_create_bat(BlockBackend *blk, BDRVVHDXState *s, uint64_t image_size, VHDXImageType type, bool use_zero_blocks, uint64_t file_offset, uint32_t length) @@ -1588,12 +1597,12 @@ static int vhdx_create_bat(BlockDriverState *bs, BDRVVHDXState *s, if (type == VHDX_TYPE_DYNAMIC) { /* All zeroes, so we can just extend the file - the end of the BAT * is the furthest thing we have written yet */ - ret = bdrv_truncate(bs, data_file_offset); + ret = blk_truncate(blk, data_file_offset); if (ret < 0) { goto exit; } } else if (type == VHDX_TYPE_FIXED) { - ret = bdrv_truncate(bs, data_file_offset + image_size); + ret = blk_truncate(blk, data_file_offset + image_size); if (ret < 0) { goto exit; } @@ -1604,7 +1613,7 @@ static int vhdx_create_bat(BlockDriverState *bs, BDRVVHDXState *s, if (type == VHDX_TYPE_FIXED || use_zero_blocks || - bdrv_has_zero_init(bs) == 0) { + bdrv_has_zero_init(blk_bs(blk)) == 0) { /* for a fixed file, the default BAT entry is not zero */ s->bat = g_try_malloc0(length); if (length && s->bat == NULL) { @@ -1620,12 +1629,12 @@ static int vhdx_create_bat(BlockDriverState *bs, BDRVVHDXState *s, sinfo.file_offset = data_file_offset + (sector_num << s->logical_sector_size_bits); sinfo.file_offset = ROUND_UP(sinfo.file_offset, MiB); - vhdx_update_bat_table_entry(bs, s, &sinfo, &unused, &unused, + vhdx_update_bat_table_entry(blk_bs(blk), s, &sinfo, &unused, &unused, block_state); cpu_to_le64s(&s->bat[sinfo.bat_idx]); sector_num += s->sectors_per_block; } - ret = bdrv_pwrite(bs, file_offset, s->bat, length); + ret = blk_pwrite(blk, file_offset, s->bat, length, 0); if (ret < 0) { goto exit; } @@ -1645,7 +1654,7 @@ exit: * to create the BAT itself, we will also cause the BAT to be * created. */ -static int vhdx_create_new_region_table(BlockDriverState *bs, +static int vhdx_create_new_region_table(BlockBackend *blk, uint64_t image_size, uint32_t block_size, uint32_t sector_size, @@ -1720,21 +1729,21 @@ static int vhdx_create_new_region_table(BlockDriverState *bs, /* The region table gives us the data we need to create the BAT, * so do that now */ - ret = vhdx_create_bat(bs, s, image_size, type, use_zero_blocks, + ret = vhdx_create_bat(blk, s, image_size, type, use_zero_blocks, bat_file_offset, bat_length); if (ret < 0) { goto exit; } /* Now write out the region headers to disk */ - ret = bdrv_pwrite(bs, VHDX_REGION_TABLE_OFFSET, buffer, - VHDX_HEADER_BLOCK_SIZE); + ret = blk_pwrite(blk, VHDX_REGION_TABLE_OFFSET, buffer, + VHDX_HEADER_BLOCK_SIZE, 0); if (ret < 0) { goto exit; } - ret = bdrv_pwrite(bs, VHDX_REGION_TABLE2_OFFSET, buffer, - VHDX_HEADER_BLOCK_SIZE); + ret = blk_pwrite(blk, VHDX_REGION_TABLE2_OFFSET, buffer, + VHDX_HEADER_BLOCK_SIZE, 0); if (ret < 0) { goto exit; } @@ -1871,13 +1880,13 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp) /* Creates (B),(C) */ - ret = vhdx_create_new_headers(blk_bs(blk), image_size, log_size); + ret = vhdx_create_new_headers(blk, image_size, log_size); if (ret < 0) { goto delete_and_exit; } /* Creates (D),(E),(G) explicitly. (F) created as by-product */ - ret = vhdx_create_new_region_table(blk_bs(blk), image_size, block_size, 512, + ret = vhdx_create_new_region_table(blk, image_size, block_size, 512, log_size, use_zero_blocks, image_type, &metadata_offset); if (ret < 0) { @@ -1885,7 +1894,7 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp) } /* Creates (H) */ - ret = vhdx_create_new_metadata(blk_bs(blk), image_size, block_size, 512, + ret = vhdx_create_new_metadata(blk, image_size, block_size, 512, metadata_offset, image_type); if (ret < 0) { goto delete_and_exit; diff --git a/block/vmdk.c b/block/vmdk.c index 2901692700..d73f4314ba 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -252,7 +252,7 @@ static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent) int ret; desc = g_malloc0(DESC_SIZE); - ret = bdrv_pread(bs->file->bs, s->desc_offset, desc, DESC_SIZE); + ret = bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE); if (ret < 0) { g_free(desc); return 0; @@ -286,7 +286,7 @@ static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid) desc = g_malloc0(DESC_SIZE); tmp_desc = g_malloc0(DESC_SIZE); - ret = bdrv_pread(bs->file->bs, s->desc_offset, desc, DESC_SIZE); + ret = bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE); if (ret < 0) { goto out; } @@ -306,7 +306,7 @@ static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid) pstrcat(desc, DESC_SIZE, tmp_desc); } - ret = bdrv_pwrite_sync(bs->file->bs, s->desc_offset, desc, DESC_SIZE); + ret = bdrv_pwrite_sync(bs->file, s->desc_offset, desc, DESC_SIZE); out: g_free(desc); @@ -350,7 +350,7 @@ static int vmdk_parent_open(BlockDriverState *bs) int ret; desc = g_malloc0(DESC_SIZE + 1); - ret = bdrv_pread(bs->file->bs, s->desc_offset, desc, DESC_SIZE); + ret = bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE); if (ret < 0) { goto out; } @@ -454,7 +454,7 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent, return -ENOMEM; } - ret = bdrv_pread(extent->file->bs, + ret = bdrv_pread(extent->file, extent->l1_table_offset, extent->l1_table, l1_size); @@ -474,7 +474,7 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent, ret = -ENOMEM; goto fail_l1; } - ret = bdrv_pread(extent->file->bs, + ret = bdrv_pread(extent->file, extent->l1_backup_table_offset, extent->l1_backup_table, l1_size); @@ -508,7 +508,7 @@ static int vmdk_open_vmfs_sparse(BlockDriverState *bs, VMDK3Header header; VmdkExtent *extent; - ret = bdrv_pread(file->bs, sizeof(magic), &header, sizeof(header)); + ret = bdrv_pread(file, sizeof(magic), &header, sizeof(header)); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read header from file '%s'", @@ -538,14 +538,13 @@ static int vmdk_open_vmfs_sparse(BlockDriverState *bs, static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf, QDict *options, Error **errp); -static char *vmdk_read_desc(BlockDriverState *file, uint64_t desc_offset, - Error **errp) +static char *vmdk_read_desc(BdrvChild *file, uint64_t desc_offset, Error **errp) { int64_t size; char *buf; int ret; - size = bdrv_getlength(file); + size = bdrv_getlength(file->bs); if (size < 0) { error_setg_errno(errp, -size, "Could not access file"); return NULL; @@ -586,7 +585,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs, int64_t l1_backup_offset = 0; bool compressed; - ret = bdrv_pread(file->bs, sizeof(magic), &header, sizeof(header)); + ret = bdrv_pread(file, sizeof(magic), &header, sizeof(header)); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read header from file '%s'", @@ -596,7 +595,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs, if (header.capacity == 0) { uint64_t desc_offset = le64_to_cpu(header.desc_offset); if (desc_offset) { - char *buf = vmdk_read_desc(file->bs, desc_offset << 9, errp); + char *buf = vmdk_read_desc(file, desc_offset << 9, errp); if (!buf) { return -EINVAL; } @@ -636,7 +635,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs, } QEMU_PACKED eos_marker; } QEMU_PACKED footer; - ret = bdrv_pread(file->bs, + ret = bdrv_pread(file, bs->file->bs->total_sectors * 512 - 1536, &footer, sizeof(footer)); if (ret < 0) { @@ -874,7 +873,7 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, extent->flat_start_offset = flat_offset << 9; } else if (!strcmp(type, "SPARSE") || !strcmp(type, "VMFSSPARSE")) { /* SPARSE extent and VMFSSPARSE extent are both "COWD" sparse file*/ - char *buf = vmdk_read_desc(extent_file->bs, 0, errp); + char *buf = vmdk_read_desc(extent_file, 0, errp); if (!buf) { ret = -EINVAL; } else { @@ -943,7 +942,7 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags, BDRVVmdkState *s = bs->opaque; uint32_t magic; - buf = vmdk_read_desc(bs->file->bs, 0, errp); + buf = vmdk_read_desc(bs->file, 0, errp); if (!buf) { return -EINVAL; } @@ -1046,14 +1045,14 @@ static int get_whole_cluster(BlockDriverState *bs, /* Read backing data before skip range */ if (skip_start_bytes > 0) { if (bs->backing) { - ret = bdrv_pread(bs->backing->bs, offset, whole_grain, + ret = bdrv_pread(bs->backing, offset, whole_grain, skip_start_bytes); if (ret < 0) { ret = VMDK_ERROR; goto exit; } } - ret = bdrv_pwrite(extent->file->bs, cluster_offset, whole_grain, + ret = bdrv_pwrite(extent->file, cluster_offset, whole_grain, skip_start_bytes); if (ret < 0) { ret = VMDK_ERROR; @@ -1063,7 +1062,7 @@ static int get_whole_cluster(BlockDriverState *bs, /* Read backing data after skip range */ if (skip_end_bytes < cluster_bytes) { if (bs->backing) { - ret = bdrv_pread(bs->backing->bs, offset + skip_end_bytes, + ret = bdrv_pread(bs->backing, offset + skip_end_bytes, whole_grain + skip_end_bytes, cluster_bytes - skip_end_bytes); if (ret < 0) { @@ -1071,7 +1070,7 @@ static int get_whole_cluster(BlockDriverState *bs, goto exit; } } - ret = bdrv_pwrite(extent->file->bs, cluster_offset + skip_end_bytes, + ret = bdrv_pwrite(extent->file, cluster_offset + skip_end_bytes, whole_grain + skip_end_bytes, cluster_bytes - skip_end_bytes); if (ret < 0) { @@ -1091,8 +1090,7 @@ static int vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data, { offset = cpu_to_le32(offset); /* update L2 table */ - if (bdrv_pwrite_sync( - extent->file->bs, + if (bdrv_pwrite_sync(extent->file, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(offset)), &offset, sizeof(offset)) < 0) { @@ -1101,8 +1099,7 @@ static int vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data, /* update backup L2 table */ if (extent->l1_backup_table_offset != 0) { m_data->l2_offset = extent->l1_backup_table[m_data->l1_index]; - if (bdrv_pwrite_sync( - extent->file->bs, + if (bdrv_pwrite_sync(extent->file, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(offset)), &offset, sizeof(offset)) < 0) { @@ -1191,8 +1188,7 @@ static int get_cluster_offset(BlockDriverState *bs, } } l2_table = extent->l2_cache + (min_index * extent->l2_size); - if (bdrv_pread( - extent->file->bs, + if (bdrv_pread(extent->file, (int64_t)l2_offset * 512, l2_table, extent->l2_size * sizeof(uint32_t) @@ -1373,7 +1369,7 @@ static int vmdk_write_extent(VmdkExtent *extent, int64_t cluster_offset, } write_offset = cluster_offset + offset_in_cluster, - ret = bdrv_co_pwritev(extent->file->bs, write_offset, n_bytes, + ret = bdrv_co_pwritev(extent->file, write_offset, n_bytes, &local_qiov, 0); write_end_sector = DIV_ROUND_UP(write_offset + n_bytes, BDRV_SECTOR_SIZE); @@ -1411,7 +1407,7 @@ static int vmdk_read_extent(VmdkExtent *extent, int64_t cluster_offset, if (!extent->compressed) { - ret = bdrv_co_preadv(extent->file->bs, + ret = bdrv_co_preadv(extent->file, cluster_offset + offset_in_cluster, bytes, qiov, 0); if (ret < 0) { @@ -1424,7 +1420,7 @@ static int vmdk_read_extent(VmdkExtent *extent, int64_t cluster_offset, buf_bytes = cluster_bytes * 2; cluster_buf = g_malloc(buf_bytes); uncomp_buf = g_malloc(cluster_bytes); - ret = bdrv_pread(extent->file->bs, + ret = bdrv_pread(extent->file, cluster_offset, cluster_buf, buf_bytes); if (ret < 0) { @@ -1501,7 +1497,7 @@ vmdk_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, qemu_iovec_reset(&local_qiov); qemu_iovec_concat(&local_qiov, qiov, bytes_done, n_bytes); - ret = bdrv_co_preadv(bs->backing->bs, offset, n_bytes, + ret = bdrv_co_preadv(bs->backing, offset, n_bytes, &local_qiov, 0); if (ret < 0) { goto fail; diff --git a/block/vpc.c b/block/vpc.c index 076a7ce399..43707ed22c 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -237,7 +237,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - ret = bdrv_pread(bs->file->bs, 0, s->footer_buf, HEADER_SIZE); + ret = bdrv_pread(bs->file, 0, s->footer_buf, HEADER_SIZE); if (ret < 0) { error_setg(errp, "Unable to read VHD header"); goto fail; @@ -257,7 +257,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, } /* If a fixed disk, the footer is found only at the end of the file */ - ret = bdrv_pread(bs->file->bs, offset-HEADER_SIZE, s->footer_buf, + ret = bdrv_pread(bs->file, offset-HEADER_SIZE, s->footer_buf, HEADER_SIZE); if (ret < 0) { goto fail; @@ -328,7 +328,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, } if (disk_type == VHD_DYNAMIC) { - ret = bdrv_pread(bs->file->bs, be64_to_cpu(footer->data_offset), buf, + ret = bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), buf, HEADER_SIZE); if (ret < 0) { error_setg(errp, "Error reading dynamic VHD header"); @@ -385,7 +385,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, s->bat_offset = be64_to_cpu(dyndisk_header->table_offset); - ret = bdrv_pread(bs->file->bs, s->bat_offset, s->pagetable, + ret = bdrv_pread(bs->file, s->bat_offset, s->pagetable, pagetable_size); if (ret < 0) { error_setg(errp, "Error reading pagetable"); @@ -481,7 +481,7 @@ static inline int64_t get_image_offset(BlockDriverState *bs, uint64_t offset, s->last_bitmap_offset = bitmap_offset; memset(bitmap, 0xff, s->bitmap_size); - bdrv_pwrite_sync(bs->file->bs, bitmap_offset, bitmap, s->bitmap_size); + bdrv_pwrite_sync(bs->file, bitmap_offset, bitmap, s->bitmap_size); } return block_offset; @@ -505,7 +505,7 @@ static int rewrite_footer(BlockDriverState* bs) BDRVVPCState *s = bs->opaque; int64_t offset = s->free_data_block_offset; - ret = bdrv_pwrite_sync(bs->file->bs, offset, s->footer_buf, HEADER_SIZE); + ret = bdrv_pwrite_sync(bs->file, offset, s->footer_buf, HEADER_SIZE); if (ret < 0) return ret; @@ -539,7 +539,7 @@ static int64_t alloc_block(BlockDriverState* bs, int64_t offset) /* Initialize the block's bitmap */ memset(bitmap, 0xff, s->bitmap_size); - ret = bdrv_pwrite_sync(bs->file->bs, s->free_data_block_offset, bitmap, + ret = bdrv_pwrite_sync(bs->file, s->free_data_block_offset, bitmap, s->bitmap_size); if (ret < 0) { return ret; @@ -554,7 +554,7 @@ static int64_t alloc_block(BlockDriverState* bs, int64_t offset) /* Write BAT entry to disk */ bat_offset = s->bat_offset + (4 * index); bat_value = cpu_to_be32(s->pagetable[index]); - ret = bdrv_pwrite_sync(bs->file->bs, bat_offset, &bat_value, 4); + ret = bdrv_pwrite_sync(bs->file, bat_offset, &bat_value, 4); if (ret < 0) goto fail; @@ -591,7 +591,7 @@ vpc_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector local_qiov; if (be32_to_cpu(footer->type) == VHD_FIXED) { - return bdrv_co_preadv(bs->file->bs, offset, bytes, qiov, 0); + return bdrv_co_preadv(bs->file, offset, bytes, qiov, 0); } qemu_co_mutex_lock(&s->lock); @@ -607,7 +607,7 @@ vpc_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, qemu_iovec_reset(&local_qiov); qemu_iovec_concat(&local_qiov, qiov, bytes_done, n_bytes); - ret = bdrv_co_preadv(bs->file->bs, image_offset, n_bytes, + ret = bdrv_co_preadv(bs->file, image_offset, n_bytes, &local_qiov, 0); if (ret < 0) { goto fail; @@ -640,7 +640,7 @@ vpc_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector local_qiov; if (be32_to_cpu(footer->type) == VHD_FIXED) { - return bdrv_co_pwritev(bs->file->bs, offset, bytes, qiov, 0); + return bdrv_co_pwritev(bs->file, offset, bytes, qiov, 0); } qemu_co_mutex_lock(&s->lock); @@ -661,7 +661,7 @@ vpc_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, qemu_iovec_reset(&local_qiov); qemu_iovec_concat(&local_qiov, qiov, bytes_done, n_bytes); - ret = bdrv_co_pwritev(bs->file->bs, image_offset, n_bytes, + ret = bdrv_co_pwritev(bs->file, image_offset, n_bytes, &local_qiov, 0); if (ret < 0) { goto fail; diff --git a/block/vvfat.c b/block/vvfat.c index 5569450616..c3f24c6eb0 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -341,9 +341,8 @@ typedef struct BDRVVVFATState { unsigned int current_cluster; /* write support */ - BlockDriverState* write_target; char* qcow_filename; - BlockDriverState* qcow; + BdrvChild* qcow; void* fat2; char* used_clusters; array_t commits; @@ -981,7 +980,7 @@ static int init_directories(BDRVVVFATState* s, static BDRVVVFATState *vvv = NULL; #endif -static int enable_write_target(BDRVVVFATState *s, Error **errp); +static int enable_write_target(BlockDriverState *bs, Error **errp); static int is_consistent(BDRVVVFATState *s); static QemuOptsList runtime_opts = { @@ -1158,8 +1157,8 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags, s->current_cluster=0xffffffff; /* read only is the default for safety */ - bs->read_only = 1; - s->qcow = s->write_target = NULL; + bs->read_only = true; + s->qcow = NULL; s->qcow_filename = NULL; s->fat2 = NULL; s->downcase_short_names = 1; @@ -1170,14 +1169,13 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags, s->sector_count = cyls * heads * secs - (s->first_sectors_number - 1); if (qemu_opt_get_bool(opts, "rw", false)) { - ret = enable_write_target(s, errp); + ret = enable_write_target(bs, errp); if (ret < 0) { goto fail; } - bs->read_only = 0; + bs->read_only = false; } - bs->request_alignment = BDRV_SECTOR_SIZE; /* No sub-sector I/O supported */ bs->total_sectors = cyls * heads * secs; if (init_directories(s, dirname, heads, secs, errp)) { @@ -1209,6 +1207,11 @@ fail: return ret; } +static void vvfat_refresh_limits(BlockDriverState *bs, Error **errp) +{ + bs->bl.request_alignment = BDRV_SECTOR_SIZE; /* No sub-sector I/O */ +} + static inline void vvfat_close_current_file(BDRVVVFATState *s) { if(s->current_mapping) { @@ -1387,9 +1390,10 @@ static int vvfat_read(BlockDriverState *bs, int64_t sector_num, return -1; if (s->qcow) { int n; - if (bdrv_is_allocated(s->qcow, sector_num, nb_sectors-i, &n)) { -DLOG(fprintf(stderr, "sectors %d+%d allocated\n", (int)sector_num, n)); - if (bdrv_read(s->qcow, sector_num, buf + i*0x200, n)) { + if (bdrv_is_allocated(s->qcow->bs, sector_num, nb_sectors-i, &n)) { + DLOG(fprintf(stderr, "sectors %d+%d allocated\n", + (int)sector_num, n)); + if (bdrv_read(s->qcow, sector_num, buf + i * 0x200, n)) { return -1; } i += n - 1; @@ -1665,12 +1669,15 @@ static inline int cluster_was_modified(BDRVVVFATState* s, uint32_t cluster_num) int was_modified = 0; int i, dummy; - if (s->qcow == NULL) - return 0; + if (s->qcow == NULL) { + return 0; + } - for (i = 0; !was_modified && i < s->sectors_per_cluster; i++) - was_modified = bdrv_is_allocated(s->qcow, - cluster2sector(s, cluster_num) + i, 1, &dummy); + for (i = 0; !was_modified && i < s->sectors_per_cluster; i++) { + was_modified = bdrv_is_allocated(s->qcow->bs, + cluster2sector(s, cluster_num) + i, + 1, &dummy); + } return was_modified; } @@ -1819,11 +1826,16 @@ static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s, vvfat_close_current_file(s); for (i = 0; i < s->sectors_per_cluster; i++) { - if (!bdrv_is_allocated(s->qcow, offset + i, 1, &dummy)) { - if (vvfat_read(s->bs, offset, s->cluster_buffer, 1)) { + int res; + + res = bdrv_is_allocated(s->qcow->bs, offset + i, 1, &dummy); + if (!res) { + res = vvfat_read(s->bs, offset, s->cluster_buffer, 1); + if (res) { return -1; } - if (bdrv_write(s->qcow, offset, s->cluster_buffer, 1)) { + res = bdrv_write(s->qcow, offset, s->cluster_buffer, 1); + if (res) { return -2; } } @@ -2779,8 +2791,8 @@ static int do_commit(BDRVVVFATState* s) return ret; } - if (s->qcow->drv->bdrv_make_empty) { - s->qcow->drv->bdrv_make_empty(s->qcow); + if (s->qcow->bs->drv->bdrv_make_empty) { + s->qcow->bs->drv->bdrv_make_empty(s->qcow->bs); } memset(s->used_clusters, 0, sector2cluster(s, s->sector_count)); @@ -2946,7 +2958,7 @@ write_target_commit(BlockDriverState *bs, uint64_t offset, uint64_t bytes, static void write_target_close(BlockDriverState *bs) { BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque); - bdrv_unref(s->qcow); + bdrv_unref_child(s->bs, s->qcow); g_free(s->qcow_filename); } @@ -2956,8 +2968,19 @@ static BlockDriver vvfat_write_target = { .bdrv_close = write_target_close, }; -static int enable_write_target(BDRVVVFATState *s, Error **errp) +static void vvfat_qcow_options(int *child_flags, QDict *child_options, + int parent_flags, QDict *parent_options) { + *child_flags = BDRV_O_RDWR | BDRV_O_NO_FLUSH; +} + +static const BdrvChildRole child_vvfat_qcow = { + .inherit_options = vvfat_qcow_options, +}; + +static int enable_write_target(BlockDriverState *bs, Error **errp) +{ + BDRVVVFATState *s = bs->opaque; BlockDriver *bdrv_qcow = NULL; BlockDriverState *backing; QemuOpts *opts = NULL; @@ -2996,8 +3019,8 @@ static int enable_write_target(BDRVVVFATState *s, Error **errp) options = qdict_new(); qdict_put(options, "driver", qstring_from_str("qcow")); - s->qcow = bdrv_open(s->qcow_filename, NULL, options, - BDRV_O_RDWR | BDRV_O_NO_FLUSH, errp); + s->qcow = bdrv_open_child(s->qcow_filename, options, "write-target", bs, + &child_vvfat_qcow, false, errp); if (!s->qcow) { ret = -EINVAL; goto err; @@ -3046,6 +3069,7 @@ static BlockDriver bdrv_vvfat = { .bdrv_parse_filename = vvfat_parse_filename, .bdrv_file_open = vvfat_open, + .bdrv_refresh_limits = vvfat_refresh_limits, .bdrv_close = vvfat_close, .bdrv_co_preadv = vvfat_co_preadv, diff --git a/blockdev.c b/blockdev.c index 3a104a0aaa..0f8065c4a5 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3950,10 +3950,10 @@ out: void qmp_blockdev_add(BlockdevOptions *options, Error **errp) { - QmpOutputVisitor *ov = qmp_output_visitor_new(); BlockDriverState *bs; BlockBackend *blk = NULL; QObject *obj; + Visitor *v = qmp_output_visitor_new(&obj); QDict *qdict; Error *local_err = NULL; @@ -3972,14 +3972,13 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp) } } - visit_type_BlockdevOptions(qmp_output_get_visitor(ov), NULL, &options, - &local_err); + visit_type_BlockdevOptions(v, NULL, &options, &local_err); if (local_err) { error_propagate(errp, local_err); goto fail; } - obj = qmp_output_get_qobject(ov); + visit_complete(v, &obj); qdict = qobject_to_qdict(obj); qdict_flatten(qdict); @@ -4020,7 +4019,7 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp) } fail: - qmp_output_visitor_cleanup(ov); + visit_free(v); } void qmp_x_blockdev_del(bool has_id, const char *id, @@ -305,8 +305,8 @@ archipelago="no" gtk="" gtkabi="" gtk_gl="no" +tls_priority="NORMAL" gnutls="" -gnutls_hash="" gnutls_rnd="" nettle="" nettle_kdf="no" @@ -369,6 +369,7 @@ fi ar="${AR-${cross_prefix}ar}" as="${AS-${cross_prefix}as}" +ccas="${CCAS-$cc}" cpp="${CPP-$cc -E}" objcopy="${OBJCOPY-${cross_prefix}objcopy}" ld="${LD-${cross_prefix}ld}" @@ -1097,6 +1098,8 @@ for opt do ;; --enable-gtk) gtk="yes" ;; + --tls-priority=*) tls_priority="$optarg" + ;; --disable-gnutls) gnutls="no" ;; --enable-gnutls) gnutls="yes" @@ -1308,6 +1311,7 @@ Advanced options (experts only): --disable-blobs disable installing provided firmware blobs --with-vss-sdk=SDK-path enable Windows VSS support in QEMU Guest Agent --with-win-sdk=SDK-path path to Windows Platform SDK (to build VSS .tlb) + --tls-priority default TLS protocol/cipher priority string Optional features, enabled with --enable-FEATURE and disabled with --disable-FEATURE, default is enabled if available: @@ -2218,13 +2222,6 @@ if test "$gnutls" != "no"; then QEMU_CFLAGS="$QEMU_CFLAGS $gnutls_cflags" gnutls="yes" - # gnutls_hash_init requires >= 2.9.10 - if $pkg_config --exists "gnutls >= 2.9.10"; then - gnutls_hash="yes" - else - gnutls_hash="no" - fi - # gnutls_rnd requires >= 2.11.0 if $pkg_config --exists "gnutls >= 2.11.0"; then gnutls_rnd="yes" @@ -2258,11 +2255,9 @@ if test "$gnutls" != "no"; then feature_not_found "gnutls" "Install gnutls devel" else gnutls="no" - gnutls_hash="no" gnutls_rnd="no" fi else - gnutls_hash="no" gnutls_rnd="no" fi @@ -4812,8 +4807,8 @@ echo "SDL support $sdl $(echo_version $sdl $sdlversion)" echo "GTK support $gtk $(echo_version $gtk $gtk_version)" echo "GTK GL support $gtk_gl" echo "VTE support $vte $(echo_version $vte $vteversion)" +echo "TLS priority $tls_priority" echo "GNUTLS support $gnutls" -echo "GNUTLS hash $gnutls_hash" echo "GNUTLS rnd $gnutls_rnd" echo "libgcrypt $gcrypt" echo "libgcrypt kdf $gcrypt_kdf" @@ -5176,12 +5171,10 @@ if test "$gtk" = "yes" ; then echo "CONFIG_GTK_GL=y" >> $config_host_mak fi fi +echo "CONFIG_TLS_PRIORITY=\"$tls_priority\"" >> $config_host_mak if test "$gnutls" = "yes" ; then echo "CONFIG_GNUTLS=y" >> $config_host_mak fi -if test "$gnutls_hash" = "yes" ; then - echo "CONFIG_GNUTLS_HASH=y" >> $config_host_mak -fi if test "$gnutls_rnd" = "yes" ; then echo "CONFIG_GNUTLS_RND=y" >> $config_host_mak fi @@ -5517,6 +5510,7 @@ echo "OBJCC=$objcc" >> $config_host_mak echo "AR=$ar" >> $config_host_mak echo "ARFLAGS=$ARFLAGS" >> $config_host_mak echo "AS=$as" >> $config_host_mak +echo "CCAS=$ccas" >> $config_host_mak echo "CPP=$cpp" >> $config_host_mak echo "OBJCOPY=$objcopy" >> $config_host_mak echo "LD=$ld" >> $config_host_mak @@ -5990,6 +5984,7 @@ for rom in seabios vgabios ; do echo "# Automatically generated by configure - do not modify" > $config_mak echo "SRC_PATH=$source_path/roms/$rom" >> $config_mak echo "AS=$as" >> $config_mak + echo "CCAS=$ccas" >> $config_mak echo "CC=$cc" >> $config_mak echo "BCC=bcc" >> $config_mak echo "CPP=$cpp" >> $config_mak @@ -498,6 +498,35 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr) return qemu_ram_addr_from_host_nofail(p); } +/* Return true if ADDR is present in the victim tlb, and has been copied + back to the main tlb. */ +static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index, + size_t elt_ofs, target_ulong page) +{ + size_t vidx; + for (vidx = 0; vidx < CPU_VTLB_SIZE; ++vidx) { + CPUTLBEntry *vtlb = &env->tlb_v_table[mmu_idx][vidx]; + target_ulong cmp = *(target_ulong *)((uintptr_t)vtlb + elt_ofs); + + if (cmp == page) { + /* Found entry in victim tlb, swap tlb and iotlb. */ + CPUTLBEntry tmptlb, *tlb = &env->tlb_table[mmu_idx][index]; + CPUIOTLBEntry tmpio, *io = &env->iotlb[mmu_idx][index]; + CPUIOTLBEntry *vio = &env->iotlb_v[mmu_idx][vidx]; + + tmptlb = *tlb; *tlb = *vtlb; *vtlb = tmptlb; + tmpio = *io; *io = *vio; *vio = tmpio; + return true; + } + } + return false; +} + +/* Macro to call the above, with local variables from the use context. */ +#define VICTIM_TLB_HIT(TY, ADDR) \ + victim_tlb_hit(env, mmu_idx, index, offsetof(CPUTLBEntry, TY), \ + (ADDR) & TARGET_PAGE_MASK) + #define MMUSUFFIX _mmu #define SHIFT 0 diff --git a/crypto/Makefile.objs b/crypto/Makefile.objs index 0737f48118..1f86f4f07f 100644 --- a/crypto/Makefile.objs +++ b/crypto/Makefile.objs @@ -1,5 +1,7 @@ crypto-obj-y = init.o crypto-obj-y += hash.o +crypto-obj-$(CONFIG_NETTLE) += hash-nettle.o +crypto-obj-$(if $(CONFIG_NETTLE),n,$(CONFIG_GCRYPT)) += hash-gcrypt.o crypto-obj-y += aes.o crypto-obj-y += desrfb.o crypto-obj-y += cipher.o @@ -28,3 +30,4 @@ crypto-aes-obj-y = aes.o stub-obj-y += random-stub.o stub-obj-y += pbkdf-stub.o +stub-obj-y += hash-stub.o diff --git a/crypto/block-luks.c b/crypto/block-luks.c index 63649f1091..fcf3b040e4 100644 --- a/crypto/block-luks.c +++ b/crypto/block-luks.c @@ -776,6 +776,11 @@ qcrypto_block_luks_open(QCryptoBlock *block, } if (ivalg == QCRYPTO_IVGEN_ALG_ESSIV) { + if (!ivhash_name) { + ret = -EINVAL; + error_setg(errp, "Missing IV generator hash specification"); + goto fail; + } ivcipheralg = qcrypto_block_luks_essiv_cipher(cipheralg, ivhash, &local_err); @@ -785,6 +790,13 @@ qcrypto_block_luks_open(QCryptoBlock *block, goto fail; } } else { + /* Note we parsed the ivhash_name earlier in the cipher_mode + * spec string even with plain/plain64 ivgens, but we + * will ignore it, since it is irrelevant for these ivgens. + * This is for compat with dm-crypt which will silently + * ignore hash names with these ivgens rather than report + * an error about the invalid usage + */ ivcipheralg = cipheralg; } @@ -904,6 +916,15 @@ qcrypto_block_luks_create(QCryptoBlock *block, if (!luks_opts.has_hash_alg) { luks_opts.hash_alg = QCRYPTO_HASH_ALG_SHA256; } + if (luks_opts.ivgen_alg == QCRYPTO_IVGEN_ALG_ESSIV) { + if (!luks_opts.has_ivgen_hash_alg) { + luks_opts.ivgen_hash_alg = QCRYPTO_HASH_ALG_SHA256; + luks_opts.has_ivgen_hash_alg = true; + } + } + /* Note we're allowing ivgen_hash_alg to be set even for + * non-essiv iv generators that don't need a hash. It will + * be silently ignored, for compatibility with dm-crypt */ if (!options->u.luks.key_secret) { error_setg(errp, "Parameter 'key-secret' is required for cipher"); diff --git a/crypto/hash-gcrypt.c b/crypto/hash-gcrypt.c new file mode 100644 index 0000000000..8ea5aff4ee --- /dev/null +++ b/crypto/hash-gcrypt.c @@ -0,0 +1,110 @@ +/* + * QEMU Crypto hash algorithms + * + * Copyright (c) 2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "crypto/hash.h" +#include "gcrypt.h" + + +static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALG__MAX] = { + [QCRYPTO_HASH_ALG_MD5] = GCRY_MD_MD5, + [QCRYPTO_HASH_ALG_SHA1] = GCRY_MD_SHA1, + [QCRYPTO_HASH_ALG_SHA224] = GCRY_MD_SHA224, + [QCRYPTO_HASH_ALG_SHA256] = GCRY_MD_SHA256, + [QCRYPTO_HASH_ALG_SHA384] = GCRY_MD_SHA384, + [QCRYPTO_HASH_ALG_SHA512] = GCRY_MD_SHA512, + [QCRYPTO_HASH_ALG_RIPEMD160] = GCRY_MD_RMD160, +}; + +gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg) +{ + if (alg < G_N_ELEMENTS(qcrypto_hash_alg_map) && + qcrypto_hash_alg_map[alg] != GCRY_MD_NONE) { + return true; + } + return false; +} + + +int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg, + const struct iovec *iov, + size_t niov, + uint8_t **result, + size_t *resultlen, + Error **errp) +{ + int i, ret; + gcry_md_hd_t md; + unsigned char *digest; + + if (alg >= G_N_ELEMENTS(qcrypto_hash_alg_map) || + qcrypto_hash_alg_map[alg] == GCRY_MD_NONE) { + error_setg(errp, + "Unknown hash algorithm %d", + alg); + return -1; + } + + ret = gcry_md_open(&md, qcrypto_hash_alg_map[alg], 0); + + if (ret < 0) { + error_setg(errp, + "Unable to initialize hash algorithm: %s", + gcry_strerror(ret)); + return -1; + } + + for (i = 0; i < niov; i++) { + gcry_md_write(md, iov[i].iov_base, iov[i].iov_len); + } + + ret = gcry_md_get_algo_dlen(qcrypto_hash_alg_map[alg]); + if (ret <= 0) { + error_setg(errp, + "Unable to get hash length: %s", + gcry_strerror(ret)); + goto error; + } + if (*resultlen == 0) { + *resultlen = ret; + *result = g_new0(uint8_t, *resultlen); + } else if (*resultlen != ret) { + error_setg(errp, + "Result buffer size %zu is smaller than hash %d", + *resultlen, ret); + goto error; + } + + digest = gcry_md_read(md, 0); + if (!digest) { + error_setg(errp, + "No digest produced"); + goto error; + } + memcpy(*result, digest, *resultlen); + + gcry_md_close(md); + return 0; + + error: + gcry_md_close(md); + return -1; +} diff --git a/crypto/hash-nettle.c b/crypto/hash-nettle.c new file mode 100644 index 0000000000..4c6f50b65d --- /dev/null +++ b/crypto/hash-nettle.c @@ -0,0 +1,155 @@ +/* + * QEMU Crypto hash algorithms + * + * Copyright (c) 2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "crypto/hash.h" +#include <nettle/md5.h> +#include <nettle/sha.h> +#include <nettle/ripemd160.h> + +typedef void (*qcrypto_nettle_init)(void *ctx); +typedef void (*qcrypto_nettle_write)(void *ctx, + unsigned int len, + const uint8_t *buf); +typedef void (*qcrypto_nettle_result)(void *ctx, + unsigned int len, + uint8_t *buf); + +union qcrypto_hash_ctx { + struct md5_ctx md5; + struct sha1_ctx sha1; + struct sha224_ctx sha224; + struct sha256_ctx sha256; + struct sha384_ctx sha384; + struct sha512_ctx sha512; + struct ripemd160_ctx ripemd160; +}; + +struct qcrypto_hash_alg { + qcrypto_nettle_init init; + qcrypto_nettle_write write; + qcrypto_nettle_result result; + size_t len; +} qcrypto_hash_alg_map[] = { + [QCRYPTO_HASH_ALG_MD5] = { + .init = (qcrypto_nettle_init)md5_init, + .write = (qcrypto_nettle_write)md5_update, + .result = (qcrypto_nettle_result)md5_digest, + .len = MD5_DIGEST_SIZE, + }, + [QCRYPTO_HASH_ALG_SHA1] = { + .init = (qcrypto_nettle_init)sha1_init, + .write = (qcrypto_nettle_write)sha1_update, + .result = (qcrypto_nettle_result)sha1_digest, + .len = SHA1_DIGEST_SIZE, + }, + [QCRYPTO_HASH_ALG_SHA224] = { + .init = (qcrypto_nettle_init)sha224_init, + .write = (qcrypto_nettle_write)sha224_update, + .result = (qcrypto_nettle_result)sha224_digest, + .len = SHA224_DIGEST_SIZE, + }, + [QCRYPTO_HASH_ALG_SHA256] = { + .init = (qcrypto_nettle_init)sha256_init, + .write = (qcrypto_nettle_write)sha256_update, + .result = (qcrypto_nettle_result)sha256_digest, + .len = SHA256_DIGEST_SIZE, + }, + [QCRYPTO_HASH_ALG_SHA384] = { + .init = (qcrypto_nettle_init)sha384_init, + .write = (qcrypto_nettle_write)sha384_update, + .result = (qcrypto_nettle_result)sha384_digest, + .len = SHA384_DIGEST_SIZE, + }, + [QCRYPTO_HASH_ALG_SHA512] = { + .init = (qcrypto_nettle_init)sha512_init, + .write = (qcrypto_nettle_write)sha512_update, + .result = (qcrypto_nettle_result)sha512_digest, + .len = SHA512_DIGEST_SIZE, + }, + [QCRYPTO_HASH_ALG_RIPEMD160] = { + .init = (qcrypto_nettle_init)ripemd160_init, + .write = (qcrypto_nettle_write)ripemd160_update, + .result = (qcrypto_nettle_result)ripemd160_digest, + .len = RIPEMD160_DIGEST_SIZE, + }, +}; + +gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg) +{ + if (alg < G_N_ELEMENTS(qcrypto_hash_alg_map) && + qcrypto_hash_alg_map[alg].init != NULL) { + return true; + } + return false; +} + + +int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg, + const struct iovec *iov, + size_t niov, + uint8_t **result, + size_t *resultlen, + Error **errp) +{ + int i; + union qcrypto_hash_ctx ctx; + + if (alg >= G_N_ELEMENTS(qcrypto_hash_alg_map) || + qcrypto_hash_alg_map[alg].init == NULL) { + error_setg(errp, + "Unknown hash algorithm %d", + alg); + return -1; + } + + qcrypto_hash_alg_map[alg].init(&ctx); + + for (i = 0; i < niov; i++) { + /* Some versions of nettle have functions + * declared with 'int' instead of 'size_t' + * so to be safe avoid writing more than + * UINT_MAX bytes at a time + */ + size_t len = iov[i].iov_len; + uint8_t *base = iov[i].iov_base; + while (len) { + size_t shortlen = MIN(len, UINT_MAX); + qcrypto_hash_alg_map[alg].write(&ctx, len, base); + len -= shortlen; + base += len; + } + } + + if (*resultlen == 0) { + *resultlen = qcrypto_hash_alg_map[alg].len; + *result = g_new0(uint8_t, *resultlen); + } else if (*resultlen != qcrypto_hash_alg_map[alg].len) { + error_setg(errp, + "Result buffer size %zu is smaller than hash %zu", + *resultlen, qcrypto_hash_alg_map[alg].len); + return -1; + } + + qcrypto_hash_alg_map[alg].result(&ctx, *resultlen, *result); + + return 0; +} diff --git a/crypto/hash-stub.c b/crypto/hash-stub.c new file mode 100644 index 0000000000..8a9b8d4c09 --- /dev/null +++ b/crypto/hash-stub.c @@ -0,0 +1,41 @@ +/* + * QEMU Crypto hash algorithms + * + * Copyright (c) 2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "crypto/hash.h" + +gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg G_GNUC_UNUSED) +{ + return false; +} + +int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg, + const struct iovec *iov G_GNUC_UNUSED, + size_t niov G_GNUC_UNUSED, + uint8_t **result G_GNUC_UNUSED, + size_t *resultlen G_GNUC_UNUSED, + Error **errp) +{ + error_setg(errp, + "Hash algorithm %d not supported without GNUTLS", + alg); + return -1; +} diff --git a/crypto/hash.c b/crypto/hash.c index 2907bffd2e..0f1ceac66a 100644 --- a/crypto/hash.c +++ b/crypto/hash.c @@ -22,16 +22,14 @@ #include "qapi/error.h" #include "crypto/hash.h" -#ifdef CONFIG_GNUTLS_HASH -#include <gnutls/gnutls.h> -#include <gnutls/crypto.h> -#endif - - static size_t qcrypto_hash_alg_size[QCRYPTO_HASH_ALG__MAX] = { [QCRYPTO_HASH_ALG_MD5] = 16, [QCRYPTO_HASH_ALG_SHA1] = 20, + [QCRYPTO_HASH_ALG_SHA224] = 28, [QCRYPTO_HASH_ALG_SHA256] = 32, + [QCRYPTO_HASH_ALG_SHA384] = 48, + [QCRYPTO_HASH_ALG_SHA512] = 64, + [QCRYPTO_HASH_ALG_RIPEMD160] = 20, }; size_t qcrypto_hash_digest_len(QCryptoHashAlgorithm alg) @@ -41,105 +39,6 @@ size_t qcrypto_hash_digest_len(QCryptoHashAlgorithm alg) } -#ifdef CONFIG_GNUTLS_HASH -static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALG__MAX] = { - [QCRYPTO_HASH_ALG_MD5] = GNUTLS_DIG_MD5, - [QCRYPTO_HASH_ALG_SHA1] = GNUTLS_DIG_SHA1, - [QCRYPTO_HASH_ALG_SHA256] = GNUTLS_DIG_SHA256, -}; - -gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg) -{ - if (alg < G_N_ELEMENTS(qcrypto_hash_alg_map)) { - return true; - } - return false; -} - - -int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg, - const struct iovec *iov, - size_t niov, - uint8_t **result, - size_t *resultlen, - Error **errp) -{ - int i, ret; - gnutls_hash_hd_t dig; - - if (alg >= G_N_ELEMENTS(qcrypto_hash_alg_map)) { - error_setg(errp, - "Unknown hash algorithm %d", - alg); - return -1; - } - - ret = gnutls_hash_init(&dig, qcrypto_hash_alg_map[alg]); - - if (ret < 0) { - error_setg(errp, - "Unable to initialize hash algorithm: %s", - gnutls_strerror(ret)); - return -1; - } - - for (i = 0; i < niov; i++) { - ret = gnutls_hash(dig, iov[i].iov_base, iov[i].iov_len); - if (ret < 0) { - error_setg(errp, - "Unable process hash data: %s", - gnutls_strerror(ret)); - goto error; - } - } - - ret = gnutls_hash_get_len(qcrypto_hash_alg_map[alg]); - if (ret <= 0) { - error_setg(errp, - "Unable to get hash length: %s", - gnutls_strerror(ret)); - goto error; - } - if (*resultlen == 0) { - *resultlen = ret; - *result = g_new0(uint8_t, *resultlen); - } else if (*resultlen != ret) { - error_setg(errp, - "Result buffer size %zu is smaller than hash %d", - *resultlen, ret); - goto error; - } - - gnutls_hash_deinit(dig, *result); - return 0; - - error: - gnutls_hash_deinit(dig, NULL); - return -1; -} - -#else /* ! CONFIG_GNUTLS_HASH */ - -gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg G_GNUC_UNUSED) -{ - return false; -} - -int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg, - const struct iovec *iov G_GNUC_UNUSED, - size_t niov G_GNUC_UNUSED, - uint8_t **result G_GNUC_UNUSED, - size_t *resultlen G_GNUC_UNUSED, - Error **errp) -{ - error_setg(errp, - "Hash algorithm %d not supported without GNUTLS", - alg); - return -1; -} - -#endif /* ! CONFIG_GNUTLS_HASH */ - int qcrypto_hash_bytes(QCryptoHashAlgorithm alg, const char *buf, size_t len, diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c index 1620e126ae..a8965531b6 100644 --- a/crypto/tlscreds.c +++ b/crypto/tlscreds.c @@ -179,6 +179,27 @@ qcrypto_tls_creds_prop_get_dir(Object *obj, static void +qcrypto_tls_creds_prop_set_priority(Object *obj, + const char *value, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); + + creds->priority = g_strdup(value); +} + + +static char * +qcrypto_tls_creds_prop_get_priority(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); + + return g_strdup(creds->priority); +} + + +static void qcrypto_tls_creds_prop_set_endpoint(Object *obj, int value, Error **errp G_GNUC_UNUSED) @@ -216,6 +237,10 @@ qcrypto_tls_creds_class_init(ObjectClass *oc, void *data) qcrypto_tls_creds_prop_get_endpoint, qcrypto_tls_creds_prop_set_endpoint, NULL); + object_class_property_add_str(oc, "priority", + qcrypto_tls_creds_prop_get_priority, + qcrypto_tls_creds_prop_set_priority, + NULL); } @@ -234,6 +259,7 @@ qcrypto_tls_creds_finalize(Object *obj) QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); g_free(creds->dir); + g_free(creds->priority); } diff --git a/crypto/tlssession.c b/crypto/tlssession.c index a543e5a576..2de42c61cb 100644 --- a/crypto/tlssession.c +++ b/crypto/tlssession.c @@ -132,14 +132,22 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds, if (object_dynamic_cast(OBJECT(creds), TYPE_QCRYPTO_TLS_CREDS_ANON)) { QCryptoTLSCredsAnon *acreds = QCRYPTO_TLS_CREDS_ANON(creds); + char *prio; - ret = gnutls_priority_set_direct(session->handle, - "NORMAL:+ANON-DH", NULL); + if (creds->priority != NULL) { + prio = g_strdup_printf("%s:+ANON-DH", creds->priority); + } else { + prio = g_strdup(CONFIG_TLS_PRIORITY ":+ANON-DH"); + } + + ret = gnutls_priority_set_direct(session->handle, prio, NULL); if (ret < 0) { - error_setg(errp, "Unable to set TLS session priority: %s", - gnutls_strerror(ret)); + error_setg(errp, "Unable to set TLS session priority %s: %s", + prio, gnutls_strerror(ret)); + g_free(prio); goto error; } + g_free(prio); if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { ret = gnutls_credentials_set(session->handle, GNUTLS_CRD_ANON, @@ -157,11 +165,15 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds, } else if (object_dynamic_cast(OBJECT(creds), TYPE_QCRYPTO_TLS_CREDS_X509)) { QCryptoTLSCredsX509 *tcreds = QCRYPTO_TLS_CREDS_X509(creds); + const char *prio = creds->priority; + if (!prio) { + prio = CONFIG_TLS_PRIORITY; + } - ret = gnutls_set_default_priority(session->handle); + ret = gnutls_priority_set_direct(session->handle, prio, NULL); if (ret < 0) { - error_setg(errp, "Cannot set default TLS session priority: %s", - gnutls_strerror(ret)); + error_setg(errp, "Cannot set default TLS session priority %s: %s", + prio, gnutls_strerror(ret)); goto error; } ret = gnutls_credentials_set(session->handle, diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index c5bcba754a..7a19863b18 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -66,6 +66,7 @@ CONFIG_PXA2XX=y CONFIG_BITBANG_I2C=y CONFIG_FRAMEBUFFER=y CONFIG_XILINX_SPIPS=y +CONFIG_ZYNQ_DEVCFG=y CONFIG_ARM11SCU=y CONFIG_A9SCU=y diff --git a/docs/memory.txt b/docs/memory.txt index 431d9ca88f..811b1bd3c5 100644 --- a/docs/memory.txt +++ b/docs/memory.txt @@ -41,8 +41,13 @@ MemoryRegion): MemoryRegionOps structure describing the callbacks. - ROM: a ROM memory region works like RAM for reads (directly accessing - a region of host memory), but like MMIO for writes (invoking a callback). - You initialize these with memory_region_init_rom_device(). + a region of host memory), and forbids writes. You initialize these with + memory_region_init_rom(). + +- ROM device: a ROM device memory region works like RAM for reads + (directly accessing a region of host memory), but like MMIO for + writes (invoking a callback). You initialize these with + memory_region_init_rom_device(). - IOMMU region: an IOMMU region translates addresses of accesses made to it and forwards them to some other target memory region. As the name suggests, diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt index eff207502f..48b0b31f2e 100644 --- a/docs/qapi-code-gen.txt +++ b/docs/qapi-code-gen.txt @@ -802,32 +802,28 @@ Example: void qapi_free_UserDefOne(UserDefOne *obj) { - QapiDeallocVisitor *qdv; Visitor *v; if (!obj) { return; } - qdv = qapi_dealloc_visitor_new(); - v = qapi_dealloc_get_visitor(qdv); + v = qapi_dealloc_visitor_new(); visit_type_UserDefOne(v, NULL, &obj, NULL); - qapi_dealloc_visitor_cleanup(qdv); + visit_free(v); } void qapi_free_UserDefOneList(UserDefOneList *obj) { - QapiDeallocVisitor *qdv; Visitor *v; if (!obj) { return; } - qdv = qapi_dealloc_visitor_new(); - v = qapi_dealloc_get_visitor(qdv); + v = qapi_dealloc_visitor_new(); visit_type_UserDefOneList(v, NULL, &obj, NULL); - qapi_dealloc_visitor_cleanup(qdv); + visit_free(v); } === scripts/qapi-visit.py === @@ -904,7 +900,7 @@ Example: } visit_check_struct(v, &err); out_obj: - visit_end_struct(v); + visit_end_struct(v, (void **)obj); if (err && visit_is_input(v)) { qapi_free_UserDefOne(*obj); *obj = NULL; @@ -932,7 +928,7 @@ Example: } } - visit_end_list(v); + visit_end_list(v, (void **)obj); if (err && visit_is_input(v)) { qapi_free_UserDefOneList(*obj); *obj = NULL; @@ -984,36 +980,28 @@ Example: static void qmp_marshal_output_UserDefOne(UserDefOne *ret_in, QObject **ret_out, Error **errp) { Error *err = NULL; - QmpOutputVisitor *qov = qmp_output_visitor_new(); - QapiDeallocVisitor *qdv; Visitor *v; - v = qmp_output_get_visitor(qov); + v = qmp_output_visitor_new(ret_out); visit_type_UserDefOne(v, "unused", &ret_in, &err); - if (err) { - goto out; + if (!err) { + visit_complete(v, ret_out); } - *ret_out = qmp_output_get_qobject(qov); - - out: error_propagate(errp, err); - qmp_output_visitor_cleanup(qov); - qdv = qapi_dealloc_visitor_new(); - v = qapi_dealloc_get_visitor(qdv); + visit_free(v); + v = qapi_dealloc_visitor_new(); visit_type_UserDefOne(v, "unused", &ret_in, NULL); - qapi_dealloc_visitor_cleanup(qdv); + visit_free(v); } static void qmp_marshal_my_command(QDict *args, QObject **ret, Error **errp) { Error *err = NULL; UserDefOne *retval; - QmpInputVisitor *qiv = qmp_input_visitor_new(QOBJECT(args), true); - QapiDeallocVisitor *qdv; Visitor *v; UserDefOneList *arg1 = NULL; - v = qmp_input_get_visitor(qiv); + v = qmp_input_visitor_new(QOBJECT(args), true); visit_start_struct(v, NULL, NULL, 0, &err); if (err) { goto out; @@ -1022,7 +1010,7 @@ Example: if (!err) { visit_check_struct(v, &err); } - visit_end_struct(v); + visit_end_struct(v, NULL); if (err) { goto out; } @@ -1036,13 +1024,12 @@ Example: out: error_propagate(errp, err); - qmp_input_visitor_cleanup(qiv); - qdv = qapi_dealloc_visitor_new(); - v = qapi_dealloc_get_visitor(qdv); + visit_free(v); + v = qapi_dealloc_visitor_new(); visit_start_struct(v, NULL, NULL, 0, NULL); visit_type_UserDefOneList(v, "arg1", &arg1, NULL); - visit_end_struct(v); - qapi_dealloc_visitor_cleanup(qdv); + visit_end_struct(v, NULL); + visit_free(v); } static void qmp_init_marshal(void) diff --git a/docs/usb-storage.txt b/docs/usb-storage.txt index c5a3866eeb..fbc1f2edd8 100644 --- a/docs/usb-storage.txt +++ b/docs/usb-storage.txt @@ -40,6 +40,18 @@ numbers must be continuous, i.e. for three devices you must use 0+1+2. The 0+1+5 numbering from the "usb-uas" example isn't going to work with "usb-bot". +Starting with qemu version 2.7 usb-bot and usb-uas devices can be +hotplugged. In the hotplug case they are added with "attached = +false" so the guest will not see the device until the "attached" +property is explicitly set to true. That allows to attach one or more +scsi devices before making the device visible to the guest, i.e. the +workflow looks like this: + + (1) device-add usb-bot,id=foo + (2) device-add scsi-{hd,cd},bus=foo.0,lun=0 + (2b) optionally add more devices (luns 1 ... 15). + (3) scripts/qmp/qom-set foo.attached = true + enjoy, Gerd @@ -1722,7 +1722,7 @@ void hmp_object_add(Monitor *mon, const QDict *qdict) { Error *err = NULL; QemuOpts *opts; - OptsVisitor *ov; + Visitor *v; Object *obj = NULL; opts = qemu_opts_from_qdict(qemu_find_opts("object"), qdict, &err); @@ -1731,9 +1731,9 @@ void hmp_object_add(Monitor *mon, const QDict *qdict) return; } - ov = opts_visitor_new(opts); - obj = user_creatable_add(qdict, opts_get_visitor(ov), &err); - opts_visitor_cleanup(ov); + v = opts_visitor_new(opts); + obj = user_creatable_add(qdict, v, &err); + visit_free(v); qemu_opts_del(opts); if (err) { @@ -1983,15 +1983,14 @@ void hmp_info_memdev(Monitor *mon, const QDict *qdict) Error *err = NULL; MemdevList *memdev_list = qmp_query_memdev(&err); MemdevList *m = memdev_list; - StringOutputVisitor *ov; + Visitor *v; char *str; int i = 0; while (m) { - ov = string_output_visitor_new(false); - visit_type_uint16List(string_output_get_visitor(ov), NULL, - &m->value->host_nodes, NULL); + v = string_output_visitor_new(false, &str); + visit_type_uint16List(v, NULL, &m->value->host_nodes, NULL); monitor_printf(mon, "memory backend: %d\n", i); monitor_printf(mon, " size: %" PRId64 "\n", m->value->size); monitor_printf(mon, " merge: %s\n", @@ -2002,11 +2001,11 @@ void hmp_info_memdev(Monitor *mon, const QDict *qdict) m->value->prealloc ? "true" : "false"); monitor_printf(mon, " policy: %s\n", HostMemPolicy_lookup[m->value->policy]); - str = string_output_get_string(ov); + visit_complete(v, &str); monitor_printf(mon, " host nodes: %s\n", str); g_free(str); - string_output_visitor_cleanup(ov); + visit_free(v); m = m->next; i++; } diff --git a/hw/acpi/core.c b/hw/acpi/core.c index d24b9a98c8..e890a5d675 100644 --- a/hw/acpi/core.c +++ b/hw/acpi/core.c @@ -239,11 +239,11 @@ void acpi_table_add(const QemuOpts *opts, Error **errp) char unsigned *blob = NULL; { - OptsVisitor *ov; + Visitor *v; - ov = opts_visitor_new(opts); - visit_type_AcpiTableOptions(opts_get_visitor(ov), NULL, &hdrs, &err); - opts_visitor_cleanup(ov); + v = opts_visitor_new(opts); + visit_type_AcpiTableOptions(v, NULL, &hdrs, &err); + visit_free(v); } if (err) { diff --git a/hw/arm/ast2400.c b/hw/arm/ast2400.c index b14a82fcde..0555843620 100644 --- a/hw/arm/ast2400.c +++ b/hw/arm/ast2400.c @@ -23,11 +23,17 @@ #define AST2400_UART_5_BASE 0x00184000 #define AST2400_IOMEM_SIZE 0x00200000 #define AST2400_IOMEM_BASE 0x1E600000 +#define AST2400_SMC_BASE AST2400_IOMEM_BASE /* Legacy SMC */ +#define AST2400_FMC_BASE 0X1E620000 +#define AST2400_SPI_BASE 0X1E630000 #define AST2400_VIC_BASE 0x1E6C0000 #define AST2400_SCU_BASE 0x1E6E2000 #define AST2400_TIMER_BASE 0x1E782000 #define AST2400_I2C_BASE 0x1E78A000 +#define AST2400_FMC_FLASH_BASE 0x20000000 +#define AST2400_SPI_FLASH_BASE 0x30000000 + #define AST2400_A0_SILICON_REV 0x02000303 static const int uart_irqs[] = { 9, 32, 33, 34, 10 }; @@ -85,13 +91,21 @@ static void ast2400_init(Object *obj) "hw-strap1", &error_abort); object_property_add_alias(obj, "hw-strap2", OBJECT(&s->scu), "hw-strap2", &error_abort); + + object_initialize(&s->smc, sizeof(s->smc), "aspeed.smc.fmc"); + object_property_add_child(obj, "smc", OBJECT(&s->smc), NULL); + qdev_set_parent_bus(DEVICE(&s->smc), sysbus_get_default()); + + object_initialize(&s->spi, sizeof(s->spi), "aspeed.smc.spi"); + object_property_add_child(obj, "spi", OBJECT(&s->spi), NULL); + qdev_set_parent_bus(DEVICE(&s->spi), sysbus_get_default()); } static void ast2400_realize(DeviceState *dev, Error **errp) { int i; AST2400State *s = AST2400(dev); - Error *err = NULL; + Error *err = NULL, *local_err = NULL; /* IO space */ memory_region_init_io(&s->iomem, NULL, &ast2400_io_ops, NULL, @@ -147,6 +161,30 @@ static void ast2400_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c), 0, AST2400_I2C_BASE); sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c), 0, qdev_get_gpio_in(DEVICE(&s->vic), 12)); + + /* SMC */ + object_property_set_int(OBJECT(&s->smc), 1, "num-cs", &err); + object_property_set_bool(OBJECT(&s->smc), true, "realized", &local_err); + error_propagate(&err, local_err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->smc), 0, AST2400_FMC_BASE); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->smc), 1, AST2400_FMC_FLASH_BASE); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->smc), 0, + qdev_get_gpio_in(DEVICE(&s->vic), 19)); + + /* SPI */ + object_property_set_int(OBJECT(&s->spi), 1, "num-cs", &err); + object_property_set_bool(OBJECT(&s->spi), true, "realized", &local_err); + error_propagate(&err, local_err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi), 0, AST2400_SPI_BASE); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi), 1, AST2400_SPI_FLASH_BASE); } static void ast2400_class_init(ObjectClass *oc, void *data) diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c index 1cd749aa4b..b4e358db65 100644 --- a/hw/arm/fsl-imx25.c +++ b/hw/arm/fsl-imx25.c @@ -51,7 +51,7 @@ static void fsl_imx25_init(Object *obj) } for (i = 0; i < FSL_IMX25_NUM_GPTS; i++) { - object_initialize(&s->gpt[i], sizeof(s->gpt[i]), TYPE_IMX_GPT); + object_initialize(&s->gpt[i], sizeof(s->gpt[i]), TYPE_IMX25_GPT); qdev_set_parent_bus(DEVICE(&s->gpt[i]), sysbus_get_default()); } @@ -249,16 +249,16 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp) } /* initialize 2 x 16 KB ROM */ - memory_region_init_rom_device(&s->rom[0], NULL, NULL, NULL, - "imx25.rom0", FSL_IMX25_ROM0_SIZE, &err); + memory_region_init_rom(&s->rom[0], NULL, + "imx25.rom0", FSL_IMX25_ROM0_SIZE, &err); if (err) { error_propagate(errp, err); return; } memory_region_add_subregion(get_system_memory(), FSL_IMX25_ROM0_ADDR, &s->rom[0]); - memory_region_init_rom_device(&s->rom[1], NULL, NULL, NULL, - "imx25.rom1", FSL_IMX25_ROM1_SIZE, &err); + memory_region_init_rom(&s->rom[1], NULL, + "imx25.rom1", FSL_IMX25_ROM1_SIZE, &err); if (err) { error_propagate(errp, err); return; diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c index 31a3a87911..fe204ace62 100644 --- a/hw/arm/fsl-imx31.c +++ b/hw/arm/fsl-imx31.c @@ -47,7 +47,7 @@ static void fsl_imx31_init(Object *obj) qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus_get_default()); } - object_initialize(&s->gpt, sizeof(s->gpt), TYPE_IMX_GPT); + object_initialize(&s->gpt, sizeof(s->gpt), TYPE_IMX31_GPT); qdev_set_parent_bus(DEVICE(&s->gpt), sysbus_get_default()); for (i = 0; i < FSL_IMX31_NUM_EPITS; i++) { @@ -219,9 +219,8 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp) } /* On a real system, the first 16k is a `secure boot rom' */ - memory_region_init_rom_device(&s->secure_rom, NULL, NULL, NULL, - "imx31.secure_rom", - FSL_IMX31_SECURE_ROM_SIZE, &err); + memory_region_init_rom(&s->secure_rom, NULL, "imx31.secure_rom", + FSL_IMX31_SECURE_ROM_SIZE, &err); if (err) { error_propagate(errp, err); return; @@ -230,8 +229,8 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp) &s->secure_rom); /* There is also a 16k ROM */ - memory_region_init_rom_device(&s->rom, NULL, NULL, NULL, "imx31.rom", - FSL_IMX31_ROM_SIZE, &err); + memory_region_init_rom(&s->rom, NULL, "imx31.rom", + FSL_IMX31_ROM_SIZE, &err); if (err) { error_propagate(errp, err); return; diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c index 0c00e7a560..6a1bf263a5 100644 --- a/hw/arm/fsl-imx6.c +++ b/hw/arm/fsl-imx6.c @@ -67,7 +67,7 @@ static void fsl_imx6_init(Object *obj) object_property_add_child(obj, name, OBJECT(&s->uart[i]), NULL); } - object_initialize(&s->gpt, sizeof(s->gpt), TYPE_IMX_GPT); + object_initialize(&s->gpt, sizeof(s->gpt), TYPE_IMX6_GPT); qdev_set_parent_bus(DEVICE(&s->gpt), sysbus_get_default()); object_property_add_child(obj, "gpt", OBJECT(&s->gpt), NULL); @@ -399,8 +399,8 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) FSL_IMX6_ENET_MAC_1588_IRQ)); /* ROM memory */ - memory_region_init_rom_device(&s->rom, NULL, NULL, NULL, "imx6.rom", - FSL_IMX6_ROM_SIZE, &err); + memory_region_init_rom(&s->rom, NULL, "imx6.rom", + FSL_IMX6_ROM_SIZE, &err); if (err) { error_propagate(errp, err); return; @@ -409,8 +409,8 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) &s->rom); /* CAAM memory */ - memory_region_init_rom_device(&s->caam, NULL, NULL, NULL, "imx6.caam", - FSL_IMX6_CAAM_MEM_SIZE, &err); + memory_region_init_rom(&s->caam, NULL, "imx6.caam", + FSL_IMX6_CAAM_MEM_SIZE, &err); if (err) { error_propagate(errp, err); return; diff --git a/hw/arm/palmetto-bmc.c b/hw/arm/palmetto-bmc.c index b8eed21348..54e29a865d 100644 --- a/hw/arm/palmetto-bmc.c +++ b/hw/arm/palmetto-bmc.c @@ -18,6 +18,8 @@ #include "hw/arm/ast2400.h" #include "hw/boards.h" #include "qemu/log.h" +#include "sysemu/block-backend.h" +#include "sysemu/blockdev.h" static struct arm_boot_info palmetto_bmc_binfo = { .loader_start = AST2400_SDRAM_BASE, @@ -30,6 +32,32 @@ typedef struct PalmettoBMCState { MemoryRegion ram; } PalmettoBMCState; +static void palmetto_bmc_init_flashes(AspeedSMCState *s, const char *flashtype, + Error **errp) +{ + int i ; + + for (i = 0; i < s->num_cs; ++i) { + AspeedSMCFlash *fl = &s->flashes[i]; + DriveInfo *dinfo = drive_get_next(IF_MTD); + qemu_irq cs_line; + + /* + * FIXME: check that we are not using a flash module exceeding + * the controller segment size + */ + fl->flash = ssi_create_slave_no_init(s->spi, flashtype); + if (dinfo) { + qdev_prop_set_drive(fl->flash, "drive", blk_by_legacy_dinfo(dinfo), + errp); + } + qdev_init_nofail(fl->flash); + + cs_line = qdev_get_gpio_in_named(fl->flash, SSI_GPIO_CS, 0); + sysbus_connect_irq(SYS_BUS_DEVICE(s), i + 1, cs_line); + } +} + static void palmetto_bmc_init(MachineState *machine) { PalmettoBMCState *bmc; @@ -49,6 +77,9 @@ static void palmetto_bmc_init(MachineState *machine) object_property_set_bool(OBJECT(&bmc->soc), true, "realized", &error_abort); + palmetto_bmc_init_flashes(&bmc->soc.smc, "n25q256a", &error_abort); + palmetto_bmc_init_flashes(&bmc->soc.spi, "mx25l25635e", &error_abort); + palmetto_bmc_binfo.kernel_filename = machine->kernel_filename; palmetto_bmc_binfo.initrd_filename = machine->initrd_filename; palmetto_bmc_binfo.kernel_cmdline = machine->kernel_cmdline; diff --git a/hw/arm/sabrelite.c b/hw/arm/sabrelite.c index 776c51e398..4e7ac8cc4f 100644 --- a/hw/arm/sabrelite.c +++ b/hw/arm/sabrelite.c @@ -86,13 +86,19 @@ static void sabrelite_init(MachineState *machine) spi_bus = (SSIBus *)qdev_get_child_bus(DEVICE(spi_dev), "spi"); if (spi_bus) { DeviceState *flash_dev; - - flash_dev = ssi_create_slave(spi_bus, "sst25vf016b"); - if (flash_dev) { - qemu_irq cs_line = qdev_get_gpio_in_named(flash_dev, - SSI_GPIO_CS, 0); - sysbus_connect_irq(SYS_BUS_DEVICE(spi_dev), 1, cs_line); + qemu_irq cs_line; + DriveInfo *dinfo = drive_get_next(IF_MTD); + + flash_dev = ssi_create_slave_no_init(spi_bus, "sst25vf016b"); + if (dinfo) { + qdev_prop_set_drive(flash_dev, "drive", + blk_by_legacy_dinfo(dinfo), + &error_fatal); } + qdev_init_nofail(flash_dev); + + cs_line = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0); + sysbus_connect_irq(SYS_BUS_DEVICE(spi_dev), 1, cs_line); } } } diff --git a/hw/arm/spitz.c b/hw/arm/spitz.c index ba40f8302b..41cc2eeeb1 100644 --- a/hw/arm/spitz.c +++ b/hw/arm/spitz.c @@ -598,15 +598,13 @@ static uint32_t spitz_lcdtg_transfer(SSISlave *dev, uint32_t value) return 0; } -static int spitz_lcdtg_init(SSISlave *dev) +static void spitz_lcdtg_realize(SSISlave *dev, Error **errp) { SpitzLCDTG *s = FROM_SSI_SLAVE(SpitzLCDTG, dev); spitz_lcdtg = s; s->bl_power = 0; s->bl_intensity = 0x20; - - return 0; } /* SSP devices */ @@ -666,7 +664,7 @@ static void spitz_adc_temp_on(void *opaque, int line, int level) max111x_set_input(max1111, MAX1111_BATT_TEMP, 0); } -static int corgi_ssp_init(SSISlave *d) +static void corgi_ssp_realize(SSISlave *d, Error **errp) { DeviceState *dev = DEVICE(d); CorgiSSPState *s = FROM_SSI_SLAVE(CorgiSSPState, d); @@ -675,8 +673,6 @@ static int corgi_ssp_init(SSISlave *d) s->bus[0] = ssi_create_bus(dev, "ssi0"); s->bus[1] = ssi_create_bus(dev, "ssi1"); s->bus[2] = ssi_create_bus(dev, "ssi2"); - - return 0; } static void spitz_ssp_attach(PXA2xxState *cpu) @@ -1121,7 +1117,7 @@ static void corgi_ssp_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); SSISlaveClass *k = SSI_SLAVE_CLASS(klass); - k->init = corgi_ssp_init; + k->realize = corgi_ssp_realize; k->transfer = corgi_ssp_transfer; dc->vmsd = &vmstate_corgi_ssp_regs; } @@ -1150,7 +1146,7 @@ static void spitz_lcdtg_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); SSISlaveClass *k = SSI_SLAVE_CLASS(klass); - k->init = spitz_lcdtg_init; + k->realize = spitz_lcdtg_realize; k->transfer = spitz_lcdtg_transfer; dc->vmsd = &vmstate_spitz_lcdtg_regs; } diff --git a/hw/arm/tosa.c b/hw/arm/tosa.c index 4e9494f94c..2db66508b5 100644 --- a/hw/arm/tosa.c +++ b/hw/arm/tosa.c @@ -127,10 +127,9 @@ static uint32_t tosa_ssp_tansfer(SSISlave *dev, uint32_t value) return 0; } -static int tosa_ssp_init(SSISlave *dev) +static void tosa_ssp_realize(SSISlave *dev, Error **errp) { /* Nothing to do. */ - return 0; } #define TYPE_TOSA_DAC "tosa_dac" @@ -283,7 +282,7 @@ static void tosa_ssp_class_init(ObjectClass *klass, void *data) { SSISlaveClass *k = SSI_SLAVE_CLASS(klass); - k->init = tosa_ssp_init; + k->realize = tosa_ssp_realize; k->transfer = tosa_ssp_tansfer; } diff --git a/hw/arm/virt.c b/hw/arm/virt.c index c5c125e920..4dafd42be8 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1021,6 +1021,7 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic, qemu_fdt_setprop_cell(vbi->fdt, nodename, "#size-cells", 2); qemu_fdt_setprop_cells(vbi->fdt, nodename, "bus-range", 0, nr_pcie_buses - 1); + qemu_fdt_setprop(vbi->fdt, nodename, "dma-coherent", NULL, 0); if (vbi->v2m_phandle) { qemu_fdt_setprop_cells(vbi->fdt, nodename, "msi-parent", @@ -1175,6 +1176,10 @@ static void machvirt_init(MachineState *machine) VirtGuestInfoState *guest_info_state = g_malloc0(sizeof *guest_info_state); VirtGuestInfo *guest_info = &guest_info_state->info; char **cpustr; + ObjectClass *oc; + const char *typename; + CPUClass *cc; + Error *err = NULL; bool firmware_loaded = bios_name || drive_get(IF_PFLASH, 0, 0); if (!cpu_model) { @@ -1258,26 +1263,24 @@ static void machvirt_init(MachineState *machine) create_fdt(vbi); - for (n = 0; n < smp_cpus; n++) { - ObjectClass *oc = cpu_class_by_name(TYPE_ARM_CPU, cpustr[0]); - CPUClass *cc = CPU_CLASS(oc); - Object *cpuobj; - Error *err = NULL; - char *cpuopts = g_strdup(cpustr[1]); - - if (!oc) { - error_report("Unable to find CPU definition"); - exit(1); - } - cpuobj = object_new(object_class_get_name(oc)); + oc = cpu_class_by_name(TYPE_ARM_CPU, cpustr[0]); + if (!oc) { + error_report("Unable to find CPU definition"); + exit(1); + } + typename = object_class_get_name(oc); - /* Handle any CPU options specified by the user */ - cc->parse_features(CPU(cpuobj), cpuopts, &err); - g_free(cpuopts); - if (err) { - error_report_err(err); - exit(1); - } + /* convert -smp CPU options specified by the user into global props */ + cc = CPU_CLASS(oc); + cc->parse_features(typename, cpustr[1], &err); + g_strfreev(cpustr); + if (err) { + error_report_err(err); + exit(1); + } + + for (n = 0; n < smp_cpus; n++) { + Object *cpuobj = object_new(typename); if (!vms->secure) { object_property_set_bool(cpuobj, false, "has_el3", NULL); @@ -1308,7 +1311,6 @@ static void machvirt_init(MachineState *machine) object_property_set_bool(cpuobj, true, "realized", NULL); } - g_strfreev(cpustr); fdt_add_timer_nodes(vbi, gic_version); fdt_add_cpu_nodes(vbi); fdt_add_psci_node(vbi); diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index aefebcfa6d..7dac20d67d 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -138,7 +138,13 @@ static inline void zynq_init_spi_flashes(uint32_t base_addr, qemu_irq irq, spi = (SSIBus *)qdev_get_child_bus(dev, bus_name); for (j = 0; j < num_ss; ++j) { - flash_dev = ssi_create_slave(spi, "n25q128"); + DriveInfo *dinfo = drive_get_next(IF_MTD); + flash_dev = ssi_create_slave_no_init(spi, "n25q128"); + if (dinfo) { + qdev_prop_set_drive(flash_dev, "drive", + blk_by_legacy_dinfo(dinfo), &error_fatal); + } + qdev_init_nofail(flash_dev); cs_line = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0); sysbus_connect_irq(busdev, i * num_ss + j + 1, cs_line); @@ -294,6 +300,12 @@ static void zynq_init(MachineState *machine) sysbus_connect_irq(busdev, n + 1, pic[dma_irqs[n] - IRQ_OFFSET]); } + dev = qdev_create(NULL, "xlnx.ps7-dev-cfg"); + qdev_init_nofail(dev); + busdev = SYS_BUS_DEVICE(dev); + sysbus_connect_irq(busdev, 0, pic[40 - IRQ_OFFSET]); + sysbus_mmio_map(busdev, 0, 0xF8007000); + zynq_binfo.ram_size = ram_size; zynq_binfo.kernel_filename = kernel_filename; zynq_binfo.kernel_cmdline = kernel_cmdline; diff --git a/hw/arm/xlnx-ep108.c b/hw/arm/xlnx-ep108.c index 34b4641712..4ec590a25d 100644 --- a/hw/arm/xlnx-ep108.c +++ b/hw/arm/xlnx-ep108.c @@ -88,12 +88,19 @@ static void xlnx_ep108_init(MachineState *machine) SSIBus *spi_bus; DeviceState *flash_dev; qemu_irq cs_line; + DriveInfo *dinfo = drive_get_next(IF_MTD); gchar *bus_name = g_strdup_printf("spi%d", i); spi_bus = (SSIBus *)qdev_get_child_bus(DEVICE(&s->soc), bus_name); g_free(bus_name); - flash_dev = ssi_create_slave(spi_bus, "sst25wf080"); + flash_dev = ssi_create_slave_no_init(spi_bus, "sst25wf080"); + if (dinfo) { + qdev_prop_set_drive(flash_dev, "drive", blk_by_legacy_dinfo(dinfo), + &error_fatal); + } + qdev_init_nofail(flash_dev); + cs_line = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0); sysbus_connect_irq(SYS_BUS_DEVICE(&s->soc.spi[i]), 1, cs_line); diff --git a/hw/arm/z2.c b/hw/arm/z2.c index aea895a500..68a92f3184 100644 --- a/hw/arm/z2.c +++ b/hw/arm/z2.c @@ -151,14 +151,12 @@ static void z2_lcd_cs(void *opaque, int line, int level) z2_lcd->selected = !level; } -static int zipit_lcd_init(SSISlave *dev) +static void zipit_lcd_realize(SSISlave *dev, Error **errp) { ZipitLCD *z = FROM_SSI_SLAVE(ZipitLCD, dev); z->selected = 0; z->enabled = 0; z->pos = 0; - - return 0; } static VMStateDescription vmstate_zipit_lcd_state = { @@ -181,7 +179,7 @@ static void zipit_lcd_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); SSISlaveClass *k = SSI_SLAVE_CLASS(klass); - k->init = zipit_lcd_init; + k->realize = zipit_lcd_realize; k->transfer = zipit_lcd_transfer; dc->vmsd = &vmstate_zipit_lcd_state; } diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c index 098b17d020..cd95340cd9 100644 --- a/hw/audio/intel-hda.c +++ b/hw/audio/intel-hda.c @@ -191,7 +191,7 @@ struct IntelHDAState { /* properties */ uint32_t debug; - uint32_t msi; + OnOffAuto msi; bool old_msi_addr; }; @@ -256,7 +256,7 @@ static void intel_hda_update_int_sts(IntelHDAState *d) static void intel_hda_update_irq(IntelHDAState *d) { - int msi = d->msi && msi_enabled(&d->pci); + bool msi = msi_enabled(&d->pci); int level; intel_hda_update_int_sts(d); @@ -1132,6 +1132,8 @@ static void intel_hda_realize(PCIDevice *pci, Error **errp) { IntelHDAState *d = INTEL_HDA(pci); uint8_t *conf = d->pci.config; + Error *err = NULL; + int ret; d->name = object_get_typename(OBJECT(d)); @@ -1140,12 +1142,27 @@ static void intel_hda_realize(PCIDevice *pci, Error **errp) /* HDCTL off 0x40 bit 0 selects signaling mode (1-HDA, 0 - Ac97) 18.1.19 */ conf[0x40] = 0x01; + if (d->msi != ON_OFF_AUTO_OFF) { + ret = msi_init(&d->pci, d->old_msi_addr ? 0x50 : 0x60, + 1, true, false, &err); + /* Any error other than -ENOTSUP(board's MSI support is broken) + * is a programming error */ + assert(!ret || ret == -ENOTSUP); + if (ret && d->msi == ON_OFF_AUTO_ON) { + /* Can't satisfy user's explicit msi=on request, fail */ + error_append_hint(&err, "You have to use msi=auto (default) or " + "msi=off with this machine type.\n"); + error_propagate(errp, err); + return; + } + assert(!err || d->msi == ON_OFF_AUTO_AUTO); + /* With msi=auto, we fall back to MSI off silently */ + error_free(err); + } + memory_region_init_io(&d->mmio, OBJECT(d), &intel_hda_mmio_ops, d, "intel-hda", 0x4000); pci_register_bar(&d->pci, 0, 0, &d->mmio); - if (d->msi) { - msi_init(&d->pci, d->old_msi_addr ? 0x50 : 0x60, 1, true, false); - } hda_codec_bus_init(DEVICE(pci), &d->codecs, sizeof(d->codecs), intel_hda_response, intel_hda_xfer); @@ -1235,7 +1252,7 @@ static const VMStateDescription vmstate_intel_hda = { static Property intel_hda_properties[] = { DEFINE_PROP_UINT32("debug", IntelHDAState, debug, 0), - DEFINE_PROP_UINT32("msi", IntelHDAState, msi, 1), + DEFINE_PROP_ON_OFF_AUTO("msi", IntelHDAState, msi, ON_OFF_AUTO_AUTO), DEFINE_PROP_BOOL("old_msi_addr", IntelHDAState, old_msi_addr, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index 326b688e83..ca8c12c0f8 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -28,6 +28,7 @@ #include "hw/ssi/ssi.h" #include "qemu/bitops.h" #include "qemu/log.h" +#include "qapi/error.h" #ifndef M25P80_ERR_DEBUG #define M25P80_ERR_DEBUG 0 @@ -389,7 +390,7 @@ typedef struct Flash { uint32_t pos; uint8_t needed_bytes; uint8_t cmd_in_progress; - uint64_t cur_addr; + uint32_t cur_addr; uint32_t nonvolatile_cfg; /* Configuration register for Macronix */ uint32_t volatile_cfg; @@ -446,6 +447,11 @@ static inline Manufacturer get_man(Flash *s) static void blk_sync_complete(void *opaque, int ret) { + QEMUIOVector *iov = opaque; + + qemu_iovec_destroy(iov); + g_free(iov); + /* do nothing. Masters do not directly interact with the backing store, * only the working copy so no mutexing required. */ @@ -453,31 +459,33 @@ static void blk_sync_complete(void *opaque, int ret) static void flash_sync_page(Flash *s, int page) { - QEMUIOVector iov; + QEMUIOVector *iov; if (!s->blk || blk_is_read_only(s->blk)) { return; } - qemu_iovec_init(&iov, 1); - qemu_iovec_add(&iov, s->storage + page * s->pi->page_size, + iov = g_new(QEMUIOVector, 1); + qemu_iovec_init(iov, 1); + qemu_iovec_add(iov, s->storage + page * s->pi->page_size, s->pi->page_size); - blk_aio_pwritev(s->blk, page * s->pi->page_size, &iov, 0, - blk_sync_complete, NULL); + blk_aio_pwritev(s->blk, page * s->pi->page_size, iov, 0, + blk_sync_complete, iov); } static inline void flash_sync_area(Flash *s, int64_t off, int64_t len) { - QEMUIOVector iov; + QEMUIOVector *iov; if (!s->blk || blk_is_read_only(s->blk)) { return; } assert(!(len % BDRV_SECTOR_SIZE)); - qemu_iovec_init(&iov, 1); - qemu_iovec_add(&iov, s->storage + off, len); - blk_aio_pwritev(s->blk, off, &iov, 0, blk_sync_complete, NULL); + iov = g_new(QEMUIOVector, 1); + qemu_iovec_init(iov, 1); + qemu_iovec_add(iov, s->storage + off, len); + blk_aio_pwritev(s->blk, off, iov, 0, blk_sync_complete, iov); } static void flash_erase(Flash *s, int offset, FlashCMD cmd) @@ -530,9 +538,9 @@ static inline void flash_sync_dirty(Flash *s, int64_t newpage) } static inline -void flash_write8(Flash *s, uint64_t addr, uint8_t data) +void flash_write8(Flash *s, uint32_t addr, uint8_t data) { - int64_t page = addr / s->pi->page_size; + uint32_t page = addr / s->pi->page_size; uint8_t prev = s->storage[s->cur_addr]; if (!s->write_enable) { @@ -540,7 +548,7 @@ void flash_write8(Flash *s, uint64_t addr, uint8_t data) } if ((prev ^ data) & data) { - DB_PRINT_L(1, "programming zero to one! addr=%" PRIx64 " %" PRIx8 + DB_PRINT_L(1, "programming zero to one! addr=%" PRIx32 " %" PRIx8 " -> %" PRIx8 "\n", addr, prev, data); } @@ -581,18 +589,16 @@ static inline int get_addr_length(Flash *s) static void complete_collecting_data(Flash *s) { - int i; - - s->cur_addr = 0; + int i, n; - for (i = 0; i < get_addr_length(s); ++i) { + n = get_addr_length(s); + s->cur_addr = (n == 3 ? s->ear : 0); + for (i = 0; i < n; ++i) { s->cur_addr <<= 8; s->cur_addr |= s->data[i]; } - if (get_addr_length(s) == 3) { - s->cur_addr += s->ear * MAX_3BYTES_SIZE; - } + s->cur_addr &= s->size - 1; s->state = STATE_IDLE; @@ -1091,17 +1097,17 @@ static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx) switch (s->state) { case STATE_PAGE_PROGRAM: - DB_PRINT_L(1, "page program cur_addr=%#" PRIx64 " data=%" PRIx8 "\n", + DB_PRINT_L(1, "page program cur_addr=%#" PRIx32 " data=%" PRIx8 "\n", s->cur_addr, (uint8_t)tx); flash_write8(s, s->cur_addr, (uint8_t)tx); - s->cur_addr++; + s->cur_addr = (s->cur_addr + 1) & (s->size - 1); break; case STATE_READ: r = s->storage[s->cur_addr]; - DB_PRINT_L(1, "READ 0x%" PRIx64 "=%" PRIx8 "\n", s->cur_addr, + DB_PRINT_L(1, "READ 0x%" PRIx32 "=%" PRIx8 "\n", s->cur_addr, (uint8_t)r); - s->cur_addr = (s->cur_addr + 1) % s->size; + s->cur_addr = (s->cur_addr + 1) & (s->size - 1); break; case STATE_COLLECTING_DATA: @@ -1132,9 +1138,8 @@ static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx) return r; } -static int m25p80_init(SSISlave *ss) +static void m25p80_realize(SSISlave *ss, Error **errp) { - DriveInfo *dinfo; Flash *s = M25P80(ss); M25P80Class *mc = M25P80_GET_CLASS(s); @@ -1143,28 +1148,19 @@ static int m25p80_init(SSISlave *ss) s->size = s->pi->sector_size * s->pi->n_sectors; s->dirty_page = -1; - /* FIXME use a qdev drive property instead of drive_get_next() */ - dinfo = drive_get_next(IF_MTD); - - if (dinfo) { + if (s->blk) { DB_PRINT_L(0, "Binding to IF_MTD drive\n"); - s->blk = blk_by_legacy_dinfo(dinfo); - blk_attach_dev_nofail(s->blk, s); - s->storage = blk_blockalign(s->blk, s->size); - /* FIXME: Move to late init */ if (blk_pread(s->blk, 0, s->storage, s->size) != s->size) { - fprintf(stderr, "Failed to initialize SPI flash!\n"); - return 1; + error_setg(errp, "failed to read the initial flash content"); + return; } } else { DB_PRINT_L(0, "No BDRV - binding to RAM\n"); s->storage = blk_blockalign(NULL, s->size); memset(s->storage, 0xFF, s->size); } - - return 0; } static void m25p80_reset(DeviceState *d) @@ -1186,6 +1182,7 @@ static Property m25p80_properties[] = { DEFINE_PROP_UINT8("spansion-cr2nv", Flash, spansion_cr2nv, 0x8), DEFINE_PROP_UINT8("spansion-cr3nv", Flash, spansion_cr3nv, 0x2), DEFINE_PROP_UINT8("spansion-cr4nv", Flash, spansion_cr4nv, 0x10), + DEFINE_PROP_DRIVE("drive", Flash, blk), DEFINE_PROP_END_OF_LIST(), }; @@ -1201,7 +1198,8 @@ static const VMStateDescription vmstate_m25p80 = { VMSTATE_UINT32(pos, Flash), VMSTATE_UINT8(needed_bytes, Flash), VMSTATE_UINT8(cmd_in_progress, Flash), - VMSTATE_UINT64(cur_addr, Flash), + VMSTATE_UNUSED(4), + VMSTATE_UINT32(cur_addr, Flash), VMSTATE_BOOL(write_enable, Flash), VMSTATE_BOOL_V(reset_enable, Flash, 2), VMSTATE_UINT8_V(ear, Flash, 2), @@ -1224,7 +1222,7 @@ static void m25p80_class_init(ObjectClass *klass, void *data) SSISlaveClass *k = SSI_SLAVE_CLASS(klass); M25P80Class *mc = M25P80_CLASS(klass); - k->init = m25p80_init; + k->realize = m25p80_realize; k->transfer = m25p80_transfer8; k->set_cs = m25p80_cs; k->cs_polarity = SSI_CS_LOW; diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index fb43bbaa46..ae86e944ea 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -384,7 +384,7 @@ static int multireq_compare(const void *a, const void *b) void virtio_blk_submit_multireq(BlockBackend *blk, MultiReqBuffer *mrb) { int i = 0, start = 0, num_reqs = 0, niov = 0, nb_sectors = 0; - int max_xfer_len = 0; + uint32_t max_transfer; int64_t sector_num = 0; if (mrb->num_reqs == 1) { @@ -393,8 +393,7 @@ void virtio_blk_submit_multireq(BlockBackend *blk, MultiReqBuffer *mrb) return; } - max_xfer_len = blk_get_max_transfer_length(mrb->reqs[0]->dev->blk); - max_xfer_len = MIN_NON_ZERO(max_xfer_len, BDRV_REQUEST_MAX_SECTORS); + max_transfer = blk_get_max_transfer(mrb->reqs[0]->dev->blk); qsort(mrb->reqs, mrb->num_reqs, sizeof(*mrb->reqs), &multireq_compare); @@ -410,8 +409,9 @@ void virtio_blk_submit_multireq(BlockBackend *blk, MultiReqBuffer *mrb) */ if (sector_num + nb_sectors != req->sector_num || niov > blk_get_max_iov(blk) - req->qiov.niov || - req->qiov.size / BDRV_SECTOR_SIZE > max_xfer_len || - nb_sectors > max_xfer_len - req->qiov.size / BDRV_SECTOR_SIZE) { + req->qiov.size > max_transfer || + nb_sectors > (max_transfer - + req->qiov.size) / BDRV_SECTOR_SIZE) { submit_requests(blk, mrb, start, num_reqs, niov); num_reqs = 0; } diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs index 82a9ef84f8..cfd4840397 100644 --- a/hw/core/Makefile.objs +++ b/hw/core/Makefile.objs @@ -15,4 +15,5 @@ common-obj-$(CONFIG_SOFTMMU) += machine.o common-obj-$(CONFIG_SOFTMMU) += null-machine.o common-obj-$(CONFIG_SOFTMMU) += loader.o common-obj-$(CONFIG_SOFTMMU) += qdev-properties-system.o +common-obj-$(CONFIG_SOFTMMU) += register.o common-obj-$(CONFIG_PLATFORM_BUS) += platform-bus.o diff --git a/hw/core/machine.c b/hw/core/machine.c index ccdd5fa3e7..2fe6ff6f30 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -300,20 +300,6 @@ static void machine_set_firmware(Object *obj, const char *value, Error **errp) ms->firmware = g_strdup(value); } -static bool machine_get_iommu(Object *obj, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - return ms->iommu; -} - -static void machine_set_iommu(Object *obj, bool value, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - ms->iommu = value; -} - static void machine_set_suppress_vmdesc(Object *obj, bool value, Error **errp) { MachineState *ms = MACHINE(obj); @@ -493,12 +479,6 @@ static void machine_initfn(Object *obj) object_property_set_description(obj, "firmware", "Firmware image", NULL); - object_property_add_bool(obj, "iommu", - machine_get_iommu, - machine_set_iommu, NULL); - object_property_set_description(obj, "iommu", - "Set on/off to enable/disable Intel IOMMU (VT-d)", - NULL); object_property_add_bool(obj, "suppress-vmdesc", machine_get_suppress_vmdesc, machine_set_suppress_vmdesc, NULL); @@ -580,6 +560,24 @@ static void machine_class_finalize(ObjectClass *klass, void *data) } } +void machine_register_compat_props(MachineState *machine) +{ + MachineClass *mc = MACHINE_GET_CLASS(machine); + int i; + GlobalProperty *p; + + if (!mc->compat_props) { + return; + } + + for (i = 0; i < mc->compat_props->len; i++) { + p = g_array_index(mc->compat_props, GlobalProperty *, i); + /* Machine compat_props must never cause errors: */ + p->errp = &error_abort; + qdev_prop_register_global(p); + } +} + static const TypeInfo machine_info = { .name = TYPE_MACHINE, .parent = TYPE_OBJECT, diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index 891219ae05..65d9fa9f53 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -1,5 +1,5 @@ /* - * qdev property parsing and global properties + * qdev property parsing * (parts specific for qemu-system-*) * * This file is based on code from hw/qdev-properties.c from @@ -82,7 +82,7 @@ static void parse_drive(DeviceState *dev, const char *str, void **ptr, if (blk_attach_dev(blk, dev) < 0) { DriveInfo *dinfo = blk_legacy_dinfo(blk); - if (dinfo->type != IF_NONE) { + if (dinfo && dinfo->type != IF_NONE) { error_setg(errp, "Drive '%s' is already in use because " "it has been automatically connected to another " "device (did you need 'if=none' in the drive options?)", @@ -394,22 +394,3 @@ void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd) } nd->instantiated = 1; } - -static int qdev_add_one_global(void *opaque, QemuOpts *opts, Error **errp) -{ - GlobalProperty *g; - - g = g_malloc0(sizeof(*g)); - g->driver = qemu_opt_get(opts, "driver"); - g->property = qemu_opt_get(opts, "property"); - g->value = qemu_opt_get(opts, "value"); - g->user_provided = true; - qdev_prop_register_global(g); - return 0; -} - -void qemu_add_globals(void) -{ - qemu_opts_foreach(qemu_find_opts("global"), - qdev_add_one_global, NULL, NULL); -} diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index e3b2184a60..3c20c8e4b2 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -1085,10 +1085,14 @@ static void qdev_prop_set_globals_for_type(DeviceState *dev, prop->used = true; object_property_parse(OBJECT(dev), prop->value, prop->property, &err); if (err != NULL) { - assert(prop->user_provided); - error_reportf_err(err, "Warning: global %s.%s=%s ignored: ", - prop->driver, prop->property, prop->value); - return; + error_prepend(&err, "can't apply global %s.%s=%s: ", + prop->driver, prop->property, prop->value); + if (prop->errp) { + error_propagate(prop->errp, err); + } else { + assert(prop->user_provided); + error_reportf_err(err, "Warning: "); + } } } } diff --git a/hw/core/register.c b/hw/core/register.c new file mode 100644 index 0000000000..4bfbc508de --- /dev/null +++ b/hw/core/register.c @@ -0,0 +1,287 @@ +/* + * Register Definition API + * + * Copyright (c) 2016 Xilinx Inc. + * Copyright (c) 2013 Peter Crosthwaite <peter.crosthwaite@xilinx.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "qemu/osdep.h" +#include "hw/register.h" +#include "hw/qdev.h" +#include "qemu/log.h" + +static inline void register_write_val(RegisterInfo *reg, uint64_t val) +{ + g_assert(reg->data); + + switch (reg->data_size) { + case 1: + *(uint8_t *)reg->data = val; + break; + case 2: + *(uint16_t *)reg->data = val; + break; + case 4: + *(uint32_t *)reg->data = val; + break; + case 8: + *(uint64_t *)reg->data = val; + break; + default: + g_assert_not_reached(); + } +} + +static inline uint64_t register_read_val(RegisterInfo *reg) +{ + switch (reg->data_size) { + case 1: + return *(uint8_t *)reg->data; + case 2: + return *(uint16_t *)reg->data; + case 4: + return *(uint32_t *)reg->data; + case 8: + return *(uint64_t *)reg->data; + default: + g_assert_not_reached(); + } + return 0; /* unreachable */ +} + +void register_write(RegisterInfo *reg, uint64_t val, uint64_t we, + const char *prefix, bool debug) +{ + uint64_t old_val, new_val, test, no_w_mask; + const RegisterAccessInfo *ac; + + assert(reg); + + ac = reg->access; + + if (!ac || !ac->name) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: write to undefined device state " + "(written value: %#" PRIx64 ")\n", prefix, val); + return; + } + + old_val = reg->data ? register_read_val(reg) : ac->reset; + + test = (old_val ^ val) & ac->rsvd; + if (test) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: change of value in reserved bit" + "fields: %#" PRIx64 ")\n", prefix, test); + } + + test = val & ac->unimp; + if (test) { + qemu_log_mask(LOG_UNIMP, + "%s:%s writing %#" PRIx64 " to unimplemented bits:" \ + " %#" PRIx64 "", + prefix, reg->access->name, val, ac->unimp); + } + + /* Create the no write mask based on the read only, write to clear and + * reserved bit masks. + */ + no_w_mask = ac->ro | ac->w1c | ac->rsvd | ~we; + new_val = (val & ~no_w_mask) | (old_val & no_w_mask); + new_val &= ~(val & ac->w1c); + + if (ac->pre_write) { + new_val = ac->pre_write(reg, new_val); + } + + if (debug) { + qemu_log("%s:%s: write of value %#" PRIx64 "\n", prefix, ac->name, + new_val); + } + + register_write_val(reg, new_val); + + if (ac->post_write) { + ac->post_write(reg, new_val); + } +} + +uint64_t register_read(RegisterInfo *reg, uint64_t re, const char* prefix, + bool debug) +{ + uint64_t ret; + const RegisterAccessInfo *ac; + + assert(reg); + + ac = reg->access; + if (!ac || !ac->name) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: read from undefined device state\n", + prefix); + return 0; + } + + ret = reg->data ? register_read_val(reg) : ac->reset; + + register_write_val(reg, ret & ~(ac->cor & re)); + + /* Mask based on the read enable size */ + ret &= re; + + if (ac->post_read) { + ret = ac->post_read(reg, ret); + } + + if (debug) { + qemu_log("%s:%s: read of value %#" PRIx64 "\n", prefix, + ac->name, ret); + } + + return ret; +} + +void register_reset(RegisterInfo *reg) +{ + g_assert(reg); + + if (!reg->data || !reg->access) { + return; + } + + register_write_val(reg, reg->access->reset); +} + +void register_init(RegisterInfo *reg) +{ + assert(reg); + + if (!reg->data || !reg->access) { + return; + } + + object_initialize((void *)reg, sizeof(*reg), TYPE_REGISTER); +} + +void register_write_memory(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + RegisterInfoArray *reg_array = opaque; + RegisterInfo *reg = NULL; + uint64_t we; + int i; + + for (i = 0; i < reg_array->num_elements; i++) { + if (reg_array->r[i]->access->addr == addr) { + reg = reg_array->r[i]; + break; + } + } + + if (!reg) { + qemu_log_mask(LOG_GUEST_ERROR, "Write to unimplemented register at " \ + "address: %#" PRIx64 "\n", addr); + return; + } + + /* Generate appropriate write enable mask */ + if (reg->data_size < size) { + we = MAKE_64BIT_MASK(0, reg->data_size * 8); + } else { + we = MAKE_64BIT_MASK(0, size * 8); + } + + register_write(reg, value, we, reg_array->prefix, + reg_array->debug); +} + +uint64_t register_read_memory(void *opaque, hwaddr addr, + unsigned size) +{ + RegisterInfoArray *reg_array = opaque; + RegisterInfo *reg = NULL; + uint64_t read_val; + int i; + + for (i = 0; i < reg_array->num_elements; i++) { + if (reg_array->r[i]->access->addr == addr) { + reg = reg_array->r[i]; + break; + } + } + + if (!reg) { + qemu_log_mask(LOG_GUEST_ERROR, "Read to unimplemented register at " \ + "address: %#" PRIx64 "\n", addr); + return 0; + } + + read_val = register_read(reg, size * 8, reg_array->prefix, + reg_array->debug); + + return extract64(read_val, 0, size * 8); +} + +RegisterInfoArray *register_init_block32(DeviceState *owner, + const RegisterAccessInfo *rae, + int num, RegisterInfo *ri, + uint32_t *data, + const MemoryRegionOps *ops, + bool debug_enabled, + uint64_t memory_size) +{ + const char *device_prefix = object_get_typename(OBJECT(owner)); + RegisterInfoArray *r_array = g_new0(RegisterInfoArray, 1); + int i; + + r_array->r = g_new0(RegisterInfo *, num); + r_array->num_elements = num; + r_array->debug = debug_enabled; + r_array->prefix = device_prefix; + + for (i = 0; i < num; i++) { + int index = rae[i].addr / 4; + RegisterInfo *r = &ri[index]; + + *r = (RegisterInfo) { + .data = &data[index], + .data_size = sizeof(uint32_t), + .access = &rae[i], + .opaque = owner, + }; + register_init(r); + + r_array->r[i] = r; + } + + memory_region_init_io(&r_array->mem, OBJECT(owner), ops, r_array, + device_prefix, memory_size); + + return r_array; +} + +void register_finalize_block(RegisterInfoArray *r_array) +{ + object_unparent(OBJECT(&r_array->mem)); + g_free(r_array->r); + g_free(r_array); +} + +static const TypeInfo register_info = { + .name = TYPE_REGISTER, + .parent = TYPE_DEVICE, +}; + +static void register_register_types(void) +{ + type_register_static(®ister_info); +} + +type_init(register_register_types) diff --git a/hw/display/ads7846.c b/hw/display/ads7846.c index 05aa2d1e6b..166edade7d 100644 --- a/hw/display/ads7846.c +++ b/hw/display/ads7846.c @@ -133,7 +133,7 @@ static const VMStateDescription vmstate_ads7846 = { } }; -static int ads7846_init(SSISlave *d) +static void ads7846_realize(SSISlave *d, Error **errp) { DeviceState *dev = DEVICE(d); ADS7846State *s = FROM_SSI_SLAVE(ADS7846State, d); @@ -152,14 +152,13 @@ static int ads7846_init(SSISlave *d) ads7846_int_update(s); vmstate_register(NULL, -1, &vmstate_ads7846, s); - return 0; } static void ads7846_class_init(ObjectClass *klass, void *data) { SSISlaveClass *k = SSI_SLAVE_CLASS(klass); - k->init = ads7846_init; + k->realize = ads7846_realize; k->transfer = ads7846_transfer; } diff --git a/hw/display/dpcd.c b/hw/display/dpcd.c index 5a36855240..ce92ff6e2a 100644 --- a/hw/display/dpcd.c +++ b/hw/display/dpcd.c @@ -28,7 +28,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" -#include "hw/misc/aux.h" +#include "hw/misc/auxbus.h" #include "hw/display/dpcd.h" #ifndef DEBUG_DPCD diff --git a/hw/display/qxl.c b/hw/display/qxl.c index 919dc5cd36..46cc86690c 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -504,6 +504,7 @@ static void interface_set_compression_level(QXLInstance *sin, int level) qxl_rom_set_dirty(qxl); } +#if SPICE_NEEDS_SET_MM_TIME static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time) { PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); @@ -517,6 +518,7 @@ static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time) qxl->rom->mm_clock = cpu_to_le32(mm_time); qxl_rom_set_dirty(qxl); } +#endif static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info) { @@ -893,7 +895,8 @@ static void interface_update_area_complete(QXLInstance *sin, int qxl_i; qemu_mutex_lock(&qxl->ssd.lock); - if (surface_id != 0 || !qxl->render_update_cookie_num) { + if (surface_id != 0 || !num_updated_rects || + !qxl->render_update_cookie_num) { qemu_mutex_unlock(&qxl->ssd.lock); return; } @@ -1068,7 +1071,9 @@ static const QXLInterface qxl_interface = { .attache_worker = interface_attach_worker, .set_compression_level = interface_set_compression_level, +#if SPICE_NEEDS_SET_MM_TIME .set_mm_time = interface_set_mm_time, +#endif .get_init_info = interface_get_init_info, /* the callbacks below are called from spice server thread context */ @@ -1243,6 +1248,7 @@ static int qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta, int pci_region; pcibus_t pci_start; pcibus_t pci_end; + MemoryRegion *mr; intptr_t virt_start; QXLDevMemSlot memslot; int i; @@ -1289,11 +1295,11 @@ static int qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta, switch (pci_region) { case QXL_RAM_RANGE_INDEX: - virt_start = (intptr_t)memory_region_get_ram_ptr(&d->vga.vram); + mr = &d->vga.vram; break; case QXL_VRAM_RANGE_INDEX: case 4 /* vram 64bit */: - virt_start = (intptr_t)memory_region_get_ram_ptr(&d->vram_bar); + mr = &d->vram_bar; break; default: /* should not happen */ @@ -1301,6 +1307,7 @@ static int qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta, return 1; } + virt_start = (intptr_t)memory_region_get_ram_ptr(mr); memslot.slot_id = slot_id; memslot.slot_group_id = MEMSLOT_GROUP_GUEST; /* guest group */ memslot.virt_start = virt_start + (guest_start - pci_start); @@ -1310,7 +1317,8 @@ static int qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta, qxl_rom_set_dirty(d); qemu_spice_add_memslot(&d->ssd, &memslot, async); - d->guest_slots[slot_id].ptr = (void*)memslot.virt_start; + d->guest_slots[slot_id].mr = mr; + d->guest_slots[slot_id].offset = memslot.virt_start - virt_start; d->guest_slots[slot_id].size = memslot.virt_end - memslot.virt_start; d->guest_slots[slot_id].delta = delta; d->guest_slots[slot_id].active = 1; @@ -1337,39 +1345,60 @@ static void qxl_reset_surfaces(PCIQXLDevice *d) } /* can be also called from spice server thread context */ -void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id) +static bool qxl_get_check_slot_offset(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, + uint32_t *s, uint64_t *o) { uint64_t phys = le64_to_cpu(pqxl); uint32_t slot = (phys >> (64 - 8)) & 0xff; uint64_t offset = phys & 0xffffffffffff; - switch (group_id) { - case MEMSLOT_GROUP_HOST: - return (void *)(intptr_t)offset; - case MEMSLOT_GROUP_GUEST: - if (slot >= NUM_MEMSLOTS) { - qxl_set_guest_bug(qxl, "slot too large %d >= %d", slot, - NUM_MEMSLOTS); - return NULL; - } - if (!qxl->guest_slots[slot].active) { - qxl_set_guest_bug(qxl, "inactive slot %d\n", slot); - return NULL; - } - if (offset < qxl->guest_slots[slot].delta) { - qxl_set_guest_bug(qxl, + if (slot >= NUM_MEMSLOTS) { + qxl_set_guest_bug(qxl, "slot too large %d >= %d", slot, + NUM_MEMSLOTS); + return false; + } + if (!qxl->guest_slots[slot].active) { + qxl_set_guest_bug(qxl, "inactive slot %d\n", slot); + return false; + } + if (offset < qxl->guest_slots[slot].delta) { + qxl_set_guest_bug(qxl, "slot %d offset %"PRIu64" < delta %"PRIu64"\n", slot, offset, qxl->guest_slots[slot].delta); - return NULL; - } - offset -= qxl->guest_slots[slot].delta; - if (offset > qxl->guest_slots[slot].size) { - qxl_set_guest_bug(qxl, + return false; + } + offset -= qxl->guest_slots[slot].delta; + if (offset > qxl->guest_slots[slot].size) { + qxl_set_guest_bug(qxl, "slot %d offset %"PRIu64" > size %"PRIu64"\n", slot, offset, qxl->guest_slots[slot].size); + return false; + } + + *s = slot; + *o = offset; + return true; +} + +/* can be also called from spice server thread context */ +void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id) +{ + uint64_t offset; + uint32_t slot; + void *ptr; + + switch (group_id) { + case MEMSLOT_GROUP_HOST: + offset = le64_to_cpu(pqxl) & 0xffffffffffff; + return (void *)(intptr_t)offset; + case MEMSLOT_GROUP_GUEST: + if (!qxl_get_check_slot_offset(qxl, pqxl, &slot, &offset)) { return NULL; } - return qxl->guest_slots[slot].ptr + offset; + ptr = memory_region_get_ram_ptr(qxl->guest_slots[slot].mr); + ptr += qxl->guest_slots[slot].offset; + ptr += offset; + return ptr; } return NULL; } @@ -1784,9 +1813,23 @@ static void qxl_hw_update(void *opaque) qxl_render_update(qxl); } +static void qxl_dirty_one_surface(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, + uint32_t height, int32_t stride) +{ + uint64_t offset; + uint32_t slot, size; + bool rc; + + rc = qxl_get_check_slot_offset(qxl, pqxl, &slot, &offset); + assert(rc == true); + size = height * abs(stride); + trace_qxl_surfaces_dirty(qxl->id, (int)offset, size); + qxl_set_dirty(qxl->guest_slots[slot].mr, + qxl->guest_slots[slot].offset + offset, size); +} + static void qxl_dirty_surfaces(PCIQXLDevice *qxl) { - uintptr_t vram_start; int i; if (qxl->mode != QXL_MODE_NATIVE && qxl->mode != QXL_MODE_COMPAT) { @@ -1794,16 +1837,13 @@ static void qxl_dirty_surfaces(PCIQXLDevice *qxl) } /* dirty the primary surface */ - qxl_set_dirty(&qxl->vga.vram, qxl->shadow_rom.draw_area_offset, - qxl->shadow_rom.surface0_area_size); - - vram_start = (uintptr_t)memory_region_get_ram_ptr(&qxl->vram_bar); + qxl_dirty_one_surface(qxl, qxl->guest_primary.surface.mem, + qxl->guest_primary.surface.height, + qxl->guest_primary.surface.stride); /* dirty the off-screen surfaces */ for (i = 0; i < qxl->ssd.num_surfaces; i++) { QXLSurfaceCmd *cmd; - intptr_t surface_offset; - int surface_size; if (qxl->guest_surfaces.cmds[i] == 0) { continue; @@ -1813,15 +1853,9 @@ static void qxl_dirty_surfaces(PCIQXLDevice *qxl) MEMSLOT_GROUP_GUEST); assert(cmd); assert(cmd->type == QXL_SURFACE_CMD_CREATE); - surface_offset = (intptr_t)qxl_phys2virt(qxl, - cmd->u.surface_create.data, - MEMSLOT_GROUP_GUEST); - assert(surface_offset); - surface_offset -= vram_start; - surface_size = cmd->u.surface_create.height * - abs(cmd->u.surface_create.stride); - trace_qxl_surfaces_dirty(qxl->id, i, (int)surface_offset, surface_size); - qxl_set_dirty(&qxl->vram_bar, surface_offset, surface_size); + qxl_dirty_one_surface(qxl, cmd->u.surface_create.data, + cmd->u.surface_create.height, + cmd->u.surface_create.stride); } } @@ -1914,7 +1948,7 @@ static void qxl_init_ramsize(PCIQXLDevice *qxl) /* vram (surfaces, 64bit, bar 4+5) */ if (qxl->vram_size_mb != -1) { - qxl->vram_size = qxl->vram_size_mb * 1024 * 1024; + qxl->vram_size = (uint64_t)qxl->vram_size_mb * 1024 * 1024; } if (qxl->vram_size < qxl->vram32_size) { qxl->vram_size = qxl->vram32_size; @@ -2020,9 +2054,9 @@ static void qxl_realize_common(PCIQXLDevice *qxl, Error **errp) dprint(qxl, 1, "ram/%s: %d MB [region 0]\n", qxl->id == 0 ? "pri" : "sec", qxl->vga.vram_size / (1024*1024)); - dprint(qxl, 1, "vram/32: %d MB [region 1]\n", + dprint(qxl, 1, "vram/32: %" PRIx64 "d MB [region 1]\n", qxl->vram32_size / (1024*1024)); - dprint(qxl, 1, "vram/64: %d MB %s\n", + dprint(qxl, 1, "vram/64: %" PRIx64 "d MB %s\n", qxl->vram_size / (1024*1024), qxl->vram32_size < qxl->vram_size ? "[region 4]" : "[unmapped]"); @@ -2276,7 +2310,7 @@ static VMStateDescription qxl_vmstate = { static Property qxl_properties[] = { DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, 64 * 1024 * 1024), - DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram32_size, + DEFINE_PROP_UINT64("vram_size", PCIQXLDevice, vram32_size, 64 * 1024 * 1024), DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision, QXL_DEFAULT_REVISION), diff --git a/hw/display/qxl.h b/hw/display/qxl.h index 2ddf065e1f..fdb619d4a7 100644 --- a/hw/display/qxl.h +++ b/hw/display/qxl.h @@ -53,7 +53,8 @@ typedef struct PCIQXLDevice { struct guest_slots { QXLMemSlot slot; - void *ptr; + MemoryRegion *mr; + uint64_t offset; uint64_t size; uint64_t delta; uint32_t active; @@ -104,9 +105,9 @@ typedef struct PCIQXLDevice { #endif /* vram pci bar */ - uint32_t vram_size; + uint64_t vram_size; MemoryRegion vram_bar; - uint32_t vram32_size; + uint64_t vram32_size; MemoryRegion vram32_bar; /* io bar */ diff --git a/hw/display/ssd0323.c b/hw/display/ssd0323.c index 14c1bf339c..6d1faf44af 100644 --- a/hw/display/ssd0323.c +++ b/hw/display/ssd0323.c @@ -361,7 +361,7 @@ static const GraphicHwOps ssd0323_ops = { .gfx_update = ssd0323_update_display, }; -static int ssd0323_init(SSISlave *d) +static void ssd0323_realize(SSISlave *d, Error **errp) { DeviceState *dev = DEVICE(d); ssd0323_state *s = FROM_SSI_SLAVE(ssd0323_state, d); @@ -375,14 +375,13 @@ static int ssd0323_init(SSISlave *d) register_savevm(dev, "ssd0323_oled", -1, 1, ssd0323_save, ssd0323_load, s); - return 0; } static void ssd0323_class_init(ObjectClass *klass, void *data) { SSISlaveClass *k = SSI_SLAVE_CLASS(klass); - k->init = ssd0323_init; + k->realize = ssd0323_realize; k->transfer = ssd0323_transfer; k->cs_polarity = SSI_CS_HIGH; } diff --git a/hw/display/trace-events b/hw/display/trace-events index 30bebffd8b..9dd82cecde 100644 --- a/hw/display/trace-events +++ b/hw/display/trace-events @@ -105,7 +105,7 @@ qxl_spice_reset_image_cache(int qid) "%d" qxl_spice_reset_memslots(int qid) "%d" qxl_spice_update_area(int qid, uint32_t surface_id, uint32_t left, uint32_t right, uint32_t top, uint32_t bottom) "%d sid=%d [%d,%d,%d,%d]" qxl_spice_update_area_rest(int qid, uint32_t num_dirty_rects, uint32_t clear_dirty_region) "%d #d=%d clear=%d" -qxl_surfaces_dirty(int qid, int surface, int offset, int size) "%d surface=%d offset=%d size=%d" +qxl_surfaces_dirty(int qid, int offset, int size) "%d offset=%d size=%d" qxl_send_events(int qid, uint32_t events) "%d %d" qxl_send_events_vm_stopped(int qid, uint32_t events) "%d %d" qxl_set_guest_bug(int qid) "%d" diff --git a/hw/display/virtio-gpu-3d.c b/hw/display/virtio-gpu-3d.c index 29918a090b..d6c8c6e2dc 100644 --- a/hw/display/virtio-gpu-3d.c +++ b/hw/display/virtio-gpu-3d.c @@ -171,13 +171,14 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g, virgl_renderer_force_ctx_0(); dpy_gl_scanout(g->scanout[ss.scanout_id].con, info.tex_id, info.flags & 1 /* FIXME: Y_0_TOP */, + info.width, info.height, ss.r.x, ss.r.y, ss.r.width, ss.r.height); } else { if (ss.scanout_id != 0) { dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, NULL); } dpy_gl_scanout(g->scanout[ss.scanout_id].con, 0, false, - 0, 0, 0, 0); + 0, 0, 0, 0, 0, 0); } g->scanout[ss.scanout_id].resource_id = ss.resource_id; } @@ -580,7 +581,7 @@ void virtio_gpu_virgl_reset(VirtIOGPU *g) if (i != 0) { dpy_gfx_replace_surface(g->scanout[i].con, NULL); } - dpy_gl_scanout(g->scanout[i].con, 0, false, 0, 0, 0, 0); + dpy_gl_scanout(g->scanout[i].con, 0, false, 0, 0, 0, 0, 0, 0); } } diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 136c095b7d..f8b0274752 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -934,8 +934,14 @@ static void virtio_gpu_gl_block(void *opaque, bool block) { VirtIOGPU *g = opaque; - g->renderer_blocked = block; - if (!block) { + if (block) { + g->renderer_blocked++; + } else { + g->renderer_blocked--; + } + assert(g->renderer_blocked >= 0); + + if (g->renderer_blocked == 0) { virtio_gpu_process_cmdq(g); } } diff --git a/hw/display/xlnx_dp.c b/hw/display/xlnx_dp.c index be53b756c3..f43eb09304 100644 --- a/hw/display/xlnx_dp.c +++ b/hw/display/xlnx_dp.c @@ -438,10 +438,10 @@ static void xlnx_dp_aux_clear_tx_fifo(XlnxDPState *s) fifo8_reset(&s->tx_fifo); } -static void xlnx_dp_aux_push_tx_fifo(XlnxDPState *s, uint8_t val, size_t len) +static void xlnx_dp_aux_push_tx_fifo(XlnxDPState *s, uint8_t *buf, size_t len) { DPRINTF("Push %u data in tx_fifo\n", (unsigned)len); - fifo8_push_all(&s->tx_fifo, &val, len); + fifo8_push_all(&s->tx_fifo, buf, len); } static uint8_t xlnx_dp_aux_pop_tx_fifo(XlnxDPState *s) @@ -806,9 +806,11 @@ static void xlnx_dp_write(void *opaque, hwaddr offset, uint64_t value, * TODO: Power down things? */ break; - case DP_AUX_WRITE_FIFO: - xlnx_dp_aux_push_tx_fifo(s, value, 1); + case DP_AUX_WRITE_FIFO: { + uint8_t c = value; + xlnx_dp_aux_push_tx_fifo(s, &c, 1); break; + } case DP_AUX_CLOCK_DIVIDER: break; case DP_AUX_REPLY_COUNT: diff --git a/hw/dma/Makefile.objs b/hw/dma/Makefile.objs index 8b0823e593..087c8e6855 100644 --- a/hw/dma/Makefile.objs +++ b/hw/dma/Makefile.objs @@ -5,6 +5,7 @@ common-obj-$(CONFIG_PL330) += pl330.o common-obj-$(CONFIG_I82374) += i82374.o common-obj-$(CONFIG_I8257) += i8257.o common-obj-$(CONFIG_XILINX_AXI) += xilinx_axidma.o +common-obj-$(CONFIG_ZYNQ_DEVCFG) += xlnx-zynq-devcfg.o common-obj-$(CONFIG_ETRAXFS) += etraxfs_dma.o common-obj-$(CONFIG_STP2000) += sparc32_dma.o common-obj-$(CONFIG_SUN4M) += sun4m_iommu.o diff --git a/hw/dma/xlnx-zynq-devcfg.c b/hw/dma/xlnx-zynq-devcfg.c new file mode 100644 index 0000000000..3b10523430 --- /dev/null +++ b/hw/dma/xlnx-zynq-devcfg.c @@ -0,0 +1,400 @@ +/* + * QEMU model of the Xilinx Zynq Devcfg Interface + * + * (C) 2011 PetaLogix Pty Ltd + * (C) 2014 Xilinx Inc. + * Written by Peter Crosthwaite <peter.crosthwaite@xilinx.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "hw/dma/xlnx-zynq-devcfg.h" +#include "qemu/bitops.h" +#include "sysemu/sysemu.h" +#include "sysemu/dma.h" +#include "qemu/log.h" + +#define FREQ_HZ 900000000 + +#define BTT_MAX 0x400 + +#ifndef XLNX_ZYNQ_DEVCFG_ERR_DEBUG +#define XLNX_ZYNQ_DEVCFG_ERR_DEBUG 0 +#endif + +#define DB_PRINT(fmt, args...) do { \ + if (XLNX_ZYNQ_DEVCFG_ERR_DEBUG) { \ + qemu_log("%s: " fmt, __func__, ## args); \ + } \ +} while (0); + +REG32(CTRL, 0x00) + FIELD(CTRL, FORCE_RST, 31, 1) /* Not supported, wr ignored */ + FIELD(CTRL, PCAP_PR, 27, 1) /* Forced to 0 on bad unlock */ + FIELD(CTRL, PCAP_MODE, 26, 1) + FIELD(CTRL, MULTIBOOT_EN, 24, 1) + FIELD(CTRL, USER_MODE, 15, 1) + FIELD(CTRL, PCFG_AES_FUSE, 12, 1) + FIELD(CTRL, PCFG_AES_EN, 9, 3) + FIELD(CTRL, SEU_EN, 8, 1) + FIELD(CTRL, SEC_EN, 7, 1) + FIELD(CTRL, SPNIDEN, 6, 1) + FIELD(CTRL, SPIDEN, 5, 1) + FIELD(CTRL, NIDEN, 4, 1) + FIELD(CTRL, DBGEN, 3, 1) + FIELD(CTRL, DAP_EN, 0, 3) + +REG32(LOCK, 0x04) +#define AES_FUSE_LOCK 4 +#define AES_EN_LOCK 3 +#define SEU_LOCK 2 +#define SEC_LOCK 1 +#define DBG_LOCK 0 + +/* mapping bits in R_LOCK to what they lock in R_CTRL */ +static const uint32_t lock_ctrl_map[] = { + [AES_FUSE_LOCK] = R_CTRL_PCFG_AES_FUSE_MASK, + [AES_EN_LOCK] = R_CTRL_PCFG_AES_EN_MASK, + [SEU_LOCK] = R_CTRL_SEU_EN_MASK, + [SEC_LOCK] = R_CTRL_SEC_EN_MASK, + [DBG_LOCK] = R_CTRL_SPNIDEN_MASK | R_CTRL_SPIDEN_MASK | + R_CTRL_NIDEN_MASK | R_CTRL_DBGEN_MASK | + R_CTRL_DAP_EN_MASK, +}; + +REG32(CFG, 0x08) + FIELD(CFG, RFIFO_TH, 10, 2) + FIELD(CFG, WFIFO_TH, 8, 2) + FIELD(CFG, RCLK_EDGE, 7, 1) + FIELD(CFG, WCLK_EDGE, 6, 1) + FIELD(CFG, DISABLE_SRC_INC, 5, 1) + FIELD(CFG, DISABLE_DST_INC, 4, 1) +#define R_CFG_RESET 0x50B + +REG32(INT_STS, 0x0C) + FIELD(INT_STS, PSS_GTS_USR_B, 31, 1) + FIELD(INT_STS, PSS_FST_CFG_B, 30, 1) + FIELD(INT_STS, PSS_CFG_RESET_B, 27, 1) + FIELD(INT_STS, RX_FIFO_OV, 18, 1) + FIELD(INT_STS, WR_FIFO_LVL, 17, 1) + FIELD(INT_STS, RD_FIFO_LVL, 16, 1) + FIELD(INT_STS, DMA_CMD_ERR, 15, 1) + FIELD(INT_STS, DMA_Q_OV, 14, 1) + FIELD(INT_STS, DMA_DONE, 13, 1) + FIELD(INT_STS, DMA_P_DONE, 12, 1) + FIELD(INT_STS, P2D_LEN_ERR, 11, 1) + FIELD(INT_STS, PCFG_DONE, 2, 1) +#define R_INT_STS_RSVD ((0x7 << 24) | (0x1 << 19) | (0xF < 7)) + +REG32(INT_MASK, 0x10) + +REG32(STATUS, 0x14) + FIELD(STATUS, DMA_CMD_Q_F, 31, 1) + FIELD(STATUS, DMA_CMD_Q_E, 30, 1) + FIELD(STATUS, DMA_DONE_CNT, 28, 2) + FIELD(STATUS, RX_FIFO_LVL, 20, 5) + FIELD(STATUS, TX_FIFO_LVL, 12, 7) + FIELD(STATUS, PSS_GTS_USR_B, 11, 1) + FIELD(STATUS, PSS_FST_CFG_B, 10, 1) + FIELD(STATUS, PSS_CFG_RESET_B, 5, 1) + +REG32(DMA_SRC_ADDR, 0x18) +REG32(DMA_DST_ADDR, 0x1C) +REG32(DMA_SRC_LEN, 0x20) +REG32(DMA_DST_LEN, 0x24) +REG32(ROM_SHADOW, 0x28) +REG32(SW_ID, 0x30) +REG32(UNLOCK, 0x34) + +#define R_UNLOCK_MAGIC 0x757BDF0D + +REG32(MCTRL, 0x80) + FIELD(MCTRL, PS_VERSION, 28, 4) + FIELD(MCTRL, PCFG_POR_B, 8, 1) + FIELD(MCTRL, INT_PCAP_LPBK, 4, 1) + FIELD(MCTRL, QEMU, 3, 1) + +static void xlnx_zynq_devcfg_update_ixr(XlnxZynqDevcfg *s) +{ + qemu_set_irq(s->irq, ~s->regs[R_INT_MASK] & s->regs[R_INT_STS]); +} + +static void xlnx_zynq_devcfg_reset(DeviceState *dev) +{ + XlnxZynqDevcfg *s = XLNX_ZYNQ_DEVCFG(dev); + int i; + + for (i = 0; i < XLNX_ZYNQ_DEVCFG_R_MAX; ++i) { + register_reset(&s->regs_info[i]); + } +} + +static void xlnx_zynq_devcfg_dma_go(XlnxZynqDevcfg *s) +{ + do { + uint8_t buf[BTT_MAX]; + XlnxZynqDevcfgDMACmd *dmah = s->dma_cmd_fifo; + uint32_t btt = BTT_MAX; + bool loopback = s->regs[R_MCTRL] & R_MCTRL_INT_PCAP_LPBK_MASK; + + btt = MIN(btt, dmah->src_len); + if (loopback) { + btt = MIN(btt, dmah->dest_len); + } + DB_PRINT("reading %x bytes from %x\n", btt, dmah->src_addr); + dma_memory_read(&address_space_memory, dmah->src_addr, buf, btt); + dmah->src_len -= btt; + dmah->src_addr += btt; + if (loopback && (dmah->src_len || dmah->dest_len)) { + DB_PRINT("writing %x bytes from %x\n", btt, dmah->dest_addr); + dma_memory_write(&address_space_memory, dmah->dest_addr, buf, btt); + dmah->dest_len -= btt; + dmah->dest_addr += btt; + } + if (!dmah->src_len && !dmah->dest_len) { + DB_PRINT("dma operation finished\n"); + s->regs[R_INT_STS] |= R_INT_STS_DMA_DONE_MASK | + R_INT_STS_DMA_P_DONE_MASK; + s->dma_cmd_fifo_num--; + memmove(s->dma_cmd_fifo, &s->dma_cmd_fifo[1], + sizeof(s->dma_cmd_fifo) - sizeof(s->dma_cmd_fifo[0])); + } + xlnx_zynq_devcfg_update_ixr(s); + } while (s->dma_cmd_fifo_num); +} + +static void r_ixr_post_write(RegisterInfo *reg, uint64_t val) +{ + XlnxZynqDevcfg *s = XLNX_ZYNQ_DEVCFG(reg->opaque); + + xlnx_zynq_devcfg_update_ixr(s); +} + +static uint64_t r_ctrl_pre_write(RegisterInfo *reg, uint64_t val) +{ + XlnxZynqDevcfg *s = XLNX_ZYNQ_DEVCFG(reg->opaque); + int i; + + for (i = 0; i < ARRAY_SIZE(lock_ctrl_map); ++i) { + if (s->regs[R_LOCK] & 1 << i) { + val &= ~lock_ctrl_map[i]; + val |= lock_ctrl_map[i] & s->regs[R_CTRL]; + } + } + return val; +} + +static void r_ctrl_post_write(RegisterInfo *reg, uint64_t val) +{ + const char *device_prefix = object_get_typename(OBJECT(reg->opaque)); + uint32_t aes_en = FIELD_EX32(val, CTRL, PCFG_AES_EN); + + if (aes_en != 0 && aes_en != 7) { + qemu_log_mask(LOG_UNIMP, "%s: warning, aes-en bits inconsistent," + "unimplemented security reset should happen!\n", + device_prefix); + } +} + +static void r_unlock_post_write(RegisterInfo *reg, uint64_t val) +{ + XlnxZynqDevcfg *s = XLNX_ZYNQ_DEVCFG(reg->opaque); + const char *device_prefix = object_get_typename(OBJECT(s)); + + if (val == R_UNLOCK_MAGIC) { + DB_PRINT("successful unlock\n"); + s->regs[R_CTRL] |= R_CTRL_PCAP_PR_MASK; + s->regs[R_CTRL] |= R_CTRL_PCFG_AES_EN_MASK; + memory_region_set_enabled(&s->iomem, true); + } else { /* bad unlock attempt */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: failed unlock\n", device_prefix); + s->regs[R_CTRL] &= ~R_CTRL_PCAP_PR_MASK; + s->regs[R_CTRL] &= ~R_CTRL_PCFG_AES_EN_MASK; + /* core becomes inaccessible */ + memory_region_set_enabled(&s->iomem, false); + } +} + +static uint64_t r_lock_pre_write(RegisterInfo *reg, uint64_t val) +{ + XlnxZynqDevcfg *s = XLNX_ZYNQ_DEVCFG(reg->opaque); + + /* once bits are locked they stay locked */ + return s->regs[R_LOCK] | val; +} + +static void r_dma_dst_len_post_write(RegisterInfo *reg, uint64_t val) +{ + XlnxZynqDevcfg *s = XLNX_ZYNQ_DEVCFG(reg->opaque); + + s->dma_cmd_fifo[s->dma_cmd_fifo_num] = (XlnxZynqDevcfgDMACmd) { + .src_addr = s->regs[R_DMA_SRC_ADDR] & ~0x3UL, + .dest_addr = s->regs[R_DMA_DST_ADDR] & ~0x3UL, + .src_len = s->regs[R_DMA_SRC_LEN] << 2, + .dest_len = s->regs[R_DMA_DST_LEN] << 2, + }; + s->dma_cmd_fifo_num++; + DB_PRINT("dma transfer started; %d total transfers pending\n", + s->dma_cmd_fifo_num); + xlnx_zynq_devcfg_dma_go(s); +} + +static const RegisterAccessInfo xlnx_zynq_devcfg_regs_info[] = { + { .name = "CTRL", .addr = A_CTRL, + .reset = R_CTRL_PCAP_PR_MASK | R_CTRL_PCAP_MODE_MASK | 0x3 << 13, + .rsvd = 0x1 << 28 | 0x3ff << 13 | 0x3 << 13, + .pre_write = r_ctrl_pre_write, + .post_write = r_ctrl_post_write, + }, + { .name = "LOCK", .addr = A_LOCK, + .rsvd = MAKE_64BIT_MASK(5, 64 - 5), + .pre_write = r_lock_pre_write, + }, + { .name = "CFG", .addr = A_CFG, + .reset = R_CFG_RESET, + .rsvd = 0xfffff00f, + }, + { .name = "INT_STS", .addr = A_INT_STS, + .w1c = ~R_INT_STS_RSVD, + .reset = R_INT_STS_PSS_GTS_USR_B_MASK | + R_INT_STS_PSS_CFG_RESET_B_MASK | + R_INT_STS_WR_FIFO_LVL_MASK, + .rsvd = R_INT_STS_RSVD, + .post_write = r_ixr_post_write, + }, + { .name = "INT_MASK", .addr = A_INT_MASK, + .reset = ~0, + .rsvd = R_INT_STS_RSVD, + .post_write = r_ixr_post_write, + }, + { .name = "STATUS", .addr = A_STATUS, + .reset = R_STATUS_DMA_CMD_Q_E_MASK | + R_STATUS_PSS_GTS_USR_B_MASK | + R_STATUS_PSS_CFG_RESET_B_MASK, + .ro = ~0, + }, + { .name = "DMA_SRC_ADDR", .addr = A_DMA_SRC_ADDR, }, + { .name = "DMA_DST_ADDR", .addr = A_DMA_DST_ADDR, }, + { .name = "DMA_SRC_LEN", .addr = A_DMA_SRC_LEN, + .ro = MAKE_64BIT_MASK(27, 64 - 27) }, + { .name = "DMA_DST_LEN", .addr = A_DMA_DST_LEN, + .ro = MAKE_64BIT_MASK(27, 64 - 27), + .post_write = r_dma_dst_len_post_write, + }, + { .name = "ROM_SHADOW", .addr = A_ROM_SHADOW, + .rsvd = ~0ull, + }, + { .name = "SW_ID", .addr = A_SW_ID, }, + { .name = "UNLOCK", .addr = A_UNLOCK, + .post_write = r_unlock_post_write, + }, + { .name = "MCTRL", .addr = R_MCTRL * 4, + /* Silicon 3.0 for version field, the mysterious reserved bit 23 + * and QEMU platform identifier. + */ + .reset = 0x2 << R_MCTRL_PS_VERSION_SHIFT | 1 << 23 | R_MCTRL_QEMU_MASK, + .ro = ~R_MCTRL_INT_PCAP_LPBK_MASK, + .rsvd = 0x00f00303, + }, +}; + +static const MemoryRegionOps xlnx_zynq_devcfg_reg_ops = { + .read = register_read_memory, + .write = register_write_memory, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + } +}; + +static const VMStateDescription vmstate_xlnx_zynq_devcfg_dma_cmd = { + .name = "xlnx_zynq_devcfg_dma_cmd", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(src_addr, XlnxZynqDevcfgDMACmd), + VMSTATE_UINT32(dest_addr, XlnxZynqDevcfgDMACmd), + VMSTATE_UINT32(src_len, XlnxZynqDevcfgDMACmd), + VMSTATE_UINT32(dest_len, XlnxZynqDevcfgDMACmd), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_xlnx_zynq_devcfg = { + .name = "xlnx_zynq_devcfg", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT_ARRAY(dma_cmd_fifo, XlnxZynqDevcfg, + XLNX_ZYNQ_DEVCFG_DMA_CMD_FIFO_LEN, 0, + vmstate_xlnx_zynq_devcfg_dma_cmd, + XlnxZynqDevcfgDMACmd), + VMSTATE_UINT8(dma_cmd_fifo_num, XlnxZynqDevcfg), + VMSTATE_UINT32_ARRAY(regs, XlnxZynqDevcfg, XLNX_ZYNQ_DEVCFG_R_MAX), + VMSTATE_END_OF_LIST() + } +}; + +static void xlnx_zynq_devcfg_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + XlnxZynqDevcfg *s = XLNX_ZYNQ_DEVCFG(obj); + RegisterInfoArray *reg_array; + + sysbus_init_irq(sbd, &s->irq); + + memory_region_init(&s->iomem, obj, "devcfg", XLNX_ZYNQ_DEVCFG_R_MAX * 4); + reg_array = + register_init_block32(DEVICE(obj), xlnx_zynq_devcfg_regs_info, + ARRAY_SIZE(xlnx_zynq_devcfg_regs_info), + s->regs_info, s->regs, + &xlnx_zynq_devcfg_reg_ops, + XLNX_ZYNQ_DEVCFG_ERR_DEBUG, + XLNX_ZYNQ_DEVCFG_R_MAX); + memory_region_add_subregion(&s->iomem, + A_CTRL, + ®_array->mem); + + sysbus_init_mmio(sbd, &s->iomem); +} + +static void xlnx_zynq_devcfg_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = xlnx_zynq_devcfg_reset; + dc->vmsd = &vmstate_xlnx_zynq_devcfg; +} + +static const TypeInfo xlnx_zynq_devcfg_info = { + .name = TYPE_XLNX_ZYNQ_DEVCFG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XlnxZynqDevcfg), + .instance_init = xlnx_zynq_devcfg_init, + .class_init = xlnx_zynq_devcfg_class_init, +}; + +static void xlnx_zynq_devcfg_register_types(void) +{ + type_register_static(&xlnx_zynq_devcfg_info); +} + +type_init(xlnx_zynq_devcfg_register_types) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 5a594be8ee..fbba461a87 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -229,26 +229,27 @@ static Object *acpi_get_i386_pci_host(void) return OBJECT(host); } -static void acpi_get_pci_info(PcPciInfo *info) +static void acpi_get_pci_holes(Range *hole, Range *hole64) { Object *pci_host; - pci_host = acpi_get_i386_pci_host(); g_assert(pci_host); - info->w32.begin = object_property_get_int(pci_host, + range_set_bounds1(hole, + object_property_get_int(pci_host, PCI_HOST_PROP_PCI_HOLE_START, - NULL); - info->w32.end = object_property_get_int(pci_host, - PCI_HOST_PROP_PCI_HOLE_END, - NULL); - info->w64.begin = object_property_get_int(pci_host, + NULL), + object_property_get_int(pci_host, + PCI_HOST_PROP_PCI_HOLE_END, + NULL)); + range_set_bounds1(hole64, + object_property_get_int(pci_host, PCI_HOST_PROP_PCI_HOLE64_START, - NULL); - info->w64.end = object_property_get_int(pci_host, - PCI_HOST_PROP_PCI_HOLE64_END, - NULL); + NULL), + object_property_get_int(pci_host, + PCI_HOST_PROP_PCI_HOLE64_END, + NULL)); } #define ACPI_PORT_SMI_CMD 0x00b2 /* TODO: this is APM_CNT_IOPORT */ @@ -1890,7 +1891,7 @@ static Aml *build_q35_osc_method(void) static void build_dsdt(GArray *table_data, BIOSLinker *linker, AcpiPmInfo *pm, AcpiMiscInfo *misc, - PcPciInfo *pci, MachineState *machine) + Range *pci_hole, Range *pci_hole64, MachineState *machine) { CrsRangeEntry *entry; Aml *dsdt, *sb_scope, *scope, *dev, *method, *field, *pkg, *crs; @@ -2047,7 +2048,9 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, AML_CACHEABLE, AML_READ_WRITE, 0, 0x000A0000, 0x000BFFFF, 0, 0x00020000)); - crs_replace_with_free_ranges(mem_ranges, pci->w32.begin, pci->w32.end - 1); + crs_replace_with_free_ranges(mem_ranges, + range_lob(pci_hole), + range_upb(pci_hole)); for (i = 0; i < mem_ranges->len; i++) { entry = g_ptr_array_index(mem_ranges, i); aml_append(crs, @@ -2057,12 +2060,12 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, 0, entry->limit - entry->base + 1)); } - if (pci->w64.begin) { + if (!range_is_empty(pci_hole64)) { aml_append(crs, aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, AML_CACHEABLE, AML_READ_WRITE, - 0, pci->w64.begin, pci->w64.end - 1, 0, - pci->w64.end - pci->w64.begin)); + 0, range_lob(pci_hole64), range_upb(pci_hole64), 0, + range_upb(pci_hole64) + 1 - range_lob(pci_hole64))); } if (misc->tpm_version != TPM_VERSION_UNSPEC) { @@ -2554,7 +2557,7 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) AcpiPmInfo pm; AcpiMiscInfo misc; AcpiMcfgInfo mcfg; - PcPciInfo pci; + Range pci_hole, pci_hole64; uint8_t *u; size_t aml_len = 0; GArray *tables_blob = tables->table_data; @@ -2562,7 +2565,7 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) acpi_get_pm_info(&pm); acpi_get_misc_info(&misc); - acpi_get_pci_info(&pci); + acpi_get_pci_holes(&pci_hole, &pci_hole64); acpi_get_slic_oem(&slic_oem); table_offsets = g_array_new(false, true /* clear */, @@ -2584,7 +2587,8 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) /* DSDT is pointed to by FADT */ dsdt = tables_blob->len; - build_dsdt(tables_blob, tables->linker, &pm, &misc, &pci, machine); + build_dsdt(tables_blob, tables->linker, &pm, &misc, + &pci_hole, &pci_hole64, machine); /* Count the size of the DSDT and SSDT, we will need it for legacy * sizing of ACPI tables. diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 5eba704477..464f2a0518 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -25,6 +25,7 @@ #include "intel_iommu_internal.h" #include "hw/pci/pci.h" #include "hw/pci/pci_bus.h" +#include "hw/i386/pc.h" /*#define DEBUG_INTEL_IOMMU*/ #ifdef DEBUG_INTEL_IOMMU @@ -2026,8 +2027,20 @@ static void vtd_reset(DeviceState *dev) vtd_init(s); } +static AddressSpace *vtd_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) +{ + IntelIOMMUState *s = opaque; + VTDAddressSpace *vtd_as; + + assert(0 <= devfn && devfn <= VTD_PCI_DEVFN_MAX); + + vtd_as = vtd_find_add_as(s, bus, devfn); + return &vtd_as->as; +} + static void vtd_realize(DeviceState *dev, Error **errp) { + PCIBus *bus = PC_MACHINE(qdev_get_machine())->bus; IntelIOMMUState *s = INTEL_IOMMU_DEVICE(dev); VTD_DPRINTF(GENERAL, ""); @@ -2041,6 +2054,8 @@ static void vtd_realize(DeviceState *dev, Error **errp) s->vtd_as_by_busptr = g_hash_table_new_full(vtd_uint64_hash, vtd_uint64_equal, g_free, g_free); vtd_init(s); + sysbus_mmio_map(SYS_BUS_DEVICE(s), 0, Q35_HOST_BRIDGE_IOMMU_ADDR); + pci_setup_iommu(bus, vtd_host_dma_iommu, dev); } static void vtd_class_init(ObjectClass *klass, void *data) @@ -2051,6 +2066,7 @@ static void vtd_class_init(ObjectClass *klass, void *data) dc->realize = vtd_realize; dc->vmsd = &vtd_vmstate; dc->props = vtd_properties; + dc->hotpluggable = false; } static const TypeInfo vtd_info = { diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 44a8f3bcbd..f56e225a99 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1039,21 +1039,17 @@ void pc_acpi_smi_interrupt(void *opaque, int irq, int level) } } -static X86CPU *pc_new_cpu(const char *cpu_model, int64_t apic_id, +static X86CPU *pc_new_cpu(const char *typename, int64_t apic_id, Error **errp) { X86CPU *cpu = NULL; Error *local_err = NULL; - cpu = cpu_x86_create(cpu_model, &local_err); - if (local_err != NULL) { - goto out; - } + cpu = X86_CPU(object_new(typename)); object_property_set_int(OBJECT(cpu), apic_id, "apic-id", &local_err); object_property_set_bool(OBJECT(cpu), true, "realized", &local_err); -out: if (local_err) { error_propagate(errp, local_err); object_unref(OBJECT(cpu)); @@ -1065,7 +1061,8 @@ out: void pc_hot_add_cpu(const int64_t id, Error **errp) { X86CPU *cpu; - MachineState *machine = MACHINE(qdev_get_machine()); + ObjectClass *oc; + PCMachineState *pcms = PC_MACHINE(qdev_get_machine()); int64_t apic_id = x86_cpu_apic_id_from_index(id); Error *local_err = NULL; @@ -1093,7 +1090,9 @@ void pc_hot_add_cpu(const int64_t id, Error **errp) return; } - cpu = pc_new_cpu(machine->cpu_model, apic_id, &local_err); + assert(pcms->possible_cpus->cpus[0].cpu); /* BSP is always present */ + oc = OBJECT_CLASS(CPU_GET_CLASS(pcms->possible_cpus->cpus[0].cpu)); + cpu = pc_new_cpu(object_class_get_name(oc), apic_id, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -1104,6 +1103,10 @@ void pc_hot_add_cpu(const int64_t id, Error **errp) void pc_cpus_init(PCMachineState *pcms) { int i; + CPUClass *cc; + ObjectClass *oc; + const char *typename; + gchar **model_pieces; X86CPU *cpu = NULL; MachineState *machine = MACHINE(pcms); @@ -1116,6 +1119,22 @@ void pc_cpus_init(PCMachineState *pcms) #endif } + model_pieces = g_strsplit(machine->cpu_model, ",", 2); + if (!model_pieces[0]) { + error_report("Invalid/empty CPU model name"); + exit(1); + } + + oc = cpu_class_by_name(TYPE_X86_CPU, model_pieces[0]); + if (oc == NULL) { + error_report("Unable to find CPU definition: %s", model_pieces[0]); + exit(1); + } + typename = object_class_get_name(oc); + cc = CPU_CLASS(oc); + cc->parse_features(typename, model_pieces[1], &error_fatal); + g_strfreev(model_pieces); + /* Calculates the limit to CPU APIC ID values * * Limit for the APIC ID value, so that all @@ -1136,7 +1155,7 @@ void pc_cpus_init(PCMachineState *pcms) pcms->possible_cpus->cpus[i].arch_id = x86_cpu_apic_id_from_index(i); pcms->possible_cpus->len++; if (i < smp_cpus) { - cpu = pc_new_cpu(machine->cpu_model, x86_cpu_apic_id_from_index(i), + cpu = pc_new_cpu(typename, x86_cpu_apic_id_from_index(i), &error_fatal); pcms->possible_cpus->cpus[i].cpu = CPU(cpu); object_unref(OBJECT(cpu)); @@ -1147,6 +1166,34 @@ void pc_cpus_init(PCMachineState *pcms) smbios_set_cpuid(cpu->env.cpuid_version, cpu->env.features[FEAT_1_EDX]); } +static void pc_build_feature_control_file(PCMachineState *pcms) +{ + X86CPU *cpu = X86_CPU(pcms->possible_cpus->cpus[0].cpu); + CPUX86State *env = &cpu->env; + uint32_t unused, ecx, edx; + uint64_t feature_control_bits = 0; + uint64_t *val; + + cpu_x86_cpuid(env, 1, 0, &unused, &unused, &ecx, &edx); + if (ecx & CPUID_EXT_VMX) { + feature_control_bits |= FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX; + } + + if ((edx & (CPUID_EXT2_MCE | CPUID_EXT2_MCA)) == + (CPUID_EXT2_MCE | CPUID_EXT2_MCA) && + (env->mcg_cap & MCG_LMCE_P)) { + feature_control_bits |= FEATURE_CONTROL_LMCE; + } + + if (!feature_control_bits) { + return; + } + + val = g_malloc(sizeof(*val)); + *val = cpu_to_le64(feature_control_bits | FEATURE_CONTROL_LOCKED); + fw_cfg_add_file(pcms->fw_cfg, "etc/msr_feature_control", val, sizeof(*val)); +} + static void pc_machine_done(Notifier *notifier, void *data) { @@ -1174,6 +1221,7 @@ void pc_machine_done(Notifier *notifier, void *data) acpi_setup(); if (pcms->fw_cfg) { pc_build_smbios(pcms->fw_cfg); + pc_build_feature_control_file(pcms); } } @@ -1919,7 +1967,7 @@ static void pc_machine_initfn(Object *obj) pc_machine_get_hotplug_memory_region_size, NULL, NULL, NULL, &error_abort); - pcms->max_ram_below_4g = 0xe0000000; /* 3.5G */ + pcms->max_ram_below_4g = 0; /* use default */ object_property_add(obj, PC_MACHINE_MAX_RAM_BELOW_4G, "size", pc_machine_get_max_ram_below_4g, pc_machine_set_max_ram_below_4g, diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index c7d70af253..a07dc816bf 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -108,37 +108,43 @@ static void pc_init1(MachineState *machine, * so legacy non-PAE guests can get as much memory as possible in * the 32bit address space below 4G. * + * - Note that Xen has its own ram setp code in xen_ram_init(), + * called via xen_hvm_init(). + * * Examples: * qemu -M pc-1.7 -m 4G (old default) -> 3584M low, 512M high * qemu -M pc -m 4G (new default) -> 3072M low, 1024M high * qemu -M pc,max-ram-below-4g=2G -m 4G -> 2048M low, 2048M high * qemu -M pc,max-ram-below-4g=4G -m 3968M -> 3968M low (=4G-128M) */ - lowmem = pcms->max_ram_below_4g; - if (machine->ram_size >= pcms->max_ram_below_4g) { - if (pcmc->gigabyte_align) { - if (lowmem > 0xc0000000) { - lowmem = 0xc0000000; - } - if (lowmem & ((1ULL << 30) - 1)) { - error_report("Warning: Large machine and max_ram_below_4g " - "(%" PRIu64 ") not a multiple of 1G; " - "possible bad performance.", - pcms->max_ram_below_4g); + if (xen_enabled()) { + xen_hvm_init(pcms, &ram_memory); + } else { + if (!pcms->max_ram_below_4g) { + pcms->max_ram_below_4g = 0xe0000000; /* default: 3.5G */ + } + lowmem = pcms->max_ram_below_4g; + if (machine->ram_size >= pcms->max_ram_below_4g) { + if (pcmc->gigabyte_align) { + if (lowmem > 0xc0000000) { + lowmem = 0xc0000000; + } + if (lowmem & ((1ULL << 30) - 1)) { + error_report("Warning: Large machine and max_ram_below_4g " + "(%" PRIu64 ") not a multiple of 1G; " + "possible bad performance.", + pcms->max_ram_below_4g); + } } } - } - if (machine->ram_size >= lowmem) { - pcms->above_4g_mem_size = machine->ram_size - lowmem; - pcms->below_4g_mem_size = lowmem; - } else { - pcms->above_4g_mem_size = 0; - pcms->below_4g_mem_size = machine->ram_size; - } - - if (xen_enabled()) { - xen_hvm_init(pcms, &ram_memory); + if (machine->ram_size >= lowmem) { + pcms->above_4g_mem_size = machine->ram_size - lowmem; + pcms->below_4g_mem_size = lowmem; + } else { + pcms->above_4g_mem_size = 0; + pcms->below_4g_mem_size = machine->ram_size; + } } pc_cpus_init(pcms); diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 04b2684d37..c0b9961928 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -94,6 +94,9 @@ static void pc_q35_init(MachineState *machine) /* Handle the machine opt max-ram-below-4g. It is basically doing * min(qemu limit, user limit). */ + if (!pcms->max_ram_below_4g) { + pcms->max_ram_below_4g = 1ULL << 32; /* default: 4G */; + } if (lowmem > pcms->max_ram_below_4g) { lowmem = pcms->max_ram_below_4g; if (machine->ram_size - lowmem > lowmem && @@ -176,7 +179,6 @@ static void pc_q35_init(MachineState *machine) qdev_init_nofail(DEVICE(q35_host)); phb = PCI_HOST_BRIDGE(q35_host); host_bus = phb->bus; - pcms->bus = phb->bus; /* create ISA bus */ lpc = pci_create_simple_multifunction(host_bus, PCI_DEVFN(ICH9_LPC_DEV, ICH9_LPC_FUNC), true, @@ -287,6 +289,7 @@ static void pc_q35_machine_options(MachineClass *m) m->default_machine_opts = "firmware=bios-256k.bin"; m->default_display = "std"; m->no_floppy = 1; + m->has_dynamic_sysbus = true; } static void pc_q35_2_7_machine_options(MachineClass *m) diff --git a/hw/ide/ich.c b/hw/ide/ich.c index 0a13334baa..920ec276ed 100644 --- a/hw/ide/ich.c +++ b/hw/ide/ich.c @@ -68,7 +68,6 @@ #include <hw/isa/isa.h> #include "sysemu/block-backend.h" #include "sysemu/dma.h" - #include <hw/ide/pci.h> #include <hw/ide/ahci.h> @@ -111,6 +110,7 @@ static void pci_ich9_ahci_realize(PCIDevice *dev, Error **errp) int sata_cap_offset; uint8_t *sata_cap; d = ICH_AHCI(dev); + int ret; ahci_realize(&d->ahci, DEVICE(dev), pci_get_address_space(dev), 6); @@ -146,7 +146,10 @@ static void pci_ich9_ahci_realize(PCIDevice *dev, Error **errp) /* Although the AHCI 1.3 specification states that the first capability * should be PMCAP, the Intel ICH9 data sheet specifies that the ICH9 * AHCI device puts the MSI capability first, pointing to 0x80. */ - msi_init(dev, ICH9_MSI_CAP_OFFSET, 1, true, false); + ret = msi_init(dev, ICH9_MSI_CAP_OFFSET, 1, true, false, NULL); + /* Any error other than -ENOTSUP(board's MSI support is broken) + * is a programming error. Fall back to INTx silently on -ENOTSUP */ + assert(!ret || ret == -ENOTSUP); } static void pci_ich9_uninit(PCIDevice *dev) diff --git a/hw/input/hid.c b/hw/input/hid.c index d92c7463ba..5e2850e655 100644 --- a/hw/input/hid.c +++ b/hw/input/hid.c @@ -27,6 +27,7 @@ #include "ui/console.h" #include "qemu/timer.h" #include "hw/input/hid.h" +#include "trace.h" #define HID_USAGE_ERROR_ROLLOVER 0x01 #define HID_USAGE_POSTFAIL 0x02 @@ -234,7 +235,7 @@ static void hid_keyboard_event(DeviceState *dev, QemuConsole *src, key->down, scancodes); if (hs->n + count > QUEUE_LENGTH) { - fprintf(stderr, "usb-kbd: warning: key event queue full\n"); + trace_hid_kbd_queue_full(); return; } for (i = 0; i < count; i++) { diff --git a/hw/input/trace-events b/hw/input/trace-events index 00fcec12b9..f24dff2f8b 100644 --- a/hw/input/trace-events +++ b/hw/input/trace-events @@ -23,3 +23,9 @@ milkymist_softusb_memory_write(uint32_t addr, uint32_t value) "addr %08x value % milkymist_softusb_mevt(uint8_t m) "m %d" milkymist_softusb_kevt(uint8_t m) "m %d" milkymist_softusb_pulse_irq(void) "Pulse IRQ" + +# hw/input/hid.c +hid_kbd_queue_full(void) "queue full" + +# hw/input/virtio +virtio_input_queue_full(void) "queue full" diff --git a/hw/input/virtio-input.c b/hw/input/virtio-input.c index f59749a943..edf69903a6 100644 --- a/hw/input/virtio-input.c +++ b/hw/input/virtio-input.c @@ -7,6 +7,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/iov.h" +#include "trace.h" #include "hw/qdev.h" #include "hw/virtio/virtio.h" @@ -47,7 +48,7 @@ void virtio_input_send(VirtIOInput *vinput, virtio_input_event *event) virtqueue_get_avail_bytes(vinput->evt, &have, NULL, need, 0); if (have < need) { vinput->qindex = 0; - fprintf(stderr, "%s: ENOSPC in vq, dropping events\n", __func__); + trace_virtio_input_queue_full(); return; } diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 890d5d7442..06d8db6bd6 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -187,11 +187,11 @@ static uint32_t nvic_readl(nvic_state *s, uint32_t offset) case 0x1c: /* SysTick Calibration Value. */ return 10000; case 0xd00: /* CPUID Base. */ - cpu = ARM_CPU(current_cpu); + cpu = ARM_CPU(qemu_get_cpu(0)); return cpu->midr; case 0xd04: /* Interrupt Control State. */ /* VECTACTIVE */ - cpu = ARM_CPU(current_cpu); + cpu = ARM_CPU(qemu_get_cpu(0)); val = cpu->env.v7m.exception; if (val == 1023) { val = 0; @@ -222,7 +222,7 @@ static uint32_t nvic_readl(nvic_state *s, uint32_t offset) val |= (1 << 31); return val; case 0xd08: /* Vector Table Offset. */ - cpu = ARM_CPU(current_cpu); + cpu = ARM_CPU(qemu_get_cpu(0)); return cpu->env.v7m.vecbase; case 0xd0c: /* Application Interrupt/Reset Control. */ return 0xfa050000; @@ -349,7 +349,7 @@ static void nvic_writel(nvic_state *s, uint32_t offset, uint32_t value) } break; case 0xd08: /* Vector Table Offset. */ - cpu = ARM_CPU(current_cpu); + cpu = ARM_CPU(qemu_get_cpu(0)); cpu->env.v7m.vecbase = value & 0xffffff80; break; case 0xd0c: /* Application Interrupt/Reset Control. */ diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c index 07527b677b..4968bdbb28 100644 --- a/hw/microblaze/petalogix_ml605_mmu.c +++ b/hw/microblaze/petalogix_ml605_mmu.c @@ -191,9 +191,16 @@ petalogix_ml605_init(MachineState *machine) spi = (SSIBus *)qdev_get_child_bus(dev, "spi"); for (i = 0; i < NUM_SPI_FLASHES; i++) { + DriveInfo *dinfo = drive_get_next(IF_MTD); qemu_irq cs_line; - dev = ssi_create_slave(spi, "n25q128"); + dev = ssi_create_slave_no_init(spi, "n25q128"); + if (dinfo) { + qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(dinfo), + &error_fatal); + } + qdev_init_nofail(dev); + cs_line = qdev_get_gpio_in_named(dev, SSI_GPIO_CS, 0); sysbus_connect_irq(busdev, i+1, cs_line); } diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 54020aa06c..4cfbd1024a 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -51,5 +51,5 @@ obj-$(CONFIG_MIPS_ITU) += mips_itu.o obj-$(CONFIG_PVPANIC) += pvpanic.o obj-$(CONFIG_EDU) += edu.o obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o -obj-$(CONFIG_AUX) += aux.o +obj-$(CONFIG_AUX) += auxbus.o obj-$(CONFIG_ASPEED_SOC) += aspeed_scu.o diff --git a/hw/misc/aux.c b/hw/misc/auxbus.c index 25d7712398..e4a7ba41de 100644 --- a/hw/misc/aux.c +++ b/hw/misc/auxbus.c @@ -1,5 +1,5 @@ /* - * aux.c + * auxbus.c * * Copyright 2015 : GreenSocs Ltd * http://www.greensocs.com/ , email: info@greensocs.com @@ -28,7 +28,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" -#include "hw/misc/aux.h" +#include "hw/misc/auxbus.h" #include "hw/i2c/i2c.h" #include "monitor/monitor.h" @@ -153,12 +153,12 @@ AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address, case WRITE_I2C_MOT: case READ_I2C_MOT: is_write = cmd == READ_I2C_MOT ? false : true; + ret = AUX_I2C_NACK; if (!i2c_bus_busy(i2c_bus)) { /* * No transactions started.. */ if (i2c_start_transfer(i2c_bus, address, is_write)) { - ret = AUX_I2C_NACK; break; } } else if ((address != bus->last_i2c_address) || @@ -168,22 +168,22 @@ AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address, */ i2c_end_transfer(i2c_bus); if (i2c_start_transfer(i2c_bus, address, is_write)) { - ret = AUX_I2C_NACK; break; } } + bus->last_transaction = cmd; + bus->last_i2c_address = address; while (len > 0) { if (i2c_send_recv(i2c_bus, data++, is_write) < 0) { - ret = AUX_I2C_NACK; i2c_end_transfer(i2c_bus); break; } len--; } - bus->last_transaction = cmd; - bus->last_i2c_address = address; - ret = AUX_I2C_ACK; + if (len == 0) { + ret = AUX_I2C_ACK; + } break; default: DPRINTF("Not implemented!\n"); diff --git a/hw/misc/imx6_ccm.c b/hw/misc/imx6_ccm.c index ec58eef92d..17e15d4c92 100644 --- a/hw/misc/imx6_ccm.c +++ b/hw/misc/imx6_ccm.c @@ -371,6 +371,12 @@ static uint32_t imx6_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock) case CLK_32k: freq = CKIL_FREQ; break; + case CLK_HIGH: + freq = 24000000; + break; + case CLK_HIGH_DIV: + freq = 24000000 / 8; + break; default: qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n", TYPE_IMX6_CCM, __func__, clock); diff --git a/hw/misc/max111x.c b/hw/misc/max111x.c index 9014f0f705..2a277bdb86 100644 --- a/hw/misc/max111x.c +++ b/hw/misc/max111x.c @@ -147,14 +147,14 @@ static int max111x_init(SSISlave *d, int inputs) return 0; } -static int max1110_init(SSISlave *dev) +static void max1110_realize(SSISlave *dev, Error **errp) { - return max111x_init(dev, 8); + max111x_init(dev, 8); } -static int max1111_init(SSISlave *dev) +static void max1111_realize(SSISlave *dev, Error **errp) { - return max111x_init(dev, 4); + max111x_init(dev, 4); } void max111x_set_input(DeviceState *dev, int line, uint8_t value) @@ -183,7 +183,7 @@ static void max1110_class_init(ObjectClass *klass, void *data) { SSISlaveClass *k = SSI_SLAVE_CLASS(klass); - k->init = max1110_init; + k->realize = max1110_realize; } static const TypeInfo max1110_info = { @@ -196,7 +196,7 @@ static void max1111_class_init(ObjectClass *klass, void *data) { SSISlaveClass *k = SSI_SLAVE_CLASS(klass); - k->init = max1111_init; + k->realize = max1111_realize; } static const TypeInfo max1111_info = { diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c index 692283fdd7..b4758bc441 100644 --- a/hw/net/e1000e.c +++ b/hw/net/e1000e.c @@ -89,8 +89,7 @@ typedef struct E1000EState { #define E1000E_MSIX_TABLE (0x0000) #define E1000E_MSIX_PBA (0x2000) -#define E1000E_USE_MSI BIT(0) -#define E1000E_USE_MSIX BIT(1) +#define E1000E_USE_MSIX BIT(0) static uint64_t e1000e_mmio_read(void *opaque, hwaddr addr, unsigned size) @@ -264,32 +263,6 @@ static void e1000e_core_realize(E1000EState *s) } static void -e1000e_init_msi(E1000EState *s) -{ - int res; - - res = msi_init(PCI_DEVICE(s), - 0xD0, /* MSI capability offset */ - 1, /* MAC MSI interrupts */ - true, /* 64-bit message addresses supported */ - false); /* Per vector mask supported */ - - if (res > 0) { - s->intr_state |= E1000E_USE_MSI; - } else { - trace_e1000e_msi_init_fail(res); - } -} - -static void -e1000e_cleanup_msi(E1000EState *s) -{ - if (s->intr_state & E1000E_USE_MSI) { - msi_uninit(PCI_DEVICE(s)); - } -} - -static void e1000e_unuse_msix_vectors(E1000EState *s, int num_vectors) { int i; @@ -444,6 +417,7 @@ static void e1000e_pci_realize(PCIDevice *pci_dev, Error **errp) static const uint16_t e1000e_dsn_offset = 0x140; E1000EState *s = E1000E(pci_dev); uint8_t *macaddr; + int ret; trace_e1000e_cb_pci_realize(); @@ -493,7 +467,10 @@ static void e1000e_pci_realize(PCIDevice *pci_dev, Error **errp) hw_error("Failed to initialize PCIe capability"); } - e1000e_init_msi(s); + ret = msi_init(PCI_DEVICE(s), 0xD0, 1, true, false, NULL); + if (ret) { + trace_e1000e_msi_init_fail(ret); + } if (e1000e_add_pm_capability(pci_dev, e1000e_pmrb_offset, PCI_PM_CAP_DSI) < 0) { @@ -532,7 +509,7 @@ static void e1000e_pci_uninit(PCIDevice *pci_dev) qemu_del_nic(s->nic); e1000e_cleanup_msix(s); - e1000e_cleanup_msi(s); + msi_uninit(pci_dev); } static void e1000e_qdev_reset(DeviceState *dev) @@ -693,6 +670,7 @@ static void e1000e_class_init(ObjectClass *class, void *data) c->vendor_id = PCI_VENDOR_ID_INTEL; c->device_id = E1000_DEV_ID_82574L; c->revision = 0; + c->romfile = "efi-e1000e.rom"; c->class_id = PCI_CLASS_NETWORK_ETHERNET; c->is_express = 1; diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 50f4dcd655..11fabc0b0a 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -172,7 +172,7 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) } r = vhost_dev_init(&net->dev, options->opaque, - options->backend_type); + options->backend_type, options->busyloop_timeout); if (r < 0) { goto fail; } diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 7e6a60aa12..999989934e 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -1542,33 +1542,11 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) { VirtIONet *n = opaque; VirtIODevice *vdev = VIRTIO_DEVICE(n); - int ret; if (version_id < 2 || version_id > VIRTIO_NET_VM_VERSION) return -EINVAL; - ret = virtio_load(vdev, f, version_id); - if (ret) { - return ret; - } - - if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { - n->curr_guest_offloads = qemu_get_be64(f); - } else { - n->curr_guest_offloads = virtio_net_supported_guest_offloads(n); - } - - if (peer_has_vnet_hdr(n)) { - virtio_net_apply_guest_offloads(n); - } - - if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE) && - virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) { - n->announce_counter = SELF_ANNOUNCE_ROUNDS; - timer_mod(n->announce_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL)); - } - - return 0; + return virtio_load(vdev, f, version_id); } static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f, @@ -1665,6 +1643,16 @@ static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f, } } + if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { + n->curr_guest_offloads = qemu_get_be64(f); + } else { + n->curr_guest_offloads = virtio_net_supported_guest_offloads(n); + } + + if (peer_has_vnet_hdr(n)) { + virtio_net_apply_guest_offloads(n); + } + virtio_net_set_queues(n); /* Find the first multicast entry in the saved MAC filter */ @@ -1682,6 +1670,12 @@ static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f, qemu_get_subqueue(n->nic, i)->link_down = link_down; } + if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE) && + virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) { + n->announce_counter = SELF_ANNOUNCE_ROUNDS; + timer_mod(n->announce_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL)); + } + return 0; } diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index 92236d3919..e767fc64b8 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -283,8 +283,6 @@ typedef struct { /* Whether MSI-X support was installed successfully */ bool msix_used; - /* Whether MSI support was installed successfully */ - bool msi_used; hwaddr drv_shmem; hwaddr temp_shared_guest_driver_memory; @@ -366,7 +364,7 @@ static bool _vmxnet3_assert_interrupt_line(VMXNET3State *s, uint32_t int_idx) msix_notify(d, int_idx); return false; } - if (s->msi_used && msi_enabled(d)) { + if (msi_enabled(d)) { VMW_IRPRN("Sending MSI notification for vector %u", int_idx); msi_notify(d, int_idx); return false; @@ -390,7 +388,7 @@ static void _vmxnet3_deassert_interrupt_line(VMXNET3State *s, int lidx) * This function should never be called for MSI(X) interrupts * because deassertion never required for message interrupts */ - assert(!s->msi_used || !msi_enabled(d)); + assert(!msi_enabled(d)); VMW_IRPRN("Deasserting line for interrupt %u", lidx); pci_irq_deassert(d); @@ -427,7 +425,7 @@ static void vmxnet3_trigger_interrupt(VMXNET3State *s, int lidx) goto do_automask; } - if (s->msi_used && msi_enabled(d) && s->auto_int_masking) { + if (msi_enabled(d) && s->auto_int_masking) { goto do_automask; } @@ -1425,8 +1423,8 @@ static void vmxnet3_update_features(VMXNET3State *s) static bool vmxnet3_verify_intx(VMXNET3State *s, int intx) { - return s->msix_used || s->msi_used || (intx == - (pci_get_byte(s->parent_obj.config + PCI_INTERRUPT_PIN) - 1)); + return s->msix_used || msi_enabled(PCI_DEVICE(s)) + || intx == pci_get_byte(s->parent_obj.config + PCI_INTERRUPT_PIN) - 1; } static void vmxnet3_validate_interrupt_idx(bool is_msix, int idx) @@ -2216,35 +2214,12 @@ vmxnet3_cleanup_msix(VMXNET3State *s) } } -#define VMXNET3_USE_64BIT (true) -#define VMXNET3_PER_VECTOR_MASK (false) - -static bool -vmxnet3_init_msi(VMXNET3State *s) -{ - PCIDevice *d = PCI_DEVICE(s); - int res; - - res = msi_init(d, VMXNET3_MSI_OFFSET(s), VMXNET3_MAX_NMSIX_INTRS, - VMXNET3_USE_64BIT, VMXNET3_PER_VECTOR_MASK); - if (0 > res) { - VMW_WRPRN("Failed to initialize MSI, error %d", res); - s->msi_used = false; - } else { - s->msi_used = true; - } - - return s->msi_used; -} - static void vmxnet3_cleanup_msi(VMXNET3State *s) { PCIDevice *d = PCI_DEVICE(s); - if (s->msi_used) { - msi_uninit(d); - } + msi_uninit(d); } static void @@ -2298,10 +2273,15 @@ static uint64_t vmxnet3_device_serial_num(VMXNET3State *s) return dsn_payload; } + +#define VMXNET3_USE_64BIT (true) +#define VMXNET3_PER_VECTOR_MASK (false) + static void vmxnet3_pci_realize(PCIDevice *pci_dev, Error **errp) { DeviceState *dev = DEVICE(pci_dev); VMXNET3State *s = VMXNET3(pci_dev); + int ret; VMW_CBPRN("Starting init..."); @@ -2325,14 +2305,16 @@ static void vmxnet3_pci_realize(PCIDevice *pci_dev, Error **errp) /* Interrupt pin A */ pci_dev->config[PCI_INTERRUPT_PIN] = 0x01; + ret = msi_init(pci_dev, VMXNET3_MSI_OFFSET(s), VMXNET3_MAX_NMSIX_INTRS, + VMXNET3_USE_64BIT, VMXNET3_PER_VECTOR_MASK, NULL); + /* Any error other than -ENOTSUP(board's MSI support is broken) + * is a programming error. Fall back to INTx silently on -ENOTSUP */ + assert(!ret || ret == -ENOTSUP); + if (!vmxnet3_init_msix(s)) { VMW_WRPRN("Failed to initialize MSI-X, configuration is inconsistent."); } - if (!vmxnet3_init_msi(s)) { - VMW_WRPRN("Failed to initialize MSI, configuration is inconsistent."); - } - vmxnet3_net_init(s); if (pci_is_express(pci_dev)) { @@ -2719,6 +2701,7 @@ static void vmxnet3_class_init(ObjectClass *class, void *data) c->vendor_id = PCI_VENDOR_ID_VMWARE; c->device_id = PCI_DEVICE_ID_VMWARE_VMXNET3; c->revision = PCI_DEVICE_ID_VMWARE_VMXNET3_REVISION; + c->romfile = "efi-vmxnet3.rom"; c->class_id = PCI_CLASS_NETWORK_ETHERNET; c->subsystem_vendor_id = PCI_VENDOR_ID_VMWARE; c->subsystem_id = PCI_DEVICE_ID_VMWARE_VMXNET3; diff --git a/hw/pci-bridge/ioh3420.c b/hw/pci-bridge/ioh3420.c index b4a7806e2e..93c6f0b7a2 100644 --- a/hw/pci-bridge/ioh3420.c +++ b/hw/pci-bridge/ioh3420.c @@ -25,6 +25,7 @@ #include "hw/pci/msi.h" #include "hw/pci/pcie.h" #include "ioh3420.h" +#include "qapi/error.h" #define PCI_DEVICE_ID_IOH_EPORT 0x3420 /* D0:F0 express mode */ #define PCI_DEVICE_ID_IOH_REV 0x2 @@ -97,6 +98,7 @@ static int ioh3420_initfn(PCIDevice *d) PCIEPort *p = PCIE_PORT(d); PCIESlot *s = PCIE_SLOT(d); int rc; + Error *err = NULL; pci_bridge_initfn(d, TYPE_PCIE_BUS); pcie_port_init_reg(d); @@ -109,8 +111,10 @@ static int ioh3420_initfn(PCIDevice *d) rc = msi_init(d, IOH_EP_MSI_OFFSET, IOH_EP_MSI_NR_VECTOR, IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT, - IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT); + IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT, &err); if (rc < 0) { + assert(rc == -ENOTSUP); + error_report_err(err); goto err_bridge; } diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c index 41ca47b15a..5dbd933cc1 100644 --- a/hw/pci-bridge/pci_bridge_dev.c +++ b/hw/pci-bridge/pci_bridge_dev.c @@ -42,9 +42,10 @@ struct PCIBridgeDev { MemoryRegion bar; uint8_t chassis_nr; -#define PCI_BRIDGE_DEV_F_MSI_REQ 0 -#define PCI_BRIDGE_DEV_F_SHPC_REQ 1 +#define PCI_BRIDGE_DEV_F_SHPC_REQ 0 uint32_t flags; + + OnOffAuto msi; }; typedef struct PCIBridgeDev PCIBridgeDev; @@ -53,6 +54,7 @@ static int pci_bridge_dev_initfn(PCIDevice *dev) PCIBridge *br = PCI_BRIDGE(dev); PCIBridgeDev *bridge_dev = PCI_BRIDGE_DEV(dev); int err; + Error *local_err = NULL; pci_bridge_initfn(dev, TYPE_PCI_BUS); @@ -66,7 +68,7 @@ static int pci_bridge_dev_initfn(PCIDevice *dev) } } else { /* MSI is not applicable without SHPC */ - bridge_dev->flags &= ~(1 << PCI_BRIDGE_DEV_F_MSI_REQ); + bridge_dev->msi = ON_OFF_AUTO_OFF; } err = slotid_cap_init(dev, 0, bridge_dev->chassis_nr, 0); @@ -74,12 +76,23 @@ static int pci_bridge_dev_initfn(PCIDevice *dev) goto slotid_error; } - if ((bridge_dev->flags & (1 << PCI_BRIDGE_DEV_F_MSI_REQ)) && - msi_nonbroken) { - err = msi_init(dev, 0, 1, true, true); - if (err < 0) { + if (bridge_dev->msi != ON_OFF_AUTO_OFF) { + /* it means SHPC exists, because MSI is needed by SHPC */ + + err = msi_init(dev, 0, 1, true, true, &local_err); + /* Any error other than -ENOTSUP(board's MSI support is broken) + * is a programming error */ + assert(!err || err == -ENOTSUP); + if (err && bridge_dev->msi == ON_OFF_AUTO_ON) { + /* Can't satisfy user's explicit msi=on request, fail */ + error_append_hint(&local_err, "You have to use msi=auto (default) " + "or msi=off with this machine type.\n"); + error_report_err(local_err); goto msi_error; } + assert(!local_err || bridge_dev->msi == ON_OFF_AUTO_AUTO); + /* With msi=auto, we fall back to MSI off silently */ + error_free(local_err); } if (shpc_present(dev)) { @@ -147,8 +160,8 @@ static Property pci_bridge_dev_properties[] = { /* Note: 0 is not a legal chassis number. */ DEFINE_PROP_UINT8(PCI_BRIDGE_DEV_PROP_CHASSIS_NR, PCIBridgeDev, chassis_nr, 0), - DEFINE_PROP_BIT(PCI_BRIDGE_DEV_PROP_MSI, PCIBridgeDev, flags, - PCI_BRIDGE_DEV_F_MSI_REQ, true), + DEFINE_PROP_ON_OFF_AUTO(PCI_BRIDGE_DEV_PROP_MSI, PCIBridgeDev, msi, + ON_OFF_AUTO_AUTO), DEFINE_PROP_BIT(PCI_BRIDGE_DEV_PROP_SHPC, PCIBridgeDev, flags, PCI_BRIDGE_DEV_F_SHPC_REQ, true), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/pci-bridge/pci_expander_bridge.c b/hw/pci-bridge/pci_expander_bridge.c index ba320bd857..ab8612158d 100644 --- a/hw/pci-bridge/pci_expander_bridge.c +++ b/hw/pci-bridge/pci_expander_bridge.c @@ -149,6 +149,8 @@ static void pxb_host_class_init(ObjectClass *class, void *data) PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(class); dc->fw_name = "pci"; + /* Reason: Internal part of the pxb/pxb-pcie device, not usable by itself */ + dc->cannot_instantiate_with_device_add_yet = true; sbc->explicit_ofw_unit_address = pxb_host_ofw_unit_address; hc->root_bus_path = pxb_host_root_bus_path; } diff --git a/hw/pci-bridge/xio3130_downstream.c b/hw/pci-bridge/xio3130_downstream.c index e6d653de4f..f6149a302d 100644 --- a/hw/pci-bridge/xio3130_downstream.c +++ b/hw/pci-bridge/xio3130_downstream.c @@ -24,6 +24,7 @@ #include "hw/pci/msi.h" #include "hw/pci/pcie.h" #include "xio3130_downstream.h" +#include "qapi/error.h" #define PCI_DEVICE_ID_TI_XIO3130D 0x8233 /* downstream port */ #define XIO3130_REVISION 0x1 @@ -60,14 +61,17 @@ static int xio3130_downstream_initfn(PCIDevice *d) PCIEPort *p = PCIE_PORT(d); PCIESlot *s = PCIE_SLOT(d); int rc; + Error *err = NULL; pci_bridge_initfn(d, TYPE_PCIE_BUS); pcie_port_init_reg(d); rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR, XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT, - XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT); + XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT, &err); if (rc < 0) { + assert(rc == -ENOTSUP); + error_report_err(err); goto err_bridge; } diff --git a/hw/pci-bridge/xio3130_upstream.c b/hw/pci-bridge/xio3130_upstream.c index d97684474f..487edacc1d 100644 --- a/hw/pci-bridge/xio3130_upstream.c +++ b/hw/pci-bridge/xio3130_upstream.c @@ -24,6 +24,7 @@ #include "hw/pci/msi.h" #include "hw/pci/pcie.h" #include "xio3130_upstream.h" +#include "qapi/error.h" #define PCI_DEVICE_ID_TI_XIO3130U 0x8232 /* upstream port */ #define XIO3130_REVISION 0x2 @@ -56,14 +57,17 @@ static int xio3130_upstream_initfn(PCIDevice *d) { PCIEPort *p = PCIE_PORT(d); int rc; + Error *err = NULL; pci_bridge_initfn(d, TYPE_PCIE_BUS); pcie_port_init_reg(d); rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR, XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT, - XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT); + XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT, &err); if (rc < 0) { + assert(rc == -ENOTSUP); + error_report_err(err); goto err_bridge; } diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c index df2b0e26f5..f9218aa952 100644 --- a/hw/pci-host/piix.c +++ b/hw/pci-host/piix.c @@ -48,7 +48,7 @@ typedef struct I440FXState { PCIHostState parent_obj; - PcPciInfo pci_info; + Range pci_hole; uint64_t pci_hole64_size; uint32_t short_root_bus; } I440FXState; @@ -221,8 +221,12 @@ static void i440fx_pcihost_get_pci_hole_start(Object *obj, Visitor *v, Error **errp) { I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj); - uint32_t value = s->pci_info.w32.begin; + uint64_t val64; + uint32_t value; + val64 = range_is_empty(&s->pci_hole) ? 0 : range_lob(&s->pci_hole); + value = val64; + assert(value == val64); visit_type_uint32(v, name, &value, errp); } @@ -231,8 +235,12 @@ static void i440fx_pcihost_get_pci_hole_end(Object *obj, Visitor *v, Error **errp) { I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj); - uint32_t value = s->pci_info.w32.end; + uint64_t val64; + uint32_t value; + val64 = range_is_empty(&s->pci_hole) ? 0 : range_upb(&s->pci_hole) + 1; + value = val64; + assert(value == val64); visit_type_uint32(v, name, &value, errp); } @@ -242,10 +250,11 @@ static void i440fx_pcihost_get_pci_hole64_start(Object *obj, Visitor *v, { PCIHostState *h = PCI_HOST_BRIDGE(obj); Range w64; + uint64_t value; pci_bus_get_w64_range(h->bus, &w64); - - visit_type_uint64(v, name, &w64.begin, errp); + value = range_is_empty(&w64) ? 0 : range_lob(&w64); + visit_type_uint64(v, name, &value, errp); } static void i440fx_pcihost_get_pci_hole64_end(Object *obj, Visitor *v, @@ -254,16 +263,16 @@ static void i440fx_pcihost_get_pci_hole64_end(Object *obj, Visitor *v, { PCIHostState *h = PCI_HOST_BRIDGE(obj); Range w64; + uint64_t value; pci_bus_get_w64_range(h->bus, &w64); - - visit_type_uint64(v, name, &w64.end, errp); + value = range_is_empty(&w64) ? 0 : range_upb(&w64) + 1; + visit_type_uint64(v, name, &value, errp); } static void i440fx_pcihost_initfn(Object *obj) { PCIHostState *s = PCI_HOST_BRIDGE(obj); - I440FXState *d = I440FX_PCI_HOST_BRIDGE(obj); memory_region_init_io(&s->conf_mem, obj, &pci_host_conf_le_ops, s, "pci-conf-idx", 4); @@ -285,8 +294,6 @@ static void i440fx_pcihost_initfn(Object *obj) object_property_add(obj, PCI_HOST_PROP_PCI_HOLE64_END, "int", i440fx_pcihost_get_pci_hole64_end, NULL, NULL, NULL, NULL); - - d->pci_info.w32.end = IO_APIC_DEFAULT_ADDRESS; } static void i440fx_pcihost_realize(DeviceState *dev, Error **errp) @@ -347,7 +354,8 @@ PCIBus *i440fx_init(const char *host_type, const char *pci_type, f->ram_memory = ram_memory; i440fx = I440FX_PCI_HOST_BRIDGE(dev); - i440fx->pci_info.w32.begin = below_4g_mem_size; + range_set_bounds(&i440fx->pci_hole, below_4g_mem_size, + IO_APIC_DEFAULT_ADDRESS - 1); /* setup pci memory mapping */ pc_pci_as_mapping_init(OBJECT(f), f->system_memory, @@ -865,6 +873,8 @@ static void i440fx_pcihost_class_init(ObjectClass *klass, void *data) dc->realize = i440fx_pcihost_realize; dc->fw_name = "pci"; dc->props = i440fx_props; + /* Reason: needs to be wired up by pc_init1 */ + dc->cannot_instantiate_with_device_add_yet = true; } static const TypeInfo i440fx_pcihost_info = { diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index 03be05dc0d..344f77b10c 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -52,6 +52,7 @@ static void q35_host_realize(DeviceState *dev, Error **errp) pci->bus = pci_bus_new(DEVICE(s), "pcie.0", s->mch.pci_address_space, s->mch.address_space_io, 0, TYPE_PCIE_BUS); + PC_MACHINE(qdev_get_machine())->bus = pci->bus; qdev_set_parent_bus(DEVICE(&s->mch), BUS(pci->bus)); qdev_init_nofail(DEVICE(&s->mch)); } @@ -73,8 +74,13 @@ static void q35_host_get_pci_hole_start(Object *obj, Visitor *v, Error **errp) { Q35PCIHost *s = Q35_HOST_DEVICE(obj); - uint32_t value = s->mch.pci_info.w32.begin; + uint64_t val64; + uint32_t value; + val64 = range_is_empty(&s->mch.pci_hole) + ? 0 : range_lob(&s->mch.pci_hole); + value = val64; + assert(value == val64); visit_type_uint32(v, name, &value, errp); } @@ -83,8 +89,13 @@ static void q35_host_get_pci_hole_end(Object *obj, Visitor *v, Error **errp) { Q35PCIHost *s = Q35_HOST_DEVICE(obj); - uint32_t value = s->mch.pci_info.w32.end; + uint64_t val64; + uint32_t value; + val64 = range_is_empty(&s->mch.pci_hole) + ? 0 : range_upb(&s->mch.pci_hole) + 1; + value = val64; + assert(value == val64); visit_type_uint32(v, name, &value, errp); } @@ -94,10 +105,11 @@ static void q35_host_get_pci_hole64_start(Object *obj, Visitor *v, { PCIHostState *h = PCI_HOST_BRIDGE(obj); Range w64; + uint64_t value; pci_bus_get_w64_range(h->bus, &w64); - - visit_type_uint64(v, name, &w64.begin, errp); + value = range_is_empty(&w64) ? 0 : range_lob(&w64); + visit_type_uint64(v, name, &value, errp); } static void q35_host_get_pci_hole64_end(Object *obj, Visitor *v, @@ -106,10 +118,11 @@ static void q35_host_get_pci_hole64_end(Object *obj, Visitor *v, { PCIHostState *h = PCI_HOST_BRIDGE(obj); Range w64; + uint64_t value; pci_bus_get_w64_range(h->bus, &w64); - - visit_type_uint64(v, name, &w64.end, errp); + value = range_is_empty(&w64) ? 0 : range_upb(&w64) + 1; + visit_type_uint64(v, name, &value, errp); } static void q35_host_get_mmcfg_size(Object *obj, Visitor *v, const char *name, @@ -142,6 +155,8 @@ static void q35_host_class_init(ObjectClass *klass, void *data) hc->root_bus_path = q35_host_root_bus_path; dc->realize = q35_host_realize; dc->props = mch_props; + /* Reason: needs to be wired up by pc_q35_init */ + dc->cannot_instantiate_with_device_add_yet = true; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->fw_name = "pci"; } @@ -202,9 +217,9 @@ static void q35_host_initfn(Object *obj) * it's not a power of two, which means an MTRR * can't cover it exactly. */ - s->mch.pci_info.w32.begin = MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT + - MCH_HOST_BRIDGE_PCIEXBAR_MAX; - s->mch.pci_info.w32.end = IO_APIC_DEFAULT_ADDRESS; + range_set_bounds(&s->mch.pci_hole, + MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT + MCH_HOST_BRIDGE_PCIEXBAR_MAX, + IO_APIC_DEFAULT_ADDRESS - 1); } static const TypeInfo q35_host_info = { @@ -272,10 +287,7 @@ static void mch_update_pciexbar(MCHPCIState *mch) break; case MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_RVD: default: - enable = 0; - length = 0; abort(); - break; } addr = pciexbar & addr_mask; pcie_host_mmcfg_update(pehb, enable, addr, length); @@ -285,9 +297,13 @@ static void mch_update_pciexbar(MCHPCIState *mch) * which means an MTRR can't cover it exactly. */ if (enable) { - mch->pci_info.w32.begin = addr + length; + range_set_bounds(&mch->pci_hole, + addr + length, + IO_APIC_DEFAULT_ADDRESS - 1); } else { - mch->pci_info.w32.begin = MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT; + range_set_bounds(&mch->pci_hole, + MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT, + IO_APIC_DEFAULT_ADDRESS - 1); } } @@ -444,30 +460,6 @@ static void mch_reset(DeviceState *qdev) mch_update(mch); } -static AddressSpace *q35_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) -{ - IntelIOMMUState *s = opaque; - VTDAddressSpace *vtd_as; - - assert(0 <= devfn && devfn <= VTD_PCI_DEVFN_MAX); - - vtd_as = vtd_find_add_as(s, bus, devfn); - return &vtd_as->as; -} - -static void mch_init_dmar(MCHPCIState *mch) -{ - PCIBus *pci_bus = PCI_BUS(qdev_get_parent_bus(DEVICE(mch))); - - mch->iommu = INTEL_IOMMU_DEVICE(qdev_create(NULL, TYPE_INTEL_IOMMU_DEVICE)); - object_property_add_child(OBJECT(mch), "intel-iommu", - OBJECT(mch->iommu), NULL); - qdev_init_nofail(DEVICE(mch->iommu)); - sysbus_mmio_map(SYS_BUS_DEVICE(mch->iommu), 0, Q35_HOST_BRIDGE_IOMMU_ADDR); - - pci_setup_iommu(pci_bus, q35_host_dma_iommu, mch->iommu); -} - static void mch_realize(PCIDevice *d, Error **errp) { int i; @@ -526,10 +518,6 @@ static void mch_realize(PCIDevice *d, Error **errp) mch->pci_address_space, &mch->pam_regions[i+1], PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE, PAM_EXPAN_SIZE); } - /* Intel IOMMU (VT-d) */ - if (object_property_get_bool(qdev_get_machine(), "iommu", NULL)) { - mch_init_dmar(mch); - } } uint64_t mch_mcfg_base(void) diff --git a/hw/pci/msi.c b/hw/pci/msi.c index ed792251dd..a87b2278a3 100644 --- a/hw/pci/msi.c +++ b/hw/pci/msi.c @@ -22,6 +22,7 @@ #include "hw/pci/msi.h" #include "hw/xen/xen.h" #include "qemu/range.h" +#include "qapi/error.h" /* PCI_MSI_ADDRESS_LO */ #define PCI_MSI_ADDRESS_LO_MASK (~0x3) @@ -173,7 +174,8 @@ bool msi_enabled(const PCIDevice *dev) * If @msi64bit, make the device capable of sending a 64-bit message * address. * If @msi_per_vector_mask, make the device support per-vector masking. - * Return 0 on success, return -errno on error. + * @errp is for returning errors. + * Return 0 on success; set @errp and return -errno on error. * * -ENOTSUP means lacking msi support for a msi-capable platform. * -EINVAL means capability overlap, happens when @offset is non-zero, @@ -181,7 +183,8 @@ bool msi_enabled(const PCIDevice *dev) * if a real HW is broken. */ int msi_init(struct PCIDevice *dev, uint8_t offset, - unsigned int nr_vectors, bool msi64bit, bool msi_per_vector_mask) + unsigned int nr_vectors, bool msi64bit, + bool msi_per_vector_mask, Error **errp) { unsigned int vectors_order; uint16_t flags; @@ -189,6 +192,7 @@ int msi_init(struct PCIDevice *dev, uint8_t offset, int config_offset; if (!msi_nonbroken) { + error_setg(errp, "MSI is not supported by interrupt controller"); return -ENOTSUP; } @@ -212,7 +216,8 @@ int msi_init(struct PCIDevice *dev, uint8_t offset, } cap_size = msi_cap_sizeof(flags); - config_offset = pci_add_capability(dev, PCI_CAP_ID_MSI, offset, cap_size); + config_offset = pci_add_capability2(dev, PCI_CAP_ID_MSI, offset, + cap_size, errp); if (config_offset < 0) { return config_offset; } diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 4b585f47b6..149994b815 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -78,10 +78,37 @@ static const VMStateDescription vmstate_pcibus = { } }; +static void pci_init_bus_master(PCIDevice *pci_dev) +{ + AddressSpace *dma_as = pci_device_iommu_address_space(pci_dev); + + memory_region_init_alias(&pci_dev->bus_master_enable_region, + OBJECT(pci_dev), "bus master", + dma_as->root, 0, memory_region_size(dma_as->root)); + memory_region_set_enabled(&pci_dev->bus_master_enable_region, false); + address_space_init(&pci_dev->bus_master_as, + &pci_dev->bus_master_enable_region, pci_dev->name); +} + +static void pcibus_machine_done(Notifier *notifier, void *data) +{ + PCIBus *bus = container_of(notifier, PCIBus, machine_done); + int i; + + for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) { + if (bus->devices[i]) { + pci_init_bus_master(bus->devices[i]); + } + } +} + static void pci_bus_realize(BusState *qbus, Error **errp) { PCIBus *bus = PCI_BUS(qbus); + bus->machine_done.notify = pcibus_machine_done; + qemu_add_machine_init_done_notifier(&bus->machine_done); + vmstate_register(NULL, -1, &vmstate_pcibus, bus); } @@ -89,6 +116,8 @@ static void pci_bus_unrealize(BusState *qbus, Error **errp) { PCIBus *bus = PCI_BUS(qbus); + qemu_remove_machine_init_done_notifier(&bus->machine_done); + vmstate_unregister(NULL, &vmstate_pcibus, bus); } @@ -920,7 +949,6 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, PCIConfigReadFunc *config_read = pc->config_read; PCIConfigWriteFunc *config_write = pc->config_write; Error *local_err = NULL; - AddressSpace *dma_as; DeviceState *dev = DEVICE(pci_dev); pci_dev->bus = bus; @@ -961,15 +989,10 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, pci_dev->devfn = devfn; pci_dev->requester_id_cache = pci_req_id_cache_get(pci_dev); - dma_as = pci_device_iommu_address_space(pci_dev); - - memory_region_init_alias(&pci_dev->bus_master_enable_region, - OBJECT(pci_dev), "bus master", - dma_as->root, 0, memory_region_size(dma_as->root)); - memory_region_set_enabled(&pci_dev->bus_master_enable_region, false); - address_space_init(&pci_dev->bus_master_as, &pci_dev->bus_master_enable_region, - name); + if (qdev_hotplug) { + pci_init_bus_master(pci_dev); + } pstrcpy(pci_dev->name, sizeof(pci_dev->name), name); pci_dev->irq_state = 0; pci_config_alloc(pci_dev); @@ -1051,7 +1074,7 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, uint8_t type, MemoryRegion *memory) { PCIIORegion *r; - uint32_t addr; + uint32_t addr; /* offset in pci config space */ uint64_t wmask; pcibus_t size = memory_region_size(memory); @@ -1067,15 +1090,20 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, r->addr = PCI_BAR_UNMAPPED; r->size = size; r->type = type; - r->memory = NULL; + r->memory = memory; + r->address_space = type & PCI_BASE_ADDRESS_SPACE_IO + ? pci_dev->bus->address_space_io + : pci_dev->bus->address_space_mem; wmask = ~(size - 1); - addr = pci_bar(pci_dev, region_num); if (region_num == PCI_ROM_SLOT) { /* ROM enable bit is writable */ wmask |= PCI_ROM_ADDRESS_ENABLE; } + + addr = pci_bar(pci_dev, region_num); pci_set_long(pci_dev->config + addr, type); + if (!(r->type & PCI_BASE_ADDRESS_SPACE_IO) && r->type & PCI_BASE_ADDRESS_MEM_TYPE_64) { pci_set_quad(pci_dev->wmask + addr, wmask); @@ -1084,11 +1112,6 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, pci_set_long(pci_dev->wmask + addr, wmask & 0xffffffff); pci_set_long(pci_dev->cmask + addr, 0xffffffff); } - pci_dev->io_regions[region_num].memory = memory; - pci_dev->io_regions[region_num].address_space - = type & PCI_BASE_ADDRESS_SPACE_IO - ? pci_dev->bus->address_space_io - : pci_dev->bus->address_space_mem; } static void pci_update_vga(PCIDevice *pci_dev) @@ -2510,13 +2533,13 @@ static void pci_dev_get_w64(PCIBus *b, PCIDevice *dev, void *opaque) if (limit >= base) { Range pref_range; - pref_range.begin = base; - pref_range.end = limit + 1; + range_set_bounds(&pref_range, base, limit); range_extend(range, &pref_range); } } for (i = 0; i < PCI_NUM_REGIONS; ++i) { PCIIORegion *r = &dev->io_regions[i]; + pcibus_t lob, upb; Range region_range; if (!r->size || @@ -2524,16 +2547,17 @@ static void pci_dev_get_w64(PCIBus *b, PCIDevice *dev, void *opaque) !(r->type & PCI_BASE_ADDRESS_MEM_TYPE_64)) { continue; } - region_range.begin = pci_bar_address(dev, i, r->type, r->size); - region_range.end = region_range.begin + r->size; - if (region_range.begin == PCI_BAR_UNMAPPED) { + lob = pci_bar_address(dev, i, r->type, r->size); + upb = lob + r->size - 1; + if (lob == PCI_BAR_UNMAPPED) { continue; } - region_range.begin = MAX(region_range.begin, 0x1ULL << 32); + lob = MAX(lob, 0x1ULL << 32); - if (region_range.end - 1 >= region_range.begin) { + if (upb >= lob) { + range_set_bounds(®ion_range, lob, upb); range_extend(range, ®ion_range); } } @@ -2541,7 +2565,7 @@ static void pci_dev_get_w64(PCIBus *b, PCIDevice *dev, void *opaque) void pci_bus_get_w64_range(PCIBus *bus, Range *range) { - range->begin = range->end = 0; + range_make_empty(range); pci_for_each_device_under_bus(bus, pci_dev_get_w64, range); } diff --git a/hw/pci/pcie_aer.c b/hw/pci/pcie_aer.c index e2d4e68ba3..048ce6a424 100644 --- a/hw/pci/pcie_aer.c +++ b/hw/pci/pcie_aer.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "sysemu/sysemu.h" #include "qapi/qmp/types.h" +#include "qapi/qmp/qjson.h" #include "monitor/monitor.h" #include "hw/pci/pci_bridge.h" #include "hw/pci/pcie.h" diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index 5cc6608e50..91a3420f47 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -8,6 +8,7 @@ obj-$(CONFIG_PSERIES) += spapr_cpu_core.o ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy) obj-y += spapr_pci_vfio.o endif +obj-$(CONFIG_PSERIES) += spapr_rtas_ddw.o # PowerPC 4xx boards obj-y += ppc405_boards.o ppc4xx_devs.o ppc405_uc.o ppc440_bamboo.o obj-y += ppc4xx_pci.o diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 32e88b3786..7d2510658d 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -380,6 +380,7 @@ static void ppc_core99_init(MachineState *machine) pci_bus = pci_pmac_init(pic, get_system_memory(), get_system_io()); machine_arch = ARCH_MAC99; } + object_property_set_bool(OBJECT(pci_bus), true, "realized", &error_abort); machine->usb |= defaults_enabled() && !machine->usb_disabled; diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 78ebd9ee38..7f33a1b2b5 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1771,6 +1771,13 @@ static void ppc_spapr_init(MachineState *machine) spapr->vrma_adjust = 1; spapr->rma_size = MIN(spapr->rma_size, 0x10000000); } + + /* Actually we don't support unbounded RMA anymore since we + * added proper emulation of HV mode. The max we can get is + * 16G which also happens to be what we configure for PAPR + * mode so make sure we don't do anything bigger than that + */ + spapr->rma_size = MIN(spapr->rma_size, 0x400000000ull); } if (spapr->rma_size > node0_size) { @@ -2489,7 +2496,12 @@ DEFINE_SPAPR_MACHINE(2_7, "2.7", true); * pseries-2.6 */ #define SPAPR_COMPAT_2_6 \ - HW_COMPAT_2_6 + HW_COMPAT_2_6 \ + { \ + .driver = TYPE_SPAPR_PCI_HOST_BRIDGE,\ + .property = "ddw",\ + .value = stringify(off),\ + }, static void spapr_machine_2_6_instance_options(MachineState *machine) { diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index a384db5204..70b6b0b5ee 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -259,9 +259,9 @@ out: error_propagate(errp, local_err); } -static int spapr_cpu_core_realize_child(Object *child, void *opaque) +static void spapr_cpu_core_realize_child(Object *child, Error **errp) { - Error **errp = opaque, *local_err = NULL; + Error *local_err = NULL; sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); CPUState *cs = CPU(child); PowerPCCPU *cpu = POWERPC_CPU(cs); @@ -269,15 +269,14 @@ static int spapr_cpu_core_realize_child(Object *child, void *opaque) object_property_set_bool(child, true, "realized", &local_err); if (local_err) { error_propagate(errp, local_err); - return 1; + return; } spapr_cpu_init(spapr, cpu, &local_err); if (local_err) { error_propagate(errp, local_err); - return 1; + return; } - return 0; } static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) @@ -287,13 +286,13 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) const char *typename = object_class_get_name(sc->cpu_class); size_t size = object_type_get_instance_size(typename); Error *local_err = NULL; - Object *obj; - int i; + void *obj; + int i, j; sc->threads = g_malloc0(size * cc->nr_threads); for (i = 0; i < cc->nr_threads; i++) { char id[32]; - void *obj = sc->threads + i * size; + obj = sc->threads + i * size; object_initialize(obj, size, typename); snprintf(id, sizeof(id), "thread[%d]", i); @@ -303,12 +302,16 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) } object_unref(obj); } - object_child_foreach(OBJECT(dev), spapr_cpu_core_realize_child, &local_err); - if (local_err) { - goto err; - } else { - return; + + for (j = 0; j < cc->nr_threads; j++) { + obj = sc->threads + j * size; + + spapr_cpu_core_realize_child(obj, &local_err); + if (local_err) { + goto err; + } } + return; err: while (--i >= 0) { diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c index d276db3a72..26a067951c 100644 --- a/hw/ppc/spapr_drc.c +++ b/hw/ppc/spapr_drc.c @@ -300,7 +300,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name, /* shouldn't ever see an FDT_END_NODE before FDT_BEGIN_NODE */ g_assert(fdt_depth > 0); visit_check_struct(v, &err); - visit_end_struct(v); + visit_end_struct(v, NULL); if (err) { error_propagate(errp, err); return; @@ -323,7 +323,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name, return; } } - visit_end_list(v); + visit_end_list(v, NULL); break; } default: diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index e011ed4b66..73af112e1d 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -83,12 +83,12 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong pte_index = args[1]; target_ulong pteh = args[2]; target_ulong ptel = args[3]; - unsigned apshift, spshift; + unsigned apshift; target_ulong raddr; target_ulong index; uint64_t token; - apshift = ppc_hash64_hpte_page_shift_noslb(cpu, pteh, ptel, &spshift); + apshift = ppc_hash64_hpte_page_shift_noslb(cpu, pteh, ptel); if (!apshift) { /* Bad page size encoding */ return H_PARAMETER; diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index e230bacae1..d57b05d5c0 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -156,6 +156,16 @@ static uint64_t spapr_tce_get_min_page_size(MemoryRegion *iommu) return 1ULL << tcet->page_shift; } +static void spapr_tce_notify_started(MemoryRegion *iommu) +{ + spapr_tce_set_need_vfio(container_of(iommu, sPAPRTCETable, iommu), true); +} + +static void spapr_tce_notify_stopped(MemoryRegion *iommu) +{ + spapr_tce_set_need_vfio(container_of(iommu, sPAPRTCETable, iommu), false); +} + static int spapr_tce_table_post_load(void *opaque, int version_id) { sPAPRTCETable *tcet = SPAPR_TCE_TABLE(opaque); @@ -236,6 +246,8 @@ static const VMStateDescription vmstate_spapr_tce_table = { static MemoryRegionIOMMUOps spapr_iommu_ops = { .translate = spapr_tce_translate_iommu, .get_min_page_size = spapr_tce_get_min_page_size, + .notify_started = spapr_tce_notify_started, + .notify_stopped = spapr_tce_notify_stopped, }; static int spapr_tce_table_realize(DeviceState *dev) diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 8c1e6b17c3..949c44fec8 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -35,6 +35,7 @@ #include "hw/ppc/spapr.h" #include "hw/pci-host/spapr.h" #include "exec/address-spaces.h" +#include "exec/ram_addr.h" #include <libfdt.h> #include "trace.h" #include "qemu/error-report.h" @@ -45,6 +46,7 @@ #include "hw/ppc/spapr_drc.h" #include "sysemu/device_tree.h" #include "sysemu/kvm.h" +#include "sysemu/hostmem.h" #include "hw/vfio/vfio.h" @@ -1087,12 +1089,6 @@ static void spapr_phb_add_pci_device(sPAPRDRConnector *drc, void *fdt = NULL; int fdt_start_offset = 0, fdt_size; - if (object_dynamic_cast(OBJECT(pdev), "vfio-pci")) { - sPAPRTCETable *tcet = spapr_tce_find_by_liobn(phb->dma_liobn); - - spapr_tce_set_need_vfio(tcet, true); - } - fdt = create_device_tree(&fdt_size); fdt_start_offset = spapr_create_pci_child_dt(phb, pdev, fdt, 0); if (!fdt_start_offset) { @@ -1310,11 +1306,14 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) PCIBus *bus; uint64_t msi_window_size = 4096; sPAPRTCETable *tcet; + const unsigned windows_supported = + sphb->ddw_enabled ? SPAPR_PCI_DMA_MAX_WINDOWS : 1; if (sphb->index != (uint32_t)-1) { hwaddr windows_base; - if ((sphb->buid != (uint64_t)-1) || (sphb->dma_liobn != (uint32_t)-1) + if ((sphb->buid != (uint64_t)-1) || (sphb->dma_liobn[0] != (uint32_t)-1) + || (sphb->dma_liobn[1] != (uint32_t)-1 && windows_supported == 2) || (sphb->mem_win_addr != (hwaddr)-1) || (sphb->io_win_addr != (hwaddr)-1)) { error_setg(errp, "Either \"index\" or other parameters must" @@ -1329,7 +1328,9 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) } sphb->buid = SPAPR_PCI_BASE_BUID + sphb->index; - sphb->dma_liobn = SPAPR_PCI_LIOBN(sphb->index, 0); + for (i = 0; i < windows_supported; ++i) { + sphb->dma_liobn[i] = SPAPR_PCI_LIOBN(sphb->index, i); + } windows_base = SPAPR_PCI_WINDOW_BASE + sphb->index * SPAPR_PCI_WINDOW_SPACING; @@ -1342,8 +1343,9 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) return; } - if (sphb->dma_liobn == (uint32_t)-1) { - error_setg(errp, "LIOBN not specified for PHB"); + if ((sphb->dma_liobn[0] == (uint32_t)-1) || + ((sphb->dma_liobn[1] == (uint32_t)-1) && (windows_supported > 1))) { + error_setg(errp, "LIOBN(s) not specified for PHB"); return; } @@ -1462,16 +1464,18 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) } } - tcet = spapr_tce_new_table(DEVICE(sphb), sphb->dma_liobn); - if (!tcet) { - error_setg(errp, "Unable to create TCE table for %s", - sphb->dtbusname); - return; + /* DMA setup */ + for (i = 0; i < windows_supported; ++i) { + tcet = spapr_tce_new_table(DEVICE(sphb), sphb->dma_liobn[i]); + if (!tcet) { + error_setg(errp, "Creating window#%d failed for %s", + i, sphb->dtbusname); + return; + } + memory_region_add_subregion_overlap(&sphb->iommu_root, 0, + spapr_tce_get_iommu(tcet), 0); } - memory_region_add_subregion_overlap(&sphb->iommu_root, 0, - spapr_tce_get_iommu(tcet), 0); - sphb->msi = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free); } @@ -1488,13 +1492,19 @@ static int spapr_phb_children_reset(Object *child, void *opaque) void spapr_phb_dma_reset(sPAPRPHBState *sphb) { - sPAPRTCETable *tcet = spapr_tce_find_by_liobn(sphb->dma_liobn); + int i; + sPAPRTCETable *tcet; - if (tcet && tcet->nb_table) { - spapr_tce_table_disable(tcet); + for (i = 0; i < SPAPR_PCI_DMA_MAX_WINDOWS; ++i) { + tcet = spapr_tce_find_by_liobn(sphb->dma_liobn[i]); + + if (tcet && tcet->nb_table) { + spapr_tce_table_disable(tcet); + } } /* Register default 32bit DMA window */ + tcet = spapr_tce_find_by_liobn(sphb->dma_liobn[0]); spapr_tce_table_enable(tcet, SPAPR_TCE_PAGE_SHIFT, sphb->dma_win_addr, sphb->dma_win_size >> SPAPR_TCE_PAGE_SHIFT); } @@ -1516,7 +1526,8 @@ static void spapr_phb_reset(DeviceState *qdev) static Property spapr_phb_properties[] = { DEFINE_PROP_UINT32("index", sPAPRPHBState, index, -1), DEFINE_PROP_UINT64("buid", sPAPRPHBState, buid, -1), - DEFINE_PROP_UINT32("liobn", sPAPRPHBState, dma_liobn, -1), + DEFINE_PROP_UINT32("liobn", sPAPRPHBState, dma_liobn[0], -1), + DEFINE_PROP_UINT32("liobn64", sPAPRPHBState, dma_liobn[1], -1), DEFINE_PROP_UINT64("mem_win_addr", sPAPRPHBState, mem_win_addr, -1), DEFINE_PROP_UINT64("mem_win_size", sPAPRPHBState, mem_win_size, SPAPR_PCI_MMIO_WIN_SIZE), @@ -1528,6 +1539,11 @@ static Property spapr_phb_properties[] = { /* Default DMA window is 0..1GB */ DEFINE_PROP_UINT64("dma_win_addr", sPAPRPHBState, dma_win_addr, 0), DEFINE_PROP_UINT64("dma_win_size", sPAPRPHBState, dma_win_size, 0x40000000), + DEFINE_PROP_UINT64("dma64_win_addr", sPAPRPHBState, dma64_win_addr, + 0x800000000000000ULL), + DEFINE_PROP_BOOL("ddw", sPAPRPHBState, ddw_enabled, true), + DEFINE_PROP_UINT64("pgsz", sPAPRPHBState, page_size_mask, + (1ULL << 12) | (1ULL << 16)), DEFINE_PROP_END_OF_LIST(), }; @@ -1604,7 +1620,7 @@ static const VMStateDescription vmstate_spapr_pci = { .post_load = spapr_pci_post_load, .fields = (VMStateField[]) { VMSTATE_UINT64_EQUAL(buid, sPAPRPHBState), - VMSTATE_UINT32_EQUAL(dma_liobn, sPAPRPHBState), + VMSTATE_UINT32_EQUAL(dma_liobn[0], sPAPRPHBState), VMSTATE_UINT64_EQUAL(mem_win_addr, sPAPRPHBState), VMSTATE_UINT64_EQUAL(mem_win_size, sPAPRPHBState), VMSTATE_UINT64_EQUAL(io_win_addr, sPAPRPHBState), @@ -1780,6 +1796,15 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, uint32_t interrupt_map_mask[] = { cpu_to_be32(b_ddddd(-1)|b_fff(0)), 0x0, 0x0, cpu_to_be32(-1)}; uint32_t interrupt_map[PCI_SLOT_MAX * PCI_NUM_PINS][7]; + uint32_t ddw_applicable[] = { + cpu_to_be32(RTAS_IBM_QUERY_PE_DMA_WINDOW), + cpu_to_be32(RTAS_IBM_CREATE_PE_DMA_WINDOW), + cpu_to_be32(RTAS_IBM_REMOVE_PE_DMA_WINDOW) + }; + uint32_t ddw_extensions[] = { + cpu_to_be32(1), + cpu_to_be32(RTAS_IBM_RESET_PE_DMA_WINDOW) + }; sPAPRTCETable *tcet; PCIBus *bus = PCI_HOST_BRIDGE(phb)->bus; sPAPRFDT s_fdt; @@ -1804,6 +1829,14 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, _FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pci-config-space-type", 0x1)); _FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pe-total-#msi", XICS_IRQS_SPAPR)); + /* Dynamic DMA window */ + if (phb->ddw_enabled) { + _FDT(fdt_setprop(fdt, bus_off, "ibm,ddw-applicable", &ddw_applicable, + sizeof(ddw_applicable))); + _FDT(fdt_setprop(fdt, bus_off, "ibm,ddw-extensions", + &ddw_extensions, sizeof(ddw_extensions))); + } + /* Build the interrupt-map, this must matches what is done * in pci_spapr_map_irq */ @@ -1827,7 +1860,7 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, _FDT(fdt_setprop(fdt, bus_off, "interrupt-map", &interrupt_map, sizeof(interrupt_map))); - tcet = spapr_tce_find_by_liobn(phb->dma_liobn); + tcet = spapr_tce_find_by_liobn(phb->dma_liobn[0]); if (!tcet) { return -1; } diff --git a/hw/ppc/spapr_rtas_ddw.c b/hw/ppc/spapr_rtas_ddw.c new file mode 100644 index 0000000000..177dcffc9b --- /dev/null +++ b/hw/ppc/spapr_rtas_ddw.c @@ -0,0 +1,295 @@ +/* + * QEMU sPAPR Dynamic DMA windows support + * + * Copyright (c) 2015 Alexey Kardashevskiy, IBM Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "qemu/error-report.h" +#include "hw/ppc/spapr.h" +#include "hw/pci-host/spapr.h" +#include "trace.h" + +static int spapr_phb_get_active_win_num_cb(Object *child, void *opaque) +{ + sPAPRTCETable *tcet; + + tcet = (sPAPRTCETable *) object_dynamic_cast(child, TYPE_SPAPR_TCE_TABLE); + if (tcet && tcet->nb_table) { + ++*(unsigned *)opaque; + } + return 0; +} + +static unsigned spapr_phb_get_active_win_num(sPAPRPHBState *sphb) +{ + unsigned ret = 0; + + object_child_foreach(OBJECT(sphb), spapr_phb_get_active_win_num_cb, &ret); + + return ret; +} + +static int spapr_phb_get_free_liobn_cb(Object *child, void *opaque) +{ + sPAPRTCETable *tcet; + + tcet = (sPAPRTCETable *) object_dynamic_cast(child, TYPE_SPAPR_TCE_TABLE); + if (tcet && !tcet->nb_table) { + *(uint32_t *)opaque = tcet->liobn; + return 1; + } + return 0; +} + +static unsigned spapr_phb_get_free_liobn(sPAPRPHBState *sphb) +{ + uint32_t liobn = 0; + + object_child_foreach(OBJECT(sphb), spapr_phb_get_free_liobn_cb, &liobn); + + return liobn; +} + +static uint32_t spapr_page_mask_to_query_mask(uint64_t page_mask) +{ + int i; + uint32_t mask = 0; + const struct { int shift; uint32_t mask; } masks[] = { + { 12, RTAS_DDW_PGSIZE_4K }, + { 16, RTAS_DDW_PGSIZE_64K }, + { 24, RTAS_DDW_PGSIZE_16M }, + { 25, RTAS_DDW_PGSIZE_32M }, + { 26, RTAS_DDW_PGSIZE_64M }, + { 27, RTAS_DDW_PGSIZE_128M }, + { 28, RTAS_DDW_PGSIZE_256M }, + { 34, RTAS_DDW_PGSIZE_16G }, + }; + + for (i = 0; i < ARRAY_SIZE(masks); ++i) { + if (page_mask & (1ULL << masks[i].shift)) { + mask |= masks[i].mask; + } + } + + return mask; +} + +static void rtas_ibm_query_pe_dma_window(PowerPCCPU *cpu, + sPAPRMachineState *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, + uint32_t nret, target_ulong rets) +{ + sPAPRPHBState *sphb; + uint64_t buid, max_window_size; + uint32_t avail, addr, pgmask = 0; + MachineState *machine = MACHINE(spapr); + + if ((nargs != 3) || (nret != 5)) { + goto param_error_exit; + } + + buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + addr = rtas_ld(args, 0); + sphb = spapr_pci_find_phb(spapr, buid); + if (!sphb || !sphb->ddw_enabled) { + goto param_error_exit; + } + + /* Translate page mask to LoPAPR format */ + pgmask = spapr_page_mask_to_query_mask(sphb->page_size_mask); + + /* + * This is "Largest contiguous block of TCEs allocated specifically + * for (that is, are reserved for) this PE". + * Return the maximum number as maximum supported RAM size was in 4K pages. + */ + if (machine->ram_size == machine->maxram_size) { + max_window_size = machine->ram_size; + } else { + MemoryHotplugState *hpms = &spapr->hotplug_memory; + + max_window_size = hpms->base + memory_region_size(&hpms->mr); + } + + avail = SPAPR_PCI_DMA_MAX_WINDOWS - spapr_phb_get_active_win_num(sphb); + + rtas_st(rets, 0, RTAS_OUT_SUCCESS); + rtas_st(rets, 1, avail); + rtas_st(rets, 2, max_window_size >> SPAPR_TCE_PAGE_SHIFT); + rtas_st(rets, 3, pgmask); + rtas_st(rets, 4, 0); /* DMA migration mask, not supported */ + + trace_spapr_iommu_ddw_query(buid, addr, avail, max_window_size, pgmask); + return; + +param_error_exit: + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); +} + +static void rtas_ibm_create_pe_dma_window(PowerPCCPU *cpu, + sPAPRMachineState *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, + uint32_t nret, target_ulong rets) +{ + sPAPRPHBState *sphb; + sPAPRTCETable *tcet = NULL; + uint32_t addr, page_shift, window_shift, liobn; + uint64_t buid, win_addr; + int windows; + + if ((nargs != 5) || (nret != 4)) { + goto param_error_exit; + } + + buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + addr = rtas_ld(args, 0); + sphb = spapr_pci_find_phb(spapr, buid); + if (!sphb || !sphb->ddw_enabled) { + goto param_error_exit; + } + + page_shift = rtas_ld(args, 3); + window_shift = rtas_ld(args, 4); + liobn = spapr_phb_get_free_liobn(sphb); + windows = spapr_phb_get_active_win_num(sphb); + + if (!(sphb->page_size_mask & (1ULL << page_shift)) || + (window_shift < page_shift)) { + goto param_error_exit; + } + + if (!liobn || !sphb->ddw_enabled || windows == SPAPR_PCI_DMA_MAX_WINDOWS) { + goto hw_error_exit; + } + + tcet = spapr_tce_find_by_liobn(liobn); + if (!tcet) { + goto hw_error_exit; + } + + win_addr = (windows == 0) ? sphb->dma_win_addr : sphb->dma64_win_addr; + spapr_tce_table_enable(tcet, page_shift, win_addr, + 1ULL << (window_shift - page_shift)); + if (!tcet->nb_table) { + goto hw_error_exit; + } + + trace_spapr_iommu_ddw_create(buid, addr, 1ULL << page_shift, + 1ULL << window_shift, tcet->bus_offset, liobn); + + rtas_st(rets, 0, RTAS_OUT_SUCCESS); + rtas_st(rets, 1, liobn); + rtas_st(rets, 2, tcet->bus_offset >> 32); + rtas_st(rets, 3, tcet->bus_offset & ((uint32_t) -1)); + + return; + +hw_error_exit: + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); + return; + +param_error_exit: + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); +} + +static void rtas_ibm_remove_pe_dma_window(PowerPCCPU *cpu, + sPAPRMachineState *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, + uint32_t nret, target_ulong rets) +{ + sPAPRPHBState *sphb; + sPAPRTCETable *tcet; + uint32_t liobn; + + if ((nargs != 1) || (nret != 1)) { + goto param_error_exit; + } + + liobn = rtas_ld(args, 0); + tcet = spapr_tce_find_by_liobn(liobn); + if (!tcet) { + goto param_error_exit; + } + + sphb = SPAPR_PCI_HOST_BRIDGE(OBJECT(tcet)->parent); + if (!sphb || !sphb->ddw_enabled || !tcet->nb_table) { + goto param_error_exit; + } + + spapr_tce_table_disable(tcet); + trace_spapr_iommu_ddw_remove(liobn); + + rtas_st(rets, 0, RTAS_OUT_SUCCESS); + return; + +param_error_exit: + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); +} + +static void rtas_ibm_reset_pe_dma_window(PowerPCCPU *cpu, + sPAPRMachineState *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, + uint32_t nret, target_ulong rets) +{ + sPAPRPHBState *sphb; + uint64_t buid; + uint32_t addr; + + if ((nargs != 3) || (nret != 1)) { + goto param_error_exit; + } + + buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + addr = rtas_ld(args, 0); + sphb = spapr_pci_find_phb(spapr, buid); + if (!sphb || !sphb->ddw_enabled) { + goto param_error_exit; + } + + spapr_phb_dma_reset(sphb); + trace_spapr_iommu_ddw_reset(buid, addr); + + rtas_st(rets, 0, RTAS_OUT_SUCCESS); + + return; + +param_error_exit: + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); +} + +static void spapr_rtas_ddw_init(void) +{ + spapr_rtas_register(RTAS_IBM_QUERY_PE_DMA_WINDOW, + "ibm,query-pe-dma-window", + rtas_ibm_query_pe_dma_window); + spapr_rtas_register(RTAS_IBM_CREATE_PE_DMA_WINDOW, + "ibm,create-pe-dma-window", + rtas_ibm_create_pe_dma_window); + spapr_rtas_register(RTAS_IBM_REMOVE_PE_DMA_WINDOW, + "ibm,remove-pe-dma-window", + rtas_ibm_remove_pe_dma_window); + spapr_rtas_register(RTAS_IBM_RESET_PE_DMA_WINDOW, + "ibm,reset-pe-dma-window", + rtas_ibm_reset_pe_dma_window); +} + +type_init(spapr_rtas_ddw_init) diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events index 6da713547f..900679bc9d 100644 --- a/hw/ppc/trace-events +++ b/hw/ppc/trace-events @@ -30,6 +30,10 @@ spapr_iommu_xlate(uint64_t liobn, uint64_t ioba, uint64_t tce, unsigned perm, un spapr_iommu_new_table(uint64_t liobn, void *table, int fd) "liobn=%"PRIx64" table=%p fd=%d" spapr_iommu_pre_save(uint64_t liobn, uint32_t nb, uint64_t offs, uint32_t ps) "liobn=%"PRIx64" %"PRIx32" bus_offset=%"PRIx64" ps=%"PRIu32 spapr_iommu_post_load(uint64_t liobn, uint32_t pre_nb, uint32_t post_nb, uint64_t offs, uint32_t ps) "liobn=%"PRIx64" %"PRIx32" => %"PRIx32" bus_offset=%"PRIx64" ps=%"PRIu32 +spapr_iommu_ddw_query(uint64_t buid, uint32_t cfgaddr, unsigned wa, uint64_t win_size, uint32_t pgmask) "buid=%"PRIx64" addr=%"PRIx32", %u windows available, max window size=%"PRIx64", mask=%"PRIx32 +spapr_iommu_ddw_create(uint64_t buid, uint32_t cfgaddr, uint64_t pg_size, uint64_t req_size, uint64_t start, uint32_t liobn) "buid=%"PRIx64" addr=%"PRIx32", page size=0x%"PRIx64", requested=0x%"PRIx64", start addr=%"PRIx64", liobn=%"PRIx32 +spapr_iommu_ddw_remove(uint32_t liobn) "liobn=%"PRIx32 +spapr_iommu_ddw_reset(uint64_t buid, uint32_t cfgaddr) "buid=%"PRIx64" addr=%"PRIx32 # hw/ppc/ppc.c ppc_tb_adjust(uint64_t offs1, uint64_t offs2, int64_t diff, int64_t seconds) "adjusted from 0x%"PRIx64" to 0x%"PRIx64", diff %"PRId64" (%"PRId64"s)" diff --git a/hw/s390x/Makefile.objs b/hw/s390x/Makefile.objs index 220361782d..41ac4ec325 100644 --- a/hw/s390x/Makefile.objs +++ b/hw/s390x/Makefile.objs @@ -8,6 +8,8 @@ obj-y += ipl.o obj-y += css.o obj-y += s390-virtio-ccw.o obj-y += virtio-ccw.o +obj-y += css-bridge.o +obj-y += ccw-device.o obj-y += s390-pci-bus.o s390-pci-inst.o obj-y += s390-skeys.o obj-$(CONFIG_KVM) += s390-skeys-kvm.o diff --git a/hw/s390x/ccw-device.c b/hw/s390x/ccw-device.c new file mode 100644 index 0000000000..28ea20440e --- /dev/null +++ b/hw/s390x/ccw-device.c @@ -0,0 +1,27 @@ +/* + * Common device infrastructure for devices in the virtual css + * + * Copyright 2016 IBM Corp. + * Author(s): Jing Liu <liujbjl@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ +#include "qemu/osdep.h" +#include "ccw-device.h" + +static const TypeInfo ccw_device_info = { + .name = TYPE_CCW_DEVICE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(CcwDevice), + .class_size = sizeof(CCWDeviceClass), + .abstract = true, +}; + +static void ccw_device_register(void) +{ + type_register_static(&ccw_device_info); +} + +type_init(ccw_device_register) diff --git a/hw/s390x/ccw-device.h b/hw/s390x/ccw-device.h new file mode 100644 index 0000000000..59ba01b6c5 --- /dev/null +++ b/hw/s390x/ccw-device.h @@ -0,0 +1,43 @@ +/* + * Common device infrastructure for devices in the virtual css + * + * Copyright 2016 IBM Corp. + * Author(s): Jing Liu <liujbjl@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#ifndef HW_S390X_CCW_DEVICE_H +#define HW_S390X_CCW_DEVICE_H +#include "qom/object.h" +#include "hw/qdev-core.h" +#include "hw/s390x/css.h" + +typedef struct CcwDevice { + DeviceState parent_obj; + SubchDev *sch; + /* <cssid>.<ssid>.<device number> */ + CssDevId bus_id; +} CcwDevice; + +typedef struct CCWDeviceClass { + DeviceClass parent_class; + void (*unplug)(HotplugHandler *, DeviceState *, Error **); +} CCWDeviceClass; + +static inline CcwDevice *to_ccw_dev_fast(DeviceState *d) +{ + return container_of(d, CcwDevice, parent_obj); +} + +#define TYPE_CCW_DEVICE "ccw-device" + +#define CCW_DEVICE(obj) OBJECT_CHECK(CcwDevice, (obj), TYPE_CCW_DEVICE) +#define CCW_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(CCWDeviceClass, (obj), TYPE_CCW_DEVICE) +#define CCW_DEVICE_CLASS(klass) \ + OBJECT_CLASS_CHECK(CCWDeviceClass, (klass), TYPE_CCW_DEVICE) + +#endif diff --git a/hw/s390x/css-bridge.c b/hw/s390x/css-bridge.c new file mode 100644 index 0000000000..e4c24e21f3 --- /dev/null +++ b/hw/s390x/css-bridge.c @@ -0,0 +1,124 @@ +/* + * css bridge implementation + * + * Copyright 2012,2016 IBM Corp. + * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> + * Pierre Morel <pmorel@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/hotplug.h" +#include "hw/sysbus.h" +#include "qemu/bitops.h" +#include "hw/s390x/css.h" +#include "ccw-device.h" +#include "hw/s390x/css-bridge.h" + +/* + * Invoke device-specific unplug handler, disable the subchannel + * (including sending a channel report to the guest) and remove the + * device from the virtual css bus. + */ +static void ccw_device_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + CcwDevice *ccw_dev = CCW_DEVICE(dev); + CCWDeviceClass *k = CCW_DEVICE_GET_CLASS(ccw_dev); + SubchDev *sch = ccw_dev->sch; + Error *err = NULL; + + if (k->unplug) { + k->unplug(hotplug_dev, dev, &err); + if (err) { + error_propagate(errp, err); + return; + } + } + + /* + * We should arrive here only for device_del, since we don't support + * direct hot(un)plug of channels. + */ + assert(sch != NULL); + /* Subchannel is now disabled and no longer valid. */ + sch->curr_status.pmcw.flags &= ~(PMCW_FLAGS_MASK_ENA | + PMCW_FLAGS_MASK_DNV); + + css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, 1, 0); + + object_unparent(OBJECT(dev)); +} + +static void virtual_css_bus_reset(BusState *qbus) +{ + /* This should actually be modelled via the generic css */ + css_reset(); +} + +static 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, +}; + +VirtualCssBus *virtual_css_bus_init(void) +{ + VirtualCssBus *cbus; + BusState *bus; + DeviceState *dev; + + /* Create bridge device */ + dev = qdev_create(NULL, TYPE_VIRTUAL_CSS_BRIDGE); + qdev_init_nofail(dev); + + /* Create bus on bridge device */ + bus = qbus_create(TYPE_VIRTUAL_CSS_BUS, dev, "virtual-css"); + cbus = VIRTUAL_CSS_BUS(bus); + + /* Enable hotplugging */ + qbus_set_hotplug_handler(bus, dev, &error_abort); + + return cbus; + } + +/***************** Virtual-css Bus Bridge Device ********************/ + +static void virtual_css_bridge_class_init(ObjectClass *klass, void *data) +{ + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + hc->unplug = ccw_device_unplug; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); +} + +static const TypeInfo virtual_css_bridge_info = { + .name = TYPE_VIRTUAL_CSS_BRIDGE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SysBusDevice), + .class_init = virtual_css_bridge_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + } +}; + +static void virtual_css_register(void) +{ + type_register_static(&virtual_css_bridge_info); + type_register_static(&virtual_css_bus_info); +} + +type_init(virtual_css_register) diff --git a/hw/s390x/css.c b/hw/s390x/css.c index 76668814da..54991f5d6f 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -1340,6 +1340,116 @@ SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid, uint16_t schid) return channel_subsys.css[real_cssid]->sch_set[ssid]->sch[schid]; } +/** + * Return free device number in subchannel set. + * + * Return index of the first free device number in the subchannel set + * identified by @p cssid and @p ssid, beginning the search at @p + * start and wrapping around at MAX_DEVNO. Return a value exceeding + * MAX_SCHID if there are no free device numbers in the subchannel + * set. + */ +static uint32_t css_find_free_devno(uint8_t cssid, uint8_t ssid, + uint16_t start) +{ + uint32_t round; + + for (round = 0; round <= MAX_DEVNO; round++) { + uint16_t devno = (start + round) % MAX_DEVNO; + + if (!css_devno_used(cssid, ssid, devno)) { + return devno; + } + } + return MAX_DEVNO + 1; +} + +/** + * Return first free subchannel (id) in subchannel set. + * + * Return index of the first free subchannel in the subchannel set + * identified by @p cssid and @p ssid, if there is any. Return a value + * exceeding MAX_SCHID if there are no free subchannels in the + * subchannel set. + */ +static uint32_t css_find_free_subch(uint8_t cssid, uint8_t ssid) +{ + uint32_t schid; + + for (schid = 0; schid <= MAX_SCHID; schid++) { + if (!css_find_subch(1, cssid, ssid, schid)) { + return schid; + } + } + return MAX_SCHID + 1; +} + +/** + * Return first free subchannel (id) in subchannel set for a device number + * + * Verify the device number @p devno is not used yet in the subchannel + * set identified by @p cssid and @p ssid. Set @p schid to the index + * of the first free subchannel in the subchannel set, if there is + * any. Return true if everything succeeded and false otherwise. + */ +static bool css_find_free_subch_for_devno(uint8_t cssid, uint8_t ssid, + uint16_t devno, uint16_t *schid, + Error **errp) +{ + uint32_t free_schid; + + assert(schid); + if (css_devno_used(cssid, ssid, devno)) { + error_setg(errp, "Device %x.%x.%04x already exists", + cssid, ssid, devno); + return false; + } + free_schid = css_find_free_subch(cssid, ssid); + if (free_schid > MAX_SCHID) { + error_setg(errp, "No free subchannel found for %x.%x.%04x", + cssid, ssid, devno); + return false; + } + *schid = free_schid; + return true; +} + +/** + * Return first free subchannel (id) and device number + * + * Locate the first free subchannel and first free device number in + * any of the subchannel sets of the channel subsystem identified by + * @p cssid. Return false if no free subchannel / device number could + * be found. Otherwise set @p ssid, @p devno and @p schid to identify + * the available subchannel and device number and return true. + * + * May modify @p ssid, @p devno and / or @p schid even if no free + * subchannel / device number could be found. + */ +static bool css_find_free_subch_and_devno(uint8_t cssid, uint8_t *ssid, + uint16_t *devno, uint16_t *schid, + Error **errp) +{ + uint32_t free_schid, free_devno; + + assert(ssid && devno && schid); + for (*ssid = 0; *ssid <= MAX_SSID; (*ssid)++) { + free_schid = css_find_free_subch(cssid, *ssid); + if (free_schid > MAX_SCHID) { + continue; + } + free_devno = css_find_free_devno(cssid, *ssid, free_schid); + if (free_devno > MAX_DEVNO) { + continue; + } + *schid = free_schid; + *devno = free_devno; + return true; + } + error_setg(errp, "Virtual channel subsystem is full!"); + return false; +} + bool css_subch_visible(SubchDev *sch) { if (sch->ssid > channel_subsys.max_ssid) { @@ -1762,3 +1872,36 @@ PropertyInfo css_devid_propinfo = { .get = get_css_devid, .set = set_css_devid, }; + +SubchDev *css_create_virtual_sch(CssDevId bus_id, Error **errp) +{ + uint16_t schid = 0; + SubchDev *sch; + + if (bus_id.valid) { + /* Enforce use of virtual cssid. */ + if (bus_id.cssid != VIRTUAL_CSSID) { + error_setg(errp, "cssid %hhx not valid for virtual devices", + bus_id.cssid); + return NULL; + } + if (!css_find_free_subch_for_devno(bus_id.cssid, bus_id.ssid, + bus_id.devid, &schid, errp)) { + return NULL; + } + } else { + bus_id.cssid = VIRTUAL_CSSID; + if (!css_find_free_subch_and_devno(bus_id.cssid, &bus_id.ssid, + &bus_id.devid, &schid, errp)) { + return NULL; + } + } + + sch = g_malloc0(sizeof(*sch)); + sch->cssid = bus_id.cssid; + sch->ssid = bus_id.ssid; + sch->devno = bus_id.devid; + sch->schid = schid; + css_subch_assign(sch->cssid, sch->ssid, schid, sch->devno, sch); + return sch; +} diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index e6bf7cf7c0..2e2664f22e 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -69,8 +69,8 @@ static const VMStateDescription vmstate_ipl = { .version_id = 0, .minimum_version_id = 0, .fields = (VMStateField[]) { - VMSTATE_UINT64(start_addr, S390IPLState), - VMSTATE_UINT64(bios_start_addr, S390IPLState), + VMSTATE_UINT64(compat_start_addr, S390IPLState), + VMSTATE_UINT64(compat_bios_start_addr, S390IPLState), VMSTATE_STRUCT(iplb, S390IPLState, 0, vmstate_iplb, IplParameterBlock), VMSTATE_BOOL(iplb_valid, S390IPLState), VMSTATE_UINT8(cssid, S390IPLState), @@ -192,6 +192,13 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) stq_p(rom_ptr(INITRD_PARM_SIZE), initrd_size); } } + /* + * Don't ever use the migrated values, they could come from a different + * BIOS and therefore don't work. But still migrate the values, so + * QEMUs relying on it don't break. + */ + ipl->compat_start_addr = ipl->start_addr; + ipl->compat_bios_start_addr = ipl->bios_start_addr; qemu_register_reset(qdev_reset_all_fn, dev); error: error_propagate(errp, err); @@ -214,10 +221,14 @@ static bool s390_gen_initial_iplb(S390IPLState *ipl) dev_st = get_boot_device(0); if (dev_st) { - VirtioCcwDevice *ccw_dev = (VirtioCcwDevice *) object_dynamic_cast( - OBJECT(qdev_get_parent_bus(dev_st)->parent), + VirtioCcwDevice *virtio_ccw_dev = (VirtioCcwDevice *) + object_dynamic_cast(OBJECT(qdev_get_parent_bus(dev_st)->parent), TYPE_VIRTIO_CCW_DEVICE); - if (ccw_dev) { + SCSIDevice *sd = (SCSIDevice *) object_dynamic_cast(OBJECT(dev_st), + TYPE_SCSI_DEVICE); + if (virtio_ccw_dev) { + CcwDevice *ccw_dev = CCW_DEVICE(virtio_ccw_dev); + ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN); ipl->iplb.blk0_len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN - S390_IPLB_HEADER_LEN); @@ -225,6 +236,22 @@ static bool s390_gen_initial_iplb(S390IPLState *ipl) ipl->iplb.ccw.devno = cpu_to_be16(ccw_dev->sch->devno); ipl->iplb.ccw.ssid = ccw_dev->sch->ssid & 3; return true; + } else if (sd) { + SCSIBus *bus = scsi_bus_from_device(sd); + VirtIOSCSI *vdev = container_of(bus, VirtIOSCSI, bus); + VirtIOSCSICcw *scsi_ccw = container_of(vdev, VirtIOSCSICcw, vdev); + CcwDevice *ccw_dev = CCW_DEVICE(scsi_ccw); + + ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN); + ipl->iplb.blk0_len = + cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN - S390_IPLB_HEADER_LEN); + ipl->iplb.pbt = S390_IPL_TYPE_QEMU_SCSI; + ipl->iplb.scsi.lun = cpu_to_be32(sd->lun); + ipl->iplb.scsi.target = cpu_to_be16(sd->id); + ipl->iplb.scsi.channel = cpu_to_be16(sd->channel); + ipl->iplb.scsi.devno = cpu_to_be16(ccw_dev->sch->devno); + ipl->iplb.scsi.ssid = ccw_dev->sch->ssid & 3; + return true; } } diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h index 9aa4d942a7..c89109585a 100644 --- a/hw/s390x/ipl.h +++ b/hw/s390x/ipl.h @@ -46,6 +46,16 @@ struct IplBlockFcp { } QEMU_PACKED; typedef struct IplBlockFcp IplBlockFcp; +struct IplBlockQemuScsi { + uint32_t lun; + uint16_t target; + uint16_t channel; + uint8_t reserved0[77]; + uint8_t ssid; + uint16_t devno; +} QEMU_PACKED; +typedef struct IplBlockQemuScsi IplBlockQemuScsi; + union IplParameterBlock { struct { uint32_t len; @@ -59,6 +69,7 @@ union IplParameterBlock { union { IplBlockCcw ccw; IplBlockFcp fcp; + IplBlockQemuScsi scsi; }; } QEMU_PACKED; struct { @@ -82,7 +93,9 @@ struct S390IPLState { /*< private >*/ DeviceState parent_obj; uint64_t start_addr; + uint64_t compat_start_addr; uint64_t bios_start_addr; + uint64_t compat_bios_start_addr; bool enforce_bios; IplParameterBlock iplb; bool iplb_valid; @@ -102,10 +115,12 @@ typedef struct S390IPLState S390IPLState; #define S390_IPL_TYPE_FCP 0x00 #define S390_IPL_TYPE_CCW 0x02 +#define S390_IPL_TYPE_QEMU_SCSI 0xff #define S390_IPLB_HEADER_LEN 8 #define S390_IPLB_MIN_CCW_LEN 200 #define S390_IPLB_MIN_FCP_LEN 384 +#define S390_IPLB_MIN_QEMU_SCSI_LEN 200 static inline bool iplb_valid_len(IplParameterBlock *iplb) { diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index a77c10ce9e..640a4eaa61 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -12,6 +12,8 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/visitor.h" #include "qemu-common.h" #include "cpu.h" #include "s390-pci-bus.h" @@ -29,6 +31,19 @@ do { } while (0) #endif +static S390pciState *s390_get_phb(void) +{ + static S390pciState *phb; + + if (!phb) { + phb = S390_PCI_HOST_BRIDGE( + object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); + assert(phb != NULL); + } + + return phb; +} + int chsc_sei_nt2_get_event(void *res) { ChscSeiNt2Res *nt2_res = (ChscSeiNt2Res *)res; @@ -36,12 +51,7 @@ int chsc_sei_nt2_get_event(void *res) PciCcdfErr *eccdf; int rc = 1; SeiContainer *sei_cont; - S390pciState *s = S390_PCI_HOST_BRIDGE( - object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); - - if (!s) { - return rc; - } + S390pciState *s = s390_get_phb(); sei_cont = QTAILQ_FIRST(&s->pending_sei); if (sei_cont) { @@ -76,30 +86,40 @@ int chsc_sei_nt2_get_event(void *res) int chsc_sei_nt2_have_event(void) { - S390pciState *s = S390_PCI_HOST_BRIDGE( - object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); + S390pciState *s = s390_get_phb(); - if (!s) { - return 0; + return !QTAILQ_EMPTY(&s->pending_sei); +} + +S390PCIBusDevice *s390_pci_find_next_avail_dev(S390PCIBusDevice *pbdev) +{ + int idx = 0; + S390PCIBusDevice *dev = NULL; + S390pciState *s = s390_get_phb(); + + if (pbdev) { + idx = (pbdev->fh & FH_MASK_INDEX) + 1; } - return !QTAILQ_EMPTY(&s->pending_sei); + for (; idx < PCI_SLOT_MAX; idx++) { + dev = s->pbdev[idx]; + if (dev && dev->state != ZPCI_FS_RESERVED) { + return dev; + } + } + + return NULL; } S390PCIBusDevice *s390_pci_find_dev_by_fid(uint32_t fid) { S390PCIBusDevice *pbdev; int i; - S390pciState *s = S390_PCI_HOST_BRIDGE( - object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); - - if (!s) { - return NULL; - } + S390pciState *s = s390_get_phb(); for (i = 0; i < PCI_SLOT_MAX; i++) { - pbdev = &s->pbdev[i]; - if ((pbdev->fh != 0) && (pbdev->fid == fid)) { + pbdev = s->pbdev[i]; + if (pbdev && pbdev->fid == fid) { return pbdev; } } @@ -118,16 +138,22 @@ void s390_pci_sclp_configure(SCCB *sccb) goto out; } - if (pbdev) { - if (pbdev->configured) { - rc = SCLP_RC_NO_ACTION_REQUIRED; - } else { - pbdev->configured = true; - rc = SCLP_RC_NORMAL_COMPLETION; - } - } else { + if (!pbdev) { DPRINTF("sclp config no dev found\n"); rc = SCLP_RC_ADAPTER_ID_NOT_RECOGNIZED; + goto out; + } + + switch (pbdev->state) { + case ZPCI_FS_RESERVED: + rc = SCLP_RC_ADAPTER_IN_RESERVED_STATE; + break; + case ZPCI_FS_STANDBY: + pbdev->state = ZPCI_FS_DISABLED; + rc = SCLP_RC_NORMAL_COMPLETION; + break; + default: + rc = SCLP_RC_NO_ACTION_REQUIRED; } out: psccb->header.response_code = cpu_to_be16(rc); @@ -144,81 +170,96 @@ void s390_pci_sclp_deconfigure(SCCB *sccb) goto out; } - if (pbdev) { - if (!pbdev->configured) { - rc = SCLP_RC_NO_ACTION_REQUIRED; - } else { - if (pbdev->summary_ind) { - pci_dereg_irqs(pbdev); - } - if (pbdev->iommu_enabled) { - pci_dereg_ioat(pbdev); - } - pbdev->configured = false; - rc = SCLP_RC_NORMAL_COMPLETION; - } - } else { + if (!pbdev) { DPRINTF("sclp deconfig no dev found\n"); rc = SCLP_RC_ADAPTER_ID_NOT_RECOGNIZED; + goto out; + } + + switch (pbdev->state) { + case ZPCI_FS_RESERVED: + rc = SCLP_RC_ADAPTER_IN_RESERVED_STATE; + break; + case ZPCI_FS_STANDBY: + rc = SCLP_RC_NO_ACTION_REQUIRED; + break; + default: + if (pbdev->summary_ind) { + pci_dereg_irqs(pbdev); + } + if (pbdev->iommu_enabled) { + pci_dereg_ioat(pbdev); + } + pbdev->state = ZPCI_FS_STANDBY; + rc = SCLP_RC_NORMAL_COMPLETION; + + if (pbdev->release_timer) { + qdev_unplug(DEVICE(pbdev->pdev), NULL); + } } out: psccb->header.response_code = cpu_to_be16(rc); } -static uint32_t s390_pci_get_pfid(PCIDevice *pdev) +static S390PCIBusDevice *s390_pci_find_dev_by_uid(uint16_t uid) { - return PCI_SLOT(pdev->devfn); -} + int i; + S390PCIBusDevice *pbdev; + S390pciState *s = s390_get_phb(); -static uint32_t s390_pci_get_pfh(PCIDevice *pdev) -{ - return PCI_SLOT(pdev->devfn) | FH_VIRT; + for (i = 0; i < PCI_SLOT_MAX; i++) { + pbdev = s->pbdev[i]; + if (!pbdev) { + continue; + } + + if (pbdev->uid == uid) { + return pbdev; + } + } + + return NULL; } -S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx) +static S390PCIBusDevice *s390_pci_find_dev_by_target(const char *target) { - S390PCIBusDevice *pbdev; int i; - int j = 0; - S390pciState *s = S390_PCI_HOST_BRIDGE( - object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); + S390PCIBusDevice *pbdev; + S390pciState *s = s390_get_phb(); - if (!s) { + if (!target) { return NULL; } for (i = 0; i < PCI_SLOT_MAX; i++) { - pbdev = &s->pbdev[i]; - - if (pbdev->fh == 0) { + pbdev = s->pbdev[i]; + if (!pbdev) { continue; } - if (j == idx) { + if (!strcmp(pbdev->target, target)) { return pbdev; } - j++; } return NULL; } +S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx) +{ + S390pciState *s = s390_get_phb(); + + return s->pbdev[idx & FH_MASK_INDEX]; +} + S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh) { + S390pciState *s = s390_get_phb(); S390PCIBusDevice *pbdev; - int i; - S390pciState *s = S390_PCI_HOST_BRIDGE( - object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); - if (!s || !fh) { - return NULL; - } - - for (i = 0; i < PCI_SLOT_MAX; i++) { - pbdev = &s->pbdev[i]; - if (pbdev->fh == fh) { - return pbdev; - } + pbdev = s->pbdev[fh & FH_MASK_INDEX]; + if (pbdev && pbdev->fh == fh) { + return pbdev; } return NULL; @@ -228,12 +269,7 @@ static void s390_pci_generate_event(uint8_t cc, uint16_t pec, uint32_t fh, uint32_t fid, uint64_t faddr, uint32_t e) { SeiContainer *sei_cont; - S390pciState *s = S390_PCI_HOST_BRIDGE( - object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); - - if (!s) { - return; - } + S390pciState *s = s390_get_phb(); sei_cont = g_malloc0(sizeof(SeiContainer)); sei_cont->fh = fh; @@ -253,9 +289,8 @@ static void s390_pci_generate_plug_event(uint16_t pec, uint32_t fh, s390_pci_generate_event(2, pec, fh, fid, 0, 0); } -static void s390_pci_generate_error_event(uint16_t pec, uint32_t fh, - uint32_t fid, uint64_t faddr, - uint32_t e) +void s390_pci_generate_error_event(uint16_t pec, uint32_t fh, uint32_t fid, + uint64_t faddr, uint32_t e) { s390_pci_generate_event(1, pec, fh, fid, faddr, e); } @@ -357,8 +392,14 @@ static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *iommu, hwaddr addr, .perm = IOMMU_NONE, }; - if (!pbdev->configured || !pbdev->pdev || - !(pbdev->fh & FH_ENABLED) || !pbdev->iommu_enabled) { + switch (pbdev->state) { + case ZPCI_FS_ENABLED: + case ZPCI_FS_BLOCKED: + if (!pbdev->iommu_enabled) { + return ret; + } + break; + default: return ret; } @@ -377,30 +418,13 @@ static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *iommu, hwaddr addr, return ret; } - if (!pbdev->g_iota) { - pbdev->error_state = true; - pbdev->lgstg_blocked = true; - s390_pci_generate_error_event(ERR_EVENT_INVALAS, pbdev->fh, pbdev->fid, - addr, 0); - return ret; - } - if (addr < pbdev->pba || addr > pbdev->pal) { - pbdev->error_state = true; - pbdev->lgstg_blocked = true; - s390_pci_generate_error_event(ERR_EVENT_OORANGE, pbdev->fh, pbdev->fid, - addr, 0); return ret; } pte = s390_guest_io_table_walk(s390_pci_get_table_origin(pbdev->g_iota), addr); - if (!pte) { - pbdev->error_state = true; - pbdev->lgstg_blocked = true; - s390_pci_generate_error_event(ERR_EVENT_SERR, pbdev->fh, pbdev->fid, - addr, ERR_EVENT_Q_BIT); return ret; } @@ -426,7 +450,7 @@ static AddressSpace *s390_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) { S390pciState *s = opaque; - return &s->pbdev[PCI_SLOT(devfn)].as; + return &s->iommu[PCI_SLOT(devfn)]->as; } static uint8_t set_ind_atomic(uint64_t ind_loc, uint8_t to_be_set) @@ -454,22 +478,22 @@ static void s390_msi_ctrl_write(void *opaque, hwaddr addr, uint64_t data, { S390PCIBusDevice *pbdev; uint32_t io_int_word; - uint32_t fid = data >> ZPCI_MSI_VEC_BITS; + uint32_t idx = data >> ZPCI_MSI_VEC_BITS; uint32_t vec = data & ZPCI_MSI_VEC_MASK; uint64_t ind_bit; uint32_t sum_bit; uint32_t e = 0; - DPRINTF("write_msix data 0x%" PRIx64 " fid %d vec 0x%x\n", data, fid, vec); + DPRINTF("write_msix data 0x%" PRIx64 " idx %d vec 0x%x\n", data, idx, vec); - pbdev = s390_pci_find_dev_by_fid(fid); + pbdev = s390_pci_find_dev_by_idx(idx); if (!pbdev) { e |= (vec << ERR_EVENT_MVN_OFFSET); - s390_pci_generate_error_event(ERR_EVENT_NOMSI, 0, fid, addr, e); + s390_pci_generate_error_event(ERR_EVENT_NOMSI, idx, 0, addr, e); return; } - if (!(pbdev->fh & FH_ENABLED)) { + if (pbdev->state != ZPCI_FS_ENABLED) { return; } @@ -498,17 +522,15 @@ static const MemoryRegionOps s390_msi_ctrl_ops = { void s390_pci_iommu_enable(S390PCIBusDevice *pbdev) { - uint64_t size = pbdev->pal - pbdev->pba + 1; - - memory_region_init_iommu(&pbdev->iommu_mr, OBJECT(&pbdev->mr), - &s390_iommu_ops, "iommu-s390", size); - memory_region_add_subregion(&pbdev->mr, pbdev->pba, &pbdev->iommu_mr); + memory_region_init_iommu(&pbdev->iommu_mr, OBJECT(&pbdev->iommu->mr), + &s390_iommu_ops, "iommu-s390", pbdev->pal + 1); + memory_region_add_subregion(&pbdev->iommu->mr, 0, &pbdev->iommu_mr); pbdev->iommu_enabled = true; } void s390_pci_iommu_disable(S390PCIBusDevice *pbdev) { - memory_region_del_subregion(&pbdev->mr, &pbdev->iommu_mr); + memory_region_del_subregion(&pbdev->iommu->mr, &pbdev->iommu_mr); object_unparent(OBJECT(&pbdev->iommu_mr)); pbdev->iommu_enabled = false; } @@ -516,13 +538,15 @@ void s390_pci_iommu_disable(S390PCIBusDevice *pbdev) static void s390_pcihost_init_as(S390pciState *s) { int i; - S390PCIBusDevice *pbdev; + S390PCIIOMMU *iommu; for (i = 0; i < PCI_SLOT_MAX; i++) { - pbdev = &s->pbdev[i]; - memory_region_init(&pbdev->mr, OBJECT(s), + iommu = g_malloc0(sizeof(S390PCIIOMMU)); + memory_region_init(&iommu->mr, OBJECT(s), "iommu-root-s390", UINT64_MAX); - address_space_init(&pbdev->as, &pbdev->mr, "iommu-pci"); + address_space_init(&iommu->as, &iommu->mr, "iommu-pci"); + + s->iommu[i] = iommu; } memory_region_init_io(&s->msix_notify_mr, OBJECT(s), @@ -549,6 +573,10 @@ static int s390_pcihost_init(SysBusDevice *dev) bus = BUS(b); qbus_set_hotplug_handler(bus, DEVICE(dev), NULL); phb->bus = b; + + s->bus = S390_PCI_BUS(qbus_create(TYPE_S390_PCI_BUS, DEVICE(s), NULL)); + qbus_set_hotplug_handler(BUS(s->bus), DEVICE(s), NULL); + QTAILQ_INIT(&s->pending_sei); return 0; } @@ -581,51 +609,155 @@ static int s390_pcihost_setup_msix(S390PCIBusDevice *pbdev) return 0; } +static S390PCIBusDevice *s390_pci_device_new(const char *target) +{ + DeviceState *dev = NULL; + S390pciState *s = s390_get_phb(); + + dev = qdev_try_create(BUS(s->bus), TYPE_S390_PCI_DEVICE); + if (!dev) { + return NULL; + } + + qdev_prop_set_string(dev, "target", target); + qdev_init_nofail(dev); + + return S390_PCI_DEVICE(dev); +} + static void s390_pcihost_hot_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - PCIDevice *pci_dev = PCI_DEVICE(dev); - S390PCIBusDevice *pbdev; - S390pciState *s = S390_PCI_HOST_BRIDGE(pci_device_root_bus(pci_dev) - ->qbus.parent); + PCIDevice *pdev = NULL; + S390PCIBusDevice *pbdev = NULL; + S390pciState *s = s390_get_phb(); - pbdev = &s->pbdev[PCI_SLOT(pci_dev->devfn)]; + if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { + pdev = PCI_DEVICE(dev); - pbdev->fid = s390_pci_get_pfid(pci_dev); - pbdev->pdev = pci_dev; - pbdev->configured = true; - pbdev->fh = s390_pci_get_pfh(pci_dev); + if (!dev->id) { + /* In the case the PCI device does not define an id */ + /* we generate one based on the PCI address */ + dev->id = g_strdup_printf("auto_%02x:%02x.%01x", + pci_bus_num(pdev->bus), + PCI_SLOT(pdev->devfn), + PCI_FUNC(pdev->devfn)); + } - s390_pcihost_setup_msix(pbdev); + pbdev = s390_pci_find_dev_by_target(dev->id); + if (!pbdev) { + pbdev = s390_pci_device_new(dev->id); + if (!pbdev) { + error_setg(errp, "create zpci device failed"); + } + } - if (dev->hotplugged) { - s390_pci_generate_plug_event(HP_EVENT_RESERVED_TO_STANDBY, - pbdev->fh, pbdev->fid); - s390_pci_generate_plug_event(HP_EVENT_TO_CONFIGURED, - pbdev->fh, pbdev->fid); + if (object_dynamic_cast(OBJECT(dev), "vfio-pci")) { + pbdev->fh |= FH_SHM_VFIO; + } else { + pbdev->fh |= FH_SHM_EMUL; + } + + pbdev->pdev = pdev; + pbdev->iommu = s->iommu[PCI_SLOT(pdev->devfn)]; + pbdev->state = ZPCI_FS_STANDBY; + s390_pcihost_setup_msix(pbdev); + + if (dev->hotplugged) { + s390_pci_generate_plug_event(HP_EVENT_RESERVED_TO_STANDBY, + pbdev->fh, pbdev->fid); + } + } else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) { + int idx; + + pbdev = S390_PCI_DEVICE(dev); + for (idx = 0; idx < PCI_SLOT_MAX; idx++) { + if (!s->pbdev[idx]) { + s->pbdev[idx] = pbdev; + pbdev->fh = idx; + return; + } + } + + error_setg(errp, "no slot for plugging zpci device"); } } +static void s390_pcihost_timer_cb(void *opaque) +{ + S390PCIBusDevice *pbdev = opaque; + + if (pbdev->summary_ind) { + pci_dereg_irqs(pbdev); + } + if (pbdev->iommu_enabled) { + pci_dereg_ioat(pbdev); + } + + pbdev->state = ZPCI_FS_STANDBY; + s390_pci_generate_plug_event(HP_EVENT_CONFIGURED_TO_STBRES, + pbdev->fh, pbdev->fid); + qdev_unplug(DEVICE(pbdev), NULL); +} + static void s390_pcihost_hot_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - PCIDevice *pci_dev = PCI_DEVICE(dev); - S390pciState *s = S390_PCI_HOST_BRIDGE(pci_device_root_bus(pci_dev) - ->qbus.parent); - S390PCIBusDevice *pbdev = &s->pbdev[PCI_SLOT(pci_dev->devfn)]; + int i; + PCIDevice *pci_dev = NULL; + S390PCIBusDevice *pbdev = NULL; + S390pciState *s = s390_get_phb(); + + if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { + pci_dev = PCI_DEVICE(dev); - if (pbdev->configured) { - pbdev->configured = false; - s390_pci_generate_plug_event(HP_EVENT_CONFIGURED_TO_STBRES, + for (i = 0 ; i < PCI_SLOT_MAX; i++) { + if (s->pbdev[i]->pdev == pci_dev) { + pbdev = s->pbdev[i]; + break; + } + } + + if (!pbdev) { + object_unparent(OBJECT(pci_dev)); + return; + } + } else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) { + pbdev = S390_PCI_DEVICE(dev); + pci_dev = pbdev->pdev; + } + + switch (pbdev->state) { + case ZPCI_FS_RESERVED: + goto out; + case ZPCI_FS_STANDBY: + break; + default: + s390_pci_generate_plug_event(HP_EVENT_DECONFIGURE_REQUEST, pbdev->fh, pbdev->fid); + pbdev->release_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + s390_pcihost_timer_cb, + pbdev); + timer_mod(pbdev->release_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + HOT_UNPLUG_TIMEOUT); + return; + } + + if (pbdev->release_timer && timer_pending(pbdev->release_timer)) { + timer_del(pbdev->release_timer); + timer_free(pbdev->release_timer); + pbdev->release_timer = NULL; } s390_pci_generate_plug_event(HP_EVENT_STANDBY_TO_RESERVED, pbdev->fh, pbdev->fid); - pbdev->fh = 0; - pbdev->fid = 0; - pbdev->pdev = NULL; object_unparent(OBJECT(pci_dev)); + pbdev->pdev = NULL; + pbdev->state = ZPCI_FS_RESERVED; +out: + pbdev->fid = 0; + s->pbdev[pbdev->fh & FH_MASK_INDEX] = NULL; + object_unparent(OBJECT(pbdev)); } static void s390_pcihost_class_init(ObjectClass *klass, void *data) @@ -652,9 +784,178 @@ static const TypeInfo s390_pcihost_info = { } }; +static const TypeInfo s390_pcibus_info = { + .name = TYPE_S390_PCI_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(S390PCIBus), +}; + +static uint16_t s390_pci_generate_uid(void) +{ + uint16_t uid = 0; + + do { + uid++; + if (!s390_pci_find_dev_by_uid(uid)) { + return uid; + } + } while (uid < ZPCI_MAX_UID); + + return UID_UNDEFINED; +} + +static uint32_t s390_pci_generate_fid(Error **errp) +{ + uint32_t fid = 0; + + while (fid <= ZPCI_MAX_FID) { + if (!s390_pci_find_dev_by_fid(fid)) { + return fid; + } + + if (fid == ZPCI_MAX_FID) { + break; + } + + fid++; + } + + error_setg(errp, "no free fid could be found"); + return 0; +} + +static void s390_pci_device_realize(DeviceState *dev, Error **errp) +{ + S390PCIBusDevice *zpci = S390_PCI_DEVICE(dev); + + if (!zpci->target) { + error_setg(errp, "target must be defined"); + return; + } + + if (s390_pci_find_dev_by_target(zpci->target)) { + error_setg(errp, "target %s already has an associated zpci device", + zpci->target); + return; + } + + if (zpci->uid == UID_UNDEFINED) { + zpci->uid = s390_pci_generate_uid(); + if (!zpci->uid) { + error_setg(errp, "no free uid could be found"); + return; + } + } else if (s390_pci_find_dev_by_uid(zpci->uid)) { + error_setg(errp, "uid %u already in use", zpci->uid); + return; + } + + if (!zpci->fid_defined) { + Error *local_error = NULL; + + zpci->fid = s390_pci_generate_fid(&local_error); + if (local_error) { + error_propagate(errp, local_error); + return; + } + } else if (s390_pci_find_dev_by_fid(zpci->fid)) { + error_setg(errp, "fid %u already in use", zpci->fid); + return; + } + + zpci->state = ZPCI_FS_RESERVED; +} + +static void s390_pci_device_reset(DeviceState *dev) +{ + S390PCIBusDevice *pbdev = S390_PCI_DEVICE(dev); + + switch (pbdev->state) { + case ZPCI_FS_RESERVED: + return; + case ZPCI_FS_STANDBY: + break; + default: + pbdev->fh &= ~FH_MASK_ENABLE; + pbdev->state = ZPCI_FS_DISABLED; + break; + } + + if (pbdev->summary_ind) { + pci_dereg_irqs(pbdev); + } + if (pbdev->iommu_enabled) { + pci_dereg_ioat(pbdev); + } + + pbdev->fmb_addr = 0; +} + +static void s390_pci_get_fid(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + Property *prop = opaque; + uint32_t *ptr = qdev_get_prop_ptr(DEVICE(obj), prop); + + visit_type_uint32(v, name, ptr, errp); +} + +static void s390_pci_set_fid(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + S390PCIBusDevice *zpci = S390_PCI_DEVICE(obj); + Property *prop = opaque; + uint32_t *ptr = qdev_get_prop_ptr(dev, prop); + + if (dev->realized) { + qdev_prop_set_after_realize(dev, name, errp); + return; + } + + visit_type_uint32(v, name, ptr, errp); + zpci->fid_defined = true; +} + +static PropertyInfo s390_pci_fid_propinfo = { + .name = "zpci_fid", + .get = s390_pci_get_fid, + .set = s390_pci_set_fid, +}; + +#define DEFINE_PROP_S390_PCI_FID(_n, _s, _f) \ + DEFINE_PROP(_n, _s, _f, s390_pci_fid_propinfo, uint32_t) + +static Property s390_pci_device_properties[] = { + DEFINE_PROP_UINT16("uid", S390PCIBusDevice, uid, UID_UNDEFINED), + DEFINE_PROP_S390_PCI_FID("fid", S390PCIBusDevice, fid), + DEFINE_PROP_STRING("target", S390PCIBusDevice, target), + DEFINE_PROP_END_OF_LIST(), +}; + +static void s390_pci_device_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "zpci device"; + dc->reset = s390_pci_device_reset; + dc->bus_type = TYPE_S390_PCI_BUS; + dc->realize = s390_pci_device_realize; + dc->props = s390_pci_device_properties; +} + +static const TypeInfo s390_pci_device_info = { + .name = TYPE_S390_PCI_DEVICE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(S390PCIBusDevice), + .class_init = s390_pci_device_class_init, +}; + static void s390_pci_register_types(void) { type_register_static(&s390_pcihost_info); + type_register_static(&s390_pcibus_info); + type_register_static(&s390_pci_device_info); } type_init(s390_pci_register_types) diff --git a/hw/s390x/s390-pci-bus.h b/hw/s390x/s390-pci-bus.h index 2c852d47fa..f1fbd3c1b6 100644 --- a/hw/s390x/s390-pci-bus.h +++ b/hw/s390x/s390-pci-bus.h @@ -21,16 +21,31 @@ #include "hw/s390x/css.h" #define TYPE_S390_PCI_HOST_BRIDGE "s390-pcihost" -#define FH_VIRT 0x00ff0000 -#define ENABLE_BIT_OFFSET 31 -#define FH_ENABLED (1 << ENABLE_BIT_OFFSET) +#define TYPE_S390_PCI_BUS "s390-pcibus" +#define TYPE_S390_PCI_DEVICE "zpci" +#define FH_MASK_ENABLE 0x80000000 +#define FH_MASK_INSTANCE 0x7f000000 +#define FH_MASK_SHM 0x00ff0000 +#define FH_MASK_INDEX 0x0000001f +#define FH_SHM_VFIO 0x00010000 +#define FH_SHM_EMUL 0x00020000 #define S390_PCIPT_ADAPTER 2 +#define ZPCI_MAX_FID 0xffffffff +#define ZPCI_MAX_UID 0xffff +#define UID_UNDEFINED 0 +#define UID_CHECKING_ENABLED 0x01 +#define HOT_UNPLUG_TIMEOUT (NANOSECONDS_PER_SECOND * 60 * 5) #define S390_PCI_HOST_BRIDGE(obj) \ OBJECT_CHECK(S390pciState, (obj), TYPE_S390_PCI_HOST_BRIDGE) +#define S390_PCI_BUS(obj) \ + OBJECT_CHECK(S390PCIBus, (obj), TYPE_S390_PCI_BUS) +#define S390_PCI_DEVICE(obj) \ + OBJECT_CHECK(S390PCIBusDevice, (obj), TYPE_S390_PCI_DEVICE) #define HP_EVENT_TO_CONFIGURED 0x0301 #define HP_EVENT_RESERVED_TO_STANDBY 0x0302 +#define HP_EVENT_DECONFIGURE_REQUEST 0x0303 #define HP_EVENT_CONFIGURED_TO_STBRES 0x0304 #define HP_EVENT_STANDBY_TO_RESERVED 0x0308 @@ -150,6 +165,34 @@ enum ZpciIoatDtype { #define ZPCI_TABLE_VALID_MASK 0x20 #define ZPCI_TABLE_PROT_MASK 0x200 +/* PCI Function States + * + * reserved: default; device has just been plugged or is in progress of being + * unplugged + * standby: device is present but not configured; transition from any + * configured state/to this state via sclp configure/deconfigure + * + * The following states make up the "configured" meta-state: + * disabled: device is configured but not enabled; transition between this + * state and enabled via clp enable/disable + * enbaled: device is ready for use; transition to disabled via clp disable; + * may enter an error state + * blocked: ignore all DMA and interrupts; transition back to enabled or from + * error state via mpcifc + * error: an error occured; transition back to enabled via mpcifc + * permanent error: an unrecoverable error occured; transition to standby via + * sclp deconfigure + */ +typedef enum { + ZPCI_FS_RESERVED, + ZPCI_FS_STANDBY, + ZPCI_FS_DISABLED, + ZPCI_FS_ENABLED, + ZPCI_FS_BLOCKED, + ZPCI_FS_ERROR, + ZPCI_FS_PERMANENT_ERROR, +} ZpciState; + typedef struct SeiContainer { QTAILQ_ENTRY(SeiContainer) link; uint32_t fid; @@ -214,14 +257,21 @@ typedef struct S390MsixInfo { uint32_t pba_offset; } S390MsixInfo; +typedef struct S390PCIIOMMU { + AddressSpace as; + MemoryRegion mr; +} S390PCIIOMMU; + typedef struct S390PCIBusDevice { + DeviceState qdev; PCIDevice *pdev; - bool configured; - bool error_state; - bool lgstg_blocked; + ZpciState state; bool iommu_enabled; + char *target; + uint16_t uid; uint32_t fh; uint32_t fid; + bool fid_defined; uint64_t g_iota; uint64_t pba; uint64_t pal; @@ -231,16 +281,22 @@ typedef struct S390PCIBusDevice { uint8_t sum; S390MsixInfo msix; AdapterRoutes routes; - AddressSpace as; - MemoryRegion mr; + S390PCIIOMMU *iommu; MemoryRegion iommu_mr; IndAddr *summary_ind; IndAddr *indicator; + QEMUTimer *release_timer; } S390PCIBusDevice; +typedef struct S390PCIBus { + BusState qbus; +} S390PCIBus; + typedef struct S390pciState { PCIHostState parent_obj; - S390PCIBusDevice pbdev[PCI_SLOT_MAX]; + S390PCIBus *bus; + S390PCIBusDevice *pbdev[PCI_SLOT_MAX]; + S390PCIIOMMU *iommu[PCI_SLOT_MAX]; AddressSpace msix_notify_as; MemoryRegion msix_notify_mr; QTAILQ_HEAD(, SeiContainer) pending_sei; @@ -252,8 +308,11 @@ void s390_pci_sclp_configure(SCCB *sccb); void s390_pci_sclp_deconfigure(SCCB *sccb); void s390_pci_iommu_enable(S390PCIBusDevice *pbdev); void s390_pci_iommu_disable(S390PCIBusDevice *pbdev); +void s390_pci_generate_error_event(uint16_t pec, uint32_t fh, uint32_t fid, + uint64_t faddr, uint32_t e); S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx); S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh); S390PCIBusDevice *s390_pci_find_dev_by_fid(uint32_t fid); +S390PCIBusDevice *s390_pci_find_next_avail_dev(S390PCIBusDevice *pbdev); #endif diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index 479375f65d..331bc4cfde 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -37,9 +37,9 @@ static void s390_set_status_code(CPUS390XState *env, static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc) { - S390PCIBusDevice *pbdev; - uint32_t res_code, initial_l2, g_l2, finish; - int rc, idx; + S390PCIBusDevice *pbdev = NULL; + uint32_t res_code, initial_l2, g_l2; + int rc, i; uint64_t resume_token; rc = 0; @@ -56,8 +56,7 @@ static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc) } if ((ldl_p(&rrb->request.fmt) & ~CLP_MASK_FMT) != 0 || - ldq_p(&rrb->request.reserved1) != 0 || - ldq_p(&rrb->request.reserved2) != 0) { + ldq_p(&rrb->request.reserved1) != 0) { res_code = CLP_RC_RESNOT0; rc = -EINVAL; goto out; @@ -72,6 +71,8 @@ static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc) rc = -EINVAL; goto out; } + } else { + pbdev = s390_pci_find_next_avail_dev(NULL); } if (lduw_p(&rrb->response.hdr.len) < 48) { @@ -91,43 +92,40 @@ static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc) stl_p(&rrb->response.fmt, 0); stq_p(&rrb->response.reserved1, 0); - stq_p(&rrb->response.reserved2, 0); - stl_p(&rrb->response.mdd, FH_VIRT); + stl_p(&rrb->response.mdd, FH_MASK_SHM); stw_p(&rrb->response.max_fn, PCI_MAX_FUNCTIONS); + rrb->response.flags = UID_CHECKING_ENABLED; rrb->response.entry_size = sizeof(ClpFhListEntry); - finish = 0; - idx = resume_token; + + i = 0; g_l2 = LIST_PCI_HDR_LEN; - do { - pbdev = s390_pci_find_dev_by_idx(idx); - if (!pbdev) { - finish = 1; - break; - } - stw_p(&rrb->response.fh_list[idx - resume_token].device_id, + while (g_l2 < initial_l2 && pbdev) { + stw_p(&rrb->response.fh_list[i].device_id, pci_get_word(pbdev->pdev->config + PCI_DEVICE_ID)); - stw_p(&rrb->response.fh_list[idx - resume_token].vendor_id, + stw_p(&rrb->response.fh_list[i].vendor_id, pci_get_word(pbdev->pdev->config + PCI_VENDOR_ID)); - stl_p(&rrb->response.fh_list[idx - resume_token].config, - pbdev->configured << 31); - stl_p(&rrb->response.fh_list[idx - resume_token].fid, pbdev->fid); - stl_p(&rrb->response.fh_list[idx - resume_token].fh, pbdev->fh); + /* Ignore RESERVED devices. */ + stl_p(&rrb->response.fh_list[i].config, + pbdev->state == ZPCI_FS_STANDBY ? 0 : 1 << 31); + stl_p(&rrb->response.fh_list[i].fid, pbdev->fid); + stl_p(&rrb->response.fh_list[i].fh, pbdev->fh); g_l2 += sizeof(ClpFhListEntry); /* Add endian check for DPRINTF? */ DPRINTF("g_l2 %d vendor id 0x%x device id 0x%x fid 0x%x fh 0x%x\n", - g_l2, - lduw_p(&rrb->response.fh_list[idx - resume_token].vendor_id), - lduw_p(&rrb->response.fh_list[idx - resume_token].device_id), - ldl_p(&rrb->response.fh_list[idx - resume_token].fid), - ldl_p(&rrb->response.fh_list[idx - resume_token].fh)); - idx++; - } while (g_l2 < initial_l2); - - if (finish == 1) { + g_l2, + lduw_p(&rrb->response.fh_list[i].vendor_id), + lduw_p(&rrb->response.fh_list[i].device_id), + ldl_p(&rrb->response.fh_list[i].fid), + ldl_p(&rrb->response.fh_list[i].fh)); + pbdev = s390_pci_find_next_avail_dev(pbdev); + i++; + } + + if (!pbdev) { resume_token = 0; } else { - resume_token = idx; + resume_token = pbdev->fh & FH_MASK_INDEX; } stq_p(&rrb->response.resume_token, resume_token); stw_p(&rrb->response.hdr.len, g_l2); @@ -212,14 +210,35 @@ int clp_service_call(S390CPU *cpu, uint8_t r2) switch (reqsetpci->oc) { case CLP_SET_ENABLE_PCI_FN: - pbdev->fh = pbdev->fh | FH_ENABLED; + switch (reqsetpci->ndas) { + case 0: + stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_DMAAS); + goto out; + case 1: + break; + default: + stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_RES); + goto out; + } + + if (pbdev->fh & FH_MASK_ENABLE) { + stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_FHOP); + goto out; + } + + pbdev->fh |= FH_MASK_ENABLE; + pbdev->state = ZPCI_FS_ENABLED; stl_p(&ressetpci->fh, pbdev->fh); stw_p(&ressetpci->hdr.rsp, CLP_RC_OK); break; case CLP_SET_DISABLE_PCI_FN: - pbdev->fh = pbdev->fh & ~FH_ENABLED; - pbdev->error_state = false; - pbdev->lgstg_blocked = false; + if (!(pbdev->fh & FH_MASK_ENABLE)) { + stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_FHOP); + goto out; + } + device_reset(DEVICE(pbdev)); + pbdev->fh &= ~FH_MASK_ENABLE; + pbdev->state = ZPCI_FS_DISABLED; stl_p(&ressetpci->fh, pbdev->fh); stw_p(&ressetpci->hdr.rsp, CLP_RC_OK); break; @@ -256,9 +275,10 @@ int clp_service_call(S390CPU *cpu, uint8_t r2) stq_p(&resquery->sdma, ZPCI_SDMA_ADDR); stq_p(&resquery->edma, ZPCI_EDMA_ADDR); + stl_p(&resquery->fid, pbdev->fid); stw_p(&resquery->pchid, 0); stw_p(&resquery->ug, 1); - stl_p(&resquery->uid, pbdev->fid); + stl_p(&resquery->uid, pbdev->uid); stw_p(&resquery->hdr.rsp, CLP_RC_OK); break; } @@ -317,16 +337,25 @@ int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) offset = env->regs[r2 + 1]; pbdev = s390_pci_find_dev_by_fh(fh); - if (!pbdev || !(pbdev->fh & FH_ENABLED)) { + if (!pbdev) { DPRINTF("pcilg no pci dev\n"); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); return 0; } - if (pbdev->lgstg_blocked) { + switch (pbdev->state) { + case ZPCI_FS_RESERVED: + case ZPCI_FS_STANDBY: + case ZPCI_FS_DISABLED: + case ZPCI_FS_PERMANENT_ERROR: + setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); + return 0; + case ZPCI_FS_ERROR: setcc(cpu, ZPCI_PCI_LS_ERR); s390_set_status_code(env, r2, ZPCI_PCI_ST_BLOCKED); return 0; + default: + break; } if (pcias < 6) { @@ -390,7 +419,8 @@ static void update_msix_table_msg_data(S390PCIBusDevice *pbdev, uint64_t offset, msg_data = (uint8_t *)data - offset % PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL; - val = pci_get_long(msg_data) | (pbdev->fid << ZPCI_MSI_VEC_BITS); + val = pci_get_long(msg_data) | + ((pbdev->fh & FH_MASK_INDEX) << ZPCI_MSI_VEC_BITS); pci_set_long(msg_data, val); DPRINTF("update msix msg_data to 0x%" PRIx64 "\n", *data); } @@ -434,16 +464,25 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) offset = env->regs[r2 + 1]; pbdev = s390_pci_find_dev_by_fh(fh); - if (!pbdev || !(pbdev->fh & FH_ENABLED)) { + if (!pbdev) { DPRINTF("pcistg no pci dev\n"); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); return 0; } - if (pbdev->lgstg_blocked) { + switch (pbdev->state) { + case ZPCI_FS_RESERVED: + case ZPCI_FS_STANDBY: + case ZPCI_FS_DISABLED: + case ZPCI_FS_PERMANENT_ERROR: + setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); + return 0; + case ZPCI_FS_ERROR: setcc(cpu, ZPCI_PCI_LS_ERR); s390_set_status_code(env, r2, ZPCI_PCI_ST_BLOCKED); return 0; + default: + break; } data = env->regs[r1]; @@ -525,18 +564,55 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) end = start + env->regs[r2 + 1]; pbdev = s390_pci_find_dev_by_fh(fh); - if (!pbdev || !(pbdev->fh & FH_ENABLED)) { + if (!pbdev) { DPRINTF("rpcit no pci dev\n"); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); goto out; } + switch (pbdev->state) { + case ZPCI_FS_RESERVED: + case ZPCI_FS_STANDBY: + case ZPCI_FS_DISABLED: + case ZPCI_FS_PERMANENT_ERROR: + setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); + return 0; + case ZPCI_FS_ERROR: + setcc(cpu, ZPCI_PCI_LS_ERR); + s390_set_status_code(env, r1, ZPCI_MOD_ST_ERROR_RECOVER); + return 0; + default: + break; + } + + if (!pbdev->g_iota) { + pbdev->state = ZPCI_FS_ERROR; + setcc(cpu, ZPCI_PCI_LS_ERR); + s390_set_status_code(env, r1, ZPCI_PCI_ST_INSUF_RES); + s390_pci_generate_error_event(ERR_EVENT_INVALAS, pbdev->fh, pbdev->fid, + start, 0); + goto out; + } + + if (end < pbdev->pba || start > pbdev->pal) { + pbdev->state = ZPCI_FS_ERROR; + setcc(cpu, ZPCI_PCI_LS_ERR); + s390_set_status_code(env, r1, ZPCI_PCI_ST_INSUF_RES); + s390_pci_generate_error_event(ERR_EVENT_OORANGE, pbdev->fh, pbdev->fid, + start, 0); + goto out; + } + mr = &pbdev->iommu_mr; while (start < end) { entry = mr->iommu_ops->translate(mr, start, 0); if (!entry.translated_addr) { + pbdev->state = ZPCI_FS_ERROR; setcc(cpu, ZPCI_PCI_LS_ERR); + s390_set_status_code(env, r1, ZPCI_PCI_ST_INSUF_RES); + s390_pci_generate_error_event(ERR_EVENT_SERR, pbdev->fh, pbdev->fid, + start, ERR_EVENT_Q_BIT); goto out; } @@ -589,16 +665,25 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr, } pbdev = s390_pci_find_dev_by_fh(fh); - if (!pbdev || !(pbdev->fh & FH_ENABLED)) { + if (!pbdev) { DPRINTF("pcistb no pci dev fh 0x%x\n", fh); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); return 0; } - if (pbdev->lgstg_blocked) { + switch (pbdev->state) { + case ZPCI_FS_RESERVED: + case ZPCI_FS_STANDBY: + case ZPCI_FS_DISABLED: + case ZPCI_FS_PERMANENT_ERROR: + setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); + return 0; + case ZPCI_FS_ERROR: setcc(cpu, ZPCI_PCI_LS_ERR); s390_set_status_code(env, r1, ZPCI_PCI_ST_BLOCKED); return 0; + default: + break; } mr = pbdev->pdev->io_regions[pcias].memory; @@ -742,12 +827,23 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) } pbdev = s390_pci_find_dev_by_fh(fh); - if (!pbdev || !(pbdev->fh & FH_ENABLED)) { + if (!pbdev) { DPRINTF("mpcifc no pci dev fh 0x%x\n", fh); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); return 0; } + switch (pbdev->state) { + case ZPCI_FS_RESERVED: + case ZPCI_FS_STANDBY: + case ZPCI_FS_DISABLED: + case ZPCI_FS_PERMANENT_ERROR: + setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); + return 0; + default: + break; + } + if (s390_cpu_virt_mem_read(cpu, fiba, ar, (uint8_t *)&fib, sizeof(fib))) { return 0; } @@ -814,11 +910,25 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) } break; case ZPCI_MOD_FC_RESET_ERROR: - pbdev->error_state = false; - pbdev->lgstg_blocked = false; + switch (pbdev->state) { + case ZPCI_FS_BLOCKED: + case ZPCI_FS_ERROR: + pbdev->state = ZPCI_FS_ENABLED; + break; + default: + cc = ZPCI_PCI_LS_ERR; + s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE); + } break; case ZPCI_MOD_FC_RESET_BLOCK: - pbdev->lgstg_blocked = false; + switch (pbdev->state) { + case ZPCI_FS_ERROR: + pbdev->state = ZPCI_FS_BLOCKED; + break; + default: + cc = ZPCI_PCI_LS_ERR; + s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE); + } break; case ZPCI_MOD_FC_SET_MEASURE: pbdev->fmb_addr = ldq_p(&fib.fmb_addr); @@ -835,6 +945,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) { CPUS390XState *env = &cpu->env; + uint8_t dmaas; uint32_t fh; ZpciFib fib; S390PCIBusDevice *pbdev; @@ -847,19 +958,59 @@ int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) } fh = env->regs[r1] >> 32; + dmaas = (env->regs[r1] >> 16) & 0xff; + + if (dmaas) { + setcc(cpu, ZPCI_PCI_LS_ERR); + s390_set_status_code(env, r1, ZPCI_STPCIFC_ST_INVAL_DMAAS); + return 0; + } if (fiba & 0x7) { program_interrupt(env, PGM_SPECIFICATION, 6); return 0; } - pbdev = s390_pci_find_dev_by_fh(fh); + pbdev = s390_pci_find_dev_by_idx(fh & FH_MASK_INDEX); if (!pbdev) { setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); return 0; } memset(&fib, 0, sizeof(fib)); + + switch (pbdev->state) { + case ZPCI_FS_RESERVED: + case ZPCI_FS_STANDBY: + setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); + return 0; + case ZPCI_FS_DISABLED: + if (fh & FH_MASK_ENABLE) { + setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); + return 0; + } + goto out; + /* BLOCKED bit is set to one coincident with the setting of ERROR bit. + * FH Enabled bit is set to one in states of ENABLED, BLOCKED or ERROR. */ + case ZPCI_FS_ERROR: + fib.fc |= 0x20; + case ZPCI_FS_BLOCKED: + fib.fc |= 0x40; + case ZPCI_FS_ENABLED: + fib.fc |= 0x80; + if (pbdev->iommu_enabled) { + fib.fc |= 0x10; + } + if (!(fh & FH_MASK_ENABLE)) { + env->regs[r1] |= 1ULL << 63; + } + break; + case ZPCI_FS_PERMANENT_ERROR: + setcc(cpu, ZPCI_PCI_LS_ERR); + s390_set_status_code(env, r1, ZPCI_STPCIFC_ST_PERM_ERROR); + return 0; + } + stq_p(&fib.pba, pbdev->pba); stq_p(&fib.pal, pbdev->pal); stq_p(&fib.iota, pbdev->g_iota); @@ -872,22 +1023,7 @@ int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) ((uint32_t)pbdev->sum << 7) | pbdev->routes.adapter.summary_offset; stl_p(&fib.data, data); - if (pbdev->fh & FH_ENABLED) { - fib.fc |= 0x80; - } - - if (pbdev->error_state) { - fib.fc |= 0x40; - } - - if (pbdev->lgstg_blocked) { - fib.fc |= 0x20; - } - - if (pbdev->g_iota) { - fib.fc |= 0x10; - } - +out: if (s390_cpu_virt_mem_write(cpu, fiba, ar, (uint8_t *)&fib, sizeof(fib))) { return 0; } diff --git a/hw/s390x/s390-pci-inst.h b/hw/s390x/s390-pci-inst.h index b084f2346b..e1c2ee1529 100644 --- a/hw/s390x/s390-pci-inst.h +++ b/hw/s390x/s390-pci-inst.h @@ -104,7 +104,7 @@ typedef struct ClpRspListPci { uint64_t resume_token; uint32_t mdd; uint16_t max_fn; - uint8_t reserved2; + uint8_t flags; uint8_t entry_size; ClpFhListEntry fh_list[CLP_FH_LIST_NR_ENTRIES]; } QEMU_PACKED ClpRspListPci; @@ -249,6 +249,11 @@ typedef struct ClpReqRspQueryPciGrp { #define ZPCI_MOD_FC_RESET_BLOCK 9 #define ZPCI_MOD_FC_SET_MEASURE 10 +/* Store PCI Function Controls status codes */ +#define ZPCI_STPCIFC_ST_PERM_ERROR 8 +#define ZPCI_STPCIFC_ST_INVAL_DMAAS 28 +#define ZPCI_STPCIFC_ST_ERROR_RECOVER 40 + /* FIB function controls */ #define ZPCI_FIB_FC_ENABLED 0x80 #define ZPCI_FIB_FC_ERROR 0x40 diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 52f079a884..caf0a682a7 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -27,9 +27,10 @@ #include "hw/compat.h" #include "ipl.h" #include "hw/s390x/s390-virtio-ccw.h" +#include "hw/s390x/css-bridge.h" static const char *const reset_dev_types[] = { - "virtual-css-bridge", + TYPE_VIRTUAL_CSS_BRIDGE, "s390-sclp-event-facility", "s390-flic", "diag288", diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 8b709e362e..a554a24d06 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -33,31 +33,11 @@ #include "hw/s390x/css.h" #include "virtio-ccw.h" #include "trace.h" +#include "hw/s390x/css-bridge.h" static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size, VirtioCcwDevice *dev); -static void virtual_css_bus_reset(BusState *qbus) -{ - /* This should actually be modelled via the generic css */ - css_reset(); -} - - -static void virtual_css_bus_class_init(ObjectClass *klass, void *data) -{ - BusClass *k = BUS_CLASS(klass); - - k->reset = virtual_css_bus_reset; -} - -static const TypeInfo virtual_css_bus_info = { - .name = TYPE_VIRTUAL_CSS_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(VirtualCssBus), - .class_init = virtual_css_bus_class_init, -}; - VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch) { VirtIODevice *vdev = NULL; @@ -117,32 +97,13 @@ static int virtio_ccw_ioeventfd_assign(DeviceState *d, EventNotifier *notifier, int n, bool assign) { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); - SubchDev *sch = dev->sch; + CcwDevice *ccw_dev = CCW_DEVICE(dev); + SubchDev *sch = ccw_dev->sch; uint32_t sch_id = (css_build_subchannel_id(sch) << 16) | sch->schid; return s390_assign_subch_ioeventfd(notifier, sch_id, n, assign); } -VirtualCssBus *virtual_css_bus_init(void) -{ - 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 */ - qbus_set_hotplug_handler(bus, dev, &error_abort); - - return cbus; -} - /* Communication blocks used by several channel commands. */ typedef struct VqInfoBlockLegacy { uint64_t queue; @@ -234,6 +195,8 @@ static int virtio_ccw_set_vqs(SubchDev *sch, VqInfoBlock *info, static void virtio_ccw_reset_virtio(VirtioCcwDevice *dev, VirtIODevice *vdev) { + CcwDevice *ccw_dev = CCW_DEVICE(dev); + virtio_ccw_stop_ioeventfd(dev); virtio_reset(vdev); if (dev->indicators) { @@ -248,7 +211,7 @@ static void virtio_ccw_reset_virtio(VirtioCcwDevice *dev, VirtIODevice *vdev) release_indicator(&dev->routes.adapter, dev->summary_indicator); dev->summary_indicator = NULL; } - dev->sch->thinint_active = false; + ccw_dev->sch->thinint_active = false; } static int virtio_ccw_handle_set_vq(SubchDev *sch, CCW1 ccw, bool check_len, @@ -703,116 +666,28 @@ static void virtio_sch_disable_cb(SubchDev *sch) static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp) { - unsigned int schid; - bool found = false; - SubchDev *sch; - Error *err = NULL; VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_GET_CLASS(dev); + CcwDevice *ccw_dev = CCW_DEVICE(dev); + SubchDev *sch = css_create_virtual_sch(ccw_dev->bus_id, errp); + Error *err = NULL; - sch = g_malloc0(sizeof(SubchDev)); - - sch->driver_data = dev; - dev->sch = sch; - - dev->indicators = NULL; - - /* Initialize subchannel structure. */ - sch->channel_prog = 0x0; - sch->last_cmd_valid = false; - sch->thinint_active = false; - /* - * Use a device number if provided. Otherwise, fall back to subchannel - * number. - */ - if (dev->bus_id.valid) { - /* Enforce use of virtual cssid. */ - if (dev->bus_id.cssid != VIRTUAL_CSSID) { - error_setg(errp, "cssid %x not valid for virtio devices", - dev->bus_id.cssid); - goto out_err; - } - if (css_devno_used(dev->bus_id.cssid, dev->bus_id.ssid, - dev->bus_id.devid)) { - error_setg(errp, "Device %x.%x.%04x already exists", - dev->bus_id.cssid, dev->bus_id.ssid, - dev->bus_id.devid); - goto out_err; - } - sch->cssid = dev->bus_id.cssid; - sch->ssid = dev->bus_id.ssid; - sch->devno = dev->bus_id.devid; - - /* Find the next free id. */ - for (schid = 0; schid <= MAX_SCHID; schid++) { - if (!css_find_subch(1, sch->cssid, sch->ssid, schid)) { - sch->schid = schid; - css_subch_assign(sch->cssid, sch->ssid, sch->schid, - sch->devno, sch); - found = true; - break; - } - } - if (!found) { - error_setg(errp, "No free subchannel found for %x.%x.%04x", - sch->cssid, sch->ssid, sch->devno); - goto out_err; - } - trace_virtio_ccw_new_device(sch->cssid, sch->ssid, sch->schid, - sch->devno, "user-configured"); - } else { - unsigned int cssid = VIRTUAL_CSSID, ssid, devno; - - for (ssid = 0; ssid <= MAX_SSID; ssid++) { - for (schid = 0; schid <= MAX_SCHID; schid++) { - if (!css_find_subch(1, cssid, ssid, schid)) { - sch->cssid = cssid; - sch->ssid = ssid; - sch->schid = schid; - devno = schid; - /* - * If the devno is already taken, look further in this - * subchannel set. - */ - while (css_devno_used(cssid, ssid, devno)) { - if (devno == MAX_SCHID) { - devno = 0; - } else if (devno == schid - 1) { - error_setg(errp, "No free devno found"); - goto out_err; - } else { - devno++; - } - } - sch->devno = devno; - css_subch_assign(cssid, ssid, schid, devno, sch); - found = true; - break; - } - } - if (found) { - break; - } - } - if (!found) { - error_setg(errp, "Virtual channel subsystem is full!"); - goto out_err; - } - trace_virtio_ccw_new_device(cssid, ssid, schid, devno, - "auto-configured"); + if (!sch) { + return; } - /* Build initial schib. */ - css_sch_build_virtual_schib(sch, 0, VIRTIO_CCW_CHPID_TYPE); - + sch->driver_data = dev; sch->ccw_cb = virtio_ccw_cb; sch->disable_cb = virtio_sch_disable_cb; - - /* Build senseid data. */ - memset(&sch->id, 0, sizeof(SenseId)); sch->id.reserved = 0xff; sch->id.cu_type = VIRTIO_CCW_CU_TYPE; - + ccw_dev->sch = sch; + dev->indicators = NULL; dev->revision = -1; + css_sch_build_virtual_schib(sch, 0, VIRTIO_CCW_CHPID_TYPE); + + trace_virtio_ccw_new_device( + sch->cssid, sch->ssid, sch->schid, sch->devno, + ccw_dev->bus_id.valid ? "user-configured" : "auto-configured"); if (k->realize) { k->realize(dev, &err); @@ -820,19 +695,15 @@ static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp) if (err) { error_propagate(errp, err); css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); - goto out_err; + ccw_dev->sch = NULL; + g_free(sch); } - - return; - -out_err: - dev->sch = NULL; - g_free(sch); } static int virtio_ccw_exit(VirtioCcwDevice *dev) { - SubchDev *sch = dev->sch; + CcwDevice *ccw_dev = CCW_DEVICE(dev); + SubchDev *sch = ccw_dev->sch; if (sch) { css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); @@ -1013,7 +884,9 @@ static void virtio_ccw_rng_realize(VirtioCcwDevice *ccw_dev, Error **errp) */ static inline VirtioCcwDevice *to_virtio_ccw_dev_fast(DeviceState *d) { - return container_of(d, VirtioCcwDevice, parent_obj); + CcwDevice *ccw_dev = to_ccw_dev_fast(d); + + return container_of(ccw_dev, VirtioCcwDevice, parent_obj); } static uint8_t virtio_set_ind_atomic(SubchDev *sch, uint64_t ind_loc, @@ -1042,7 +915,8 @@ static uint8_t virtio_set_ind_atomic(SubchDev *sch, uint64_t ind_loc, static void virtio_ccw_notify(DeviceState *d, uint16_t vector) { VirtioCcwDevice *dev = to_virtio_ccw_dev_fast(d); - SubchDev *sch = dev->sch; + CcwDevice *ccw_dev = to_ccw_dev_fast(d); + SubchDev *sch = ccw_dev->sch; uint64_t indicators; /* queue indicators + secondary indicators */ @@ -1100,9 +974,10 @@ static void virtio_ccw_reset(DeviceState *d) { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); + CcwDevice *ccw_dev = CCW_DEVICE(d); virtio_ccw_reset_virtio(dev, vdev); - css_reset_sch(dev->sch); + css_reset_sch(ccw_dev->sch); } static void virtio_ccw_vmstate_change(DeviceState *d, bool running) @@ -1118,7 +993,7 @@ static void virtio_ccw_vmstate_change(DeviceState *d, bool running) static bool virtio_ccw_query_guest_notifiers(DeviceState *d) { - VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); + CcwDevice *dev = CCW_DEVICE(d); return !!(dev->sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ENA); } @@ -1126,8 +1001,9 @@ static bool virtio_ccw_query_guest_notifiers(DeviceState *d) static int virtio_ccw_get_mappings(VirtioCcwDevice *dev) { int r; + CcwDevice *ccw_dev = CCW_DEVICE(dev); - if (!dev->sch->thinint_active) { + if (!ccw_dev->sch->thinint_active) { return -EINVAL; } @@ -1249,7 +1125,8 @@ static int virtio_ccw_set_guest_notifiers(DeviceState *d, int nvqs, { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); - bool with_irqfd = dev->sch->thinint_active && kvm_irqfds_enabled(); + CcwDevice *ccw_dev = CCW_DEVICE(d); + bool with_irqfd = ccw_dev->sch->thinint_active && kvm_irqfds_enabled(); int r, n; if (with_irqfd && assigned) { @@ -1308,7 +1185,8 @@ static int virtio_ccw_load_queue(DeviceState *d, int n, QEMUFile *f) static void virtio_ccw_save_config(DeviceState *d, QEMUFile *f) { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); - SubchDev *s = dev->sch; + CcwDevice *ccw_dev = CCW_DEVICE(d); + SubchDev *s = ccw_dev->sch; VirtIODevice *vdev = virtio_ccw_get_vdev(s); subch_device_save(s, f); @@ -1342,7 +1220,8 @@ static void virtio_ccw_save_config(DeviceState *d, QEMUFile *f) static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f) { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); - SubchDev *s = dev->sch; + CcwDevice *ccw_dev = CCW_DEVICE(d); + SubchDev *s = ccw_dev->sch; VirtIODevice *vdev = virtio_ccw_get_vdev(s); int len; @@ -1387,7 +1266,8 @@ static void virtio_ccw_device_plugged(DeviceState *d, Error **errp) { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); - SubchDev *sch = dev->sch; + CcwDevice *ccw_dev = CCW_DEVICE(d); + SubchDev *sch = ccw_dev->sch; int n = virtio_get_num_queues(vdev); if (virtio_get_num_queues(vdev) > VIRTIO_CCW_QUEUE_MAX) { @@ -1431,7 +1311,7 @@ static void virtio_ccw_device_unplugged(DeviceState *d) /**************** Virtio-ccw Bus Device Descriptions *******************/ static Property virtio_ccw_net_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, bus_id), + DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1460,7 +1340,7 @@ static const TypeInfo virtio_ccw_net = { }; static Property virtio_ccw_blk_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, bus_id), + DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1489,7 +1369,7 @@ static const TypeInfo virtio_ccw_blk = { }; static Property virtio_ccw_serial_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, bus_id), + DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1518,7 +1398,7 @@ static const TypeInfo virtio_ccw_serial = { }; static Property virtio_ccw_balloon_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, bus_id), + DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1547,7 +1427,7 @@ static const TypeInfo virtio_ccw_balloon = { }; static Property virtio_ccw_scsi_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, bus_id), + DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1577,7 +1457,7 @@ static const TypeInfo virtio_ccw_scsi = { #ifdef CONFIG_VHOST_SCSI static Property vhost_ccw_scsi_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, bus_id), + DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), DEFINE_PROP_END_OF_LIST(), @@ -1615,7 +1495,7 @@ static void virtio_ccw_rng_instance_init(Object *obj) } static Property virtio_ccw_rng_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, bus_id), + DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1662,29 +1542,17 @@ static int virtio_ccw_busdev_exit(DeviceState *dev) static void virtio_ccw_busdev_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev; - SubchDev *sch = _dev->sch; + VirtioCcwDevice *_dev = to_virtio_ccw_dev_fast(dev); virtio_ccw_stop_ioeventfd(_dev); - - /* - * We should arrive here only for device_del, since we don't support - * direct hot(un)plug of channels, but only through virtio. - */ - assert(sch != NULL); - /* Subchannel is now disabled and no longer valid. */ - sch->curr_status.pmcw.flags &= ~(PMCW_FLAGS_MASK_ENA | - PMCW_FLAGS_MASK_DNV); - - css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, 1, 0); - - object_unparent(OBJECT(dev)); } static void virtio_ccw_device_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + CCWDeviceClass *k = CCW_DEVICE_CLASS(dc); + k->unplug = virtio_ccw_busdev_unplug; dc->realize = virtio_ccw_busdev_realize; dc->exit = virtio_ccw_busdev_exit; dc->bus_type = TYPE_VIRTUAL_CSS_BUS; @@ -1692,44 +1560,13 @@ static void virtio_ccw_device_class_init(ObjectClass *klass, void *data) static const TypeInfo virtio_ccw_device_info = { .name = TYPE_VIRTIO_CCW_DEVICE, - .parent = TYPE_DEVICE, + .parent = TYPE_CCW_DEVICE, .instance_size = sizeof(VirtioCcwDevice), .class_init = virtio_ccw_device_class_init, .class_size = sizeof(VirtIOCCWDeviceClass), .abstract = true, }; -/***************** Virtual-css Bus Bridge Device ********************/ -/* Only required to have the virtio bus as child in the system bus */ - -static int virtual_css_bridge_init(SysBusDevice *dev) -{ - /* nothing */ - return 0; -} - -static void virtual_css_bridge_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->init = virtual_css_bridge_init; - hc->unplug = virtio_ccw_busdev_unplug; - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); -} - -static const TypeInfo virtual_css_bridge_info = { - .name = "virtual-css-bridge", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SysBusDevice), - .class_init = virtual_css_bridge_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_HOTPLUG_HANDLER }, - { } - } -}; - /* virtio-ccw-bus */ static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size, @@ -1775,7 +1612,7 @@ static const TypeInfo virtio_ccw_bus_info = { #ifdef CONFIG_VIRTFS static Property virtio_ccw_9p_properties[] = { - DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, bus_id), + DEFINE_PROP_CSS_DEV_ID("devno", VirtioCcwDevice, parent_obj.bus_id), DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags, VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, @@ -1824,7 +1661,6 @@ static const TypeInfo virtio_ccw_9p_info = { static void virtio_ccw_register(void) { type_register_static(&virtio_ccw_bus_info); - type_register_static(&virtual_css_bus_info); type_register_static(&virtio_ccw_device_info); type_register_static(&virtio_ccw_serial); type_register_static(&virtio_ccw_blk); @@ -1835,7 +1671,6 @@ static void virtio_ccw_register(void) type_register_static(&vhost_ccw_scsi); #endif type_register_static(&virtio_ccw_rng); - type_register_static(&virtual_css_bridge_info); #ifdef CONFIG_VIRTFS type_register_static(&virtio_ccw_9p_info); #endif diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h index 0bfb5d93c6..3f76443fba 100644 --- a/hw/s390x/virtio-ccw.h +++ b/hw/s390x/virtio-ccw.h @@ -26,8 +26,8 @@ #include <hw/s390x/s390_flic.h> #include <hw/s390x/css.h> - -#define VIRTUAL_CSSID 0xfe +#include "ccw-device.h" +#include "hw/s390x/css-bridge.h" #define VIRTIO_CCW_CU_TYPE 0x3832 #define VIRTIO_CCW_CHPID_TYPE 0x32 @@ -67,7 +67,7 @@ typedef struct VirtioBusClass VirtioCcwBusClass; typedef struct VirtioCcwDevice VirtioCcwDevice; typedef struct VirtIOCCWDeviceClass { - DeviceClass parent_class; + CCWDeviceClass parent_class; void (*realize)(VirtioCcwDevice *dev, Error **errp); int (*exit)(VirtioCcwDevice *dev); } VirtIOCCWDeviceClass; @@ -78,9 +78,7 @@ typedef struct VirtIOCCWDeviceClass { #define VIRTIO_CCW_FLAG_USE_IOEVENTFD (1 << VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT) struct VirtioCcwDevice { - DeviceState parent_obj; - SubchDev *sch; - CssDevId bus_id; + CcwDevice parent_obj; int revision; uint32_t max_rev; VirtioBusState bus; @@ -103,15 +101,6 @@ static inline int virtio_ccw_rev_max(VirtioCcwDevice *dev) return dev->max_rev; } -/* virtual css bus type */ -typedef struct VirtualCssBus { - BusState parent_obj; -} VirtualCssBus; - -#define TYPE_VIRTUAL_CSS_BUS "virtual-css-bus" -#define VIRTUAL_CSS_BUS(obj) \ - OBJECT_CHECK(VirtualCssBus, (obj), TYPE_VIRTUAL_CSS_BUS) - /* virtio-scsi-ccw */ #define TYPE_VIRTIO_SCSI_CCW "virtio-scsi-ccw" @@ -191,7 +180,6 @@ typedef struct VirtIORNGCcw { VirtIORNG vdev; } VirtIORNGCcw; -VirtualCssBus *virtual_css_bus_init(void); void virtio_ccw_device_update_status(SubchDev *sch); VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch); diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index d1772183cf..52a41239cf 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -29,7 +29,7 @@ #include "hw/scsi/scsi.h" #include "block/scsi.h" #include "trace.h" - +#include "qapi/error.h" #include "mfi.h" #define MEGASAS_VERSION_GEN1 "1.70" @@ -48,11 +48,7 @@ #define MEGASAS_FLAG_USE_JBOD 0 #define MEGASAS_MASK_USE_JBOD (1 << MEGASAS_FLAG_USE_JBOD) -#define MEGASAS_FLAG_USE_MSI 1 -#define MEGASAS_MASK_USE_MSI (1 << MEGASAS_FLAG_USE_MSI) -#define MEGASAS_FLAG_USE_MSIX 2 -#define MEGASAS_MASK_USE_MSIX (1 << MEGASAS_FLAG_USE_MSIX) -#define MEGASAS_FLAG_USE_QUEUE64 3 +#define MEGASAS_FLAG_USE_QUEUE64 1 #define MEGASAS_MASK_USE_QUEUE64 (1 << MEGASAS_FLAG_USE_QUEUE64) static const char *mfi_frame_desc[] = { @@ -96,6 +92,8 @@ typedef struct MegasasState { int busy; int diag; int adp_reset; + OnOffAuto msi; + OnOffAuto msix; MegasasCmd *event_cmd; int event_locale; @@ -157,14 +155,9 @@ static bool megasas_use_queue64(MegasasState *s) return s->flags & MEGASAS_MASK_USE_QUEUE64; } -static bool megasas_use_msi(MegasasState *s) -{ - return s->flags & MEGASAS_MASK_USE_MSI; -} - static bool megasas_use_msix(MegasasState *s) { - return s->flags & MEGASAS_MASK_USE_MSIX; + return s->msix != ON_OFF_AUTO_OFF; } static bool megasas_is_jbod(MegasasState *s) @@ -2309,9 +2302,7 @@ static void megasas_scsi_uninit(PCIDevice *d) if (megasas_use_msix(s)) { msix_uninit(d, &s->mmio_io, &s->mmio_io); } - if (megasas_use_msi(s)) { - msi_uninit(d); - } + msi_uninit(d); } static const struct SCSIBusInfo megasas_scsi_info = { @@ -2332,6 +2323,8 @@ static void megasas_scsi_realize(PCIDevice *dev, Error **errp) MegasasBaseClass *b = MEGASAS_DEVICE_GET_CLASS(s); uint8_t *pci_conf; int i, bar_type; + Error *err = NULL; + int ret; pci_conf = dev->config; @@ -2340,6 +2333,24 @@ static void megasas_scsi_realize(PCIDevice *dev, Error **errp) /* Interrupt pin 1 */ pci_conf[PCI_INTERRUPT_PIN] = 0x01; + if (s->msi != ON_OFF_AUTO_OFF) { + ret = msi_init(dev, 0x50, 1, true, false, &err); + /* Any error other than -ENOTSUP(board's MSI support is broken) + * is a programming error */ + assert(!ret || ret == -ENOTSUP); + if (ret && s->msi == ON_OFF_AUTO_ON) { + /* Can't satisfy user's explicit msi=on request, fail */ + error_append_hint(&err, "You have to use msi=auto (default) or " + "msi=off with this machine type.\n"); + error_propagate(errp, err); + return; + } else if (ret) { + /* With msi=auto, we fall back to MSI off silently */ + s->msi = ON_OFF_AUTO_OFF; + error_free(err); + } + } + memory_region_init_io(&s->mmio_io, OBJECT(s), &megasas_mmio_ops, s, "megasas-mmio", 0x4000); memory_region_init_io(&s->port_io, OBJECT(s), &megasas_port_ops, s, @@ -2347,14 +2358,10 @@ static void megasas_scsi_realize(PCIDevice *dev, Error **errp) memory_region_init_io(&s->queue_io, OBJECT(s), &megasas_queue_ops, s, "megasas-queue", 0x40000); - if (megasas_use_msi(s) && - msi_init(dev, 0x50, 1, true, false)) { - s->flags &= ~MEGASAS_MASK_USE_MSI; - } if (megasas_use_msix(s) && msix_init(dev, 15, &s->mmio_io, b->mmio_bar, 0x2000, &s->mmio_io, b->mmio_bar, 0x3800, 0x68)) { - s->flags &= ~MEGASAS_MASK_USE_MSIX; + s->msix = ON_OFF_AUTO_OFF; } if (pci_is_express(dev)) { pcie_endpoint_cap_init(dev, 0xa0); @@ -2422,10 +2429,8 @@ static Property megasas_properties_gen1[] = { MEGASAS_DEFAULT_FRAMES), DEFINE_PROP_STRING("hba_serial", MegasasState, hba_serial), DEFINE_PROP_UINT64("sas_address", MegasasState, sas_addr, 0), - DEFINE_PROP_BIT("use_msi", MegasasState, flags, - MEGASAS_FLAG_USE_MSI, false), - DEFINE_PROP_BIT("use_msix", MegasasState, flags, - MEGASAS_FLAG_USE_MSIX, false), + DEFINE_PROP_ON_OFF_AUTO("msi", MegasasState, msi, ON_OFF_AUTO_AUTO), + DEFINE_PROP_ON_OFF_AUTO("msix", MegasasState, msix, ON_OFF_AUTO_AUTO), DEFINE_PROP_BIT("use_jbod", MegasasState, flags, MEGASAS_FLAG_USE_JBOD, false), DEFINE_PROP_END_OF_LIST(), @@ -2438,10 +2443,8 @@ static Property megasas_properties_gen2[] = { MEGASAS_GEN2_DEFAULT_FRAMES), DEFINE_PROP_STRING("hba_serial", MegasasState, hba_serial), DEFINE_PROP_UINT64("sas_address", MegasasState, sas_addr, 0), - DEFINE_PROP_BIT("use_msi", MegasasState, flags, - MEGASAS_FLAG_USE_MSI, true), - DEFINE_PROP_BIT("use_msix", MegasasState, flags, - MEGASAS_FLAG_USE_MSIX, true), + DEFINE_PROP_ON_OFF_AUTO("msi", MegasasState, msi, ON_OFF_AUTO_AUTO), + DEFINE_PROP_ON_OFF_AUTO("msix", MegasasState, msix, ON_OFF_AUTO_AUTO), DEFINE_PROP_BIT("use_jbod", MegasasState, flags, MEGASAS_FLAG_USE_JBOD, false), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c index be88e161a9..1ae32fb7d3 100644 --- a/hw/scsi/mptsas.c +++ b/hw/scsi/mptsas.c @@ -32,7 +32,7 @@ #include "hw/scsi/scsi.h" #include "block/scsi.h" #include "trace.h" - +#include "qapi/error.h" #include "mptsas.h" #include "mpi.h" @@ -63,7 +63,7 @@ static void mptsas_update_interrupt(MPTSASState *s) PCIDevice *pci = (PCIDevice *) s; uint32_t state = s->intr_status & ~(s->intr_mask | MPI_HIS_IOP_DOORBELL_STATUS); - if (s->msi_in_use && msi_enabled(pci)) { + if (msi_enabled(pci)) { if (state) { trace_mptsas_irq_msi(s); msi_notify(pci, 0); @@ -1273,10 +1273,30 @@ static void mptsas_scsi_init(PCIDevice *dev, Error **errp) { DeviceState *d = DEVICE(dev); MPTSASState *s = MPT_SAS(dev); + Error *err = NULL; + int ret; dev->config[PCI_LATENCY_TIMER] = 0; dev->config[PCI_INTERRUPT_PIN] = 0x01; + if (s->msi != ON_OFF_AUTO_OFF) { + ret = msi_init(dev, 0, 1, true, false, &err); + /* Any error other than -ENOTSUP(board's MSI support is broken) + * is a programming error */ + assert(!ret || ret == -ENOTSUP); + if (ret && s->msi == ON_OFF_AUTO_ON) { + /* Can't satisfy user's explicit msi=on request, fail */ + error_append_hint(&err, "You have to use msi=auto (default) or " + "msi=off with this machine type.\n"); + error_propagate(errp, err); + return; + } + assert(!err || s->msi == ON_OFF_AUTO_AUTO); + /* With msi=auto, we fall back to MSI off silently */ + error_free(err); + + } + memory_region_init_io(&s->mmio_io, OBJECT(s), &mptsas_mmio_ops, s, "mptsas-mmio", 0x4000); memory_region_init_io(&s->port_io, OBJECT(s), &mptsas_port_ops, s, @@ -1284,11 +1304,6 @@ static void mptsas_scsi_init(PCIDevice *dev, Error **errp) memory_region_init_io(&s->diag_io, OBJECT(s), &mptsas_diag_ops, s, "mptsas-diag", 0x10000); - if (s->msi_available && - msi_init(dev, 0, 1, true, false) >= 0) { - s->msi_in_use = true; - } - pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->port_io); pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_32, &s->mmio_io); @@ -1319,9 +1334,7 @@ static void mptsas_scsi_uninit(PCIDevice *dev) MPTSASState *s = MPT_SAS(dev); qemu_bh_delete(s->request_bh); - if (s->msi_in_use) { - msi_uninit(dev); - } + msi_uninit(dev); } static void mptsas_reset(DeviceState *dev) @@ -1357,7 +1370,6 @@ static const VMStateDescription vmstate_mptsas = { .post_load = mptsas_post_load, .fields = (VMStateField[]) { VMSTATE_PCI_DEVICE(dev, MPTSASState), - VMSTATE_BOOL(msi_in_use, MPTSASState), VMSTATE_UINT32(state, MPTSASState), VMSTATE_UINT8(who_init, MPTSASState), @@ -1403,7 +1415,7 @@ static const VMStateDescription vmstate_mptsas = { static Property mptsas_properties[] = { DEFINE_PROP_UINT64("sas_address", MPTSASState, sas_addr, 0), /* TODO: test MSI support under Windows */ - DEFINE_PROP_BIT("msi", MPTSASState, msi_available, 0, true), + DEFINE_PROP_ON_OFF_AUTO("msi", MPTSASState, msi, ON_OFF_AUTO_AUTO), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/scsi/mptsas.h b/hw/scsi/mptsas.h index 595f81fb5b..da014a397e 100644 --- a/hw/scsi/mptsas.h +++ b/hw/scsi/mptsas.h @@ -27,11 +27,10 @@ struct MPTSASState { MemoryRegion diag_io; QEMUBH *request_bh; - uint32_t msi_available; + /* properties */ + OnOffAuto msi; uint64_t sas_addr; - bool msi_in_use; - /* Doorbell register */ uint32_t state; uint8_t who_init; diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index 6a2d89afba..7a588a7ad4 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -225,13 +225,14 @@ static void scsi_read_complete(void * opaque, int ret) if (s->type == TYPE_DISK && r->req.cmd.buf[0] == INQUIRY && r->req.cmd.buf[2] == 0xb0) { - uint32_t max_xfer_len = blk_get_max_transfer_length(s->conf.blk); - if (max_xfer_len) { - stl_be_p(&r->buf[8], max_xfer_len); - /* Also take care of the opt xfer len. */ - if (ldl_be_p(&r->buf[12]) > max_xfer_len) { - stl_be_p(&r->buf[12], max_xfer_len); - } + uint32_t max_transfer = + blk_get_max_transfer(s->conf.blk) / s->blocksize; + + assert(max_transfer); + stl_be_p(&r->buf[8], max_transfer); + /* Also take care of the opt xfer len. */ + if (ldl_be_p(&r->buf[12]) > max_transfer) { + stl_be_p(&r->buf[12], max_transfer); } } scsi_req_data(&r->req, len); diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c index 9261d51da7..2a00f2f3c8 100644 --- a/hw/scsi/vhost-scsi.c +++ b/hw/scsi/vhost-scsi.c @@ -248,7 +248,7 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp) s->dev.backend_features = 0; ret = vhost_dev_init(&s->dev, (void *)(uintptr_t)vhostfd, - VHOST_BACKEND_TYPE_KERNEL); + VHOST_BACKEND_TYPE_KERNEL, 0); if (ret < 0) { error_setg(errp, "vhost-scsi: vhost initialization failed: %s", strerror(-ret)); diff --git a/hw/scsi/vmw_pvscsi.c b/hw/scsi/vmw_pvscsi.c index 2d7528d1dd..da71c8c8a5 100644 --- a/hw/scsi/vmw_pvscsi.c +++ b/hw/scsi/vmw_pvscsi.c @@ -121,8 +121,7 @@ typedef struct { uint8_t msg_ring_info_valid; /* Whether message ring initialized */ uint8_t use_msg; /* Whether to use message ring */ - uint8_t msi_used; /* Whether MSI support was installed successfully */ - + uint8_t msi_used; /* For migration compatibility */ PVSCSIRingInfo rings; /* Data transfer rings manager */ uint32_t resetting; /* Reset in progress */ @@ -362,7 +361,7 @@ pvscsi_update_irq_status(PVSCSIState *s) trace_pvscsi_update_irq_level(should_raise, s->reg_interrupt_enabled, s->reg_interrupt_status); - if (s->msi_used && msi_enabled(d)) { + if (msi_enabled(d)) { if (should_raise) { trace_pvscsi_update_irq_msi(); msi_notify(d, PVSCSI_VECTOR_COMPLETION); @@ -1056,22 +1055,20 @@ pvscsi_io_read(void *opaque, hwaddr addr, unsigned size) } -static bool +static void pvscsi_init_msi(PVSCSIState *s) { int res; PCIDevice *d = PCI_DEVICE(s); res = msi_init(d, PVSCSI_MSI_OFFSET(s), PVSCSI_MSIX_NUM_VECTORS, - PVSCSI_USE_64BIT, PVSCSI_PER_VECTOR_MASK); + PVSCSI_USE_64BIT, PVSCSI_PER_VECTOR_MASK, NULL); if (res < 0) { trace_pvscsi_init_msi_fail(res); s->msi_used = false; } else { s->msi_used = true; } - - return s->msi_used; } static void @@ -1079,9 +1076,7 @@ pvscsi_cleanup_msi(PVSCSIState *s) { PCIDevice *d = PCI_DEVICE(s); - if (s->msi_used) { - msi_uninit(d); - } + msi_uninit(d); } static const MemoryRegionOps pvscsi_ops = { diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c index 075e4ed5df..3ff0886dd5 100644 --- a/hw/sd/ssi-sd.c +++ b/hw/sd/ssi-sd.c @@ -15,6 +15,7 @@ #include "sysemu/blockdev.h" #include "hw/ssi/ssi.h" #include "hw/sd/sd.h" +#include "qapi/error.h" //#define DEBUG_SSI_SD 1 @@ -249,7 +250,7 @@ static int ssi_sd_load(QEMUFile *f, void *opaque, int version_id) return 0; } -static int ssi_sd_init(SSISlave *d) +static void ssi_sd_realize(SSISlave *d, Error **errp) { DeviceState *dev = DEVICE(d); ssi_sd_state *s = FROM_SSI_SLAVE(ssi_sd_state, d); @@ -260,17 +261,17 @@ static int ssi_sd_init(SSISlave *d) dinfo = drive_get_next(IF_SD); s->sd = sd_init(dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, true); if (s->sd == NULL) { - return -1; + error_setg(errp, "Device initialization failed."); + return; } register_savevm(dev, "ssi_sd", -1, 1, ssi_sd_save, ssi_sd_load, s); - return 0; } static void ssi_sd_class_init(ObjectClass *klass, void *data) { SSISlaveClass *k = SSI_SLAVE_CLASS(klass); - k->init = ssi_sd_init; + k->realize = ssi_sd_realize; k->transfer = ssi_sd_transfer; k->cs_polarity = SSI_CS_LOW; } diff --git a/hw/ssi/Makefile.objs b/hw/ssi/Makefile.objs index fcbb79ef01..c79a8dcd86 100644 --- a/hw/ssi/Makefile.objs +++ b/hw/ssi/Makefile.objs @@ -2,6 +2,7 @@ common-obj-$(CONFIG_PL022) += pl022.o common-obj-$(CONFIG_SSI) += ssi.o common-obj-$(CONFIG_XILINX_SPI) += xilinx_spi.o common-obj-$(CONFIG_XILINX_SPIPS) += xilinx_spips.o +common-obj-$(CONFIG_ASPEED_SOC) += aspeed_smc.o obj-$(CONFIG_OMAP) += omap_spi.o obj-$(CONFIG_IMX) += imx_spi.o diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c new file mode 100644 index 0000000000..a371e302d4 --- /dev/null +++ b/hw/ssi/aspeed_smc.c @@ -0,0 +1,470 @@ +/* + * ASPEED AST2400 SMC Controller (SPI Flash Only) + * + * Copyright (C) 2016 IBM Corp. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "sysemu/sysemu.h" +#include "qemu/log.h" +#include "include/qemu/error-report.h" +#include "exec/address-spaces.h" + +#include "hw/ssi/aspeed_smc.h" + +/* CE Type Setting Register */ +#define R_CONF (0x00 / 4) +#define CONF_LEGACY_DISABLE (1 << 31) +#define CONF_ENABLE_W4 20 +#define CONF_ENABLE_W3 19 +#define CONF_ENABLE_W2 18 +#define CONF_ENABLE_W1 17 +#define CONF_ENABLE_W0 16 +#define CONF_FLASH_TYPE4 9 +#define CONF_FLASH_TYPE3 7 +#define CONF_FLASH_TYPE2 5 +#define CONF_FLASH_TYPE1 3 +#define CONF_FLASH_TYPE0 1 + +/* CE Control Register */ +#define R_CE_CTRL (0x04 / 4) +#define CTRL_EXTENDED4 4 /* 32 bit addressing for SPI */ +#define CTRL_EXTENDED3 3 /* 32 bit addressing for SPI */ +#define CTRL_EXTENDED2 2 /* 32 bit addressing for SPI */ +#define CTRL_EXTENDED1 1 /* 32 bit addressing for SPI */ +#define CTRL_EXTENDED0 0 /* 32 bit addressing for SPI */ + +/* Interrupt Control and Status Register */ +#define R_INTR_CTRL (0x08 / 4) +#define INTR_CTRL_DMA_STATUS (1 << 11) +#define INTR_CTRL_CMD_ABORT_STATUS (1 << 10) +#define INTR_CTRL_WRITE_PROTECT_STATUS (1 << 9) +#define INTR_CTRL_DMA_EN (1 << 3) +#define INTR_CTRL_CMD_ABORT_EN (1 << 2) +#define INTR_CTRL_WRITE_PROTECT_EN (1 << 1) + +/* CEx Control Register */ +#define R_CTRL0 (0x10 / 4) +#define CTRL_CMD_SHIFT 16 +#define CTRL_CMD_MASK 0xff +#define CTRL_CE_STOP_ACTIVE (1 << 2) +#define CTRL_CMD_MODE_MASK 0x3 +#define CTRL_READMODE 0x0 +#define CTRL_FREADMODE 0x1 +#define CTRL_WRITEMODE 0x2 +#define CTRL_USERMODE 0x3 +#define R_CTRL1 (0x14 / 4) +#define R_CTRL2 (0x18 / 4) +#define R_CTRL3 (0x1C / 4) +#define R_CTRL4 (0x20 / 4) + +/* CEx Segment Address Register */ +#define R_SEG_ADDR0 (0x30 / 4) +#define SEG_SIZE_SHIFT 24 /* 8MB units */ +#define SEG_SIZE_MASK 0x7f +#define SEG_START_SHIFT 16 /* address bit [A29-A23] */ +#define SEG_START_MASK 0x7f +#define R_SEG_ADDR1 (0x34 / 4) +#define R_SEG_ADDR2 (0x38 / 4) +#define R_SEG_ADDR3 (0x3C / 4) +#define R_SEG_ADDR4 (0x40 / 4) + +/* Misc Control Register #1 */ +#define R_MISC_CTRL1 (0x50 / 4) + +/* Misc Control Register #2 */ +#define R_MISC_CTRL2 (0x54 / 4) + +/* DMA Control/Status Register */ +#define R_DMA_CTRL (0x80 / 4) +#define DMA_CTRL_DELAY_MASK 0xf +#define DMA_CTRL_DELAY_SHIFT 8 +#define DMA_CTRL_FREQ_MASK 0xf +#define DMA_CTRL_FREQ_SHIFT 4 +#define DMA_CTRL_MODE (1 << 3) +#define DMA_CTRL_CKSUM (1 << 2) +#define DMA_CTRL_DIR (1 << 1) +#define DMA_CTRL_EN (1 << 0) + +/* DMA Flash Side Address */ +#define R_DMA_FLASH_ADDR (0x84 / 4) + +/* DMA DRAM Side Address */ +#define R_DMA_DRAM_ADDR (0x88 / 4) + +/* DMA Length Register */ +#define R_DMA_LEN (0x8C / 4) + +/* Checksum Calculation Result */ +#define R_DMA_CHECKSUM (0x90 / 4) + +/* Misc Control Register #2 */ +#define R_TIMINGS (0x94 / 4) + +/* SPI controller registers and bits */ +#define R_SPI_CONF (0x00 / 4) +#define SPI_CONF_ENABLE_W0 0 +#define R_SPI_CTRL0 (0x4 / 4) +#define R_SPI_MISC_CTRL (0x10 / 4) +#define R_SPI_TIMINGS (0x14 / 4) + +/* + * Default segments mapping addresses and size for each slave per + * controller. These can be changed when board is initialized with the + * Segment Address Registers but they don't seem do be used on the + * field. + */ +static const AspeedSegments aspeed_segments_legacy[] = { + { 0x10000000, 32 * 1024 * 1024 }, +}; + +static const AspeedSegments aspeed_segments_fmc[] = { + { 0x20000000, 64 * 1024 * 1024 }, + { 0x24000000, 32 * 1024 * 1024 }, + { 0x26000000, 32 * 1024 * 1024 }, + { 0x28000000, 32 * 1024 * 1024 }, + { 0x2A000000, 32 * 1024 * 1024 } +}; + +static const AspeedSegments aspeed_segments_spi[] = { + { 0x30000000, 64 * 1024 * 1024 }, +}; + +static const AspeedSMCController controllers[] = { + { "aspeed.smc.smc", R_CONF, R_CE_CTRL, R_CTRL0, R_TIMINGS, + CONF_ENABLE_W0, 5, aspeed_segments_legacy, 0x6000000 }, + { "aspeed.smc.fmc", R_CONF, R_CE_CTRL, R_CTRL0, R_TIMINGS, + CONF_ENABLE_W0, 5, aspeed_segments_fmc, 0x10000000 }, + { "aspeed.smc.spi", R_SPI_CONF, 0xff, R_SPI_CTRL0, R_SPI_TIMINGS, + SPI_CONF_ENABLE_W0, 1, aspeed_segments_spi, 0x10000000 }, +}; + +static uint64_t aspeed_smc_flash_default_read(void *opaque, hwaddr addr, + unsigned size) +{ + qemu_log_mask(LOG_GUEST_ERROR, "%s: To 0x%" HWADDR_PRIx " of size %u" + PRIx64 "\n", __func__, addr, size); + return 0; +} + +static void aspeed_smc_flash_default_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + qemu_log_mask(LOG_GUEST_ERROR, "%s: To 0x%" HWADDR_PRIx " of size %u: 0x%" + PRIx64 "\n", __func__, addr, size, data); +} + +static const MemoryRegionOps aspeed_smc_flash_default_ops = { + .read = aspeed_smc_flash_default_read, + .write = aspeed_smc_flash_default_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static inline int aspeed_smc_flash_mode(const AspeedSMCState *s, int cs) +{ + return s->regs[s->r_ctrl0 + cs] & CTRL_CMD_MODE_MASK; +} + +static inline bool aspeed_smc_is_usermode(const AspeedSMCState *s, int cs) +{ + return aspeed_smc_flash_mode(s, cs) == CTRL_USERMODE; +} + +static inline bool aspeed_smc_is_writable(const AspeedSMCState *s, int cs) +{ + return s->regs[s->r_conf] & (1 << (s->conf_enable_w0 + cs)); +} + +static uint64_t aspeed_smc_flash_read(void *opaque, hwaddr addr, unsigned size) +{ + AspeedSMCFlash *fl = opaque; + const AspeedSMCState *s = fl->controller; + uint64_t ret = 0; + int i; + + if (aspeed_smc_is_usermode(s, fl->id)) { + for (i = 0; i < size; i++) { + ret |= ssi_transfer(s->spi, 0x0) << (8 * i); + } + } else { + qemu_log_mask(LOG_UNIMP, "%s: usermode not implemented\n", + __func__); + ret = -1; + } + + return ret; +} + +static void aspeed_smc_flash_write(void *opaque, hwaddr addr, uint64_t data, + unsigned size) +{ + AspeedSMCFlash *fl = opaque; + const AspeedSMCState *s = fl->controller; + int i; + + if (!aspeed_smc_is_writable(s, fl->id)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: flash is not writable at 0x%" + HWADDR_PRIx "\n", __func__, addr); + return; + } + + if (!aspeed_smc_is_usermode(s, fl->id)) { + qemu_log_mask(LOG_UNIMP, "%s: usermode not implemented\n", + __func__); + return; + } + + for (i = 0; i < size; i++) { + ssi_transfer(s->spi, (data >> (8 * i)) & 0xff); + } +} + +static const MemoryRegionOps aspeed_smc_flash_ops = { + .read = aspeed_smc_flash_read, + .write = aspeed_smc_flash_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static bool aspeed_smc_is_ce_stop_active(const AspeedSMCState *s, int cs) +{ + return s->regs[s->r_ctrl0 + cs] & CTRL_CE_STOP_ACTIVE; +} + +static void aspeed_smc_update_cs(const AspeedSMCState *s) +{ + int i; + + for (i = 0; i < s->num_cs; ++i) { + qemu_set_irq(s->cs_lines[i], aspeed_smc_is_ce_stop_active(s, i)); + } +} + +static void aspeed_smc_reset(DeviceState *d) +{ + AspeedSMCState *s = ASPEED_SMC(d); + int i; + + memset(s->regs, 0, sizeof s->regs); + + /* Unselect all slaves */ + for (i = 0; i < s->num_cs; ++i) { + s->regs[s->r_ctrl0 + i] |= CTRL_CE_STOP_ACTIVE; + } + + aspeed_smc_update_cs(s); +} + +static bool aspeed_smc_is_implemented(AspeedSMCState *s, hwaddr addr) +{ + return (addr == s->r_conf || addr == s->r_timings || addr == s->r_ce_ctrl || + (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + s->num_cs)); +} + +static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size) +{ + AspeedSMCState *s = ASPEED_SMC(opaque); + + addr >>= 2; + + if (addr >= ARRAY_SIZE(s->regs)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds read at 0x%" HWADDR_PRIx "\n", + __func__, addr); + return 0; + } + + if (!aspeed_smc_is_implemented(s, addr)) { + qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n", + __func__, addr); + return 0; + } + + return s->regs[addr]; +} + +static void aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data, + unsigned int size) +{ + AspeedSMCState *s = ASPEED_SMC(opaque); + uint32_t value = data; + + addr >>= 2; + + if (addr >= ARRAY_SIZE(s->regs)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds write at 0x%" HWADDR_PRIx "\n", + __func__, addr); + return; + } + + if (!aspeed_smc_is_implemented(s, addr)) { + qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n", + __func__, addr); + return; + } + + /* + * Not much to do apart from storing the value and set the cs + * lines if the register is a controlling one. + */ + s->regs[addr] = value; + if (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + s->num_cs) { + aspeed_smc_update_cs(s); + } +} + +static const MemoryRegionOps aspeed_smc_ops = { + .read = aspeed_smc_read, + .write = aspeed_smc_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.unaligned = true, +}; + +static void aspeed_smc_realize(DeviceState *dev, Error **errp) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + AspeedSMCState *s = ASPEED_SMC(dev); + AspeedSMCClass *mc = ASPEED_SMC_GET_CLASS(s); + int i; + char name[32]; + hwaddr offset = 0; + + s->ctrl = mc->ctrl; + + /* keep a copy under AspeedSMCState to speed up accesses */ + s->r_conf = s->ctrl->r_conf; + s->r_ce_ctrl = s->ctrl->r_ce_ctrl; + s->r_ctrl0 = s->ctrl->r_ctrl0; + s->r_timings = s->ctrl->r_timings; + s->conf_enable_w0 = s->ctrl->conf_enable_w0; + + /* Enforce some real HW limits */ + if (s->num_cs > s->ctrl->max_slaves) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: num_cs cannot exceed: %d\n", + __func__, s->ctrl->max_slaves); + s->num_cs = s->ctrl->max_slaves; + } + + s->spi = ssi_create_bus(dev, "spi"); + + /* Setup cs_lines for slaves */ + sysbus_init_irq(sbd, &s->irq); + s->cs_lines = g_new0(qemu_irq, s->num_cs); + ssi_auto_connect_slaves(dev, s->cs_lines, s->spi); + + for (i = 0; i < s->num_cs; ++i) { + sysbus_init_irq(sbd, &s->cs_lines[i]); + } + + aspeed_smc_reset(dev); + + memory_region_init_io(&s->mmio, OBJECT(s), &aspeed_smc_ops, s, + s->ctrl->name, ASPEED_SMC_R_MAX * 4); + sysbus_init_mmio(sbd, &s->mmio); + + /* + * Memory region where flash modules are remapped + */ + snprintf(name, sizeof(name), "%s.flash", s->ctrl->name); + + memory_region_init_io(&s->mmio_flash, OBJECT(s), + &aspeed_smc_flash_default_ops, s, name, + s->ctrl->mapping_window_size); + sysbus_init_mmio(sbd, &s->mmio_flash); + + s->flashes = g_new0(AspeedSMCFlash, s->num_cs); + + for (i = 0; i < s->num_cs; ++i) { + AspeedSMCFlash *fl = &s->flashes[i]; + + snprintf(name, sizeof(name), "%s.%d", s->ctrl->name, i); + + fl->id = i; + fl->controller = s; + fl->size = s->ctrl->segments[i].size; + memory_region_init_io(&fl->mmio, OBJECT(s), &aspeed_smc_flash_ops, + fl, name, fl->size); + memory_region_add_subregion(&s->mmio_flash, offset, &fl->mmio); + offset += fl->size; + } +} + +static const VMStateDescription vmstate_aspeed_smc = { + .name = "aspeed.smc", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, AspeedSMCState, ASPEED_SMC_R_MAX), + VMSTATE_END_OF_LIST() + } +}; + +static Property aspeed_smc_properties[] = { + DEFINE_PROP_UINT32("num-cs", AspeedSMCState, num_cs, 1), + DEFINE_PROP_END_OF_LIST(), +}; + +static void aspeed_smc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSMCClass *mc = ASPEED_SMC_CLASS(klass); + + dc->realize = aspeed_smc_realize; + dc->reset = aspeed_smc_reset; + dc->props = aspeed_smc_properties; + dc->vmsd = &vmstate_aspeed_smc; + mc->ctrl = data; +} + +static const TypeInfo aspeed_smc_info = { + .name = TYPE_ASPEED_SMC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AspeedSMCState), + .class_size = sizeof(AspeedSMCClass), + .abstract = true, +}; + +static void aspeed_smc_register_types(void) +{ + int i; + + type_register_static(&aspeed_smc_info); + for (i = 0; i < ARRAY_SIZE(controllers); ++i) { + TypeInfo ti = { + .name = controllers[i].name, + .parent = TYPE_ASPEED_SMC, + .class_init = aspeed_smc_class_init, + .class_data = (void *)&controllers[i], + }; + type_register(&ti); + } +} + +type_init(aspeed_smc_register_types) diff --git a/hw/ssi/ssi.c b/hw/ssi/ssi.c index 9791c0d947..7eaaf565fd 100644 --- a/hw/ssi/ssi.c +++ b/hw/ssi/ssi.c @@ -54,7 +54,7 @@ static uint32_t ssi_transfer_raw_default(SSISlave *dev, uint32_t val) return 0; } -static int ssi_slave_init(DeviceState *dev) +static void ssi_slave_realize(DeviceState *dev, Error **errp) { SSISlave *s = SSI_SLAVE(dev); SSISlaveClass *ssc = SSI_SLAVE_GET_CLASS(s); @@ -64,7 +64,7 @@ static int ssi_slave_init(DeviceState *dev) qdev_init_gpio_in_named(dev, ssi_cs_default, SSI_GPIO_CS, 1); } - return ssc->init(s); + ssc->realize(s, errp); } static void ssi_slave_class_init(ObjectClass *klass, void *data) @@ -72,7 +72,7 @@ static void ssi_slave_class_init(ObjectClass *klass, void *data) SSISlaveClass *ssc = SSI_SLAVE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - dc->init = ssi_slave_init; + dc->realize = ssi_slave_realize; dc->bus_type = TYPE_SSI_BUS; if (!ssc->transfer_raw) { ssc->transfer_raw = ssi_transfer_raw_default; diff --git a/hw/timer/imx_gpt.c b/hw/timer/imx_gpt.c index 3c2f01ab99..82bc73cb86 100644 --- a/hw/timer/imx_gpt.c +++ b/hw/timer/imx_gpt.c @@ -14,7 +14,6 @@ #include "qemu/osdep.h" #include "hw/timer/imx_gpt.h" -#include "hw/misc/imx_ccm.h" #include "qemu/main-loop.h" #include "qemu/log.h" @@ -81,7 +80,18 @@ static const VMStateDescription vmstate_imx_timer_gpt = { } }; -static const IMXClk imx_gpt_clocks[] = { +static const IMXClk imx25_gpt_clocks[] = { + CLK_NONE, /* 000 No clock source */ + CLK_IPG, /* 001 ipg_clk, 532MHz*/ + CLK_IPG_HIGH, /* 010 ipg_clk_highfreq */ + CLK_NONE, /* 011 not defined */ + CLK_32k, /* 100 ipg_clk_32k */ + CLK_32k, /* 101 ipg_clk_32k */ + CLK_32k, /* 110 ipg_clk_32k */ + CLK_32k, /* 111 ipg_clk_32k */ +}; + +static const IMXClk imx31_gpt_clocks[] = { CLK_NONE, /* 000 No clock source */ CLK_IPG, /* 001 ipg_clk, 532MHz*/ CLK_IPG_HIGH, /* 010 ipg_clk_highfreq */ @@ -92,12 +102,23 @@ static const IMXClk imx_gpt_clocks[] = { CLK_NONE, /* 111 not defined */ }; +static const IMXClk imx6_gpt_clocks[] = { + CLK_NONE, /* 000 No clock source */ + CLK_IPG, /* 001 ipg_clk, 532MHz*/ + CLK_IPG_HIGH, /* 010 ipg_clk_highfreq */ + CLK_EXT, /* 011 External clock */ + CLK_32k, /* 100 ipg_clk_32k */ + CLK_HIGH_DIV, /* 101 reference clock / 8 */ + CLK_NONE, /* 110 not defined */ + CLK_HIGH, /* 111 reference clock */ +}; + static void imx_gpt_set_freq(IMXGPTState *s) { uint32_t clksrc = extract32(s->cr, GPT_CR_CLKSRC_SHIFT, 3); s->freq = imx_ccm_get_clock_frequency(s->ccm, - imx_gpt_clocks[clksrc]) / (1 + s->pr); + s->clocks[clksrc]) / (1 + s->pr); DPRINTF("Setting clksrc %d to frequency %d\n", clksrc, s->freq); @@ -453,16 +474,52 @@ static void imx_gpt_class_init(ObjectClass *klass, void *data) dc->desc = "i.MX general timer"; } -static const TypeInfo imx_gpt_info = { - .name = TYPE_IMX_GPT, +static void imx25_gpt_init(Object *obj) +{ + IMXGPTState *s = IMX_GPT(obj); + + s->clocks = imx25_gpt_clocks; +} + +static void imx31_gpt_init(Object *obj) +{ + IMXGPTState *s = IMX_GPT(obj); + + s->clocks = imx31_gpt_clocks; +} + +static void imx6_gpt_init(Object *obj) +{ + IMXGPTState *s = IMX_GPT(obj); + + s->clocks = imx6_gpt_clocks; +} + +static const TypeInfo imx25_gpt_info = { + .name = TYPE_IMX25_GPT, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(IMXGPTState), + .instance_init = imx25_gpt_init, .class_init = imx_gpt_class_init, }; +static const TypeInfo imx31_gpt_info = { + .name = TYPE_IMX31_GPT, + .parent = TYPE_IMX25_GPT, + .instance_init = imx31_gpt_init, +}; + +static const TypeInfo imx6_gpt_info = { + .name = TYPE_IMX6_GPT, + .parent = TYPE_IMX25_GPT, + .instance_init = imx6_gpt_init, +}; + static void imx_gpt_register_types(void) { - type_register_static(&imx_gpt_info); + type_register_static(&imx25_gpt_info); + type_register_static(&imx31_gpt_info); + type_register_static(&imx6_gpt_info); } type_init(imx_gpt_register_types) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 43ba61599a..976bfb0659 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -26,6 +26,7 @@ #include "hw/pci/msi.h" #include "hw/pci/msix.h" #include "trace.h" +#include "qapi/error.h" //#define DEBUG_XHCI //#define DEBUG_DATA @@ -461,6 +462,8 @@ struct XHCIState { uint32_t numslots; uint32_t flags; uint32_t max_pstreams_mask; + OnOffAuto msi; + OnOffAuto msix; /* Operational Registers */ uint32_t usbcmd; @@ -498,9 +501,7 @@ typedef struct XHCIEvRingSeg { } XHCIEvRingSeg; enum xhci_flags { - XHCI_FLAG_USE_MSI = 1, - XHCI_FLAG_USE_MSI_X, - XHCI_FLAG_SS_FIRST, + XHCI_FLAG_SS_FIRST = 1, XHCI_FLAG_FORCE_PCIE_ENDCAP, XHCI_FLAG_ENABLE_STREAMS, }; @@ -2363,6 +2364,8 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid, slot->uport = uport; slot->ctx = octx; + /* Make sure device is in USB_STATE_DEFAULT state */ + usb_device_reset(dev); if (bsr) { slot_ctx[3] = SLOT_DEFAULT << SLOT_STATE_SHIFT; } else { @@ -2370,7 +2373,6 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid, uint8_t buf[1]; slot_ctx[3] = (SLOT_ADDRESSED << SLOT_STATE_SHIFT) | slotid; - usb_device_reset(dev); memset(&p, 0, sizeof(p)); usb_packet_addbuf(&p, buf, sizeof(buf)); usb_packet_setup(&p, USB_TOKEN_OUT, @@ -3581,6 +3583,7 @@ static void usb_xhci_init(XHCIState *xhci) static void usb_xhci_realize(struct PCIDevice *dev, Error **errp) { int i, ret; + Error *err = NULL; XHCIState *xhci = XHCI(dev); @@ -3591,6 +3594,23 @@ static void usb_xhci_realize(struct PCIDevice *dev, Error **errp) usb_xhci_init(xhci); + if (xhci->msi != ON_OFF_AUTO_OFF) { + ret = msi_init(dev, 0x70, xhci->numintrs, true, false, &err); + /* Any error other than -ENOTSUP(board's MSI support is broken) + * is a programming error */ + assert(!ret || ret == -ENOTSUP); + if (ret && xhci->msi == ON_OFF_AUTO_ON) { + /* Can't satisfy user's explicit msi=on request, fail */ + error_append_hint(&err, "You have to use msi=auto (default) or " + "msi=off with this machine type.\n"); + error_propagate(errp, err); + return; + } + assert(!err || xhci->msi == ON_OFF_AUTO_AUTO); + /* With msi=auto, we fall back to MSI off silently */ + error_free(err); + } + if (xhci->numintrs > MAXINTRS) { xhci->numintrs = MAXINTRS; } @@ -3648,10 +3668,8 @@ static void usb_xhci_realize(struct PCIDevice *dev, Error **errp) assert(ret >= 0); } - if (xhci_get_flag(xhci, XHCI_FLAG_USE_MSI)) { - msi_init(dev, 0x70, xhci->numintrs, true, false); - } - if (xhci_get_flag(xhci, XHCI_FLAG_USE_MSI_X)) { + if (xhci->msix != ON_OFF_AUTO_OFF) { + /* TODO check for errors */ msix_init(dev, xhci->numintrs, &xhci->mem, 0, OFF_MSIX_TABLE, &xhci->mem, 0, OFF_MSIX_PBA, @@ -3872,8 +3890,8 @@ static const VMStateDescription vmstate_xhci = { }; static Property xhci_properties[] = { - DEFINE_PROP_BIT("msi", XHCIState, flags, XHCI_FLAG_USE_MSI, true), - DEFINE_PROP_BIT("msix", XHCIState, flags, XHCI_FLAG_USE_MSI_X, true), + DEFINE_PROP_ON_OFF_AUTO("msi", XHCIState, msi, ON_OFF_AUTO_AUTO), + DEFINE_PROP_ON_OFF_AUTO("msix", XHCIState, msix, ON_OFF_AUTO_AUTO), DEFINE_PROP_BIT("superspeed-ports-first", XHCIState, flags, XHCI_FLAG_SS_FIRST, true), DEFINE_PROP_BIT("force-pcie-endcap", XHCIState, flags, diff --git a/hw/usb/xen-usb.c b/hw/usb/xen-usb.c index 0fd34c62c4..7bed0cebe3 100644 --- a/hw/usb/xen-usb.c +++ b/hw/usb/xen-usb.c @@ -253,7 +253,8 @@ static int usbback_init_packet(struct usbback_req *usbback_req) case USBIF_PIPE_TYPE_CTRL: packet->parameter = *(uint64_t *)usbback_req->req.u.ctrl; - TR_REQ(xendev, "ctrl parameter: %lx, buflen: %x\n", packet->parameter, + TR_REQ(xendev, "ctrl parameter: %"PRIx64", buflen: %x\n", + packet->parameter, usbback_req->req.buffer_length); break; diff --git a/hw/vfio/Makefile.objs b/hw/vfio/Makefile.objs index ceddbb8f99..c25e32b029 100644 --- a/hw/vfio/Makefile.objs +++ b/hw/vfio/Makefile.objs @@ -4,4 +4,5 @@ obj-$(CONFIG_PCI) += pci.o pci-quirks.o obj-$(CONFIG_SOFTMMU) += platform.o obj-$(CONFIG_SOFTMMU) += calxeda-xgmac.o obj-$(CONFIG_SOFTMMU) += amd-xgbe.o +obj-$(CONFIG_SOFTMMU) += spapr.o endif diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 7be638e0e3..f3c0522e7e 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -28,6 +28,7 @@ #include "exec/memory.h" #include "hw/hw.h" #include "qemu/error-report.h" +#include "qemu/range.h" #include "sysemu/kvm.h" #ifdef CONFIG_KVM #include "linux/kvm.h" @@ -241,6 +242,44 @@ static int vfio_dma_map(VFIOContainer *container, hwaddr iova, return -errno; } +static void vfio_host_win_add(VFIOContainer *container, + hwaddr min_iova, hwaddr max_iova, + uint64_t iova_pgsizes) +{ + VFIOHostDMAWindow *hostwin; + + QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) { + if (ranges_overlap(hostwin->min_iova, + hostwin->max_iova - hostwin->min_iova + 1, + min_iova, + max_iova - min_iova + 1)) { + hw_error("%s: Overlapped IOMMU are not enabled", __func__); + } + } + + hostwin = g_malloc0(sizeof(*hostwin)); + + hostwin->min_iova = min_iova; + hostwin->max_iova = max_iova; + hostwin->iova_pgsizes = iova_pgsizes; + QLIST_INSERT_HEAD(&container->hostwin_list, hostwin, hostwin_next); +} + +static int vfio_host_win_del(VFIOContainer *container, hwaddr min_iova, + hwaddr max_iova) +{ + VFIOHostDMAWindow *hostwin; + + QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) { + if (hostwin->min_iova == min_iova && hostwin->max_iova == max_iova) { + QLIST_REMOVE(hostwin, hostwin_next); + return 0; + } + } + + return -1; +} + static bool vfio_listener_skipped_section(MemoryRegionSection *section) { return (!memory_region_is_ram(section->mr) && @@ -329,6 +368,8 @@ static void vfio_listener_region_add(MemoryListener *listener, Int128 llend, llsize; void *vaddr; int ret; + VFIOHostDMAWindow *hostwin; + bool hostwin_found; if (vfio_listener_skipped_section(section)) { trace_vfio_listener_region_add_skip( @@ -354,7 +395,40 @@ static void vfio_listener_region_add(MemoryListener *listener, } end = int128_get64(int128_sub(llend, int128_one())); - if ((iova < container->min_iova) || (end > container->max_iova)) { + if (container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU) { + VFIOHostDMAWindow *hostwin; + hwaddr pgsize = 0; + + /* For now intersections are not allowed, we may relax this later */ + QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) { + if (ranges_overlap(hostwin->min_iova, + hostwin->max_iova - hostwin->min_iova + 1, + section->offset_within_address_space, + int128_get64(section->size))) { + ret = -1; + goto fail; + } + } + + ret = vfio_spapr_create_window(container, section, &pgsize); + if (ret) { + goto fail; + } + + vfio_host_win_add(container, section->offset_within_address_space, + section->offset_within_address_space + + int128_get64(section->size) - 1, pgsize); + } + + hostwin_found = false; + QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) { + if (hostwin->min_iova <= iova && end <= hostwin->max_iova) { + hostwin_found = true; + break; + } + } + + if (!hostwin_found) { error_report("vfio: IOMMU container %p can't map guest IOVA region" " 0x%"HWADDR_PRIx"..0x%"HWADDR_PRIx, container, iova, end); @@ -369,10 +443,6 @@ static void vfio_listener_region_add(MemoryListener *listener, trace_vfio_listener_region_add_iommu(iova, end); /* - * FIXME: We should do some checking to see if the - * capabilities of the host VFIO IOMMU are adequate to model - * the guest IOMMU - * * FIXME: For VFIO iommu types which have KVM acceleration to * avoid bouncing all map/unmaps through qemu this way, this * would be the right place to wire that up (tell the KVM @@ -493,6 +563,18 @@ static void vfio_listener_region_del(MemoryListener *listener, "0x%"HWADDR_PRIx") = %d (%m)", container, iova, int128_get64(llsize), ret); } + + if (container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU) { + vfio_spapr_remove_window(container, + section->offset_within_address_space); + if (vfio_host_win_del(container, + section->offset_within_address_space, + section->offset_within_address_space + + int128_get64(section->size) - 1) < 0) { + hw_error("%s: Cannot delete missing window at %"HWADDR_PRIx, + __func__, section->offset_within_address_space); + } + } } static const MemoryListener vfio_memory_listener = { @@ -503,6 +585,9 @@ static const MemoryListener vfio_memory_listener = { static void vfio_listener_release(VFIOContainer *container) { memory_listener_unregister(&container->listener); + if (container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU) { + memory_listener_unregister(&container->prereg_listener); + } } static struct vfio_info_cap_header * @@ -861,8 +946,8 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as) goto free_container_exit; } - ret = ioctl(fd, VFIO_SET_IOMMU, - v2 ? VFIO_TYPE1v2_IOMMU : VFIO_TYPE1_IOMMU); + container->iommu_type = v2 ? VFIO_TYPE1v2_IOMMU : VFIO_TYPE1_IOMMU; + ret = ioctl(fd, VFIO_SET_IOMMU, container->iommu_type); if (ret) { error_report("vfio: failed to set iommu for container: %m"); ret = -errno; @@ -876,19 +961,18 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as) * existing Type1 IOMMUs generally support any IOVA we're * going to actually try in practice. */ - container->min_iova = 0; - container->max_iova = (hwaddr)-1; - - /* Assume just 4K IOVA page size */ - container->iova_pgsizes = 0x1000; info.argsz = sizeof(info); ret = ioctl(fd, VFIO_IOMMU_GET_INFO, &info); /* Ignore errors */ - if ((ret == 0) && (info.flags & VFIO_IOMMU_INFO_PGSIZES)) { - container->iova_pgsizes = info.iova_pgsizes; + if (ret || !(info.flags & VFIO_IOMMU_INFO_PGSIZES)) { + /* Assume 4k IOVA page size */ + info.iova_pgsizes = 4096; } - } else if (ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_SPAPR_TCE_IOMMU)) { + vfio_host_win_add(container, 0, (hwaddr)-1, info.iova_pgsizes); + } else if (ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_SPAPR_TCE_IOMMU) || + ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_SPAPR_TCE_v2_IOMMU)) { struct vfio_iommu_spapr_tce_info info; + bool v2 = !!ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_SPAPR_TCE_v2_IOMMU); ret = ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &fd); if (ret) { @@ -896,7 +980,9 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as) ret = -errno; goto free_container_exit; } - ret = ioctl(fd, VFIO_SET_IOMMU, VFIO_SPAPR_TCE_IOMMU); + container->iommu_type = + v2 ? VFIO_SPAPR_TCE_v2_IOMMU : VFIO_SPAPR_TCE_IOMMU; + ret = ioctl(fd, VFIO_SET_IOMMU, container->iommu_type); if (ret) { error_report("vfio: failed to set iommu for container: %m"); ret = -errno; @@ -908,30 +994,54 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as) * when container fd is closed so we do not call it explicitly * in this file. */ - ret = ioctl(fd, VFIO_IOMMU_ENABLE); - if (ret) { - error_report("vfio: failed to enable container: %m"); - ret = -errno; - goto free_container_exit; + if (!v2) { + ret = ioctl(fd, VFIO_IOMMU_ENABLE); + if (ret) { + error_report("vfio: failed to enable container: %m"); + ret = -errno; + goto free_container_exit; + } + } else { + container->prereg_listener = vfio_prereg_listener; + + memory_listener_register(&container->prereg_listener, + &address_space_memory); + if (container->error) { + memory_listener_unregister(&container->prereg_listener); + error_report("vfio: RAM memory listener initialization failed for container"); + goto free_container_exit; + } } - /* - * This only considers the host IOMMU's 32-bit window. At - * some point we need to add support for the optional 64-bit - * window and dynamic windows - */ info.argsz = sizeof(info); ret = ioctl(fd, VFIO_IOMMU_SPAPR_TCE_GET_INFO, &info); if (ret) { error_report("vfio: VFIO_IOMMU_SPAPR_TCE_GET_INFO failed: %m"); ret = -errno; + if (v2) { + memory_listener_unregister(&container->prereg_listener); + } goto free_container_exit; } - container->min_iova = info.dma32_window_start; - container->max_iova = container->min_iova + info.dma32_window_size - 1; - /* Assume just 4K IOVA pages for now */ - container->iova_pgsizes = 0x1000; + if (v2) { + /* + * There is a default window in just created container. + * To make region_add/del simpler, we better remove this + * window now and let those iommu_listener callbacks + * create/remove them when needed. + */ + ret = vfio_spapr_remove_window(container, info.dma32_window_start); + if (ret) { + goto free_container_exit; + } + } else { + /* The default table uses 4K pages */ + vfio_host_win_add(container, info.dma32_window_start, + info.dma32_window_start + + info.dma32_window_size - 1, + 0x1000); + } } else { error_report("vfio: No available IOMMU models"); ret = -EINVAL; diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index f2c679e47c..44783c50ab 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -31,6 +31,7 @@ #include "sysemu/sysemu.h" #include "pci.h" #include "trace.h" +#include "qapi/error.h" #define MSIX_CAP_LENGTH 12 @@ -1170,6 +1171,7 @@ static int vfio_msi_setup(VFIOPCIDevice *vdev, int pos) uint16_t ctrl; bool msi_64bit, msi_maskbit; int ret, entries; + Error *err = NULL; if (pread(vdev->vbasedev.fd, &ctrl, sizeof(ctrl), vdev->config_offset + pos + PCI_CAP_FLAGS) != sizeof(ctrl)) { @@ -1183,12 +1185,13 @@ static int vfio_msi_setup(VFIOPCIDevice *vdev, int pos) trace_vfio_msi_setup(vdev->vbasedev.name, pos); - ret = msi_init(&vdev->pdev, pos, entries, msi_64bit, msi_maskbit); + ret = msi_init(&vdev->pdev, pos, entries, msi_64bit, msi_maskbit, &err); if (ret < 0) { if (ret == -ENOTSUP) { return 0; } - error_report("vfio: msi_init failed"); + error_prepend(&err, "vfio: msi_init failed: "); + error_report_err(err); return ret; } vdev->msi_cap_size = 0xa + (msi_maskbit ? 0xa : 0) + (msi_64bit ? 0x4 : 0); diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c new file mode 100644 index 0000000000..0af342332c --- /dev/null +++ b/hw/vfio/spapr.c @@ -0,0 +1,210 @@ +/* + * DMA memory preregistration + * + * Authors: + * Alexey Kardashevskiy <aik@ozlabs.ru> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include <sys/ioctl.h> +#include <linux/vfio.h> + +#include "hw/vfio/vfio-common.h" +#include "hw/hw.h" +#include "qemu/error-report.h" +#include "trace.h" + +static bool vfio_prereg_listener_skipped_section(MemoryRegionSection *section) +{ + if (memory_region_is_iommu(section->mr)) { + hw_error("Cannot possibly preregister IOMMU memory"); + } + + return !memory_region_is_ram(section->mr) || + memory_region_is_skip_dump(section->mr); +} + +static void *vfio_prereg_gpa_to_vaddr(MemoryRegionSection *section, hwaddr gpa) +{ + return memory_region_get_ram_ptr(section->mr) + + section->offset_within_region + + (gpa - section->offset_within_address_space); +} + +static void vfio_prereg_listener_region_add(MemoryListener *listener, + MemoryRegionSection *section) +{ + VFIOContainer *container = container_of(listener, VFIOContainer, + prereg_listener); + const hwaddr gpa = section->offset_within_address_space; + hwaddr end; + int ret; + hwaddr page_mask = qemu_real_host_page_mask; + struct vfio_iommu_spapr_register_memory reg = { + .argsz = sizeof(reg), + .flags = 0, + }; + + if (vfio_prereg_listener_skipped_section(section)) { + trace_vfio_prereg_listener_region_add_skip( + section->offset_within_address_space, + section->offset_within_address_space + + int128_get64(int128_sub(section->size, int128_one()))); + return; + } + + if (unlikely((section->offset_within_address_space & ~page_mask) || + (section->offset_within_region & ~page_mask) || + (int128_get64(section->size) & ~page_mask))) { + error_report("%s received unaligned region", __func__); + return; + } + + end = section->offset_within_address_space + int128_get64(section->size); + if (gpa >= end) { + return; + } + + memory_region_ref(section->mr); + + reg.vaddr = (uintptr_t) vfio_prereg_gpa_to_vaddr(section, gpa); + reg.size = end - gpa; + + ret = ioctl(container->fd, VFIO_IOMMU_SPAPR_REGISTER_MEMORY, ®); + trace_vfio_prereg_register(reg.vaddr, reg.size, ret ? -errno : 0); + if (ret) { + /* + * On the initfn path, store the first error in the container so we + * can gracefully fail. Runtime, there's not much we can do other + * than throw a hardware error. + */ + if (!container->initialized) { + if (!container->error) { + container->error = ret; + } + } else { + hw_error("vfio: Memory registering failed, unable to continue"); + } + } +} + +static void vfio_prereg_listener_region_del(MemoryListener *listener, + MemoryRegionSection *section) +{ + VFIOContainer *container = container_of(listener, VFIOContainer, + prereg_listener); + const hwaddr gpa = section->offset_within_address_space; + hwaddr end; + int ret; + hwaddr page_mask = qemu_real_host_page_mask; + struct vfio_iommu_spapr_register_memory reg = { + .argsz = sizeof(reg), + .flags = 0, + }; + + if (vfio_prereg_listener_skipped_section(section)) { + trace_vfio_prereg_listener_region_del_skip( + section->offset_within_address_space, + section->offset_within_address_space + + int128_get64(int128_sub(section->size, int128_one()))); + return; + } + + if (unlikely((section->offset_within_address_space & ~page_mask) || + (section->offset_within_region & ~page_mask) || + (int128_get64(section->size) & ~page_mask))) { + error_report("%s received unaligned region", __func__); + return; + } + + end = section->offset_within_address_space + int128_get64(section->size); + if (gpa >= end) { + return; + } + + reg.vaddr = (uintptr_t) vfio_prereg_gpa_to_vaddr(section, gpa); + reg.size = end - gpa; + + ret = ioctl(container->fd, VFIO_IOMMU_SPAPR_UNREGISTER_MEMORY, ®); + trace_vfio_prereg_unregister(reg.vaddr, reg.size, ret ? -errno : 0); +} + +const MemoryListener vfio_prereg_listener = { + .region_add = vfio_prereg_listener_region_add, + .region_del = vfio_prereg_listener_region_del, +}; + +int vfio_spapr_create_window(VFIOContainer *container, + MemoryRegionSection *section, + hwaddr *pgsize) +{ + int ret; + unsigned pagesize = memory_region_iommu_get_min_page_size(section->mr); + unsigned entries, pages; + struct vfio_iommu_spapr_tce_create create = { .argsz = sizeof(create) }; + + /* + * FIXME: For VFIO iommu types which have KVM acceleration to + * avoid bouncing all map/unmaps through qemu this way, this + * would be the right place to wire that up (tell the KVM + * device emulation the VFIO iommu handles to use). + */ + create.window_size = int128_get64(section->size); + create.page_shift = ctz64(pagesize); + /* + * SPAPR host supports multilevel TCE tables, there is some + * heuristic to decide how many levels we want for our table: + * 0..64 = 1; 65..4096 = 2; 4097..262144 = 3; 262145.. = 4 + */ + entries = create.window_size >> create.page_shift; + pages = MAX((entries * sizeof(uint64_t)) / getpagesize(), 1); + pages = MAX(pow2ceil(pages) - 1, 1); /* Round up */ + create.levels = ctz64(pages) / 6 + 1; + + ret = ioctl(container->fd, VFIO_IOMMU_SPAPR_TCE_CREATE, &create); + if (ret) { + error_report("Failed to create a window, ret = %d (%m)", ret); + return -errno; + } + + if (create.start_addr != section->offset_within_address_space) { + vfio_spapr_remove_window(container, create.start_addr); + + error_report("Host doesn't support DMA window at %"HWADDR_PRIx", must be %"PRIx64, + section->offset_within_address_space, + (uint64_t)create.start_addr); + ioctl(container->fd, VFIO_IOMMU_SPAPR_TCE_REMOVE, &remove); + return -EINVAL; + } + trace_vfio_spapr_create_window(create.page_shift, + create.window_size, + create.start_addr); + *pgsize = pagesize; + + return 0; +} + +int vfio_spapr_remove_window(VFIOContainer *container, + hwaddr offset_within_address_space) +{ + struct vfio_iommu_spapr_tce_remove remove = { + .argsz = sizeof(remove), + .start_addr = offset_within_address_space, + }; + int ret; + + ret = ioctl(container->fd, VFIO_IOMMU_SPAPR_TCE_REMOVE, &remove); + if (ret) { + error_report("Failed to remove window at %"PRIx64, + (uint64_t)remove.start_addr); + return -errno; + } + + trace_vfio_spapr_remove_window(offset_within_address_space); + + return 0; +} diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index a768fb54ec..4bb7690c46 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -115,3 +115,11 @@ vfio_platform_populate_interrupts(int pin, int count, int flags) "- IRQ index %d vfio_intp_interrupt_set_pending(int index) "irq %d is set PENDING" vfio_platform_start_level_irqfd_injection(int index, int fd, int resamplefd) "IRQ index=%d, fd = %d, resamplefd = %d" vfio_platform_start_edge_irqfd_injection(int index, int fd) "IRQ index=%d, fd = %d" + +# hw/vfio/spapr.c +vfio_prereg_listener_region_add_skip(uint64_t start, uint64_t end) "%"PRIx64" - %"PRIx64 +vfio_prereg_listener_region_del_skip(uint64_t start, uint64_t end) "%"PRIx64" - %"PRIx64 +vfio_prereg_register(uint64_t va, uint64_t size, int ret) "va=%"PRIx64" size=%"PRIx64" ret=%d" +vfio_prereg_unregister(uint64_t va, uint64_t size, int ret) "va=%"PRIx64" size=%"PRIx64" ret=%d" +vfio_spapr_create_window(int ps, uint64_t ws, uint64_t off) "pageshift=0x%x winsize=0x%"PRIx64" offset=0x%"PRIx64 +vfio_spapr_remove_window(uint64_t off) "offset=%"PRIx64 diff --git a/hw/virtio/vhost-backend.c b/hw/virtio/vhost-backend.c index b35890289f..d62372e597 100644 --- a/hw/virtio/vhost-backend.c +++ b/hw/virtio/vhost-backend.c @@ -138,6 +138,12 @@ static int vhost_kernel_set_vring_call(struct vhost_dev *dev, return vhost_kernel_call(dev, VHOST_SET_VRING_CALL, file); } +static int vhost_kernel_set_vring_busyloop_timeout(struct vhost_dev *dev, + struct vhost_vring_state *s) +{ + return vhost_kernel_call(dev, VHOST_SET_VRING_BUSYLOOP_TIMEOUT, s); +} + static int vhost_kernel_set_features(struct vhost_dev *dev, uint64_t features) { @@ -185,6 +191,8 @@ static const VhostOps kernel_ops = { .vhost_get_vring_base = vhost_kernel_get_vring_base, .vhost_set_vring_kick = vhost_kernel_set_vring_kick, .vhost_set_vring_call = vhost_kernel_set_vring_call, + .vhost_set_vring_busyloop_timeout = + vhost_kernel_set_vring_busyloop_timeout, .vhost_set_features = vhost_kernel_set_features, .vhost_get_features = vhost_kernel_get_features, .vhost_set_owner = vhost_kernel_set_owner, diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index a01394d5ac..ec3abda9d5 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -960,6 +960,28 @@ static void vhost_eventfd_del(MemoryListener *listener, { } +static int vhost_virtqueue_set_busyloop_timeout(struct vhost_dev *dev, + int n, uint32_t timeout) +{ + int vhost_vq_index = dev->vhost_ops->vhost_get_vq_index(dev, n); + struct vhost_vring_state state = { + .index = vhost_vq_index, + .num = timeout, + }; + int r; + + if (!dev->vhost_ops->vhost_set_vring_busyloop_timeout) { + return -EINVAL; + } + + r = dev->vhost_ops->vhost_set_vring_busyloop_timeout(dev, &state); + if (r) { + return r; + } + + return 0; +} + static int vhost_virtqueue_init(struct vhost_dev *dev, struct vhost_virtqueue *vq, int n) { @@ -990,7 +1012,7 @@ static void vhost_virtqueue_cleanup(struct vhost_virtqueue *vq) } int vhost_dev_init(struct vhost_dev *hdev, void *opaque, - VhostBackendType backend_type) + VhostBackendType backend_type, uint32_t busyloop_timeout) { uint64_t features; int i, r; @@ -1031,6 +1053,17 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, goto fail_vq; } } + + if (busyloop_timeout) { + for (i = 0; i < hdev->nvqs; ++i) { + r = vhost_virtqueue_set_busyloop_timeout(hdev, hdev->vq_index + i, + busyloop_timeout); + if (r < 0) { + goto fail_busyloop; + } + } + } + hdev->features = features; hdev->memory_listener = (MemoryListener) { @@ -1073,6 +1106,11 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, hdev->memory_changed = false; memory_listener_register(&hdev->memory_listener, &address_space_memory); return 0; +fail_busyloop: + while (--i >= 0) { + vhost_virtqueue_set_busyloop_timeout(hdev, hdev->vq_index + i, 0); + } + i = hdev->nvqs; fail_vq: while (--i >= 0) { vhost_virtqueue_cleanup(hdev->vqs + i); diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index 557d3f9e0c..1a22e6d993 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -139,13 +139,13 @@ static void balloon_stats_get_all(Object *obj, Visitor *v, const char *name, } visit_check_struct(v, &err); out_nested: - visit_end_struct(v); + visit_end_struct(v, NULL); if (!err) { visit_check_struct(v, &err); } out_end: - visit_end_struct(v); + visit_end_struct(v, NULL); out: error_propagate(errp, err); } diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c index 131376027b..a85b7c8abe 100644 --- a/hw/virtio/virtio-bus.c +++ b/hw/virtio/virtio-bus.c @@ -176,8 +176,8 @@ static int set_host_notifier_internal(DeviceState *proxy, VirtioBusState *bus, return r; } } else { - virtio_queue_set_host_notifier_fd_handler(vq, false, false); k->ioeventfd_assign(proxy, notifier, n, assign); + virtio_queue_set_host_notifier_fd_handler(vq, false, false); event_notifier_cleanup(notifier); } return r; @@ -251,31 +251,25 @@ int virtio_bus_set_host_notifier(VirtioBusState *bus, int n, bool assign) { VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(bus); DeviceState *proxy = DEVICE(BUS(bus)->parent); - VirtIODevice *vdev = virtio_bus_get_device(bus); - VirtQueue *vq = virtio_get_queue(vdev, n); if (!k->ioeventfd_started) { return -ENOSYS; } + k->ioeventfd_set_disabled(proxy, assign); if (assign) { /* * Stop using the generic ioeventfd, we are doing eventfd handling * ourselves below + * + * FIXME: We should just switch the handler and not deassign the + * ioeventfd. + * Otherwise, there's a window where we don't have an + * ioeventfd and we may end up with a notification where + * we don't expect one. */ - k->ioeventfd_set_disabled(proxy, true); - } - /* - * Just switch the handler, don't deassign the ioeventfd. - * Otherwise, there's a window where we don't have an - * ioeventfd and we may end up with a notification where - * we don't expect one. - */ - virtio_queue_set_host_notifier_fd_handler(vq, assign, !assign); - if (!assign) { - /* Use generic ioeventfd handler again. */ - k->ioeventfd_set_disabled(proxy, false); + virtio_bus_stop_ioeventfd(bus); } - return 0; + return set_host_notifier_internal(proxy, bus, n, assign, false); } static char *virtio_bus_get_dev_path(DeviceState *dev) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 7ed06eafa6..18153d5a39 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -1499,6 +1499,16 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) } qemu_get_be32s(f, &features); + /* + * Temporarily set guest_features low bits - needed by + * virtio net load code testing for VIRTIO_NET_F_CTRL_GUEST_OFFLOADS + * VIRTIO_NET_F_GUEST_ANNOUNCE and VIRTIO_NET_F_CTRL_VQ. + * + * Note: devices should always test host features in future - don't create + * new dependencies like this. + */ + vdev->guest_features = features; + config_len = qemu_get_be32(f); /* diff --git a/include/block/block.h b/include/block/block.h index 733a8ec2ec..616d8b9f12 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -226,33 +226,31 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, Error **errp); void bdrv_reopen_commit(BDRVReopenState *reopen_state); void bdrv_reopen_abort(BDRVReopenState *reopen_state); -int bdrv_read(BlockDriverState *bs, int64_t sector_num, +int bdrv_read(BdrvChild *child, int64_t sector_num, uint8_t *buf, int nb_sectors); -int bdrv_write(BlockDriverState *bs, int64_t sector_num, +int bdrv_write(BdrvChild *child, int64_t sector_num, const uint8_t *buf, int nb_sectors); -int bdrv_pwrite_zeroes(BlockDriverState *bs, int64_t offset, +int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset, int count, BdrvRequestFlags flags); -int bdrv_make_zero(BlockDriverState *bs, BdrvRequestFlags flags); -int bdrv_pread(BlockDriverState *bs, int64_t offset, - void *buf, int count); -int bdrv_preadv(BlockDriverState *bs, int64_t offset, QEMUIOVector *qiov); -int bdrv_pwrite(BlockDriverState *bs, int64_t offset, - const void *buf, int count); -int bdrv_pwritev(BlockDriverState *bs, int64_t offset, QEMUIOVector *qiov); -int bdrv_pwrite_sync(BlockDriverState *bs, int64_t offset, - const void *buf, int count); -int coroutine_fn bdrv_co_readv(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov); -int coroutine_fn bdrv_co_writev(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov); +int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags); +int bdrv_pread(BdrvChild *child, int64_t offset, void *buf, int bytes); +int bdrv_preadv(BdrvChild *child, int64_t offset, QEMUIOVector *qiov); +int bdrv_pwrite(BdrvChild *child, int64_t offset, const void *buf, int bytes); +int bdrv_pwritev(BdrvChild *child, int64_t offset, QEMUIOVector *qiov); +int bdrv_pwrite_sync(BdrvChild *child, int64_t offset, + const void *buf, int count); +int coroutine_fn bdrv_co_readv(BdrvChild *child, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov); +int coroutine_fn bdrv_co_writev(BdrvChild *child, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov); /* * Efficiently zero a region of the disk image. Note that this is a regular * I/O request like read or write and should have a reasonable size. This * function is not suitable for zeroing the entire image in a single request * because it may allocate memory for the entire region. */ -int coroutine_fn bdrv_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, - int count, BdrvRequestFlags flags); +int coroutine_fn bdrv_co_pwrite_zeroes(BdrvChild *child, int64_t offset, + int count, BdrvRequestFlags flags); BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs, const char *backing_file); int bdrv_get_backing_file_depth(BlockDriverState *bs); @@ -310,10 +308,10 @@ BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs, const char *node_name, Error **errp); /* async block I/O */ -BlockAIOCB *bdrv_aio_readv(BlockDriverState *bs, int64_t sector_num, +BlockAIOCB *bdrv_aio_readv(BdrvChild *child, int64_t sector_num, QEMUIOVector *iov, int nb_sectors, BlockCompletionFunc *cb, void *opaque); -BlockAIOCB *bdrv_aio_writev(BlockDriverState *bs, int64_t sector_num, +BlockAIOCB *bdrv_aio_writev(BdrvChild *child, int64_t sector_num, QEMUIOVector *iov, int nb_sectors, BlockCompletionFunc *cb, void *opaque); BlockAIOCB *bdrv_aio_flush(BlockDriverState *bs, @@ -362,8 +360,8 @@ int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base, int64_t sector_num, int nb_sectors, int *pnum); -int bdrv_is_read_only(BlockDriverState *bs); -int bdrv_is_sg(BlockDriverState *bs); +bool bdrv_is_read_only(BlockDriverState *bs); +bool bdrv_is_sg(BlockDriverState *bs); bool bdrv_is_inserted(BlockDriverState *bs); int bdrv_media_changed(BlockDriverState *bs); void bdrv_lock_medium(BlockDriverState *bs, bool locked); @@ -390,8 +388,8 @@ BlockDriverState *bdrv_first(BdrvNextIterator *it); BlockDriverState *bdrv_next(BdrvNextIterator *it); BlockDriverState *bdrv_next_monitor_owned(BlockDriverState *bs); -int bdrv_is_encrypted(BlockDriverState *bs); -int bdrv_key_required(BlockDriverState *bs); +bool bdrv_is_encrypted(BlockDriverState *bs); +bool bdrv_key_required(BlockDriverState *bs); int bdrv_set_key(BlockDriverState *bs, const char *key); void bdrv_add_key(BlockDriverState *bs, const char *key, Error **errp); int bdrv_query_missing_keys(void); diff --git a/include/block/block_int.h b/include/block/block_int.h index 205715600b..47b9aac71b 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -324,30 +324,48 @@ struct BlockDriver { }; typedef struct BlockLimits { - /* maximum number of sectors that can be discarded at once */ - int max_discard; - - /* optimal alignment for discard requests in sectors */ - int64_t discard_alignment; + /* Alignment requirement, in bytes, for offset/length of I/O + * requests. Must be a power of 2 less than INT_MAX; defaults to + * 1 for drivers with modern byte interfaces, and to 512 + * otherwise. */ + uint32_t request_alignment; + + /* maximum number of bytes that can be discarded at once (since it + * is signed, it must be < 2G, if set), should be multiple of + * pdiscard_alignment, but need not be power of 2. May be 0 if no + * inherent 32-bit limit */ + int32_t max_pdiscard; + + /* optimal alignment for discard requests in bytes, must be power + * of 2, less than max_pdiscard if that is set, and multiple of + * bl.request_alignment. May be 0 if bl.request_alignment is good + * enough */ + uint32_t pdiscard_alignment; /* maximum number of bytes that can zeroized at once (since it is - * signed, it must be < 2G, if set) */ + * signed, it must be < 2G, if set), should be multiple of + * pwrite_zeroes_alignment. May be 0 if no inherent 32-bit limit */ int32_t max_pwrite_zeroes; /* optimal alignment for write zeroes requests in bytes, must be - * power of 2, and less than max_pwrite_zeroes if that is set */ + * power of 2, less than max_pwrite_zeroes if that is set, and + * multiple of bl.request_alignment. May be 0 if + * bl.request_alignment is good enough */ uint32_t pwrite_zeroes_alignment; - /* optimal transfer length in sectors */ - int opt_transfer_length; + /* optimal transfer length in bytes (must be power of 2, and + * multiple of bl.request_alignment), or 0 if no preferred size */ + uint32_t opt_transfer; - /* maximal transfer length in sectors */ - int max_transfer_length; + /* maximal transfer length in bytes (need not be power of 2, but + * should be multiple of opt_transfer), or 0 for no 32-bit limit. + * For now, anything larger than INT_MAX is clamped down. */ + uint32_t max_transfer; - /* memory alignment so that no bounce buffer is needed */ + /* memory alignment, in bytes so that no bounce buffer is needed */ size_t min_mem_alignment; - /* memory alignment for bounce buffer */ + /* memory alignment, in bytes, for bounce buffer */ size_t opt_mem_alignment; /* maximum number of iovec elements */ @@ -411,14 +429,15 @@ struct BdrvChild { struct BlockDriverState { int64_t total_sectors; /* if we are reading a disk image, give its size in sectors */ - int read_only; /* if true, the media is read only */ int open_flags; /* flags used to open the file, re-used for re-open */ - int encrypted; /* if true, the media is encrypted */ - int valid_key; /* if true, a valid encryption key has been set */ - int sg; /* if true, the device is a /dev/sg* */ - int copy_on_read; /* if true, copy read backing sectors into image + bool read_only; /* if true, the media is read only */ + bool encrypted; /* if true, the media is encrypted */ + bool valid_key; /* if true, a valid encryption key has been set */ + bool sg; /* if true, the device is a /dev/sg* */ + bool probed; /* if true, format was probed rather than specified */ + + int copy_on_read; /* if nonzero, copy read backing sectors into image. note this is a reference count */ - bool probed; BlockDriver *drv; /* NULL means no media */ void *opaque; @@ -453,8 +472,6 @@ struct BlockDriverState { /* I/O Limits */ BlockLimits bl; - /* Alignment requirement for offset/length of I/O requests */ - unsigned int request_alignment; /* Flags honored during pwrite (so far: BDRV_REQ_FUA) */ unsigned int supported_write_flags; /* Flags honored during pwrite_zeroes (so far: BDRV_REQ_FUA, @@ -546,10 +563,10 @@ extern BlockDriver bdrv_qcow2; */ void bdrv_setup_io_funcs(BlockDriver *bdrv); -int coroutine_fn bdrv_co_preadv(BlockDriverState *bs, +int coroutine_fn bdrv_co_preadv(BdrvChild *child, int64_t offset, unsigned int bytes, QEMUIOVector *qiov, BdrvRequestFlags flags); -int coroutine_fn bdrv_co_pwritev(BlockDriverState *bs, +int coroutine_fn bdrv_co_pwritev(BdrvChild *child, int64_t offset, unsigned int bytes, QEMUIOVector *qiov, BdrvRequestFlags flags); diff --git a/include/block/nbd.h b/include/block/nbd.h index df1f804338..eeda3ebdf7 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -77,6 +77,8 @@ enum { /* Maximum size of a single READ/WRITE data buffer */ #define NBD_MAX_BUFFER_SIZE (32 * 1024 * 1024) +#define NBD_MAX_SECTORS (NBD_MAX_BUFFER_SIZE / BDRV_SECTOR_SIZE) + /* Maximum size of an export name. The NBD spec requires 256 and * suggests that servers support up to 4096, but we stick to only the * required size so that we can stack-allocate the names, and because diff --git a/include/crypto/tlscreds.h b/include/crypto/tlscreds.h index 8e2babd533..59e91875c1 100644 --- a/include/crypto/tlscreds.h +++ b/include/crypto/tlscreds.h @@ -54,6 +54,7 @@ struct QCryptoTLSCreds { gnutls_dh_params_t dh_params; #endif bool verifyPeer; + char *priority; }; diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 6a6796d0c9..b6a705982f 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -288,14 +288,22 @@ CPUArchState *cpu_copy(CPUArchState *env); #if !defined(CONFIG_USER_ONLY) /* Flags stored in the low bits of the TLB virtual address. These are - defined so that fast path ram access is all zeros. */ + * defined so that fast path ram access is all zeros. + * The flags all must be between TARGET_PAGE_BITS and + * maximum address alignment bit. + */ /* Zero if TLB entry is valid. */ -#define TLB_INVALID_MASK (1 << 3) +#define TLB_INVALID_MASK (1 << (TARGET_PAGE_BITS - 1)) /* Set if TLB entry references a clean RAM page. The iotlb entry will contain the page physical address. */ -#define TLB_NOTDIRTY (1 << 4) +#define TLB_NOTDIRTY (1 << (TARGET_PAGE_BITS - 2)) /* Set if TLB entry is an IO callback. */ -#define TLB_MMIO (1 << 5) +#define TLB_MMIO (1 << (TARGET_PAGE_BITS - 3)) + +/* Use this mask to check interception with an alignment mask + * in a TCG backend. + */ +#define TLB_FLAGS_MASK (TLB_INVALID_MASK | TLB_NOTDIRTY | TLB_MMIO) void dump_exec_info(FILE *f, fprintf_function cpu_fprintf); void dump_opcount_info(FILE *f, fprintf_function cpu_fprintf); diff --git a/include/exec/memory.h b/include/exec/memory.h index 23c7399131..3e4d4164cd 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -445,15 +445,31 @@ void memory_region_init_alias(MemoryRegion *mr, uint64_t size); /** + * memory_region_init_rom: Initialize a ROM memory region. + * + * This has the same effect as calling memory_region_init_ram() + * and then marking the resulting region read-only with + * memory_region_set_readonly(). + * + * @mr: the #MemoryRegion to be initialized. + * @owner: the object that tracks the region's reference count + * @name: the name of the region. + * @size: size of the region. + * @errp: pointer to Error*, to store an error if it happens. + */ +void memory_region_init_rom(MemoryRegion *mr, + struct Object *owner, + const char *name, + uint64_t size, + Error **errp); + +/** * memory_region_init_rom_device: Initialize a ROM memory region. Writes are * handled via callbacks. * - * If NULL callbacks pointer is given, then I/O space is not supposed to be - * handled by QEMU itself. Any access via the memory API will cause an abort(). - * * @mr: the #MemoryRegion to be initialized. * @owner: the object that tracks the region's reference count - * @ops: callbacks for write access handling. + * @ops: callbacks for write access handling (must not be NULL). * @name: the name of the region. * @size: size of the region. * @errp: pointer to Error*, to store an error if it happens. diff --git a/include/hw/arm/ast2400.h b/include/hw/arm/ast2400.h index f1a64fd389..7833bc716c 100644 --- a/include/hw/arm/ast2400.h +++ b/include/hw/arm/ast2400.h @@ -17,6 +17,7 @@ #include "hw/misc/aspeed_scu.h" #include "hw/timer/aspeed_timer.h" #include "hw/i2c/aspeed_i2c.h" +#include "hw/ssi/aspeed_smc.h" typedef struct AST2400State { /*< private >*/ @@ -29,6 +30,8 @@ typedef struct AST2400State { AspeedTimerCtrlState timerctrl; AspeedI2CState i2c; AspeedSCUState scu; + AspeedSMCState smc; + AspeedSMCState spi; } AST2400State; #define TYPE_AST2400 "ast2400" diff --git a/include/hw/boards.h b/include/hw/boards.h index 3ed6155ee4..3e69eca038 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -40,6 +40,7 @@ int machine_kvm_shadow_mem(MachineState *machine); int machine_phandle_start(MachineState *machine); bool machine_dump_guest_core(MachineState *machine); bool machine_mem_merge(MachineState *machine); +void machine_register_compat_props(MachineState *machine); /** * CPUArchId: diff --git a/include/hw/display/xlnx_dp.h b/include/hw/display/xlnx_dp.h index d3a03f118c..ee046a5fac 100644 --- a/include/hw/display/xlnx_dp.h +++ b/include/hw/display/xlnx_dp.h @@ -24,7 +24,7 @@ #include "hw/sysbus.h" #include "ui/console.h" -#include "hw/misc/aux.h" +#include "hw/misc/auxbus.h" #include "hw/i2c/i2c.h" #include "hw/display/dpcd.h" #include "hw/i2c/i2c-ddc.h" diff --git a/include/hw/dma/xlnx-zynq-devcfg.h b/include/hw/dma/xlnx-zynq-devcfg.h new file mode 100644 index 0000000000..d40e5c8df6 --- /dev/null +++ b/include/hw/dma/xlnx-zynq-devcfg.h @@ -0,0 +1,62 @@ +/* + * QEMU model of the Xilinx Devcfg Interface + * + * (C) 2011 PetaLogix Pty Ltd + * (C) 2014 Xilinx Inc. + * Written by Peter Crosthwaite <peter.crosthwaite@xilinx.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef XLNX_ZYNQ_DEVCFG_H + +#include "hw/register.h" +#include "hw/sysbus.h" + +#define TYPE_XLNX_ZYNQ_DEVCFG "xlnx.ps7-dev-cfg" + +#define XLNX_ZYNQ_DEVCFG(obj) \ + OBJECT_CHECK(XlnxZynqDevcfg, (obj), TYPE_XLNX_ZYNQ_DEVCFG) + +#define XLNX_ZYNQ_DEVCFG_R_MAX 0x118 + +#define XLNX_ZYNQ_DEVCFG_DMA_CMD_FIFO_LEN 10 + +typedef struct XlnxZynqDevcfgDMACmd { + uint32_t src_addr; + uint32_t dest_addr; + uint32_t src_len; + uint32_t dest_len; +} XlnxZynqDevcfgDMACmd; + +typedef struct XlnxZynqDevcfg { + SysBusDevice parent_obj; + + MemoryRegion iomem; + qemu_irq irq; + + XlnxZynqDevcfgDMACmd dma_cmd_fifo[XLNX_ZYNQ_DEVCFG_DMA_CMD_FIFO_LEN]; + uint8_t dma_cmd_fifo_num; + + uint32_t regs[XLNX_ZYNQ_DEVCFG_R_MAX]; + RegisterInfo regs_info[XLNX_ZYNQ_DEVCFG_R_MAX]; +} XlnxZynqDevcfg; + +#define XLNX_ZYNQ_DEVCFG_H +#endif diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 7e43b20b4b..21235322fd 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -150,11 +150,6 @@ struct PCMachineClass { /* PC-style peripherals (also used by other machines). */ -typedef struct PcPciInfo { - Range w32; - Range w64; -} PcPciInfo; - #define ACPI_PM_PROP_S3_DISABLED "disable_s3" #define ACPI_PM_PROP_S4_DISABLED "disable_s4" #define ACPI_PM_PROP_S4_VAL "s4_val" @@ -374,6 +369,10 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *); .driver = TYPE_X86_CPU,\ .property = "cpuid-0xb",\ .value = "off",\ + },{\ + .driver = "vmxnet3",\ + .property = "romfile",\ + .value = "",\ }, #define PC_COMPAT_2_5 \ diff --git a/include/hw/misc/aux.h b/include/hw/misc/auxbus.h index 759c3bf20c..af39db7d79 100644 --- a/include/hw/misc/aux.h +++ b/include/hw/misc/auxbus.h @@ -1,5 +1,5 @@ /* - * aux.h + * auxbus.h * * Copyright (C)2014 : GreenSocs Ltd * http://www.greensocs.com/ , email: info@greensocs.com diff --git a/include/hw/misc/imx_ccm.h b/include/hw/misc/imx_ccm.h index 48a7afad5e..33cbc09952 100644 --- a/include/hw/misc/imx_ccm.h +++ b/include/hw/misc/imx_ccm.h @@ -46,7 +46,10 @@ typedef enum { CLK_NONE, CLK_IPG, CLK_IPG_HIGH, - CLK_32k + CLK_32k, + CLK_EXT, + CLK_HIGH_DIV, + CLK_HIGH, } IMXClk; typedef struct IMXCCMClass { diff --git a/include/hw/pci-host/q35.h b/include/hw/pci-host/q35.h index 1075f3ea50..0d64032d87 100644 --- a/include/hw/pci-host/q35.h +++ b/include/hw/pci-host/q35.h @@ -55,12 +55,11 @@ typedef struct MCHPCIState { MemoryRegion smram_region, open_high_smram; MemoryRegion smram, low_smram, high_smram; MemoryRegion tseg_blackhole, tseg_window; - PcPciInfo pci_info; + Range pci_hole; uint64_t below_4g_mem_size; uint64_t above_4g_mem_size; uint64_t pci_hole64_size; uint32_t short_root_bus; - IntelIOMMUState *iommu; } MCHPCIState; typedef struct Q35PCIHost { diff --git a/include/hw/pci-host/spapr.h b/include/hw/pci-host/spapr.h index 288b89c04a..193631d2dc 100644 --- a/include/hw/pci-host/spapr.h +++ b/include/hw/pci-host/spapr.h @@ -32,6 +32,8 @@ #define SPAPR_PCI_HOST_BRIDGE(obj) \ OBJECT_CHECK(sPAPRPHBState, (obj), TYPE_SPAPR_PCI_HOST_BRIDGE) +#define SPAPR_PCI_DMA_MAX_WINDOWS 2 + typedef struct sPAPRPHBState sPAPRPHBState; typedef struct spapr_pci_msi { @@ -56,7 +58,7 @@ struct sPAPRPHBState { hwaddr mem_win_addr, mem_win_size, io_win_addr, io_win_size; MemoryRegion memwindow, iowindow, msiwindow; - uint32_t dma_liobn; + uint32_t dma_liobn[SPAPR_PCI_DMA_MAX_WINDOWS]; hwaddr dma_win_addr, dma_win_size; AddressSpace iommu_as; MemoryRegion iommu_root; @@ -71,6 +73,10 @@ struct sPAPRPHBState { spapr_pci_msi_mig *msi_devs; QLIST_ENTRY(sPAPRPHBState) list; + + bool ddw_enabled; + uint64_t page_size_mask; + uint64_t dma64_win_addr; }; #define SPAPR_PCI_MAX_INDEX 255 diff --git a/include/hw/pci/msi.h b/include/hw/pci/msi.h index 8124908abd..4837bcf490 100644 --- a/include/hw/pci/msi.h +++ b/include/hw/pci/msi.h @@ -35,7 +35,8 @@ void msi_set_message(PCIDevice *dev, MSIMessage msg); MSIMessage msi_get_message(PCIDevice *dev, unsigned int vector); bool msi_enabled(const PCIDevice *dev); int msi_init(struct PCIDevice *dev, uint8_t offset, - unsigned int nr_vectors, bool msi64bit, bool msi_per_vector_mask); + unsigned int nr_vectors, bool msi64bit, + bool msi_per_vector_mask, Error **errp); void msi_uninit(struct PCIDevice *dev); void msi_reset(PCIDevice *dev); void msi_notify(PCIDevice *dev, unsigned int vector); diff --git a/include/hw/pci/pci_bus.h b/include/hw/pci/pci_bus.h index 403fec6e58..5484a9b5c5 100644 --- a/include/hw/pci/pci_bus.h +++ b/include/hw/pci/pci_bus.h @@ -39,6 +39,8 @@ struct PCIBus { Keep a count of the number of devices with raised IRQs. */ int nirq; int *irq_count; + + Notifier machine_done; }; typedef struct PCIBridgeWindows PCIBridgeWindows; diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 49325555d3..2e2dd14c30 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -416,6 +416,16 @@ int spapr_allocate_irq_block(int num, bool lsi, bool msi); #define RTAS_OUT_NOT_AUTHORIZED -9002 #define RTAS_OUT_SYSPARM_PARAM_ERROR -9999 +/* DDW pagesize mask values from ibm,query-pe-dma-window */ +#define RTAS_DDW_PGSIZE_4K 0x01 +#define RTAS_DDW_PGSIZE_64K 0x02 +#define RTAS_DDW_PGSIZE_16M 0x04 +#define RTAS_DDW_PGSIZE_32M 0x08 +#define RTAS_DDW_PGSIZE_64M 0x10 +#define RTAS_DDW_PGSIZE_128M 0x20 +#define RTAS_DDW_PGSIZE_256M 0x40 +#define RTAS_DDW_PGSIZE_16G 0x80 + /* RTAS tokens */ #define RTAS_TOKEN_BASE 0x2000 @@ -457,8 +467,12 @@ int spapr_allocate_irq_block(int num, bool lsi, bool msi); #define RTAS_IBM_SET_SLOT_RESET (RTAS_TOKEN_BASE + 0x23) #define RTAS_IBM_CONFIGURE_PE (RTAS_TOKEN_BASE + 0x24) #define RTAS_IBM_SLOT_ERROR_DETAIL (RTAS_TOKEN_BASE + 0x25) +#define RTAS_IBM_QUERY_PE_DMA_WINDOW (RTAS_TOKEN_BASE + 0x26) +#define RTAS_IBM_CREATE_PE_DMA_WINDOW (RTAS_TOKEN_BASE + 0x27) +#define RTAS_IBM_REMOVE_PE_DMA_WINDOW (RTAS_TOKEN_BASE + 0x28) +#define RTAS_IBM_RESET_PE_DMA_WINDOW (RTAS_TOKEN_BASE + 0x29) -#define RTAS_TOKEN_MAX (RTAS_TOKEN_BASE + 0x26) +#define RTAS_TOKEN_MAX (RTAS_TOKEN_BASE + 0x2A) /* RTAS ibm,get-system-parameter token values */ #define RTAS_SYSPARM_SPLPAR_CHARACTERISTICS 20 diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 24aa0a7949..1d1f8612a9 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -259,6 +259,9 @@ struct PropertyInfo { * @user_provided: Set to true if property comes from user-provided config * (command-line or config file). * @used: Set to true if property was used when initializing a device. + * @errp: Error destination, used like first argument of error_setg() + * in case property setting fails later. If @errp is NULL, we + * print warnings instead of ignoring errors silently. */ typedef struct GlobalProperty { const char *driver; @@ -266,6 +269,7 @@ typedef struct GlobalProperty { const char *value; bool user_provided; bool used; + Error **errp; } GlobalProperty; /*** Board API. This should go away once we have a machine config file. ***/ diff --git a/include/hw/register.h b/include/hw/register.h new file mode 100644 index 0000000000..8c12233b75 --- /dev/null +++ b/include/hw/register.h @@ -0,0 +1,255 @@ +/* + * Register Definition API + * + * Copyright (c) 2016 Xilinx Inc. + * Copyright (c) 2013 Peter Crosthwaite <peter.crosthwaite@xilinx.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#ifndef REGISTER_H +#define REGISTER_H + +#include "hw/qdev-core.h" +#include "exec/memory.h" + +typedef struct RegisterInfo RegisterInfo; +typedef struct RegisterAccessInfo RegisterAccessInfo; +typedef struct RegisterInfoArray RegisterInfoArray; + +/** + * Access description for a register that is part of guest accessible device + * state. + * + * @name: String name of the register + * @ro: whether or not the bit is read-only + * @w1c: bits with the common write 1 to clear semantic. + * @reset: reset value. + * @cor: Bits that are clear on read + * @rsvd: Bits that are reserved and should not be changed + * + * @pre_write: Pre write callback. Passed the value that's to be written, + * immediately before the actual write. The returned value is what is written, + * giving the handler a chance to modify the written value. + * @post_write: Post write callback. Passed the written value. Most write side + * effects should be implemented here. + * + * @post_read: Post read callback. Passes the value that is about to be returned + * for a read. The return value from this function is what is ultimately read, + * allowing this function to modify the value before return to the client. + */ + +struct RegisterAccessInfo { + const char *name; + uint64_t ro; + uint64_t w1c; + uint64_t reset; + uint64_t cor; + uint64_t rsvd; + uint64_t unimp; + + uint64_t (*pre_write)(RegisterInfo *reg, uint64_t val); + void (*post_write)(RegisterInfo *reg, uint64_t val); + + uint64_t (*post_read)(RegisterInfo *reg, uint64_t val); + + hwaddr addr; +}; + +/** + * A register that is part of guest accessible state + * @data: pointer to the register data. Will be cast + * to the relevant uint type depending on data_size. + * @data_size: Size of the register in bytes. Must be + * 1, 2, 4 or 8 + * + * @access: Access description of this register + * + * @debug: Whether or not verbose debug is enabled + * @prefix: String prefix for log and debug messages + * + * @opaque: Opaque data for the register + */ + +struct RegisterInfo { + /* <private> */ + DeviceState parent_obj; + + /* <public> */ + void *data; + int data_size; + + const RegisterAccessInfo *access; + + void *opaque; +}; + +#define TYPE_REGISTER "qemu,register" +#define REGISTER(obj) OBJECT_CHECK(RegisterInfo, (obj), TYPE_REGISTER) + +/** + * This structure is used to group all of the individual registers which are + * modeled using the RegisterInfo structure. + * + * @r is an aray containing of all the relevent RegisterInfo structures. + * + * @num_elements is the number of elements in the array r + * + * @mem: optional Memory region for the register + */ + +struct RegisterInfoArray { + MemoryRegion mem; + + int num_elements; + RegisterInfo **r; + + bool debug; + const char *prefix; +}; + +/** + * write a value to a register, subject to its restrictions + * @reg: register to write to + * @val: value to write + * @we: write enable mask + * @prefix: The device prefix that should be printed before the register name + * @debug: Should the write operation debug information be printed? + */ + +void register_write(RegisterInfo *reg, uint64_t val, uint64_t we, + const char *prefix, bool debug); + +/** + * read a value from a register, subject to its restrictions + * @reg: register to read from + * @re: read enable mask + * @prefix: The device prefix that should be printed before the register name + * @debug: Should the read operation debug information be printed? + * returns: value read + */ + +uint64_t register_read(RegisterInfo *reg, uint64_t re, const char* prefix, + bool debug); + +/** + * reset a register + * @reg: register to reset + */ + +void register_reset(RegisterInfo *reg); + +/** + * Initialize a register. + * @reg: Register to initialize + */ + +void register_init(RegisterInfo *reg); + +/** + * Memory API MMIO write handler that will write to a Register API register. + * @opaque: RegisterInfo to write to + * @addr: Address to write + * @value: Value to write + * @size: Number of bytes to write + */ + +void register_write_memory(void *opaque, hwaddr addr, uint64_t value, + unsigned size); + +/** + * Memory API MMIO read handler that will read from a Register API register. + * @opaque: RegisterInfo to read from + * @addr: Address to read + * @size: Number of bytes to read + * returns: Value read from register + */ + +uint64_t register_read_memory(void *opaque, hwaddr addr, unsigned size); + +/** + * Init a block of registers into a container MemoryRegion. A + * number of constant register definitions are parsed to create a corresponding + * array of RegisterInfo's. + * + * @owner: device owning the registers + * @rae: Register definitions to init + * @num: number of registers to init (length of @rae) + * @ri: Register array to init, must already be allocated + * @data: Array to use for register data, must already be allocated + * @ops: Memory region ops to access registers. + * @debug enabled: turn on/off verbose debug information + * returns: A structure containing all of the registers and an initialized + * memory region (r_array->mem) the caller should add to a container. + */ + +RegisterInfoArray *register_init_block32(DeviceState *owner, + const RegisterAccessInfo *rae, + int num, RegisterInfo *ri, + uint32_t *data, + const MemoryRegionOps *ops, + bool debug_enabled, + uint64_t memory_size); + +/** + * This function should be called to cleanup the registers that were initialized + * when calling register_init_block32(). This function should only be called + * from the device's instance_finalize function. + * + * Any memory operations that the device performed that require cleanup (such + * as creating subregions) need to be called before calling this function. + * + * @r_array: A structure containing all of the registers, as returned by + * register_init_block32() + */ + +void register_finalize_block(RegisterInfoArray *r_array); + +/* Define constants for a 32 bit register */ + +/* This macro will define A_FOO, for the byte address of a register + * as well as R_FOO for the uint32_t[] register number (A_FOO / 4). + */ +#define REG32(reg, addr) \ + enum { A_ ## reg = (addr) }; \ + enum { R_ ## reg = (addr) / 4 }; + +/* Define SHIFT, LENGTH and MASK constants for a field within a register */ + +/* This macro will define FOO_BAR_MASK, FOO_BAR_SHIFT and FOO_BAR_LENGTH + * constants for field BAR in register FOO. + */ +#define FIELD(reg, field, shift, length) \ + enum { R_ ## reg ## _ ## field ## _SHIFT = (shift)}; \ + enum { R_ ## reg ## _ ## field ## _LENGTH = (length)}; \ + enum { R_ ## reg ## _ ## field ## _MASK = \ + MAKE_64BIT_MASK(shift, length)}; + +/* Extract a field from a register */ +#define FIELD_EX32(storage, reg, field) \ + extract32((storage), R_ ## reg ## _ ## field ## _SHIFT, \ + R_ ## reg ## _ ## field ## _LENGTH) + +/* Extract a field from an array of registers */ +#define ARRAY_FIELD_EX32(regs, reg, field) \ + FIELD_EX32((regs)[R_ ## reg], reg, field) + +/* Deposit a register field. + * Assigning values larger then the target field will result in + * compilation warnings. + */ +#define FIELD_DP32(storage, reg, field, val) ({ \ + struct { \ + unsigned int v:R_ ## reg ## _ ## field ## _LENGTH; \ + } v = { .v = val }; \ + uint32_t d; \ + d = deposit32((storage), R_ ## reg ## _ ## field ## _SHIFT, \ + R_ ## reg ## _ ## field ## _LENGTH, v.v); \ + d; }) + +/* Deposit a field to array of registers. */ +#define ARRAY_FIELD_DP32(regs, reg, field, val) \ + (regs)[R_ ## reg] = FIELD_DP32((regs)[R_ ## reg], reg, field, val); + +#endif diff --git a/include/hw/s390x/css-bridge.h b/include/hw/s390x/css-bridge.h new file mode 100644 index 0000000000..ad73c1faf6 --- /dev/null +++ b/include/hw/s390x/css-bridge.h @@ -0,0 +1,31 @@ +/* + * virtual css bridge definition + * + * Copyright 2012,2016 IBM Corp. + * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> + * Pierre Morel <pmorel@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#ifndef HW_S390X_CSS_BRIDGE_H +#define HW_S390X_CSS_BRIDGE_H +#include "qom/object.h" +#include "hw/qdev-core.h" + +/* virtual css bridge */ +#define TYPE_VIRTUAL_CSS_BRIDGE "virtual-css-bridge" + +/* 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); + +#endif diff --git a/include/hw/s390x/css.h b/include/hw/s390x/css.h index 38f4d77779..1da63e361d 100644 --- a/include/hw/s390x/css.h +++ b/include/hw/s390x/css.h @@ -17,6 +17,7 @@ #include "hw/s390x/ioinst.h" /* Channel subsystem constants. */ +#define MAX_DEVNO 65535 #define MAX_SCHID 65535 #define MAX_SSID 3 #define MAX_CSSID 254 /* 255 is reserved */ @@ -24,6 +25,8 @@ #define MAX_CIWS 62 +#define VIRTUAL_CSSID 0xfe + typedef struct CIW { uint8_t type; uint8_t command; @@ -169,4 +172,19 @@ extern PropertyInfo css_devid_propinfo; #define DEFINE_PROP_CSS_DEV_ID(_n, _s, _f) \ DEFINE_PROP(_n, _s, _f, css_devid_propinfo, CssDevId) +/** + * Create a subchannel for the given bus id. + * + * If @p bus_id is valid, verify that it uses the virtual channel + * subsystem id and is not already in use, and find a free subchannel + * id for it. If @p bus_id is not valid, find a free subchannel id and + * device number across all subchannel sets. If either of the former + * actions succeed, allocate a subchannel structure, initialise it + * with the bus id, subchannel id and device number, register it with + * the CSS and return it. Otherwise return NULL. + * + * The caller becomes owner of the returned subchannel structure and + * is responsible for unregistering and freeing it. + */ +SubchDev *css_create_virtual_sch(CssDevId bus_id, Error **errp); #endif diff --git a/include/hw/s390x/sclp.h b/include/hw/s390x/sclp.h index b0c71b5550..fbf357d2ed 100644 --- a/include/hw/s390x/sclp.h +++ b/include/hw/s390x/sclp.h @@ -58,6 +58,7 @@ #define SCLP_RC_CONTAINED_EQUIPMENT_CHECK 0x0340 #define SCLP_RC_INSUFFICIENT_SCCB_LENGTH 0x0300 #define SCLP_RC_STANDBY_READ_COMPLETION 0x0410 +#define SCLP_RC_ADAPTER_IN_RESERVED_STATE 0x05f0 #define SCLP_RC_ADAPTER_ID_NOT_RECOGNIZED 0x09f0 #define SCLP_RC_INVALID_FUNCTION 0x40f0 #define SCLP_RC_NO_EVENT_BUFFERS_STORED 0x60f0 diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h new file mode 100644 index 0000000000..def3b4507e --- /dev/null +++ b/include/hw/ssi/aspeed_smc.h @@ -0,0 +1,100 @@ +/* + * ASPEED AST2400 SMC Controller (SPI Flash Only) + * + * Copyright (C) 2016 IBM Corp. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef ASPEED_SMC_H +#define ASPEED_SMC_H + +#include "hw/ssi/ssi.h" + +typedef struct AspeedSegments { + hwaddr addr; + uint32_t size; +} AspeedSegments; + +struct AspeedSMCState; +typedef struct AspeedSMCController { + const char *name; + uint8_t r_conf; + uint8_t r_ce_ctrl; + uint8_t r_ctrl0; + uint8_t r_timings; + uint8_t conf_enable_w0; + uint8_t max_slaves; + const AspeedSegments *segments; + uint32_t mapping_window_size; +} AspeedSMCController; + +typedef struct AspeedSMCFlash { + const struct AspeedSMCState *controller; + + uint8_t id; + uint32_t size; + + MemoryRegion mmio; + DeviceState *flash; +} AspeedSMCFlash; + +#define TYPE_ASPEED_SMC "aspeed.smc" +#define ASPEED_SMC(obj) OBJECT_CHECK(AspeedSMCState, (obj), TYPE_ASPEED_SMC) +#define ASPEED_SMC_CLASS(klass) \ + OBJECT_CLASS_CHECK(AspeedSMCClass, (klass), TYPE_ASPEED_SMC) +#define ASPEED_SMC_GET_CLASS(obj) \ + OBJECT_GET_CLASS(AspeedSMCClass, (obj), TYPE_ASPEED_SMC) + +typedef struct AspeedSMCClass { + SysBusDevice parent_obj; + const AspeedSMCController *ctrl; +} AspeedSMCClass; + +#define ASPEED_SMC_R_MAX (0x100 / 4) + +typedef struct AspeedSMCState { + SysBusDevice parent_obj; + + const AspeedSMCController *ctrl; + + MemoryRegion mmio; + MemoryRegion mmio_flash; + + qemu_irq irq; + int irqline; + + uint32_t num_cs; + qemu_irq *cs_lines; + + SSIBus *spi; + + uint32_t regs[ASPEED_SMC_R_MAX]; + + /* depends on the controller type */ + uint8_t r_conf; + uint8_t r_ce_ctrl; + uint8_t r_ctrl0; + uint8_t r_timings; + uint8_t conf_enable_w0; + + AspeedSMCFlash *flashes; +} AspeedSMCState; + +#endif /* ASPEED_SMC_H */ diff --git a/include/hw/ssi/ssi.h b/include/hw/ssi/ssi.h index 4a0a53903c..6a0c3c3cdb 100644 --- a/include/hw/ssi/ssi.h +++ b/include/hw/ssi/ssi.h @@ -37,7 +37,7 @@ enum SSICSMode { struct SSISlaveClass { DeviceClass parent_class; - int (*init)(SSISlave *dev); + void (*realize)(SSISlave *dev, Error **errp); /* if you have standard or no CS behaviour, just override transfer. * This is called when the device cs is active (true by default). diff --git a/include/hw/timer/imx_gpt.h b/include/hw/timer/imx_gpt.h index 461adbe53f..eac59b2a70 100644 --- a/include/hw/timer/imx_gpt.h +++ b/include/hw/timer/imx_gpt.h @@ -74,7 +74,12 @@ #define GPT_IR_OF3IE (1 << 2) #define GPT_IR_ROVIE (1 << 5) -#define TYPE_IMX_GPT "imx.gpt" +#define TYPE_IMX25_GPT "imx25.gpt" +#define TYPE_IMX31_GPT "imx31.gpt" +#define TYPE_IMX6_GPT "imx6.gpt" + +#define TYPE_IMX_GPT TYPE_IMX25_GPT + #define IMX_GPT(obj) OBJECT_CHECK(IMXGPTState, (obj), TYPE_IMX_GPT) typedef struct IMXGPTState{ @@ -103,6 +108,8 @@ typedef struct IMXGPTState{ uint32_t freq; qemu_irq irq; + + const IMXClk *clocks; } IMXGPTState; #endif /* IMX_GPT_H */ diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 0610377789..07f7188df4 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -73,6 +73,8 @@ typedef struct VFIOContainer { VFIOAddressSpace *space; int fd; /* /dev/vfio/vfio, empowered by the attached groups */ MemoryListener listener; + MemoryListener prereg_listener; + unsigned iommu_type; int error; bool initialized; /* @@ -80,9 +82,8 @@ typedef struct VFIOContainer { * contiguous IOVA window. We may need to generalize that in * future */ - hwaddr min_iova, max_iova; - uint64_t iova_pgsizes; QLIST_HEAD(, VFIOGuestIOMMU) giommu_list; + QLIST_HEAD(, VFIOHostDMAWindow) hostwin_list; QLIST_HEAD(, VFIOGroup) group_list; QLIST_ENTRY(VFIOContainer) next; } VFIOContainer; @@ -95,6 +96,13 @@ typedef struct VFIOGuestIOMMU { QLIST_ENTRY(VFIOGuestIOMMU) giommu_next; } VFIOGuestIOMMU; +typedef struct VFIOHostDMAWindow { + hwaddr min_iova; + hwaddr max_iova; + uint64_t iova_pgsizes; + QLIST_ENTRY(VFIOHostDMAWindow) hostwin_next; +} VFIOHostDMAWindow; + typedef struct VFIODeviceOps VFIODeviceOps; typedef struct VFIODevice { @@ -158,4 +166,12 @@ int vfio_get_region_info(VFIODevice *vbasedev, int index, int vfio_get_dev_region_info(VFIODevice *vbasedev, uint32_t type, uint32_t subtype, struct vfio_region_info **info); #endif +extern const MemoryListener vfio_prereg_listener; + +int vfio_spapr_create_window(VFIOContainer *container, + MemoryRegionSection *section, + hwaddr *pgsize); +int vfio_spapr_remove_window(VFIOContainer *container, + hwaddr offset_within_address_space); + #endif /* !HW_VFIO_VFIO_COMMON_H */ diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h index 95fcc96676..84e1cb79ff 100644 --- a/include/hw/virtio/vhost-backend.h +++ b/include/hw/virtio/vhost-backend.h @@ -57,6 +57,8 @@ typedef int (*vhost_set_vring_kick_op)(struct vhost_dev *dev, struct vhost_vring_file *file); typedef int (*vhost_set_vring_call_op)(struct vhost_dev *dev, struct vhost_vring_file *file); +typedef int (*vhost_set_vring_busyloop_timeout_op)(struct vhost_dev *dev, + struct vhost_vring_state *r); typedef int (*vhost_set_features_op)(struct vhost_dev *dev, uint64_t features); typedef int (*vhost_get_features_op)(struct vhost_dev *dev, @@ -91,6 +93,7 @@ typedef struct VhostOps { vhost_get_vring_base_op vhost_get_vring_base; vhost_set_vring_kick_op vhost_set_vring_kick; vhost_set_vring_call_op vhost_set_vring_call; + vhost_set_vring_busyloop_timeout_op vhost_set_vring_busyloop_timeout; vhost_set_features_op vhost_set_features; vhost_get_features_op vhost_get_features; vhost_set_owner_op vhost_set_owner; diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index b60d7585b4..2106ed8c0d 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -64,7 +64,8 @@ struct vhost_dev { }; int vhost_dev_init(struct vhost_dev *hdev, void *opaque, - VhostBackendType backend_type); + VhostBackendType backend_type, + uint32_t busyloop_timeout); void vhost_dev_cleanup(struct vhost_dev *hdev); int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev); void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev); diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index 89f4879f55..3dff0c9a76 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -107,7 +107,7 @@ typedef struct VirtIOGPU { bool use_virgl_renderer; bool renderer_inited; - bool renderer_blocked; + int renderer_blocked; QEMUTimer *fence_poll; QEMUTimer *print_stats; diff --git a/include/io/task.h b/include/io/task.h index a993212ad9..df9499aa3a 100644 --- a/include/io/task.h +++ b/include/io/task.h @@ -159,7 +159,7 @@ typedef int (*QIOTaskWorker)(QIOTask *task, * QIOTask *task; * SocketAddress *addrCopy; * - * qapi_copy_SocketAddress(&addrCopy, addr); + * addrCopy = QAPI_CLONE(SocketAddress, addr); * task = qio_task_new(OBJECT(obj), func, opaque, notify); * * qio_task_run_in_thread(task, myobject_listen_worker, diff --git a/include/net/vhost_net.h b/include/net/vhost_net.h index 0bd48770d8..5a08efffef 100644 --- a/include/net/vhost_net.h +++ b/include/net/vhost_net.h @@ -10,6 +10,7 @@ typedef struct vhost_net VHostNetState; typedef struct VhostNetOptions { VhostBackendType backend_type; NetClientState *net_backend; + uint32_t busyloop_timeout; void *opaque; } VhostNetOptions; diff --git a/include/qapi/clone-visitor.h b/include/qapi/clone-visitor.h new file mode 100644 index 0000000000..b16177e1ee --- /dev/null +++ b/include/qapi/clone-visitor.h @@ -0,0 +1,39 @@ +/* + * Clone Visitor + * + * Copyright (C) 2016 Red Hat, Inc. + * + * 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 QAPI_CLONE_VISITOR_H +#define QAPI_CLONE_VISITOR_H + +#include "qemu/typedefs.h" +#include "qapi/visitor.h" +#include "qapi-visit.h" + +/* + * The clone visitor is for direct use only by the QAPI_CLONE() macro; + * it requires that the root visit occur on an object, list, or + * alternate, and is not usable directly on built-in QAPI types. + */ +typedef struct QapiCloneVisitor QapiCloneVisitor; + +void *qapi_clone(const void *src, void (*visit_type)(Visitor *, const char *, + void **, Error **)); + +/* + * Deep-clone QAPI object @src of the given @type, and return the result. + * + * Not usable on QAPI scalars (integers, strings, enums), nor on a + * QAPI object that references the 'any' type. Safe when @src is NULL. + */ +#define QAPI_CLONE(type, src) \ + ((type *)qapi_clone(src, \ + (void (*)(Visitor *, const char *, void**, \ + Error **))visit_type_ ## type)) + +#endif diff --git a/include/qapi/dealloc-visitor.h b/include/qapi/dealloc-visitor.h index 45b06b248c..b3e5c85fd8 100644 --- a/include/qapi/dealloc-visitor.h +++ b/include/qapi/dealloc-visitor.h @@ -23,9 +23,6 @@ typedef struct QapiDeallocVisitor QapiDeallocVisitor; * qapi_free_FOO() functions, and is the only visitor designed to work * correctly in the face of a partially-constructed QAPI tree. */ -QapiDeallocVisitor *qapi_dealloc_visitor_new(void); -void qapi_dealloc_visitor_cleanup(QapiDeallocVisitor *d); - -Visitor *qapi_dealloc_get_visitor(QapiDeallocVisitor *v); +Visitor *qapi_dealloc_visitor_new(void); #endif diff --git a/include/qapi/opts-visitor.h b/include/qapi/opts-visitor.h index ae1bf7cf51..6462c96c29 100644 --- a/include/qapi/opts-visitor.h +++ b/include/qapi/opts-visitor.h @@ -35,8 +35,6 @@ typedef struct OptsVisitor OptsVisitor; * QTypes. It also requires a non-null list argument to * visit_start_list(). */ -OptsVisitor *opts_visitor_new(const QemuOpts *opts); -void opts_visitor_cleanup(OptsVisitor *nv); -Visitor *opts_get_visitor(OptsVisitor *nv); +Visitor *opts_visitor_new(const QemuOpts *opts); #endif diff --git a/include/qapi/qmp-input-visitor.h b/include/qapi/qmp-input-visitor.h index b0624d8683..f3ff5f3e98 100644 --- a/include/qapi/qmp-input-visitor.h +++ b/include/qapi/qmp-input-visitor.h @@ -25,10 +25,6 @@ typedef struct QmpInputVisitor QmpInputVisitor; * Set @strict to reject a parse that doesn't consume all keys of a * dictionary; otherwise excess input is ignored. */ -QmpInputVisitor *qmp_input_visitor_new(QObject *obj, bool strict); - -void qmp_input_visitor_cleanup(QmpInputVisitor *v); - -Visitor *qmp_input_get_visitor(QmpInputVisitor *v); +Visitor *qmp_input_visitor_new(QObject *obj, bool strict); #endif diff --git a/include/qapi/qmp-output-visitor.h b/include/qapi/qmp-output-visitor.h index 22667706ab..040fdda142 100644 --- a/include/qapi/qmp-output-visitor.h +++ b/include/qapi/qmp-output-visitor.h @@ -19,10 +19,12 @@ typedef struct QmpOutputVisitor QmpOutputVisitor; -QmpOutputVisitor *qmp_output_visitor_new(void); -void qmp_output_visitor_cleanup(QmpOutputVisitor *v); - -QObject *qmp_output_get_qobject(QmpOutputVisitor *v); -Visitor *qmp_output_get_visitor(QmpOutputVisitor *v); +/* + * Create a new QMP output visitor. + * + * If everything else succeeds, pass @result to visit_complete() to + * collect the result of the visit. + */ +Visitor *qmp_output_visitor_new(QObject **result); #endif diff --git a/include/qapi/qmp/types.h b/include/qapi/qmp/types.h index 7782ec5a60..f21ecf48fe 100644 --- a/include/qapi/qmp/types.h +++ b/include/qapi/qmp/types.h @@ -20,6 +20,5 @@ #include "qapi/qmp/qstring.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qlist.h" -#include "qapi/qmp/qjson.h" #endif /* QEMU_OBJECTS_H */ diff --git a/include/qapi/string-input-visitor.h b/include/qapi/string-input-visitor.h index 7b76c2b9e3..33551340e3 100644 --- a/include/qapi/string-input-visitor.h +++ b/include/qapi/string-input-visitor.h @@ -22,9 +22,6 @@ typedef struct StringInputVisitor StringInputVisitor; * QAPI structs, alternates, null, or arbitrary QTypes. It also * requires a non-null list argument to visit_start_list(). */ -StringInputVisitor *string_input_visitor_new(const char *str); -void string_input_visitor_cleanup(StringInputVisitor *v); - -Visitor *string_input_get_visitor(StringInputVisitor *v); +Visitor *string_input_visitor_new(const char *str); #endif diff --git a/include/qapi/string-output-visitor.h b/include/qapi/string-output-visitor.h index e10522a35b..268dfe9986 100644 --- a/include/qapi/string-output-visitor.h +++ b/include/qapi/string-output-visitor.h @@ -18,14 +18,18 @@ typedef struct StringOutputVisitor StringOutputVisitor; /* + * Create a new string output visitor. + * + * Using @human creates output that is a bit easier for humans to read + * (for example, showing integer values in both decimal and hex). + * + * If everything else succeeds, pass @result to visit_complete() to + * collect the result of the visit. + * * The string output visitor does not implement support for visiting * QAPI structs, alternates, null, or arbitrary QTypes. It also * requires a non-null list argument to visit_start_list(). */ -StringOutputVisitor *string_output_visitor_new(bool human); -void string_output_visitor_cleanup(StringOutputVisitor *v); - -char *string_output_get_string(StringOutputVisitor *v); -Visitor *string_output_get_visitor(StringOutputVisitor *v); +Visitor *string_output_visitor_new(bool human, char **result); #endif diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h index 145afd03e7..8bd47ee4bb 100644 --- a/include/qapi/visitor-impl.h +++ b/include/qapi/visitor-impl.h @@ -27,14 +27,18 @@ */ /* - * There are three classes of visitors; setting the class determines + * There are four classes of visitors; setting the class determines * how QAPI enums are visited, as well as what additional restrictions - * can be asserted. + * can be asserted. The values are intentionally chosen so as to + * permit some assertions based on whether a given bit is set (that + * is, some assertions apply to input and clone visitors, some + * assertions apply to output and clone visitors). */ typedef enum VisitorType { - VISITOR_INPUT, - VISITOR_OUTPUT, - VISITOR_DEALLOC, + VISITOR_INPUT = 1, + VISITOR_OUTPUT = 2, + VISITOR_CLONE = 3, + VISITOR_DEALLOC = 4, } VisitorType; struct Visitor @@ -47,7 +51,7 @@ struct Visitor void (*check_struct)(Visitor *v, Error **errp); /* Must be set to visit structs */ - void (*end_struct)(Visitor *v); + void (*end_struct)(Visitor *v, void **obj); /* Must be set; implementations may require @list to be non-null, * but must document it. */ @@ -58,7 +62,7 @@ struct Visitor GenericList *(*next_list)(Visitor *v, GenericList *tail, size_t size); /* Must be set */ - void (*end_list)(Visitor *v); + void (*end_list)(Visitor *v, void **list); /* Must be set by input and dealloc visitors to visit alternates; * optional for output visitors. */ @@ -67,7 +71,7 @@ struct Visitor bool promote_int, Error **errp); /* Optional, needed for dealloc visitor */ - void (*end_alternate)(Visitor *v); + void (*end_alternate)(Visitor *v, void **obj); /* Must be set */ void (*type_int64)(Visitor *v, const char *name, int64_t *obj, @@ -104,6 +108,12 @@ struct Visitor /* Must be set */ VisitorType type; + + /* Must be set for output visitors, optional otherwise. */ + void (*complete)(Visitor *v, void *opaque); + + /* Must be set */ + void (*free)(Visitor *v); }; #endif diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h index 4d12167bdc..fb8f4eb6ec 100644 --- a/include/qapi/visitor.h +++ b/include/qapi/visitor.h @@ -24,19 +24,32 @@ * for doing work at each node of a QAPI graph; it can also be used * for a virtual walk, where there is no actual QAPI C struct. * - * There are three kinds of visitor classes: input visitors (QMP, + * There are four kinds of visitor classes: input visitors (QMP, * string, and QemuOpts) parse an external representation and build * the corresponding QAPI graph, output visitors (QMP and string) take - * a completed QAPI graph and generate an external representation, and - * the dealloc visitor can take a QAPI graph (possibly partially - * constructed) and recursively free its resources. While the dealloc - * and QMP input/output visitors are general, the string and QemuOpts - * visitors have some implementation limitations; see the - * documentation for each visitor for more details on what it + * a completed QAPI graph and generate an external representation, the + * dealloc visitor can take a QAPI graph (possibly partially + * constructed) and recursively free its resources, and the clone + * visitor performs a deep clone of one QAPI object to another. While + * the dealloc and QMP input/output visitors are general, the string, + * QemuOpts, and clone visitors have some implementation limitations; + * see the documentation for each visitor for more details on what it * supports. Also, see visitor-impl.h for the callback contracts * implemented by each visitor, and docs/qapi-code-gen.txt for more * about the QAPI code generator. * + * All of the visitors are created via: + * + * Visitor *subtype_visitor_new(parameters...); + * + * A visitor should be used for exactly one top-level visit_type_FOO() + * or virtual walk; if that is successful, the caller can optionally + * call visit_complete() (for now, useful only for output visits, but + * safe to call on all visits). Then, regardless of success or + * failure, the user should call visit_free() to clean up resources. + * It is okay to free the visitor without completing the visit, if + * some other error is detected in the meantime. + * * All QAPI types have a corresponding function with a signature * roughly compatible with this: * @@ -68,9 +81,9 @@ * * If an error is detected during visit_type_FOO() with an input * visitor, then *@obj will be NULL for pointer types, and left - * unchanged for scalar types. Using an output visitor with an - * incomplete object has undefined behavior (other than a special case - * for visit_type_str() treating NULL like ""), while the dealloc + * unchanged for scalar types. Using an output or clone visitor with + * an incomplete object has undefined behavior (other than a special + * case for visit_type_str() treating NULL like ""), while the dealloc * visitor safely handles incomplete objects. Since input visitors * never produce an incomplete object, such an object is possible only * by manual construction. @@ -90,11 +103,19 @@ * * void qapi_free_FOO(FOO *obj); * - * which behaves like free() in that @obj may be NULL. Because of - * these functions, the dealloc visitor is seldom used directly - * outside of generated code. QAPI types can also inherit from a base - * class; when this happens, a function is generated for easily going - * from the derived type to the base type: + * where behaves like free() in that @obj may be NULL. Such objects + * may also be used with the following macro, provided alongside the + * clone visitor: + * + * Type *QAPI_CLONE(Type, src); + * + * in order to perform a deep clone of @src. Because of the generated + * qapi_free functions and the QAPI_CLONE() macro, the clone and + * dealloc visitor should not be used directly outside of QAPI code. + * + * QAPI types can also inherit from a base class; when this happens, a + * function is generated for easily going from the derived type to the + * base type: * * BASE *qapi_CHILD_base(CHILD *obj); * @@ -105,14 +126,14 @@ * Error *err = NULL; * Visitor *v; * - * v = ...obtain input visitor... + * v = FOO_visitor_new(...); * visit_type_Foo(v, NULL, &f, &err); * if (err) { * ...handle error... * } else { * ...use f... * } - * ...clean up v... + * visit_free(v); * qapi_free_Foo(f); * </example> * @@ -122,7 +143,7 @@ * Error *err = NULL; * Visitor *v; * - * v = ...obtain input visitor... + * v = FOO_visitor_new(...); * visit_type_FooList(v, NULL, &l, &err); * if (err) { * ...handle error... @@ -131,7 +152,7 @@ * ...use l->value... * } * } - * ...clean up v... + * visit_free(v); * qapi_free_FooList(l); * </example> * @@ -141,13 +162,17 @@ * Foo *f = ...obtain populated object... * Error *err = NULL; * Visitor *v; + * Type *result; * - * v = ...obtain output visitor... + * v = FOO_visitor_new(..., &result); * visit_type_Foo(v, NULL, &f, &err); * if (err) { * ...handle error... + * } else { + * visit_complete(v, &result); + * ...use result... * } - * ...clean up v... + * visit_free(v); * </example> * * When visiting a real QAPI struct, this file provides several @@ -173,7 +198,7 @@ * Error *err = NULL; * int value; * - * v = ...obtain visitor... + * v = FOO_visitor_new(...); * visit_start_struct(v, NULL, NULL, 0, &err); * if (err) { * goto out; @@ -193,15 +218,15 @@ * goto outlist; * } * outlist: - * visit_end_list(v); + * visit_end_list(v, NULL); * if (!err) { * visit_check_struct(v, &err); * } * outobj: - * visit_end_struct(v); + * visit_end_struct(v, NULL); * out: * error_propagate(errp, err); - * ...clean up v... + * visit_free(v); * </example> */ @@ -222,6 +247,31 @@ typedef struct GenericAlternate { char padding[]; } GenericAlternate; +/*** Visitor cleanup ***/ + +/* + * Complete the visit, collecting any output. + * + * May only be called only once after a successful top-level + * visit_type_FOO() or visit_end_ITEM(), and marks the end of the + * visit. The @opaque pointer should match the output parameter + * passed to the subtype_visitor_new() used to create an output + * visitor, or NULL for any other visitor. Needed for output + * visitors, but may also be called with other visitors. + */ +void visit_complete(Visitor *v, void *opaque); + +/* + * Free @v and any resources it has tied up. + * + * May be called whether or not the visit has been successfully + * completed, but should not be called until a top-level + * visit_type_FOO() or visit_start_ITEM() has been performed on the + * visitor. Safe if @v is NULL. + */ +void visit_free(Visitor *v); + + /*** Visiting structures ***/ /* @@ -231,9 +281,9 @@ typedef struct GenericAlternate { * container; see the general description of @name above. * * @obj must be non-NULL for a real walk, in which case @size - * determines how much memory an input visitor will allocate into - * *@obj. @obj may also be NULL for a virtual walk, in which case - * @size is ignored. + * determines how much memory an input or clone visitor will allocate + * into *@obj. @obj may also be NULL for a virtual walk, in which + * case @size is ignored. * * @errp obeys typical error usage, and reports failures such as a * member @name is not present, or present but not an object. On @@ -242,8 +292,8 @@ typedef struct GenericAlternate { * After visit_start_struct() succeeds, the caller may visit its * members one after the other, passing the member's name and address * within the struct. Finally, visit_end_struct() needs to be called - * to clean up, even if intermediate visits fail. See the examples - * above. + * with the same @obj to clean up, even if intermediate visits fail. + * See the examples above. * * FIXME Should this be named visit_start_object, since it is also * used for QAPI unions, and maps to JSON objects? @@ -267,12 +317,14 @@ void visit_check_struct(Visitor *v, Error **errp); /* * Complete an object visit started earlier. * + * @obj must match what was passed to the paired visit_start_struct(). + * * Must be called after any successful use of visit_start_struct(), * even if intermediate processing was skipped due to errors, to allow * the backend to release any resources. Destroying the visitor early - * behaves as if this was implicitly called. + * with visit_free() behaves as if this was implicitly called. */ -void visit_end_struct(Visitor *v); +void visit_end_struct(Visitor *v, void **obj); /*** Visiting lists ***/ @@ -284,9 +336,9 @@ void visit_end_struct(Visitor *v); * container; see the general description of @name above. * * @list must be non-NULL for a real walk, in which case @size - * determines how much memory an input visitor will allocate into - * *@list (at least sizeof(GenericList)). Some visitors also allow - * @list to be NULL for a virtual walk, in which case @size is + * determines how much memory an input or clone visitor will allocate + * into *@list (at least sizeof(GenericList)). Some visitors also + * allow @list to be NULL for a virtual walk, in which case @size is * ignored. * * @errp obeys typical error usage, and reports failures such as a @@ -299,8 +351,9 @@ void visit_end_struct(Visitor *v); * visit (where @obj is NULL) uses other means. For each list * element, call the appropriate visit_type_FOO() with name set to * NULL and obj set to the address of the value member of the list - * element. Finally, visit_end_list() needs to be called to clean up, - * even if intermediate visits fail. See the examples above. + * element. Finally, visit_end_list() needs to be called with the + * same @list to clean up, even if intermediate visits fail. See the + * examples above. */ void visit_start_list(Visitor *v, const char *name, GenericList **list, size_t size, Error **errp); @@ -324,12 +377,14 @@ GenericList *visit_next_list(Visitor *v, GenericList *tail, size_t size); /* * Complete a list visit started earlier. * + * @list must match what was passed to the paired visit_start_list(). + * * Must be called after any successful use of visit_start_list(), even * if intermediate processing was skipped due to errors, to allow the * backend to release any resources. Destroying the visitor early - * behaves as if this was implicitly called. + * with visit_free() behaves as if this was implicitly called. */ -void visit_end_list(Visitor *v); +void visit_end_list(Visitor *v, void **list); /*** Visiting alternates ***/ @@ -340,15 +395,16 @@ void visit_end_list(Visitor *v); * @name expresses the relationship of this alternate to its parent * container; see the general description of @name above. * - * @obj must not be NULL. Input visitors use @size to determine how - * much memory to allocate into *@obj, then determine the qtype of the - * next thing to be visited, stored in (*@obj)->type. Other visitors - * will leave @obj unchanged. + * @obj must not be NULL. Input and clone visitors use @size to + * determine how much memory to allocate into *@obj, then determine + * the qtype of the next thing to be visited, stored in (*@obj)->type. + * Other visitors will leave @obj unchanged. * * If @promote_int, treat integers as QTYPE_FLOAT. * - * If successful, this must be paired with visit_end_alternate() to - * clean up, even if visiting the contents of the alternate fails. + * If successful, this must be paired with visit_end_alternate() with + * the same @obj to clean up, even if visiting the contents of the + * alternate fails. */ void visit_start_alternate(Visitor *v, const char *name, GenericAlternate **obj, size_t size, @@ -357,15 +413,15 @@ void visit_start_alternate(Visitor *v, const char *name, /* * Finish visiting an alternate type. * + * @obj must match what was passed to the paired visit_start_alternate(). + * * Must be called after any successful use of visit_start_alternate(), * even if intermediate processing was skipped due to errors, to allow * the backend to release any resources. Destroying the visitor early - * behaves as if this was implicitly called. + * with visit_free() behaves as if this was implicitly called. * - * TODO: Should all the visit_end_* interfaces take obj parameter, so - * that dealloc visitor need not track what was passed in visit_start? */ -void visit_end_alternate(Visitor *v); +void visit_end_alternate(Visitor *v, void **obj); /*** Other helpers ***/ @@ -507,9 +563,10 @@ void visit_type_bool(Visitor *v, const char *name, bool *obj, Error **errp); * @name expresses the relationship of this string to its parent * container; see the general description of @name above. * - * @obj must be non-NULL. Input visitors set *@obj to the value - * (never NULL). Other visitors leave *@obj unchanged, and commonly - * treat NULL like "". + * @obj must be non-NULL. Input and clone visitors set *@obj to the + * value (always using "" rather than NULL for an empty string). + * Other visitors leave *@obj unchanged, and commonly treat NULL like + * "". * * It is safe to cast away const when preparing a (const char *) value * into @obj for use by an output visitor. diff --git a/include/qemu/bitops.h b/include/qemu/bitops.h index 15418a86df..98fb005aba 100644 --- a/include/qemu/bitops.h +++ b/include/qemu/bitops.h @@ -24,6 +24,9 @@ #define BIT_WORD(nr) ((nr) / BITS_PER_LONG) #define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) +#define MAKE_64BIT_MASK(shift, length) \ + (((~0ULL) >> (64 - (length))) << (shift)) + /** * set_bit - Set a bit in memory * @nr: the bit to set diff --git a/include/qemu/config-file.h b/include/qemu/config-file.h index 3b8ecb0953..8603e86395 100644 --- a/include/qemu/config-file.h +++ b/include/qemu/config-file.h @@ -12,7 +12,6 @@ void qemu_add_opts(QemuOptsList *list); void qemu_add_drive_opts(QemuOptsList *list); int qemu_set_option(const char *str); int qemu_global_option(const char *str); -void qemu_add_globals(void); void qemu_config_write(FILE *fp); int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname); diff --git a/include/qemu/range.h b/include/qemu/range.h index 3970f00089..f28f0c1825 100644 --- a/include/qemu/range.h +++ b/include/qemu/range.h @@ -26,32 +26,111 @@ /* * Operations on 64 bit address ranges. * Notes: - * - ranges must not wrap around 0, but can include the last byte ~0x0LL. - * - this can not represent a full 0 to ~0x0LL range. + * - Ranges must not wrap around 0, but can include UINT64_MAX. */ -/* A structure representing a range of addresses. */ struct Range { - uint64_t begin; /* First byte of the range, or 0 if empty. */ - uint64_t end; /* 1 + the last byte. 0 if range empty or ends at ~0x0LL. */ + /* + * Do not access members directly, use the functions! + * A non-empty range has @lob <= @upb. + * An empty range has @lob == @upb + 1. + */ + uint64_t lob; /* inclusive lower bound */ + uint64_t upb; /* inclusive upper bound */ }; +static inline void range_invariant(Range *range) +{ + assert(range->lob <= range->upb || range->lob == range->upb + 1); +} + +/* Compound literal encoding the empty range */ +#define range_empty ((Range){ .lob = 1, .upb = 0 }) + +/* Is @range empty? */ +static inline bool range_is_empty(Range *range) +{ + range_invariant(range); + return range->lob > range->upb; +} + +/* Does @range contain @val? */ +static inline bool range_contains(Range *range, uint64_t val) +{ + return val >= range->lob && val <= range->upb; +} + +/* Initialize @range to the empty range */ +static inline void range_make_empty(Range *range) +{ + *range = range_empty; + assert(range_is_empty(range)); +} + +/* + * Initialize @range to span the interval [@lob,@upb]. + * Both bounds are inclusive. + * The interval must not be empty, i.e. @lob must be less than or + * equal @upb. + */ +static inline void range_set_bounds(Range *range, uint64_t lob, uint64_t upb) +{ + range->lob = lob; + range->upb = upb; + assert(!range_is_empty(range)); +} + +/* + * Initialize @range to span the interval [@lob,@upb_plus1). + * The lower bound is inclusive, the upper bound is exclusive. + * Zero @upb_plus1 is special: if @lob is also zero, set @range to the + * empty range. Else, set @range to [@lob,UINT64_MAX]. + */ +static inline void range_set_bounds1(Range *range, + uint64_t lob, uint64_t upb_plus1) +{ + if (!lob && !upb_plus1) { + *range = range_empty; + } else { + range->lob = lob; + range->upb = upb_plus1 - 1; + } + range_invariant(range); +} + +/* Return @range's lower bound. @range must not be empty. */ +static inline uint64_t range_lob(Range *range) +{ + assert(!range_is_empty(range)); + return range->lob; +} + +/* Return @range's upper bound. @range must not be empty. */ +static inline uint64_t range_upb(Range *range) +{ + assert(!range_is_empty(range)); + return range->upb; +} + +/* + * Extend @range to the smallest interval that includes @extend_by, too. + */ static inline void range_extend(Range *range, Range *extend_by) { - if (!extend_by->begin && !extend_by->end) { + if (range_is_empty(extend_by)) { return; } - if (!range->begin && !range->end) { + if (range_is_empty(range)) { *range = *extend_by; return; } - if (range->begin > extend_by->begin) { - range->begin = extend_by->begin; + if (range->lob > extend_by->lob) { + range->lob = extend_by->lob; } - /* Compare last byte in case region ends at ~0x0LL */ - if (range->end - 1 < extend_by->end - 1) { - range->end = extend_by->end; + if (range->upb < extend_by->upb) { + range->upb = extend_by->upb; } + range_invariant(range); } /* Get last byte of a range from offset + length. diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h index 462033a4de..2f3763f781 100644 --- a/include/qemu/sockets.h +++ b/include/qemu/sockets.h @@ -107,10 +107,6 @@ SocketAddress *socket_local_address(int fd, Error **errp); */ SocketAddress *socket_remote_address(int fd, Error **errp); - -void qapi_copy_SocketAddress(SocketAddress **p_dest, - SocketAddress *src); - /** * socket_address_to_string: * @addr: the socket address struct diff --git a/include/qom/cpu.h b/include/qom/cpu.h index 32f3af3e1c..cacb100f7b 100644 --- a/include/qom/cpu.h +++ b/include/qom/cpu.h @@ -134,7 +134,7 @@ typedef struct CPUClass { /*< public >*/ ObjectClass *(*class_by_name)(const char *cpu_model); - void (*parse_features)(CPUState *cpu, char *str, Error **errp); + void (*parse_features)(const char *typename, char *str, Error **errp); void (*reset)(CPUState *cpu); int reset_dump_flags; diff --git a/include/qom/object.h b/include/qom/object.h index 2f8ac47c7c..5ecc2d166d 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -901,7 +901,7 @@ GSList *object_class_get_list(const char *implements_type, void object_ref(Object *obj); /** - * qdef_unref: + * object_unref: * @obj: the object * * Decrease the reference count of a object. A object cannot be freed as long diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index c04af8ea46..3c3e82f05d 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -124,6 +124,7 @@ int blk_pwrite_zeroes(BlockBackend *blk, int64_t offset, BlockAIOCB *blk_aio_pwrite_zeroes(BlockBackend *blk, int64_t offset, int count, BdrvRequestFlags flags, BlockCompletionFunc *cb, void *opaque); +int blk_make_zero(BlockBackend *blk, BdrvRequestFlags flags); int blk_pread(BlockBackend *blk, int64_t offset, void *buf, int count); int blk_pwrite(BlockBackend *blk, int64_t offset, const void *buf, int count, BdrvRequestFlags flags); @@ -170,7 +171,7 @@ bool blk_is_available(BlockBackend *blk); void blk_lock_medium(BlockBackend *blk, bool locked); void blk_eject(BlockBackend *blk, bool eject_flag); int blk_get_flags(BlockBackend *blk); -int blk_get_max_transfer_length(BlockBackend *blk); +uint32_t blk_get_max_transfer(BlockBackend *blk); int blk_get_max_iov(BlockBackend *blk); void blk_set_guest_block_size(BlockBackend *blk, int align); void *blk_try_blockalign(BlockBackend *blk, size_t size); diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 7313673b66..ee7c7608e0 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -75,6 +75,7 @@ void qemu_add_exit_notifier(Notifier *notify); void qemu_remove_exit_notifier(Notifier *notify); void qemu_add_machine_init_done_notifier(Notifier *notify); +void qemu_remove_machine_init_done_notifier(Notifier *notify); void hmp_savevm(Monitor *mon, const QDict *qdict); int load_vmstate(const char *name); diff --git a/include/ui/console.h b/include/ui/console.h index 7c1fdbad6f..2703a3aa5a 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -217,6 +217,7 @@ typedef struct DisplayChangeListenerOps { void (*dpy_gl_scanout)(DisplayChangeListener *dcl, uint32_t backing_id, bool backing_y_0_top, + uint32_t backing_width, uint32_t backing_height, uint32_t x, uint32_t y, uint32_t w, uint32_t h); void (*dpy_gl_update)(DisplayChangeListener *dcl, uint32_t x, uint32_t y, uint32_t w, uint32_t h); @@ -285,6 +286,7 @@ bool dpy_gfx_check_format(QemuConsole *con, void dpy_gl_scanout(QemuConsole *con, uint32_t backing_id, bool backing_y_0_top, + uint32_t backing_width, uint32_t backing_height, uint32_t x, uint32_t y, uint32_t w, uint32_t h); void dpy_gl_update(QemuConsole *con, uint32_t x, uint32_t y, uint32_t w, uint32_t h); diff --git a/include/ui/gtk.h b/include/ui/gtk.h index 2bf60f3ec5..42ca0fea8b 100644 --- a/include/ui/gtk.h +++ b/include/ui/gtk.h @@ -101,6 +101,7 @@ QEMUGLContext gd_egl_create_context(DisplayChangeListener *dcl, QEMUGLParams *params); void gd_egl_scanout(DisplayChangeListener *dcl, uint32_t backing_id, bool backing_y_0_top, + uint32_t backing_width, uint32_t backing_height, uint32_t x, uint32_t y, uint32_t w, uint32_t h); void gd_egl_scanout_flush(DisplayChangeListener *dcl, @@ -123,6 +124,7 @@ void gd_gl_area_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx); void gd_gl_area_scanout(DisplayChangeListener *dcl, uint32_t backing_id, bool backing_y_0_top, + uint32_t backing_width, uint32_t backing_height, uint32_t x, uint32_t y, uint32_t w, uint32_t h); void gd_gl_area_scanout_flush(DisplayChangeListener *dcl, diff --git a/include/ui/qemu-spice.h b/include/ui/qemu-spice.h index 57ac91b921..edad5e7bbf 100644 --- a/include/ui/qemu-spice.h +++ b/include/ui/qemu-spice.h @@ -42,6 +42,9 @@ int qemu_spice_set_pw_expire(time_t expires); int qemu_spice_migrate_info(const char *hostname, int port, int tls_port, const char *subject); +#define SPICE_NEEDS_SET_MM_TIME \ + (!defined(SPICE_SERVER_VERSION) || (SPICE_SERVER_VERSION < 0xc06)) + #if SPICE_SERVER_VERSION >= 0x000c02 void qemu_spice_register_ports(void); #else diff --git a/include/ui/sdl2.h b/include/ui/sdl2.h index 3f0b57bb16..683bb6af2e 100644 --- a/include/ui/sdl2.h +++ b/include/ui/sdl2.h @@ -64,6 +64,7 @@ QEMUGLContext sdl2_gl_get_current_context(DisplayChangeListener *dcl); void sdl2_gl_scanout(DisplayChangeListener *dcl, uint32_t backing_id, bool backing_y_0_top, + uint32_t backing_width, uint32_t backing_height, uint32_t x, uint32_t y, uint32_t w, uint32_t h); void sdl2_gl_scanout_flush(DisplayChangeListener *dcl, diff --git a/io/channel-socket.c b/io/channel-socket.c index 6ec87f8cdb..196a4f18f7 100644 --- a/io/channel-socket.c +++ b/io/channel-socket.c @@ -23,6 +23,7 @@ #include "io/channel-socket.h" #include "io/channel-watch.h" #include "trace.h" +#include "qapi/clone-visitor.h" #define SOCKET_MAX_FDS 16 @@ -189,7 +190,7 @@ void qio_channel_socket_connect_async(QIOChannelSocket *ioc, OBJECT(ioc), callback, opaque, destroy); SocketAddress *addrCopy; - qapi_copy_SocketAddress(&addrCopy, addr); + addrCopy = QAPI_CLONE(SocketAddress, addr); /* socket_connect() does a non-blocking connect(), but it * still blocks in DNS lookups, so we must use a thread */ @@ -251,7 +252,7 @@ void qio_channel_socket_listen_async(QIOChannelSocket *ioc, OBJECT(ioc), callback, opaque, destroy); SocketAddress *addrCopy; - qapi_copy_SocketAddress(&addrCopy, addr); + addrCopy = QAPI_CLONE(SocketAddress, addr); /* socket_listen() blocks in DNS lookups, so we must use a thread */ trace_qio_channel_socket_listen_async(ioc, addr); @@ -331,8 +332,8 @@ void qio_channel_socket_dgram_async(QIOChannelSocket *ioc, struct QIOChannelSocketDGramWorkerData *data = g_new0( struct QIOChannelSocketDGramWorkerData, 1); - qapi_copy_SocketAddress(&data->localAddr, localAddr); - qapi_copy_SocketAddress(&data->remoteAddr, remoteAddr); + data->localAddr = QAPI_CLONE(SocketAddress, localAddr); + data->remoteAddr = QAPI_CLONE(SocketAddress, remoteAddr); trace_qio_channel_socket_dgram_async(ioc, localAddr, remoteAddr); qio_task_run_in_thread(task, diff --git a/linux-user/host/i386/safe-syscall.inc.S b/linux-user/host/i386/safe-syscall.inc.S index 766d0ded98..9e58fc6504 100644 --- a/linux-user/host/i386/safe-syscall.inc.S +++ b/linux-user/host/i386/safe-syscall.inc.S @@ -69,7 +69,7 @@ safe_syscall_base: safe_syscall_start: /* if signal_pending is non-zero, don't do the call */ mov 4+16(%esp), %eax /* signal_pending */ - cmp $0, (%eax) + cmpl $0, (%eax) jnz 1f mov 8+16(%esp), %eax /* syscall number */ int $0x80 @@ -77,16 +77,16 @@ safe_syscall_end: /* code path for having successfully executed the syscall */ pop %ebx .cfi_remember_state - .cfi_def_cfa_offset -4 + .cfi_adjust_cfa_offset -4 .cfi_restore ebx pop %edi - .cfi_def_cfa_offset -4 + .cfi_adjust_cfa_offset -4 .cfi_restore edi pop %esi - .cfi_def_cfa_offset -4 + .cfi_adjust_cfa_offset -4 .cfi_restore esi pop %ebp - .cfi_def_cfa_offset -4 + .cfi_adjust_cfa_offset -4 .cfi_restore ebp ret @@ -94,19 +94,7 @@ safe_syscall_end: /* code path when we didn't execute the syscall */ .cfi_restore_state mov $-TARGET_ERESTARTSYS, %eax - pop %ebx - .cfi_def_cfa_offset -4 - .cfi_restore ebx - pop %edi - .cfi_def_cfa_offset -4 - .cfi_restore edi - pop %esi - .cfi_def_cfa_offset -4 - .cfi_restore esi - pop %ebp - .cfi_def_cfa_offset -4 - .cfi_restore ebp - ret + jmp safe_syscall_end .cfi_endproc .size safe_syscall_base, .-safe_syscall_base diff --git a/linux-user/qemu.h b/linux-user/qemu.h index e8a5aede95..cdf23a723a 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -116,10 +116,10 @@ typedef struct TaskState { #endif #if defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_UNICORE32) /* Extra fields for semihosted binaries. */ - uint32_t heap_base; - uint32_t heap_limit; + abi_ulong heap_base; + abi_ulong heap_limit; #endif - uint32_t stack_base; + abi_ulong stack_base; int used; /* non zero if used */ struct image_info *info; struct linux_binprm *bprm; @@ -1376,6 +1376,21 @@ void memory_region_init_alias(MemoryRegion *mr, mr->alias_offset = offset; } +void memory_region_init_rom(MemoryRegion *mr, + struct Object *owner, + const char *name, + uint64_t size, + Error **errp) +{ + memory_region_init(mr, owner, name, size); + mr->ram = true; + mr->readonly = true; + mr->terminates = true; + mr->destructor = memory_region_destructor_ram; + mr->ram_block = qemu_ram_alloc(size, mr, errp); + mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0; +} + void memory_region_init_rom_device(MemoryRegion *mr, Object *owner, const MemoryRegionOps *ops, @@ -1384,6 +1399,7 @@ void memory_region_init_rom_device(MemoryRegion *mr, uint64_t size, Error **errp) { + assert(ops); memory_region_init(mr, owner, name, size); mr->ops = ops; mr->opaque = opaque; @@ -54,11 +54,7 @@ #include "qemu/acl.h" #include "sysemu/tpm.h" #include "qapi/qmp/qerror.h" -#include "qapi/qmp/qint.h" -#include "qapi/qmp/qfloat.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qstring.h" +#include "qapi/qmp/types.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/json-streamer.h" #include "qapi/qmp/json-parser.h" @@ -1024,8 +1024,7 @@ int net_client_init(QemuOpts *opts, int is_netdev, Error **errp) void *object = NULL; Error *err = NULL; int ret = -1; - OptsVisitor *ov = opts_visitor_new(opts); - Visitor *v = opts_get_visitor(ov); + Visitor *v = opts_visitor_new(opts); { /* Parse convenience option format ip6-net=fec0::0[/64] */ @@ -1075,7 +1074,7 @@ int net_client_init(QemuOpts *opts, int is_netdev, Error **errp) } error_propagate(errp, err); - opts_visitor_cleanup(ov); + visit_free(v); return ret; } @@ -1199,7 +1198,7 @@ static void netfilter_print_info(Monitor *mon, NetFilterState *nf) char *str; ObjectProperty *prop; ObjectPropertyIterator iter; - StringOutputVisitor *ov; + Visitor *v; /* generate info str */ object_property_iter_init(&iter, OBJECT(nf)); @@ -1207,11 +1206,10 @@ static void netfilter_print_info(Monitor *mon, NetFilterState *nf) if (!strcmp(prop->name, "type")) { continue; } - ov = string_output_visitor_new(false); - object_property_get(OBJECT(nf), string_output_get_visitor(ov), - prop->name, NULL); - str = string_output_get_string(ov); - string_output_visitor_cleanup(ov); + v = string_output_visitor_new(false, &str); + object_property_get(OBJECT(nf), v, prop->name, NULL); + visit_complete(v, &str); + visit_free(v); monitor_printf(mon, ",%s=%s", prop->name, str); g_free(str); } @@ -663,6 +663,11 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, options.backend_type = VHOST_BACKEND_TYPE_KERNEL; options.net_backend = &s->nc; + if (tap->has_poll_us) { + options.busyloop_timeout = tap->poll_us; + } else { + options.busyloop_timeout = 0; + } if (vhostfdname) { vhostfd = monitor_fd_param(cur_mon, vhostfdname, &err); @@ -687,7 +692,7 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, return; } } else if (vhostfdname) { - error_setg(errp, "vhostfd= is not valid without vhost"); + error_setg(errp, "vhostfd(s)= is not valid without vhost"); } } diff --git a/net/vhost-user.c b/net/vhost-user.c index 92f4cfd1b1..a88dfe0655 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -91,6 +91,7 @@ static int vhost_user_start(int queues, NetClientState *ncs[]) options.net_backend = ncs[i]; options.opaque = s->chr; + options.busyloop_timeout = 0; s->vhost_net = vhost_net_init(&options); if (!s->vhost_net) { error_report("failed to init vhost_net for queue %d", i); @@ -217,9 +217,9 @@ static int parse_numa(void *opaque, QemuOpts *opts, Error **errp) Error *err = NULL; { - OptsVisitor *ov = opts_visitor_new(opts); - visit_type_NumaOptions(opts_get_visitor(ov), NULL, &object, &err); - opts_visitor_cleanup(ov); + Visitor *v = opts_visitor_new(opts); + visit_type_NumaOptions(v, NULL, &object, &err); + visit_free(v); } if (err) { diff --git a/pc-bios/bios-256k.bin b/pc-bios/bios-256k.bin Binary files differindex e7a7e72e32..57fb4d88b1 100644 --- a/pc-bios/bios-256k.bin +++ b/pc-bios/bios-256k.bin diff --git a/pc-bios/bios.bin b/pc-bios/bios.bin Binary files differindex b0ae502f6e..8a6869ff1b 100644 --- a/pc-bios/bios.bin +++ b/pc-bios/bios.bin diff --git a/pc-bios/efi-e1000.rom b/pc-bios/efi-e1000.rom Binary files differindex 4bc89a3d25..4e61f9b2df 100644 --- a/pc-bios/efi-e1000.rom +++ b/pc-bios/efi-e1000.rom diff --git a/pc-bios/efi-e1000e.rom b/pc-bios/efi-e1000e.rom Binary files differnew file mode 100644 index 0000000000..192a43729d --- /dev/null +++ b/pc-bios/efi-e1000e.rom diff --git a/pc-bios/efi-eepro100.rom b/pc-bios/efi-eepro100.rom Binary files differindex 85b7f9bc7f..66c52269cd 100644 --- a/pc-bios/efi-eepro100.rom +++ b/pc-bios/efi-eepro100.rom diff --git a/pc-bios/efi-ne2k_pci.rom b/pc-bios/efi-ne2k_pci.rom Binary files differindex ebafd84521..8c3e5fd6d1 100644 --- a/pc-bios/efi-ne2k_pci.rom +++ b/pc-bios/efi-ne2k_pci.rom diff --git a/pc-bios/efi-pcnet.rom b/pc-bios/efi-pcnet.rom Binary files differindex 6f19723751..802e225cb5 100644 --- a/pc-bios/efi-pcnet.rom +++ b/pc-bios/efi-pcnet.rom diff --git a/pc-bios/efi-rtl8139.rom b/pc-bios/efi-rtl8139.rom Binary files differindex 086551b760..8827181f31 100644 --- a/pc-bios/efi-rtl8139.rom +++ b/pc-bios/efi-rtl8139.rom diff --git a/pc-bios/efi-virtio.rom b/pc-bios/efi-virtio.rom Binary files differindex 140c6806db..2fc0497958 100644 --- a/pc-bios/efi-virtio.rom +++ b/pc-bios/efi-virtio.rom diff --git a/pc-bios/efi-vmxnet3.rom b/pc-bios/efi-vmxnet3.rom Binary files differnew file mode 100644 index 0000000000..3d42635d16 --- /dev/null +++ b/pc-bios/efi-vmxnet3.rom diff --git a/pc-bios/optionrom/Makefile b/pc-bios/optionrom/Makefile index ce4852a4d5..2cdda87f0e 100644 --- a/pc-bios/optionrom/Makefile +++ b/pc-bios/optionrom/Makefile @@ -20,6 +20,9 @@ build-all: multiboot.bin linuxboot.bin kvmvapic.bin # suppress auto-removal of intermediate files .SECONDARY: +%.o: %.S + $(call quiet-command,$(CPP) $(QEMU_INCLUDES) $(QEMU_DGFLAGS) $(CFLAGS) -c -o - $< | $(AS) $(ASFLAGS) -o $@," AS $(TARGET_DIR)$@") + %.img: %.o $(call quiet-command,$(LD) $(LDFLAGS_NOPIE) -Ttext 0 -e _start -s -o $@ $<," Building $(TARGET_DIR)$@") diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img Binary files differindex ea5fd9a479..089f6ba5e9 100644 --- a/pc-bios/s390-ccw.img +++ b/pc-bios/s390-ccw.img diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h index 1cf509f497..86abc56a90 100644 --- a/pc-bios/s390-ccw/iplb.h +++ b/pc-bios/s390-ccw/iplb.h @@ -43,6 +43,16 @@ struct IplBlockFcp { } __attribute__ ((packed)); typedef struct IplBlockFcp IplBlockFcp; +struct IplBlockQemuScsi { + uint32_t lun; + uint16_t target; + uint16_t channel; + uint8_t reserved0[77]; + uint8_t ssid; + uint16_t devno; +} __attribute__ ((packed)); +typedef struct IplBlockQemuScsi IplBlockQemuScsi; + struct IplParameterBlock { uint32_t len; uint8_t reserved0[3]; @@ -55,6 +65,7 @@ struct IplParameterBlock { union { IplBlockCcw ccw; IplBlockFcp fcp; + IplBlockQemuScsi scsi; }; } __attribute__ ((packed)); typedef struct IplParameterBlock IplParameterBlock; @@ -63,6 +74,7 @@ extern IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE))); #define S390_IPL_TYPE_FCP 0x00 #define S390_IPL_TYPE_CCW 0x02 +#define S390_IPL_TYPE_QEMU_SCSI 0xff static inline bool store_iplb(IplParameterBlock *iplb) { diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c index 9446ecc235..345b848752 100644 --- a/pc-bios/s390-ccw/main.c +++ b/pc-bios/s390-ccw/main.c @@ -84,6 +84,18 @@ static void virtio_setup(void) debug_print_int("ssid ", blk_schid.ssid); found = find_dev(&schib, dev_no); break; + case S390_IPL_TYPE_QEMU_SCSI: + { + VDev *vdev = virtio_get_device(); + + vdev->scsi_device_selected = true; + vdev->selected_scsi_device.channel = iplb.scsi.channel; + vdev->selected_scsi_device.target = iplb.scsi.target; + vdev->selected_scsi_device.lun = iplb.scsi.lun; + blk_schid.ssid = iplb.scsi.ssid & 0x3; + found = find_dev(&schib, iplb.scsi.devno); + break; + } default: panic("List-directed IPL not supported yet!\n"); } diff --git a/pc-bios/s390-ccw/virtio-scsi.c b/pc-bios/s390-ccw/virtio-scsi.c index 3bb48e917e..d850a8deed 100644 --- a/pc-bios/s390-ccw/virtio-scsi.c +++ b/pc-bios/s390-ccw/virtio-scsi.c @@ -204,6 +204,17 @@ static void virtio_scsi_locate_device(VDev *vdev) debug_print_int("config.scsi.max_target ", vdev->config.scsi.max_target); debug_print_int("config.scsi.max_lun ", vdev->config.scsi.max_lun); + if (vdev->scsi_device_selected) { + sdev->channel = vdev->selected_scsi_device.channel; + sdev->target = vdev->selected_scsi_device.target; + sdev->lun = vdev->selected_scsi_device.lun; + + IPL_check(sdev->channel == 0, "non-zero channel requested"); + IPL_check(sdev->target <= vdev->config.scsi.max_target, "target# high"); + IPL_check(sdev->lun <= vdev->config.scsi.max_lun, "LUN# high"); + return; + } + for (target = 0; target <= vdev->config.scsi.max_target; target++) { sdev->channel = channel; sdev->target = target; /* sdev->lun will be 0 here */ diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h index 3c6e91510e..eb35ea5faf 100644 --- a/pc-bios/s390-ccw/virtio.h +++ b/pc-bios/s390-ccw/virtio.h @@ -274,6 +274,8 @@ struct VDev { uint64_t scsi_last_block; uint32_t scsi_dev_cyls; uint8_t scsi_dev_heads; + bool scsi_device_selected; + ScsiDevice selected_scsi_device; }; typedef struct VDev VDev; diff --git a/qapi-schema.json b/qapi-schema.json index ba3bf14749..d2d650673b 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -2592,6 +2592,9 @@ # # @queues: #optional number of queues to be created for multiqueue capable tap # +# @poll-us: #optional maximum number of microseconds that could +# be spent on busy polling for tap (since 2.7) +# # Since 1.2 ## { 'struct': 'NetdevTapOptions', @@ -2608,7 +2611,8 @@ '*vhostfd': 'str', '*vhostfds': 'str', '*vhostforce': 'bool', - '*queues': 'uint32'} } + '*queues': 'uint32', + '*poll-us': 'uint32'} } ## # @NetdevSocketOptions diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs index 2278970690..7ea4aebb00 100644 --- a/qapi/Makefile.objs +++ b/qapi/Makefile.objs @@ -1,6 +1,6 @@ util-obj-y = qapi-visit-core.o qapi-dealloc-visitor.o qmp-input-visitor.o util-obj-y += qmp-output-visitor.o qmp-registry.o qmp-dispatch.o util-obj-y += string-input-visitor.o string-output-visitor.o -util-obj-y += opts-visitor.o +util-obj-y += opts-visitor.o qapi-clone-visitor.o util-obj-y += qmp-event.o util-obj-y += qapi-util.o diff --git a/qapi/block-core.json b/qapi/block-core.json index 98a20d22f4..ac8f5f61d4 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1961,7 +1961,8 @@ # # @config: #optional filename of the configuration file # -# @align: #optional required alignment for requests in bytes +# @align: #optional required alignment for requests in bytes, +# must be power of 2, or 0 for default # # @inject-error: #optional array of error injection descriptions # diff --git a/qapi/crypto.json b/qapi/crypto.json index 760d0c0577..4c4a3e07f4 100644 --- a/qapi/crypto.json +++ b/qapi/crypto.json @@ -42,12 +42,16 @@ # # @md5: MD5. Should not be used in any new code, legacy compat only # @sha1: SHA-1. Should not be used in any new code, legacy compat only +# @sha224: SHA-224. (since 2.7) # @sha256: SHA-256. Current recommended strong hash. +# @sha384: SHA-384. (since 2.7) +# @sha512: SHA-512. (since 2.7) +# @ripemd160: RIPEMD-160. (since 2.7) # Since: 2.6 ## { 'enum': 'QCryptoHashAlgorithm', 'prefix': 'QCRYPTO_HASH_ALG', - 'data': ['md5', 'sha1', 'sha256']} + 'data': ['md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'ripemd160']} ## diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c index 4cf1cf885b..1048bbc84e 100644 --- a/qapi/opts-visitor.c +++ b/qapi/opts-visitor.c @@ -180,7 +180,7 @@ opts_check_struct(Visitor *v, Error **errp) static void -opts_end_struct(Visitor *v) +opts_end_struct(Visitor *v, void **obj) { OptsVisitor *ov = to_ov(v); @@ -273,7 +273,7 @@ opts_next_list(Visitor *v, GenericList *tail, size_t size) static void -opts_end_list(Visitor *v) +opts_end_list(Visitor *v, void **obj) { OptsVisitor *ov = to_ov(v); @@ -513,7 +513,20 @@ opts_optional(Visitor *v, const char *name, bool *present) } -OptsVisitor * +static void +opts_free(Visitor *v) +{ + OptsVisitor *ov = to_ov(v); + + if (ov->unprocessed_opts != NULL) { + g_hash_table_destroy(ov->unprocessed_opts); + } + g_free(ov->fake_id_opt); + g_free(ov); +} + + +Visitor * opts_visitor_new(const QemuOpts *opts) { OptsVisitor *ov; @@ -540,26 +553,9 @@ opts_visitor_new(const QemuOpts *opts) * skip some mandatory methods... */ ov->visitor.optional = &opts_optional; + ov->visitor.free = opts_free; ov->opts_root = opts; - return ov; -} - - -void -opts_visitor_cleanup(OptsVisitor *ov) -{ - if (ov->unprocessed_opts != NULL) { - g_hash_table_destroy(ov->unprocessed_opts); - } - g_free(ov->fake_id_opt); - g_free(ov); -} - - -Visitor * -opts_get_visitor(OptsVisitor *ov) -{ return &ov->visitor; } diff --git a/qapi/qapi-clone-visitor.c b/qapi/qapi-clone-visitor.c new file mode 100644 index 0000000000..0bb8216372 --- /dev/null +++ b/qapi/qapi-clone-visitor.c @@ -0,0 +1,182 @@ +/* + * Copy one QAPI object to another + * + * Copyright (C) 2016 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qapi/clone-visitor.h" +#include "qapi/visitor-impl.h" +#include "qapi/error.h" + +struct QapiCloneVisitor { + Visitor visitor; + size_t depth; +}; + +static QapiCloneVisitor *to_qcv(Visitor *v) +{ + return container_of(v, QapiCloneVisitor, visitor); +} + +static void qapi_clone_start_struct(Visitor *v, const char *name, void **obj, + size_t size, Error **errp) +{ + QapiCloneVisitor *qcv = to_qcv(v); + + if (!obj) { + assert(qcv->depth); + /* Only possible when visiting an alternate's object + * branch. Nothing further to do here, since the earlier + * visit_start_alternate() already copied memory. */ + return; + } + + *obj = g_memdup(*obj, size); + qcv->depth++; +} + +static void qapi_clone_end(Visitor *v, void **obj) +{ + QapiCloneVisitor *qcv = to_qcv(v); + + assert(qcv->depth); + if (obj) { + qcv->depth--; + } +} + +static void qapi_clone_start_list(Visitor *v, const char *name, + GenericList **listp, size_t size, + Error **errp) +{ + qapi_clone_start_struct(v, name, (void **)listp, size, errp); +} + +static GenericList *qapi_clone_next_list(Visitor *v, GenericList *tail, + size_t size) +{ + QapiCloneVisitor *qcv = to_qcv(v); + + assert(qcv->depth); + /* Unshare the tail of the list cloned by g_memdup() */ + tail->next = g_memdup(tail->next, size); + return tail->next; +} + +static void qapi_clone_start_alternate(Visitor *v, const char *name, + GenericAlternate **obj, size_t size, + bool promote_int, Error **errp) +{ + qapi_clone_start_struct(v, name, (void **)obj, size, errp); +} + +static void qapi_clone_type_int64(Visitor *v, const char *name, int64_t *obj, + Error **errp) +{ + QapiCloneVisitor *qcv = to_qcv(v); + + assert(qcv->depth); + /* Value was already cloned by g_memdup() */ +} + +static void qapi_clone_type_uint64(Visitor *v, const char *name, + uint64_t *obj, Error **errp) +{ + QapiCloneVisitor *qcv = to_qcv(v); + + assert(qcv->depth); + /* Value was already cloned by g_memdup() */ +} + +static void qapi_clone_type_bool(Visitor *v, const char *name, bool *obj, + Error **errp) +{ + QapiCloneVisitor *qcv = to_qcv(v); + + assert(qcv->depth); + /* Value was already cloned by g_memdup() */ +} + +static void qapi_clone_type_str(Visitor *v, const char *name, char **obj, + Error **errp) +{ + QapiCloneVisitor *qcv = to_qcv(v); + + assert(qcv->depth); + /* + * Pointer was already cloned by g_memdup; create fresh copy. + * Note that as long as qmp-output-visitor accepts NULL instead of + * "", then we must do likewise. However, we want to obey the + * input visitor semantics of never producing NULL when the empty + * string is intended. + */ + *obj = g_strdup(*obj ?: ""); +} + +static void qapi_clone_type_number(Visitor *v, const char *name, double *obj, + Error **errp) +{ + QapiCloneVisitor *qcv = to_qcv(v); + + assert(qcv->depth); + /* Value was already cloned by g_memdup() */ +} + +static void qapi_clone_type_null(Visitor *v, const char *name, Error **errp) +{ + QapiCloneVisitor *qcv = to_qcv(v); + + assert(qcv->depth); + /* Nothing to do */ +} + +static void qapi_clone_free(Visitor *v) +{ + g_free(v); +} + +static Visitor *qapi_clone_visitor_new(void) +{ + QapiCloneVisitor *v; + + v = g_malloc0(sizeof(*v)); + + v->visitor.type = VISITOR_CLONE; + v->visitor.start_struct = qapi_clone_start_struct; + v->visitor.end_struct = qapi_clone_end; + v->visitor.start_list = qapi_clone_start_list; + v->visitor.next_list = qapi_clone_next_list; + v->visitor.end_list = qapi_clone_end; + v->visitor.start_alternate = qapi_clone_start_alternate; + v->visitor.end_alternate = qapi_clone_end; + v->visitor.type_int64 = qapi_clone_type_int64; + v->visitor.type_uint64 = qapi_clone_type_uint64; + v->visitor.type_bool = qapi_clone_type_bool; + v->visitor.type_str = qapi_clone_type_str; + v->visitor.type_number = qapi_clone_type_number; + v->visitor.type_null = qapi_clone_type_null; + v->visitor.free = qapi_clone_free; + + return &v->visitor; +} + +void *qapi_clone(const void *src, void (*visit_type)(Visitor *, const char *, + void **, Error **)) +{ + Visitor *v; + void *dst = (void *) src; /* Cast away const */ + + if (!src) { + return NULL; + } + + v = qapi_clone_visitor_new(); + visit_type(v, NULL, &dst, &error_abort); + visit_free(v); + return dst; +} diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c index cd68b55a1a..e39457bc79 100644 --- a/qapi/qapi-dealloc-visitor.c +++ b/qapi/qapi-dealloc-visitor.c @@ -19,53 +19,18 @@ #include "qapi/qmp/types.h" #include "qapi/visitor-impl.h" -typedef struct StackEntry -{ - void *value; - QTAILQ_ENTRY(StackEntry) node; -} StackEntry; - struct QapiDeallocVisitor { Visitor visitor; - QTAILQ_HEAD(, StackEntry) stack; }; -static QapiDeallocVisitor *to_qov(Visitor *v) -{ - return container_of(v, QapiDeallocVisitor, visitor); -} - -static void qapi_dealloc_push(QapiDeallocVisitor *qov, void *value) -{ - StackEntry *e = g_malloc0(sizeof(*e)); - - e->value = value; - - QTAILQ_INSERT_HEAD(&qov->stack, e, node); -} - -static void *qapi_dealloc_pop(QapiDeallocVisitor *qov) -{ - StackEntry *e = QTAILQ_FIRST(&qov->stack); - QObject *value; - QTAILQ_REMOVE(&qov->stack, e, node); - value = e->value; - g_free(e); - return value; -} - static void qapi_dealloc_start_struct(Visitor *v, const char *name, void **obj, size_t unused, Error **errp) { - QapiDeallocVisitor *qov = to_qov(v); - qapi_dealloc_push(qov, obj); } -static void qapi_dealloc_end_struct(Visitor *v) +static void qapi_dealloc_end_struct(Visitor *v, void **obj) { - QapiDeallocVisitor *qov = to_qov(v); - void **obj = qapi_dealloc_pop(qov); if (obj) { g_free(*obj); } @@ -75,14 +40,10 @@ static void qapi_dealloc_start_alternate(Visitor *v, const char *name, GenericAlternate **obj, size_t size, bool promote_int, Error **errp) { - QapiDeallocVisitor *qov = to_qov(v); - qapi_dealloc_push(qov, obj); } -static void qapi_dealloc_end_alternate(Visitor *v) +static void qapi_dealloc_end_alternate(Visitor *v, void **obj) { - QapiDeallocVisitor *qov = to_qov(v); - void **obj = qapi_dealloc_pop(qov); if (obj) { g_free(*obj); } @@ -102,7 +63,7 @@ static GenericList *qapi_dealloc_next_list(Visitor *v, GenericList *tail, return next; } -static void qapi_dealloc_end_list(Visitor *v) +static void qapi_dealloc_end_list(Visitor *v, void **obj) { } @@ -146,17 +107,12 @@ static void qapi_dealloc_type_null(Visitor *v, const char *name, Error **errp) { } -Visitor *qapi_dealloc_get_visitor(QapiDeallocVisitor *v) +static void qapi_dealloc_free(Visitor *v) { - return &v->visitor; + g_free(container_of(v, QapiDeallocVisitor, visitor)); } -void qapi_dealloc_visitor_cleanup(QapiDeallocVisitor *v) -{ - g_free(v); -} - -QapiDeallocVisitor *qapi_dealloc_visitor_new(void) +Visitor *qapi_dealloc_visitor_new(void) { QapiDeallocVisitor *v; @@ -177,8 +133,7 @@ QapiDeallocVisitor *qapi_dealloc_visitor_new(void) v->visitor.type_number = qapi_dealloc_type_number; v->visitor.type_any = qapi_dealloc_type_anything; v->visitor.type_null = qapi_dealloc_type_null; + v->visitor.free = qapi_dealloc_free; - QTAILQ_INIT(&v->stack); - - return v; + return &v->visitor; } diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c index eada4676a2..55f5876dc0 100644 --- a/qapi/qapi-visit-core.c +++ b/qapi/qapi-visit-core.c @@ -20,6 +20,21 @@ #include "qapi/visitor.h" #include "qapi/visitor-impl.h" +void visit_complete(Visitor *v, void *opaque) +{ + assert(v->type != VISITOR_OUTPUT || v->complete); + if (v->complete) { + v->complete(v, opaque); + } +} + +void visit_free(Visitor *v) +{ + if (v) { + v->free(v); + } +} + void visit_start_struct(Visitor *v, const char *name, void **obj, size_t size, Error **errp) { @@ -27,10 +42,10 @@ void visit_start_struct(Visitor *v, const char *name, void **obj, if (obj) { assert(size); - assert(v->type != VISITOR_OUTPUT || *obj); + assert(!(v->type & VISITOR_OUTPUT) || *obj); } v->start_struct(v, name, obj, size, &err); - if (obj && v->type == VISITOR_INPUT) { + if (obj && (v->type & VISITOR_INPUT)) { assert(!err != !*obj); } error_propagate(errp, err); @@ -43,9 +58,9 @@ void visit_check_struct(Visitor *v, Error **errp) } } -void visit_end_struct(Visitor *v) +void visit_end_struct(Visitor *v, void **obj) { - v->end_struct(v); + v->end_struct(v, obj); } void visit_start_list(Visitor *v, const char *name, GenericList **list, @@ -55,7 +70,7 @@ void visit_start_list(Visitor *v, const char *name, GenericList **list, assert(!list || size >= sizeof(GenericList)); v->start_list(v, name, list, size, &err); - if (list && v->type == VISITOR_INPUT) { + if (list && (v->type & VISITOR_INPUT)) { assert(!(err && *list)); } error_propagate(errp, err); @@ -67,9 +82,9 @@ GenericList *visit_next_list(Visitor *v, GenericList *tail, size_t size) return v->next_list(v, tail, size); } -void visit_end_list(Visitor *v) +void visit_end_list(Visitor *v, void **obj) { - v->end_list(v); + v->end_list(v, obj); } void visit_start_alternate(Visitor *v, const char *name, @@ -79,20 +94,20 @@ void visit_start_alternate(Visitor *v, const char *name, Error *err = NULL; assert(obj && size >= sizeof(GenericAlternate)); - assert(v->type != VISITOR_OUTPUT || *obj); + assert(!(v->type & VISITOR_OUTPUT) || *obj); if (v->start_alternate) { v->start_alternate(v, name, obj, size, promote_int, &err); } - if (v->type == VISITOR_INPUT) { + if (v->type & VISITOR_INPUT) { assert(v->start_alternate && !err != !*obj); } error_propagate(errp, err); } -void visit_end_alternate(Visitor *v) +void visit_end_alternate(Visitor *v, void **obj) { if (v->end_alternate) { - v->end_alternate(v); + v->end_alternate(v, obj); } } @@ -235,10 +250,10 @@ void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp) assert(obj); /* TODO: Fix callers to not pass NULL when they mean "", so that we * can enable: - assert(v->type != VISITOR_OUTPUT || *obj); + assert(!(v->type & VISITOR_OUTPUT) || *obj); */ v->type_str(v, name, obj, &err); - if (v->type == VISITOR_INPUT) { + if (v->type & VISITOR_INPUT) { assert(!err != !*obj); } error_propagate(errp, err); @@ -320,9 +335,19 @@ void visit_type_enum(Visitor *v, const char *name, int *obj, const char *const strings[], Error **errp) { assert(obj && strings); - if (v->type == VISITOR_INPUT) { + switch (v->type) { + case VISITOR_INPUT: input_type_enum(v, name, obj, strings, errp); - } else if (v->type == VISITOR_OUTPUT) { + break; + case VISITOR_OUTPUT: output_type_enum(v, name, obj, strings, errp); + break; + case VISITOR_CLONE: + /* nothing further to do, scalar value was already copied by + * g_memdup() during visit_start_*() */ + break; + case VISITOR_DEALLOC: + /* nothing to deallocate for a scalar */ + break; } } diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 08faf853ac..505eb418ac 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -16,6 +16,7 @@ #include "qapi/qmp/types.h" #include "qapi/qmp/dispatch.h" #include "qapi/qmp/json-parser.h" +#include "qapi/qmp/qjson.h" #include "qapi-types.h" #include "qapi/qmp/qerror.h" diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c index aea90a1378..21edb3928e 100644 --- a/qapi/qmp-input-visitor.c +++ b/qapi/qmp-input-visitor.c @@ -26,6 +26,7 @@ typedef struct StackObject { QObject *obj; /* Object being visited */ + void *qapi; /* sanity check that caller uses same pointer */ GHashTable *h; /* If obj is dict: unvisited keys */ const QListEntry *entry; /* If obj is list: unvisited tail */ @@ -96,7 +97,7 @@ static void qdict_add_key(const char *key, QObject *obj, void *opaque) } static const QListEntry *qmp_input_push(QmpInputVisitor *qiv, QObject *obj, - Error **errp) + void *qapi, Error **errp) { GHashTable *h; StackObject *tos = &qiv->stack[qiv->nb_stack]; @@ -108,6 +109,7 @@ static const QListEntry *qmp_input_push(QmpInputVisitor *qiv, QObject *obj, } tos->obj = obj; + tos->qapi = qapi; assert(!tos->h); assert(!tos->entry); @@ -145,12 +147,13 @@ static void qmp_input_check_struct(Visitor *v, Error **errp) } } -static void qmp_input_pop(Visitor *v) +static void qmp_input_pop(Visitor *v, void **obj) { QmpInputVisitor *qiv = to_qiv(v); StackObject *tos = &qiv->stack[qiv->nb_stack - 1]; assert(qiv->nb_stack > 0); + assert(tos->qapi == obj); if (qiv->strict) { GHashTable * const top_ht = qiv->stack[qiv->nb_stack - 1].h; @@ -179,7 +182,7 @@ static void qmp_input_start_struct(Visitor *v, const char *name, void **obj, return; } - qmp_input_push(qiv, qobj, &err); + qmp_input_push(qiv, qobj, obj, &err); if (err) { error_propagate(errp, err); return; @@ -207,7 +210,7 @@ static void qmp_input_start_list(Visitor *v, const char *name, return; } - entry = qmp_input_push(qiv, qobj, errp); + entry = qmp_input_push(qiv, qobj, list, errp); if (list) { if (entry) { *list = g_malloc0(size); @@ -370,18 +373,15 @@ static void qmp_input_optional(Visitor *v, const char *name, bool *present) *present = true; } -Visitor *qmp_input_get_visitor(QmpInputVisitor *v) +static void qmp_input_free(Visitor *v) { - return &v->visitor; -} + QmpInputVisitor *qiv = to_qiv(v); -void qmp_input_visitor_cleanup(QmpInputVisitor *v) -{ - qobject_decref(v->root); - g_free(v); + qobject_decref(qiv->root); + g_free(qiv); } -QmpInputVisitor *qmp_input_visitor_new(QObject *obj, bool strict) +Visitor *qmp_input_visitor_new(QObject *obj, bool strict) { QmpInputVisitor *v; @@ -403,10 +403,11 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj, bool strict) v->visitor.type_any = qmp_input_type_any; v->visitor.type_null = qmp_input_type_null; v->visitor.optional = qmp_input_optional; + v->visitor.free = qmp_input_free; v->strict = strict; v->root = obj; qobject_incref(obj); - return v; + return &v->visitor; } diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c index 4d3cf78333..0452056d42 100644 --- a/qapi/qmp-output-visitor.c +++ b/qapi/qmp-output-visitor.c @@ -22,6 +22,7 @@ typedef struct QStackEntry { QObject *value; + void *qapi; /* sanity check that caller uses same pointer */ QTAILQ_ENTRY(QStackEntry) node; } QStackEntry; @@ -32,11 +33,13 @@ struct QmpOutputVisitor Visitor visitor; QStack stack; /* Stack of containers that haven't yet been finished */ QObject *root; /* Root of the output visit */ + QObject **result; /* User's storage location for result */ }; #define qmp_output_add(qov, name, value) \ qmp_output_add_obj(qov, name, QOBJECT(value)) -#define qmp_output_push(qov, value) qmp_output_push_obj(qov, QOBJECT(value)) +#define qmp_output_push(qov, value, qapi) \ + qmp_output_push_obj(qov, QOBJECT(value), qapi) static QmpOutputVisitor *to_qov(Visitor *v) { @@ -44,23 +47,26 @@ static QmpOutputVisitor *to_qov(Visitor *v) } /* Push @value onto the stack of current QObjects being built */ -static void qmp_output_push_obj(QmpOutputVisitor *qov, QObject *value) +static void qmp_output_push_obj(QmpOutputVisitor *qov, QObject *value, + void *qapi) { QStackEntry *e = g_malloc0(sizeof(*e)); assert(qov->root); assert(value); e->value = value; + e->qapi = qapi; QTAILQ_INSERT_HEAD(&qov->stack, e, node); } /* Pop a value off the stack of QObjects being built, and return it. */ -static QObject *qmp_output_pop(QmpOutputVisitor *qov) +static QObject *qmp_output_pop(QmpOutputVisitor *qov, void *qapi) { QStackEntry *e = QTAILQ_FIRST(&qov->stack); QObject *value; assert(e); + assert(e->qapi == qapi); QTAILQ_REMOVE(&qov->stack, e, node); value = e->value; assert(value); @@ -104,13 +110,13 @@ static void qmp_output_start_struct(Visitor *v, const char *name, void **obj, QDict *dict = qdict_new(); qmp_output_add(qov, name, dict); - qmp_output_push(qov, dict); + qmp_output_push(qov, dict, obj); } -static void qmp_output_end_struct(Visitor *v) +static void qmp_output_end_struct(Visitor *v, void **obj) { QmpOutputVisitor *qov = to_qov(v); - QObject *value = qmp_output_pop(qov); + QObject *value = qmp_output_pop(qov, obj); assert(qobject_type(value) == QTYPE_QDICT); } @@ -122,7 +128,7 @@ static void qmp_output_start_list(Visitor *v, const char *name, QList *list = qlist_new(); qmp_output_add(qov, name, list); - qmp_output_push(qov, list); + qmp_output_push(qov, list, listp); } static GenericList *qmp_output_next_list(Visitor *v, GenericList *tail, @@ -131,10 +137,10 @@ static GenericList *qmp_output_next_list(Visitor *v, GenericList *tail, return tail->next; } -static void qmp_output_end_list(Visitor *v) +static void qmp_output_end_list(Visitor *v, void **obj) { QmpOutputVisitor *qov = to_qov(v); - QObject *value = qmp_output_pop(qov); + QObject *value = qmp_output_pop(qov, obj); assert(qobject_type(value) == QTYPE_QLIST); } @@ -195,34 +201,34 @@ static void qmp_output_type_null(Visitor *v, const char *name, Error **errp) /* Finish building, and return the root object. * The root object is never null. The caller becomes the object's * owner, and should use qobject_decref() when done with it. */ -QObject *qmp_output_get_qobject(QmpOutputVisitor *qov) +static void qmp_output_complete(Visitor *v, void *opaque) { + QmpOutputVisitor *qov = to_qov(v); + /* A visit must have occurred, with each start paired with end. */ assert(qov->root && QTAILQ_EMPTY(&qov->stack)); + assert(opaque == qov->result); qobject_incref(qov->root); - return qov->root; -} - -Visitor *qmp_output_get_visitor(QmpOutputVisitor *v) -{ - return &v->visitor; + *qov->result = qov->root; + qov->result = NULL; } -void qmp_output_visitor_cleanup(QmpOutputVisitor *v) +static void qmp_output_free(Visitor *v) { + QmpOutputVisitor *qov = to_qov(v); QStackEntry *e, *tmp; - QTAILQ_FOREACH_SAFE(e, &v->stack, node, tmp) { - QTAILQ_REMOVE(&v->stack, e, node); + QTAILQ_FOREACH_SAFE(e, &qov->stack, node, tmp) { + QTAILQ_REMOVE(&qov->stack, e, node); g_free(e); } - qobject_decref(v->root); - g_free(v); + qobject_decref(qov->root); + g_free(qov); } -QmpOutputVisitor *qmp_output_visitor_new(void) +Visitor *qmp_output_visitor_new(QObject **result) { QmpOutputVisitor *v; @@ -241,8 +247,12 @@ QmpOutputVisitor *qmp_output_visitor_new(void) v->visitor.type_number = qmp_output_type_number; v->visitor.type_any = qmp_output_type_any; v->visitor.type_null = qmp_output_type_null; + v->visitor.complete = qmp_output_complete; + v->visitor.free = qmp_output_free; QTAILQ_INIT(&v->stack); + *result = NULL; + v->result = result; - return v; + return &v->visitor; } diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c index b546e5f76a..8dfa561252 100644 --- a/qapi/string-input-visitor.c +++ b/qapi/string-input-visitor.c @@ -30,6 +30,7 @@ struct StringInputVisitor int64_t cur; const char *string; + void *list; /* Only needed for sanity checking the caller */ }; static StringInputVisitor *to_siv(Visitor *v) @@ -59,8 +60,7 @@ static int parse_str(StringInputVisitor *siv, const char *name, Error **errp) if (errno == 0 && endptr > str) { if (*endptr == '\0') { cur = g_malloc0(sizeof(*cur)); - cur->begin = start; - cur->end = start + 1; + range_set_bounds(cur, start, start); siv->ranges = range_list_insert(siv->ranges, cur); cur = NULL; str = NULL; @@ -73,16 +73,14 @@ static int parse_str(StringInputVisitor *siv, const char *name, Error **errp) end < start + 65536)) { if (*endptr == '\0') { cur = g_malloc0(sizeof(*cur)); - cur->begin = start; - cur->end = end + 1; + range_set_bounds(cur, start, end); siv->ranges = range_list_insert(siv->ranges, cur); cur = NULL; str = NULL; } else if (*endptr == ',') { str = endptr + 1; cur = g_malloc0(sizeof(*cur)); - cur->begin = start; - cur->end = end + 1; + range_set_bounds(cur, start, end); siv->ranges = range_list_insert(siv->ranges, cur); cur = NULL; } else { @@ -94,8 +92,7 @@ static int parse_str(StringInputVisitor *siv, const char *name, Error **errp) } else if (*endptr == ',') { str = endptr + 1; cur = g_malloc0(sizeof(*cur)); - cur->begin = start; - cur->end = start + 1; + range_set_bounds(cur, start, start); siv->ranges = range_list_insert(siv->ranges, cur); cur = NULL; } else { @@ -124,6 +121,7 @@ start_list(Visitor *v, const char *name, GenericList **list, size_t size, /* We don't support visits without a list */ assert(list); + siv->list = list; if (parse_str(siv, name, errp) < 0) { *list = NULL; @@ -134,7 +132,7 @@ start_list(Visitor *v, const char *name, GenericList **list, size_t size, if (siv->cur_range) { Range *r = siv->cur_range->data; if (r) { - siv->cur = r->begin; + siv->cur = range_lob(r); } *list = g_malloc0(size); } else { @@ -156,7 +154,7 @@ static GenericList *next_list(Visitor *v, GenericList *tail, size_t size) return NULL; } - if (siv->cur < r->begin || siv->cur >= r->end) { + if (!range_contains(r, siv->cur)) { siv->cur_range = g_list_next(siv->cur_range); if (!siv->cur_range) { return NULL; @@ -165,15 +163,18 @@ static GenericList *next_list(Visitor *v, GenericList *tail, size_t size) if (!r) { return NULL; } - siv->cur = r->begin; + siv->cur = range_lob(r); } tail->next = g_malloc0(size); return tail->next; } -static void end_list(Visitor *v) +static void end_list(Visitor *v, void **obj) { + StringInputVisitor *siv = to_siv(v); + + assert(siv->list == obj); } static void parse_type_int64(Visitor *v, const char *name, int64_t *obj, @@ -208,7 +209,7 @@ static void parse_type_int64(Visitor *v, const char *name, int64_t *obj, goto error; } - siv->cur = r->begin; + siv->cur = range_lob(r); } *obj = siv->cur; @@ -325,19 +326,16 @@ static void parse_optional(Visitor *v, const char *name, bool *present) *present = true; } -Visitor *string_input_get_visitor(StringInputVisitor *v) +static void string_input_free(Visitor *v) { - return &v->visitor; -} + StringInputVisitor *siv = to_siv(v); -void string_input_visitor_cleanup(StringInputVisitor *v) -{ - g_list_foreach(v->ranges, free_range, NULL); - g_list_free(v->ranges); - g_free(v); + g_list_foreach(siv->ranges, free_range, NULL); + g_list_free(siv->ranges); + g_free(siv); } -StringInputVisitor *string_input_visitor_new(const char *str) +Visitor *string_input_visitor_new(const char *str) { StringInputVisitor *v; @@ -354,7 +352,8 @@ StringInputVisitor *string_input_visitor_new(const char *str) v->visitor.next_list = next_list; v->visitor.end_list = end_list; v->visitor.optional = parse_optional; + v->visitor.free = string_input_free; v->string = str; - return v; + return &v->visitor; } diff --git a/qapi/string-output-visitor.c b/qapi/string-output-visitor.c index 5ea395ab98..94ac8211d1 100644 --- a/qapi/string-output-visitor.c +++ b/qapi/string-output-visitor.c @@ -58,12 +58,14 @@ struct StringOutputVisitor Visitor visitor; bool human; GString *string; + char **result; ListMode list_mode; union { int64_t s; uint64_t u; } range_start, range_end; GList *ranges; + void *list; /* Only needed for sanity checking the caller */ }; static StringOutputVisitor *to_sov(Visitor *v) @@ -83,8 +85,8 @@ static void string_output_set(StringOutputVisitor *sov, char *string) static void string_output_append(StringOutputVisitor *sov, int64_t a) { Range *r = g_malloc0(sizeof(*r)); - r->begin = a; - r->end = a + 1; + + range_set_bounds(r, a, a); sov->ranges = range_list_insert(sov->ranges, r); } @@ -92,28 +94,28 @@ static void string_output_append_range(StringOutputVisitor *sov, int64_t s, int64_t e) { Range *r = g_malloc0(sizeof(*r)); - r->begin = s; - r->end = e + 1; + + range_set_bounds(r, s, e); sov->ranges = range_list_insert(sov->ranges, r); } static void format_string(StringOutputVisitor *sov, Range *r, bool next, bool human) { - if (r->end - r->begin > 1) { + if (range_lob(r) != range_upb(r)) { if (human) { g_string_append_printf(sov->string, "0x%" PRIx64 "-0x%" PRIx64, - r->begin, r->end - 1); + range_lob(r), range_upb(r)); } else { g_string_append_printf(sov->string, "%" PRId64 "-%" PRId64, - r->begin, r->end - 1); + range_lob(r), range_upb(r)); } } else { if (human) { - g_string_append_printf(sov->string, "0x%" PRIx64, r->begin); + g_string_append_printf(sov->string, "0x%" PRIx64, range_lob(r)); } else { - g_string_append_printf(sov->string, "%" PRId64, r->begin); + g_string_append_printf(sov->string, "%" PRId64, range_lob(r)); } } if (next) { @@ -274,6 +276,7 @@ start_list(Visitor *v, const char *name, GenericList **list, size_t size, assert(sov->list_mode == LM_NONE); /* We don't support visits without a list */ assert(list); + sov->list = list; /* List handling is only needed if there are at least two elements */ if (*list && (*list)->next) { sov->list_mode = LM_STARTED; @@ -291,10 +294,11 @@ static GenericList *next_list(Visitor *v, GenericList *tail, size_t size) return ret; } -static void end_list(Visitor *v) +static void end_list(Visitor *v, void **obj) { StringOutputVisitor *sov = to_sov(v); + assert(sov->list == obj); assert(sov->list_mode == LM_STARTED || sov->list_mode == LM_END || sov->list_mode == LM_NONE || @@ -302,16 +306,13 @@ static void end_list(Visitor *v) sov->list_mode = LM_NONE; } -char *string_output_get_string(StringOutputVisitor *sov) +static void string_output_complete(Visitor *v, void *opaque) { - char *string = g_string_free(sov->string, false); - sov->string = NULL; - return string; -} + StringOutputVisitor *sov = to_sov(v); -Visitor *string_output_get_visitor(StringOutputVisitor *sov) -{ - return &sov->visitor; + assert(opaque == sov->result); + *sov->result = g_string_free(sov->string, false); + sov->string = NULL; } static void free_range(void *range, void *dummy) @@ -319,8 +320,10 @@ static void free_range(void *range, void *dummy) g_free(range); } -void string_output_visitor_cleanup(StringOutputVisitor *sov) +static void string_output_free(Visitor *v) { + StringOutputVisitor *sov = to_sov(v); + if (sov->string) { g_string_free(sov->string, true); } @@ -330,7 +333,7 @@ void string_output_visitor_cleanup(StringOutputVisitor *sov) g_free(sov); } -StringOutputVisitor *string_output_visitor_new(bool human) +Visitor *string_output_visitor_new(bool human, char **result) { StringOutputVisitor *v; @@ -338,6 +341,9 @@ StringOutputVisitor *string_output_visitor_new(bool human) v->string = g_string_new(NULL); v->human = human; + v->result = result; + *result = NULL; + v->visitor.type = VISITOR_OUTPUT; v->visitor.type_int64 = print_type_int64; v->visitor.type_uint64 = print_type_uint64; @@ -348,6 +354,8 @@ StringOutputVisitor *string_output_visitor_new(bool human) v->visitor.start_list = start_list; v->visitor.next_list = next_list; v->visitor.end_list = end_list; + v->visitor.complete = string_output_complete; + v->visitor.free = string_output_free; - return v; + return &v->visitor; } diff --git a/qemu-char.c b/qemu-char.c index b73969ddbd..0698b98750 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -32,8 +32,7 @@ #include "sysemu/char.h" #include "hw/usb.h" #include "qmp-commands.h" -#include "qapi/qmp-input-visitor.h" -#include "qapi/qmp-output-visitor.h" +#include "qapi/clone-visitor.h" #include "qapi-visit.h" #include "qemu/base64.h" #include "io/channel-socket.h" @@ -4389,7 +4388,7 @@ static CharDriverState *qmp_chardev_open_socket(const char *id, } } - qapi_copy_SocketAddress(&s->addr, sock->addr); + s->addr = QAPI_CLONE(SocketAddress, sock->addr); chr->opaque = s; chr->chr_write = tcp_chr_write; diff --git a/qemu-img.c b/qemu-img.c index 3322a1e5fc..ea5970bdd3 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -490,18 +490,17 @@ fail: static void dump_json_image_check(ImageCheck *check, bool quiet) { - Error *local_err = NULL; QString *str; - QmpOutputVisitor *ov = qmp_output_visitor_new(); QObject *obj; - visit_type_ImageCheck(qmp_output_get_visitor(ov), NULL, &check, - &local_err); - obj = qmp_output_get_qobject(ov); + Visitor *v = qmp_output_visitor_new(&obj); + + visit_type_ImageCheck(v, NULL, &check, &error_abort); + visit_complete(v, &obj); str = qobject_to_json_pretty(obj); assert(str != NULL); qprintf(quiet, "%s\n", qstring_get_str(str)); qobject_decref(obj); - qmp_output_visitor_cleanup(ov); + visit_free(v); QDECREF(str); } @@ -1648,7 +1647,7 @@ static int convert_do_copy(ImgConvertState *s) if (!s->has_zero_init && !s->target_has_backing && bdrv_can_write_zeroes_with_unmap(blk_bs(s->target))) { - ret = bdrv_make_zero(blk_bs(s->target), BDRV_REQ_MAY_UNMAP); + ret = blk_make_zero(s->target, BDRV_REQ_MAY_UNMAP); if (ret == 0) { s->has_zero_init = true; } @@ -2085,13 +2084,14 @@ static int img_convert(int argc, char **argv) } out_bs = blk_bs(out_blk); - /* increase bufsectors from the default 4096 (2M) if opt_transfer_length + /* increase bufsectors from the default 4096 (2M) if opt_transfer * or discard_alignment of the out_bs is greater. Limit to 32768 (16MB) * as maximum. */ bufsectors = MIN(32768, - MAX(bufsectors, MAX(out_bs->bl.opt_transfer_length, - out_bs->bl.discard_alignment)) - ); + MAX(bufsectors, + MAX(out_bs->bl.opt_transfer >> BDRV_SECTOR_BITS, + out_bs->bl.pdiscard_alignment >> + BDRV_SECTOR_BITS))); if (skip_create) { int64_t output_sectors = blk_nb_sectors(out_blk); @@ -2181,34 +2181,33 @@ static void dump_snapshots(BlockDriverState *bs) static void dump_json_image_info_list(ImageInfoList *list) { - Error *local_err = NULL; QString *str; - QmpOutputVisitor *ov = qmp_output_visitor_new(); QObject *obj; - visit_type_ImageInfoList(qmp_output_get_visitor(ov), NULL, &list, - &local_err); - obj = qmp_output_get_qobject(ov); + Visitor *v = qmp_output_visitor_new(&obj); + + visit_type_ImageInfoList(v, NULL, &list, &error_abort); + visit_complete(v, &obj); str = qobject_to_json_pretty(obj); assert(str != NULL); printf("%s\n", qstring_get_str(str)); qobject_decref(obj); - qmp_output_visitor_cleanup(ov); + visit_free(v); QDECREF(str); } static void dump_json_image_info(ImageInfo *info) { - Error *local_err = NULL; QString *str; - QmpOutputVisitor *ov = qmp_output_visitor_new(); QObject *obj; - visit_type_ImageInfo(qmp_output_get_visitor(ov), NULL, &info, &local_err); - obj = qmp_output_get_qobject(ov); + Visitor *v = qmp_output_visitor_new(&obj); + + visit_type_ImageInfo(v, NULL, &info, &error_abort); + visit_complete(v, &obj); str = qobject_to_json_pretty(obj); assert(str != NULL); printf("%s\n", qstring_get_str(str)); qobject_decref(obj); - qmp_output_visitor_cleanup(ov); + visit_free(v); QDECREF(str); } @@ -3866,7 +3865,7 @@ int main(int argc, char **argv) return 0; } argv += optind; - optind = 1; + optind = 0; if (!trace_init_backends()) { exit(1); diff --git a/qemu-options.hx b/qemu-options.hx index a95a936e55..8e0d9a51bf 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -38,7 +38,6 @@ DEF("machine", HAS_ARG, QEMU_OPTION_machine, \ " kvm_shadow_mem=size of KVM shadow MMU in bytes\n" " dump-guest-core=on|off include guest memory in a core dump (default=on)\n" " mem-merge=on|off controls memory merge support (default: on)\n" - " iommu=on|off controls emulated Intel IOMMU (VT-d) support (default=off)\n" " igd-passthru=on|off controls IGD GFX passthrough support (default=off)\n" " aes-key-wrap=on|off controls support for AES key wrapping (default=on)\n" " dea-key-wrap=on|off controls support for DEA key wrapping (default=on)\n" @@ -73,8 +72,6 @@ Include guest memory in a core dump. The default is on. Enables or disables memory merge support. This feature, when supported by the host, de-duplicates identical memory pages among VMs instances (enabled by default). -@item iommu=on|off -Enables or disables emulated Intel IOMMU (VT-d) support. The default is off. @item aes-key-wrap=on|off Enables or disables AES key wrapping support on s390-ccw hosts. This feature controls whether AES wrapping keys will be created to allow @@ -1584,6 +1581,7 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, "-netdev tap,id=str[,fd=h][,fds=x:y:...:z][,ifname=name][,script=file][,downscript=dfile]\n" " [,helper=helper][,sndbuf=nbytes][,vnet_hdr=on|off][,vhost=on|off]\n" " [,vhostfd=h][,vhostfds=x:y:...:z][,vhostforce=on|off][,queues=n]\n" + " [,poll-us=n]\n" " configure a host TAP network backend with ID 'str'\n" " use network scripts 'file' (default=" DEFAULT_NETWORK_SCRIPT ")\n" " to configure it and 'dfile' (default=" DEFAULT_NETWORK_DOWN_SCRIPT ")\n" @@ -1603,6 +1601,8 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, " use 'vhostfd=h' to connect to an already opened vhost net device\n" " use 'vhostfds=x:y:...:z to connect to multiple already opened vhost net devices\n" " use 'queues=n' to specify the number of queues to be created for multiqueue TAP\n" + " use 'poll-us=n' to speciy the maximum number of microseconds that could be\n" + " spent on busy polling for vhost net\n" "-netdev bridge,id=str[,br=bridge][,helper=helper]\n" " configure a host TAP network backend with ID 'str' that is\n" " connected to a bridge (default=" DEFAULT_BRIDGE_INTERFACE ")\n" @@ -655,7 +655,7 @@ void qmp_object_add(const char *type, const char *id, bool has_props, QObject *props, Error **errp) { const QDict *pdict = NULL; - QmpInputVisitor *qiv; + Visitor *v; Object *obj; if (props) { @@ -666,10 +666,9 @@ void qmp_object_add(const char *type, const char *id, } } - qiv = qmp_input_visitor_new(props, true); - obj = user_creatable_add_type(type, id, pdict, - qmp_input_get_visitor(qiv), errp); - qmp_input_visitor_cleanup(qiv); + v = qmp_input_visitor_new(props, true); + obj = user_creatable_add_type(type, id, pdict, v, errp); + visit_free(v); if (obj) { object_unref(obj); } diff --git a/qobject/json-parser.c b/qobject/json-parser.c index 67ed727318..c18e48ab94 100644 --- a/qobject/json-parser.c +++ b/qobject/json-parser.c @@ -14,12 +14,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu-common.h" -#include "qapi/qmp/qstring.h" -#include "qapi/qmp/qint.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qfloat.h" -#include "qapi/qmp/qbool.h" +#include "qapi/qmp/types.h" #include "qapi/qmp/json-parser.h" #include "qapi/qmp/json-lexer.h" #include "qapi/qmp/json-streamer.h" diff --git a/qobject/qjson.c b/qobject/qjson.c index ef160d2119..9a0de89079 100644 --- a/qobject/qjson.c +++ b/qobject/qjson.c @@ -16,11 +16,7 @@ #include "qapi/qmp/json-parser.h" #include "qapi/qmp/json-streamer.h" #include "qapi/qmp/qjson.h" -#include "qapi/qmp/qint.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qfloat.h" -#include "qapi/qmp/qdict.h" +#include "qapi/qmp/types.h" #include "qemu/unicode.h" typedef struct JSONParsingState diff --git a/qobject/qobject.c b/qobject/qobject.c index cd41fb940b..fe4fa10989 100644 --- a/qobject/qobject.c +++ b/qobject/qobject.c @@ -9,12 +9,7 @@ #include "qemu/osdep.h" #include "qemu-common.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qfloat.h" -#include "qapi/qmp/qint.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qstring.h" +#include "qapi/qmp/types.h" static void (*qdestroy[QTYPE__MAX])(QObject *) = { [QTYPE_NONE] = NULL, /* No such object exists */ @@ -28,6 +28,7 @@ #include "exec/log.h" #include "qemu/error-report.h" #include "sysemu/sysemu.h" +#include "hw/qdev-properties.h" bool cpu_exists(int64_t id) { @@ -46,7 +47,7 @@ bool cpu_exists(int64_t id) CPUState *cpu_generic_init(const char *typename, const char *cpu_model) { char *str, *name, *featurestr; - CPUState *cpu; + CPUState *cpu = NULL; ObjectClass *oc; CPUClass *cc; Error *err = NULL; @@ -60,16 +61,18 @@ CPUState *cpu_generic_init(const char *typename, const char *cpu_model) return NULL; } - cpu = CPU(object_new(object_class_get_name(oc))); - cc = CPU_GET_CLASS(cpu); - + cc = CPU_CLASS(oc); featurestr = strtok(NULL, ","); - cc->parse_features(cpu, featurestr, &err); + /* TODO: all callers of cpu_generic_init() need to be converted to + * call parse_features() only once, before calling cpu_generic_init(). + */ + cc->parse_features(object_class_get_name(oc), featurestr, &err); g_free(str); if (err != NULL) { goto out; } + cpu = CPU(object_new(object_class_get_name(oc))); object_property_set_bool(OBJECT(cpu), true, "realized", &err); out: @@ -282,25 +285,37 @@ static ObjectClass *cpu_common_class_by_name(const char *cpu_model) return NULL; } -static void cpu_common_parse_features(CPUState *cpu, char *features, +static void cpu_common_parse_features(const char *typename, char *features, Error **errp) { char *featurestr; /* Single "key=value" string being parsed */ char *val; - Error *err = NULL; + static bool cpu_globals_initialized; + + /* TODO: all callers of ->parse_features() need to be changed to + * call it only once, so we can remove this check (or change it + * to assert(!cpu_globals_initialized). + * Current callers of ->parse_features() are: + * - cpu_generic_init() + */ + if (cpu_globals_initialized) { + return; + } + cpu_globals_initialized = true; featurestr = features ? strtok(features, ",") : NULL; while (featurestr) { val = strchr(featurestr, '='); if (val) { + GlobalProperty *prop = g_new0(typeof(*prop), 1); *val = 0; val++; - object_property_parse(OBJECT(cpu), val, featurestr, &err); - if (err) { - error_propagate(errp, err); - return; - } + prop->driver = typename; + prop->property = g_strdup(featurestr); + prop->value = g_strdup(val); + prop->errp = &error_fatal; + qdev_prop_register_global(prop); } else { error_setg(errp, "Expected key=value format, found %s.", featurestr); diff --git a/qom/object.c b/qom/object.c index 9743ea4708..8166b7dace 100644 --- a/qom/object.c +++ b/qom/object.c @@ -1221,8 +1221,7 @@ int object_property_get_enum(Object *obj, const char *name, const char *typename, Error **errp) { Error *err = NULL; - StringOutputVisitor *sov; - StringInputVisitor *siv; + Visitor *v; char *str; int ret; ObjectProperty *prop = object_property_find(obj, name, errp); @@ -1241,21 +1240,20 @@ int object_property_get_enum(Object *obj, const char *name, enumprop = prop->opaque; - sov = string_output_visitor_new(false); - object_property_get(obj, string_output_get_visitor(sov), name, &err); + v = string_output_visitor_new(false, &str); + object_property_get(obj, v, name, &err); if (err) { error_propagate(errp, err); - string_output_visitor_cleanup(sov); + visit_free(v); return 0; } - str = string_output_get_string(sov); - siv = string_input_visitor_new(str); - string_output_visitor_cleanup(sov); - visit_type_enum(string_input_get_visitor(siv), name, &ret, - enumprop->strings, errp); + visit_complete(v, &str); + visit_free(v); + v = string_input_visitor_new(str); + visit_type_enum(v, name, &ret, enumprop->strings, errp); g_free(str); - string_input_visitor_cleanup(siv); + visit_free(v); return ret; } @@ -1264,55 +1262,51 @@ void object_property_get_uint16List(Object *obj, const char *name, uint16List **list, Error **errp) { Error *err = NULL; - StringOutputVisitor *ov; - StringInputVisitor *iv; + Visitor *v; char *str; - ov = string_output_visitor_new(false); - object_property_get(obj, string_output_get_visitor(ov), - name, &err); + v = string_output_visitor_new(false, &str); + object_property_get(obj, v, name, &err); if (err) { error_propagate(errp, err); goto out; } - str = string_output_get_string(ov); - iv = string_input_visitor_new(str); - visit_type_uint16List(string_input_get_visitor(iv), NULL, list, errp); + visit_complete(v, &str); + visit_free(v); + v = string_input_visitor_new(str); + visit_type_uint16List(v, NULL, list, errp); g_free(str); - string_input_visitor_cleanup(iv); out: - string_output_visitor_cleanup(ov); + visit_free(v); } void object_property_parse(Object *obj, const char *string, const char *name, Error **errp) { - StringInputVisitor *siv; - siv = string_input_visitor_new(string); - object_property_set(obj, string_input_get_visitor(siv), name, errp); - - string_input_visitor_cleanup(siv); + Visitor *v = string_input_visitor_new(string); + object_property_set(obj, v, name, errp); + visit_free(v); } char *object_property_print(Object *obj, const char *name, bool human, Error **errp) { - StringOutputVisitor *sov; + Visitor *v; char *string = NULL; Error *local_err = NULL; - sov = string_output_visitor_new(human); - object_property_get(obj, string_output_get_visitor(sov), name, &local_err); + v = string_output_visitor_new(human, &string); + object_property_get(obj, v, name, &local_err); if (local_err) { error_propagate(errp, local_err); goto out; } - string = string_output_get_string(sov); + visit_complete(v, &string); out: - string_output_visitor_cleanup(sov); + visit_free(v); return string; } @@ -2044,7 +2038,7 @@ static void property_get_tm(Object *obj, Visitor *v, const char *name, } visit_check_struct(v, &err); out_end: - visit_end_struct(v); + visit_end_struct(v, NULL); out: error_propagate(errp, err); diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c index 51e62e29d6..bf598468ab 100644 --- a/qom/object_interfaces.c +++ b/qom/object_interfaces.c @@ -71,7 +71,7 @@ Object *user_creatable_add(const QDict *qdict, obj = user_creatable_add_type(type, id, pdict, v, &local_err); out_visit: - visit_end_struct(v); + visit_end_struct(v, NULL); out: QDECREF(pdict); @@ -127,7 +127,7 @@ Object *user_creatable_add_type(const char *type, const char *id, if (!local_err) { visit_check_struct(v, &local_err); } - visit_end_struct(v); + visit_end_struct(v, NULL); if (local_err) { goto out; } @@ -156,15 +156,15 @@ out: Object *user_creatable_add_opts(QemuOpts *opts, Error **errp) { - OptsVisitor *ov; + Visitor *v; QDict *pdict; Object *obj = NULL; - ov = opts_visitor_new(opts); + v = opts_visitor_new(opts); pdict = qemu_opts_to_qdict(opts, NULL); - obj = user_creatable_add(pdict, opts_get_visitor(ov), errp); - opts_visitor_cleanup(ov); + obj = user_creatable_add(pdict, v, errp); + visit_free(v); QDECREF(pdict); return obj; } diff --git a/qom/qom-qobject.c b/qom/qom-qobject.c index b66088d730..c225abcbad 100644 --- a/qom/qom-qobject.c +++ b/qom/qom-qobject.c @@ -21,12 +21,11 @@ void object_property_set_qobject(Object *obj, QObject *value, const char *name, Error **errp) { - QmpInputVisitor *qiv; + Visitor *v; /* TODO: Should we reject, rather than ignore, excess input? */ - qiv = qmp_input_visitor_new(value, false); - object_property_set(obj, qmp_input_get_visitor(qiv), name, errp); - - qmp_input_visitor_cleanup(qiv); + v = qmp_input_visitor_new(value, false); + object_property_set(obj, v, name, errp); + visit_free(v); } QObject *object_property_get_qobject(Object *obj, const char *name, @@ -34,14 +33,14 @@ QObject *object_property_get_qobject(Object *obj, const char *name, { QObject *ret = NULL; Error *local_err = NULL; - QmpOutputVisitor *qov; + Visitor *v; - qov = qmp_output_visitor_new(); - object_property_get(obj, qmp_output_get_visitor(qov), name, &local_err); + v = qmp_output_visitor_new(&ret); + object_property_get(obj, v, name, &local_err); if (!local_err) { - ret = qmp_output_get_qobject(qov); + visit_complete(v, &ret); } error_propagate(errp, local_err); - qmp_output_visitor_cleanup(qov); + visit_free(v); return ret; } diff --git a/replay/replay-input.c b/replay/replay-input.c index 03e99d5aba..bd93554d8e 100644 --- a/replay/replay-input.c +++ b/replay/replay-input.c @@ -16,35 +16,7 @@ #include "replay-internal.h" #include "qemu/notify.h" #include "ui/input.h" -#include "qapi/qmp-output-visitor.h" -#include "qapi/qmp-input-visitor.h" -#include "qapi-visit.h" - -static InputEvent *qapi_clone_InputEvent(InputEvent *src) -{ - QmpOutputVisitor *qov; - QmpInputVisitor *qiv; - Visitor *ov, *iv; - QObject *obj; - InputEvent *dst = NULL; - - qov = qmp_output_visitor_new(); - ov = qmp_output_get_visitor(qov); - visit_type_InputEvent(ov, NULL, &src, &error_abort); - obj = qmp_output_get_qobject(qov); - qmp_output_visitor_cleanup(qov); - if (!obj) { - return NULL; - } - - qiv = qmp_input_visitor_new(obj, true); - iv = qmp_input_get_visitor(qiv); - visit_type_InputEvent(iv, NULL, &dst, &error_abort); - qmp_input_visitor_cleanup(qiv); - qobject_decref(obj); - - return dst; -} +#include "qapi/clone-visitor.h" void replay_save_input_event(InputEvent *evt) { @@ -143,7 +115,7 @@ InputEvent *replay_read_input_event(void) break; } - return qapi_clone_InputEvent(&evt); + return QAPI_CLONE(InputEvent, &evt); } void replay_input_event(QemuConsole *src, InputEvent *evt) @@ -151,7 +123,7 @@ void replay_input_event(QemuConsole *src, InputEvent *evt) if (replay_mode == REPLAY_MODE_PLAY) { /* Nothing */ } else if (replay_mode == REPLAY_MODE_RECORD) { - replay_add_input_event(qapi_clone_InputEvent(evt)); + replay_add_input_event(QAPI_CLONE(InputEvent, evt)); } else { qemu_input_event_send_impl(src, evt); } diff --git a/roms/Makefile b/roms/Makefile index 7bd1252737..88b3709d4d 100644 --- a/roms/Makefile +++ b/roms/Makefile @@ -1,11 +1,13 @@ vgabios_variants := stdvga cirrus vmware qxl isavga virtio vgabios_targets := $(subst -isavga,,$(patsubst %,vgabios-%.bin,$(vgabios_variants))) -pxerom_variants := e1000 eepro100 ne2k_pci pcnet rtl8139 virtio -pxerom_targets := 8086100e 80861209 10500940 10222000 10ec8139 1af41000 +pxerom_variants := e1000 e1000e eepro100 ne2k_pci pcnet rtl8139 virtio vmxnet3 +pxerom_targets := 8086100e 808610d3 80861209 10500940 10222000 10ec8139 1af41000 15ad07b0 pxe-rom-e1000 efi-rom-e1000 : VID := 8086 pxe-rom-e1000 efi-rom-e1000 : DID := 100e +pxe-rom-e1000e efi-rom-e1000e : VID := 8086 +pxe-rom-e1000e efi-rom-e1000e : DID := 10d3 pxe-rom-eepro100 efi-rom-eepro100 : VID := 8086 pxe-rom-eepro100 efi-rom-eepro100 : DID := 1209 pxe-rom-ne2k_pci efi-rom-ne2k_pci : VID := 1050 @@ -16,6 +18,8 @@ pxe-rom-rtl8139 efi-rom-rtl8139 : VID := 10ec pxe-rom-rtl8139 efi-rom-rtl8139 : DID := 8139 pxe-rom-virtio efi-rom-virtio : VID := 1af4 pxe-rom-virtio efi-rom-virtio : DID := 1000 +pxe-rom-vmxnet3 efi-rom-vmxnet3 : VID := 15ad +pxe-rom-vmxnet3 efi-rom-vmxnet3 : DID := 07b0 # # cross compiler auto detection diff --git a/roms/config.seabios-128k b/roms/config.seabios-128k index 0a9da77a5e..93203af0de 100644 --- a/roms/config.seabios-128k +++ b/roms/config.seabios-128k @@ -2,9 +2,11 @@ # need to turn off features (xhci,uas) to make it fit into 128k CONFIG_QEMU=y CONFIG_ROM_SIZE=128 +CONFIG_BOOTSPLASH=n CONFIG_XEN=n CONFIG_USB_OHCI=n CONFIG_USB_XHCI=n CONFIG_USB_UAS=n CONFIG_SDCARD=n CONFIG_TCGBIOS=n +CONFIG_MPT_SCSI=n diff --git a/roms/ipxe b/roms/ipxe -Subproject 4e03af8ec2d497e725566a91fd5c19dd604c18a +Subproject 04186319181298083ef28695a8309028b26fe83 diff --git a/roms/seabios b/roms/seabios -Subproject b3ef39f532db52bf17457ba931da758eeb38d6b +Subproject e2fc41e24ee0ada60fc511d60b15a41b294538b @@ -68,11 +68,8 @@ LINK = $(call quiet-command, $(LINKPROG) $(QEMU_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $(call process-archive-undefs, $1) \ $(version-obj-y) $(call extract-libs,$1) $(LIBS)," LINK $(TARGET_DIR)$@") -%.asm: %.S - $(call quiet-command,$(CPP) $(QEMU_INCLUDES) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) -o $@ $<," CPP $(TARGET_DIR)$@") - -%.o: %.asm - $(call quiet-command,$(AS) $(ASFLAGS) -o $@ $<," AS $(TARGET_DIR)$@") +%.o: %.S + $(call quiet-command,$(CCAS) $(QEMU_INCLUDES) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<," CCAS $(TARGET_DIR)$@") %.o: %.cc $(call quiet-command,$(CXX) $(QEMU_INCLUDES) $(QEMU_CXXFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) $($@-cflags) -c -o $@ $<," CXX $(TARGET_DIR)$@") diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py index 8c6acb3f3f..34b6a3a07f 100644 --- a/scripts/qapi-commands.py +++ b/scripts/qapi-commands.py @@ -61,24 +61,18 @@ def gen_marshal_output(ret_type): static void qmp_marshal_output_%(c_name)s(%(c_type)s ret_in, QObject **ret_out, Error **errp) { Error *err = NULL; - QmpOutputVisitor *qov = qmp_output_visitor_new(); - QapiDeallocVisitor *qdv; Visitor *v; - v = qmp_output_get_visitor(qov); + v = qmp_output_visitor_new(ret_out); visit_type_%(c_name)s(v, "unused", &ret_in, &err); - if (err) { - goto out; + if (!err) { + visit_complete(v, ret_out); } - *ret_out = qmp_output_get_qobject(qov); - -out: error_propagate(errp, err); - qmp_output_visitor_cleanup(qov); - qdv = qapi_dealloc_visitor_new(); - v = qapi_dealloc_get_visitor(qdv); + visit_free(v); + v = qapi_dealloc_visitor_new(); visit_type_%(c_name)s(v, "unused", &ret_in, NULL); - qapi_dealloc_visitor_cleanup(qdv); + visit_free(v); } ''', c_type=ret_type.c_type(), c_name=ret_type.c_name()) @@ -115,12 +109,10 @@ def gen_marshal(name, arg_type, ret_type): if arg_type and arg_type.members: ret += mcgen(''' - QmpInputVisitor *qiv = qmp_input_visitor_new(QOBJECT(args), true); - QapiDeallocVisitor *qdv; Visitor *v; %(c_name)s arg = {0}; - v = qmp_input_get_visitor(qiv); + v = qmp_input_visitor_new(QOBJECT(args), true); visit_start_struct(v, NULL, NULL, 0, &err); if (err) { goto out; @@ -129,7 +121,7 @@ def gen_marshal(name, arg_type, ret_type): if (!err) { visit_check_struct(v, &err); } - visit_end_struct(v); + visit_end_struct(v, NULL); if (err) { goto out; } @@ -155,13 +147,12 @@ out: ''') if arg_type and arg_type.members: ret += mcgen(''' - qmp_input_visitor_cleanup(qiv); - qdv = qapi_dealloc_visitor_new(); - v = qapi_dealloc_get_visitor(qdv); + visit_free(v); + v = qapi_dealloc_visitor_new(); visit_start_struct(v, NULL, NULL, 0, NULL); visit_type_%(c_name)s_members(v, &arg, NULL); - visit_end_struct(v); - qapi_dealloc_visitor_cleanup(qdv); + visit_end_struct(v, NULL); + visit_free(v); ''', c_name=arg_type.c_name()) diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py index 21fb16744d..9c88627c9f 100644 --- a/scripts/qapi-event.py +++ b/scripts/qapi-event.py @@ -71,7 +71,7 @@ def gen_event_send(name, arg_type): if arg_type and arg_type.members: ret += mcgen(''' - QmpOutputVisitor *qov; + QObject *obj; Visitor *v; ''') ret += gen_param_var(arg_type) @@ -90,8 +90,7 @@ def gen_event_send(name, arg_type): if arg_type and arg_type.members: ret += mcgen(''' - qov = qmp_output_visitor_new(); - v = qmp_output_get_visitor(qov); + v = qmp_output_visitor_new(&obj); visit_start_struct(v, "%(name)s", NULL, 0, &err); if (err) { @@ -101,12 +100,13 @@ def gen_event_send(name, arg_type): if (!err) { visit_check_struct(v, &err); } - visit_end_struct(v); + visit_end_struct(v, NULL); if (err) { goto out; } - qdict_put_obj(qmp, "data", qmp_output_get_qobject(qov)); + visit_complete(v, &obj); + qdict_put_obj(qmp, "data", obj); ''', name=name, c_name=arg_type.c_name()) @@ -119,7 +119,7 @@ def gen_event_send(name, arg_type): if arg_type and arg_type.members: ret += mcgen(''' out: - qmp_output_visitor_cleanup(qov); + visit_free(v); ''') ret += mcgen(''' error_propagate(errp, err); diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py index 437cf6c8e3..5ace2cf065 100644 --- a/scripts/qapi-types.py +++ b/scripts/qapi-types.py @@ -150,17 +150,15 @@ def gen_type_cleanup(name): void qapi_free_%(c_name)s(%(c_name)s *obj) { - QapiDeallocVisitor *qdv; Visitor *v; if (!obj) { return; } - qdv = qapi_dealloc_visitor_new(); - v = qapi_dealloc_get_visitor(qdv); + v = qapi_dealloc_visitor_new(); visit_type_%(c_name)s(v, NULL, &obj, NULL); - qapi_dealloc_visitor_cleanup(qdv); + visit_free(v); } ''', c_name=c_name(name)) diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py index ffb635c508..0b9e298bc4 100644 --- a/scripts/qapi-visit.py +++ b/scripts/qapi-visit.py @@ -129,7 +129,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error } } - visit_end_list(v); + visit_end_list(v, (void **)obj); if (err && visit_is_input(v)) { qapi_free_%(c_name)s(*obj); *obj = NULL; @@ -194,7 +194,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error if (!err) { visit_check_struct(v, &err); } - visit_end_struct(v); + visit_end_struct(v, NULL); ''', c_type=var.type.c_name(), c_name=c_name(var.name)) @@ -216,7 +216,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error "%(name)s"); } out_obj: - visit_end_alternate(v); + visit_end_alternate(v, (void **)obj); if (err && visit_is_input(v)) { qapi_free_%(c_name)s(*obj); *obj = NULL; @@ -250,7 +250,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error } visit_check_struct(v, &err); out_obj: - visit_end_struct(v); + visit_end_struct(v, (void **)obj); if (err && visit_is_input(v)) { qapi_free_%(c_name)s(*obj); *obj = NULL; diff --git a/softmmu_template.h b/softmmu_template.h index 208f808f3e..284ab2c7b2 100644 --- a/softmmu_template.h +++ b/softmmu_template.h @@ -116,31 +116,6 @@ # define helper_te_st_name helper_le_st_name #endif -/* macro to check the victim tlb */ -#define VICTIM_TLB_HIT(ty) \ -({ \ - /* we are about to do a page table walk. our last hope is the \ - * victim tlb. try to refill from the victim tlb before walking the \ - * page table. */ \ - int vidx; \ - CPUIOTLBEntry tmpiotlb; \ - CPUTLBEntry tmptlb; \ - for (vidx = CPU_VTLB_SIZE-1; vidx >= 0; --vidx) { \ - if (env->tlb_v_table[mmu_idx][vidx].ty == (addr & TARGET_PAGE_MASK)) {\ - /* found entry in victim tlb, swap tlb and iotlb */ \ - tmptlb = env->tlb_table[mmu_idx][index]; \ - env->tlb_table[mmu_idx][index] = env->tlb_v_table[mmu_idx][vidx]; \ - env->tlb_v_table[mmu_idx][vidx] = tmptlb; \ - tmpiotlb = env->iotlb[mmu_idx][index]; \ - env->iotlb[mmu_idx][index] = env->iotlb_v[mmu_idx][vidx]; \ - env->iotlb_v[mmu_idx][vidx] = tmpiotlb; \ - break; \ - } \ - } \ - /* return true when there is a vtlb hit, i.e. vidx >=0 */ \ - vidx >= 0; \ -}) - #ifndef SOFTMMU_CODE_ACCESS static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env, CPUIOTLBEntry *iotlbentry, @@ -171,21 +146,22 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, unsigned mmu_idx = get_mmuidx(oi); int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); target_ulong tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ; + int a_bits = get_alignment_bits(get_memop(oi)); uintptr_t haddr; DATA_TYPE res; /* Adjust the given return address. */ retaddr -= GETPC_ADJ; + if (a_bits > 0 && (addr & ((1 << a_bits) - 1)) != 0) { + cpu_unaligned_access(ENV_GET_CPU(env), addr, READ_ACCESS_TYPE, + mmu_idx, retaddr); + } + /* If the TLB entry is for a different page, reload and try again. */ if ((addr & TARGET_PAGE_MASK) != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { - if ((addr & (DATA_SIZE - 1)) != 0 - && (get_memop(oi) & MO_AMASK) == MO_ALIGN) { - cpu_unaligned_access(ENV_GET_CPU(env), addr, READ_ACCESS_TYPE, - mmu_idx, retaddr); - } - if (!VICTIM_TLB_HIT(ADDR_READ)) { + if (!VICTIM_TLB_HIT(ADDR_READ, addr)) { tlb_fill(ENV_GET_CPU(env), addr, READ_ACCESS_TYPE, mmu_idx, retaddr); } @@ -215,10 +191,6 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, DATA_TYPE res1, res2; unsigned shift; do_unaligned_access: - if ((get_memop(oi) & MO_AMASK) == MO_ALIGN) { - cpu_unaligned_access(ENV_GET_CPU(env), addr, READ_ACCESS_TYPE, - mmu_idx, retaddr); - } addr1 = addr & ~(DATA_SIZE - 1); addr2 = addr1 + DATA_SIZE; /* Note the adjustment at the beginning of the function. @@ -232,13 +204,6 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, return res; } - /* Handle aligned access or unaligned access in the same page. */ - if ((addr & (DATA_SIZE - 1)) != 0 - && (get_memop(oi) & MO_AMASK) == MO_ALIGN) { - cpu_unaligned_access(ENV_GET_CPU(env), addr, READ_ACCESS_TYPE, - mmu_idx, retaddr); - } - haddr = addr + env->tlb_table[mmu_idx][index].addend; #if DATA_SIZE == 1 res = glue(glue(ld, LSUFFIX), _p)((uint8_t *)haddr); @@ -255,21 +220,22 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, unsigned mmu_idx = get_mmuidx(oi); int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); target_ulong tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ; + int a_bits = get_alignment_bits(get_memop(oi)); uintptr_t haddr; DATA_TYPE res; /* Adjust the given return address. */ retaddr -= GETPC_ADJ; + if (a_bits > 0 && (addr & ((1 << a_bits) - 1)) != 0) { + cpu_unaligned_access(ENV_GET_CPU(env), addr, READ_ACCESS_TYPE, + mmu_idx, retaddr); + } + /* If the TLB entry is for a different page, reload and try again. */ if ((addr & TARGET_PAGE_MASK) != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { - if ((addr & (DATA_SIZE - 1)) != 0 - && (get_memop(oi) & MO_AMASK) == MO_ALIGN) { - cpu_unaligned_access(ENV_GET_CPU(env), addr, READ_ACCESS_TYPE, - mmu_idx, retaddr); - } - if (!VICTIM_TLB_HIT(ADDR_READ)) { + if (!VICTIM_TLB_HIT(ADDR_READ, addr)) { tlb_fill(ENV_GET_CPU(env), addr, READ_ACCESS_TYPE, mmu_idx, retaddr); } @@ -299,10 +265,6 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, DATA_TYPE res1, res2; unsigned shift; do_unaligned_access: - if ((get_memop(oi) & MO_AMASK) == MO_ALIGN) { - cpu_unaligned_access(ENV_GET_CPU(env), addr, READ_ACCESS_TYPE, - mmu_idx, retaddr); - } addr1 = addr & ~(DATA_SIZE - 1); addr2 = addr1 + DATA_SIZE; /* Note the adjustment at the beginning of the function. @@ -316,13 +278,6 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, return res; } - /* Handle aligned access or unaligned access in the same page. */ - if ((addr & (DATA_SIZE - 1)) != 0 - && (get_memop(oi) & MO_AMASK) == MO_ALIGN) { - cpu_unaligned_access(ENV_GET_CPU(env), addr, READ_ACCESS_TYPE, - mmu_idx, retaddr); - } - haddr = addr + env->tlb_table[mmu_idx][index].addend; res = glue(glue(ld, LSUFFIX), _be_p)((uint8_t *)haddr); return res; @@ -376,20 +331,21 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, unsigned mmu_idx = get_mmuidx(oi); int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write; + int a_bits = get_alignment_bits(get_memop(oi)); uintptr_t haddr; /* Adjust the given return address. */ retaddr -= GETPC_ADJ; + if (a_bits > 0 && (addr & ((1 << a_bits) - 1)) != 0) { + cpu_unaligned_access(ENV_GET_CPU(env), addr, MMU_DATA_STORE, + mmu_idx, retaddr); + } + /* If the TLB entry is for a different page, reload and try again. */ if ((addr & TARGET_PAGE_MASK) != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { - if ((addr & (DATA_SIZE - 1)) != 0 - && (get_memop(oi) & MO_AMASK) == MO_ALIGN) { - cpu_unaligned_access(ENV_GET_CPU(env), addr, MMU_DATA_STORE, - mmu_idx, retaddr); - } - if (!VICTIM_TLB_HIT(addr_write)) { + if (!VICTIM_TLB_HIT(addr_write, addr)) { tlb_fill(ENV_GET_CPU(env), addr, MMU_DATA_STORE, mmu_idx, retaddr); } tlb_addr = env->tlb_table[mmu_idx][index].addr_write; @@ -414,16 +370,25 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, if (DATA_SIZE > 1 && unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1 >= TARGET_PAGE_SIZE)) { - int i; + int i, index2; + target_ulong page2, tlb_addr2; do_unaligned_access: - if ((get_memop(oi) & MO_AMASK) == MO_ALIGN) { - cpu_unaligned_access(ENV_GET_CPU(env), addr, MMU_DATA_STORE, - mmu_idx, retaddr); + /* Ensure the second page is in the TLB. Note that the first page + is already guaranteed to be filled, and that the second page + cannot evict the first. */ + page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK; + index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write; + if (page2 != (tlb_addr2 & (TARGET_PAGE_MASK | TLB_INVALID_MASK)) + && !VICTIM_TLB_HIT(addr_write, page2)) { + tlb_fill(ENV_GET_CPU(env), page2, MMU_DATA_STORE, + mmu_idx, retaddr); } - /* XXX: not efficient, but simple */ - /* Note: relies on the fact that tlb_fill() does not remove the - * previous page from the TLB cache. */ - for (i = DATA_SIZE - 1; i >= 0; i--) { + + /* XXX: not efficient, but simple. */ + /* This loop must go in the forward direction to avoid issues + with self-modifying code in Windows 64-bit. */ + for (i = 0; i < DATA_SIZE; ++i) { /* Little-endian extract. */ uint8_t val8 = val >> (i * 8); /* Note the adjustment at the beginning of the function. @@ -434,13 +399,6 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, return; } - /* Handle aligned access or unaligned access in the same page. */ - if ((addr & (DATA_SIZE - 1)) != 0 - && (get_memop(oi) & MO_AMASK) == MO_ALIGN) { - cpu_unaligned_access(ENV_GET_CPU(env), addr, MMU_DATA_STORE, - mmu_idx, retaddr); - } - haddr = addr + env->tlb_table[mmu_idx][index].addend; #if DATA_SIZE == 1 glue(glue(st, SUFFIX), _p)((uint8_t *)haddr, val); @@ -456,20 +414,21 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, unsigned mmu_idx = get_mmuidx(oi); int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write; + int a_bits = get_alignment_bits(get_memop(oi)); uintptr_t haddr; /* Adjust the given return address. */ retaddr -= GETPC_ADJ; + if (a_bits > 0 && (addr & ((1 << a_bits) - 1)) != 0) { + cpu_unaligned_access(ENV_GET_CPU(env), addr, MMU_DATA_STORE, + mmu_idx, retaddr); + } + /* If the TLB entry is for a different page, reload and try again. */ if ((addr & TARGET_PAGE_MASK) != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { - if ((addr & (DATA_SIZE - 1)) != 0 - && (get_memop(oi) & MO_AMASK) == MO_ALIGN) { - cpu_unaligned_access(ENV_GET_CPU(env), addr, MMU_DATA_STORE, - mmu_idx, retaddr); - } - if (!VICTIM_TLB_HIT(addr_write)) { + if (!VICTIM_TLB_HIT(addr_write, addr)) { tlb_fill(ENV_GET_CPU(env), addr, MMU_DATA_STORE, mmu_idx, retaddr); } tlb_addr = env->tlb_table[mmu_idx][index].addr_write; @@ -494,16 +453,25 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, if (DATA_SIZE > 1 && unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1 >= TARGET_PAGE_SIZE)) { - int i; + int i, index2; + target_ulong page2, tlb_addr2; do_unaligned_access: - if ((get_memop(oi) & MO_AMASK) == MO_ALIGN) { - cpu_unaligned_access(ENV_GET_CPU(env), addr, MMU_DATA_STORE, - mmu_idx, retaddr); + /* Ensure the second page is in the TLB. Note that the first page + is already guaranteed to be filled, and that the second page + cannot evict the first. */ + page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK; + index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write; + if (page2 != (tlb_addr2 & (TARGET_PAGE_MASK | TLB_INVALID_MASK)) + && !VICTIM_TLB_HIT(addr_write, page2)) { + tlb_fill(ENV_GET_CPU(env), page2, MMU_DATA_STORE, + mmu_idx, retaddr); } + /* XXX: not efficient, but simple */ - /* Note: relies on the fact that tlb_fill() does not remove the - * previous page from the TLB cache. */ - for (i = DATA_SIZE - 1; i >= 0; i--) { + /* This loop must go in the forward direction to avoid issues + with self-modifying code. */ + for (i = 0; i < DATA_SIZE; ++i) { /* Big-endian extract. */ uint8_t val8 = val >> (((DATA_SIZE - 1) * 8) - (i * 8)); /* Note the adjustment at the beginning of the function. @@ -514,13 +482,6 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, return; } - /* Handle aligned access or unaligned access in the same page. */ - if ((addr & (DATA_SIZE - 1)) != 0 - && (get_memop(oi) & MO_AMASK) == MO_ALIGN) { - cpu_unaligned_access(ENV_GET_CPU(env), addr, MMU_DATA_STORE, - mmu_idx, retaddr); - } - haddr = addr + env->tlb_table[mmu_idx][index].addend; glue(glue(st, SUFFIX), _be_p)((uint8_t *)haddr, val); } @@ -542,7 +503,7 @@ void probe_write(CPUArchState *env, target_ulong addr, int mmu_idx, if ((addr & TARGET_PAGE_MASK) != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { /* TLB entry is for a different page */ - if (!VICTIM_TLB_HIT(addr_write)) { + if (!VICTIM_TLB_HIT(addr_write, addr)) { tlb_fill(ENV_GET_CPU(env), addr, MMU_DATA_STORE, mmu_idx, retaddr); } } diff --git a/target-arm/arm-semi.c b/target-arm/arm-semi.c index 8be0645eb0..7cac8734c7 100644 --- a/target-arm/arm-semi.c +++ b/target-arm/arm-semi.c @@ -564,8 +564,10 @@ target_ulong do_arm_semihosting(CPUARMState *env) } case TARGET_SYS_HEAPINFO: { - uint32_t *ptr; - uint32_t limit; + target_ulong retvals[4]; + target_ulong limit; + int i; + GET_ARG(0); #ifdef CONFIG_USER_ONLY @@ -587,30 +589,33 @@ target_ulong do_arm_semihosting(CPUARMState *env) ts->heap_limit = limit; } - ptr = lock_user(VERIFY_WRITE, arg0, 16, 0); - if (!ptr) { - /* FIXME - should this error code be -TARGET_EFAULT ? */ - return (uint32_t)-1; - } - ptr[0] = tswap32(ts->heap_base); - ptr[1] = tswap32(ts->heap_limit); - ptr[2] = tswap32(ts->stack_base); - ptr[3] = tswap32(0); /* Stack limit. */ - unlock_user(ptr, arg0, 16); + retvals[0] = ts->heap_base; + retvals[1] = ts->heap_limit; + retvals[2] = ts->stack_base; + retvals[3] = 0; /* Stack limit. */ #else limit = ram_size; - ptr = lock_user(VERIFY_WRITE, arg0, 16, 0); - if (!ptr) { - /* FIXME - should this error code be -TARGET_EFAULT ? */ - return (uint32_t)-1; - } /* TODO: Make this use the limit of the loaded application. */ - ptr[0] = tswap32(limit / 2); - ptr[1] = tswap32(limit); - ptr[2] = tswap32(limit); /* Stack base */ - ptr[3] = tswap32(0); /* Stack limit. */ - unlock_user(ptr, arg0, 16); + retvals[0] = limit / 2; + retvals[1] = limit; + retvals[2] = limit; /* Stack base */ + retvals[3] = 0; /* Stack limit. */ #endif + + for (i = 0; i < ARRAY_SIZE(retvals); i++) { + bool fail; + + if (is_a64(env)) { + fail = put_user_u64(retvals[i], arg0 + i * 8); + } else { + fail = put_user_u32(retvals[i], arg0 + i * 4); + } + + if (fail) { + /* Couldn't write back to argument block */ + return -1; + } + } return 0; } case TARGET_SYS_EXIT: diff --git a/target-i386/cpu.c b/target-i386/cpu.c index 3bd3cfc3ad..fc209ee1cb 100644 --- a/target-i386/cpu.c +++ b/target-i386/cpu.c @@ -245,6 +245,47 @@ static const char *kvm_feature_name[] = { NULL, NULL, NULL, NULL, }; +static const char *hyperv_priv_feature_name[] = { + NULL /* hv_msr_vp_runtime_access */, NULL /* hv_msr_time_refcount_access */, + NULL /* hv_msr_synic_access */, NULL /* hv_msr_stimer_access */, + NULL /* hv_msr_apic_access */, NULL /* hv_msr_hypercall_access */, + NULL /* hv_vpindex_access */, NULL /* hv_msr_reset_access */, + NULL /* hv_msr_stats_access */, NULL /* hv_reftsc_access */, + NULL /* hv_msr_idle_access */, NULL /* hv_msr_frequency_access */, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, +}; + +static const char *hyperv_ident_feature_name[] = { + NULL /* hv_create_partitions */, NULL /* hv_access_partition_id */, + NULL /* hv_access_memory_pool */, NULL /* hv_adjust_message_buffers */, + NULL /* hv_post_messages */, NULL /* hv_signal_events */, + NULL /* hv_create_port */, NULL /* hv_connect_port */, + NULL /* hv_access_stats */, NULL, NULL, NULL /* hv_debugging */, + NULL /* hv_cpu_power_management */, NULL /* hv_configure_profiler */, + NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, +}; + +static const char *hyperv_misc_feature_name[] = { + NULL /* hv_mwait */, NULL /* hv_guest_debugging */, + NULL /* hv_perf_monitor */, NULL /* hv_cpu_dynamic_part */, + NULL /* hv_hypercall_params_xmm */, NULL /* hv_guest_idle_state */, + NULL, NULL, + NULL, NULL, NULL /* hv_guest_crash_msr */, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, +}; + static const char *svm_feature_name[] = { "npt", "lbrv", "svm_lock", "nrip_save", "tsc_scale", "vmcb_clean", "flushbyasid", "decodeassists", @@ -358,10 +399,11 @@ static const char *cpuid_6_feature_name[] = { #define TCG_7_0_EBX_FEATURES (CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_SMAP | \ CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ADX | \ CPUID_7_0_EBX_PCOMMIT | CPUID_7_0_EBX_CLFLUSHOPT | \ - CPUID_7_0_EBX_CLWB | CPUID_7_0_EBX_MPX | CPUID_7_0_EBX_FSGSBASE) + CPUID_7_0_EBX_CLWB | CPUID_7_0_EBX_MPX | CPUID_7_0_EBX_FSGSBASE | \ + CPUID_7_0_EBX_ERMS) /* missing: CPUID_7_0_EBX_HLE, CPUID_7_0_EBX_AVX2, - CPUID_7_0_EBX_ERMS, CPUID_7_0_EBX_INVPCID, CPUID_7_0_EBX_RTM, + CPUID_7_0_EBX_INVPCID, CPUID_7_0_EBX_RTM, CPUID_7_0_EBX_RDSEED */ #define TCG_7_0_ECX_FEATURES (CPUID_7_0_ECX_PKU | CPUID_7_0_ECX_OSPKE) #define TCG_APM_FEATURES 0 @@ -411,6 +453,18 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { .cpuid_eax = KVM_CPUID_FEATURES, .cpuid_reg = R_EAX, .tcg_features = TCG_KVM_FEATURES, }, + [FEAT_HYPERV_EAX] = { + .feat_names = hyperv_priv_feature_name, + .cpuid_eax = 0x40000003, .cpuid_reg = R_EAX, + }, + [FEAT_HYPERV_EBX] = { + .feat_names = hyperv_ident_feature_name, + .cpuid_eax = 0x40000003, .cpuid_reg = R_EBX, + }, + [FEAT_HYPERV_EDX] = { + .feat_names = hyperv_misc_feature_name, + .cpuid_eax = 0x40000003, .cpuid_reg = R_EDX, + }, [FEAT_SVM] = { .feat_names = svm_feature_name, .cpuid_eax = 0x8000000A, .cpuid_reg = R_EDX, @@ -1493,6 +1547,17 @@ static uint32_t x86_cpu_get_supported_feature_word(FeatureWord w, #ifdef CONFIG_KVM +static bool lmce_supported(void) +{ + uint64_t mce_cap; + + if (kvm_ioctl(kvm_state, KVM_X86_GET_MCE_CAP_SUPPORTED, &mce_cap) < 0) { + return false; + } + + return !!(mce_cap & MCG_LMCE_P); +} + static int cpu_x86_fill_model_id(char *str) { uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0; @@ -1565,6 +1630,10 @@ static void host_x86_cpu_initfn(Object *obj) env->cpuid_level = kvm_arch_get_supported_cpuid(s, 0x0, 0, R_EAX); env->cpuid_xlevel = kvm_arch_get_supported_cpuid(s, 0x80000000, 0, R_EAX); env->cpuid_xlevel2 = kvm_arch_get_supported_cpuid(s, 0xC0000000, 0, R_EAX); + + if (lmce_supported()) { + object_property_set_bool(OBJECT(cpu), true, "lmce", &error_abort); + } } object_property_set_bool(OBJECT(cpu), true, "pmu", &error_abort); @@ -1957,12 +2026,17 @@ static FeatureWordArray minus_features = { 0 }; /* Parse "+feature,-feature,feature=foo" CPU feature string */ -static void x86_cpu_parse_featurestr(CPUState *cs, char *features, +static void x86_cpu_parse_featurestr(const char *typename, char *features, Error **errp) { - X86CPU *cpu = X86_CPU(cs); char *featurestr; /* Single 'key=value" string being parsed */ Error *local_err = NULL; + static bool cpu_globals_initialized; + + if (cpu_globals_initialized) { + return; + } + cpu_globals_initialized = true; if (!features) { return; @@ -1974,6 +2048,8 @@ static void x86_cpu_parse_featurestr(CPUState *cs, char *features, const char *name; const char *val = NULL; char *eq = NULL; + char num[32]; + GlobalProperty *prop; /* Compatibility syntax: */ if (featurestr[0] == '+') { @@ -1999,7 +2075,6 @@ static void x86_cpu_parse_featurestr(CPUState *cs, char *features, if (!strcmp(name, "tsc-freq")) { int64_t tsc_freq; char *err; - char num[32]; tsc_freq = qemu_strtosz_suffix_unit(val, &err, QEMU_STRTOSZ_DEFSUFFIX_B, 1000); @@ -2012,7 +2087,12 @@ static void x86_cpu_parse_featurestr(CPUState *cs, char *features, name = "tsc-frequency"; } - object_property_parse(OBJECT(cpu), val, name, &local_err); + prop = g_new0(typeof(*prop), 1); + prop->driver = typename; + prop->property = g_strdup(name); + prop->value = g_strdup(val); + prop->errp = &error_fatal; + qdev_prop_register_global(prop); } if (local_err) { @@ -2197,47 +2277,6 @@ static void x86_cpu_load_def(X86CPU *cpu, X86CPUDefinition *def, Error **errp) } -X86CPU *cpu_x86_create(const char *cpu_model, Error **errp) -{ - X86CPU *cpu = NULL; - ObjectClass *oc; - gchar **model_pieces; - char *name, *features; - Error *error = NULL; - - model_pieces = g_strsplit(cpu_model, ",", 2); - if (!model_pieces[0]) { - error_setg(&error, "Invalid/empty CPU model name"); - goto out; - } - name = model_pieces[0]; - features = model_pieces[1]; - - oc = x86_cpu_class_by_name(name); - if (oc == NULL) { - error_setg(&error, "Unable to find CPU definition: %s", name); - goto out; - } - - cpu = X86_CPU(object_new(object_class_get_name(oc))); - - x86_cpu_parse_featurestr(CPU(cpu), features, &error); - if (error) { - goto out; - } - -out: - if (error != NULL) { - error_propagate(errp, error); - if (cpu) { - object_unref(OBJECT(cpu)); - cpu = NULL; - } - } - g_strfreev(model_pieces); - return cpu; -} - X86CPU *cpu_x86_init(const char *cpu_model) { return X86_CPU(cpu_generic_init(TYPE_X86_CPU, cpu_model)); @@ -2815,7 +2854,8 @@ static void mce_init(X86CPU *cpu) if (((cenv->cpuid_version >> 8) & 0xf) >= 6 && (cenv->features[FEAT_1_EDX] & (CPUID_MCE | CPUID_MCA)) == (CPUID_MCE | CPUID_MCA)) { - cenv->mcg_cap = MCE_CAP_DEF | MCE_BANKS_DEF; + cenv->mcg_cap = MCE_CAP_DEF | MCE_BANKS_DEF | + (cpu->enable_lmce ? MCG_LMCE_P : 0); cenv->mcg_ctl = ~(uint64_t)0; for (bank = 0; bank < MCE_BANKS_DEF; bank++) { cenv->mce_banks[bank * 4] = ~(uint64_t)0; @@ -3262,6 +3302,7 @@ static Property x86_cpu_properties[] = { DEFINE_PROP_UINT32("xlevel2", X86CPU, env.cpuid_xlevel2, 0), DEFINE_PROP_STRING("hv-vendor-id", X86CPU, hyperv_vendor_id), DEFINE_PROP_BOOL("cpuid-0xb", X86CPU, enable_cpuid_0xb, true), + DEFINE_PROP_BOOL("lmce", X86CPU, enable_lmce, false), DEFINE_PROP_END_OF_LIST() }; diff --git a/target-i386/cpu.h b/target-i386/cpu.h index 474b0b937d..5c7a2791f3 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -292,6 +292,7 @@ #define MCG_CTL_P (1ULL<<8) /* MCG_CAP register available */ #define MCG_SER_P (1ULL<<24) /* MCA recovery/new status bits */ +#define MCG_LMCE_P (1ULL<<27) /* Local Machine Check Supported */ #define MCE_CAP_DEF (MCG_CTL_P|MCG_SER_P) #define MCE_BANKS_DEF 10 @@ -301,6 +302,9 @@ #define MCG_STATUS_RIPV (1ULL<<0) /* restart ip valid */ #define MCG_STATUS_EIPV (1ULL<<1) /* ip points to correct instruction */ #define MCG_STATUS_MCIP (1ULL<<2) /* machine check in progress */ +#define MCG_STATUS_LMCE (1ULL<<3) /* Local MCE signaled */ + +#define MCG_EXT_CTL_LMCE_EN (1ULL<<0) /* Local MCE enabled */ #define MCI_STATUS_VAL (1ULL<<63) /* valid error */ #define MCI_STATUS_OVER (1ULL<<62) /* previous errors lost */ @@ -328,6 +332,10 @@ #define MSR_TSC_ADJUST 0x0000003b #define MSR_IA32_TSCDEADLINE 0x6e0 +#define FEATURE_CONTROL_LOCKED (1<<0) +#define FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX (1<<2) +#define FEATURE_CONTROL_LMCE (1<<20) + #define MSR_P6_PERFCTR0 0xc1 #define MSR_IA32_SMBASE 0x9e @@ -343,6 +351,7 @@ #define MSR_MCG_CAP 0x179 #define MSR_MCG_STATUS 0x17a #define MSR_MCG_CTL 0x17b +#define MSR_MCG_EXT_CTL 0x4d0 #define MSR_P6_EVNTSEL0 0x186 @@ -440,6 +449,9 @@ typedef enum FeatureWord { FEAT_8000_0007_EDX, /* CPUID[8000_0007].EDX */ FEAT_C000_0001_EDX, /* CPUID[C000_0001].EDX */ FEAT_KVM, /* CPUID[4000_0001].EAX (KVM_CPUID_FEATURES) */ + FEAT_HYPERV_EAX, /* CPUID[4000_0003].EAX */ + FEAT_HYPERV_EBX, /* CPUID[4000_0003].EBX */ + FEAT_HYPERV_EDX, /* CPUID[4000_0003].EDX */ FEAT_SVM, /* CPUID[8000_000A].EDX */ FEAT_XSAVE, /* CPUID[EAX=0xd,ECX=1].EAX */ FEAT_6_EAX, /* CPUID[6].EAX */ @@ -1111,6 +1123,7 @@ typedef struct CPUX86State { uint64_t mcg_cap; uint64_t mcg_ctl; + uint64_t mcg_ext_ctl; uint64_t mce_banks[MCE_BANKS_DEF*4]; uint64_t tsc_aux; @@ -1178,6 +1191,12 @@ struct X86CPU { */ bool enable_pmu; + /* LMCE support can be enabled/disabled via cpu option 'lmce=on/off'. It is + * disabled by default to avoid breaking migration between QEMU with + * different LMCE configurations. + */ + bool enable_lmce; + /* Compatibility bits for old machine types: */ bool enable_cpuid_0xb; @@ -1234,7 +1253,6 @@ void x86_cpu_exec_enter(CPUState *cpu); void x86_cpu_exec_exit(CPUState *cpu); X86CPU *cpu_x86_init(const char *cpu_model); -X86CPU *cpu_x86_create(const char *cpu_model, Error **errp); void x86_cpu_list(FILE *f, fprintf_function cpu_fprintf); int cpu_x86_support_mca_broadcast(CPUX86State *env); diff --git a/target-i386/kvm.c b/target-i386/kvm.c index f3698f19b5..93275231ec 100644 --- a/target-i386/kvm.c +++ b/target-i386/kvm.c @@ -106,6 +106,8 @@ static int has_xsave; static int has_xcrs; static int has_pit_state2; +static bool has_msr_mcg_ext_ctl; + static struct kvm_cpuid2 *cpuid_cache; int kvm_has_pit_state2(void) @@ -382,10 +384,12 @@ static int kvm_get_mce_cap_supported(KVMState *s, uint64_t *mce_cap, static void kvm_mce_inject(X86CPU *cpu, hwaddr paddr, int code) { + CPUState *cs = CPU(cpu); CPUX86State *env = &cpu->env; uint64_t status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN | MCI_STATUS_MISCV | MCI_STATUS_ADDRV | MCI_STATUS_S; uint64_t mcg_status = MCG_STATUS_MCIP; + int flags = 0; if (code == BUS_MCEERR_AR) { status |= MCI_STATUS_AR | 0x134; @@ -394,10 +398,19 @@ static void kvm_mce_inject(X86CPU *cpu, hwaddr paddr, int code) status |= 0xc0; mcg_status |= MCG_STATUS_RIPV; } + + flags = cpu_x86_support_mca_broadcast(env) ? MCE_INJECT_BROADCAST : 0; + /* We need to read back the value of MSR_EXT_MCG_CTL that was set by the + * guest kernel back into env->mcg_ext_ctl. + */ + cpu_synchronize_state(cs); + if (env->mcg_ext_ctl & MCG_EXT_CTL_LMCE_EN) { + mcg_status |= MCG_STATUS_LMCE; + flags = 0; + } + cpu_x86_inject_mce(NULL, cpu, 9, status, mcg_status, paddr, - (MCM_ADDR_PHYS << 6) | 0xc, - cpu_x86_support_mca_broadcast(env) ? - MCE_INJECT_BROADCAST : 0); + (MCM_ADDR_PHYS << 6) | 0xc, flags); } static void hardware_memory_error(void) @@ -566,7 +579,9 @@ static int kvm_arch_set_tsc_khz(CPUState *cs) -ENOTSUP; if (cur_freq <= 0 || cur_freq != env->tsc_khz) { error_report("warning: TSC frequency mismatch between " - "VM and host, and TSC scaling unavailable"); + "VM (%" PRId64 " kHz) and host (%d kHz), " + "and TSC scaling unavailable", + env->tsc_khz, cur_freq); return r; } } @@ -574,6 +589,64 @@ static int kvm_arch_set_tsc_khz(CPUState *cs) return 0; } +static int hyperv_handle_properties(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + if (cpu->hyperv_relaxed_timing) { + env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_HYPERCALL_AVAILABLE; + } + if (cpu->hyperv_vapic) { + env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_HYPERCALL_AVAILABLE; + env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_APIC_ACCESS_AVAILABLE; + has_msr_hv_vapic = true; + } + if (cpu->hyperv_time && + kvm_check_extension(cs->kvm_state, KVM_CAP_HYPERV_TIME) > 0) { + env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_HYPERCALL_AVAILABLE; + env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_TIME_REF_COUNT_AVAILABLE; + env->features[FEAT_HYPERV_EAX] |= 0x200; + has_msr_hv_tsc = true; + } + if (cpu->hyperv_crash && has_msr_hv_crash) { + env->features[FEAT_HYPERV_EDX] |= HV_X64_GUEST_CRASH_MSR_AVAILABLE; + } + env->features[FEAT_HYPERV_EDX] |= HV_X64_CPU_DYNAMIC_PARTITIONING_AVAILABLE; + if (cpu->hyperv_reset && has_msr_hv_reset) { + env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_RESET_AVAILABLE; + } + if (cpu->hyperv_vpindex && has_msr_hv_vpindex) { + env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_VP_INDEX_AVAILABLE; + } + if (cpu->hyperv_runtime && has_msr_hv_runtime) { + env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_VP_RUNTIME_AVAILABLE; + } + if (cpu->hyperv_synic) { + int sint; + + if (!has_msr_hv_synic || + kvm_vcpu_enable_cap(cs, KVM_CAP_HYPERV_SYNIC, 0)) { + fprintf(stderr, "Hyper-V SynIC is not supported by kernel\n"); + return -ENOSYS; + } + + env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_SYNIC_AVAILABLE; + env->msr_hv_synic_version = HV_SYNIC_VERSION_1; + for (sint = 0; sint < ARRAY_SIZE(env->msr_hv_synic_sint); sint++) { + env->msr_hv_synic_sint[sint] = HV_SYNIC_SINT_MASKED; + } + } + if (cpu->hyperv_stimer) { + if (!has_msr_hv_stimer) { + fprintf(stderr, "Hyper-V timers aren't supported by kernel\n"); + return -ENOSYS; + } + env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_SYNTIMER_AVAILABLE; + } + return 0; +} + static Error *invtsc_mig_blocker; #define KVM_MAX_CPUID_ENTRIES 100 @@ -633,56 +706,14 @@ int kvm_arch_init_vcpu(CPUState *cs) c = &cpuid_data.entries[cpuid_i++]; c->function = HYPERV_CPUID_FEATURES; - if (cpu->hyperv_relaxed_timing) { - c->eax |= HV_X64_MSR_HYPERCALL_AVAILABLE; - } - if (cpu->hyperv_vapic) { - c->eax |= HV_X64_MSR_HYPERCALL_AVAILABLE; - c->eax |= HV_X64_MSR_APIC_ACCESS_AVAILABLE; - has_msr_hv_vapic = true; - } - if (cpu->hyperv_time && - kvm_check_extension(cs->kvm_state, KVM_CAP_HYPERV_TIME) > 0) { - c->eax |= HV_X64_MSR_HYPERCALL_AVAILABLE; - c->eax |= HV_X64_MSR_TIME_REF_COUNT_AVAILABLE; - c->eax |= 0x200; - has_msr_hv_tsc = true; - } - if (cpu->hyperv_crash && has_msr_hv_crash) { - c->edx |= HV_X64_GUEST_CRASH_MSR_AVAILABLE; - } - c->edx |= HV_X64_CPU_DYNAMIC_PARTITIONING_AVAILABLE; - if (cpu->hyperv_reset && has_msr_hv_reset) { - c->eax |= HV_X64_MSR_RESET_AVAILABLE; - } - if (cpu->hyperv_vpindex && has_msr_hv_vpindex) { - c->eax |= HV_X64_MSR_VP_INDEX_AVAILABLE; - } - if (cpu->hyperv_runtime && has_msr_hv_runtime) { - c->eax |= HV_X64_MSR_VP_RUNTIME_AVAILABLE; + r = hyperv_handle_properties(cs); + if (r) { + return r; } - if (cpu->hyperv_synic) { - int sint; + c->eax = env->features[FEAT_HYPERV_EAX]; + c->ebx = env->features[FEAT_HYPERV_EBX]; + c->edx = env->features[FEAT_HYPERV_EDX]; - if (!has_msr_hv_synic || - kvm_vcpu_enable_cap(cs, KVM_CAP_HYPERV_SYNIC, 0)) { - fprintf(stderr, "Hyper-V SynIC is not supported by kernel\n"); - return -ENOSYS; - } - - c->eax |= HV_X64_MSR_SYNIC_AVAILABLE; - env->msr_hv_synic_version = HV_SYNIC_VERSION_1; - for (sint = 0; sint < ARRAY_SIZE(env->msr_hv_synic_sint); sint++) { - env->msr_hv_synic_sint[sint] = HV_SYNIC_SINT_MASKED; - } - } - if (cpu->hyperv_stimer) { - if (!has_msr_hv_stimer) { - fprintf(stderr, "Hyper-V timers aren't supported by kernel\n"); - return -ENOSYS; - } - c->eax |= HV_X64_MSR_SYNTIMER_AVAILABLE; - } c = &cpuid_data.entries[cpuid_i++]; c->function = HYPERV_CPUID_ENLIGHTMENT_INFO; if (cpu->hyperv_relaxed_timing) { @@ -865,6 +896,10 @@ int kvm_arch_init_vcpu(CPUState *cs) unsupported_caps = env->mcg_cap & ~(mcg_cap | MCG_CAP_BANKS_MASK); if (unsupported_caps) { + if (unsupported_caps & MCG_LMCE_P) { + error_report("kvm: LMCE not supported"); + return -ENOTSUP; + } error_report("warning: Unsupported MCG_CAP bits: 0x%" PRIx64, unsupported_caps); } @@ -885,6 +920,10 @@ int kvm_arch_init_vcpu(CPUState *cs) !!(c->ecx & CPUID_EXT_SMX); } + if (env->mcg_cap & MCG_LMCE_P) { + has_msr_mcg_ext_ctl = has_msr_feature_control = true; + } + c = cpuid_find_entry(&cpuid_data.cpuid, 0x80000007, 0); if (c && (c->edx & 1<<8) && invtsc_mig_blocker == NULL) { /* for migration */ @@ -1705,6 +1744,9 @@ static int kvm_put_msrs(X86CPU *cpu, int level) kvm_msr_entry_add(cpu, MSR_MCG_STATUS, env->mcg_status); kvm_msr_entry_add(cpu, MSR_MCG_CTL, env->mcg_ctl); + if (has_msr_mcg_ext_ctl) { + kvm_msr_entry_add(cpu, MSR_MCG_EXT_CTL, env->mcg_ext_ctl); + } for (i = 0; i < (env->mcg_cap & 0xff) * 4; i++) { kvm_msr_entry_add(cpu, MSR_MC0_CTL + i, env->mce_banks[i]); } @@ -2008,6 +2050,9 @@ static int kvm_get_msrs(X86CPU *cpu) if (env->mcg_cap) { kvm_msr_entry_add(cpu, MSR_MCG_STATUS, 0); kvm_msr_entry_add(cpu, MSR_MCG_CTL, 0); + if (has_msr_mcg_ext_ctl) { + kvm_msr_entry_add(cpu, MSR_MCG_EXT_CTL, 0); + } for (i = 0; i < (env->mcg_cap & 0xff) * 4; i++) { kvm_msr_entry_add(cpu, MSR_MC0_CTL + i, 0); } @@ -2136,6 +2181,9 @@ static int kvm_get_msrs(X86CPU *cpu) case MSR_MCG_CTL: env->mcg_ctl = msrs[i].data; break; + case MSR_MCG_EXT_CTL: + env->mcg_ext_ctl = msrs[i].data; + break; case MSR_IA32_MISC_ENABLE: env->msr_ia32_misc_enable = msrs[i].data; break; diff --git a/target-i386/machine.c b/target-i386/machine.c index cb9adf2b02..71c0e4dc47 100644 --- a/target-i386/machine.c +++ b/target-i386/machine.c @@ -896,6 +896,24 @@ static const VMStateDescription vmstate_tsc_khz = { } }; +static bool mcg_ext_ctl_needed(void *opaque) +{ + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; + return cpu->enable_lmce && env->mcg_ext_ctl; +} + +static const VMStateDescription vmstate_mcg_ext_ctl = { + .name = "cpu/mcg_ext_ctl", + .version_id = 1, + .minimum_version_id = 1, + .needed = mcg_ext_ctl_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT64(env.mcg_ext_ctl, X86CPU), + VMSTATE_END_OF_LIST() + } +}; + VMStateDescription vmstate_x86_cpu = { .name = "cpu", .version_id = 12, @@ -1022,6 +1040,7 @@ VMStateDescription vmstate_x86_cpu = { #ifdef TARGET_X86_64 &vmstate_pkru, #endif + &vmstate_mcg_ext_ctl, NULL } }; diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index af73bced9f..2666a3f80d 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -1047,6 +1047,8 @@ struct CPUPPCState { uint64_t insns_flags2; #if defined(TARGET_PPC64) struct ppc_segment_page_sizes sps; + ppc_slb_t vrma_slb; + target_ulong rmls; bool ci_large_pages; #endif diff --git a/target-ppc/fpu_helper.c b/target-ppc/fpu_helper.c index 4ef893be2c..d9795d04d0 100644 --- a/target-ppc/fpu_helper.c +++ b/target-ppc/fpu_helper.c @@ -2689,19 +2689,19 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ helper_float_check_status(env); \ } -VSX_ROUND(xsrdpi, 1, float64, VsrD(0), float_round_nearest_even, 1) +VSX_ROUND(xsrdpi, 1, float64, VsrD(0), float_round_ties_away, 1) VSX_ROUND(xsrdpic, 1, float64, VsrD(0), FLOAT_ROUND_CURRENT, 1) VSX_ROUND(xsrdpim, 1, float64, VsrD(0), float_round_down, 1) VSX_ROUND(xsrdpip, 1, float64, VsrD(0), float_round_up, 1) VSX_ROUND(xsrdpiz, 1, float64, VsrD(0), float_round_to_zero, 1) -VSX_ROUND(xvrdpi, 2, float64, VsrD(i), float_round_nearest_even, 0) +VSX_ROUND(xvrdpi, 2, float64, VsrD(i), float_round_ties_away, 0) VSX_ROUND(xvrdpic, 2, float64, VsrD(i), FLOAT_ROUND_CURRENT, 0) VSX_ROUND(xvrdpim, 2, float64, VsrD(i), float_round_down, 0) VSX_ROUND(xvrdpip, 2, float64, VsrD(i), float_round_up, 0) VSX_ROUND(xvrdpiz, 2, float64, VsrD(i), float_round_to_zero, 0) -VSX_ROUND(xvrspi, 4, float32, VsrW(i), float_round_nearest_even, 0) +VSX_ROUND(xvrspi, 4, float32, VsrW(i), float_round_ties_away, 0) VSX_ROUND(xvrspic, 4, float32, VsrW(i), FLOAT_ROUND_CURRENT, 0) VSX_ROUND(xvrspim, 4, float32, VsrW(i), float_round_down, 0) VSX_ROUND(xvrspip, 4, float32, VsrW(i), float_round_up, 0) diff --git a/target-ppc/mmu-hash64.c b/target-ppc/mmu-hash64.c index 3b1357a648..82c2186bcf 100644 --- a/target-ppc/mmu-hash64.c +++ b/target-ppc/mmu-hash64.c @@ -450,31 +450,47 @@ void ppc_hash64_stop_access(PowerPCCPU *cpu, uint64_t token) } } -/* Returns the effective page shift or 0. MPSS isn't supported yet so - * this will always be the slb_pshift or 0 - */ -static uint32_t ppc_hash64_pte_size_decode(uint64_t pte1, uint32_t slb_pshift) +static unsigned hpte_page_shift(const struct ppc_one_seg_page_size *sps, + uint64_t pte0, uint64_t pte1) { - switch (slb_pshift) { - case 12: + int i; + + if (!(pte0 & HPTE64_V_LARGE)) { + if (sps->page_shift != 12) { + /* 4kiB page in a non 4kiB segment */ + return 0; + } + /* Normal 4kiB page */ return 12; - case 16: - if ((pte1 & 0xf000) == 0x1000) { - return 16; + } + + for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { + const struct ppc_one_page_size *ps = &sps->enc[i]; + uint64_t mask; + + if (!ps->page_shift) { + break; } - return 0; - case 24: - if ((pte1 & 0xff000) == 0) { - return 24; + + if (ps->page_shift == 12) { + /* L bit is set so this can't be a 4kiB page */ + continue; + } + + mask = ((1ULL << ps->page_shift) - 1) & HPTE64_R_RPN; + + if ((pte1 & mask) == (ps->pte_enc << HPTE64_R_RPN_SHIFT)) { + return ps->page_shift; } - return 0; } - return 0; + + return 0; /* Bad page size encoding */ } static hwaddr ppc_hash64_pteg_search(PowerPCCPU *cpu, hwaddr hash, - uint32_t slb_pshift, bool secondary, - target_ulong ptem, ppc_hash_pte64_t *pte) + const struct ppc_one_seg_page_size *sps, + target_ulong ptem, + ppc_hash_pte64_t *pte, unsigned *pshift) { CPUPPCState *env = &cpu->env; int i; @@ -491,11 +507,17 @@ static hwaddr ppc_hash64_pteg_search(PowerPCCPU *cpu, hwaddr hash, pte0 = ppc_hash64_load_hpte0(cpu, token, i); pte1 = ppc_hash64_load_hpte1(cpu, token, i); - if ((pte0 & HPTE64_V_VALID) - && (secondary == !!(pte0 & HPTE64_V_SECONDARY)) - && HPTE64_V_COMPARE(pte0, ptem)) { - uint32_t pshift = ppc_hash64_pte_size_decode(pte1, slb_pshift); - if (pshift == 0) { + /* This compares V, B, H (secondary) and the AVPN */ + if (HPTE64_V_COMPARE(pte0, ptem)) { + *pshift = hpte_page_shift(sps, pte0, pte1); + /* + * If there is no match, ignore the PTE, it could simply + * be for a different segment size encoding and the + * architecture specifies we should not match. Linux will + * potentially leave behind PTEs for the wrong base page + * size when demoting segments. + */ + if (*pshift == 0) { continue; } /* We don't do anything with pshift yet as qemu TLB only deals @@ -516,31 +538,40 @@ static hwaddr ppc_hash64_pteg_search(PowerPCCPU *cpu, hwaddr hash, static hwaddr ppc_hash64_htab_lookup(PowerPCCPU *cpu, ppc_slb_t *slb, target_ulong eaddr, - ppc_hash_pte64_t *pte) + ppc_hash_pte64_t *pte, unsigned *pshift) { CPUPPCState *env = &cpu->env; hwaddr pte_offset; hwaddr hash; uint64_t vsid, epnmask, epn, ptem; + const struct ppc_one_seg_page_size *sps = slb->sps; /* The SLB store path should prevent any bad page size encodings * getting in there, so: */ - assert(slb->sps); + assert(sps); + + /* If ISL is set in LPCR we need to clamp the page size to 4K */ + if (env->spr[SPR_LPCR] & LPCR_ISL) { + /* We assume that when using TCG, 4k is first entry of SPS */ + sps = &env->sps.sps[0]; + assert(sps->page_shift == 12); + } - epnmask = ~((1ULL << slb->sps->page_shift) - 1); + epnmask = ~((1ULL << sps->page_shift) - 1); if (slb->vsid & SLB_VSID_B) { /* 1TB segment */ vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT_1T; epn = (eaddr & ~SEGMENT_MASK_1T) & epnmask; - hash = vsid ^ (vsid << 25) ^ (epn >> slb->sps->page_shift); + hash = vsid ^ (vsid << 25) ^ (epn >> sps->page_shift); } else { /* 256M segment */ vsid = (slb->vsid & SLB_VSID_VSID) >> SLB_VSID_SHIFT; epn = (eaddr & ~SEGMENT_MASK_256M) & epnmask; - hash = vsid ^ (epn >> slb->sps->page_shift); + hash = vsid ^ (epn >> sps->page_shift); } ptem = (slb->vsid & SLB_VSID_PTEM) | ((epn >> 16) & HPTE64_V_AVPN); + ptem |= HPTE64_V_VALID; /* Page address translation */ qemu_log_mask(CPU_LOG_MMU, @@ -554,70 +585,30 @@ static hwaddr ppc_hash64_htab_lookup(PowerPCCPU *cpu, " vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx " hash=" TARGET_FMT_plx "\n", env->htab_base, env->htab_mask, vsid, ptem, hash); - pte_offset = ppc_hash64_pteg_search(cpu, hash, slb->sps->page_shift, - 0, ptem, pte); + pte_offset = ppc_hash64_pteg_search(cpu, hash, sps, ptem, pte, pshift); if (pte_offset == -1) { /* Secondary PTEG lookup */ + ptem |= HPTE64_V_SECONDARY; qemu_log_mask(CPU_LOG_MMU, "1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx " vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx " hash=" TARGET_FMT_plx "\n", env->htab_base, env->htab_mask, vsid, ptem, ~hash); - pte_offset = ppc_hash64_pteg_search(cpu, ~hash, slb->sps->page_shift, 1, - ptem, pte); + pte_offset = ppc_hash64_pteg_search(cpu, ~hash, sps, ptem, pte, pshift); } return pte_offset; } -static unsigned hpte_page_shift(const struct ppc_one_seg_page_size *sps, - uint64_t pte0, uint64_t pte1) -{ - int i; - - if (!(pte0 & HPTE64_V_LARGE)) { - if (sps->page_shift != 12) { - /* 4kiB page in a non 4kiB segment */ - return 0; - } - /* Normal 4kiB page */ - return 12; - } - - for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { - const struct ppc_one_page_size *ps = &sps->enc[i]; - uint64_t mask; - - if (!ps->page_shift) { - break; - } - - if (ps->page_shift == 12) { - /* L bit is set so this can't be a 4kiB page */ - continue; - } - - mask = ((1ULL << ps->page_shift) - 1) & HPTE64_R_RPN; - - if ((pte1 & mask) == (ps->pte_enc << HPTE64_R_RPN_SHIFT)) { - return ps->page_shift; - } - } - - return 0; /* Bad page size encoding */ -} - unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu, - uint64_t pte0, uint64_t pte1, - unsigned *seg_page_shift) + uint64_t pte0, uint64_t pte1) { CPUPPCState *env = &cpu->env; int i; if (!(pte0 & HPTE64_V_LARGE)) { - *seg_page_shift = 12; return 12; } @@ -635,12 +626,10 @@ unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu, shift = hpte_page_shift(sps, pte0, pte1); if (shift) { - *seg_page_shift = sps->page_shift; return shift; } } - *seg_page_shift = 0; return 0; } @@ -701,11 +690,52 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, assert((rwx == 0) || (rwx == 1) || (rwx == 2)); + /* Note on LPCR usage: 970 uses HID4, but our special variant + * of store_spr copies relevant fields into env->spr[SPR_LPCR]. + * Similarily we filter unimplemented bits when storing into + * LPCR depending on the MMU version. This code can thus just + * use the LPCR "as-is". + */ + /* 1. Handle real mode accesses */ if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) { - /* Translation is off */ - /* In real mode the top 4 effective address bits are ignored */ + /* Translation is supposedly "off" */ + /* In real mode the top 4 effective address bits are (mostly) ignored */ raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL; + + /* In HV mode, add HRMOR if top EA bit is clear */ + if (msr_hv || !env->has_hv_mode) { + if (!(eaddr >> 63)) { + raddr |= env->spr[SPR_HRMOR]; + } + } else { + /* Otherwise, check VPM for RMA vs VRMA */ + if (env->spr[SPR_LPCR] & LPCR_VPM0) { + slb = &env->vrma_slb; + if (slb->sps) { + goto skip_slb_search; + } + /* Not much else to do here */ + cs->exception_index = POWERPC_EXCP_MCHECK; + env->error_code = 0; + return 1; + } else if (raddr < env->rmls) { + /* RMA. Check bounds in RMLS */ + raddr |= env->spr[SPR_RMOR]; + } else { + /* The access failed, generate the approriate interrupt */ + if (rwx == 2) { + ppc_hash64_set_isi(cs, env, 0x08000000); + } else { + dsisr = 0x08000000; + if (rwx == 1) { + dsisr |= 0x02000000; + } + ppc_hash64_set_dsi(cs, env, eaddr, dsisr); + } + return 1; + } + } tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx, TARGET_PAGE_SIZE); @@ -714,7 +744,6 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, /* 2. Translation is on, so look up the SLB */ slb = slb_lookup(cpu, eaddr); - if (!slb) { if (rwx == 2) { cs->exception_index = POWERPC_EXCP_ISEG; @@ -727,6 +756,8 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, return 1; } +skip_slb_search: + /* 3. Check for segment level no-execute violation */ if ((rwx == 2) && (slb->vsid & SLB_VSID_N)) { ppc_hash64_set_isi(cs, env, 0x10000000); @@ -734,7 +765,7 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, } /* 4. Locate the PTE in the hash table */ - pte_offset = ppc_hash64_htab_lookup(cpu, slb, eaddr, &pte); + pte_offset = ppc_hash64_htab_lookup(cpu, slb, eaddr, &pte, &apshift); if (pte_offset == -1) { dsisr = 0x40000000; if (rwx == 2) { @@ -750,18 +781,6 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, qemu_log_mask(CPU_LOG_MMU, "found PTE at offset %08" HWADDR_PRIx "\n", pte_offset); - /* Validate page size encoding */ - apshift = hpte_page_shift(slb->sps, pte.pte0, pte.pte1); - if (!apshift) { - error_report("Bad page size encoding in HPTE 0x%"PRIx64" - 0x%"PRIx64 - " @ 0x%"HWADDR_PRIx, pte.pte0, pte.pte1, pte_offset); - /* Not entirely sure what the right action here, but machine - * check seems reasonable */ - cs->exception_index = POWERPC_EXCP_MCHECK; - env->error_code = 0; - return 1; - } - /* 5. Check access permissions */ pp_prot = ppc_hash64_pte_prot(cpu, slb, pte); @@ -821,27 +840,41 @@ hwaddr ppc_hash64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong addr) { CPUPPCState *env = &cpu->env; ppc_slb_t *slb; - hwaddr pte_offset; + hwaddr pte_offset, raddr; ppc_hash_pte64_t pte; unsigned apshift; + /* Handle real mode */ if (msr_dr == 0) { /* In real mode the top 4 effective address bits are ignored */ - return addr & 0x0FFFFFFFFFFFFFFFULL; - } + raddr = addr & 0x0FFFFFFFFFFFFFFFULL; - slb = slb_lookup(cpu, addr); - if (!slb) { - return -1; - } + /* In HV mode, add HRMOR if top EA bit is clear */ + if ((msr_hv || !env->has_hv_mode) && !(addr >> 63)) { + return raddr | env->spr[SPR_HRMOR]; + } - pte_offset = ppc_hash64_htab_lookup(cpu, slb, addr, &pte); - if (pte_offset == -1) { - return -1; + /* Otherwise, check VPM for RMA vs VRMA */ + if (env->spr[SPR_LPCR] & LPCR_VPM0) { + slb = &env->vrma_slb; + if (!slb->sps) { + return -1; + } + } else if (raddr < env->rmls) { + /* RMA. Check bounds in RMLS */ + return raddr | env->spr[SPR_RMOR]; + } else { + return -1; + } + } else { + slb = slb_lookup(cpu, addr); + if (!slb) { + return -1; + } } - apshift = hpte_page_shift(slb->sps, pte.pte0, pte.pte1); - if (!apshift) { + pte_offset = ppc_hash64_htab_lookup(cpu, slb, addr, &pte, &apshift); + if (pte_offset == -1) { return -1; } @@ -883,6 +916,90 @@ void ppc_hash64_tlb_flush_hpte(PowerPCCPU *cpu, tlb_flush(CPU(cpu), 1); } +void ppc_hash64_update_rmls(CPUPPCState *env) +{ + uint64_t lpcr = env->spr[SPR_LPCR]; + + /* + * This is the full 4 bits encoding of POWER8. Previous + * CPUs only support a subset of these but the filtering + * is done when writing LPCR + */ + switch ((lpcr & LPCR_RMLS) >> LPCR_RMLS_SHIFT) { + case 0x8: /* 32MB */ + env->rmls = 0x2000000ull; + break; + case 0x3: /* 64MB */ + env->rmls = 0x4000000ull; + break; + case 0x7: /* 128MB */ + env->rmls = 0x8000000ull; + break; + case 0x4: /* 256MB */ + env->rmls = 0x10000000ull; + break; + case 0x2: /* 1GB */ + env->rmls = 0x40000000ull; + break; + case 0x1: /* 16GB */ + env->rmls = 0x400000000ull; + break; + default: + /* What to do here ??? */ + env->rmls = 0; + } +} + +void ppc_hash64_update_vrma(CPUPPCState *env) +{ + const struct ppc_one_seg_page_size *sps = NULL; + target_ulong esid, vsid, lpcr; + ppc_slb_t *slb = &env->vrma_slb; + uint32_t vrmasd; + int i; + + /* First clear it */ + slb->esid = slb->vsid = 0; + slb->sps = NULL; + + /* Is VRMA enabled ? */ + lpcr = env->spr[SPR_LPCR]; + if (!(lpcr & LPCR_VPM0)) { + return; + } + + /* Make one up. Mostly ignore the ESID which will not be + * needed for translation + */ + vsid = SLB_VSID_VRMA; + vrmasd = (lpcr & LPCR_VRMASD) >> LPCR_VRMASD_SHIFT; + vsid |= (vrmasd << 4) & (SLB_VSID_L | SLB_VSID_LP); + esid = SLB_ESID_V; + + for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { + const struct ppc_one_seg_page_size *sps1 = &env->sps.sps[i]; + + if (!sps1->page_shift) { + break; + } + + if ((vsid & SLB_VSID_LLP_MASK) == sps1->slb_enc) { + sps = sps1; + break; + } + } + + if (!sps) { + error_report("Bad page size encoding esid 0x"TARGET_FMT_lx + " vsid 0x"TARGET_FMT_lx, esid, vsid); + return; + } + + slb->vsid = vsid; + slb->esid = esid; + slb->sps = sps; +} + void helper_store_lpcr(CPUPPCState *env, target_ulong val) { uint64_t lpcr = 0; @@ -938,4 +1055,6 @@ void helper_store_lpcr(CPUPPCState *env, target_ulong val) ; } env->spr[SPR_LPCR] = lpcr; + ppc_hash64_update_rmls(env); + ppc_hash64_update_vrma(env); } diff --git a/target-ppc/mmu-hash64.h b/target-ppc/mmu-hash64.h index 6423b9f791..3a7476b30a 100644 --- a/target-ppc/mmu-hash64.h +++ b/target-ppc/mmu-hash64.h @@ -17,8 +17,9 @@ void ppc_hash64_tlb_flush_hpte(PowerPCCPU *cpu, target_ulong pte_index, target_ulong pte0, target_ulong pte1); unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu, - uint64_t pte0, uint64_t pte1, - unsigned *seg_page_shift); + uint64_t pte0, uint64_t pte1); +void ppc_hash64_update_vrma(CPUPPCState *env); +void ppc_hash64_update_rmls(CPUPPCState *env); #endif /* @@ -37,6 +38,7 @@ unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu, #define SLB_VSID_B_256M 0x0000000000000000ULL #define SLB_VSID_B_1T 0x4000000000000000ULL #define SLB_VSID_VSID 0x3FFFFFFFFFFFF000ULL +#define SLB_VSID_VRMA (0x0001FFFFFF000000ULL | SLB_VSID_B_1T) #define SLB_VSID_PTEM (SLB_VSID_B | SLB_VSID_VSID) #define SLB_VSID_KS 0x0000000000000800ULL #define SLB_VSID_KP 0x0000000000000400ULL @@ -63,7 +65,7 @@ unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu, #define HPTE64_V_AVPN_SHIFT 7 #define HPTE64_V_AVPN 0x3fffffffffffff80ULL #define HPTE64_V_AVPN_VAL(x) (((x) & HPTE64_V_AVPN) >> HPTE64_V_AVPN_SHIFT) -#define HPTE64_V_COMPARE(x, y) (!(((x) ^ (y)) & 0xffffffffffffff80ULL)) +#define HPTE64_V_COMPARE(x, y) (!(((x) ^ (y)) & 0xffffffffffffff83ULL)) #define HPTE64_V_LARGE 0x0000000000000004ULL #define HPTE64_V_SECONDARY 0x0000000000000002ULL #define HPTE64_V_VALID 0x0000000000000001ULL diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 843f19b748..8f257fb74a 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -8791,11 +8791,19 @@ void cpu_ppc_set_papr(PowerPCCPU *cpu) /* Set emulated LPCR to not send interrupts to hypervisor. Note that * under KVM, the actual HW LPCR will be set differently by KVM itself, * the settings below ensure proper operations with TCG in absence of - * a real hypervisor + * a real hypervisor. + * + * Clearing VPM0 will also cause us to use RMOR in mmu-hash64.c for + * real mode accesses, which thankfully defaults to 0 and isn't + * accessible in guest mode. */ lpcr->default_value &= ~(LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV); lpcr->default_value |= LPCR_LPES0 | LPCR_LPES1; + /* Set RMLS to the max (ie, 16G) */ + lpcr->default_value &= ~LPCR_RMLS; + lpcr->default_value |= 1ull << LPCR_RMLS_SHIFT; + /* P7 and P8 has slightly different PECE bits, mostly because P8 adds * bit 47 and 48 which are reserved on P7. Here we set them all, which * will work as expected for both implementations @@ -8811,6 +8819,10 @@ void cpu_ppc_set_papr(PowerPCCPU *cpu) /* Set a full AMOR so guest can use the AMR as it sees fit */ env->spr[SPR_AMOR] = amor->default_value = 0xffffffffffffffffull; + /* Update some env bits based on new LPCR value */ + ppc_hash64_update_rmls(env); + ppc_hash64_update_vrma(env); + /* Tell KVM that we're in PAPR mode */ if (kvm_enabled()) { kvmppc_set_papr(cpu); @@ -9516,7 +9528,7 @@ static void ppc_cpu_realizefn(DeviceState *dev, Error **errp) PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); Error *local_err = NULL; #if !defined(CONFIG_USER_ONLY) - int max_smt = kvm_enabled() ? kvmppc_smt_threads() : 1; + int max_smt = kvmppc_smt_threads(); #endif #if !defined(CONFIG_USER_ONLY) diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c index 45e94ca48a..2991bff6a4 100644 --- a/target-s390x/kvm.c +++ b/target-s390x/kvm.c @@ -2246,10 +2246,10 @@ int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, uint64_t address, uint32_t data, PCIDevice *dev) { S390PCIBusDevice *pbdev; - uint32_t fid = data >> ZPCI_MSI_VEC_BITS; + uint32_t idx = data >> ZPCI_MSI_VEC_BITS; uint32_t vec = data & ZPCI_MSI_VEC_MASK; - pbdev = s390_pci_find_dev_by_fid(fid); + pbdev = s390_pci_find_dev_by_idx(idx); if (!pbdev) { DPRINTF("add_msi_route no dev\n"); return -ENODEV; diff --git a/target-sparc/cpu.c b/target-sparc/cpu.c index 5b74cfcd31..e4089f2074 100644 --- a/target-sparc/cpu.c +++ b/target-sparc/cpu.c @@ -101,9 +101,11 @@ static void cpu_sparc_disas_set_info(CPUState *cpu, disassemble_info *info) #endif } +static void sparc_cpu_parse_features(CPUState *cs, char *features, + Error **errp); + static int cpu_sparc_register(SPARCCPU *cpu, const char *cpu_model) { - CPUClass *cc = CPU_GET_CLASS(cpu); CPUSPARCState *env = &cpu->env; char *s = g_strdup(cpu_model); char *featurestr, *name = strtok(s, ","); @@ -119,7 +121,7 @@ static int cpu_sparc_register(SPARCCPU *cpu, const char *cpu_model) memcpy(env->def, def, sizeof(*def)); featurestr = strtok(NULL, ","); - cc->parse_features(CPU(cpu), featurestr, &err); + sparc_cpu_parse_features(CPU(cpu), featurestr, &err); g_free(s); if (err) { error_report_err(err); @@ -840,7 +842,6 @@ static void sparc_cpu_class_init(ObjectClass *oc, void *data) scc->parent_reset = cc->reset; cc->reset = sparc_cpu_reset; - cc->parse_features = sparc_cpu_parse_features; cc->has_work = sparc_cpu_has_work; cc->do_interrupt = sparc_cpu_do_interrupt; cc->cpu_exec_interrupt = sparc_cpu_exec_interrupt; diff --git a/tcg/aarch64/tcg-target.inc.c b/tcg/aarch64/tcg-target.inc.c index 1447f7c216..08b2d031aa 100644 --- a/tcg/aarch64/tcg-target.inc.c +++ b/tcg/aarch64/tcg-target.inc.c @@ -716,6 +716,16 @@ static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, arg, arg1, arg2); } +static inline bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, + TCGReg base, intptr_t ofs) +{ + if (val == 0) { + tcg_out_st(s, type, TCG_REG_XZR, base, ofs); + return true; + } + return false; +} + static inline void tcg_out_bfm(TCGContext *s, TCGType ext, TCGReg rd, TCGReg rn, unsigned int a, unsigned int b) { @@ -1071,19 +1081,20 @@ static void tcg_out_tlb_read(TCGContext *s, TCGReg addr_reg, TCGMemOp opc, int tlb_offset = is_read ? offsetof(CPUArchState, tlb_table[mem_index][0].addr_read) : offsetof(CPUArchState, tlb_table[mem_index][0].addr_write); - int s_mask = (1 << (opc & MO_SIZE)) - 1; + int a_bits = get_alignment_bits(opc); TCGReg base = TCG_AREG0, x3; uint64_t tlb_mask; /* For aligned accesses, we check the first byte and include the alignment bits within the address. For unaligned access, we check that we don't cross pages using the address of the last byte of the access. */ - if ((opc & MO_AMASK) == MO_ALIGN || s_mask == 0) { - tlb_mask = TARGET_PAGE_MASK | s_mask; + if (a_bits >= 0) { + /* A byte access or an alignment check required */ + tlb_mask = TARGET_PAGE_MASK | ((1 << a_bits) - 1); x3 = addr_reg; } else { tcg_out_insn(s, 3401, ADDI, TARGET_LONG_BITS == 64, - TCG_REG_X3, addr_reg, s_mask); + TCG_REG_X3, addr_reg, (1 << (opc & MO_SIZE)) - 1); tlb_mask = TARGET_PAGE_MASK; x3 = TCG_REG_X3; } diff --git a/tcg/arm/tcg-target.inc.c b/tcg/arm/tcg-target.inc.c index f9f54c64c6..172febafdd 100644 --- a/tcg/arm/tcg-target.inc.c +++ b/tcg/arm/tcg-target.inc.c @@ -2046,6 +2046,12 @@ static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, tcg_out_st32(s, COND_AL, arg, arg1, arg2); } +static inline bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, + TCGReg base, intptr_t ofs) +{ + return false; +} + static inline void tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) { diff --git a/tcg/i386/tcg-target.inc.c b/tcg/i386/tcg-target.inc.c index 317484cb5d..6f8cdca756 100644 --- a/tcg/i386/tcg-target.inc.c +++ b/tcg/i386/tcg-target.inc.c @@ -710,12 +710,19 @@ static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, tcg_out_modrm_offset(s, opc, arg, arg1, arg2); } -static inline void tcg_out_sti(TCGContext *s, TCGType type, TCGReg base, - tcg_target_long ofs, tcg_target_long val) +static bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, + TCGReg base, intptr_t ofs) { - int opc = OPC_MOVL_EvIz + (type == TCG_TYPE_I64 ? P_REXW : 0); - tcg_out_modrm_offset(s, opc, 0, base, ofs); + int rexw = 0; + if (TCG_TARGET_REG_BITS == 64 && type == TCG_TYPE_I64) { + if (val != (int32_t)val) { + return false; + } + rexw = P_REXW; + } + tcg_out_modrm_offset(s, OPC_MOVL_EvIz | rexw, 0, base, ofs); tcg_out32(s, val); + return true; } static void tcg_out_shifti(TCGContext *s, int subopc, int reg, int count) @@ -1195,8 +1202,8 @@ static inline void tcg_out_tlb_load(TCGContext *s, TCGReg addrlo, TCGReg addrhi, TCGType ttype = TCG_TYPE_I32; TCGType tlbtype = TCG_TYPE_I32; int trexw = 0, hrexw = 0, tlbrexw = 0; - int s_mask = (1 << (opc & MO_SIZE)) - 1; - bool aligned = (opc & MO_AMASK) == MO_ALIGN || s_mask == 0; + int a_bits = get_alignment_bits(opc); + target_ulong tlb_mask; if (TCG_TARGET_REG_BITS == 64) { if (TARGET_LONG_BITS == 64) { @@ -1213,19 +1220,22 @@ static inline void tcg_out_tlb_load(TCGContext *s, TCGReg addrlo, TCGReg addrhi, } tcg_out_mov(s, tlbtype, r0, addrlo); - if (aligned) { + if (a_bits >= 0) { + /* A byte access or an alignment check required */ tcg_out_mov(s, ttype, r1, addrlo); + tlb_mask = TARGET_PAGE_MASK | ((1 << a_bits) - 1); } else { /* For unaligned access check that we don't cross pages using the page address of the last byte. */ - tcg_out_modrm_offset(s, OPC_LEA + trexw, r1, addrlo, s_mask); + tcg_out_modrm_offset(s, OPC_LEA + trexw, r1, addrlo, + (1 << (opc & MO_SIZE)) - 1); + tlb_mask = TARGET_PAGE_MASK; } tcg_out_shifti(s, SHIFT_SHR + tlbrexw, r0, TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); - tgen_arithi(s, ARITH_AND + trexw, r1, - TARGET_PAGE_MASK | (aligned ? s_mask : 0), 0); + tgen_arithi(s, ARITH_AND + trexw, r1, tlb_mask, 0); tgen_arithi(s, ARITH_AND + tlbrexw, r0, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS, 0); @@ -1321,10 +1331,10 @@ static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) ofs += 4; } - tcg_out_sti(s, TCG_TYPE_I32, TCG_REG_ESP, ofs, oi); + tcg_out_sti(s, TCG_TYPE_I32, oi, TCG_REG_ESP, ofs); ofs += 4; - tcg_out_sti(s, TCG_TYPE_PTR, TCG_REG_ESP, ofs, (uintptr_t)l->raddr); + tcg_out_sti(s, TCG_TYPE_PTR, (uintptr_t)l->raddr, TCG_REG_ESP, ofs); } else { tcg_out_mov(s, TCG_TYPE_PTR, tcg_target_call_iarg_regs[0], TCG_AREG0); /* The second argument is already loaded with addrlo. */ @@ -1413,7 +1423,7 @@ static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) ofs += 4; } - tcg_out_sti(s, TCG_TYPE_I32, TCG_REG_ESP, ofs, oi); + tcg_out_sti(s, TCG_TYPE_I32, oi, TCG_REG_ESP, ofs); ofs += 4; retaddr = TCG_REG_EAX; diff --git a/tcg/ia64/tcg-target.inc.c b/tcg/ia64/tcg-target.inc.c index 395223e340..c91f39281b 100644 --- a/tcg/ia64/tcg-target.inc.c +++ b/tcg/ia64/tcg-target.inc.c @@ -973,6 +973,16 @@ static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, } } +static inline bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, + TCGReg base, intptr_t ofs) +{ + if (val == 0) { + tcg_out_st(s, type, TCG_REG_R0, base, ofs); + return true; + } + return false; +} + static inline void tcg_out_alu(TCGContext *s, uint64_t opc_a1, uint64_t opc_a3, TCGReg ret, TCGArg arg1, int const_arg1, TCGArg arg2, int const_arg2) diff --git a/tcg/mips/tcg-target.inc.c b/tcg/mips/tcg-target.inc.c index 50e98ea63a..2f9be48139 100644 --- a/tcg/mips/tcg-target.inc.c +++ b/tcg/mips/tcg-target.inc.c @@ -576,6 +576,16 @@ static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, tcg_out_ldst(s, OPC_SW, arg, arg1, arg2); } +static inline bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, + TCGReg base, intptr_t ofs) +{ + if (val == 0) { + tcg_out_st(s, type, TCG_REG_ZERO, base, ofs); + return true; + } + return false; +} + static inline void tcg_out_addi(TCGContext *s, TCGReg reg, TCGArg val) { if (val == (int16_t)val) { diff --git a/tcg/ppc/tcg-target.inc.c b/tcg/ppc/tcg-target.inc.c index da100528ab..eaf1bd9bfd 100644 --- a/tcg/ppc/tcg-target.inc.c +++ b/tcg/ppc/tcg-target.inc.c @@ -857,6 +857,12 @@ static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, tcg_out_mem_long(s, opi, opx, arg, arg1, arg2); } +static inline bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, + TCGReg base, intptr_t ofs) +{ + return false; +} + static void tcg_out_cmp(TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, int const_arg2, int cr, TCGType type) { @@ -1399,6 +1405,7 @@ static TCGReg tcg_out_tlb_read(TCGContext *s, TCGMemOp opc, int add_off = offsetof(CPUArchState, tlb_table[mem_index][0].addend); TCGReg base = TCG_AREG0; TCGMemOp s_bits = opc & MO_SIZE; + int a_bits = get_alignment_bits(opc); /* Extract the page index, shifted into place for tlb index. */ if (TCG_TARGET_REG_BITS == 64) { @@ -1456,14 +1463,17 @@ static TCGReg tcg_out_tlb_read(TCGContext *s, TCGMemOp opc, * the bottom bits and thus trigger a comparison failure on * unaligned accesses */ + if (a_bits < 0) { + a_bits = s_bits; + } tcg_out_rlw(s, RLWINM, TCG_REG_R0, addrlo, 0, - (32 - s_bits) & 31, 31 - TARGET_PAGE_BITS); - } else if (s_bits) { - /* > byte access, we need to handle alignment */ - if ((opc & MO_AMASK) == MO_ALIGN) { + (32 - a_bits) & 31, 31 - TARGET_PAGE_BITS); + } else if (a_bits) { + /* More than byte access, we need to handle alignment */ + if (a_bits > 0) { /* Alignment required by the front-end, same as 32-bits */ tcg_out_rld(s, RLDICL, TCG_REG_R0, addrlo, - 64 - TARGET_PAGE_BITS, TARGET_PAGE_BITS - s_bits); + 64 - TARGET_PAGE_BITS, TARGET_PAGE_BITS - a_bits); tcg_out_rld(s, RLDICL, TCG_REG_R0, TCG_REG_R0, TARGET_PAGE_BITS, 0); } else { /* We support unaligned accesses, we need to make sure we fail diff --git a/tcg/s390/tcg-target.inc.c b/tcg/s390/tcg-target.inc.c index e0a60e618c..5a7495b063 100644 --- a/tcg/s390/tcg-target.inc.c +++ b/tcg/s390/tcg-target.inc.c @@ -798,6 +798,12 @@ static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg data, } } +static inline bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, + TCGReg base, intptr_t ofs) +{ + return false; +} + /* load data from an absolute host address */ static void tcg_out_ld_abs(TCGContext *s, TCGType type, TCGReg dest, void *abs) { @@ -1499,18 +1505,19 @@ QEMU_BUILD_BUG_ON(offsetof(CPUArchState, tlb_table[NB_MMU_MODES - 1][1]) static TCGReg tcg_out_tlb_read(TCGContext* s, TCGReg addr_reg, TCGMemOp opc, int mem_index, bool is_ld) { - int s_mask = (1 << (opc & MO_SIZE)) - 1; + int a_bits = get_alignment_bits(opc); int ofs, a_off; uint64_t tlb_mask; /* For aligned accesses, we check the first byte and include the alignment bits within the address. For unaligned access, we check that we don't cross pages using the address of the last byte of the access. */ - if ((opc & MO_AMASK) == MO_ALIGN || s_mask == 0) { + if (a_bits >= 0) { + /* A byte access or an alignment check required */ a_off = 0; - tlb_mask = TARGET_PAGE_MASK | s_mask; + tlb_mask = TARGET_PAGE_MASK | ((1 << a_bits) - 1); } else { - a_off = s_mask; + a_off = (1 << (opc & MO_SIZE)) - 1; tlb_mask = TARGET_PAGE_MASK; } diff --git a/tcg/sparc/tcg-target.inc.c b/tcg/sparc/tcg-target.inc.c index 9938a5085e..8e98172ca0 100644 --- a/tcg/sparc/tcg-target.inc.c +++ b/tcg/sparc/tcg-target.inc.c @@ -504,6 +504,16 @@ static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, tcg_out_ldst(s, arg, arg1, arg2, (type == TCG_TYPE_I32 ? STW : STX)); } +static inline bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, + TCGReg base, intptr_t ofs) +{ + if (val == 0) { + tcg_out_st(s, type, TCG_REG_G0, base, ofs); + return true; + } + return false; +} + static void tcg_out_ld_ptr(TCGContext *s, TCGReg ret, uintptr_t arg) { tcg_out_movi(s, TCG_TYPE_PTR, ret, arg & ~0x3ff); diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 569cdc642e..293b854370 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1851,6 +1851,9 @@ void tcg_gen_goto_tb(unsigned idx) static inline TCGMemOp tcg_canonicalize_memop(TCGMemOp op, bool is64, bool st) { + /* Trigger the asserts within as early as possible. */ + (void)get_alignment_bits(op); + switch (op & MO_SIZE) { case MO_8: op &= ~MO_BSWAP; @@ -108,6 +108,8 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, const int *const_args); static void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, TCGReg arg1, intptr_t arg2); +static bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, + TCGReg base, intptr_t ofs); static void tcg_out_call(TCGContext *s, tcg_insn_unit *target); static int tcg_target_const_match(tcg_target_long val, TCGType type, const TCGArgConstraint *arg_ct); @@ -557,7 +559,7 @@ int tcg_global_mem_new_internal(TCGType type, TCGv_ptr base, ts2->mem_offset = offset + (1 - bigendian) * 4; pstrcpy(buf, sizeof(buf), name); pstrcat(buf, sizeof(buf), "_1"); - ts->name = strdup(buf); + ts2->name = strdup(buf); } else { ts->base_type = type; ts->type = type; @@ -997,6 +999,22 @@ static const char * const ldst_name[] = [MO_BEQ] = "beq", }; +static const char * const alignment_name[(MO_AMASK >> MO_ASHIFT) + 1] = { +#ifdef ALIGNED_ONLY + [MO_UNALN >> MO_ASHIFT] = "un+", + [MO_ALIGN >> MO_ASHIFT] = "", +#else + [MO_UNALN >> MO_ASHIFT] = "", + [MO_ALIGN >> MO_ASHIFT] = "al+", +#endif + [MO_ALIGN_2 >> MO_ASHIFT] = "al2+", + [MO_ALIGN_4 >> MO_ASHIFT] = "al4+", + [MO_ALIGN_8 >> MO_ASHIFT] = "al8+", + [MO_ALIGN_16 >> MO_ASHIFT] = "al16+", + [MO_ALIGN_32 >> MO_ASHIFT] = "al32+", + [MO_ALIGN_64 >> MO_ASHIFT] = "al64+", +}; + void tcg_dump_ops(TCGContext *s) { char buf[128]; @@ -1098,14 +1116,8 @@ void tcg_dump_ops(TCGContext *s) if (op & ~(MO_AMASK | MO_BSWAP | MO_SSIZE)) { qemu_log(",$0x%x,%u", op, ix); } else { - const char *s_al = "", *s_op; - if (op & MO_AMASK) { - if ((op & MO_AMASK) == MO_ALIGN) { - s_al = "al+"; - } else { - s_al = "un+"; - } - } + const char *s_al, *s_op; + s_al = alignment_name[(op & MO_AMASK) >> MO_ASHIFT]; s_op = ldst_name[op & (MO_BSWAP | MO_SSIZE)]; qemu_log(",%s%s,%u", s_al, s_op, ix); } @@ -1680,35 +1692,89 @@ static void temp_allocate_frame(TCGContext *s, int temp) static void temp_load(TCGContext *, TCGTemp *, TCGRegSet, TCGRegSet); -/* sync register 'reg' by saving it to the corresponding temporary */ -static void tcg_reg_sync(TCGContext *s, TCGReg reg, TCGRegSet allocated_regs) +/* Mark a temporary as free or dead. If 'free_or_dead' is negative, + mark it free; otherwise mark it dead. */ +static void temp_free_or_dead(TCGContext *s, TCGTemp *ts, int free_or_dead) { - TCGTemp *ts = s->reg_to_temp[reg]; + if (ts->fixed_reg) { + return; + } + if (ts->val_type == TEMP_VAL_REG) { + s->reg_to_temp[ts->reg] = NULL; + } + ts->val_type = (free_or_dead < 0 + || ts->temp_local + || temp_idx(s, ts) < s->nb_globals + ? TEMP_VAL_MEM : TEMP_VAL_DEAD); +} - tcg_debug_assert(ts->val_type == TEMP_VAL_REG); - if (!ts->mem_coherent && !ts->fixed_reg) { +/* Mark a temporary as dead. */ +static inline void temp_dead(TCGContext *s, TCGTemp *ts) +{ + temp_free_or_dead(s, ts, 1); +} + +/* Sync a temporary to memory. 'allocated_regs' is used in case a temporary + registers needs to be allocated to store a constant. If 'free_or_dead' + is non-zero, subsequently release the temporary; if it is positive, the + temp is dead; if it is negative, the temp is free. */ +static void temp_sync(TCGContext *s, TCGTemp *ts, + TCGRegSet allocated_regs, int free_or_dead) +{ + if (ts->fixed_reg) { + return; + } + if (!ts->mem_coherent) { if (!ts->mem_allocated) { temp_allocate_frame(s, temp_idx(s, ts)); - } else if (ts->indirect_reg) { - tcg_regset_set_reg(allocated_regs, ts->reg); + } + if (ts->indirect_reg) { + if (ts->val_type == TEMP_VAL_REG) { + tcg_regset_set_reg(allocated_regs, ts->reg); + } temp_load(s, ts->mem_base, tcg_target_available_regs[TCG_TYPE_PTR], allocated_regs); } - tcg_out_st(s, ts->type, reg, ts->mem_base->reg, ts->mem_offset); + switch (ts->val_type) { + case TEMP_VAL_CONST: + /* If we're going to free the temp immediately, then we won't + require it later in a register, so attempt to store the + constant to memory directly. */ + if (free_or_dead + && tcg_out_sti(s, ts->type, ts->val, + ts->mem_base->reg, ts->mem_offset)) { + break; + } + temp_load(s, ts, tcg_target_available_regs[ts->type], + allocated_regs); + /* fallthrough */ + + case TEMP_VAL_REG: + tcg_out_st(s, ts->type, ts->reg, + ts->mem_base->reg, ts->mem_offset); + break; + + case TEMP_VAL_MEM: + break; + + case TEMP_VAL_DEAD: + default: + tcg_abort(); + } + ts->mem_coherent = 1; + } + if (free_or_dead) { + temp_free_or_dead(s, ts, free_or_dead); } - ts->mem_coherent = 1; } /* free register 'reg' by spilling the corresponding temporary if necessary */ static void tcg_reg_free(TCGContext *s, TCGReg reg, TCGRegSet allocated_regs) { TCGTemp *ts = s->reg_to_temp[reg]; - if (ts != NULL) { - tcg_reg_sync(s, reg, allocated_regs); - ts->val_type = TEMP_VAL_MEM; - s->reg_to_temp[reg] = NULL; + temp_sync(s, ts, allocated_regs, -1); } } @@ -1778,45 +1844,9 @@ static void temp_load(TCGContext *s, TCGTemp *ts, TCGRegSet desired_regs, s->reg_to_temp[reg] = ts; } -/* mark a temporary as dead. */ -static inline void temp_dead(TCGContext *s, TCGTemp *ts) -{ - if (ts->fixed_reg) { - return; - } - if (ts->val_type == TEMP_VAL_REG) { - s->reg_to_temp[ts->reg] = NULL; - } - ts->val_type = (temp_idx(s, ts) < s->nb_globals || ts->temp_local - ? TEMP_VAL_MEM : TEMP_VAL_DEAD); -} - -/* sync a temporary to memory. 'allocated_regs' is used in case a - temporary registers needs to be allocated to store a constant. */ -static void temp_sync(TCGContext *s, TCGTemp *ts, TCGRegSet allocated_regs) -{ - if (ts->fixed_reg) { - return; - } - switch (ts->val_type) { - case TEMP_VAL_CONST: - temp_load(s, ts, tcg_target_available_regs[ts->type], allocated_regs); - /* fallthrough */ - case TEMP_VAL_REG: - tcg_reg_sync(s, ts->reg, allocated_regs); - break; - case TEMP_VAL_DEAD: - case TEMP_VAL_MEM: - break; - default: - tcg_abort(); - } -} - -/* save a temporary to memory. 'allocated_regs' is used in case a - temporary registers needs to be allocated to store a constant. */ -static inline void temp_save(TCGContext *s, TCGTemp *ts, - TCGRegSet allocated_regs) +/* Save a temporary to memory. 'allocated_regs' is used in case a + temporary registers needs to be allocated to store a constant. */ +static void temp_save(TCGContext *s, TCGTemp *ts, TCGRegSet allocated_regs) { #ifdef USE_LIVENESS_ANALYSIS /* ??? Liveness does not yet incorporate indirect bases. */ @@ -1827,8 +1857,7 @@ static inline void temp_save(TCGContext *s, TCGTemp *ts, return; } #endif - temp_sync(s, ts, allocated_regs); - temp_dead(s, ts); + temp_sync(s, ts, allocated_regs, 1); } /* save globals to their canonical location and assume they can be @@ -1861,7 +1890,7 @@ static void sync_globals(TCGContext *s, TCGRegSet allocated_regs) continue; } #endif - temp_sync(s, ts, allocated_regs); + temp_sync(s, ts, allocated_regs, 0); } } @@ -1905,21 +1934,21 @@ static void tcg_reg_alloc_movi(TCGContext *s, const TCGArg *args, val = args[1]; if (ots->fixed_reg) { - /* for fixed registers, we do not do any constant - propagation */ + /* For fixed registers, we do not do any constant propagation. */ tcg_out_movi(s, ots->type, ots->reg, val); - } else { - /* The movi is not explicitly generated here */ - if (ots->val_type == TEMP_VAL_REG) { - s->reg_to_temp[ots->reg] = NULL; - } - ots->val_type = TEMP_VAL_CONST; - ots->val = val; + return; } - if (NEED_SYNC_ARG(0)) { - temp_sync(s, ots, s->reserved_regs); + + /* The movi is not explicitly generated here. */ + if (ots->val_type == TEMP_VAL_REG) { + s->reg_to_temp[ots->reg] = NULL; } - if (IS_DEAD_ARG(0)) { + ots->val_type = TEMP_VAL_CONST; + ots->val = val; + ots->mem_coherent = 0; + if (NEED_SYNC_ARG(0)) { + temp_sync(s, ots, s->reserved_regs, IS_DEAD_ARG(0)); + } else if (IS_DEAD_ARG(0)) { temp_dead(s, ots); } } @@ -2004,7 +2033,7 @@ static void tcg_reg_alloc_mov(TCGContext *s, const TCGOpDef *def, ots->mem_coherent = 0; s->reg_to_temp[ots->reg] = ots; if (NEED_SYNC_ARG(0)) { - tcg_reg_sync(s, ots->reg, allocated_regs); + temp_sync(s, ots, allocated_regs, 0); } } } @@ -2163,9 +2192,8 @@ static void tcg_reg_alloc_op(TCGContext *s, tcg_out_mov(s, ts->type, ts->reg, reg); } if (NEED_SYNC_ARG(i)) { - tcg_reg_sync(s, reg, allocated_regs); - } - if (IS_DEAD_ARG(i)) { + temp_sync(s, ts, allocated_regs, IS_DEAD_ARG(i)); + } else if (IS_DEAD_ARG(i)) { temp_dead(s, ts); } } @@ -2298,9 +2326,8 @@ static void tcg_reg_alloc_call(TCGContext *s, int nb_oargs, int nb_iargs, ts->mem_coherent = 0; s->reg_to_temp[reg] = ts; if (NEED_SYNC_ARG(i)) { - tcg_reg_sync(s, reg, allocated_regs); - } - if (IS_DEAD_ARG(i)) { + temp_sync(s, ts, allocated_regs, IS_DEAD_ARG(i)); + } else if (IS_DEAD_ARG(i)) { temp_dead(s, ts); } } @@ -191,6 +191,15 @@ typedef uint64_t tcg_insn_unit; #endif +#ifdef CONFIG_DEBUG_TCG +# define tcg_debug_assert(X) do { assert(X); } while (0) +#elif QEMU_GNUC_PREREQ(4, 5) +# define tcg_debug_assert(X) \ + do { if (!(X)) { __builtin_unreachable(); } } while (0) +#else +# define tcg_debug_assert(X) do { (void)(X); } while (0) +#endif + typedef struct TCGRelocation { struct TCGRelocation *next; int type; @@ -275,10 +284,26 @@ typedef enum TCGMemOp { #endif /* MO_UNALN accesses are never checked for alignment. - MO_ALIGN accesses will result in a call to the CPU's - do_unaligned_access hook if the guest address is not aligned. - The default depends on whether the target CPU defines ALIGNED_ONLY. */ - MO_AMASK = 16, + * MO_ALIGN accesses will result in a call to the CPU's + * do_unaligned_access hook if the guest address is not aligned. + * The default depends on whether the target CPU defines ALIGNED_ONLY. + * Some architectures (e.g. ARMv8) need the address which is aligned + * to a size more than the size of the memory access. + * To support such check it's enough the current costless alignment + * check implementation in QEMU, but we need to support + * an alignment size specifying. + * MO_ALIGN supposes a natural alignment + * (i.e. the alignment size is the size of a memory access). + * Note that an alignment size must be equal or greater + * than an access size. + * There are three options: + * - an alignment to the size of an access (MO_ALIGN); + * - an alignment to the specified size that is equal or greater than + * an access size (MO_ALIGN_x where 'x' is a size in bytes); + * - unaligned access permitted (MO_UNALN). + */ + MO_ASHIFT = 4, + MO_AMASK = 7 << MO_ASHIFT, #ifdef ALIGNED_ONLY MO_ALIGN = 0, MO_UNALN = MO_AMASK, @@ -286,6 +311,12 @@ typedef enum TCGMemOp { MO_ALIGN = MO_AMASK, MO_UNALN = 0, #endif + MO_ALIGN_2 = 1 << MO_ASHIFT, + MO_ALIGN_4 = 2 << MO_ASHIFT, + MO_ALIGN_8 = 3 << MO_ASHIFT, + MO_ALIGN_16 = 4 << MO_ASHIFT, + MO_ALIGN_32 = 5 << MO_ASHIFT, + MO_ALIGN_64 = 6 << MO_ASHIFT, /* Combinations of the above, for ease of use. */ MO_UB = MO_8, @@ -317,6 +348,45 @@ typedef enum TCGMemOp { MO_SSIZE = MO_SIZE | MO_SIGN, } TCGMemOp; +/** + * get_alignment_bits + * @memop: TCGMemOp value + * + * Extract the alignment size from the memop. + * + * Returns: 0 in case of byte access (which is always aligned); + * positive value - number of alignment bits; + * negative value if unaligned access enabled + * and this is not a byte access. + */ +static inline int get_alignment_bits(TCGMemOp memop) +{ + int a = memop & MO_AMASK; + int s = memop & MO_SIZE; + int r; + + if (a == MO_UNALN) { + /* Negative value if unaligned access enabled, + * or zero value in case of byte access. + */ + return -s; + } else if (a == MO_ALIGN) { + /* A natural alignment: return a number of access size bits */ + r = s; + } else { + /* Specific alignment size. It must be equal or greater + * than the access size. + */ + r = a >> MO_ASHIFT; + tcg_debug_assert(r >= s); + } +#if defined(CONFIG_SOFTMMU) + /* The requested alignment cannot overlap the TLB flags. */ + tcg_debug_assert((TLB_FLAGS_MASK & ((1 << r) - 1)) == 0); +#endif + return r; +} + typedef tcg_target_ulong TCGArg; /* Define a type and accessor macros for variables. Using pointer types @@ -790,15 +860,6 @@ do {\ abort();\ } while (0) -#ifdef CONFIG_DEBUG_TCG -# define tcg_debug_assert(X) do { assert(X); } while (0) -#elif QEMU_GNUC_PREREQ(4, 5) -# define tcg_debug_assert(X) \ - do { if (!(X)) { __builtin_unreachable(); } } while (0) -#else -# define tcg_debug_assert(X) do { (void)(X); } while (0) -#endif - void tcg_add_target_add_op_defs(const TCGTargetOpDef *tdefs); #if UINTPTR_MAX == UINT32_MAX diff --git a/tcg/tci/tcg-target.inc.c b/tcg/tci/tcg-target.inc.c index fa74d5278e..3c47ea7a9e 100644 --- a/tcg/tci/tcg-target.inc.c +++ b/tcg/tci/tcg-target.inc.c @@ -834,6 +834,12 @@ static void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, TCGReg arg1, old_code_ptr[1] = s->code_ptr - old_code_ptr; } +static inline bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, + TCGReg base, intptr_t ofs) +{ + return false; +} + /* Test if a constant matches the constraint. */ static int tcg_target_const_match(tcg_target_long val, TCGType type, const TCGArgConstraint *arg_ct) diff --git a/tests/.gitignore b/tests/.gitignore index 840ea396ab..dbb52639f6 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -13,6 +13,7 @@ test-aio test-base64 test-bitops test-blockjob-txn +test-clone-visitor test-coroutine test-crypto-afsplit test-crypto-block diff --git a/tests/Makefile.include b/tests/Makefile.include index 6c09962f75..2010b11b6c 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -22,6 +22,8 @@ check-unit-y += tests/check-qjson$(EXESUF) 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-clone-visitor$(EXESUF) +gcov-files-test-clone-visitor-y = qapi/qapi-clone-visitor.c check-unit-y += tests/test-qmp-input-visitor$(EXESUF) gcov-files-test-qmp-input-visitor-y = qapi/qmp-input-visitor.c check-unit-y += tests/test-qmp-input-strict$(EXESUF) @@ -86,7 +88,7 @@ check-unit-y += tests/test-qemu-opts$(EXESUF) gcov-files-test-qemu-opts-y = qom/test-qemu-opts.c check-unit-y += tests/test-write-threshold$(EXESUF) gcov-files-test-write-threshold-y = block/write-threshold.c -check-unit-$(CONFIG_GNUTLS_HASH) += tests/test-crypto-hash$(EXESUF) +check-unit-y += tests/test-crypto-hash$(EXESUF) check-unit-y += tests/test-crypto-cipher$(EXESUF) check-unit-y += tests/test-crypto-secret$(EXESUF) check-unit-$(CONFIG_GNUTLS) += tests/test-crypto-tlscredsx509$(EXESUF) @@ -402,6 +404,7 @@ test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \ tests/check-qjson.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-clone-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-x86-cpuid.o tests/test-mul64.o tests/test-int128.o \ @@ -499,6 +502,7 @@ tests/test-string-output-visitor$(EXESUF): tests/test-string-output-visitor.o $( tests/test-string-input-visitor$(EXESUF): tests/test-string-input-visitor.o $(test-qapi-obj-y) tests/test-qmp-event$(EXESUF): tests/test-qmp-event.o $(test-qapi-obj-y) tests/test-qmp-output-visitor$(EXESUF): tests/test-qmp-output-visitor.o $(test-qapi-obj-y) +tests/test-clone-visitor$(EXESUF): tests/test-clone-visitor.o $(test-qapi-obj-y) tests/test-qmp-input-visitor$(EXESUF): tests/test-qmp-input-visitor.o $(test-qapi-obj-y) tests/test-qmp-input-strict$(EXESUF): tests/test-qmp-input-strict.o $(test-qapi-obj-y) tests/test-qmp-commands$(EXESUF): tests/test-qmp-commands.o tests/test-qmp-marshal.o $(test-qapi-obj-y) diff --git a/tests/acpi-test-data/pc/APIC.cphp b/tests/acpi-test-data/pc/APIC.cphp Binary files differnew file mode 100644 index 0000000000..1bf8a0a63b --- /dev/null +++ b/tests/acpi-test-data/pc/APIC.cphp diff --git a/tests/acpi-test-data/pc/DSDT.cphp b/tests/acpi-test-data/pc/DSDT.cphp Binary files differnew file mode 100644 index 0000000000..e8b146208e --- /dev/null +++ b/tests/acpi-test-data/pc/DSDT.cphp diff --git a/tests/acpi-test-data/q35/APIC.cphp b/tests/acpi-test-data/q35/APIC.cphp Binary files differnew file mode 100644 index 0000000000..1bf8a0a63b --- /dev/null +++ b/tests/acpi-test-data/q35/APIC.cphp diff --git a/tests/acpi-test-data/q35/DSDT.cphp b/tests/acpi-test-data/q35/DSDT.cphp Binary files differnew file mode 100644 index 0000000000..6cc28c6dae --- /dev/null +++ b/tests/acpi-test-data/q35/DSDT.cphp diff --git a/tests/bios-tables-test.c b/tests/bios-tables-test.c index 92c90dd194..de4019e57d 100644 --- a/tests/bios-tables-test.c +++ b/tests/bios-tables-test.c @@ -801,6 +801,32 @@ static void test_acpi_q35_tcg_bridge(void) free_test_data(&data); } +static void test_acpi_piix4_tcg_cphp(void) +{ + test_data data; + + memset(&data, 0, sizeof(data)); + data.machine = MACHINE_PC; + data.variant = ".cphp"; + test_acpi_one("-machine accel=tcg" + " -smp 2,cores=3,sockets=2,maxcpus=6", + &data); + free_test_data(&data); +} + +static void test_acpi_q35_tcg_cphp(void) +{ + test_data data; + + memset(&data, 0, sizeof(data)); + data.machine = MACHINE_Q35; + data.variant = ".cphp"; + test_acpi_one("-machine q35,accel=tcg" + " -smp 2,cores=3,sockets=2,maxcpus=6", + &data); + free_test_data(&data); +} + static uint8_t ipmi_required_struct_types[] = { 0, 1, 3, 4, 16, 17, 19, 32, 38, 127 }; @@ -856,6 +882,8 @@ int main(int argc, char *argv[]) qtest_add_func("acpi/q35/tcg/bridge", test_acpi_q35_tcg_bridge); qtest_add_func("acpi/piix4/tcg/ipmi", test_acpi_piix4_tcg_ipmi); qtest_add_func("acpi/q35/tcg/ipmi", test_acpi_q35_tcg_ipmi); + qtest_add_func("acpi/piix4/tcg/cpuhp", test_acpi_piix4_tcg_cphp); + qtest_add_func("acpi/q35/tcg/cpuhp", test_acpi_q35_tcg_cphp); } ret = g_test_run(); boot_sector_cleanup(disk); diff --git a/tests/check-qjson.c b/tests/check-qjson.c index 0e158f6eac..85955744eb 100644 --- a/tests/check-qjson.c +++ b/tests/check-qjson.c @@ -12,14 +12,8 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qstring.h" -#include "qapi/qmp/qint.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qfloat.h" -#include "qapi/qmp/qbool.h" +#include "qapi/qmp/types.h" #include "qapi/qmp/qjson.h" - #include "qemu-common.h" static void escaped_string(void) diff --git a/tests/check-qnull.c b/tests/check-qnull.c index 05d562d494..dc906b116e 100644 --- a/tests/check-qnull.c +++ b/tests/check-qnull.c @@ -37,8 +37,7 @@ static void qnull_ref_test(void) static void qnull_visit_test(void) { QObject *obj; - QmpOutputVisitor *qov; - QmpInputVisitor *qiv; + Visitor *v; /* * Most tests of interactions between QObject and visitors are in @@ -48,17 +47,17 @@ static void qnull_visit_test(void) g_assert(qnull_.refcnt == 1); obj = qnull(); - qiv = qmp_input_visitor_new(obj, true); + v = qmp_input_visitor_new(obj, true); qobject_decref(obj); - visit_type_null(qmp_input_get_visitor(qiv), NULL, &error_abort); - qmp_input_visitor_cleanup(qiv); + visit_type_null(v, NULL, &error_abort); + visit_free(v); - qov = qmp_output_visitor_new(); - visit_type_null(qmp_output_get_visitor(qov), NULL, &error_abort); - obj = qmp_output_get_qobject(qov); + v = qmp_output_visitor_new(&obj); + visit_type_null(v, NULL, &error_abort); + visit_complete(v, &obj); g_assert(obj == &qnull_); qobject_decref(obj); - qmp_output_visitor_cleanup(qov); + visit_free(v); g_assert(qnull_.refcnt == 1); } diff --git a/tests/qemu-iotests/149 b/tests/qemu-iotests/149 index 52e23d2946..84072513db 100755 --- a/tests/qemu-iotests/149 +++ b/tests/qemu-iotests/149 @@ -153,6 +153,8 @@ def cryptsetup_format(config): cipher = config.cipher + "-" + config.mode + "-" + config.ivgen if config.ivgen_hash is not None: cipher = cipher + ":" + config.ivgen_hash + elif config.ivgen == "essiv": + cipher = cipher + ":" + "sha256" args.extend(["--cipher", cipher]) if config.mode == "xts": args.extend(["--key-size", str(config.keylen * 2)]) @@ -479,6 +481,16 @@ configs = [ "6": "slot6", "7": "slot7", }), + + # Check handling of default hash alg (sha256) with essiv + LUKSConfig("aes-256-cbc-essiv-auto-sha1", + "aes", 256, "cbc", "essiv", None, "sha1"), + + # Check that a useless hash provided for 'plain64' iv gen + # is ignored and no error raised + LUKSConfig("aes-256-cbc-plain64-sha256-sha1", + "aes", 256, "cbc", "plain64", "sha256", "sha1"), + ] blacklist = [ diff --git a/tests/qemu-iotests/149.out b/tests/qemu-iotests/149.out index 287f013012..90b5b55efb 100644 --- a/tests/qemu-iotests/149.out +++ b/tests/qemu-iotests/149.out @@ -1878,3 +1878,243 @@ sudo cryptsetup -q -v luksClose qiotest-145-aes-256-xts-plain-sha1-pwallslots # Delete image unlink TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img +# ================= dm-crypt aes-256-cbc-essiv-auto-sha1 ================= +# Create image +truncate TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img --size 4194304MB +# Format image +sudo cryptsetup -q -v luksFormat --cipher aes-cbc-essiv:sha256 --key-size 256 --hash sha1 --key-slot 0 --key-file - TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img qiotest-145-aes-256-cbc-essiv-auto-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-cbc-essiv-auto-sha1 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img qiotest-145-aes-256-cbc-essiv-auto-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-cbc-essiv-auto-sha1 +# Delete image +unlink TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img + +# ================= qemu-img aes-256-cbc-essiv-auto-sha1 ================= +# Create image +qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,cipher-alg=aes-256,cipher-mode=cbc,ivgen-alg=essiv,hash-alg=sha1 TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img 4194304M +Formatting 'TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=cbc ivgen-alg=essiv hash-alg=sha1 + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img qiotest-145-aes-256-cbc-essiv-auto-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-cbc-essiv-auto-sha1 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img qiotest-145-aes-256-cbc-essiv-auto-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-cbc-essiv-auto-sha1 +# Delete image +unlink TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img + +# ================= dm-crypt aes-256-cbc-plain64-sha256-sha1 ================= +# Create image +truncate TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img --size 4194304MB +# Format image +sudo cryptsetup -q -v luksFormat --cipher aes-cbc-plain64:sha256 --key-size 256 --hash sha1 --key-slot 0 --key-file - TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img qiotest-145-aes-256-cbc-plain64-sha256-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-cbc-plain64-sha256-sha1 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img qiotest-145-aes-256-cbc-plain64-sha256-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-cbc-plain64-sha256-sha1 +# Delete image +unlink TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img + +# ================= qemu-img aes-256-cbc-plain64-sha256-sha1 ================= +# Create image +qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,cipher-alg=aes-256,cipher-mode=cbc,ivgen-alg=plain64,hash-alg=sha1,ivgen-hash-alg=sha256 TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img 4194304M +Formatting 'TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=cbc ivgen-alg=plain64 ivgen-hash-alg=sha256 hash-alg=sha1 + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img qiotest-145-aes-256-cbc-plain64-sha256-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 +# Write test pattern 0xa7 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x13 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-cbc-plain64-sha256-sha1 +# Read test pattern 0xa7 +qemu-io -c read -P 0xa7 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x13 +qemu-io -c read -P 0x13 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x91 +qemu-io -c write -P 0x91 100M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img +wrote 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Write test pattern 0x5e +qemu-io -c write -P 0x5e 3145728M 10M --object secret,id=sec0,data=MTIzNDU2,format=base64 --image-opts driver=luks,key-secret=sec0,file.filename=TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img +wrote 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Open dev +sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img qiotest-145-aes-256-cbc-plain64-sha256-sha1 +# Set dev owner +sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 +# Read test pattern 0x91 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 +read 10485760/10485760 bytes at offset 104857600 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Read test pattern 0x5e +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 +read 10485760/10485760 bytes at offset 3298534883328 +10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +# Close dev +sudo cryptsetup -q -v luksClose qiotest-145-aes-256-cbc-plain64-sha256-sha1 +# Delete image +unlink TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img + diff --git a/tests/test-clone-visitor.c b/tests/test-clone-visitor.c new file mode 100644 index 0000000000..df0c045512 --- /dev/null +++ b/tests/test-clone-visitor.c @@ -0,0 +1,206 @@ +/* + * QAPI Clone Visitor unit-tests. + * + * Copyright (C) 2016 Red Hat Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include <glib.h> + +#include "qemu-common.h" +#include "qapi/clone-visitor.h" +#include "test-qapi-types.h" +#include "test-qapi-visit.h" +#include "qapi/qmp/types.h" + +static void test_clone_struct(void) +{ + UserDefOne *src, *dst; + + src = g_new0(UserDefOne, 1); + src->integer = 42; + src->string = g_strdup("Hello"); + src->has_enum1 = false; + src->enum1 = ENUM_ONE_VALUE2; + + dst = QAPI_CLONE(UserDefOne, src); + g_assert(dst); + g_assert_cmpint(dst->integer, ==, 42); + g_assert(dst->string != src->string); + g_assert_cmpstr(dst->string, ==, "Hello"); + g_assert_cmpint(dst->has_enum1, ==, false); + /* Our implementation does this, but it is not required: + g_assert_cmpint(dst->enum1, ==, ENUM_ONE_VALUE2); + */ + + qapi_free_UserDefOne(src); + qapi_free_UserDefOne(dst); +} + +static void test_clone_alternate(void) +{ + AltStrBool *b_src, *s_src, *b_dst, *s_dst; + + b_src = g_new0(AltStrBool, 1); + b_src->type = QTYPE_QBOOL; + b_src->u.b = true; + s_src = g_new0(AltStrBool, 1); + s_src->type = QTYPE_QSTRING; + s_src->u.s = g_strdup("World"); + + b_dst = QAPI_CLONE(AltStrBool, b_src); + g_assert(b_dst); + g_assert_cmpint(b_dst->type, ==, b_src->type); + g_assert_cmpint(b_dst->u.b, ==, b_src->u.b); + s_dst = QAPI_CLONE(AltStrBool, s_src); + g_assert(s_dst); + g_assert_cmpint(s_dst->type, ==, s_src->type); + g_assert_cmpstr(s_dst->u.s, ==, s_src->u.s); + g_assert(s_dst->u.s != s_src->u.s); + + qapi_free_AltStrBool(b_src); + qapi_free_AltStrBool(s_src); + qapi_free_AltStrBool(b_dst); + qapi_free_AltStrBool(s_dst); +} + +static void test_clone_native_list(void) +{ + uint8List *src, *dst; + uint8List *tmp = NULL; + int i; + + /* Build list in reverse */ + for (i = 10; i; i--) { + src = g_new0(uint8List, 1); + src->next = tmp; + src->value = i; + tmp = src; + } + + dst = QAPI_CLONE(uint8List, src); + for (tmp = dst, i = 1; i <= 10; i++) { + g_assert(tmp); + g_assert_cmpint(tmp->value, ==, i); + tmp = tmp->next; + } + g_assert(!tmp); + + qapi_free_uint8List(src); + qapi_free_uint8List(dst); +} + +static void test_clone_empty(void) +{ + Empty2 *src, *dst; + + src = g_new0(Empty2, 1); + dst = QAPI_CLONE(Empty2, src); + g_assert(dst); + qapi_free_Empty2(src); + qapi_free_Empty2(dst); +} + +static void test_clone_complex1(void) +{ + UserDefNativeListUnion *src, *dst; + + src = g_new0(UserDefNativeListUnion, 1); + src->type = USER_DEF_NATIVE_LIST_UNION_KIND_STRING; + + dst = QAPI_CLONE(UserDefNativeListUnion, src); + g_assert(dst); + g_assert_cmpint(dst->type, ==, src->type); + g_assert(!dst->u.string.data); + + qapi_free_UserDefNativeListUnion(src); + qapi_free_UserDefNativeListUnion(dst); +} + +static void test_clone_complex2(void) +{ + WrapAlternate *src, *dst; + + src = g_new0(WrapAlternate, 1); + src->alt = g_new(UserDefAlternate, 1); + src->alt->type = QTYPE_QDICT; + src->alt->u.udfu.integer = 42; + /* Clone intentionally converts NULL into "" for strings */ + src->alt->u.udfu.string = NULL; + src->alt->u.udfu.enum1 = ENUM_ONE_VALUE3; + src->alt->u.udfu.u.value3.intb = 99; + src->alt->u.udfu.u.value3.has_a_b = true; + src->alt->u.udfu.u.value3.a_b = true; + + dst = QAPI_CLONE(WrapAlternate, src); + g_assert(dst); + g_assert(dst->alt); + g_assert_cmpint(dst->alt->type, ==, QTYPE_QDICT); + g_assert_cmpint(dst->alt->u.udfu.integer, ==, 42); + g_assert_cmpstr(dst->alt->u.udfu.string, ==, ""); + g_assert_cmpint(dst->alt->u.udfu.enum1, ==, ENUM_ONE_VALUE3); + g_assert_cmpint(dst->alt->u.udfu.u.value3.intb, ==, 99); + g_assert_cmpint(dst->alt->u.udfu.u.value3.has_a_b, ==, true); + g_assert_cmpint(dst->alt->u.udfu.u.value3.a_b, ==, true); + + qapi_free_WrapAlternate(src); + qapi_free_WrapAlternate(dst); +} + +static void test_clone_complex3(void) +{ + __org_qemu_x_Struct2 *src, *dst; + __org_qemu_x_Union1List *tmp; + + src = g_new0(__org_qemu_x_Struct2, 1); + tmp = src->array = g_new0(__org_qemu_x_Union1List, 1); + tmp->value = g_new0(__org_qemu_x_Union1, 1); + tmp->value->type = ORG_QEMU_X_UNION1_KIND___ORG_QEMU_X_BRANCH; + tmp->value->u.__org_qemu_x_branch.data = g_strdup("one"); + tmp = tmp->next = g_new0(__org_qemu_x_Union1List, 1); + tmp->value = g_new0(__org_qemu_x_Union1, 1); + tmp->value->type = ORG_QEMU_X_UNION1_KIND___ORG_QEMU_X_BRANCH; + tmp->value->u.__org_qemu_x_branch.data = g_strdup("two"); + tmp = tmp->next = g_new0(__org_qemu_x_Union1List, 1); + tmp->value = g_new0(__org_qemu_x_Union1, 1); + tmp->value->type = ORG_QEMU_X_UNION1_KIND___ORG_QEMU_X_BRANCH; + tmp->value->u.__org_qemu_x_branch.data = g_strdup("three"); + + dst = QAPI_CLONE(__org_qemu_x_Struct2, src); + g_assert(dst); + tmp = dst->array; + g_assert(tmp); + g_assert(tmp->value); + g_assert_cmpstr(tmp->value->u.__org_qemu_x_branch.data, ==, "one"); + tmp = tmp->next; + g_assert(tmp); + g_assert(tmp->value); + g_assert_cmpstr(tmp->value->u.__org_qemu_x_branch.data, ==, "two"); + tmp = tmp->next; + g_assert(tmp); + g_assert(tmp->value); + g_assert_cmpstr(tmp->value->u.__org_qemu_x_branch.data, ==, "three"); + tmp = tmp->next; + g_assert(!tmp); + + qapi_free___org_qemu_x_Struct2(src); + qapi_free___org_qemu_x_Struct2(dst); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/visitor/clone/struct", test_clone_struct); + g_test_add_func("/visitor/clone/alternate", test_clone_alternate); + g_test_add_func("/visitor/clone/native_list", test_clone_native_list); + g_test_add_func("/visitor/clone/empty", test_clone_empty); + g_test_add_func("/visitor/clone/complex1", test_clone_complex1); + g_test_add_func("/visitor/clone/complex2", test_clone_complex2); + g_test_add_func("/visitor/clone/complex3", test_clone_complex3); + + return g_test_run(); +} diff --git a/tests/test-crypto-hash.c b/tests/test-crypto-hash.c index 6e0e89f7d6..42fc77a107 100644 --- a/tests/test-crypto-hash.c +++ b/tests/test-crypto-hash.c @@ -30,27 +30,56 @@ #define OUTPUT_MD5 "628d206371563035ab8ef62f492bdec9" #define OUTPUT_SHA1 "b2e74f26758a3a421e509cee045244b78753cc02" +#define OUTPUT_SHA224 "e2f7415aad33ef79f6516b0986d7175f" \ + "9ca3389a85bf6cfed078737b" #define OUTPUT_SHA256 "bc757abb0436586f392b437e5dd24096" \ "f7f224de6b74d4d86e2abc6121b160d0" +#define OUTPUT_SHA384 "887ce52efb4f46700376356583b7e279" \ + "4f612bd024e4495087ddb946c448c69d" \ + "56dbf7152a94a5e63a80f3ba9f0eed78" +#define OUTPUT_SHA512 "3a90d79638235ec6c4c11bebd84d83c0" \ + "549bc1e84edc4b6ec7086487641256cb" \ + "63b54e4cb2d2032b393994aa263c0dbb" \ + "e00a9f2fe9ef6037352232a1eec55ee7" +#define OUTPUT_RIPEMD160 "f3d658fad3fdfb2b52c9369cf0d441249ddfa8a0" #define OUTPUT_MD5_B64 "Yo0gY3FWMDWrjvYvSSveyQ==" #define OUTPUT_SHA1_B64 "sudPJnWKOkIeUJzuBFJEt4dTzAI=" +#define OUTPUT_SHA224_B64 "4vdBWq0z73n2UWsJhtcXX5yjOJqFv2z+0Hhzew==" #define OUTPUT_SHA256_B64 "vHV6uwQ2WG85K0N+XdJAlvfyJN5rdNTYbiq8YSGxYNA=" +#define OUTPUT_SHA384_B64 "iHzlLvtPRnADdjVlg7fieU9hK9Ak5ElQh925RsRI" \ + "xp1W2/cVKpSl5jqA87qfDu14" +#define OUTPUT_SHA512_B64 "OpDXljgjXsbEwRvr2E2DwFSbwehO3Etuxwhkh2QS" \ + "VstjtU5MstIDKzk5lKomPA274AqfL+nvYDc1IjKh" \ + "7sVe5w==" +#define OUTPUT_RIPEMD160_B64 "89ZY+tP9+ytSyTac8NRBJJ3fqKA=" static const char *expected_outputs[] = { [QCRYPTO_HASH_ALG_MD5] = OUTPUT_MD5, [QCRYPTO_HASH_ALG_SHA1] = OUTPUT_SHA1, + [QCRYPTO_HASH_ALG_SHA224] = OUTPUT_SHA224, [QCRYPTO_HASH_ALG_SHA256] = OUTPUT_SHA256, + [QCRYPTO_HASH_ALG_SHA384] = OUTPUT_SHA384, + [QCRYPTO_HASH_ALG_SHA512] = OUTPUT_SHA512, + [QCRYPTO_HASH_ALG_RIPEMD160] = OUTPUT_RIPEMD160, }; static const char *expected_outputs_b64[] = { [QCRYPTO_HASH_ALG_MD5] = OUTPUT_MD5_B64, [QCRYPTO_HASH_ALG_SHA1] = OUTPUT_SHA1_B64, + [QCRYPTO_HASH_ALG_SHA224] = OUTPUT_SHA224_B64, [QCRYPTO_HASH_ALG_SHA256] = OUTPUT_SHA256_B64, + [QCRYPTO_HASH_ALG_SHA384] = OUTPUT_SHA384_B64, + [QCRYPTO_HASH_ALG_SHA512] = OUTPUT_SHA512_B64, + [QCRYPTO_HASH_ALG_RIPEMD160] = OUTPUT_RIPEMD160_B64, }; static const int expected_lens[] = { [QCRYPTO_HASH_ALG_MD5] = 16, [QCRYPTO_HASH_ALG_SHA1] = 20, + [QCRYPTO_HASH_ALG_SHA224] = 28, [QCRYPTO_HASH_ALG_SHA256] = 32, + [QCRYPTO_HASH_ALG_SHA384] = 48, + [QCRYPTO_HASH_ALG_SHA512] = 64, + [QCRYPTO_HASH_ALG_RIPEMD160] = 20, }; static const char hex[] = "0123456789abcdef"; @@ -68,6 +97,10 @@ static void test_hash_alloc(void) int ret; size_t j; + if (!qcrypto_hash_supports(i)) { + continue; + } + ret = qcrypto_hash_bytes(i, INPUT_TEXT, strlen(INPUT_TEXT), @@ -98,6 +131,10 @@ static void test_hash_prealloc(void) int ret; size_t j; + if (!qcrypto_hash_supports(i)) { + continue; + } + resultlen = expected_lens[i]; result = g_new0(uint8_t, resultlen); @@ -137,6 +174,10 @@ static void test_hash_iov(void) int ret; size_t j; + if (!qcrypto_hash_supports(i)) { + continue; + } + ret = qcrypto_hash_bytesv(i, iov, 3, &result, @@ -165,6 +206,10 @@ static void test_hash_digest(void) char *digest; size_t digestsize; + if (!qcrypto_hash_supports(i)) { + continue; + } + digestsize = qcrypto_hash_digest_len(i); g_assert_cmpint(digestsize * 2, ==, strlen(expected_outputs[i])); @@ -175,7 +220,7 @@ static void test_hash_digest(void) &digest, NULL); g_assert(ret == 0); - g_assert(g_str_equal(digest, expected_outputs[i])); + g_assert_cmpstr(digest, ==, expected_outputs[i]); g_free(digest); } } @@ -191,13 +236,17 @@ static void test_hash_base64(void) int ret; char *digest; + if (!qcrypto_hash_supports(i)) { + continue; + } + ret = qcrypto_hash_base64(i, INPUT_TEXT, strlen(INPUT_TEXT), &digest, NULL); g_assert(ret == 0); - g_assert(g_str_equal(digest, expected_outputs_b64[i])); + g_assert_cmpstr(digest, ==, expected_outputs_b64[i]); g_free(digest); } } diff --git a/tests/test-crypto-xts.c b/tests/test-crypto-xts.c index 7f68b063cd..1f1412c45a 100644 --- a/tests/test-crypto-xts.c +++ b/tests/test-crypto-xts.c @@ -340,7 +340,7 @@ static void test_xts_aes_decrypt(const void *ctx, static void test_xts(const void *opaque) { const QCryptoXTSTestData *data = opaque; - unsigned char OUT[512], Torg[16], T[16]; + unsigned char out[512], Torg[16], T[16]; uint64_t seq; int j; unsigned long len; @@ -371,38 +371,38 @@ static void test_xts(const void *opaque) xts_encrypt(&aesdata, &aestweak, test_xts_aes_encrypt, test_xts_aes_decrypt, - T, data->PTLEN, OUT, data->PTX); + T, data->PTLEN, out, data->PTX); } else { xts_encrypt(&aesdata, &aestweak, test_xts_aes_encrypt, test_xts_aes_decrypt, - T, len, OUT, data->PTX); + T, len, out, data->PTX); xts_encrypt(&aesdata, &aestweak, test_xts_aes_encrypt, test_xts_aes_decrypt, - T, len, &OUT[len], &data->PTX[len]); + T, len, &out[len], &data->PTX[len]); } - g_assert(memcmp(OUT, data->CTX, data->PTLEN) == 0); + g_assert(memcmp(out, data->CTX, data->PTLEN) == 0); memcpy(T, Torg, sizeof(T)); if (j == 0) { xts_decrypt(&aesdata, &aestweak, test_xts_aes_encrypt, test_xts_aes_decrypt, - T, data->PTLEN, OUT, data->CTX); + T, data->PTLEN, out, data->CTX); } else { xts_decrypt(&aesdata, &aestweak, test_xts_aes_encrypt, test_xts_aes_decrypt, - T, len, OUT, data->CTX); + T, len, out, data->CTX); xts_decrypt(&aesdata, &aestweak, test_xts_aes_encrypt, test_xts_aes_decrypt, - T, len, &OUT[len], &data->CTX[len]); + T, len, &out[len], &data->CTX[len]); } - g_assert(memcmp(OUT, data->PTX, data->PTLEN) == 0); + g_assert(memcmp(out, data->PTX, data->PTLEN) == 0); } } diff --git a/tests/test-logging.c b/tests/test-logging.c index 440e75f5db..cdf13c6ba5 100644 --- a/tests/test-logging.c +++ b/tests/test-logging.c @@ -68,6 +68,17 @@ static void test_parse_range(void) g_assert(qemu_log_in_addr_range(0x2050)); g_assert(qemu_log_in_addr_range(0x3050)); + qemu_set_dfilter_ranges("0xffffffffffffffff-1", &error_abort); + g_assert(qemu_log_in_addr_range(UINT64_MAX)); + g_assert_false(qemu_log_in_addr_range(UINT64_MAX - 1)); + + qemu_set_dfilter_ranges("0..0xffffffffffffffff", &err); + g_assert(qemu_log_in_addr_range(0)); + g_assert(qemu_log_in_addr_range(UINT64_MAX)); + + qemu_set_dfilter_ranges("2..1", &err); + error_free_or_abort(&err); + qemu_set_dfilter_ranges("0x1000+onehundred", &err); error_free_or_abort(&err); diff --git a/tests/test-opts-visitor.c b/tests/test-opts-visitor.c index d75b114c15..0a9e75f1bb 100644 --- a/tests/test-opts-visitor.c +++ b/tests/test-opts-visitor.c @@ -37,16 +37,15 @@ setup_fixture(OptsVisitorFixture *f, gconstpointer test_data) { const char *opts_string = test_data; QemuOpts *opts; - OptsVisitor *ov; + Visitor *v; opts = qemu_opts_parse(qemu_find_opts("userdef"), opts_string, false, NULL); g_assert(opts != NULL); - ov = opts_visitor_new(opts); - visit_type_UserDefOptions(opts_get_visitor(ov), NULL, &f->userdef, - &f->err); - opts_visitor_cleanup(ov); + v = opts_visitor_new(opts); + visit_type_UserDefOptions(v, NULL, &f->userdef, &f->err); + visit_free(v); qemu_opts_del(opts); } diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c index e8f619d331..8ffeb045cd 100644 --- a/tests/test-qmp-commands.c +++ b/tests/test-qmp-commands.c @@ -216,14 +216,14 @@ static void test_dealloc_partial(void) /* create partial object */ { QDict *ud2_dict; - QmpInputVisitor *qiv; + Visitor *v; ud2_dict = qdict_new(); qdict_put_obj(ud2_dict, "string0", QOBJECT(qstring_from_str(text))); - qiv = qmp_input_visitor_new(QOBJECT(ud2_dict), true); - visit_type_UserDefTwo(qmp_input_get_visitor(qiv), NULL, &ud2, &err); - qmp_input_visitor_cleanup(qiv); + v = qmp_input_visitor_new(QOBJECT(ud2_dict), true); + visit_type_UserDefTwo(v, NULL, &ud2, &err); + visit_free(v); QDECREF(ud2_dict); } diff --git a/tests/test-qmp-input-strict.c b/tests/test-qmp-input-strict.c index d5f57c583a..814550ac71 100644 --- a/tests/test-qmp-input-strict.c +++ b/tests/test-qmp-input-strict.c @@ -19,13 +19,14 @@ #include "test-qapi-types.h" #include "test-qapi-visit.h" #include "qapi/qmp/types.h" +#include "qapi/qmp/qjson.h" #include "test-qmp-introspect.h" #include "qmp-introspect.h" #include "qapi-visit.h" typedef struct TestInputVisitorData { QObject *obj; - QmpInputVisitor *qiv; + Visitor *qiv; } TestInputVisitorData; static void validate_teardown(TestInputVisitorData *data, @@ -35,7 +36,7 @@ static void validate_teardown(TestInputVisitorData *data, data->obj = NULL; if (data->qiv) { - qmp_input_visitor_cleanup(data->qiv); + visit_free(data->qiv); data->qiv = NULL; } } @@ -47,8 +48,6 @@ static Visitor *validate_test_init_internal(TestInputVisitorData *data, const char *json_string, va_list *ap) { - Visitor *v; - validate_teardown(data, NULL); data->obj = qobject_from_jsonv(json_string, ap); @@ -56,11 +55,7 @@ static Visitor *validate_test_init_internal(TestInputVisitorData *data, data->qiv = qmp_input_visitor_new(data->obj, true); g_assert(data->qiv); - - v = qmp_input_get_visitor(data->qiv); - g_assert(v); - - return v; + return data->qiv; } static GCC_FMT_ATTR(2, 3) diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c index 1a4585c553..f583dce3ed 100644 --- a/tests/test-qmp-input-visitor.c +++ b/tests/test-qmp-input-visitor.c @@ -18,10 +18,11 @@ #include "test-qapi-types.h" #include "test-qapi-visit.h" #include "qapi/qmp/types.h" +#include "qapi/qmp/qjson.h" typedef struct TestInputVisitorData { QObject *obj; - QmpInputVisitor *qiv; + Visitor *qiv; } TestInputVisitorData; static void visitor_input_teardown(TestInputVisitorData *data, @@ -31,7 +32,7 @@ static void visitor_input_teardown(TestInputVisitorData *data, data->obj = NULL; if (data->qiv) { - qmp_input_visitor_cleanup(data->qiv); + visit_free(data->qiv); data->qiv = NULL; } } @@ -43,8 +44,6 @@ static Visitor *visitor_input_test_init_internal(TestInputVisitorData *data, const char *json_string, va_list *ap) { - Visitor *v; - visitor_input_teardown(data, NULL); data->obj = qobject_from_jsonv(json_string, ap); @@ -52,11 +51,7 @@ static Visitor *visitor_input_test_init_internal(TestInputVisitorData *data, data->qiv = qmp_input_visitor_new(data->obj, false); g_assert(data->qiv); - - v = qmp_input_get_visitor(data->qiv); - g_assert(v); - - return v; + return data->qiv; } static GCC_FMT_ATTR(2, 3) @@ -303,7 +298,7 @@ static void test_visitor_in_null(TestInputVisitorData *data, visit_type_null(v, "b", &err); error_free_or_abort(&err); visit_check_struct(v, &error_abort); - visit_end_struct(v); + visit_end_struct(v, NULL); } static void test_visitor_in_union_flat(TestInputVisitorData *data, diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c index f8a7a271a9..513d71f0d1 100644 --- a/tests/test-qmp-output-visitor.c +++ b/tests/test-qmp-output-visitor.c @@ -18,28 +18,34 @@ #include "test-qapi-types.h" #include "test-qapi-visit.h" #include "qapi/qmp/types.h" +#include "qapi/qmp/qjson.h" typedef struct TestOutputVisitorData { - QmpOutputVisitor *qov; Visitor *ov; + QObject *obj; } TestOutputVisitorData; static void visitor_output_setup(TestOutputVisitorData *data, const void *unused) { - data->qov = qmp_output_visitor_new(); - g_assert(data->qov != NULL); - - data->ov = qmp_output_get_visitor(data->qov); - g_assert(data->ov != NULL); + data->ov = qmp_output_visitor_new(&data->obj); + g_assert(data->ov); } static void visitor_output_teardown(TestOutputVisitorData *data, const void *unused) { - qmp_output_visitor_cleanup(data->qov); - data->qov = NULL; + visit_free(data->ov); data->ov = NULL; + qobject_decref(data->obj); + data->obj = NULL; +} + +static QObject *visitor_get(TestOutputVisitorData *data) +{ + visit_complete(data->ov, &data->obj); + g_assert(data->obj); + return data->obj; } static void visitor_reset(TestOutputVisitorData *data) @@ -56,12 +62,9 @@ static void test_visitor_out_int(TestOutputVisitorData *data, visit_type_int(data->ov, NULL, &value, &error_abort); - obj = qmp_output_get_qobject(data->qov); - g_assert(obj != NULL); + obj = visitor_get(data); g_assert(qobject_type(obj) == QTYPE_QINT); g_assert_cmpint(qint_get_int(qobject_to_qint(obj)), ==, value); - - qobject_decref(obj); } static void test_visitor_out_bool(TestOutputVisitorData *data, @@ -72,12 +75,9 @@ static void test_visitor_out_bool(TestOutputVisitorData *data, visit_type_bool(data->ov, NULL, &value, &error_abort); - obj = qmp_output_get_qobject(data->qov); - g_assert(obj != NULL); + obj = visitor_get(data); g_assert(qobject_type(obj) == QTYPE_QBOOL); g_assert(qbool_get_bool(qobject_to_qbool(obj)) == value); - - qobject_decref(obj); } static void test_visitor_out_number(TestOutputVisitorData *data, @@ -88,12 +88,9 @@ static void test_visitor_out_number(TestOutputVisitorData *data, visit_type_number(data->ov, NULL, &value, &error_abort); - obj = qmp_output_get_qobject(data->qov); - g_assert(obj != NULL); + obj = visitor_get(data); g_assert(qobject_type(obj) == QTYPE_QFLOAT); g_assert(qfloat_get_double(qobject_to_qfloat(obj)) == value); - - qobject_decref(obj); } static void test_visitor_out_string(TestOutputVisitorData *data, @@ -104,12 +101,9 @@ static void test_visitor_out_string(TestOutputVisitorData *data, visit_type_str(data->ov, NULL, &string, &error_abort); - obj = qmp_output_get_qobject(data->qov); - g_assert(obj != NULL); + obj = visitor_get(data); g_assert(qobject_type(obj) == QTYPE_QSTRING); g_assert_cmpstr(qstring_get_str(qobject_to_qstring(obj)), ==, string); - - qobject_decref(obj); } static void test_visitor_out_no_string(TestOutputVisitorData *data, @@ -121,12 +115,9 @@ static void test_visitor_out_no_string(TestOutputVisitorData *data, /* A null string should return "" */ visit_type_str(data->ov, NULL, &string, &error_abort); - obj = qmp_output_get_qobject(data->qov); - g_assert(obj != NULL); + obj = visitor_get(data); g_assert(qobject_type(obj) == QTYPE_QSTRING); g_assert_cmpstr(qstring_get_str(qobject_to_qstring(obj)), ==, ""); - - qobject_decref(obj); } static void test_visitor_out_enum(TestOutputVisitorData *data, @@ -138,12 +129,10 @@ static void test_visitor_out_enum(TestOutputVisitorData *data, for (i = 0; i < ENUM_ONE__MAX; i++) { visit_type_EnumOne(data->ov, "unused", &i, &error_abort); - obj = qmp_output_get_qobject(data->qov); - g_assert(obj != NULL); + obj = visitor_get(data); g_assert(qobject_type(obj) == QTYPE_QSTRING); g_assert_cmpstr(qstring_get_str(qobject_to_qstring(obj)), ==, EnumOne_lookup[i]); - qobject_decref(obj); visitor_reset(data); } } @@ -176,8 +165,7 @@ static void test_visitor_out_struct(TestOutputVisitorData *data, visit_type_TestStruct(data->ov, NULL, &p, &error_abort); - obj = qmp_output_get_qobject(data->qov); - g_assert(obj != NULL); + obj = visitor_get(data); g_assert(qobject_type(obj) == QTYPE_QDICT); qdict = qobject_to_qdict(obj); @@ -185,8 +173,6 @@ static void test_visitor_out_struct(TestOutputVisitorData *data, g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 42); g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, false); g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, "foo"); - - QDECREF(qdict); } static void test_visitor_out_struct_nested(TestOutputVisitorData *data, @@ -221,8 +207,7 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data, visit_type_UserDefTwo(data->ov, "unused", &ud2, &error_abort); - obj = qmp_output_get_qobject(data->qov); - g_assert(obj != NULL); + obj = visitor_get(data); g_assert(qobject_type(obj) == QTYPE_QDICT); qdict = qobject_to_qdict(obj); @@ -249,7 +234,6 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data, g_assert_cmpint(qdict_get_int(userdef, "integer"), ==, value); g_assert_cmpstr(qdict_get_str(userdef, "string"), ==, string); - QDECREF(qdict); qapi_free_UserDefTwo(ud2); } @@ -301,8 +285,7 @@ static void test_visitor_out_list(TestOutputVisitorData *data, visit_type_TestStructList(data->ov, NULL, &head, &error_abort); - obj = qmp_output_get_qobject(data->qov); - g_assert(obj != NULL); + obj = visitor_get(data); g_assert(qobject_type(obj) == QTYPE_QLIST); qlist = qobject_to_qlist(obj); @@ -323,7 +306,6 @@ static void test_visitor_out_list(TestOutputVisitorData *data, } g_assert_cmpint(i, ==, max_items); - QDECREF(qlist); qapi_free_TestStructList(head); } @@ -367,11 +349,9 @@ static void test_visitor_out_any(TestOutputVisitorData *data, qobj = QOBJECT(qint_from_int(-42)); visit_type_any(data->ov, NULL, &qobj, &error_abort); - obj = qmp_output_get_qobject(data->qov); - g_assert(obj != NULL); + obj = visitor_get(data); g_assert(qobject_type(obj) == QTYPE_QINT); g_assert_cmpint(qint_get_int(qobject_to_qint(obj)), ==, -42); - qobject_decref(obj); qobject_decref(qobj); visitor_reset(data); @@ -382,8 +362,7 @@ static void test_visitor_out_any(TestOutputVisitorData *data, qobj = QOBJECT(qdict); visit_type_any(data->ov, NULL, &qobj, &error_abort); qobject_decref(qobj); - obj = qmp_output_get_qobject(data->qov); - g_assert(obj != NULL); + obj = visitor_get(data); qdict = qobject_to_qdict(obj); g_assert(qdict); qobj = qdict_get(qdict, "integer"); @@ -401,7 +380,6 @@ static void test_visitor_out_any(TestOutputVisitorData *data, qstring = qobject_to_qstring(qobj); g_assert(qstring); g_assert_cmpstr(qstring_get_str(qstring), ==, "foo"); - qobject_decref(obj); } static void test_visitor_out_union_flat(TestOutputVisitorData *data, @@ -417,7 +395,7 @@ static void test_visitor_out_union_flat(TestOutputVisitorData *data, tmp->u.value1.boolean = true; visit_type_UserDefFlatUnion(data->ov, NULL, &tmp, &error_abort); - arg = qmp_output_get_qobject(data->qov); + arg = visitor_get(data); g_assert(qobject_type(arg) == QTYPE_QDICT); qdict = qobject_to_qdict(arg); @@ -428,7 +406,6 @@ static void test_visitor_out_union_flat(TestOutputVisitorData *data, g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, true); qapi_free_UserDefFlatUnion(tmp); - QDECREF(qdict); } static void test_visitor_out_alternate(TestOutputVisitorData *data, @@ -443,13 +420,12 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data, tmp->u.i = 42; visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort); - arg = qmp_output_get_qobject(data->qov); + arg = visitor_get(data); g_assert(qobject_type(arg) == QTYPE_QINT); g_assert_cmpint(qint_get_int(qobject_to_qint(arg)), ==, 42); qapi_free_UserDefAlternate(tmp); - qobject_decref(arg); visitor_reset(data); tmp = g_new0(UserDefAlternate, 1); @@ -457,13 +433,12 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data, tmp->u.s = g_strdup("hello"); visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort); - arg = qmp_output_get_qobject(data->qov); + arg = visitor_get(data); g_assert(qobject_type(arg) == QTYPE_QSTRING); g_assert_cmpstr(qstring_get_str(qobject_to_qstring(arg)), ==, "hello"); qapi_free_UserDefAlternate(tmp); - qobject_decref(arg); visitor_reset(data); tmp = g_new0(UserDefAlternate, 1); @@ -474,7 +449,7 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data, tmp->u.udfu.u.value1.boolean = true; visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort); - arg = qmp_output_get_qobject(data->qov); + arg = visitor_get(data); g_assert_cmpint(qobject_type(arg), ==, QTYPE_QDICT); qdict = qobject_to_qdict(arg); @@ -485,7 +460,6 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data, g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, true); qapi_free_UserDefAlternate(tmp); - qobject_decref(arg); } static void test_visitor_out_null(TestOutputVisitorData *data, @@ -498,15 +472,14 @@ static void test_visitor_out_null(TestOutputVisitorData *data, visit_start_struct(data->ov, NULL, NULL, 0, &error_abort); visit_type_null(data->ov, "a", &error_abort); visit_check_struct(data->ov, &error_abort); - visit_end_struct(data->ov); - arg = qmp_output_get_qobject(data->qov); + visit_end_struct(data->ov, NULL); + arg = visitor_get(data); g_assert(qobject_type(arg) == QTYPE_QDICT); qdict = qobject_to_qdict(arg); g_assert_cmpint(qdict_size(qdict), ==, 1); nil = qdict_get(qdict, "a"); g_assert(nil); g_assert(qobject_type(nil) == QTYPE_QNULL); - qobject_decref(arg); } static void init_native_list(UserDefNativeListUnion *cvalue) @@ -737,10 +710,9 @@ static void test_native_list(TestOutputVisitorData *data, visit_type_UserDefNativeListUnion(data->ov, NULL, &cvalue, &error_abort); - obj = qmp_output_get_qobject(data->qov); + obj = visitor_get(data); check_native_list(obj, cvalue->type); qapi_free_UserDefNativeListUnion(cvalue); - qobject_decref(obj); } static void test_visitor_out_native_list_int(TestOutputVisitorData *data, diff --git a/tests/test-string-input-visitor.c b/tests/test-string-input-visitor.c index 7fe7a9c270..d837ebedad 100644 --- a/tests/test-string-input-visitor.c +++ b/tests/test-string-input-visitor.c @@ -20,15 +20,15 @@ #include "qapi/qmp/types.h" typedef struct TestInputVisitorData { - StringInputVisitor *siv; + Visitor *v; } TestInputVisitorData; static void visitor_input_teardown(TestInputVisitorData *data, const void *unused) { - if (data->siv) { - string_input_visitor_cleanup(data->siv); - data->siv = NULL; + if (data->v) { + visit_free(data->v); + data->v = NULL; } } @@ -39,15 +39,9 @@ static Visitor *visitor_input_test_init(TestInputVisitorData *data, const char *string) { - Visitor *v; - - data->siv = string_input_visitor_new(string); - g_assert(data->siv != NULL); - - v = string_input_get_visitor(data->siv); - g_assert(v != NULL); - - return v; + data->v = string_input_visitor_new(string); + g_assert(data->v); + return data->v; } static void test_visitor_in_int(TestInputVisitorData *data, @@ -199,8 +193,6 @@ static void test_visitor_in_enum(TestInputVisitorData *data, visitor_input_teardown(data, NULL); } - - data->siv = NULL; } /* Try to crash the visitors */ diff --git a/tests/test-string-output-visitor.c b/tests/test-string-output-visitor.c index edff5235fe..444844a15a 100644 --- a/tests/test-string-output-visitor.c +++ b/tests/test-string-output-visitor.c @@ -20,39 +20,53 @@ #include "qapi/qmp/types.h" typedef struct TestOutputVisitorData { - StringOutputVisitor *sov; Visitor *ov; + char *str; bool human; } TestOutputVisitorData; +static void visitor_output_setup_internal(TestOutputVisitorData *data, + bool human) +{ + data->human = human; + data->ov = string_output_visitor_new(human, &data->str); + g_assert(data->ov); +} + static void visitor_output_setup(TestOutputVisitorData *data, const void *unused) { - data->human = false; - data->sov = string_output_visitor_new(data->human); - g_assert(data->sov != NULL); - - data->ov = string_output_get_visitor(data->sov); - g_assert(data->ov != NULL); + return visitor_output_setup_internal(data, false); } static void visitor_output_setup_human(TestOutputVisitorData *data, const void *unused) { - data->human = true; - data->sov = string_output_visitor_new(data->human); - g_assert(data->sov != NULL); - - data->ov = string_output_get_visitor(data->sov); - g_assert(data->ov != NULL); + return visitor_output_setup_internal(data, true); } static void visitor_output_teardown(TestOutputVisitorData *data, const void *unused) { - string_output_visitor_cleanup(data->sov); - data->sov = NULL; + visit_free(data->ov); data->ov = NULL; + g_free(data->str); + data->str = NULL; +} + +static char *visitor_get(TestOutputVisitorData *data) +{ + visit_complete(data->ov, &data->str); + g_assert(data->str); + return data->str; +} + +static void visitor_reset(TestOutputVisitorData *data) +{ + bool human = data->human; + + visitor_output_teardown(data, NULL); + visitor_output_setup_internal(data, human); } static void test_visitor_out_int(TestOutputVisitorData *data, @@ -65,14 +79,12 @@ static void test_visitor_out_int(TestOutputVisitorData *data, visit_type_int(data->ov, NULL, &value, &err); g_assert(!err); - str = string_output_get_string(data->sov); - g_assert(str != NULL); + str = visitor_get(data); if (data->human) { g_assert_cmpstr(str, ==, "42 (0x2a)"); } else { g_assert_cmpstr(str, ==, "42"); } - g_free(str); } static void test_visitor_out_intList(TestOutputVisitorData *data, @@ -94,8 +106,7 @@ static void test_visitor_out_intList(TestOutputVisitorData *data, visit_type_intList(data->ov, NULL, &list, &err); g_assert(err == NULL); - str = string_output_get_string(data->sov); - g_assert(str != NULL); + str = visitor_get(data); if (data->human) { g_assert_cmpstr(str, ==, "0-1,3-6,9-16,21-22,9223372036854775806-9223372036854775807 " @@ -105,13 +116,7 @@ static void test_visitor_out_intList(TestOutputVisitorData *data, g_assert_cmpstr(str, ==, "0-1,3-6,9-16,21-22,9223372036854775806-9223372036854775807"); } - g_free(str); - while (list) { - intList *tmp2; - tmp2 = list->next; - g_free(list); - list = tmp2; - } + qapi_free_intList(list); } static void test_visitor_out_bool(TestOutputVisitorData *data, @@ -124,10 +129,8 @@ static void test_visitor_out_bool(TestOutputVisitorData *data, visit_type_bool(data->ov, NULL, &value, &err); g_assert(!err); - str = string_output_get_string(data->sov); - g_assert(str != NULL); + str = visitor_get(data); g_assert_cmpstr(str, ==, "true"); - g_free(str); } static void test_visitor_out_number(TestOutputVisitorData *data, @@ -140,10 +143,8 @@ static void test_visitor_out_number(TestOutputVisitorData *data, visit_type_number(data->ov, NULL, &value, &err); g_assert(!err); - str = string_output_get_string(data->sov); - g_assert(str != NULL); + str = visitor_get(data); g_assert_cmpstr(str, ==, "3.140000"); - g_free(str); } static void test_visitor_out_string(TestOutputVisitorData *data, @@ -157,61 +158,50 @@ static void test_visitor_out_string(TestOutputVisitorData *data, visit_type_str(data->ov, NULL, &string, &err); g_assert(!err); - str = string_output_get_string(data->sov); - g_assert(str != NULL); + str = visitor_get(data); if (data->human) { g_assert_cmpstr(str, ==, string_human); } else { g_assert_cmpstr(str, ==, string); } - g_free(str); } static void test_visitor_out_no_string(TestOutputVisitorData *data, const void *unused) { char *string = NULL; - Error *err = NULL; char *str; /* A null string should return "" */ - visit_type_str(data->ov, NULL, &string, &err); - g_assert(!err); + visit_type_str(data->ov, NULL, &string, &error_abort); - str = string_output_get_string(data->sov); - g_assert(str != NULL); + str = visitor_get(data); if (data->human) { g_assert_cmpstr(str, ==, "<null>"); } else { g_assert_cmpstr(str, ==, ""); } - g_free(str); } static void test_visitor_out_enum(TestOutputVisitorData *data, const void *unused) { - Error *err = NULL; char *str; EnumOne i; for (i = 0; i < ENUM_ONE__MAX; i++) { - char *str_human; - - visit_type_EnumOne(data->ov, "unused", &i, &err); - g_assert(!err); + visit_type_EnumOne(data->ov, "unused", &i, &error_abort); - str_human = g_strdup_printf("\"%s\"", EnumOne_lookup[i]); - - str = string_output_get_string(data->sov); - g_assert(str != NULL); + str = visitor_get(data); if (data->human) { + char *str_human = g_strdup_printf("\"%s\"", EnumOne_lookup[i]); + g_assert_cmpstr(str, ==, str_human); + g_free(str_human); } else { g_assert_cmpstr(str, ==, EnumOne_lookup[i]); } - g_free(str_human); - g_free(str); + visitor_reset(data); } } @@ -224,8 +214,7 @@ static void test_visitor_out_enum_errors(TestOutputVisitorData *data, for (i = 0; i < ARRAY_SIZE(bad_values) ; i++) { err = NULL; visit_type_EnumOne(data->ov, "unused", &bad_values[i], &err); - g_assert(err); - error_free(err); + error_free_or_abort(&err); } } diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c index 777469f114..dba4670762 100644 --- a/tests/test-visitor-serialization.c +++ b/tests/test-visitor-serialization.c @@ -19,6 +19,7 @@ #include "test-qapi-visit.h" #include "qapi/error.h" #include "qapi/qmp/types.h" +#include "qapi/qmp/qjson.h" #include "qapi/qmp-input-visitor.h" #include "qapi/qmp-output-visitor.h" #include "qapi/string-input-visitor.h" @@ -88,11 +89,11 @@ typedef void (*VisitorFunc)(Visitor *v, void **native, Error **errp); static void dealloc_helper(void *native_in, VisitorFunc visit, Error **errp) { - QapiDeallocVisitor *qdv = qapi_dealloc_visitor_new(); + Visitor *v = qapi_dealloc_visitor_new(); - visit(qapi_dealloc_get_visitor(qdv), &native_in, errp); + visit(v, &native_in, errp); - qapi_dealloc_visitor_cleanup(qdv); + visit_free(v); } static void visit_primitive_type(Visitor *v, void **native, Error **errp) @@ -1011,8 +1012,9 @@ static PrimitiveType pt_values[] = { /* visitor-specific op implementations */ typedef struct QmpSerializeData { - QmpOutputVisitor *qov; - QmpInputVisitor *qiv; + Visitor *qov; + QObject *obj; + Visitor *qiv; } QmpSerializeData; static void qmp_serialize(void *native_in, void **datap, @@ -1020,8 +1022,8 @@ static void qmp_serialize(void *native_in, void **datap, { QmpSerializeData *d = g_malloc0(sizeof(*d)); - d->qov = qmp_output_visitor_new(); - visit(qmp_output_get_visitor(d->qov), &native_in, errp); + d->qov = qmp_output_visitor_new(&d->obj); + visit(d->qov, &native_in, errp); *datap = d; } @@ -1032,7 +1034,8 @@ static void qmp_deserialize(void **native_out, void *datap, QString *output_json; QObject *obj_orig, *obj; - obj_orig = qmp_output_get_qobject(d->qov); + visit_complete(d->qov, &d->obj); + obj_orig = d->obj; output_json = qobject_to_json(obj_orig); obj = qobject_from_json(qstring_get_str(output_json)); @@ -1040,22 +1043,22 @@ static void qmp_deserialize(void **native_out, void *datap, d->qiv = qmp_input_visitor_new(obj, true); qobject_decref(obj_orig); qobject_decref(obj); - visit(qmp_input_get_visitor(d->qiv), native_out, errp); + visit(d->qiv, native_out, errp); } static void qmp_cleanup(void *datap) { QmpSerializeData *d = datap; - qmp_output_visitor_cleanup(d->qov); - qmp_input_visitor_cleanup(d->qiv); + visit_free(d->qov); + visit_free(d->qiv); g_free(d); } typedef struct StringSerializeData { char *string; - StringOutputVisitor *sov; - StringInputVisitor *siv; + Visitor *sov; + Visitor *siv; } StringSerializeData; static void string_serialize(void *native_in, void **datap, @@ -1063,8 +1066,8 @@ static void string_serialize(void *native_in, void **datap, { StringSerializeData *d = g_malloc0(sizeof(*d)); - d->sov = string_output_visitor_new(false); - visit(string_output_get_visitor(d->sov), &native_in, errp); + d->sov = string_output_visitor_new(false, &d->string); + visit(d->sov, &native_in, errp); *datap = d; } @@ -1073,17 +1076,17 @@ static void string_deserialize(void **native_out, void *datap, { StringSerializeData *d = datap; - d->string = string_output_get_string(d->sov); + visit_complete(d->sov, &d->string); d->siv = string_input_visitor_new(d->string); - visit(string_input_get_visitor(d->siv), native_out, errp); + visit(d->siv, native_out, errp); } static void string_cleanup(void *datap) { StringSerializeData *d = datap; - string_output_visitor_cleanup(d->sov); - string_input_visitor_cleanup(d->siv); + visit_free(d->sov); + visit_free(d->siv); g_free(d->string); g_free(d); } diff --git a/translate-all.c b/translate-all.c index eaa95e4cd7..0d47c1c0cf 100644 --- a/translate-all.c +++ b/translate-all.c @@ -2000,6 +2000,7 @@ int page_check_range(target_ulong start, target_ulong len, int flags) int page_unprotect(target_ulong address, uintptr_t pc) { unsigned int prot; + bool current_tb_invalidated; PageDesc *p; target_ulong host_start, host_end, addr; @@ -2021,6 +2022,7 @@ int page_unprotect(target_ulong address, uintptr_t pc) host_end = host_start + qemu_host_page_size; prot = 0; + current_tb_invalidated = false; for (addr = host_start ; addr < host_end ; addr += TARGET_PAGE_SIZE) { p = page_find(addr >> TARGET_PAGE_BITS); p->flags |= PAGE_WRITE; @@ -2028,10 +2030,7 @@ int page_unprotect(target_ulong address, uintptr_t pc) /* and since the content will be modified, we must invalidate the corresponding translated code. */ - if (tb_invalidate_phys_page(addr, pc)) { - mmap_unlock(); - return 2; - } + current_tb_invalidated |= tb_invalidate_phys_page(addr, pc); #ifdef DEBUG_TB_CHECK tb_invalidate_check(addr); #endif @@ -2040,7 +2039,8 @@ int page_unprotect(target_ulong address, uintptr_t pc) prot & PAGE_BITS); mmap_unlock(); - return 1; + /* If current TB was invalidated return to main loop */ + return current_tb_invalidated ? 2 : 1; } mmap_unlock(); return 0; diff --git a/ui/console.c b/ui/console.c index ce1e10570f..c24bfe422d 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1709,11 +1709,13 @@ QEMUGLContext dpy_gl_ctx_get_current(QemuConsole *con) void dpy_gl_scanout(QemuConsole *con, uint32_t backing_id, bool backing_y_0_top, + uint32_t backing_width, uint32_t backing_height, uint32_t x, uint32_t y, uint32_t width, uint32_t height) { assert(con->gl); con->gl->ops->dpy_gl_scanout(con->gl, backing_id, backing_y_0_top, + backing_width, backing_height, x, y, width, height); } diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c index 431457c746..3f5d328c7b 100644 --- a/ui/gtk-egl.c +++ b/ui/gtk-egl.c @@ -172,6 +172,7 @@ QEMUGLContext gd_egl_create_context(DisplayChangeListener *dcl, void gd_egl_scanout(DisplayChangeListener *dcl, uint32_t backing_id, bool backing_y_0_top, + uint32_t backing_width, uint32_t backing_height, uint32_t x, uint32_t y, uint32_t w, uint32_t h) { diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c index b86ff3cbec..0df5a36a9f 100644 --- a/ui/gtk-gl-area.c +++ b/ui/gtk-gl-area.c @@ -169,6 +169,7 @@ void gd_gl_area_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx) void gd_gl_area_scanout(DisplayChangeListener *dcl, uint32_t backing_id, bool backing_y_0_top, + uint32_t backing_width, uint32_t backing_height, uint32_t x, uint32_t y, uint32_t w, uint32_t h) { diff --git a/ui/input-linux.c b/ui/input-linux.c index 1d33b5c121..0e230ce699 100644 --- a/ui/input-linux.c +++ b/ui/input-linux.c @@ -129,6 +129,17 @@ static int qemu_input_linux_to_qcode(unsigned int lnx) return linux_to_qcode[lnx]; } +static bool linux_is_button(unsigned int lnx) +{ + if (lnx < 0x100) { + return false; + } + if (lnx >= 0x160 && lnx < 0x2c0) { + return false; + } + return true; +} + #define TYPE_INPUT_LINUX "input-linux" #define INPUT_LINUX(obj) \ OBJECT_CHECK(InputLinux, (obj), TYPE_INPUT_LINUX) @@ -153,6 +164,12 @@ struct InputLinux { int keycount; int wheel; bool initialized; + + bool has_rel_x; + bool has_abs_x; + int num_keys; + int num_btns; + QTAILQ_ENTRY(InputLinux) next; }; @@ -188,71 +205,55 @@ static void input_linux_toggle_grab(InputLinux *il) } } -static void input_linux_event_keyboard(void *opaque) +static void input_linux_handle_keyboard(InputLinux *il, + struct input_event *event) { - InputLinux *il = opaque; - struct input_event event; - int rc; - - for (;;) { - rc = read(il->fd, &event, sizeof(event)); - if (rc != sizeof(event)) { - if (rc < 0 && errno != EAGAIN) { - fprintf(stderr, "%s: read: %s\n", __func__, strerror(errno)); - qemu_set_fd_handler(il->fd, NULL, NULL, NULL); - close(il->fd); - } - break; + if (event->type == EV_KEY) { + if (event->value > 2 || (event->value > 1 && !il->repeat)) { + /* + * ignore autorepeat + unknown key events + * 0 == up, 1 == down, 2 == autorepeat, other == undefined + */ + return; + } + if (event->code >= KEY_CNT) { + /* + * Should not happen. But better safe than sorry, + * and we make Coverity happy too. + */ + return; } - switch (event.type) { - case EV_KEY: - if (event.value > 2 || (event.value > 1 && !il->repeat)) { - /* - * ignore autorepeat + unknown key events - * 0 == up, 1 == down, 2 == autorepeat, other == undefined - */ - continue; - } - if (event.code >= KEY_CNT) { - /* - * Should not happen. But better safe than sorry, - * and we make Coverity happy too. - */ - continue; - } - /* keep track of key state */ - if (!il->keydown[event.code] && event.value) { - il->keydown[event.code] = true; - il->keycount++; - } - if (il->keydown[event.code] && !event.value) { - il->keydown[event.code] = false; - il->keycount--; - } + /* keep track of key state */ + if (!il->keydown[event->code] && event->value) { + il->keydown[event->code] = true; + il->keycount++; + } + if (il->keydown[event->code] && !event->value) { + il->keydown[event->code] = false; + il->keycount--; + } - /* send event to guest when grab is active */ - if (il->grab_active) { - int qcode = qemu_input_linux_to_qcode(event.code); - qemu_input_event_send_key_qcode(NULL, qcode, event.value); - } + /* send event to guest when grab is active */ + if (il->grab_active) { + int qcode = qemu_input_linux_to_qcode(event->code); + qemu_input_event_send_key_qcode(NULL, qcode, event->value); + } - /* hotkey -> record switch request ... */ - if (il->keydown[KEY_LEFTCTRL] && - il->keydown[KEY_RIGHTCTRL]) { - il->grab_request = true; - } + /* hotkey -> record switch request ... */ + if (il->keydown[KEY_LEFTCTRL] && + il->keydown[KEY_RIGHTCTRL]) { + il->grab_request = true; + } - /* - * ... and do the switch when all keys are lifted, so we - * confuse neither guest nor host with keys which seem to - * be stuck due to missing key-up events. - */ - if (il->grab_request && !il->keycount) { - il->grab_request = false; - input_linux_toggle_grab(il); - } - break; + /* + * ... and do the switch when all keys are lifted, so we + * confuse neither guest nor host with keys which seem to + * be stuck due to missing key-up events. + */ + if (il->grab_request && !il->keycount) { + il->grab_request = false; + input_linux_toggle_grab(il); } } } @@ -265,7 +266,59 @@ static void input_linux_event_mouse_button(int button) qemu_input_event_sync(); } -static void input_linux_event_mouse(void *opaque) +static void input_linux_handle_mouse(InputLinux *il, struct input_event *event) +{ + if (!il->grab_active) { + return; + } + + switch (event->type) { + case EV_KEY: + switch (event->code) { + case BTN_LEFT: + qemu_input_queue_btn(NULL, INPUT_BUTTON_LEFT, event->value); + break; + case BTN_RIGHT: + qemu_input_queue_btn(NULL, INPUT_BUTTON_RIGHT, event->value); + break; + case BTN_MIDDLE: + qemu_input_queue_btn(NULL, INPUT_BUTTON_MIDDLE, event->value); + break; + case BTN_GEAR_UP: + qemu_input_queue_btn(NULL, INPUT_BUTTON_WHEEL_UP, event->value); + break; + case BTN_GEAR_DOWN: + qemu_input_queue_btn(NULL, INPUT_BUTTON_WHEEL_DOWN, + event->value); + break; + }; + break; + case EV_REL: + switch (event->code) { + case REL_X: + qemu_input_queue_rel(NULL, INPUT_AXIS_X, event->value); + break; + case REL_Y: + qemu_input_queue_rel(NULL, INPUT_AXIS_Y, event->value); + break; + case REL_WHEEL: + il->wheel = event->value; + break; + } + break; + case EV_SYN: + qemu_input_event_sync(); + if (il->wheel != 0) { + input_linux_event_mouse_button((il->wheel > 0) + ? INPUT_BUTTON_WHEEL_UP + : INPUT_BUTTON_WHEEL_DOWN); + il->wheel = 0; + } + break; + } +} + +static void input_linux_event(void *opaque) { InputLinux *il = opaque; struct input_event event; @@ -282,54 +335,11 @@ static void input_linux_event_mouse(void *opaque) break; } - /* only send event to guest when grab is active */ - if (!il->grab_active) { - continue; + if (il->num_keys) { + input_linux_handle_keyboard(il, &event); } - - switch (event.type) { - case EV_KEY: - switch (event.code) { - case BTN_LEFT: - qemu_input_queue_btn(NULL, INPUT_BUTTON_LEFT, event.value); - break; - case BTN_RIGHT: - qemu_input_queue_btn(NULL, INPUT_BUTTON_RIGHT, event.value); - break; - case BTN_MIDDLE: - qemu_input_queue_btn(NULL, INPUT_BUTTON_MIDDLE, event.value); - break; - case BTN_GEAR_UP: - qemu_input_queue_btn(NULL, INPUT_BUTTON_WHEEL_UP, event.value); - break; - case BTN_GEAR_DOWN: - qemu_input_queue_btn(NULL, INPUT_BUTTON_WHEEL_DOWN, - event.value); - break; - }; - break; - case EV_REL: - switch (event.code) { - case REL_X: - qemu_input_queue_rel(NULL, INPUT_AXIS_X, event.value); - break; - case REL_Y: - qemu_input_queue_rel(NULL, INPUT_AXIS_Y, event.value); - break; - case REL_WHEEL: - il->wheel = event.value; - break; - } - break; - case EV_SYN: - qemu_input_event_sync(); - if (il->wheel != 0) { - input_linux_event_mouse_button((il->wheel > 0) - ? INPUT_BUTTON_WHEEL_UP - : INPUT_BUTTON_WHEEL_DOWN); - il->wheel = 0; - } - break; + if (il->has_rel_x && il->num_btns) { + input_linux_handle_mouse(il, &event); } } } @@ -337,7 +347,8 @@ static void input_linux_event_mouse(void *opaque) static void input_linux_complete(UserCreatable *uc, Error **errp) { InputLinux *il = INPUT_LINUX(uc); - uint32_t evtmap, relmap, absmap; + uint8_t evtmap, relmap, absmap, keymap[KEY_CNT / 8]; + unsigned int i; int rc, ver; if (!il->evdev) { @@ -365,36 +376,36 @@ static void input_linux_complete(UserCreatable *uc, Error **errp) } if (evtmap & (1 << EV_REL)) { + relmap = 0; rc = ioctl(il->fd, EVIOCGBIT(EV_REL, sizeof(relmap)), &relmap); - if (rc < 0) { - relmap = 0; + if (relmap & (1 << REL_X)) { + il->has_rel_x = true; } } if (evtmap & (1 << EV_ABS)) { - ioctl(il->fd, EVIOCGBIT(EV_ABS, sizeof(absmap)), &absmap); - if (rc < 0) { - absmap = 0; + absmap = 0; + rc = ioctl(il->fd, EVIOCGBIT(EV_ABS, sizeof(absmap)), &absmap); + if (absmap & (1 << ABS_X)) { + il->has_abs_x = true; } } - if ((evtmap & (1 << EV_REL)) && - (relmap & (1 << REL_X))) { - /* has relative x axis -> assume mouse */ - qemu_set_fd_handler(il->fd, input_linux_event_mouse, NULL, il); - } else if ((evtmap & (1 << EV_ABS)) && - (absmap & (1 << ABS_X))) { - /* has absolute x axis -> not supported */ - error_setg(errp, "tablet/touchscreen not supported"); - goto err_close; - } else if (evtmap & (1 << EV_KEY)) { - /* has keys/buttons (and no x axis) -> assume keyboard */ - qemu_set_fd_handler(il->fd, input_linux_event_keyboard, NULL, il); - } else { - /* Huh? What is this? */ - error_setg(errp, "unknown kind of input device"); - goto err_close; + if (evtmap & (1 << EV_KEY)) { + memset(keymap, 0, sizeof(keymap)); + rc = ioctl(il->fd, EVIOCGBIT(EV_KEY, sizeof(keymap)), keymap); + for (i = 0; i < KEY_CNT; i++) { + if (keymap[i / 8] & (1 << (i % 8))) { + if (linux_is_button(i)) { + il->num_btns++; + } else { + il->num_keys++; + } + } + } } + + qemu_set_fd_handler(il->fd, input_linux_event, NULL, il); input_linux_toggle_grab(il); QTAILQ_INSERT_TAIL(&inputs, il, next); il->initialized = true; diff --git a/ui/sdl2-gl.c b/ui/sdl2-gl.c index a324ecacac..039645df3e 100644 --- a/ui/sdl2-gl.c +++ b/ui/sdl2-gl.c @@ -186,6 +186,7 @@ QEMUGLContext sdl2_gl_get_current_context(DisplayChangeListener *dcl) void sdl2_gl_scanout(DisplayChangeListener *dcl, uint32_t backing_id, bool backing_y_0_top, + uint32_t backing_width, uint32_t backing_height, uint32_t x, uint32_t y, uint32_t w, uint32_t h) { diff --git a/ui/spice-display.c b/ui/spice-display.c index 34095fbc8c..99132b69b6 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -527,11 +527,13 @@ static void interface_set_compression_level(QXLInstance *sin, int level) /* nothing to do */ } +#if SPICE_NEEDS_SET_MM_TIME static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time) { dprint(3, "%s/%d:\n", __func__, sin->id); /* nothing to do */ } +#endif static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info) { @@ -686,6 +688,7 @@ static int interface_client_monitors_config(QXLInstance *sin, { SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); QemuUIInfo info; + int head; if (!dpy_ui_info_supported(ssd->dcl.con)) { return 0; /* == not supported by guest */ @@ -695,14 +698,12 @@ static int interface_client_monitors_config(QXLInstance *sin, return 1; } - /* - * FIXME: multihead is tricky due to the way - * spice has multihead implemented. - */ memset(&info, 0, sizeof(info)); - if (mc->num_of_monitors > 0) { - info.width = mc->monitors[0].width; - info.height = mc->monitors[0].height; + + head = qemu_console_get_head(ssd->dcl.con); + if (mc->num_of_monitors > head) { + info.width = mc->monitors[head].width; + info.height = mc->monitors[head].height; } dpy_set_ui_info(ssd->dcl.con, &info); dprint(1, "%s/%d: size %dx%d\n", __func__, ssd->qxl.id, @@ -718,7 +719,9 @@ static const QXLInterface dpy_interface = { .attache_worker = interface_attach_worker, .set_compression_level = interface_set_compression_level, +#if SPICE_NEEDS_SET_MM_TIME .set_mm_time = interface_set_mm_time, +#endif .get_init_info = interface_get_init_info, /* the callbacks below are called from spice server thread context */ @@ -858,6 +861,8 @@ static QEMUGLContext qemu_spice_gl_create_context(DisplayChangeListener *dcl, static void qemu_spice_gl_scanout(DisplayChangeListener *dcl, uint32_t tex_id, bool y_0_top, + uint32_t backing_width, + uint32_t backing_height, uint32_t x, uint32_t y, uint32_t w, uint32_t h) { @@ -880,9 +885,7 @@ static void qemu_spice_gl_scanout(DisplayChangeListener *dcl, assert(!tex_id || fd >= 0); /* note: spice server will close the fd */ - spice_qxl_gl_scanout(&ssd->qxl, fd, - surface_width(ssd->ds), - surface_height(ssd->ds), + spice_qxl_gl_scanout(&ssd->qxl, fd, backing_width, backing_height, stride, fourcc, y_0_top); qemu_spice_gl_monitor_config(ssd, x, y, w, h); diff --git a/ui/vnc-enc-tight.c b/ui/vnc-enc-tight.c index e5cba0e5a7..b8581dd2e9 100644 --- a/ui/vnc-enc-tight.c +++ b/ui/vnc-enc-tight.c @@ -349,7 +349,7 @@ tight_detect_smooth_image(VncState *vs, int w, int h) tight_fill_palette##bpp(VncState *vs, int x, int y, \ int max, size_t count, \ uint32_t *bg, uint32_t *fg, \ - VncPalette **palette) { \ + VncPalette *palette) { \ uint##bpp##_t *data; \ uint##bpp##_t c0, c1, ci; \ int i, n0, n1; \ @@ -396,23 +396,23 @@ tight_detect_smooth_image(VncState *vs, int w, int h) return 0; \ } \ \ - *palette = palette_new(max, bpp); \ - palette_put(*palette, c0); \ - palette_put(*palette, c1); \ - palette_put(*palette, ci); \ + palette_init(palette, max, bpp); \ + palette_put(palette, c0); \ + palette_put(palette, c1); \ + palette_put(palette, ci); \ \ for (i++; i < count; i++) { \ if (data[i] == ci) { \ continue; \ } else { \ ci = data[i]; \ - if (!palette_put(*palette, (uint32_t)ci)) { \ + if (!palette_put(palette, (uint32_t)ci)) { \ return 0; \ } \ } \ } \ \ - return palette_size(*palette); \ + return palette_size(palette); \ } DEFINE_FILL_PALETTE_FUNCTION(8) @@ -421,7 +421,7 @@ DEFINE_FILL_PALETTE_FUNCTION(32) static int tight_fill_palette(VncState *vs, int x, int y, size_t count, uint32_t *bg, uint32_t *fg, - VncPalette **palette) + VncPalette *palette) { int max; @@ -1457,9 +1457,11 @@ static int send_sub_rect_jpeg(VncState *vs, int x, int y, int w, int h, } #endif +static __thread VncPalette color_count_palette; + static int send_sub_rect(VncState *vs, int x, int y, int w, int h) { - VncPalette *palette = NULL; + VncPalette *palette = &color_count_palette; uint32_t bg = 0, fg = 0; int colors; int ret = 0; @@ -1488,7 +1490,7 @@ static int send_sub_rect(VncState *vs, int x, int y, int w, int h) } #endif - colors = tight_fill_palette(vs, x, y, w * h, &bg, &fg, &palette); + colors = tight_fill_palette(vs, x, y, w * h, &bg, &fg, palette); #ifdef CONFIG_VNC_JPEG if (allow_jpeg && vs->tight.quality != (uint8_t)-1) { @@ -1501,7 +1503,6 @@ static int send_sub_rect(VncState *vs, int x, int y, int w, int h) ret = send_sub_rect_nojpeg(vs, x, y, w, h, bg, fg, colors, palette); #endif - palette_destroy(palette); return ret; } @@ -1025,7 +1025,7 @@ static int find_and_clear_dirty_height(VncState *vs, static int vnc_update_client(VncState *vs, int has_dirty, bool sync) { vs->has_dirty += has_dirty; - if (vs->need_update && vs->ioc != NULL) { + if (vs->need_update && !vs->disconnecting) { VncDisplay *vd = vs->vd; VncJob *job; int y; @@ -1436,8 +1436,9 @@ static void vnc_jobs_bh(void *opaque) * First function called whenever there is more data to be read from * the client socket. Will delegate actual work according to whether * SASL SSF layers are enabled (thus requiring decryption calls) + * Returns 0 on success, -1 if client disconnected */ -static void vnc_client_read(VncState *vs) +static int vnc_client_read(VncState *vs) { ssize_t ret; @@ -1450,8 +1451,9 @@ static void vnc_client_read(VncState *vs) if (!ret) { if (vs->disconnecting) { vnc_disconnect_finish(vs); + return -1; } - return; + return 0; } while (vs->read_handler && vs->input.offset >= vs->read_handler_expect) { @@ -1461,7 +1463,7 @@ static void vnc_client_read(VncState *vs) ret = vs->read_handler(vs, vs->input.buffer, len); if (vs->disconnecting) { vnc_disconnect_finish(vs); - return; + return -1; } if (!ret) { @@ -1470,6 +1472,7 @@ static void vnc_client_read(VncState *vs) vs->read_handler_expect = ret; } } + return 0; } gboolean vnc_client_io(QIOChannel *ioc G_GNUC_UNUSED, @@ -1477,7 +1480,9 @@ gboolean vnc_client_io(QIOChannel *ioc G_GNUC_UNUSED, { VncState *vs = opaque; if (condition & G_IO_IN) { - vnc_client_read(vs); + if (vnc_client_read(vs) < 0) { + return TRUE; + } } if (condition & G_IO_OUT) { vnc_client_write(vs); diff --git a/util/log.c b/util/log.c index 32e416051c..b6c75b1102 100644 --- a/util/log.c +++ b/util/log.c @@ -131,8 +131,8 @@ bool qemu_log_in_addr_range(uint64_t addr) if (debug_regions) { int i = 0; for (i = 0; i < debug_regions->len; i++) { - struct Range *range = &g_array_index(debug_regions, Range, i); - if (addr >= range->begin && addr <= range->end) { + Range *range = &g_array_index(debug_regions, Range, i); + if (range_contains(range, addr)) { return true; } } @@ -158,7 +158,7 @@ void qemu_set_dfilter_ranges(const char *filter_spec, Error **errp) for (i = 0; ranges[i]; i++) { const char *r = ranges[i]; const char *range_op, *r2, *e; - uint64_t r1val, r2val; + uint64_t r1val, r2val, lob, upb; struct Range range; range_op = strstr(r, "-"); @@ -187,27 +187,28 @@ void qemu_set_dfilter_ranges(const char *filter_spec, Error **errp) (int)(r2 - range_op), range_op); goto out; } - if (r2val == 0) { - error_setg(errp, "Invalid range"); - goto out; - } switch (*range_op) { case '+': - range.begin = r1val; - range.end = r1val + (r2val - 1); + lob = r1val; + upb = r1val + r2val - 1; break; case '-': - range.end = r1val; - range.begin = r1val - (r2val - 1); + upb = r1val; + lob = r1val - (r2val - 1); break; case '.': - range.begin = r1val; - range.end = r2val; + lob = r1val; + upb = r2val; break; default: g_assert_not_reached(); } + if (lob > upb) { + error_setg(errp, "Invalid range"); + goto out; + } + range_set_bounds(&range, lob, upb); g_array_append_val(debug_regions, range); } out: diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index cc2b043907..fb83d48944 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -1143,33 +1143,6 @@ SocketAddress *socket_remote_address(int fd, Error **errp) return socket_sockaddr_to_address(&ss, sslen, errp); } - -void qapi_copy_SocketAddress(SocketAddress **p_dest, - SocketAddress *src) -{ - QmpOutputVisitor *qov; - QmpInputVisitor *qiv; - Visitor *ov, *iv; - QObject *obj; - - *p_dest = NULL; - - qov = qmp_output_visitor_new(); - ov = qmp_output_get_visitor(qov); - visit_type_SocketAddress(ov, NULL, &src, &error_abort); - obj = qmp_output_get_qobject(qov); - qmp_output_visitor_cleanup(qov); - if (!obj) { - return; - } - - qiv = qmp_input_visitor_new(obj, true); - iv = qmp_input_get_visitor(qiv); - visit_type_SocketAddress(iv, NULL, p_dest, &error_abort); - qmp_input_visitor_cleanup(qiv); - qobject_decref(obj); -} - char *socket_address_to_string(struct SocketAddress *addr, Error **errp) { char *buf; diff --git a/util/range.c b/util/range.c index e90c988dbf..416df7cdae 100644 --- a/util/range.c +++ b/util/range.c @@ -22,20 +22,18 @@ #include "qemu/range.h" /* - * Operations on 64 bit address ranges. - * Notes: - * - ranges must not wrap around 0, but can include the last byte ~0x0LL. - * - this can not represent a full 0 to ~0x0LL range. + * Return -1 if @a < @b, 1 @a > @b, and 0 if they touch or overlap. + * Both @a and @b must not be empty. */ - -/* Return -1 if @a < @b, 1 if greater, and 0 if they touch or overlap. */ static inline int range_compare(Range *a, Range *b) { - /* Zero a->end is 2**64, and therefore not less than any b->begin */ - if (a->end && a->end < b->begin) { + assert(!range_is_empty(a) && !range_is_empty(b)); + + /* Careful, avoid wraparound */ + if (b->lob && b->lob - 1 > a->upb) { return -1; } - if (b->end && a->begin > b->end) { + if (a->lob && a->lob - 1 > b->upb) { return 1; } return 0; @@ -46,8 +44,7 @@ GList *range_list_insert(GList *list, Range *data) { GList *l; - /* Range lists require no empty ranges */ - assert(data->begin < data->end || (data->begin && !data->end)); + assert(!range_is_empty(data)); /* Skip all list elements strictly less than data */ for (l = list; l && range_compare(l->data, data) < 0; l = l->next) { @@ -2675,6 +2675,11 @@ void qemu_add_machine_init_done_notifier(Notifier *notify) } } +void qemu_remove_machine_init_done_notifier(Notifier *notify) +{ + notifier_remove(notify); +} + static void qemu_run_machine_init_done_notifiers(void) { notifier_list_notify(&machine_init_done_notifiers, NULL); @@ -2908,6 +2913,19 @@ static void set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size, loc_pop(&loc); } +static int global_init_func(void *opaque, QemuOpts *opts, Error **errp) +{ + GlobalProperty *g; + + g = g_malloc0(sizeof(*g)); + g->driver = qemu_opt_get(opts, "driver"); + g->property = qemu_opt_get(opts, "property"); + g->value = qemu_opt_get(opts, "value"); + g->user_provided = true; + qdev_prop_register_global(g); + return 0; +} + int main(int argc, char **argv, char **envp) { int i; @@ -4430,14 +4448,10 @@ int main(int argc, char **argv, char **envp) exit (i == 1 ? 1 : 0); } - if (machine_class->compat_props) { - GlobalProperty *p; - for (i = 0; i < machine_class->compat_props->len; i++) { - p = g_array_index(machine_class->compat_props, GlobalProperty *, i); - qdev_prop_register_global(p); - } - } - qemu_add_globals(); + machine_register_compat_props(current_machine); + + qemu_opts_foreach(qemu_find_opts("global"), + global_init_func, NULL, NULL); /* This checkpoint is required by replay to separate prior clock reading from the other reads, because timer polling functions query @@ -190,6 +190,9 @@ static void xen_ram_init(PCMachineState *pcms, /* Handle the machine opt max-ram-below-4g. It is basically doing * min(xen limit, user limit). */ + if (!user_lowmem) { + user_lowmem = HVM_BELOW_4G_RAM_END; /* default */ + } if (HVM_BELOW_4G_RAM_END <= user_lowmem) { user_lowmem = HVM_BELOW_4G_RAM_END; } |